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>
10 KiB
10 KiB
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)
Registrationexported,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 anybr.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,imageviabr.RegisterTemplateOverride("y2k", ...). tr.RegisterEmailWrapper("y2k", Y2KEmailWrapper)wired.DefaultMasterPages()seedsy2k:default-master(applies todefault,article) andy2k:landing-master(applies tolanding,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-editorvalues 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
idvalueschrome-dream,cd-rom-after-hours,bubblegum-trapper. chrome-dreamdeclaresmode: "both"with bothlightColorsanddarkColorsblocks (38 tokens across both).cd-rom-after-hoursdeclaresmode: "dark"withdarkColorsonly.bubblegum-trapperdeclaresmode: "light"withlightColorsonly.- All 19 tokens present per colour block.
- Every value matches the regex
^\d+ \d+% \d+%$(HSL triples, nohsl()wrapper). Verified via Python regex check. - Values copied verbatim from spec §4 tables.
Fonts (fonts.json, RECOMMENDED_FONTS.md)
fonts.jsonis[]per the wave-1 policy inthemes/docs/FONTS.md.RECOMMENDED_FONTS.mdlists 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-familyreferences in templates and CSS go throughvar(--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/darkcustom properties on:rootand.dark(UAT 13.2, 13.3, 13.4). - Defines exactly one
@keyframes marquee-x, one@keyframes sparkle, one@keyframes metaball-morphblock (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 withborder-style: solid,border-width: 2px,box-shadowcontaininginset— UAT 13.9)..y2k-marquee+.y2k-marquee-trackwithoverflow: hiddenand themarquee-xanimation;animation-play-state: pausedon hover/focus (UAT 13.10, 6.7)..y2k-webring-badgeat the canonicalwidth: 88px; height: 31px(UAT 13.11)..y2k-foil::afterwithconic-gradient(andfilter: 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: reduceby 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.goor.templfile 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 fromemailCtx.Colors.Backgroundwhen 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
bodyalready 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
^replacedirectives ingo.mod. block/core v0.11.1pinned to matchcms/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.mdwave-1 policy this pass shipsfonts.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 ("NoLICENSES.mdneeded 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-modeon<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)