{"spec_id":"pictogram-basic","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\npictogram-basic: Pictogram Chart (Isotype Visualization)\nLibrary: plotly 6.7.0 | Python 3.13.13\nQuality: 88/100 | Updated: 2026-06-03\n\"\"\"\n\nimport os\n\nimport plotly.graph_objects as go\n\n\n# Theme tokens — Imprint palette (see prompts/default-style-guide.md)\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\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Imprint categorical palette — positions 1-5\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\"]\n\n# Data — fruit production (thousands of tonnes)\ncategories = [\"Apples\", \"Oranges\", \"Bananas\", \"Grapes\", \"Mangoes\"]\nvalues = [35, 22, 18, 12, 10]\nICONS_PER_UNIT = 5\n\n# Title — scale fontsize for length\ntitle = \"Fruit Production · pictogram-basic · python · plotly · anyplot.ai\"\ntitle_fontsize = round(16 * min(1.0, 67 / len(title)))\n\n# Grid layout parameters\nSPACING = 0.55\nROW_HEIGHT = 0.85\ny_positions = [i * ROW_HEIGHT for i in range(len(categories))]\nband_half = ROW_HEIGHT / 2\nmax_icons = max(v // ICONS_PER_UNIT + (1 if v % ICONS_PER_UNIT else 0) for v in values)\n\nfig = go.Figure()\n\n# Row background bands — low-opacity tint from each category's Imprint color\nfor y_pos, color in zip(y_positions, IMPRINT_PALETTE, strict=True):\n    r, g, b = int(color[1:3], 16), int(color[3:5], 16), int(color[5:7], 16)\n    fig.add_shape(\n        type=\"rect\",\n        xref=\"paper\",\n        yref=\"y\",\n        x0=0,\n        x1=1,\n        y0=y_pos - band_half,\n        y1=y_pos + band_half,\n        fillcolor=f\"rgba({r},{g},{b},0.06)\",\n        line={\"width\": 0},\n        layer=\"below\",\n    )\n\n# Track which row each trace belongs to (used for dropdown visibility)\ntrace_row_idx = []\n\nfor row, (cat, val, color) in enumerate(zip(categories, values, IMPRINT_PALETTE, strict=True)):\n    full_icons = val // ICONS_PER_UNIT\n    remainder = val % ICONS_PER_UNIT\n    y_pos = y_positions[row]\n    is_leader = row == 0\n    icon_size = 38 if is_leader else 34\n\n    if full_icons > 0:\n        x_full = [i * SPACING for i in range(full_icons)]\n        fig.add_trace(\n            go.Scatter(\n                x=x_full,\n                y=[y_pos] * full_icons,\n                mode=\"markers\",\n                marker={\"symbol\": \"square\", \"size\": icon_size, \"color\": color, \"line\": {\"color\": PAGE_BG, \"width\": 2}},\n                customdata=[[cat, val, ICONS_PER_UNIT]] * full_icons,\n                hovertemplate=(\n                    \"<b>%{customdata[0]}</b><br>%{customdata[1]}k tonnes<br>Each ▪ = %{customdata[2]}k<extra></extra>\"\n                ),\n                showlegend=False,\n            )\n        )\n        trace_row_idx.append(row)\n\n    if remainder > 0:\n        fraction = remainder / ICONS_PER_UNIT\n        fig.add_trace(\n            go.Scatter(\n                x=[full_icons * SPACING],\n                y=[y_pos],\n                mode=\"markers\",\n                marker={\n                    \"symbol\": \"square\",\n                    \"size\": icon_size,\n                    \"color\": color,\n                    \"opacity\": max(0.3, fraction),\n                    \"line\": {\"color\": PAGE_BG, \"width\": 2},\n                },\n                customdata=[[cat, val, remainder]],\n                hovertemplate=(\n                    \"<b>%{customdata[0]}</b><br>%{customdata[1]}k tonnes<br>Partial: %{customdata[2]}k<extra></extra>\"\n                ),\n                showlegend=False,\n            )\n        )\n        trace_row_idx.append(row)\n\n    # Value label at end of each row\n    total_icons = full_icons + (1 if remainder > 0 else 0)\n    fig.add_annotation(\n        x=total_icons * SPACING + 0.15,\n        y=y_pos,\n        text=f\"<b>{val}k</b>\" if is_leader else f\"{val}k\",\n        showarrow=False,\n        font={\"size\": 22 if is_leader else 18, \"color\": INK if is_leader else INK_SOFT, \"family\": \"Arial\"},\n        xanchor=\"left\",\n    )\n\n    # Dotted baseline connector under each row of icons\n    if total_icons > 1:\n        fig.add_shape(\n            type=\"line\",\n            x0=0,\n            x1=(total_icons - 1) * SPACING,\n            y0=y_pos + icon_size * 0.0085,\n            y1=y_pos + icon_size * 0.0085,\n            line={\"color\": color, \"width\": 0.5, \"dash\": \"dot\"},\n            opacity=0.25,\n            layer=\"below\",\n        )\n\n# Legend at bottom\nfig.add_annotation(\n    xref=\"paper\",\n    yref=\"paper\",\n    x=0.0,\n    y=-0.08,\n    text=f\"<b>▪</b> = {ICONS_PER_UNIT}k tonnes  |  Faded ▪ = partial unit\",\n    showarrow=False,\n    font={\"size\": 16, \"color\": INK_MUTED, \"family\": \"Arial\"},\n    xanchor=\"left\",\n)\n\n# Subtitle — key insight above the chart (left-anchored to stay clear of right-side dropdown)\nfig.add_annotation(\n    xref=\"paper\",\n    yref=\"paper\",\n    x=0.0,\n    y=1.06,\n    text=\"Apples lead with 35k tonnes — nearly double the next category\",\n    showarrow=False,\n    font={\"size\": 14, \"color\": INK_SOFT, \"family\": \"Arial\"},\n    xanchor=\"left\",\n)\n\n# Dropdown — toggle between all categories and top 3\nall_visible = [True] * len(fig.data)\ntop3_visible = [trace_row_idx[i] < 3 for i in range(len(fig.data))]\n\nfig.update_layout(\n    autosize=False,\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    font={\"color\": INK},\n    title={\n        \"text\": title,\n        \"font\": {\"size\": title_fontsize, \"color\": INK, \"family\": \"Arial\"},\n        \"x\": 0.5,\n        \"xanchor\": \"center\",\n        \"y\": 0.95,\n    },\n    xaxis={\n        \"showgrid\": False,\n        \"zeroline\": False,\n        \"showticklabels\": False,\n        \"showline\": False,\n        \"range\": [-0.5, max_icons * SPACING + 1.0],\n    },\n    yaxis={\n        \"tickvals\": y_positions,\n        \"ticktext\": [f\"<b>{c}</b>\" if i == 0 else c for i, c in enumerate(categories)],\n        \"tickfont\": {\"size\": 20, \"color\": INK_SOFT, \"family\": \"Arial\"},\n        \"showgrid\": False,\n        \"zeroline\": False,\n        \"showline\": False,\n        \"range\": [max(y_positions) + band_half + 0.1, min(y_positions) - band_half - 0.1],\n    },\n    margin={\"t\": 120, \"b\": 80, \"l\": 150, \"r\": 50},\n    hoverlabel={\"bgcolor\": ELEVATED_BG, \"font_size\": 14, \"font_family\": \"Arial\", \"font_color\": INK},\n    updatemenus=[\n        {\n            \"buttons\": [\n                {\"label\": \"All Categories\", \"method\": \"update\", \"args\": [{\"visible\": all_visible}]},\n                {\"label\": \"Top 3 Only\", \"method\": \"update\", \"args\": [{\"visible\": top3_visible}]},\n            ],\n            \"direction\": \"down\",\n            \"showactive\": True,\n            \"x\": 1.0,\n            \"xanchor\": \"right\",\n            \"y\": 1.12,\n            \"yanchor\": \"top\",\n            \"bgcolor\": ELEVATED_BG,\n            \"bordercolor\": INK_SOFT,\n            \"font\": {\"size\": 12, \"color\": INK},\n        }\n    ],\n)\n\n# Save — landscape 3200×1800 (width=800 height=450 scale=4)\nfig.write_image(f\"plot-{THEME}.png\", width=800, height=450, scale=4)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}