# 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:`. - 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.