Bootstrapped during the 2026-06-06 BlockNinja consolidation. Was previously an unversioned directory inside ~/src/blockninja-themes/y2k. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
238 lines
10 KiB
Markdown
238 lines
10 KiB
Markdown
# Y2K theme — build report
|
||
|
||
Implementation pass: 2026-06-06.
|
||
Reference style: templ (gotham-shaped, per spec §11 "Tech choice").
|
||
|
||
## What landed
|
||
|
||
### Registration (`registration.go`, `register.go`)
|
||
|
||
- `Registration` exported, `Name: "y2k"`, `Version: plugin.ParseModVersion(pluginModBytes)`.
|
||
- `tr.RegisterSystemTemplate(Key: "y2k", ...)` — exactly one call.
|
||
- `tr.RegisterPageTemplate("y2k", ...)` called four times with slots verbatim:
|
||
- `default` → `["header", "main", "footer"]`
|
||
- `landing` → `["hero", "marquee", "main", "cta", "footer"]`
|
||
- `article` → `["header", "main", "aside", "footer"]`
|
||
- `full-width` → `["header", "main", "footer"]`
|
||
- `br.LoadSchemasFromFS(Schemas())` invoked once, before any `br.Register(...)`.
|
||
- 10 theme blocks registered (unqualified keys, `Source: "y2k"`):
|
||
`chrome_navbar`, `metaball_hero`, `waveform_player`, `marquee`,
|
||
`tracklist`, `merch_card`, `webring_badge`, `glitter_divider`,
|
||
`footer_chrome`, `nft_gallery`.
|
||
- 4 overrides registered: `heading`, `text`, `button`, `image` via
|
||
`br.RegisterTemplateOverride("y2k", ...)`.
|
||
- `tr.RegisterEmailWrapper("y2k", Y2KEmailWrapper)` wired.
|
||
- `DefaultMasterPages()` seeds `y2k:default-master` (applies to `default`,
|
||
`article`) and `y2k:landing-master` (applies to `landing`, `full-width`)
|
||
with the exact block order from spec §"Master pages".
|
||
|
||
### Plugin metadata (`plugin.mod`)
|
||
|
||
- `name = "y2k"`, `display_name = "Y2K"` (3 chars, ≤40), `scope = "@themes"`.
|
||
- `kind = "theme"` (per global rule, not "plugin").
|
||
- `categories = ["templates", "media"]` — both whitelisted.
|
||
- `tags` — 8 entries (within the 5–9 UAT range).
|
||
- `version = "0.1.0"`.
|
||
- `[compatibility] block_core = ">=0.11.0 <0.12.0"` verbatim.
|
||
- Description 157 chars (≤240).
|
||
|
||
### Schemas (`schemas/*.schema.json`)
|
||
|
||
- 10 JSON Schema files, all draft-07.
|
||
- Property names match `content["…"]` reads in the Go files one-to-one (no
|
||
orphans either direction; verified by script during build).
|
||
- All `x-editor` values are members of the whitelist
|
||
`{text, richtext, media, color, select, number, slug, textarea, array,
|
||
collection, bucket-picker, menu-select, template-select, link}`.
|
||
|
||
### Presets (`presets.json`)
|
||
|
||
- Exactly 3 entries with `id` values `chrome-dream`, `cd-rom-after-hours`,
|
||
`bubblegum-trapper`.
|
||
- `chrome-dream` declares `mode: "both"` with both `lightColors` and
|
||
`darkColors` blocks (38 tokens across both).
|
||
- `cd-rom-after-hours` declares `mode: "dark"` with `darkColors` only.
|
||
- `bubblegum-trapper` declares `mode: "light"` with `lightColors` only.
|
||
- All 19 tokens present per colour block.
|
||
- Every value matches the regex `^\d+ \d+% \d+%$` (HSL triples, no `hsl()`
|
||
wrapper). Verified via Python regex check.
|
||
- Values copied verbatim from spec §4 tables.
|
||
|
||
### Fonts (`fonts.json`, `RECOMMENDED_FONTS.md`)
|
||
|
||
- `fonts.json` is `[]` per the wave-1 policy in `themes/docs/FONTS.md`.
|
||
- `RECOMMENDED_FONTS.md` lists the spec §5 fonts as Google Fonts picker
|
||
recommendations (Inter Tight via Google; VT323 via Google as the
|
||
open-licensed fallback for Stretch Pro; Departure Mono and Stretch Pro as
|
||
admin uploads).
|
||
- All `font-family` references in templates and CSS go through
|
||
`var(--font-heading|body|mono, <fallback-stack>)`. The headline heavy
|
||
fallback stack is `"Stretch Pro", "VT323", "Courier New", monospace`.
|
||
|
||
### CSS (`css.go` via `CSSManifest.InputCSSAppend`)
|
||
|
||
- Declares `--chrome-1..4`, `--mesh-a/b/c`, `--bevel-light/dark` custom
|
||
properties on `:root` and `.dark` (UAT 13.2, 13.3, 13.4).
|
||
- Defines exactly one `@keyframes marquee-x`, one `@keyframes sparkle`, one
|
||
`@keyframes metaball-morph` block (UAT 13.5).
|
||
- Provides utility classes:
|
||
- `.y2k-chrome-bg` (layered linear gradient through `--chrome-1..4`).
|
||
- `.y2k-mesh-bg` (radial-gradient mesh through `--mesh-a/b/c`).
|
||
- `.y2k-bevel` (`border: 2px solid hsl(var(--border))` + inset shadows
|
||
using `--bevel-light/dark`; UAT 13.4).
|
||
- `.y2k-button` (3D plastic bevel with `border-style: solid`,
|
||
`border-width: 2px`, `box-shadow` containing `inset` — UAT 13.9).
|
||
- `.y2k-marquee` + `.y2k-marquee-track` with `overflow: hidden` and the
|
||
`marquee-x` animation; `animation-play-state: paused` on hover/focus
|
||
(UAT 13.10, 6.7).
|
||
- `.y2k-webring-badge` at the canonical `width: 88px; height: 31px`
|
||
(UAT 13.11).
|
||
- `.y2k-foil::after` with `conic-gradient(` and `filter: hue-rotate(`
|
||
that activates on `:hover` (UAT 13.12).
|
||
- `.y2k-heading`, `.y2k-text`, `.y2k-image-frame`, `.y2k-sparkle`,
|
||
`.y2k-metaball`, `.y2k-glow`.
|
||
- Honors `prefers-reduced-motion: reduce` by disabling marquee, sparkle,
|
||
and metaball-morph animations (UAT 6.6).
|
||
- All colour references go through `hsl(var(--token))`; no hardcoded
|
||
hex/rgb appears in any `.go` or `.templ` file outside the email wrapper
|
||
(which uses hex fallbacks that translate the chrome-dream dark preset for
|
||
Gmail/Outlook compatibility, exactly as gotham does).
|
||
|
||
### Email wrapper
|
||
|
||
- `tr.RegisterEmailWrapper("y2k", Y2KEmailWrapper)` registered exactly once.
|
||
- 600px centred chrome-bordered card on a near-black background
|
||
(`#0c0820`-equivalent sourced from `emailCtx.Colors.Background` when set,
|
||
otherwise the chrome-dream dark token value).
|
||
- Gradient header bar uses `linear-gradient(90deg, primary, secondary)`.
|
||
- Inter Tight body with system fallback stack.
|
||
- Marquee footer pill renders the same items as the in-page marquee.
|
||
- Plain-text fallback is deferred to the CMS-default stripping (the wrapper
|
||
receives `body` already rendered; the marquee items are duplicated into a
|
||
visible string so a plain-text view still surfaces them).
|
||
|
||
## Build output
|
||
|
||
```
|
||
$ cd /home/alex/src/blockninja/themes/y2k && go mod tidy
|
||
(exit 0)
|
||
|
||
$ cd /home/alex/src/blockninja/themes/y2k && /home/alex/go/bin/templ generate
|
||
(✓) Complete [ updates=16 duration=10ms ]
|
||
|
||
$ cd /home/alex/src/blockninja/themes/y2k && make
|
||
CGO_ENABLED=1 go build -buildmode=plugin -ldflags="-s -w" -o y2k.so .
|
||
(exit 0, no warnings)
|
||
|
||
$ ls -la y2k.so
|
||
21,543,872 bytes (≈21 MB) ELF 64-bit LSB shared object, stripped
|
||
```
|
||
|
||
`make` exits 0 with zero `warning` lines in stdout/stderr (UAT 2.1).
|
||
|
||
## Safety check
|
||
|
||
```
|
||
$ /tmp/check-safety . --plugin-dir /home/alex/src/blockninja/themes/y2k
|
||
(... 22 checks, all OK or SKIP for "no frontend sources" ...)
|
||
exit=0
|
||
```
|
||
|
||
NOTE: the task brief references `cd ~/src/blockninja/backend && go run
|
||
./cmd/check-safety …`. The actual command path on this host is
|
||
`~/src/blockninja/check-safety/` (no `/backend/cmd/` subpath). The
|
||
invocation above is the host-local equivalent and exits 0.
|
||
|
||
Specifically verified:
|
||
- Check 21 (presets.json validation): OK — single preset file validated.
|
||
- Check 22 (no hand-rolled HTML sanitization): OK.
|
||
- No `git.dev.alexdunmow.com/block/cms/...` import boundary violations.
|
||
- No `^replace ` directives in `go.mod`.
|
||
- `block/core v0.11.1` pinned to match `cms/backend/go.mod`.
|
||
|
||
## Open items / deferred
|
||
|
||
The following spec/UAT requirements are intentionally deferred to a later
|
||
wave and would block sign-off on the running container but do not block
|
||
this build pass:
|
||
|
||
- **Bundled woff2 files**. Per `themes/docs/FONTS.md` wave-1 policy this
|
||
pass ships `fonts.json = []`. UAT §11 file-existence checks pass
|
||
trivially. Wave-2 will commission/licence Stretch Pro and Departure Mono
|
||
and bundle them.
|
||
- **`LICENSES.md`** — explicitly skipped per FONTS.md wave-1 policy
|
||
("No `LICENSES.md` needed in this pass").
|
||
- **Marketplace screenshots** (`marketplace/screenshots/*.png`, 1440×900).
|
||
Not produced in this pass — gated by a running container with deployed
|
||
CSS.
|
||
- **Demo content seed** ("Static Lagoon" fictional artist with 6 tracks,
|
||
4 merch items, 2 zine articles, 5 webring entries). Not produced — the
|
||
data plane is out of scope for the .so build pass.
|
||
- **Container-level UAT items**: §2.7–2.8 (`make rebuild`, log scrape),
|
||
§5.7 (no console errors at the rendered URL), §6.1–6.7 (Lighthouse /
|
||
computed-style assertions on a running site), §7.1–7.6 (responsive
|
||
viewport checks), §8.* (rendering each block in three states in the
|
||
browser), §9.7 (admin-replace round-trip), §10.2–10.4 (Litmus / Gmail /
|
||
Apple Mail / Outlook 365 previews), §11.4–11.5 (network-tab font 200
|
||
checks, FOUT trace), §12.* (marketplace assets, demo seed), §13.1, 13.6
|
||
(computed-style assertions on rendered pages), §14.* (three-theme
|
||
install regression), §15.* (three named reviewer sign-offs).
|
||
- **Waveform peak-data source**. The block ships with a placeholder
|
||
`<canvas data-y2k-waveform>` element that the CMS' client runtime can
|
||
populate via WebAudio decode (deferred to v0.2 per spec open question).
|
||
- **Cursor-trail sparkle script**. Only the CSS classes are shipped; the
|
||
document-level cursor-trail script is deferred and gated by
|
||
`data-editor-mode` on `<html>` per the spec's open question.
|
||
- **Mobile menu drawer JS**. The chrome navbar exposes the
|
||
`[aria-controls]` toggle button required by UAT §7.4 but the
|
||
drawer-open behaviour is owned by the host menu script.
|
||
|
||
## File inventory
|
||
|
||
```
|
||
y2k/
|
||
├── BUILD_REPORT.md (this file)
|
||
├── Makefile
|
||
├── RECOMMENDED_FONTS.md
|
||
├── assets/
|
||
│ └── placeholder.txt
|
||
├── button_override.{go,templ,_templ.go}
|
||
├── chrome_navbar.{go,templ,_templ.go}
|
||
├── css.go
|
||
├── email_wrapper.{go,templ,_templ.go}
|
||
├── embed.go
|
||
├── fonts.json
|
||
├── footer_chrome.{go,templ,_templ.go}
|
||
├── glitter_divider.{go,templ,_templ.go}
|
||
├── go.mod
|
||
├── go.sum
|
||
├── heading_override.{go,templ,_templ.go}
|
||
├── helpers.go
|
||
├── image_override.{go,templ,_templ.go}
|
||
├── marquee.{go,templ,_templ.go}
|
||
├── merch_card.{go,templ,_templ.go}
|
||
├── metaball_hero.{go,templ,_templ.go}
|
||
├── nft_gallery.{go,templ,_templ.go}
|
||
├── plugin.mod
|
||
├── presets.json
|
||
├── register.go
|
||
├── registration.go
|
||
├── schemas/
|
||
│ ├── chrome_navbar.schema.json
|
||
│ ├── footer_chrome.schema.json
|
||
│ ├── glitter_divider.schema.json
|
||
│ ├── marquee.schema.json
|
||
│ ├── merch_card.schema.json
|
||
│ ├── metaball_hero.schema.json
|
||
│ ├── nft_gallery.schema.json
|
||
│ ├── tracklist.schema.json
|
||
│ ├── waveform_player.schema.json
|
||
│ └── webring_badge.schema.json
|
||
├── template.{templ,_templ.go}
|
||
├── text_override.{go,templ,_templ.go}
|
||
├── tracklist.{go,templ,_templ.go}
|
||
├── waveform_player.{go,templ,_templ.go}
|
||
├── webring_badge.{go,templ,_templ.go}
|
||
└── y2k.so (21 MB)
|
||
```
|