{"spec_id":"sparkline-basic","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nsparkline-basic: Basic Sparkline\nLibrary: matplotlib 3.11.0 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-06-16\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\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\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\n# Imprint palette — single hue for every sparkline (one metric \"family\")\nBRAND = \"#009E73\"  # Imprint position 1 — the line, ALWAYS first series\nLOW = \"#AE3030\"  # Imprint position 5 — marks each series minimum\nHIGH = \"#4467A3\"  # Imprint position 3 — marks each series maximum\n\n# Data — a KPI dashboard of small-multiple sparklines (60-day trends).\n# Sparklines shine as small multiples in table/dashboard cells (see spec\n# \"Applications\"): each row is one metric, drawn axis-free and compact.\nnp.random.seed(42)\nn_points = 60\nx = np.arange(n_points)\n\n# Each metric: a distinct trend shape, its current-value format, and unit.\nvisitors = 1200 + 16 * x + 130 * np.sin(x / 3.0) + np.random.randn(n_points) * 55\nrevenue = 38 + 0.55 * x + 6 * np.sin(x / 5.0 + 1) + np.random.randn(n_points) * 2.2\nconversion = 2.0 + 1.4 * (x / n_points) ** 1.5 + np.random.randn(n_points) * 0.12\nactive = 1500 - 9 * x + 180 * np.sin(x / 4.0) + np.random.randn(n_points) * 70\nsession = 4.6 + 1.2 * np.sin(x / 8.0) + np.random.randn(n_points) * 0.25\nsignups = 30 + 80 * (x / n_points) ** 2 + 14 * np.sin(x / 2.5) + np.random.randn(n_points) * 6\n\nmetrics = [\n    (\"Website Visitors\", visitors, \"{:,.0f}\"),\n    (\"Daily Revenue\", revenue, \"${:.1f}k\"),\n    (\"Conversion Rate\", conversion, \"{:.2f}%\"),\n    (\"Active Users\", active, \"{:,.0f}\"),\n    (\"Avg. Session\", session, \"{:.1f} min\"),\n    (\"Newsletter Signups\", signups, \"{:,.0f}\"),\n]\n\n# Plot — one slim, chrome-free axes per metric, stacked vertically\nfig, axes = plt.subplots(len(metrics), 1, figsize=(8, 4.5), dpi=400, facecolor=PAGE_BG)\nfig.subplots_adjust(left=0.20, right=0.86, top=0.86, bottom=0.05, hspace=0.7)\n\nfor ax, (name, values, fmt) in zip(axes, metrics, strict=True):\n    ax.set_facecolor(PAGE_BG)\n\n    # Thin line + faint area fill — the defining sparkline look\n    ax.plot(x, values, color=BRAND, linewidth=1.6, solid_capstyle=\"round\")\n    ax.fill_between(x, values, values.min(), color=BRAND, alpha=0.10)\n\n    # Highlight the extremes and the latest point\n    i_min, i_max = int(values.argmin()), int(values.argmax())\n    ax.scatter(i_min, values[i_min], s=22, color=LOW, zorder=5)\n    ax.scatter(i_max, values[i_max], s=22, color=HIGH, zorder=5)\n    ax.scatter(x[-1], values[-1], s=28, color=BRAND, zorder=6)\n\n    # Strip all chart chrome — pure sparkline\n    ax.set_xticks([])\n    ax.set_yticks([])\n    for spine in ax.spines.values():\n        spine.set_visible(False)\n\n    # Breathing room so the line never touches the cell edges\n    pad = (values.max() - values.min()) * 0.28\n    ax.set_ylim(values.min() - pad, values.max() + pad)\n    ax.set_xlim(-1.5, n_points + 0.5)\n\n    # Metric name (left) and current value (right), table-cell style\n    ax.text(-0.025, 0.5, name, transform=ax.transAxes, ha=\"right\", va=\"center\", fontsize=9, color=INK_SOFT)\n    ax.text(\n        1.02,\n        0.5,\n        fmt.format(values[-1]),\n        transform=ax.transAxes,\n        ha=\"left\",\n        va=\"center\",\n        fontsize=10,\n        fontweight=\"medium\",\n        color=INK,\n    )\n\n# Title (mandated format)\nfig.suptitle(\"sparkline-basic · python · matplotlib · anyplot.ai\", fontsize=13, fontweight=\"medium\", color=INK, y=0.96)\n\n# Save (figsize 8x4.5 @ dpi 400 → 3200x1800; no bbox_inches — see library prompt)\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}