Splash & Docs Site — Implementation Plan
Goal: Ship the public marketing splash at
web/per specs/draft/splash-and-docs-site.md: a static, framework-free page that (1) explains what Tostada is in ~10s, (2) leads with community ("Join the Slack" as the loudest CTA), (3) routes one-click to GitHub, Docs, Newsletter, and the app, and (4) deploys independently of the editor. The docs tree underweb/docs/(built fromdocs/*.mdbyweb/scripts/build-docs.mjs) is already in place and only needs to run in the deploy pipeline.Architecture: Pure static HTML/CSS in
web/— no framework, no build step required to serve. The only build isbuild:docs(markdown →web/docs/*.html). The work is mostly a restructure of the existing scaffold to match the spec's section list and information hierarchy, plus three net-new sections (Manifesto, Newsletter, Footer), the Mailchimp form, social meta/OG tags, and avercel.jsondeploy config. Newsletter uses a plain<form action="…">POST (no JS) per §7. The single existing inline<script>(copy-to-clipboard) is non-critical-path.Tech Stack: HTML5 + CSS (existing
web/style.css, 1699 lines, design tokens + responsive grid already defined). Docs build: Node +marked/gray-matter/js-yaml(already inweb/package.json). Deploy: Vercel (per spec §9 Q3 + prior memory). Tests: Playwright againstnpx serve web/(thetostada/e2e/suite already exists). No new npm dependencies.
Scope check
This is one cohesive deliverable (one page + its deploy + its tests). It is not splittable into independent plans. Natural checkpoint: after Phase B the page is content-complete and locally serveable; Phases C–E are wiring (external URLs, deploy, tests) and docs. Each phase ends with the page rendering cleanly via npx serve web/ and is independently shippable.
Current state vs. spec — the gaps this plan closes
The scaffold (web/index.html + web/style.css, committed 2026-05-02) predates the spec's "community-first" pivot (spec §1 / §14 — "review session 1, paused mid-review"). Concrete deltas:
| Spec §6 expects | Scaffold today | Action |
|---|---|---|
| Hero — primary CTA "Join the Slack" (loudest), secondary "Open the app", trust line | Hero is an "Install Tostada" card (npx @jborozco/tostada + bullets + copy button); no Slack, no app link |
Restructure — demote install card, promote Slack as loudest, add "Open the app" secondary + "Open source · MIT · alpha" trust line |
| Who it's for — 2 cards (indie, AI-native) | 3 cards (solo/indie, feature squads, AI-native) | Reconcile to spec's 2 — flag for review (the 3-card version may be preferred; see Open decisions) |
| The problem — 3 pain blocks (specs-not-enforced, AI-invents-UI, hand-off-drift) | 1 narrative block + mission paragraph | Restructure into 3 blocks per §6 Section 3 |
| Manifesto (in-page anchor) | Nav links to #manifesto but no such section exists (dead anchor) |
Add Section 4 manifesto |
| What's inside — 5 cards (tokens, layout, components, doc export, rethemer) | "How it works" — 3 beats | Restructure/expand to 5 per §6 Section 5 |
| Open source section | absent | Add Section 6 |
| Community CTA — Slack (prominent) + Newsletter card | CTA exists but "Install Tostada" is primary, Slack secondary | Flip hierarchy: Slack primary/loudest |
| Newsletter — Mailchimp embedded form | absent | Add Section 7 (Mailchimp <form> POST) |
| Footer — brand/year + 3 columns + quiet copy | absent | Add Section 8 |
| OG/social meta tags | only <title> + description |
Add og:* + twitter:* + og-image.png stub |
vercel.json deploy config |
absent | Add at deploy root |
Note: the spec (§5, §6 note) explicitly authorizes restructuring the existing markup to fit the content — "copy may need to slot into the existing markup shape or trigger small restructuring." Final visual structure is reviewed during spec review.
Inputs required (external — must be supplied before the page is live-correct)
These are data, not code. The page can be built with clearly-marked placeholders, but it is not shippable until they're filled:
- Slack public invite URL — currently the scaffold uses
#slack. Needed for the hero + community CTA + footer. (Spec §8: it's just an<a href>we can refresh in one commit; monitor for expiry.) - Mailchimp form action URL + audience/list ID + hidden bot-field name — for the newsletter
<form>POST. (Free tier ≤ 500 subs per §7.) - "Open the app" / editor URL — the deployed alpha editor's public URL. Scaffold uses
#install. (Spec §9 Q6 lean: point at the live editor; copy sets a "your data lives in localStorage" expectation.) - GitHub repo URL — scaffold uses
https://github.com/jborozco/tostada. Confirm this is the public canonical URL. - Deploy root confirmation — confirm whether the git repo / Vercel project root is
tostada/(whereweb/lives) sovercel.jsonand the build command paths are correct.
Where a value is missing at build time, use a TODO(input):-commented placeholder so a single grep surfaces everything still pending.
Open decisions for spec review (carry into /spec-review)
Adopted the spec's "current lean" for each so the plan can proceed; confirm during review:
- Q1 Hero tagline — using draft "Design systems that survive the hand-off." (scaffold currently: "The open source design system orchestrator built for designers & developers."). Try ≥3 alternates in review.
- Who-it's-for card count — spec says 2; scaffold has 3. Recommend keeping the scaffold's 3 (it already distinguishes feature squads) and updating the spec, unless the 2-card framing is intentional.
- Q5 Manifesto length — 3–5 lines inline (draft in §4); longer-form deferred.
- Q6 "Open the app" — points at live editor with low-friction copy.
- Q3 Hosting — Vercel. Q4/Q7 Analytics/privacy — none for v1. Q8 OG image — wordmark on dark bg stub.
File structure
Edit:
tostada/web/index.html # restructure to spec §6 section list + content; Mailchimp form; OG meta
tostada/web/style.css # touch-ups: Slack louder than newsletter; manifesto/newsletter/footer/open-source styles
Create:
tostada/vercel.json # (or repo-root) deploy config: build:docs + outputDir web/
tostada/web/assets/og-image.png # 1200×630 stub — wordmark on dark bg
tostada/e2e/splash.spec.ts # Playwright E2E per spec §10
Confirm only (no change expected):
tostada/web/scripts/build-docs.mjs # already builds docs/*.md → web/docs/*.html
tostada/web/assets/tostada-wordmark.svg, favicon.svg # exist
Phase A — Restructure index.html to the spec's section model
Rework the single <main> so its sections and hierarchy match §6. Reuse existing classes/markup where the shape already fits; the decorative hero-grid component gallery can stay (it's aria-hidden and sells the product visually).
- A1 — Top nav (Section 0). Keep brand wordmark,
Manifestoanchor,Docs→docs/, GitHub icon. Confirm Slack is not in the nav (spec §6 §0 — it earns its big-button moment in the body). Verify mobile collapse to brand + GitHub icon. - A2 — Hero (Section 1). Set H1 (Q1 draft), sub-headline (§6 draft). Demote the install card: the loudest action becomes "Join the Slack" (primary button → invite URL, new tab,
rel="noopener"); "Open the app" as the smaller secondary button → editor URL; below, a one-line trust signal "Open source · MIT · alpha" with the GitHub icon → repo. Decide the install card's fate (move to docs / Open-source section, or keep as a tertiary detail) — review item, since the spec's hero says nothing about an npx command but the scaffold leads with it. - A3 — Who it's for (Section 2). Reconcile cards to the spec content (indie/micro-startup, AI-native), each 1 sentence + 1 detail line. See Open decisions re: card count.
- A4 — The problem (Section 3). Replace the single narrative block with three pain blocks (≤2 sentences each) using §6 Section 3 copy: "Specs that don't get enforced", "AI tools that invent UI", "Designer-developer hand-off drift".
- A5 — Manifesto (Section 4). New section with
id="manifesto"(resolves the dead nav anchor) holding the 3–5 line statement from §6 Section 4. - A6 — What's inside (Section 5). Restructure the "how it works" beats into 5 cards (Tokens, Layout principles, Component guidance, Documentation export, Rethemer), each "what it is" + "what it gives you" from §6 Section 5. No live iframe embed (non-goal §3).
- A7 — Open source (Section 6). New section: headline "Open source, MIT, alpha.", body, CTA "Read on GitHub" → repo.
- A8 — Community-first CTA (Section 7). Two cards: Slack prominent/loudest (headline, body, "Join the Slack" CTA) + Newsletter quieter (the Mailchimp form — built in Phase B).
- A9 — Footer (Section 8). New: wordmark + year; 3 columns (Product: app/docs/GitHub · Community: Slack/Newsletter/Changelog · About: Manifesto/License) collapsing to one column on mobile; quiet bottom line.
Verify A: npx serve web/ → every nav link resolves, no dead #manifesto, sections render top-to-bottom, page degrades with JS disabled.
Phase B — Newsletter (Mailchimp <form> POST)
Per §7 key-algorithm: simplest path, no JS, no script tag.
- B1 — Replace the newsletter card's placeholder with a plain
<form action="<mailchimp-url>" method="post" target="_blank">: singletype="email" requiredinput + "Subscribe" submit, plus Mailchimp's hidden anti-bot field (theb_xxxhoneypot). Nativerequiredhandles empty-submit validation (spec §10 E2E #5). - B2 — v1 accepts Mailchimp's hosted "check your inbox" page as the success state (trade in-DOM success for ship speed, §7 tradeoff). Copy the spec's state strings into nearby helper text so the intent is documented even though the success state is hosted.
Verify B: empty submit blocked by browser; valid submit POSTs to Mailchimp (once the real URL is wired).
Phase C — Social meta, assets, external URLs
- C1 — Add
<meta property="og:title|og:description|og:image|og:type|og:url">+twitter:card=summary_large_imageto<head>. Pointog:imageatassets/og-image.png; a missing image must fail silently (spec §8) — no inline broken-image fallback. - C2 — Generate the og-image.png stub (1200×630, wordmark on dark bg). Iterate post-deploy (deferred per §5).
- C3 — Wire the four external URLs (Slack, Mailchimp, app, GitHub) once supplied; until then keep
TODO(input):placeholders. Usetarget="_blank" rel="noopener"on outbound links that should open in a new tab (Slack per Flow 1).
Phase D — Deploy (Vercel) + docs in pipeline
- D1 — Add
vercel.jsonat the deploy root withoutputDirectory: "web"and a build command that runs the docs build, e.g.npm --prefix web install && npm --prefix web run build:docs(web's deps are gitignored, soinstallis needed). Confirm root per Inputs #5. - D2 — Confirm
web/docs/is produced by the build (it's gitignored —web/.gitignoreignoresdocs/but keepsdocs.css+ the script), so the deploy pipeline must regenerate it. Verify/docs/resolves to the docs index post-build. - D3 — First deploy to a generic
*.vercel.appsubdomain (custom domain deferred, §5). Capture the URL for the docs/README updates.
Verify D: a clean checkout + the build command produces web/docs/index.html; the served root shows the splash at / and docs at /docs/.
Phase E — Tests (Playwright) + Lighthouse
Add e2e/splash.spec.ts targeting npx serve web/ (per spec §10), covering:
- E1 Hero load — tagline + sub-headline visible; "Join the Slack" visible above the fold (no scroll).
- E2 Primary CTA — "Join the Slack" → invite URL, new tab.
- E3 Secondary CTAs — "Open the app" → editor URL; "Docs" →
/docs/(index renders); GitHub icon → repo. - E4 Newsletter happy path — fill email, submit, assert navigation to Mailchimp hosted confirm (URL assertion).
- E5 Newsletter validation — empty submit blocked by native
required(no POST). - E6 Anchors — "Manifesto" nav scrolls to the in-page section.
- E7 Responsive — 320px viewport: hero CTA still visible + tappable; nav collapses.
- E8 Lighthouse — mobile performance ≥ 90 and accessibility ≥ 90 on the hero view (spec goal + §10 #8).
Verify E: npm run e2e green (note: browser exec needs libnspr4/libnss3 per tostada CLAUDE.md).
Phase F — Documentation impact (§11)
- F1 — README.md: mention the public site URL once deployed.
- F2 — docs/changelog.md: "Tostada now has a public home at
. Join the Slack." (this regenerates web/docs/changelog.htmlon next build). - F3 — REBUILD_PLAN.md: "Marketing site" line under M5.5+ DONE.
- F4 — tostada/CLAUDE.md: add
web/to the module map (already partially present under "Documentation" — confirm the marketing-site line exists). - F5 — (follow-up, separate) in-app empty-state/settings link back to splash + Slack.
Risks & edge cases (from spec §8)
- JS disabled — hero/sections/nav/footer/Mailchimp form must all work; only smooth-scroll + copy-button degrade. Keep nothing critical in the inline script.
- Slack invite expiry — it's a plain
<a href>; refreshable in one commit. Monitor manually. - Google Fonts blocked region — CSS already has a
system-uifallback chain; verify it stays legible. - Docs build fails on deploy — the marketing page must still ship; verify whether Vercel serves the previous build or 404s for
/docs/(spec §8 — confirm during D1). - og-image missing — meta tag fails silently; no broken-image icon.
Definition of done
- Page matches §6 section list; "Join the Slack" is the loudest CTA above the fold.
npx serve web/renders cleanly desktop + 320px; JS-disabled still usable.- Docs resolve at
/docs/after the pipeline runs the docs build. - All four external URLs wired (no remaining
TODO(input):). - Deployed to a
*.vercel.appURL; rollback = one-click revert (§13). npm run e2e(splash suite) green; Lighthouse mobile perf ≥ 90, a11y ≥ 90.- Docs impact items (F1–F4) done.