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

9.5 KiB
Raw Blame History

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.