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

10 KiB
Raw Permalink Blame History

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

$ 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

$ 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