themes-scifi-clean/BUILD_REPORT.md
Alex Dunmow 96b87b3e81 initial: theme plugin scifi-clean
Bootstrapped during the 2026-06-06 BlockNinja consolidation. Was previously
an unversioned directory inside ~/src/blockninja-themes/scifi-clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 14:11:43 +08:00

8.8 KiB

Sci-Fi Clean — Build Report (v0.1.0, wave-1 pass)

What landed

Module scaffolding

  • plugin.mod with kind = "theme", scope = "@themes", all spec §2 fields verbatim (categories ["templates", "developer"], 9 tags), [compatibility] block_core = ">=0.11.0 <0.12.0".
  • go.mod pinned to git.dev.alexdunmow.com/block/core v0.11.1, go 1.26.4, no replace directives.
  • Makefile with all (default), clean, templ, help targets. The build line is CGO_ENABLED=1 go build -buildmode=plugin -ldflags="-s -w" -o scifi-clean.so ..
  • embed.go declares all five canonical embed directives plus ThemeCSSManifest().

Registration

  • One RegisterSystemTemplate({Key: "scifi-clean", ...}).
  • Four page templates per spec §6:
    • default — slots header, main, footer
    • landing — slots hero, specs, main, cta, footer
    • article — slots header, rail, main, footer
    • full-width — slots header, main, footer
  • br.LoadSchemasFromFS(Schemas()) runs before any br.Register(...). UAT §3.6.
  • Six theme blocks registered: tech_spec, diagram_caption, mission_stat, status_bar, footer, schematic_hero. Each has Source = "scifi-clean" and an unqualified Key. Addressed at runtime as scifi-clean:<key>.
  • Three overrides registered against theme key "scifi-clean":
    • heading → ScifiHeadingBlock (Space Grotesk via --font-heading, optional mono kicker)
    • text → ScifiTextBlock (Inter via --font-body, tabular-nums)
    • button → ScifiButtonBlock (mono uppercase label, literal U+2192 chevron + data-icon="chevron-right" for UAT §13.14)
  • tr.RegisterEmailWrapper("scifi-clean", ScifiEmailWrapper) wires the 600px white-card email shell with Space Grotesk display lockup top-left and mono callsign top-right.

Master pages

DefaultMasterPages() returns two entries:

  • scifi-clean:default-master, attached to default, article, full-width. Blocks: navbar (header, 0) + scifi-clean:status_bar (header, 10) + slot (main, 0, {"slotName": "main"}) + scifi-clean:footer (footer, 0). Matches spec §7 / UAT §9 byte-for-byte.
  • scifi-clean:landing-master, attached to landing. Pre-populates hero with scifi-clean:mission_stat and specs with scifi-clean:tech_spec, both seeded with example payloads.

Schemas

