{"spec_id":"area-basic","library":"bokeh","language":"python","code":"\"\"\" anyplot.ai\narea-basic: Basic Area Chart\nLibrary: bokeh 3.9.0 | Python 3.13.13\nQuality: 92/100 | Updated: 2026-05-28\n\"\"\"\n\nimport io\nimport os\nimport sys\nimport time\nfrom pathlib import Path\n\n\n# Prevent this file's directory from shadowing the installed bokeh package\nsys.path = [p for p in sys.path if os.path.abspath(p) != os.path.dirname(os.path.abspath(__file__))]\n\nimport numpy as np\nimport pandas as pd\nfrom bokeh.io import output_file, save\nfrom bokeh.models import ColumnDataSource, HoverTool, Label\nfrom bokeh.plotting import figure\nfrom PIL import Image\nfrom selenium import webdriver\nfrom selenium.webdriver.chrome.options import Options\n\n\n# Theme tokens\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\"\nBRAND = \"#009E73\"  # Imprint palette position 1 — always first series\n\n# Title with length-aware font scaling\ntitle_str = \"Daily Website Traffic · area-basic · python · bokeh · anyplot.ai\"\nn = len(title_str)\nratio = 67 / n if n > 67 else 1.0\ntitle_fontsize = f\"{max(34, round(50 * ratio))}pt\"\n\n# Data — tech blog visitors, March 2024\nnp.random.seed(42)\ndates = pd.date_range(start=\"2024-03-01\", periods=31, freq=\"D\")\nbase_visitors = 4200\ntrend = np.linspace(0, 800, 31)\nweekly_pattern = 600 * np.sin(np.arange(31) * 2 * np.pi / 7 - 1.2)\nnoise = np.random.randn(31) * 300\nvisitors = base_visitors + trend + weekly_pattern + noise\nvisitors = np.maximum(visitors, 1800)\n\n# Traffic dip: scheduled maintenance on day 8–9\nvisitors[7] = 2100\nvisitors[8] = 2400\n\n# Viral surge: tutorial hits Hacker News front page on day 22\nvisitors[21] = 11400\nvisitors[22] = 9900\nvisitors[23] = 7700\n\nsource = ColumnDataSource(data={\"date\": dates, \"visitors\": visitors})\n\n# Figure — 3200×1800 landscape\np = figure(\n    width=3200,\n    height=1800,\n    title=title_str,\n    x_axis_label=\"Date (March 2024)\",\n    y_axis_label=\"Daily Visitors (count)\",\n    x_axis_type=\"datetime\",\n    toolbar_location=None,\n    min_border_bottom=160,\n    min_border_left=180,\n    min_border_top=110,\n    min_border_right=50,\n)\n\n# Area fill + edge line\np.varea(x=\"date\", y1=0, y2=\"visitors\", source=source, fill_color=BRAND, fill_alpha=0.35)\np.line(x=\"date\", y=\"visitors\", source=source, line_color=BRAND, line_width=4)\n\n# Invisible scatter for hover hit targets\np.scatter(x=\"date\", y=\"visitors\", source=source, size=18, fill_alpha=0, line_alpha=0)\n\n# HoverTool with datetime formatter\nhover = HoverTool(\n    tooltips=[(\"Date\", \"@date{%b %d, %Y}\"), (\"Visitors\", \"@visitors{0,0}\")],\n    formatters={\"@date\": \"datetime\"},\n    mode=\"vline\",\n)\np.add_tools(hover)\n\n# Annotation — viral surge event\nsurge_label = Label(\n    x=dates[21],\n    y=11700,\n    text=\"HN front page  +172%\",\n    text_font_size=\"28pt\",\n    text_color=INK,\n    text_font_style=\"bold\",\n    x_offset=15,\n    y_offset=0,\n)\np.add_layout(surge_label)\n\n# Text sizing — canonical bokeh values for 3200×1800\np.title.text_font_size = title_fontsize\np.xaxis.axis_label_text_font_size = \"42pt\"\np.yaxis.axis_label_text_font_size = \"42pt\"\np.xaxis.major_label_text_font_size = \"34pt\"\np.yaxis.major_label_text_font_size = \"34pt\"\n\n# Theme-adaptive chrome\np.background_fill_color = PAGE_BG\np.border_fill_color = PAGE_BG\np.outline_line_color = INK_SOFT\n\np.title.text_color = INK\np.xaxis.axis_label_text_color = INK\np.yaxis.axis_label_text_color = INK\np.xaxis.major_label_text_color = INK_SOFT\np.yaxis.major_label_text_color = INK_SOFT\np.xaxis.axis_line_color = INK_SOFT\np.yaxis.axis_line_color = INK_SOFT\np.xaxis.major_tick_line_color = INK_SOFT\np.yaxis.major_tick_line_color = INK_SOFT\n\n# Grid — subtle, y-axis only for area chart\np.xgrid.grid_line_color = None\np.ygrid.grid_line_color = INK\np.ygrid.grid_line_alpha = 0.15\n\n# Y range with tight headroom for annotation\np.y_range.start = 0\np.y_range.end = 12800\n\n# Save HTML (interactive catalog artifact)\noutput_file(f\"plot-{THEME}.html\")\nsave(p)\n\n# Screenshot with headless Chrome (Selenium 4 / Selenium Manager).\n# Chrome's internal UI overhead shrinks the viewport below --window-size by ~139 px.\n# Use a taller window (H + 200 buffer) so the viewport is >= H, then crop to exact dims.\nW, H = 3200, 1800\nopts = Options()\nfor arg in (\n    \"--headless=new\",\n    \"--no-sandbox\",\n    \"--disable-dev-shm-usage\",\n    \"--disable-gpu\",\n    f\"--window-size={W},{H + 200}\",\n    \"--hide-scrollbars\",\n    \"--force-device-scale-factor=1\",\n):\n    opts.add_argument(arg)\ndriver = webdriver.Chrome(options=opts)\ndriver.set_window_size(W, H + 200)\ndriver.get(f\"file://{Path(f'plot-{THEME}.html').resolve()}\")\ntime.sleep(3)\nraw = driver.get_screenshot_as_png()\ndriver.quit()\nimg = Image.open(io.BytesIO(raw)).crop((0, 0, W, H))\nimg.save(f\"plot-{THEME}.png\")\n"}