{"spec_id":"timeline-basic","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\ntimeline-basic: Event Timeline\nLibrary: matplotlib 3.10.9 | Python 3.13.13\nQuality: 94/100 | Updated: 2026-05-11\n\"\"\"\n\nimport os\n\nimport matplotlib.dates as mdates\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\n\n\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\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\"]\n\nnp.random.seed(42)\nevents = pd.DataFrame(\n    {\n        \"date\": pd.to_datetime(\n            [\n                \"2024-01-08\",\n                \"2024-02-25\",\n                \"2024-04-20\",\n                \"2024-05-30\",\n                \"2024-07-15\",\n                \"2024-09-10\",\n                \"2024-10-20\",\n                \"2024-12-15\",\n            ]\n        ),\n        \"event\": [\n            \"Project Kickoff\",\n            \"Requirements Done\",\n            \"Alpha Release\",\n            \"Beta Launch\",\n            \"User Testing\",\n            \"Bug Fixes\",\n            \"Final Review\",\n            \"Go Live\",\n        ],\n        \"category\": [\"Planning\", \"Planning\", \"Development\", \"Development\", \"Testing\", \"Testing\", \"Release\", \"Release\"],\n    }\n)\n\ncategory_colors = {\n    \"Planning\": IMPRINT[0],\n    \"Development\": IMPRINT[1],\n    \"Testing\": IMPRINT[2],\n    \"Release\": IMPRINT[3],\n}\ncolors = [category_colors[cat] for cat in events[\"category\"]]\n\nfig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\nax.axhline(y=0, color=INK_SOFT, linewidth=2, zorder=1, alpha=0.4)\n\nlevels = np.tile([1, -1], len(events) // 2 + 1)[: len(events)]\nlevel_heights = levels * 0.3\n\nfor date, height, color in zip(events[\"date\"], level_heights, colors, strict=True):\n    ax.plot([date, date], [0, height], color=color, linewidth=2.5, zorder=2, alpha=0.8)\n\nax.scatter(events[\"date\"], level_heights, c=colors, s=300, zorder=3, edgecolors=PAGE_BG, linewidths=1.5)\n\nfor date, event, height, color in zip(events[\"date\"], events[\"event\"], level_heights, colors, strict=True):\n    va = \"bottom\" if height > 0 else \"top\"\n    offset = 0.08 if height > 0 else -0.08\n    ax.annotate(\n        event,\n        xy=(date, height),\n        xytext=(0, offset * 300),\n        textcoords=\"offset points\",\n        ha=\"center\",\n        va=va,\n        fontsize=14,\n        fontweight=\"bold\",\n        color=INK,\n        bbox={\n            \"boxstyle\": \"round,pad=0.3\",\n            \"facecolor\": ELEVATED_BG,\n            \"edgecolor\": color,\n            \"linewidth\": 1.5,\n            \"alpha\": 0.95,\n        },\n    )\n\nfor date, height in zip(events[\"date\"], level_heights, strict=True):\n    ax.annotate(\n        date.strftime(\"%b %d\"),\n        xy=(date, 0),\n        xytext=(0, -20 if height > 0 else 20),\n        textcoords=\"offset points\",\n        ha=\"center\",\n        va=\"top\" if height > 0 else \"bottom\",\n        fontsize=12,\n        color=INK_SOFT,\n    )\n\nlegend_handles = [\n    plt.scatter([], [], c=color, s=200, label=cat, edgecolors=PAGE_BG, linewidths=1.5)\n    for cat, color in category_colors.items()\n]\nleg = ax.legend(\n    handles=legend_handles, loc=\"upper right\", fontsize=14, title=\"Project Phase\", title_fontsize=16, frameon=True\n)\nif leg:\n    leg.get_frame().set_facecolor(ELEVATED_BG)\n    leg.get_frame().set_edgecolor(INK_SOFT)\n    leg.get_frame().set_linewidth(0.8)\n    plt.setp(leg.get_texts(), color=INK_SOFT)\n    leg.get_title().set_color(INK)\n\nax.set_xlim(events[\"date\"].min() - pd.Timedelta(days=40), events[\"date\"].max() + pd.Timedelta(days=40))\nax.set_ylim(-0.6, 0.6)\n\nax.xaxis.set_major_locator(mdates.MonthLocator())\nax.xaxis.set_major_formatter(mdates.DateFormatter(\"%b\\n%Y\"))\nax.tick_params(axis=\"x\", labelsize=14, colors=INK_SOFT, labelcolor=INK_SOFT)\n\nax.yaxis.set_visible(False)\nax.spines[\"left\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\nax.spines[\"top\"].set_visible(False)\nax.spines[\"bottom\"].set_color(INK_SOFT)\nax.spines[\"bottom\"].set_linewidth(0.8)\n\nax.set_title(\"timeline-basic · matplotlib · anyplot.ai\", fontsize=24, fontweight=\"medium\", color=INK, pad=20)\n\nax.set_xlabel(\"Project Timeline\", fontsize=20, color=INK, labelpad=10)\n\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=300, bbox_inches=\"tight\", facecolor=PAGE_BG)\n"}