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

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

184 lines
9.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Brutalist — Build Report
Theme slug: `brutalist`
Module path: `git.dev.alexdunmow.com/block/themes/brutalist`
Tech: templ-style (gotham-pattern), per spec §11.
Implementation pass: wave-1 (fonts policy applies — `fonts.json = []`).
## What landed
### Module + metadata
- `plugin.mod` per spec §2 verbatim: `kind = "theme"`, `scope = "@themes"`, `version = "0.1.0"`, `categories = ["templates"]`, tags array exactly the 8 spec values, `[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` default target builds `brutalist.so` via `CGO_ENABLED=1 go build -buildmode=plugin`. No `rebuild` target (per agent scope; deploy is explicit).
### System / page templates / blocks
- System template registered: `brutalist` ("Brutalist", spec description verbatim).
- 4 page templates registered with exact spec slot arrays:
- `default``{header, main, footer}``RenderBrutalist`
- `landing` ("Index Sheet") → `{hero, ledger, footer}``RenderBrutalistLanding`
- `article` ("Case Study") → `{header, meta, main, footer}``RenderBrutalistArticle`
- `full-width` ("Full Bleed") → `{header, main, footer}``RenderBrutalistFullWidth`
- 7 theme blocks registered (all `Source: "brutalist"`):
- `masthead`, `project_ledger`, `concrete_hero`, `meta_strip`, `caption_image`, `pull_quote`, `colophon`
- 4 built-in block overrides via `RegisterTemplateOverride("brutalist", ...)`:
- `heading`, `text`, `button`, `image` — spec §9 treatments (square corners, font-display, mono code, figure wrapping).
- Email wrapper registered via `tr.RegisterEmailWrapper("brutalist", BrutalistEmailWrapper)`.
### Master pages
3 master pages in `DefaultMasterPages()` per spec §7 row-by-row:
- `brutalist:default-master` → templates `[default, article]`, blocks navbar/masthead/slot(main)/colophon(showAddress=true)
- `brutalist:index-master` → template `[landing]`, blocks masthead(hero)/slot(ledger)/colophon(showAddress=true)
- `brutalist:fullbleed-master` → template `[full-width]`, blocks navbar/slot(main)/colophon(showAddress=false)
### Schemas
7 draft-07 schemas in `schemas/<key>.schema.json`. `x-editor` values used: `text`, `richtext`, `media`, `select`, `textarea`, `collection`, `link`. All inside the allowed set per CLAUDE.md.
- `br.LoadSchemasFromFS(Schemas())` is called BEFORE any `br.Register(...)` in `register.go` — verified by reading top-to-bottom.
### Presets
`presets.json` ships two presets:
- `concrete-red` (mode `both`) with `lightColors` + `darkColors`, all 19 tokens each, HSL triple strings only.
- `hazard-yellow` (mode `dark`) with both `darkColors` and a mirror `lightColors` for editor preview, all 19 tokens each.
- Spot values from spec verbatim: `concrete-red.lightColors.background = "40 14% 93%"`, `concrete-red.lightColors.primary = "4 86% 48%"`, `hazard-yellow.darkColors.primary = "48 100% 50%"`, `hazard-yellow.darkColors.background = "0 0% 6%"`.
- check-safety presets check (Check 21): OK.
### CSS
`CSSManifest.InputCSSAppend` ships utility CSS via `ThemeCSSManifest()`:
- `:where(.brutalist-page, .brutalist-page *) { border-radius: 0 !important; }` (kills rounding theme-wide).
- `.brutalist-display`, `.brutalist-mono` font utilities flowing through `var(--font-heading|body|mono, <fallback-stack>)`.
- Hairline / thick rule utilities (`.brutalist-rule-ink`, `.brutalist-rule-thick`, `.brutalist-divider`).
- 12-column grid scaffolding (`.brutalist-grid-12`, `.brutalist-col-span-8`).
- Block-specific styles for `.brutalist-masthead`, `.brutalist-hero`, `.brutalist-ledger-row`, `.brutalist-meta-strip`, `.brutalist-pullquote`, `.brutalist-figure figcaption`, `.brutalist-colophon`.
- Square buttons + hover-invert-to-accent (spec §9 button override surface).
- Togglable `body.brutalist-grid-debug` 12-col overlay (UAT §13.11).
- All color references go through `hsl(var(--token))`; no hex/rgb/named colors anywhere in `*.go`, `*.templ`, or the appended CSS (Check 6 OK).
### Fonts (wave-1 policy)
- `fonts.json = []` (per FONTS.md override of spec §5 / UAT §11).
- `assets/` contains only a `.gitkeep`; no woff2 bundled.
- All `font-family` declarations in templates and CSS go through `var(--font-heading|body|mono, <fallback>)` with fallback stacks derived from the spec typography list (Space Grotesk → Inter Tight → Helvetica; Inter → -apple-system → Segoe UI; JetBrains Mono → IBM Plex Mono → ui-monospace).
- `RECOMMENDED_FONTS.md` written at the theme root listing the spec §5 fonts as Google Fonts picker recommendations with admin instructions.
## Build output
```
$ make
CGO_ENABLED=1 go build -buildmode=plugin -ldflags="-s -w" -o brutalist.so .
$ ls -la brutalist.so
-rw-rw-r-- 1 alex alex 21522624 brutalist.so
```
- File: `brutalist.so` (~20.5 MB; gotham reference `.so` is 21.1 MB, lcars is similar).
- No stderr warnings.
- `templ generate` produced 13 `_templ.go` files (committed alongside `.templ` sources).
## Safety check
Run from the actual check-safety location (the path in the spec's instructions points at `~/src/blockninja/backend/cmd/check-safety` but the real tool lives at `~/src/blockninja/check-safety/`).
```
$ cd /home/alex/src/blockninja/check-safety
$ go run . /home/alex/src/blockninja/themes/brutalist --plugin-dir /home/alex/src/blockninja/themes/brutalist
... (all 22 checks)
exit=0
```
Key results:
- **Check 2c** (Standalone plugin SDK import boundaries): OK — pinned to v0.11.1, no `replace` directives.
- **Check 3** (Go lint pipeline): OK after dropping unused `brutalistAccent`, `brutalistAccentFg`, `brutalistEmailCTAStyle`, and `renderPage` helpers.
- **Check 6** (No hardcoded colors in .templ): OK — all color references use `hsl(var(--token))`.
- **Check 11** (No placeholder code): OK.
- **Check 17** (No TODO markers): OK.
- **Check 21** (presets.json validation): OK.
- **Check 22** (No hand-rolled HTML sanitization): OK.
Warnings (non-fatal):
- **Check 2e** (`any` usage): 30 warnings, all from the required SDK signature `func(ctx context.Context, content map[string]any) string` and `MasterPageBlock.Content map[string]any`. Cannot be removed without breaking the SDK contract; same surface gotham exposes.
Overall: **exit 0, plugin builds, safety passes.**
## Open items / deferred
These items are explicitly deferred per the agent's hard scope rules ("local-build only, no woff2 bundling, no live deploy, no screenshots") and the FONTS.md wave-1 policy:
1. **Bundled woff2 files** — wave-2 follow-up. Currently `fonts.json = []`; `RECOMMENDED_FONTS.md` covers the admin path until Space Grotesk, Inter, and JetBrains Mono are bundled.
2. **`LICENSES.md`** — not written this pass (FONTS.md §"Wave-1 implementation policy" explicitly says "No `LICENSES.md` needed in this pass").
3. **Marketplace screenshots (spec §13.1)** — 6 frames at 1440×900 deferred until a running CMS instance is available; agent scope says "no `make rebuild`".
4. **Demo-content seed (spec §13.2)** — not implemented in this pass.
5. **`make rebuild` against running CMS** — UAT §2 "instance-brutalist Up within 15s" check cannot run from this agent's scope; needs a live instance.
6. **UAT §6, §7, §13 runtime checks** — accessibility, responsive, and aesthetic gates require a running container and visual verification; out of scope for the build agent.
7. **Email wrapper Litmus/cross-client verification (UAT §10)** — wrapper compiles and registers; live capture testing deferred.
8. **`concrete_hero.media` video support** (spec open question) — schema is `media` (image only) per the spec, deferred to v0.2 if the media type needs to widen.
## File inventory
```
brutalist/
├── BUILD_REPORT.md
├── Makefile
├── RECOMMENDED_FONTS.md
├── assets/
│ └── .gitkeep
├── brutalist.so
├── button_override.go
├── button_override.templ
├── button_override_templ.go
├── caption_image.go
├── caption_image.templ
├── caption_image_templ.go
├── colophon.go
├── colophon.templ
├── colophon_templ.go
├── concrete_hero.go
├── concrete_hero.templ
├── concrete_hero_templ.go
├── css_append.go
├── email_wrapper.go
├── email_wrapper.templ
├── email_wrapper_templ.go
├── embed.go
├── fonts.json
├── go.mod
├── go.sum
├── heading_override.go
├── heading_override.templ
├── heading_override_templ.go
├── helpers.go
├── image_override.go
├── image_override.templ
├── image_override_templ.go
├── masthead.go
├── masthead.templ
├── masthead_templ.go
├── meta_strip.go
├── meta_strip.templ
├── meta_strip_templ.go
├── page_data.go
├── plugin.mod
├── presets.json
├── project_ledger.go
├── project_ledger.templ
├── project_ledger_templ.go
├── pull_quote.go
├── pull_quote.templ
├── pull_quote_templ.go
├── register.go
├── registration.go
├── schemas/
│ ├── caption_image.schema.json
│ ├── colophon.schema.json
│ ├── concrete_hero.schema.json
│ ├── masthead.schema.json
│ ├── meta_strip.schema.json
│ ├── project_ledger.schema.json
│ └── pull_quote.schema.json
├── template.templ
├── template_templ.go
├── text_override.go
├── text_override.templ
└── text_override_templ.go
```
7 blocks, 7 schemas, 4 page templates, 4 built-in overrides, 1 email wrapper, 3 master pages, 2 presets. brutalist.so 21.5 MB. Safety check exit 0.