{"spec_id":"step-basic","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\nstep-basic: Basic Step Plot\nLibrary: pygal 3.1.3 | Python 3.13.14\nQuality: 88/100 | Updated: 2026-07-25\n\"\"\"\n\nimport os\nimport sys\n\n\n# Pop script dir so this file (pygal.py) doesn't shadow the installed pygal package\n_script_dir = sys.path.pop(0)\nimport pygal\nfrom pygal.style import Style\n\n\nsys.path.insert(0, _script_dir)\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\nIMPRINT = (\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\")\n\n# Data - Monthly cumulative sales (in thousands)\nmonths = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\ncumulative_sales = [45, 92, 128, 165, 198, 256, 312, 378, 425, 489, 562, 635]\n\n# Find month with peak month-on-month growth for storytelling emphasis\nmonthly_growth = [cumulative_sales[i] - cumulative_sales[i - 1] for i in range(1, len(cumulative_sales))]\npeak_idx = monthly_growth.index(max(monthly_growth)) + 1  # Nov (index 10) — +$73K\n\nNODE_R = 14  # standard dot radius — bumped for stronger visibility on sparse 12-point data\nPEAK_R = 26  # peak dot radius — clearly emphasized vs. the standard dots\n\n\ndef _blank(value, **_kwargs):\n    \"\"\"Suppress the static print-value for every node except the peak (avoids clutter).\"\"\"\n    return \"\"\n\n\n# Build true step-chart data using pygal.XY coordinates.\n# Pattern per data point i: (i, prev_val)[no dot] → (i, val)[dot] → (i+1, val)[no dot]\n# This creates genuine vertical rises and horizontal holds — no diagonal approximation.\nstep_data = []\nfor i, (month, val) in enumerate(zip(months, cumulative_sales, strict=True)):\n    if i > 0:\n        # Bottom of vertical step: hold previous value up to this x position\n        step_data.append({\"value\": (i, cumulative_sales[i - 1]), \"node\": {\"r\": 0}, \"formatter\": _blank})\n\n    # Actual data point — larger dot + rich tooltip label for interactivity\n    increment = val - (cumulative_sales[i - 1] if i > 0 else 0)\n    is_peak = i == peak_idx\n    if is_peak:\n        label = f\"★ Peak growth — {month}: +${increment}K → ${val}K cumulative\"\n        point = {\n            \"value\": (i, val),\n            \"node\": {\"r\": PEAK_R},\n            \"label\": label,\n            # Defined stroke ring makes the peak marker read as a deliberate accent,\n            # not just a bigger version of the same dot.\n            \"style\": f\"fill: {IMPRINT[0]}; stroke: {INK}; stroke-width: 4\",\n            \"formatter\": lambda v, _text=f\"★ {month} +${increment}K\": _text,\n        }\n    else:\n        label = f\"{month}: ${val}K cumulative (+${increment}K)\"\n        point = {\"value\": (i, val), \"node\": {\"r\": NODE_R}, \"label\": label, \"formatter\": _blank}\n    step_data.append(point)\n\n    if i < len(months) - 1:\n        # Horizontal hold: extend current value to the next x position\n        step_data.append({\"value\": (i + 1, val), \"node\": {\"r\": 0}, \"formatter\": _blank})\n\n# Custom style\ncustom_style = Style(\n    background=PAGE_BG,\n    plot_background=PAGE_BG,\n    foreground=INK,\n    foreground_strong=INK,\n    foreground_subtle=INK_MUTED,\n    colors=IMPRINT,\n    title_font_size=66,\n    label_font_size=56,\n    major_label_font_size=50,\n    legend_font_size=44,\n    value_font_size=40,\n    stroke_width=3,\n    opacity=\"0.28\",\n    opacity_hover=\"0.9\",\n)\n\n# Chart — XY mode gives full control over x coordinates for true vertical step transitions\nchart = pygal.XY(\n    width=3200,\n    height=1800,\n    title=\"step-basic · pygal · anyplot.ai\",\n    x_title=\"Month\",\n    y_title=\"Cumulative Sales ($K)\",\n    style=custom_style,\n    show_dots=True,\n    dots_size=NODE_R,\n    stroke_style={\"width\": 6},\n    show_y_guides=True,\n    show_x_guides=False,\n    show_legend=False,\n    fill=True,\n    print_values=True,  # static peak-growth label on the PNG (per-node formatter suppresses the rest)\n    print_zeroes=False,\n    margin=80,\n    range=(0, 700),  # headroom above the Dec peak (635) so the top dot isn't flush with the frame\n    x_value_formatter=lambda x: months[round(x)] if 0 <= round(x) <= 11 else \"\",\n)\n\n# Place x-axis tick labels at integer positions 0–11 (mapped to month names by formatter)\nchart.x_labels = list(range(12))\nchart.add(\"Cumulative Sales\", step_data)\n\n# Save\nchart.render_to_png(f\"plot-{THEME}.png\")\nwith open(f\"plot-{THEME}.html\", \"wb\") as f:\n    f.write(chart.render())\n"}