{"spec_id":"lollipop-basic","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nlollipop-basic: Basic Lollipop Chart\nLibrary: altair 6.2.2 | Python 3.13.14\nQuality: 91/100 | Updated: 2026-07-01\n\"\"\"\n\nimport sys as _sys\n\n\n# Remove script directory from sys.path so `import altair` finds the installed\n# package and not this file (which shares the library name).\nif _sys.path:\n    _sys.path.pop(0)\ndel _sys\n\nimport os\n\nimport altair as alt\nimport pandas as pd\nfrom PIL import Image\n\n\n# Theme tokens (see prompts/default-style-guide.md \"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\"\n\nBRAND = \"#009E73\"  # Imprint palette position 1 — ALWAYS first series\n\n# Data — product sales by category\ncategories = [\n    \"Electronics\",\n    \"Clothing\",\n    \"Home & Garden\",\n    \"Sports\",\n    \"Books\",\n    \"Toys\",\n    \"Beauty\",\n    \"Automotive\",\n    \"Food & Grocery\",\n    \"Pet Supplies\",\n]\nvalues = [425000, 312000, 287000, 234000, 198000, 176000, 152000, 134000, 118000, 95000]\n\ndf = pd.DataFrame({\"category\": categories, \"value\": values})\ndf = df.sort_values(\"value\", ascending=False).reset_index(drop=True)\n\n# Interactive hover selection — highlight/dim on pointerover (LM-02)\nselection = alt.selection_point(fields=[\"category\"], on=\"pointerover\", empty=True)\n\n# Stems: vertical rules from baseline to value\nstems = (\n    alt.Chart(df)\n    .mark_rule(strokeWidth=4)\n    .encode(\n        x=alt.X(\"category:N\", sort=\"-y\", title=\"Category\", axis=alt.Axis(labelAngle=-45, labelFontSize=9)),\n        y=alt.Y(\"value:Q\", title=\"Sales (USD)\", axis=alt.Axis(format=\"$,.0f\")),\n        color=alt.value(BRAND),\n        opacity=alt.condition(selection, alt.value(1.0), alt.value(0.3)),\n    )\n    .add_params(selection)\n)\n\n# Dots: top performer (Electronics) gets focal emphasis via larger marker (DE-01)\ndots = (\n    alt.Chart(df)\n    .mark_circle(stroke=PAGE_BG, strokeWidth=2)\n    .encode(\n        x=alt.X(\"category:N\", sort=\"-y\"),\n        y=alt.Y(\"value:Q\"),\n        color=alt.value(BRAND),\n        size=alt.condition(alt.datum.category == \"Electronics\", alt.value(950), alt.value(480)),\n        opacity=alt.condition(selection, alt.value(1.0), alt.value(0.3)),\n        tooltip=[alt.Tooltip(\"category:N\", title=\"Category\"), alt.Tooltip(\"value:Q\", title=\"Sales\", format=\"$,.0f\")],\n    )\n)\n\n# Value annotation on top performer to anchor the narrative (DE-03)\nannotation_df = df[df[\"category\"] == \"Electronics\"].copy()\nannotation = (\n    alt.Chart(annotation_df)\n    .mark_text(dy=-18, fontSize=10, fontWeight=\"bold\")\n    .encode(\n        x=alt.X(\"category:N\", sort=\"-y\"),\n        y=alt.Y(\"value:Q\"),\n        text=alt.Text(\"value:Q\", format=\"$,.0f\"),\n        color=alt.value(INK),\n    )\n)\n\n# Title: n=73 chars → fontsize = round(16 × 67/73) = 15\nTITLE = \"Product Sales by Category · lollipop-basic · python · altair · anyplot.ai\"\n\nchart = (\n    (stems + dots + annotation)\n    .properties(\n        width=620,\n        height=320,\n        background=PAGE_BG,\n        title=alt.Title(TITLE, fontSize=15, anchor=\"start\", color=INK, offset=10),\n    )\n    .configure_view(fill=PAGE_BG, stroke=None)\n    .configure_axis(\n        domainColor=INK_SOFT,\n        domainOpacity=0,\n        domainWidth=1,\n        tickColor=INK_SOFT,\n        gridColor=INK,\n        gridOpacity=0.12,\n        gridWidth=1,\n        labelColor=INK_SOFT,\n        labelFontSize=10,\n        titleColor=INK,\n        titleFontSize=12,\n        titlePadding=12,\n    )\n    .configure_axisX(grid=False, labelPadding=8)\n    .configure_axisY(grid=True, labelPadding=8)\n    .configure_title(color=INK, fontWeight=\"bold\")\n    .configure_legend(fillColor=ELEVATED_BG, strokeColor=INK_SOFT, labelColor=INK_SOFT, titleColor=INK)\n)\n\nchart.save(f\"plot-{THEME}.png\", scale_factor=4.0)\n\n# Pad to exact 3200×1800 — altair/vl-convert canvas rule (see prompts/library/altair.md)\nTW, TH = 3200, 1800\n_img = Image.open(f\"plot-{THEME}.png\").convert(\"RGB\")\n_w, _h = _img.size\nif _w > TW or _h > TH:\n    raise SystemExit(\n        f\"altair vl-convert produced {_w}×{_h}, exceeds target {TW}×{TH}. \"\n        \"Shrink chart .properties(width=, height=) and re-render.\"\n    )\nif _w < TW or _h < TH:\n    _canvas = Image.new(\"RGB\", (TW, TH), PAGE_BG)\n    _canvas.paste(_img, ((TW - _w) // 2, (TH - _h) // 2))\n    _canvas.save(f\"plot-{THEME}.png\")\n\nchart.save(f\"plot-{THEME}.html\")\n"}