The map is a static site with no build step, served directly from GitHub Pages. There is no bundler, no npm, and no server-side rendering. Optional backend features — click tracking, analytics, and restaurant hours — use Cloudflare and Google Cloud services, all on free tiers. Every integration degrades gracefully: removing or nulling out a config key simply disables that feature without breaking anything else.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/samgutentag/sbburgerweek/llms.txt
Use this file to discover all available pages before exploring further.
The entire static site works without the Worker, Google Places API, or GitHub Actions. Set
THEME.trackUrl and THEME.cfAnalyticsToken to null in config.js and the map functions as a fully offline-capable, read-only restaurant directory.Static site layer
The core of the project is a handful of plain files served from the repo root:| File | Role |
|---|---|
index.html | Page shell — declares the OG tags, favicon, analytics snippet, and loads all scripts |
app.js | All map logic: Leaflet setup, marker rendering, sidebar, search, filtering, deep linking, popups, modals |
style.css | All styles including the responsive mobile drawer layout |
config.js | Exports the THEME object — single source of truth for event identity, dates, integrations, and map defaults |
data-YYYY.js | Production restaurant data with full menu items (e.g. data-2026.js) |
data.js | Skeleton restaurant data with empty menuItems arrays — shown before dataLiveDate |
track.js | Lightweight event tracker — defines window.track(action, label) using navigator.sendBeacon |
index.html decides at runtime which data file to load based on THEME.dataLiveDate: before that date it loads data.js (skeleton); on or after it loads data-YYYY.js (full data). Add ?year=9999 to the URL during development to force the skeleton view.
The Leaflet map and MarkerCluster plugin are loaded from CDN. There is no local copy of any dependency.
Embed and stats pages
Two additional subdirectories extend the main site:/embed/map — A compact, embeddable version of the map intended for iframing on local news sites or blogs. It shares config.js and the data files (via ../../ relative paths) but has its own independent embed.js and embed.css. Changes to app.js or style.css do not propagate to the embed. Key differences:
| Main site | Embed | |
|---|---|---|
| Sidebar width | 360px | 280px |
| Mobile breakpoint | 768px | 600px |
| JS | app.js | embed/map/embed.js |
| CSS | style.css | embed/map/embed.css |
| Header | Full nav header | Compact bar with “Open full map” link |
/embed — A showcase page with copy-paste iframe instructions for publishers.
/stats — The live engagement dashboard. stats/stats.js queries the Worker’s GET endpoints (GET /, GET /?hourly=true, GET /?upvotes=true, GET /?rum=true) and renders a leaderboard ranked by engagement score, a trends chart, and device/browser breakdowns. After the event concludes, the stats page falls back to pre-committed snapshot files in snapshots/ instead of querying the Worker.
Cloudflare Worker
The Worker lives inworkers/track/index.js and is deployed with Wrangler. It serves two roles:
-
Event ingestion (POST /) — Receives JSON payloads from
track.js(viasendBeacon) and writes them to the Cloudflare Analytics Engine dataset (sbburgerweek). Each data point stores theactioninblob1and thelabelinblob2. -
Stats queries (GET /) — Runs SQL queries against Analytics Engine and returns aggregated results used by the
/statspage. The full set of GET endpoints is documented in Worker API.
wrangler.toml declares the Analytics Engine dataset binding (TRACKER) and two plain [vars]: ACCOUNT_ID and RUM_SITE_TAG. Secrets (CF_API_TOKEN, ADMIN_TOKEN) are set separately via wrangler secret put and never appear in the committed config file.
ACCOUNT_ID with your own Cloudflare account ID and rename the dataset to match your new event.
GitHub Actions
Two workflows in.github/workflows/ handle automated data collection during the event:
fetch-hours.yml — Runs on a daily cron schedule during event week. Calls the Google Places API (fields=opening_hours) for each restaurant using place-ids.json as input, then auto-commits the updated hours.json to the repo if anything changed. The map loads hours.json at runtime to show today’s hours and open/closed badges. The schedule is commented out in the repo by default — uncomment it and update the date range before your event.
snapshot-tracking.yml — Runs hourly during event week. Queries the Cloudflare Analytics Engine SQL API directly using CF_ACCOUNT_ID and CF_API_TOKEN GitHub Secrets, and commits a JSON snapshot to snapshots/tracking-YYYY-MM-DD.json. This preserves engagement data beyond Analytics Engine’s 90-day retention window. Like fetch-hours.yml, the cron schedule is commented out by default.
Both workflows also support manual dispatch from the GitHub Actions tab, which is useful for testing before the event and for running final snapshots on wind-down day.
CDN dependencies
The project loads all JavaScript and map tile dependencies from public CDNs — there is no local copy of any library and no build step:| Dependency | Source | Purpose |
|---|---|---|
| Leaflet | CDN | Interactive map rendering, markers, popups, zoom controls |
| Leaflet.markercluster | CDN | Clusters nearby markers at low zoom levels |
| CARTO basemap tiles | {s}.basemaps.cartocdn.com | Light-gray map tile layer (no API key required) |