{"spec_id":"timeline-basic","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\ntimeline-basic: Event Timeline\nLibrary: seaborn 0.13.2 | Python 3.13.13\nQuality: 92/100 | Updated: 2026-05-11\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport pandas as pd\nimport seaborn as sns\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\"\n\n# Okabe-Ito palette (canonical order)\nIMPRINT = [\n    \"#009E73\",  # brand green\n    \"#C475FD\",  # vermillion\n    \"#4467A3\",  # blue\n    \"#BD8233\",  # reddish purple\n    \"#AE3030\",  # orange\n]\n\n# Data - Software project milestones\nevents = [\n    (\"2024-01-15\", \"Project Kickoff\", \"Planning\"),\n    (\"2024-02-20\", \"Requirements Done\", \"Planning\"),\n    (\"2024-04-01\", \"Architecture Design\", \"Design\"),\n    (\"2024-05-15\", \"UI Mockups\", \"Design\"),\n    (\"2024-07-01\", \"Backend MVP\", \"Development\"),\n    (\"2024-08-15\", \"Frontend MVP\", \"Development\"),\n    (\"2024-10-01\", \"Alpha Release\", \"Testing\"),\n    (\"2024-11-15\", \"Beta Testing\", \"Testing\"),\n    (\"2025-01-10\", \"Go Live\", \"Deployment\"),\n]\n\ndf = pd.DataFrame(events, columns=[\"date\", \"event\", \"category\"])\ndf[\"date\"] = pd.to_datetime(df[\"date\"])\n\n# Create y-offset for alternating labels (above/below axis)\ndf[\"y_offset\"] = [1 if i % 2 == 0 else -1 for i in range(len(df))]\n\n# Map categories to Okabe-Ito colors\ncategory_order = [\"Planning\", \"Design\", \"Development\", \"Testing\", \"Deployment\"]\npalette = {\n    \"Planning\": IMPRINT[0],  # green\n    \"Design\": IMPRINT[1],  # vermillion\n    \"Development\": IMPRINT[2],  # blue\n    \"Testing\": IMPRINT[3],  # reddish purple\n    \"Deployment\": IMPRINT[4],  # orange\n}\n\n# Set seaborn theme with theme-adaptive tokens\nsns.set_theme(\n    style=\"ticks\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": INK_SOFT,\n        \"axes.labelcolor\": INK,\n        \"text.color\": INK,\n        \"xtick.color\": INK_SOFT,\n        \"ytick.color\": INK_SOFT,\n        \"grid.color\": INK,\n        \"grid.alpha\": 0.10,\n        \"legend.facecolor\": PAGE_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\n# Create figure\nfig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Draw the main timeline axis\nax.axhline(y=0, color=INK_SOFT, linewidth=3, zorder=1)\n\n# Plot events using seaborn scatterplot\nsns.scatterplot(\n    data=df,\n    x=\"date\",\n    y=[0] * len(df),\n    hue=\"category\",\n    hue_order=category_order,\n    palette=palette,\n    s=500,\n    zorder=3,\n    ax=ax,\n    legend=True,\n    edgecolor=PAGE_BG,\n    linewidth=2,\n)\n\n# Add vertical connector lines and event labels\nfor _idx, row in df.iterrows():\n    y_end = row[\"y_offset\"] * 0.55\n\n    # Connector line\n    ax.plot([row[\"date\"], row[\"date\"]], [0, y_end], color=palette[row[\"category\"]], linewidth=2.5, zorder=2)\n\n    # Event label\n    va = \"bottom\" if row[\"y_offset\"] > 0 else \"top\"\n    ax.annotate(\n        row[\"event\"],\n        xy=(row[\"date\"], y_end),\n        ha=\"center\",\n        va=va,\n        fontsize=15,\n        fontweight=\"bold\",\n        color=INK,\n        xytext=(0, 10 * row[\"y_offset\"]),\n        textcoords=\"offset points\",\n    )\n\n# Style the plot\nax.set_xlim(df[\"date\"].min() - pd.Timedelta(days=40), df[\"date\"].max() + pd.Timedelta(days=60))\nax.set_ylim(-1.1, 1.1)\n\n# Remove y-axis and spines for clean timeline look\nax.set_yticks([])\nax.set_ylabel(\"\")\nax.set_xlabel(\"\")\nax.spines[\"left\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\nax.spines[\"top\"].set_visible(False)\nax.spines[\"bottom\"].set_visible(False)\n\n# Format x-axis with monthly ticks\nax.tick_params(axis=\"x\", labelsize=16, length=0, colors=INK_SOFT)\nax.xaxis.set_major_locator(plt.matplotlib.dates.MonthLocator(interval=2))\nax.xaxis.set_major_formatter(plt.matplotlib.dates.DateFormatter(\"%b %Y\"))\nplt.setp(ax.get_xticklabels(), rotation=45, ha=\"right\", color=INK_SOFT)\n\n# Title and legend\nax.set_title(\"timeline-basic · seaborn · anyplot.ai\", fontsize=24, fontweight=\"medium\", color=INK, pad=20)\n\n# Place legend at bottom center, horizontal layout\nax.legend(\n    title=\"Phase\",\n    title_fontsize=16,\n    fontsize=14,\n    loc=\"lower center\",\n    ncol=5,\n    framealpha=0.9,\n    edgecolor=INK_SOFT,\n    facecolor=PAGE_BG,\n    bbox_to_anchor=(0.5, -0.15),\n)\n\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=300, bbox_inches=\"tight\", facecolor=PAGE_BG)\n"}