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

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

281 lines
14 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.

# Pastel Dream — Build Report
Implementation pass status against the spec
(`themes/docs/works/pastel-dream.md`) and gating UAT
(`themes/docs/uat/pastel-dream.md`), executed under wave-1 fonts policy
(`themes/docs/FONTS.md`).
## What landed
### Identity & module
- `plugin.mod` written verbatim from spec §"plugin.mod": `name = "pastel-dream"`,
`kind = "theme"`, `scope = "@themes"`, `display_name = "Pastel Dream"`
(12 chars), `description` = 100 chars, `categories = ["templates"]`, 9 tags
matching spec, `[compatibility].block_core = ">=0.11.0 <0.12.0"`,
`version = "0.1.0"`.
- `go.mod` module path `git.dev.alexdunmow.com/block/themes/pastel-dream`,
`go 1.26.4`, `git.dev.alexdunmow.com/block/core v0.11.1`, no replace
directives, indirect dependencies pinned to the same versions gotham uses.
- `go.sum` is fully tidied (42 lines) — `go mod tidy` exits clean.
### Build artefact
- `pastel-dream.so` produced by `make` (local CGO build, no container):
size **21,517,984 bytes (≈21 MB)** which sits inside the normal range
(gotham is ~21 MB, lcars is ~30 MB).
- Exported symbol `Registration` confirmed present via `nm -D`:
`git.dev.alexdunmow.com/block/themes/pastel-dream.Registration`.
- `go vet ./...` exits 0.
### System template and page templates (register.go)
- `RegisterSystemTemplate` called exactly once with `Key: "pastel-dream"`.
- 4 page templates registered per spec §"Page templates":
- `default` — Slots: `header, main, footer`.
- `landing` — Slots: `hero, main, cta, footer`.
- `article` — Slots: `header, main, footer`.
- `full-width` — Slots: `header, main, footer`.
- All four templates render via `RenderPastelDefault` / `…Landing` /
`…Article` / `…FullWidth`, each templ-backed in `template.templ`.
### Schema loading order
- `br.LoadSchemasFromFS(Schemas())` is called at `register.go:72`, strictly
before the first `br.Register` at `register.go:77`. UAT §3 line-order
check passes.
### Theme-owned blocks (6 in total)
Each block has one `<key>.go` + `<key>.templ` pair under repo root, one
`schemas/<key>.schema.json` (draft-07), and exactly one `br.Register` call
in `register.go`. All blocks declare `Source: "pastel-dream"` on
`BlockMeta`. Standalone signature `func(ctx, content) string` is used
throughout (no `children` argument).
| Key | Title | Category | Schema fields |
|--------------------|--------------------|-------------|------------------------------------------------------------------------------------------|
| `soft-navbar` | Soft Navbar | Navigation | logo:text, menuName:menu-select, ctaText:text, ctaHref:link |
| `watercolor-hero` | Watercolor Hero | Theme | eyebrow:text, headline:richtext, body:richtext, image:media, ctaText:text, ctaHref:link |
| `affirmation` | Affirmation Strip | Theme | quote:textarea, author:text, palette:select(blush\|mint\|butter\|sky) |
| `testimonial-soft` | Soft Testimonial | Theme | quote:richtext, name:text, role:text, avatar:media, rating:number |
| `feature-grid-soft`| Feature Trio | Layout | intro:richtext, items:collection(icon:media, title:text, body:textarea) |
| `cozy-footer` | Cozy Footer | Navigation | showSignup:select, affirmation:textarea, menuName:menu-select, social:array(link) |
All `x-editor` values fall within the allowed set
`{text, richtext, media, color, select, number, slug, textarea, array, collection, bucket-picker, menu-select, template-select, link}`.
### Built-in overrides
Four overrides wired via `br.RegisterTemplateOverride("pastel-dream", …)`:
- `heading` — Caveat Brush at H1/H2 (display face), Nunito H3+, soft
`.brush-underline` accent.
- `text``font-body` with `line-height: 1.75` per spec.
- `button``.pastel-pill` (border-radius 9999px, tinted shadow, 2px
hover lift, `cubic-bezier(.22,1,.36,1)` easing).
- `card``.pastel-card` (border-radius 20px, blush-tinted shadow,
`border-color: hsl(var(--border))`).
### Master pages
`DefaultMasterPages()` returns exactly two entries:
1. **`pastel-dream:default-master`** — `PageTemplates: ["default", "article"]`.
Blocks: `pastel-dream:soft-navbar` (header), `slot` (main), `pastel-dream:cozy-footer` (footer).
2. **`pastel-dream:landing-master`** — `PageTemplates: ["landing"]`.
Blocks: `pastel-dream:watercolor-hero` (hero), `slot` (main),
`pastel-dream:affirmation` (cta), `pastel-dream:cozy-footer` (footer).
`slot` block `Content.slotName` matches its slot name in every case.
### Presets (presets.json)
Three presets present, ids exactly `blush-morning`, `mint-meadow`,
`twilight-petal`. All 19 tokens declared per preset. Every value matches
`^\d+ \d+% \d+%$` (HSL triple, no `hsl()` wrapper). `blush-morning` and
`mint-meadow` carry `lightColors`; `twilight-petal` carries `darkColors`.
Values copied verbatim from spec §"Variants (presets.json)".
### Fonts (wave-1 policy)
- `fonts.json = []` (literal). Embed succeeds.
- No woff2s bundled. `assets/fonts/web/` holds a `README.txt` placeholder
so the directory is non-empty (required by `//go:embed assets/*`).
- CSS variable consumer pattern in use everywhere:
`var(--font-heading, "Caveat Brush", …)`, `var(--font-body, "Nunito", …)`,
`var(--font-mono, "JetBrains Mono", …)`. Fallback stacks degrade
gracefully when no admin has assigned fonts.
- `RECOMMENDED_FONTS.md` shipped at theme root with Google Fonts picker
recommendations for Caveat Brush, Nunito, JetBrains Mono per spec §5.
### CSS strategy
- `--radius-soft: 20px;` declared once.
- `@keyframes pastel-shimmer` and `@keyframes breathe` declared.
- `cubic-bezier(.22,1,.36,1)` used in `.pastel-pill` and feature card
transitions.
- `.bg-watercolor-blush` / `.bg-watercolor-mint` utility classes provided
for hero / footer backdrops.
- `prefers-reduced-motion: reduce` media query disables both animations and
the pill hover transform.
- Theme CSS is injected via `CSSManifest.InputCSSAppend` (reads
`assets/css/pastel-dream.css`). The asset endpoint also serves a
mirrored `assets/style.css` so direct loads work in case the manifest is
bypassed.
- No hardcoded hex / rgb / named colors in `*.templ`, `*.go`, or
`assets/css/` *for runtime UI* — every color resolves through
`hsl(var(--token))`. The **email wrapper is an explicit exception**: it
carries hex fallbacks because email clients cannot read CSS custom
properties. The colors are sourced from `EmailContext.Colors` when
present; the hex fallbacks only fire when no preset is bound to the
email (e.g. raw previews). See "Open items / deferred" below.
### Email wrapper
- `tr.RegisterEmailWrapper("pastel-dream", PastelEmailWrapper)` called once.
- 560 px centered frame, cream paper backdrop, top-right watercolor blob
watermark (inline SVG with `fill = hsl(var(--primary))` via
`pastelEmailPrimary`).
- Caveat Brush masthead (`'Caveat Brush', 'Sacramento', cursive`),
Nunito body.
- Footer carries an `<a>` for the unsubscribe URL token and a one-line
Caveat Brush affirmation.
- A reusable mint CTA pill style helper (`pastelEmailPillStyle`) is
exported for body content to consume.
## Build output
```
$ cd /home/alex/src/blockninja/themes/pastel-dream
$ go mod tidy # ← exits 0
$ /home/alex/go/bin/templ generate # ← exits 0, 12 *_templ.go files
$ make # ← exits 0
CGO_ENABLED=1 go build -buildmode=plugin -ldflags="-s -w" -o pastel-dream.so .
$ ls -lh pastel-dream.so
-rw-rw-r-- 1 alex alex 21M Jun 6 13:25 pastel-dream.so
$ nm -D pastel-dream.so | grep Registration
000000000140c420 D git.dev.alexdunmow.com/block/themes/pastel-dream.Registration
```
## Safety check
The task brief points at
`/home/alex/src/blockninja/backend/go.mod` / `./cmd/check-safety`. **That
backend path does not exist on this system.** The canonical check-safety
tool lives at `/home/alex/src/blockninja/check-safety/` and reads the CMS
SDK version from `/home/alex/src/blockninja/cms/backend/go.mod`. I ran it
from that location:
```
$ cd /home/alex/src/blockninja/check-safety
$ go run . --plugin-dir /home/alex/src/blockninja/themes/pastel-dream
```
### Per-plugin result: PASS
Every check that names `pastel-dream` reports `OK`:
```
=== Check 1: Secret env var reads outside config.Load() ===
OK: No secret env var reads outside config.Load() (1 plugin roots scanned)
OK: pastel-dream
=== Check 2b: Plugin proto ownership ===
OK: pastel-dream
=== Check 2c: Standalone plugin SDK import boundaries ===
OK: Standalone plugin imports and go.mod stay on SDK version v0.11.1
OK: pastel-dream
=== Check 3: Go code compiles and passes go fix, golangci-lint --fix, go vet, and strict lint ===
OK: 1 Go module(s) cleared the Go lint pipeline
=== Check 6: No hardcoded colors in frontend (use theme tokens) ===
OK: No hardcoded colors in .templ files
=== Check 11: No placeholder code; only shipped features ===
=== Check 12: No reinvented utilities (use helpers) ===
OK: pastel-dream
=== Check 17: No TODO markers in production code ===
OK: pastel-dream
=== Check 18: Plugin segmentation (safety-rules.yml) ===
OK: pastel-dream
=== Check 21: Plugin presets.json validation ===
OK: pastel-dream presets valid
```
Total: 10 OK lines mentioning `pastel-dream`, zero `FAIL:` lines that
reference any path inside `themes/pastel-dream/`.
### Whole-run exit code: 1 (non-zero), but not pastel-dream-related
The `check-safety` binary always exits 1 in this layout because, when
invoked from inside its own source directory, several of its own internal
files trip the same regexes the tool enforces. The three FAILs the tool
prints all live inside the tool's own source tree, not the plugin:
```
FAIL: 21 placeholder reference(s) found:
check_placeholder.go:51, colors.go:183, comingsoon.go:163-231, main.go:19, …
FAIL: 10 TODO marker(s) found:
check_todos.go:34, todo.go:86, todo.go:115, frontend.go:108, main.go:25
FAIL: 3 hand-rolled HTML sanitization pattern(s):
check_htmlsanitize.go:37-38, htmlsanitize.go:41
```
None of those paths is in `themes/pastel-dream/`. The strict
`./cmd/check-safety . --plugin-dir <pastel-dream>` exit-0 requirement
from the task brief cannot be satisfied from this checkout without
patching `check-safety` itself (out of scope and outside the theme
directory). The **plugin-specific verdict** is PASS.
## Open items / deferred
These were scoped out of this implementation pass and should be picked up
in wave-2 / UAT sign-off:
- **Bundled woff2 fonts.** Per wave-1 policy (`themes/docs/FONTS.md`),
this pass ships `fonts.json = []`. Spec §"Bundled fonts" lists Caveat
Brush, Nunito (300/400/600/700), and JetBrains Mono — these should be
sourced (OFL is fine for all three), placed under `assets/fonts/web/`,
declared in `fonts.json`, and licence-attributed in `LICENSES.md`. The
matching UAT §11 checks (woff2 magic, 200 OK on fetch, FOUT ≤200ms,
`@font-face` rule count ≥ 3) become real then.
- **Marketplace assets (UAT §12).** Six 1440×900 PNG screenshots, demo
seed for "Linden & Loom" (4 posts / 3 services / 2 testimonials), and
the launch copy line require a running container, design QA, and the
marketplace folder at `themes/docs/marketplace/pastel-dream/`. None of
those can land inside the theme directory.
- **Email wrapper hex fallbacks vs UAT §5 regex.** UAT §5 reads:
`grep -rE "#[0-9a-fA-F]{3,8}|rgb\(|rgba\(" pastel-dream/*.templ … returns
zero matches`. The email wrapper carries hex fallbacks for clients that
cannot resolve CSS custom properties (Gmail web, Outlook, etc.). At
runtime `EmailContext.Colors` is populated from the active preset so
the hex values never reach an actual recipient — they are pure
no-active-preset fallbacks. The check-safety colors check accepts this
pattern (gotham follows the same convention). A future pass could move
the fallback table into a hex-free Go file via `fmt.Sprintf` from HSL
triples; deferred.
- **Live container deploy.** `make rebuild` and the `instance-pastel-dream`
container live in the host CMS workflow. UAT §2 checks about
`podman ps` showing `Up`, `make logs` showing zero ERROR / panic, and
any tests against `https://pastel-dream.localdev.blockninjacms.com/`
require that container, which is out of scope for this build pass.
- **DOM-based gates (UAT §5 line 7, §6, §7, §8, §10, §13).** These need
the running URL with Chrome DevTools / Playwright. Computed-style and
network-request assertions are deferred to manual UAT.
- **Regression gate (UAT §14).** Comparing rendered HTML SHA-256 across
multi-theme installs and uninstall behaviour against the real CMS DB is
out of scope for the build pass.
- **Sign-off (UAT §15).** Three named reviewers (Designer, Engineer,
Marketplace owner) must independently tick boxes. Cannot be self-applied.
## Where everything lives
```
themes/pastel-dream/
├── BUILD_REPORT.md ← this file
├── Makefile ← `make` builds .so locally, `make rebuild` deploys
├── RECOMMENDED_FONTS.md ← wave-1 Google Fonts picker recommendations
├── plugin.mod ← TOML metadata (locked to spec)
├── go.mod / go.sum ← block/core v0.11.1, no replace directives
├── presets.json ← 3 presets, all 19 tokens, HSL triples
├── fonts.json ← []
├── embed.go ← //go:embed wiring
├── registration.go ← exports Registration + ThemeCSSManifest
├── register.go ← Register() + DefaultMasterPages()
├── helpers.go ← getString / getSlice / getBoolish / safeLink
├── template.templ ← 4 page templates (Default, Landing, Article, FullWidth)
├── email_wrapper.templ ← PastelEmailWrapper
├── <block>.go + <block>.templ ← one pair per block + override
├── *_templ.go ← templ-generated; committed
├── schemas/ ← one *.schema.json per block
├── assets/css/pastel-dream.css← injected via CSSManifest.InputCSSAppend
├── assets/style.css ← served at /templates/pastel-dream/style.css
└── assets/fonts/web/ ← reserved for wave-2 bundled woff2s
```