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