{"spec_id":"bar-stacked-percent","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nbar-stacked-percent: 100% Stacked Bar Chart\nLibrary: plotly 6.9.0 | Python 3.13.15\nQuality: 90/100 | Updated: 2026-08-18\n\"\"\"\n\nimport os\n\nimport plotly.graph_objects as go\n\n\n# Theme tokens (see prompts/default-style-guide.md \"Background\" + \"Theme-adaptive Chrome\")\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nGRID = \"rgba(26,26,23,0.15)\" if THEME == \"light\" else \"rgba(240,239,232,0.15)\"\n\n# Imprint categorical palette — first series is always #009E73\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"]\n# \"Other\" is a semantic rest/remainder bucket — use the theme-adaptive muted anchor, not the next ordinal hue\nANYPLOT_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n\ndef _srgb_channel(c):\n    return c / 12.92 if c <= 0.03928 else ((c + 0.055) / 1.055) ** 2.4\n\n\ndef contrast_text_color(bg_hex):\n    \"\"\"Pick white or ink label text — whichever clears WCAG contrast against bg_hex.\"\"\"\n    r, g, b = (int(bg_hex.lstrip(\"#\")[i : i + 2], 16) / 255 for i in (0, 2, 4))\n    lum = 0.2126 * _srgb_channel(r) + 0.7152 * _srgb_channel(g) + 0.0722 * _srgb_channel(b)\n    contrast_white = 1.05 / (lum + 0.05)\n    contrast_ink = (lum + 0.05) / 0.06  # ink (#1A1A17) luminance ≈ 0.01\n    return \"#FFFFFF\" if contrast_white >= contrast_ink else \"#1A1A17\"\n\n\n# Data: raw device shipments (millions) by OS and region, sorted ascending by\n# iOS share so the bars read left-to-right as a rising narrative. Each region's\n# shipment total differs (reflecting real market size) — percentages below are\n# normalized from these raw counts, not hardcoded.\ncomponents = [\"iOS\", \"Android\", \"Other\"]\nraw_shipments = {\n    \"Africa\": [4, 44, 2],\n    \"Asia\": [45, 249, 6],\n    \"South America\": [27, 117, 6],\n    \"Europe\": [44, 150, 6],\n    \"North America\": [100, 288, 12],\n}\ncategories = list(raw_shipments.keys())\ndata = {cat: [v / sum(values) * 100 for v in values] for cat, values in raw_shipments.items()}\ncolors = [IMPRINT_PALETTE[0], IMPRINT_PALETTE[1], ANYPLOT_MUTED]\n\n# Plot — see default-style-guide.md \"Visual Sizing Defaults\" for the canvas + sizing values\nfig = go.Figure()\n\nfor i, component in enumerate(components):\n    values = [data[cat][i] for cat in categories]\n    fig.add_trace(\n        go.Bar(\n            name=component,\n            x=categories,\n            y=values,\n            marker=dict(color=colors[i], line=dict(color=PAGE_BG, width=2)),\n            text=[f\"{v:.0f}%\" if v >= 6 else \"\" for v in values],\n            textposition=\"inside\",\n            insidetextanchor=\"middle\",\n            textfont=dict(size=13, color=contrast_text_color(colors[i])),\n            hovertemplate=\"<b>%{x}</b><br>%{fullData.name}: %{y:.0f}%<extra></extra>\",\n        )\n    )\n\nfig.update_layout(\n    autosize=False,\n    barmode=\"stack\",\n    bargap=0.25,\n    title=dict(\n        text=\"bar-stacked-percent · python · plotly · anyplot.ai\",\n        font=dict(size=16, color=INK),\n        subtitle=dict(\n            text=\"Android leads every region — iOS share climbs from 8% in Africa to 25% in North America\",\n            font=dict(size=11, color=INK_SOFT),\n        ),\n        x=0.5,\n        xanchor=\"center\",\n    ),\n    xaxis=dict(\n        title=dict(text=\"Region\", font=dict(size=12, color=INK)),\n        tickfont=dict(size=10, color=INK_SOFT),\n        showgrid=False,\n        showline=True,\n        linecolor=INK_SOFT,\n        linewidth=1.5,\n    ),\n    yaxis=dict(\n        title=dict(text=\"Market Share (%)\", font=dict(size=12, color=INK)),\n        tickfont=dict(size=10, color=INK_SOFT),\n        range=[0, 100],\n        ticksuffix=\"%\",\n        showgrid=True,\n        gridcolor=GRID,\n        showline=True,\n        linecolor=INK_SOFT,\n        linewidth=1.5,\n    ),\n    legend=dict(\n        orientation=\"h\",\n        yanchor=\"top\",\n        y=-0.2,\n        xanchor=\"center\",\n        x=0.5,\n        font=dict(size=10, color=INK_SOFT),\n        bgcolor=ELEVATED_BG,\n        borderwidth=0,\n    ),\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    font=dict(color=INK),\n    margin=dict(l=90, r=40, t=110, b=110),\n    hovermode=\"x unified\",\n)\n\n# Save — hard target 3200×1800 (landscape), see prompts/library/plotly.md \"Canvas\"\nfig.write_image(f\"plot-{THEME}.png\", width=800, height=450, scale=4)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}