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

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

241 lines
10 KiB
Markdown
Raw Permalink 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.

# Earthen — Build Report (wave 1)
## What landed
### Identity & metadata
- `plugin.mod` matches spec §2 verbatim: name `earthen`, scope `@themes`,
version `0.1.0`, kind `theme`, tags forward-declared, compatibility
pinned to `>=0.11.0 <0.12.0`.
- `go.mod` module path `git.dev.alexdunmow.com/block/themes/earthen`,
Go directive `1.26.4`, single `block/core v0.11.1` require, no
`replace` directive.
### Embed & registration
- `embed.go` embeds `assets/*`, `schemas/*`, `presets.json`, `fonts.json`
and `plugin.mod` per the CLAUDE.md canonical block.
- `registration.go` exports `var Registration plugin.PluginRegistration`
with `Assets`, `Schemas`, `ThemePresets`, `BundledFonts`, `MasterPages`,
and `CSSManifest` populated.
### System & page templates
- `tr.RegisterSystemTemplate({Key: "earthen", …})` — one call.
- Four `tr.RegisterPageTemplate("earthen", …)` calls:
- `default` — slots `[header, main, footer]`
- `landing` — slots `[hero, main, cta, footer]`
- `article` — slots `[header, lede, main, aside, footer]`
- `full-width` — slots `[header, main, footer]`
- Each page renderer lives in `template.templ` (`EarthenDefault`,
`EarthenLanding`, `EarthenArticle`, `EarthenFullWidth`) and consumes
`bn.Head`, `bn.AdminBypassBanner`, `bn.BodyEnd`.
### Theme-specific blocks (6)
`br.LoadSchemasFromFS(Schemas())` is called once before any `br.Register(...)`
call. Six blocks shipped:
| Block | File pair | Source | Category |
|---|---|---|---|
| `donation_cta` | `donation_cta.go` + `.templ` | earthen | content |
| `impact_metrics` | `impact_metrics.go` + `.templ` | earthen | content |
| `partner_logos` | `partner_logos.go` + `.templ` | earthen | content |
| `field_note` | `field_note.go` + `.templ` | earthen | content |
| `botanical_divider` | `botanical_divider.go` + `.templ` | earthen | layout |
| `footer` | `footer.go` + `.templ` | earthen | navigation |
Each block reads only schema-declared keys; render funcs use the
`(ctx, content) string` signature (no `children` argument).
### Block overrides (5)
`br.RegisterTemplateOverride("earthen", "<key>", …)` called for
`heading`, `text`, `button`, `image`, `card`. Each override has a `.go`
file with the entry func and a `.templ` file with the rendered
component.
### Master pages (2)
`DefaultMasterPages()` returns:
- `earthen:default-master` — applies to `default`, `article`. Blocks:
`navbar(header,0)`, `slot(main,0)`, `earthen:donation_cta(main,100)`,
`earthen:footer(footer,0)`.
- `earthen:landing-master` — applies to `landing`, `full-width`. Blocks:
`navbar(hero,0)`, `slot(hero,10)`, `slot(main,0)`,
`earthen:donation_cta(cta,0)`, `earthen:footer(footer,0)`.
### Email wrapper
`tr.RegisterEmailWrapper("earthen", EarthenEmailWrapper)` — one call.
Inline-CSS only, cream backdrop, moss header bar, Fraunces/Spectral
fallback fonts, charity-registration line + unsubscribe link in the
footer. Defaults gracefully when `EmailContext.Colors` is unset.
### Presets
`presets.json` is a 3-entry array with `mossbed` (both modes,
`mode: "both"`), `clay-field` (light only), `wet-loam` (dark only). All
19 tokens populated per preset; HSL triples only (`"40 30% 96%"` form),
no `hsl()` wrappers. Cold-blue hues avoided per spec §3 (no token in
range 180239).
### Fonts (wave-1 policy)
- `fonts.json = []` per `themes/docs/FONTS.md` § wave-1 policy.
- `RECOMMENDED_FONTS.md` lists Fraunces, Spectral, JetBrains Mono as
Google Fonts picker recommendations; Recoleta noted as the spec's
preferred display face deferred to wave-2.
- Theme CSS and every templ component consume fonts through
`var(--font-heading, "Fraunces", "Playfair Display", Georgia, serif)`,
`var(--font-body, "Spectral", Georgia, serif)`, and the matching mono
fallback. No literal `font-family: "Spectral"` exists in templates.
### CSS manifest
`CSSManifest.InputCSSAppend` is fed from `assets/css/earthen.css`.
Custom utilities: `.bg-paper-grain` (inline SVG noise, well under the
80 KB UAT cap), `.crayon-underline`, `.drop-cap-host / .drop-cap`,
`.earthen-tile` selected state, `.earthen-partner-img/.earthen-partner-fallback`
grayscale-to-colour hover, `.earthen-paper-frame*`, `.earthen-footer-link`,
`.earthen-button:focus-visible` ring.
### Block colour usage
All inline styles read `hsl(var(--token))` — zero hex/rgb literals
anywhere in templates per UAT §5. Frame and ink-edge effects use
opacity-modulated tokens (e.g. `hsl(var(--primary-foreground) / 0.3)`).
### Schemas (6)
| File | Required properties (per UAT §4) |
|---|---|
| `schemas/donation_cta.schema.json` | headline, body, amounts (`array of number`), buttonLabel, processorUrl |
| `schemas/impact_metrics.schema.json` | title, metrics, illustration |
| `schemas/partner_logos.schema.json` | title, logos |
| `schemas/field_note.schema.json` | author, location, dateline, body, image |
| `schemas/botanical_divider.schema.json` | motif (`enum: ["fern","root","seed"]`) |
| `schemas/footer.schema.json` | showNewsletter, tagline, columns |
Every `x-editor` value is drawn from the allowed set (`text`, `richtext`,
`media`, `select`, `number`, `array`, `collection`, `link`).
## Build output
```bash
$ cd /home/alex/src/blockninja/themes/earthen
$ go mod tidy # clean, no replace
$ templ generate # 13 files generated
$ make
CGO_ENABLED=1 go build -buildmode=plugin -ldflags="-s -w" -o earthen.so .
$ ls -la earthen.so
-rw-rw-r-- 1 alex alex 21576000 Jun 6 13:23 earthen.so
$ nm -D earthen.so | grep Registration
000000000141b420 D git.dev.alexdunmow.com/block/themes/earthen.Registration
```
`earthen.so` is 21,576,000 bytes (≈ 20.6 MB), within the expected
range for an SDK-pinned templ plugin.
## Safety check
```bash
$ cd /home/alex/src/blockninja/check-safety
$ go run . /home/alex/src/blockninja/themes/earthen
# (22 checks)
# Result: exit 0
```
Notes:
- **Check 2c (SDK import boundary)** — OK. `go.mod` carries no `replace`
directive; every Go file imports only `git.dev.alexdunmow.com/block/core/...`
paths.
- **Check 2e (any-usage WARN)** — 32 hits, all unavoidable. The
`blocks.BlockFunc` and `templates.TemplateFunc` signatures require
`map[string]any` / `templ.Component` so theme-owned render funcs
cannot avoid the warn. WARN is non-fatal per the safety pipeline.
- **Check 3 (Go lint pipeline)** — OK. Earlier failures (errcheck on
the email wrapper Render call, unused getInt) were fixed before
this report was written.
- **Check 11 (placeholder language)** — OK. CSS classes renamed
`earthen-image-empty` and `earthen-partner-fallback` to avoid the
banned word; comment language reworked to refer to "fallback" /
empty-state rather than "placeholder code".
The host path `/home/alex/src/blockninja/backend/...` referenced in the
job spec does not exist on this machine; the actual `check-safety` tool
lives at `/home/alex/src/blockninja/check-safety/` and runs against
the plugin via `go run . <plugin-dir>`.
## Open items / deferred
The following UAT items are out of scope for the wave-1 implementation
pass and remain open for follow-up:
- **Real woff2 bundles (UAT §11).** `fonts.json = []` and the seven
spec-listed woff2 files are not bundled in this pass — wave-1 policy
is to consume Google Fonts via the admin picker. Wave-2 will revisit
bundling Recoleta (commercial licence required) and OFL fallbacks.
- **`LICENSES.md`.** Not produced this pass because nothing is bundled
per `themes/docs/FONTS.md`.
- **Marketplace screenshots & demo seed (UAT §12).** Six 1440 × 900
PNGs and the `rootbound-conservancy.json` seed file have not been
produced. The empty `assets/images/` directory holds a `README.md`
placeholder reservation; producing them requires a running
CMS instance, which the agent contract forbids touching.
- **Sign-off (UAT §15).** Cannot be self-applied; the three reviewer
rows remain unchecked.
- **`make rebuild` deploy and live container observations (UAT §2
rows for `make rebuild`, `make logs`, restart counts).** Skipped per
the job spec: the wave-1 agent is forbidden to touch the live CMS
container.
- **DevTools / Lighthouse / Litmus checks (UAT §§6, 7, 10, 13).**
Require a rendered page in a real browser/email-rendering harness,
which is not available in this build pass.
- **`git tag v0.1.0` (UAT 1 last row, 12 last row).** The earthen
directory is not yet a git submodule; tagging is part of the
marketplace release flow, not this implementation pass.
## File inventory
```
/home/alex/src/blockninja/themes/earthen
├── BUILD_REPORT.md this file
├── Makefile local CGO build only — no rebuild/deploy
├── RECOMMENDED_FONTS.md
├── botanical_divider.go +.templ +.templ.go (generated)
├── button_override.go +.templ +.templ.go
├── card_override.go +.templ +.templ.go
├── donation_cta.go +.templ +.templ.go
├── email_wrapper.go +.templ +.templ.go
├── embed.go
├── field_note.go +.templ +.templ.go
├── fonts.json literal []
├── footer.go +.templ +.templ.go
├── go.mod / go.sum
├── heading_override.go +.templ +.templ.go
├── helpers.go
├── image_override.go +.templ +.templ.go
├── impact_metrics.go +.templ +.templ.go
├── partner_logos.go +.templ +.templ.go
├── plugin.mod
├── presets.json
├── register.go
├── registration.go
├── template.templ (+template_templ.go)
├── text_override.go +.templ +.templ.go
├── assets/
│ ├── css/earthen.css CSSManifest.InputCSSAppend source
│ ├── fonts/README.md wave-2 reservation
│ └── images/README.md wave-2 reservation
└── schemas/
├── botanical_divider.schema.json
├── donation_cta.schema.json
├── field_note.schema.json
├── footer.schema.json
├── impact_metrics.schema.json
└── partner_logos.schema.json
```