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 under web/docs/ (built from docs/*.md by web/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 is build: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 a vercel.json deploy 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 in web/package.json). Deploy: Vercel (per spec §9 Q3 + prior memory). Tests: Playwright against npx serve web/ (the tostada/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:

  1. 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.)
  2. Mailchimp form action URL + audience/list ID + hidden bot-field name — for the newsletter <form> POST. (Free tier ≤ 500 subs per §7.)
  3. "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.)
  4. GitHub repo URL — scaffold uses https://github.com/jborozco/tostada. Confirm this is the public canonical URL.
  5. Deploy root confirmation — confirm whether the git repo / Vercel project root is tostada/ (where web/ lives) so vercel.json and 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, Manifesto anchor, Docsdocs/, 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">: single type="email" required input + "Subscribe" submit, plus Mailchimp's hidden anti-bot field (the b_xxx honeypot). Native required handles 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_image to <head>. Point og:image at assets/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. Use target="_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.json at the deploy root with outputDirectory: "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, so install is needed). Confirm root per Inputs #5.
  • D2 — Confirm web/docs/ is produced by the build (it's gitignored — web/.gitignore ignores docs/ but keeps docs.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.app subdomain (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)

  • F1README.md: mention the public site URL once deployed.
  • F2docs/changelog.md: "Tostada now has a public home at . Join the Slack." (this regenerates web/docs/changelog.html on next build).
  • F3REBUILD_PLAN.md: "Marketing site" line under M5.5+ DONE.
  • F4tostada/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-ui fallback 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.app URL; rollback = one-click revert (§13).
  • npm run e2e (splash suite) green; Lighthouse mobile perf ≥ 90, a11y ≥ 90.
  • Docs impact items (F1–F4) done.