From 8762e92a32a09795e45af898fc0524829ca69f6c Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Thu, 1 Oct 2026 15:56:48 +0200 Subject: [PATCH] docs(11.2-01): complete the summercms.io Nuxt landing page plan --- .../11.2-01-SUMMARY.md | 265 ++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 .planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-01-SUMMARY.md diff --git a/.planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-01-SUMMARY.md b/.planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-01-SUMMARY.md new file mode 100644 index 0000000..d10c5b6 --- /dev/null +++ b/.planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-01-SUMMARY.md @@ -0,0 +1,265 @@ +--- +phase: 11.2-ready-to-share-summercms-io-website-and-newsletter-plugin +plan: 01 +subsystem: ui +tags: [nuxt4, vue, nuxt-i18n, nuxt-seo, nuxt-fonts, ssg, node-test, imagemagick] + +requires: + - phase: 11.1-summercms-documentation-for-humans-and-ai-agents + provides: the /docs pages the landing page links to (served by the Go binary, not by Nuxt) +provides: + - "vue-summercms-app repository (master, no remote) generating the summercms.io landing page with nuxt generate" + - ".output/public contract for plan 11.2-02: index.html, 200.html, 404.html, _nuxt/, _fonts/, _i18n//en/messages.json, _payload.json, flat sitemap.xml, robots.txt, og-image.png, favicons, sun images" + - "app/data/terminal.json: the single source of the six Get started commands (groups[].comment i18n key, groups[].commands)" + - "pure utilities copyPayload (app/utils/terminal.ts) and activeSection/SPY_SECTIONS/SPY_THRESHOLD (app/utils/scrollSpy.ts)" +affects: [11.2-02 site plugin embed and link check, 11.2-02 D-40 terminal command check, 11.2-03 unit tests, 11.3 Polish locale] + +actuals: + tokens: 13246 # chars/4 over the committed text files (pnpm-lock.yaml and images excluded; lockfile alone is ~97900) + tasks: 3 + commits: 4 # MEASURED in vue-summercms-app: git rev-list --count master (repository created by this plan) +plan_head_before: null # vue-summercms-app did not exist before this plan; summercms.go HEAD was 88f21af923e0f4efff6a72437977f636e48923c7 and gained no code commits +plan_head_after: 17e9f923559f6c2a9f176bdb5ae3aa78af934d9f # vue-summercms-app master +commits_repo: /media/nvme/dev/golem15/summercms.io/summercms/sm-summercms-app/vue-summercms-app + +tech-stack: + added: ["nuxt 4.4.8", "@nuxt/fonts 0.14.0", "@nuxtjs/i18n 10.4.0", "@nuxtjs/seo 5.2.1", "vue 3.5.35", "typescript 5.9.3 (dev)"] + patterns: + - "Plain CSS: tokens as :root custom properties (tokens.css), shared shapes in base.css, scoped component styles" + - "All copy in i18n/locales/en.json; inline code inside copy via named slots, never raw HTML" + - "Width-dependent UI via CSS media queries only, so the server render never depends on the viewport" + - "Pure, erasable-TypeScript utilities under app/utils/ run directly by node --test with .ts specifiers" + - "Output tests read .output/public and en.json, resolving paths from import.meta.url" + +key-files: + created: + - ../sm-summercms-app/vue-summercms-app/nuxt.config.ts + - ../sm-summercms-app/vue-summercms-app/app/app.config.ts + - ../sm-summercms-app/vue-summercms-app/app/pages/index.vue + - ../sm-summercms-app/vue-summercms-app/app/components/SiteHeader.vue + - ../sm-summercms-app/vue-summercms-app/app/components/HeroSection.vue + - ../sm-summercms-app/vue-summercms-app/app/components/WhySection.vue + - ../sm-summercms-app/vue-summercms-app/app/components/FeaturesSection.vue + - ../sm-summercms-app/vue-summercms-app/app/components/WinterSection.vue + - ../sm-summercms-app/vue-summercms-app/app/components/StartSection.vue + - ../sm-summercms-app/vue-summercms-app/app/components/TerminalCard.vue + - ../sm-summercms-app/vue-summercms-app/app/components/SiteFooter.vue + - ../sm-summercms-app/vue-summercms-app/app/assets/css/tokens.css + - ../sm-summercms-app/vue-summercms-app/app/assets/css/base.css + - ../sm-summercms-app/vue-summercms-app/app/data/terminal.json + - ../sm-summercms-app/vue-summercms-app/app/utils/terminal.ts + - ../sm-summercms-app/vue-summercms-app/app/utils/scrollSpy.ts + - ../sm-summercms-app/vue-summercms-app/i18n/locales/en.json + - ../sm-summercms-app/vue-summercms-app/scripts/derive-images.sh + - ../sm-summercms-app/vue-summercms-app/assets-src/logo.png + - ../sm-summercms-app/vue-summercms-app/public/og-image.png + - ../sm-summercms-app/vue-summercms-app/tests/output.test.ts + - ../sm-summercms-app/vue-summercms-app/tests/terminal.test.ts + - ../sm-summercms-app/vue-summercms-app/tests/scrollSpy.test.ts + modified: [] + +key-decisions: + - "Page title is 'SummerCMS: A new dawn in content management' with titleTemplate '%s' so the site name is not appended twice; description is the hero paragraph" + - "Header sun is the 30px original scaled 1.12 without the handoff mask (the mask only cropped the screenshot placeholder); hero badge shows the sun at 136px" + - "Header at phone widths: at 720px and below the empty links wrapper is hidden and Docs is pushed right; at 420px and below the row gap is 10px and side padding 16px, because logo + pill + Docs overflowed 375px with the handoff spacing" + - "Copy falls back to a hidden textarea + execCommand('copy') when navigator.clipboard is missing or rejects; a failed copy leaves the label unchanged" + - "Footer link group may wrap (flex-wrap) so the four links never overflow a phone width" + +patterns-established: + - "en.json leaf check: every message leaf (minus interaction-only keys) must appear HTML-escaped in index.html" + - "terminal.json comment keys resolve in en.json; the output test reads commands from terminal.json, never a second list" + +requirements-completed: [] + +coverage: + - id: D1 + description: "nuxt generate prerenders index.html with header, hero #top, #why, #features, #winter, #start and the footer in handoff order" + verification: + - kind: other + ref: "pnpm generate (exit 0, no prerender errors)" + status: pass + - kind: automated_ui + ref: "tests/output.test.ts#sections appear in handoff order: top, why, features, winter, start, then the footer" + status: pass + human_judgment: false + - id: D2 + description: "Every visible string comes from i18n/locales/en.json with the single en locale at /" + verification: + - kind: automated_ui + ref: "tests/output.test.ts#every en.json string is rendered on the page" + status: pass + - kind: automated_ui + ref: "tests/output.test.ts#i18n messages are emitted for the client under /_i18n" + status: pass + human_judgment: false + - id: D3 + description: "Roboto and Roboto Mono self-hosted under /_fonts with no Google font host in generated HTML or CSS" + verification: + - kind: automated_ui + ref: "tests/output.test.ts#fonts are self-hosted under /_fonts" + status: pass + - kind: automated_ui + ref: "tests/output.test.ts#no generated HTML or CSS references a Google font host" + status: pass + human_judgment: false + - id: D4 + description: "Static SEO: canonical, OG (absolute og:image), Twitter, schema.org JSON-LD, flat sitemap.xml and robots.txt" + verification: + - kind: automated_ui + ref: "tests/output.test.ts#the page head carries canonical, Open Graph, Twitter and schema.org tags" + status: pass + - kind: automated_ui + ref: "tests/output.test.ts#robots.txt points at the flat sitemap.xml" + status: pass + human_judgment: false + - id: D5 + description: "All docs, source and credit links: eight extension-less tiles, concept map, installation guide, /docs, Source x2, golem15.com" + verification: + - kind: automated_ui + ref: "tests/output.test.ts#feature tiles and the concept map link to the docs, extension-less" + status: pass + - kind: automated_ui + ref: "tests/output.test.ts#the footer links to the source and credits Golem15" + status: pass + - kind: automated_ui + ref: "tests/output.test.ts#Get started shows the chips, the six commands, their comments and the links" + status: pass + human_judgment: false + - id: D6 + description: "Terminal card renders the six terminal.json commands; Copy writes exactly those six lines" + verification: + - kind: unit + ref: "tests/terminal.test.ts#copyPayload joins the six commands in order" + status: pass + - kind: unit + ref: "tests/terminal.test.ts#every terminal.json comment key resolves to a string in en.json" + status: pass + human_judgment: false + - id: D7 + description: "Scroll-spy selection: last of why/features/winter/start with top < 140, none above #why" + verification: + - kind: unit + ref: "tests/scrollSpy.test.ts#the last section above the threshold wins" + status: pass + - kind: unit + ref: "tests/scrollSpy.test.ts#a top of exactly the threshold is not active" + status: pass + human_judgment: false + - id: D8 + description: "Visual fidelity at 1280/721/720/375px, sticky header while scrolling, live scroll-spy highlight, Copy -> Copied for 1.5s and clipboard contents, hero rays and sun badge vs the handoff" + verification: [] + human_judgment: true + rationale: "Pixel and interaction fidelity in a real browser is the end-of-phase UAT backstop (plan must_haves); headless screenshots at 1280/375/360/720/721 were only a sanity check" + +duration: 12min +completed: 2026-10-01 +status: complete +--- + +# Phase 11.2 Plan 01: summercms.io Nuxt landing page Summary + +**Nuxt 4 static landing page in the new `vue-summercms-app` repo. It has self-hosted Roboto, a single `en` locale holding every string, static SEO and sitemap output, the original sun in every image, and a Get started terminal card driven by one `terminal.json`. It is verified by 29 node:test assertions over the utilities and the generated `.output/public`.** + +## Performance + +- **Duration:** 12 min +- **Started:** 2026-10-01T13:43:15Z +- **Completed:** 2026-10-01T13:55:16Z +- **Tasks:** 3 (tracer + auto + TDD) +- **Files created:** 35 tracked files in vue-summercms-app (no summercms.go code changes) + +## Accomplishments + +- New repository `/media/nvme/dev/golem15/summercms.io/summercms/sm-summercms-app/vue-summercms-app` (branch `master`, no remote). Packages are pinned exactly to the fonoteka versions, `pnpm-lock.yaml` is committed, and `allowBuilds` is limited to `@parcel/watcher`, `esbuild` and `vue-demi`. +- `nuxt generate` exits 0 and writes real prerendered HTML: the sticky header, the hero `#top`, `#why`, `#features`, `#winter`, `#start` and the footer, in that order. `/docs` is ignored by the prerender crawler. +- `@nuxt/fonts` self-hosts 4 woff2 files under `/_fonts/`. The generated HTML and CSS name no Google font host. +- `@nuxtjs/seo` writes the canonical link, OG tags (absolute `https://summercms.io/og-image.png`), Twitter tags, schema.org `@graph`, a flat `sitemap.xml` and a `robots.txt` with a Sitemap line. `ogImage` is disabled and a static PNG is shipped instead. +- `scripts/derive-images.sh` (ImageMagick only) derives `sun-logo.webp` (60px), `sun-badge.webp` (272px), `favicon.ico` (16/32/48), `apple-touch-icon.png` (180, on navy) and `og-image.png` (1200x630: sun, wordmark, tagline) from the committed original `assets-src/logo.png` (746x744). `sun-crop.png` is not shipped. +- `app.config.ts` has `landing.showRays` and `landing.scrollSpy`. Setting `showRays: false` was checked to remove the rays div from the output. +- Scroll-spy uses a passive scroll listener, computes on mount and is cleaned up on unmount. The server render has no active link. +- Copy uses `copyPayload` (six lines, no `$` or comments) with a textarea fallback, and shows `Copied` for 1500 ms. + +## Task Commits + +All in `vue-summercms-app`: + +1. **Task 1: Tracer: scaffold with header, hero, fonts and SEO**: `c41cdac` (feat) +2. **Task 2: Why, Features, From WinterCMS and footer**: `a29b2d2` (feat) +3. **Task 3 (TDD): terminal card, copy button and scroll-spy**: RED `d2ffb9b` (test), GREEN `17e9f92` (feat). No refactor was needed. + +**Plan metadata:** committed in summercms.go (docs commit for this SUMMARY) + +## TDD Gate Compliance + +- RED `d2ffb9b`: tests plus interface-only stubs (correct exports and signatures, placeholder bodies), so the failures are assertion failures, not load crashes. `node --test tests/terminal.test.ts tests/scrollSpy.test.ts` gave exit 1 with 8 of 13 failing. `gsd-tools check tdd-red-evidence` returned `RED_EVIDENCE_OK` for the targets "the last section above the threshold wins" (expected `winter`, got `''`) and "copyPayload skips a group without commands" (expected `one\ntwo\nthree`, got `''`). +- GREEN `17e9f92`: 29 of 29 pass in `pnpm test`, and 13 of 13 in the two unit files. + +## Files Created/Modified + +- `nuxt.config.ts`: modules, fonts, i18n (`prefix_except_default`, `prerenderMessages`), site, `ogImage` off, flat sitemap, link-checker exclusion, `nitro.prerender.ignore: ['/docs']`. +- `app/components/*.vue`: `SiteHeader` (sticky bar, scroll-spy, CSS-hidden links at 720px or less), `HeroSection` (rays behind the flag, glow, badge, gradient H1, CTAs), `WhySection` (`` slots for the file names), `FeaturesSection` (eight extension-less docs tiles), `WinterSection` (concept table), `StartSection` (chips, note, buttons), `TerminalCard`, `SiteFooter` (with the Golem15 credit). +- `app/assets/css/tokens.css`, `base.css`: tokens, globals with a reduced-motion scroll override, and the shared heading, button and chip shapes. +- `app/data/terminal.json`, `app/utils/terminal.ts`, `app/utils/scrollSpy.ts`: the single command source and the pure helpers. +- `i18n/locales/en.json`: `meta`, `header`, `hero`, `why`, `features`, `winter`, `start`, `terminal`, `footer`. +- `tests/output.test.ts`, `tests/terminal.test.ts`, `tests/scrollSpy.test.ts`: node:test, with paths resolved from `import.meta.url`. + +## Decisions Made + +See `key-decisions` in the frontmatter. In short: the full page title with a `%s` template, the unmasked sun scaled 1.12 in the header, the phone-width header fix, the clipboard fallback, and footer links that can wrap. + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 1 - Bug] The header overflowed phone widths** +- **Found during:** Task 3 (headless screenshot sanity check at 375px) +- **Issue:** With the handoff spacing (an empty `flex: 1` links wrapper and three 16px gaps), logo + pill + Docs needed about 414px. At 375px the Docs button was clipped and the right padding was lost. The handoff markup has the same overflow. +- **Fix:** At 720px and below, `.links-wrap` is hidden and `.docs` gets `margin-left: auto`. At 420px and below, `.bar` uses `gap: 10px; padding: 0 16px`. Above 720px nothing changes. +- **Files modified:** `app/components/SiteHeader.vue` +- **Verification:** Headless screenshots at 360, 375, 720 and 721px. The header fits, links are hidden at 720 and visible at 721. 29 of 29 tests pass. +- **Committed in:** `17e9f92` + +**2. [Rule 1 - Bug] Footer links could overflow a phone width** +- **Found during:** Task 2 +- **Issue:** The D-12 credit makes four footer links. The handoff's non-wrapping link row is close to the 327px content width at 375px. +- **Fix:** `flex-wrap: wrap` on the footer link group. Nothing changes when they fit. +- **Files modified:** `app/components/SiteFooter.vue` +- **Committed in:** `a29b2d2` + +**3. [Process] Task 3 committed as RED + GREEN instead of one commit** +- The plan asked for one `feat(site)` commit holding the tests and the implementation. Following the TDD reference, the tests went in a `test(site)` RED commit (with interface-only stubs so RED fails on assertions) and the implementation in the `feat(site)` commit with the planned message. `pnpm test` is red at `d2ffb9b` by design. + +**4. [Minor] Small structural choices inside the plan's latitude** +- The en.json leaf-check helper lives inside `tests/output.test.ts`, not in a separate helper file, so `tests/*.test.ts` matches only test files. It takes an `except` list for `terminal.copied`, which only appears after a click. +- The `Docs →` label is two keys (`header.docs`, `header.docsArrow`) so the arrow sits in its own span, as in the handoff. +- `` renders as `en-US` (from `language: 'en-US'`). The test matches `$ `. +- The page uses extension-less `/docs/...` hrefs (D-47), so the plugin must 301 them to `.html`. +- End-of-phase UAT: visual fidelity at 1280, 721, 720 and 375px, the sticky header, the live scroll-spy, and the Copy label and clipboard (coverage D8). + +## Self-Check: PASSED + +- Files: all 35 tracked files are present in `vue-summercms-app` (`git ls-files`), including every `key-files.created` entry. +- Commits: `c41cdac`, `a29b2d2`, `d2ffb9b` and `17e9f92` are in `vue-summercms-app` master. The commit count of 4 was measured with `git rev-list --count master`. +- Plan verification: `pnpm install --frozen-lockfile && pnpm generate && pnpm test` gives 29 pass and 0 fail. The SITE working tree is clean. summercms.go shows only its pre-existing `STATE.md`, `state.json` and zip changes. No co-author trailers. + +--- +*Phase: 11.2-ready-to-share-summercms-io-website-and-newsletter-plugin* +*Completed: 2026-10-01*