Six schemas/*.schema.json files, all draft-07, x-editor values restricted to the allowed set (text, richtext, media, select, number, array, collection, link). Property names match the corresponding Go content reads exactly.

Presets

presets.json is a JSON array of length 3 in the order required by UAT §5.1:

  1. flightline — mode light, only lightColors. Declares "primary": "215 90% 45%" and "accent": "18 95% 55%" byte-exactly (UAT §13.1).
  2. mission-control — mode dark, only darkColors. Declares "background": "220 16% 7%" and "accent": "18 100% 60%" byte-exactly (UAT §13.2).
  3. cleanroom — mode both, both lightColors and darkColors.

All three presets carry all 19 tokens in each declared color block. Every value is an HSL triple string of the shape ^\d+ \d+% \d+%$ (no hsl() wrappers).

CSS manifest

ThemeCSSManifest() returns a *plugin.CSSManifest whose InputCSSAppend contains the literal substrings UAT §13.4 grep-greps: 'Space Grotesk', 'JetBrains Mono', .hairline, .bg-grid. The .hairline declaration is exactly border: 1px solid hsl(var(--border)); (UAT §13.5).

The manifest also defines:

  • Root-level CSS-variable fallback stacks for --font-heading, --font-body, --font-mono so the theme looks correct before the admin picks Google Fonts.
  • .bg-grid blueprint grid utility.
  • .scrim utility for full-bleed hero text (UAT §6 scrim requirement).
  • .scifi-focus outlined focus ring derived from --ring.
  • .scifi-chevron Unicode arrow suffix for button labels.

Fonts policy (wave-1)

  • fonts.json is the literal []. No woff2 files bundled in this pass.
  • RECOMMENDED_FONTS.md at the theme root lists Space Grotesk / Inter / JetBrains Mono as Google Fonts picker recommendations.
  • No LICENSES.md (nothing is bundled to license).

Build output

$ cd ~/src/blockninja/themes/scifi-clean && make clean && make
rm -f scifi-clean.so
CGO_ENABLED=1 go build -buildmode=plugin -ldflags="-s -w" -o scifi-clean.so .
$ ls -la scifi-clean.so
-rw-rw-r-- 1 alex alex 21535264 ... scifi-clean.so   (≈ 20.5 MiB)
$ file scifi-clean.so
ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, stripped

make 2>&1 | grep -Ei 'warning|error' returns empty. go mod tidy is a no-op after the initial run; the resolved block/core version is v0.11.1.

Safety check

Run from the standalone check-safety module (at ~/src/blockninja/check-safety/, not backend/cmd/check-safety — that path does not exist in this tree):

$ cd ~/src/blockninja/check-safety && \
    go run . ~/src/blockninja/themes/scifi-clean \
             --plugin-dir ~/src/blockninja/themes/scifi-clean
...
EXIT: 0

All 22 checks PASS or SKIP. Specifically:

  • Check 2c (SDK import boundary): OK on v0.11.1.
  • Check 6 (no hardcoded colors in .templ/.ninjatpl files): OK.
  • Check 11 (no placeholder code): OK.
  • Check 17 (no TODO markers): OK.
  • Check 21 (presets.json validation against theme.Theme): OK.

The only non-OK output is Check 2e (any usage), which is a WARN, not a FAIL. The 34 warnings are the expected map[string]any content map signature for plugin block render funcs — that is the SDK's required block-func shape and cannot be avoided in a standalone plugin. Same as gotham/ and lcars/.

Open items / deferred

These are intentionally out of scope for the wave-1 implementation pass:

Fonts

  • No woff2 files bundled (fonts.json = [] per themes/docs/FONTS.md). Wave-2 may bundle commercial display faces (Eurostile when licensed, otherwise stick with Space Grotesk) under assets/fonts/web/.
  • Section §11 of the UAT (woff2 file presence, @font-face count, network fetch checks) is superseded by the FONTS.md wave-1 policy, which only requires fonts.json to parse as JSON, RECOMMENDED_FONTS.md to exist, and CSS to consume var(--font-*). All three pass.

Live instance / make rebuild

  • make rebuild is not wired in this Makefile. Adding it requires the blockninja-go-builder podman image and the live CMS at ~/src/blockninja. Copy gotham's rebuild, backend, build-frontend, copy-plugin-source, build-so, sync-migrations, build-css, deploy-css, logs, status targets when the next implementation pass needs container deployment.

Marketplace assets (UAT §12)

  • No screenshots captured (docs/uat-evidence/screenshots/). Requires a running instance.
  • No Project Aurora demo content seeded. Requires admin tooling and a populated database.
  • No docs/launch-copy.txt. Copy is in spec §13 verbatim; persist when the marketplace listing is created.

Versioning / git

  • The theme is not under git in this scope; UAT §1.11 (git describe --tags) cannot be evaluated until the directory is initialised as a git repo and tagged v0.1.0.

Email wrapper hex fallbacks

  • email_wrapper.templ carries a small set of hardcoded hex values (#F6F7F8, #FFFFFF, #D6D9DD, #16202E, #5C6776) that act as the ultimate fallback when EmailContext.Colors is empty. Email clients (Outlook in particular) cannot resolve hsl(var(--token)), so emails must inline hex; the runtime path always passes preset-resolved hex via EmailContext. Check-safety §6 only scans .templ files for the hardcoded-colors rule and currently passes (the hex literals live inside Go fallback funcs and are explicitly out of the templ-emit path). UAT §5.5 may need a clarifying note that email fallbacks are intentional.

Hidden blocks

  • None of the six theme blocks set Hidden: true; the spec did not request any. (Gotham hides its stat_item child block; the scifi-clean equivalent is the in-rows collection on tech_spec, which is schema-only and never registered as a standalone block, so no Hidden flag is needed.)

Block category metadata

  • All six theme blocks set Category to one of the SDK constants (CategoryContent, CategoryNavigation, CategoryLayout). The spec's §8 table lists category names like media and blog that do not map to SDK constants; those are stored implicitly via the spec source rather than the BlockMeta — flag in BUILD_REPORT for future enum extension.

Slot block placeholder

  • Master pages reference the built-in slot block with {"slotName": "main", "placeholder": "Page payload"}. The placeholder key is a CMS-side convention copied verbatim from gotham; it is not the same as the check-safety §11 "no placeholder code" rule (which fires on dev-time comments like // TODO: placeholder). Check-safety passes.