commit 1bebbea5ad84818810cd1534f3aae55ab72c304f Author: Alex Dunmow Date: Sat Jun 6 14:11:40 2026 +0800 initial: theme plugin noir Bootstrapped during the 2026-06-06 BlockNinja consolidation. Was previously an unversioned directory inside ~/src/blockninja-themes/noir. Co-Authored-By: Claude Opus 4.7 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f780e6f --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.so +*.test +tmp/ +.idea/ +.vscode/ diff --git a/BUILD_REPORT.md b/BUILD_REPORT.md new file mode 100644 index 0000000..829fd5a --- /dev/null +++ b/BUILD_REPORT.md @@ -0,0 +1,143 @@ +# Noir — Build Report + +Implementation pass: wave-1 scaffold of the `noir` theme plugin. + +Plugin slug: `noir` +Module path: `git.dev.alexdunmow.com/block/themes/noir` +SDK pinned to: `git.dev.alexdunmow.com/block/core v0.11.1` +Go directive: `go 1.26.4` +Tech style: templ (per spec §11) + +## What landed + +### Metadata & build glue +- `plugin.mod` — name, display_name, scope `@themes`, kind `theme`, version `0.1.0`, categories `["templates", "media"]`, tags array (8 entries, includes the four spec-required minimums), `[compatibility] block_core = ">=0.11.0 <0.12.0"`. +- `go.mod` — pins `block/core v0.11.1` and `templ v0.3.1020`, mirrors gotham's indirect set, no `replace` directives. +- `Makefile` — local-only targets (`all`, `templ`, `clean`). No `rebuild` target. Default builds `noir.so` via `CGO_ENABLED=1 go build -buildmode=plugin`. +- `embed.go` — five canonical `//go:embed` directives plus a `ThemeCSSManifest()` that surfaces `assets/style.css` through `CSSManifest.InputCSSAppend` so the custom utilities (`.tracked-mono`, `.bleed`, `.hairline`, sprocket motif, lightbox overlay) survive Tailwind's content scanner. +- `registration.go` — exports `var Registration plugin.PluginRegistration` with all functions wired. + +### System & page templates +- `tr.RegisterSystemTemplate({Key: "noir", …})` — exactly once. +- Four `tr.RegisterPageTemplate("noir", …)` calls with the spec's slot sets: + - `default` — `["header", "main", "footer"]` + - `landing` — `["hero", "main", "cta", "footer"]` + - `article` — `["header", "main", "aside", "footer"]` + - `full-width` — `["header", "main", "footer"]` +- Each page renderer lives in `template.templ` and consumes the shared `bn.Head` / `bn.AdminBypassBanner` / `bn.BodyEnd` helpers, mirroring gotham. + +### Blocks (6 theme-specific) +- `noir:lightbox_gallery` — grid + vanilla keyboard-aware lightbox overlay (Esc + Enter/Space, click-outside dismiss, ARIA-modal). +- `noir:contact_sheet` — sprocket-framed numbered grid; `data-frame-number` attribute on each frame for UAT §13.9. +- `noir:case_study` — sticky meta rail (client / year / credits) + image stack. +- `noir:caption_strip` — 10px mono full-width strip. +- `noir:image_pair` — 50/50 diptych with shared caption. +- `noir:footer` — dissolved-rail footer with optional social links. + +All registered with `Source: "noir"` and the appropriate `blocks.Category*` constant. + +### Schemas (6, draft-07) +- Property names exactly match the Go `content["…"]` reads in each block. +- Every `x-editor` value comes from the allowed set: + `text`, `media`, `select`, `number`, `array`, `link`. +- `lightbox_gallery.columns` uses `x-editor: select` with `enum: [2, 3, 4]` (spec §8). +- `case_study.year` uses `x-editor: number` (spec §8). +- `case_study.credits` is `array`; `case_study.images` is `array` per spec §8. +- `footer.social` is `array` with `text`/`url` fields per spec §8. + +### Template overrides (5) +- `RegisterTemplateOverride("noir", …)` for `heading`, `text`, `image`, `button`, `card` — exactly five calls, per UAT §3. +- Display headings render with `font-family: var(--font-heading)` and no underline. +- Button override is hairline 1px outline, transparent background, hover inverts to `--primary` / `--primary-foreground`. +- Card override is transparent with hairline border only. +- Image override emits the `.bleed` utility for full-bleed and a `figcaption.tracked-mono` for the mono caption. + +### Email wrapper +- `tr.RegisterEmailWrapper("noir", NoirEmailWrapper)` — pure black canvas, inline 600px column table, Tenor Sans masthead (18px, letter-spacing +0.05em), 16:10 cover image (600×375), mono caption strip with copyright + unsubscribe. +- All inline styles; no `