{"spec_id":"area-basic","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\narea-basic: Basic Area Chart\nLibrary: pygal 3.1.0 | Python 3.13.13\nQuality: 89/100 | Updated: 2026-05-28\n\"\"\"\n\nimport os\nimport sys\n\n\n# Script is named pygal.py — remove its directory from sys.path first so\n# 'import pygal' resolves to the installed package, not this file.\nsys.path = [p for p in sys.path if p != os.path.dirname(os.path.abspath(__file__))]\n\nimport pygal\nfrom pygal.style import Style\n\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_PALETTE = (\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\")\n\n# Data — daily website visitors over a month (three distinct phases)\ndays = list(range(1, 31))\nvisitors = [\n    1250,\n    1380,\n    1420,\n    1180,\n    980,\n    890,\n    920,  # Week 1: baseline, weekend dip\n    1340,\n    1520,\n    1680,\n    1590,\n    1450,\n    1120,\n    1080,  # Week 2: recovery, mid-month dip\n    1560,\n    1720,\n    1890,\n    2010,\n    1850,\n    1420,\n    1380,  # Week 3: growth phase\n    1680,\n    1920,\n    2150,\n    2080,\n    1950,\n    1620,\n    1540,  # Week 4: peak plateau\n    1780,\n    1920,  # Final uptick\n]\n\npeak_idx = visitors.index(max(visitors))\nlow_idx = visitors.index(min(visitors))\n\nn = len(visitors)\nfirst_half_avg = sum(visitors[: n // 2]) / (n // 2)\nsecond_half_avg = sum(visitors[n // 2 :]) / (n // 2)\nslope = (second_half_avg - first_half_avg) / (n // 2)\ntrend_start = first_half_avg - slope * (n // 4)\ntrend = [trend_start + slope * i for i in range(n)]\n\ntitle = \"Daily Website Visitors · area-basic · python · pygal · anyplot.ai\"\ntitle_font_size = round(66 * 67 / len(title)) if len(title) > 67 else 66\n\n# Custom style — Imprint palette, theme-adaptive chrome, canonical font sizes\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_PALETTE,\n    title_font_size=title_font_size,\n    label_font_size=56,\n    major_label_font_size=44,\n    legend_font_size=40,\n    value_font_size=36,\n    stroke_width=2.5,\n    opacity=0.47,\n    opacity_hover=0.65,\n    guide_stroke_color=INK_MUTED,\n    guide_stroke_dasharray=\"3,3\",\n    major_guide_stroke_color=INK_SOFT,\n    major_guide_stroke_dasharray=\"6,3\",\n    stroke_opacity=1.0,\n    stroke_opacity_hover=1.0,\n    tooltip_border_radius=8,\n)\n\n# Area chart — cubic interpolation for smooth, visually appealing curves\nchart = pygal.Line(\n    width=3200,\n    height=1800,\n    title=title,\n    x_title=\"Day of Month\",\n    y_title=\"Visitors\",\n    style=custom_style,\n    fill=True,\n    show_dots=True,\n    dots_size=12,\n    stroke_style={\"width\": 5},\n    show_y_guides=True,\n    show_x_guides=False,\n    x_label_rotation=0,\n    show_legend=True,\n    legend_at_bottom=True,\n    legend_box_size=20,\n    value_formatter=lambda x: f\"{x:,.0f}\",\n    x_value_formatter=lambda x: f\"Day {x}\",\n    interpolate=\"cubic\",\n    interpolation_precision=250,\n    min_scale=4,\n    max_scale=8,\n    margin_bottom=120,\n    margin_left=80,\n    margin_right=40,\n    margin_top=60,\n    spacing=10,\n    tooltip_border_radius=8,\n    tooltip_fancy_mode=True,\n    show_only_major_dots=False,\n)\n\n# Main area series (brand green — Imprint palette position 1)\nchart.add(\"Daily Visitors\", visitors, fill=True, stroke_style={\"width\": 5})\n\n# Trend line — dashed, no fill (palette position 2: lavender) — secondary context\nchart.add(\n    f\"Trend (+{slope:.0f} visitors/day)\",\n    [round(t) for t in trend],\n    fill=False,\n    show_dots=False,\n    stroke_style={\"width\": 3, \"dasharray\": \"20, 12\"},\n)\n\n# Peak marker — single-value sparse series; one non-None point needs no stroke suppression\npeak_series = [None] * n\npeak_series[peak_idx] = {\n    \"value\": visitors[peak_idx],\n    \"label\": f\"Day {peak_idx + 1}: {visitors[peak_idx]:,} visitors (+{round((visitors[peak_idx] / visitors[0] - 1) * 100)}% vs Day 1)\",\n}\nchart.add(f\"Peak: {visitors[peak_idx]:,}\", peak_series, fill=False, show_dots=True, dots_size=26)\n\n# Low marker — single-value sparse series\nlow_series = [None] * n\nlow_series[low_idx] = {\n    \"value\": visitors[low_idx],\n    \"label\": f\"Day {low_idx + 1}: {visitors[low_idx]:,} visitors ({round((visitors[low_idx] / visitors[0] - 1) * 100):+d}% vs Day 1)\",\n}\nchart.add(f\"Low: {visitors[low_idx]:,}\", low_series, fill=False, show_dots=True, dots_size=26)\n\n# X-axis labels — major every 5th day for clean spacing\nchart.x_labels = [str(d) if d % 5 == 0 or d == 1 else \"\" for d in days]\n\n# Save PNG and interactive HTML\nchart.render_to_png(f\"plot-{THEME}.png\")\nwith open(f\"plot-{THEME}.html\", \"wb\") as f:\n    f.write(chart.render())\n"}