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

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

169 lines
8.7 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.

# Art Deco — Build Report
Status: **SHIPPED**.
Compiled `.so` produced and the safety check exits 0.
## What landed
### Plugin metadata (`plugin.mod`)
- `kind = "theme"`, `scope = "@themes"`, `version = "0.1.0"`.
- `categories = ["templates"]` (single whitelist entry).
- `tags = ["luxury", "gold", "hospitality", "hotel", "wedding", "jewellery", "vintage", "glam", "fine-dining"]` (9 tags, within the 59 UAT range).
- `[compatibility].block_core = ">=0.11.0 <0.12.0"`.
### System & page templates
- System template registered with `Key: "art-deco"`.
- 4 page templates registered:
- `default` → slots `header, main, footer`.
- `landing` → slots `marquee, main, cta, footer`.
- `article` → slots `header, main, aside, footer`.
- `full-width` → slots `header, main, footer`.
### Blocks (7, all `Source: "art-deco"`)
| Key | Schema | Notes |
|---|---|---|
| `reservation` | `schemas/reservation.schema.json` | Sticky gold-on-black strip with phone, OpenTable CTA, hours. |
| `menu` | `schemas/menu.schema.json` | Symmetric menu card with rich-text course descriptions and prices. |
| `gallery_fan` | `schemas/gallery_fan.schema.json` | Mirrored gallery framed by sunburst, 2/3/4 columns, collapses to 1 col <640px. |
| `divider_fan` | `schemas/divider_fan.schema.json` | Pure-CSS sunburst / scallop / ziggurat divider falls back to sunburst on unknown variant. |
| `footer` | `schemas/footer.schema.json` | Centered footer with gold rule, address, reservations email, social row, menu name. |
| `marquee_hero` | `schemas/marquee_hero.schema.json` | Wide hero with stepped ziggurat frame and sunburst overlay. |
| `press_quote` | `schemas/press_quote.schema.json` | Italic Cormorant pull-quote with stars (clamped 05). |
Schemas are loaded BEFORE any `br.Register(...)` call (line ordering enforced in `register.go`).
### Template overrides (4)
- `heading` caps tracking + hairline gold underline, Italiana display family.
- `text` Cormorant body with italic drop-cap on first paragraph.
- `button` black pill with gold border, stamped inset shadow on hover/focus.
- `image` stepped ziggurat frame variant (also scallop / plain).
### Email wrapper
- `tr.RegisterEmailWrapper("art-deco", ArtDecoEmailWrapper)` wired.
- 600px centered column, ivory body, jet-black masthead.
- Masthead uses a flat-gold SVG `<img>` (not a CSS gradient) so Outlook renders it correctly.
- Reservation phone / `SupportEmail` auto-injected as italic postscript line.
### Master pages (2)
- `art-deco:default-master` covers `default`, `article` `navbar` (header,0), `art-deco:divider_fan` (header,1,sunburst), `slot` (main,0,slotName=main), `art-deco:footer` (footer,0).
- `art-deco:marquee-master` covers `landing`, `full-width` `navbar` (marquee,0), `art-deco:reservation` (marquee,1), `slot` (main,0,slotName=main), `art-deco:divider_fan` (cta,0,scallop), `art-deco:footer` (footer,0).
### Presets (`presets.json`, 3)
- `champagne-noir` (`mode: both`, lightColors + darkColors, all 19 tokens each).
- `ivory-rose` (`mode: light`, lightColors only, all 19 tokens).
- `velvet-onyx` (`mode: dark`, darkColors only, all 19 tokens).
- Every value is an HSL triple `H S% L%` no `hsl(...)` wrappers, no hex.
### CSS manifest
- `CSSManifest.InputCSSAppend` registered via `ThemeCSSManifest()` in `embed.go`/`registration.go`.
- The injected CSS declares `@layer utilities` blocks for `.deco-rule`, `.deco-frame`, `.deco-step`, `.deco-grain`, plus aesthetic helpers `.deco-foil`, `.deco-sunburst`, `.deco-scallop`, `.deco-ziggurat`, `.deco-caps`, `.deco-dropcap`, `.deco-underline`, `.deco-pill`, `.deco-link`.
- All colour values consume `hsl(var(--token))`; all `font-family` declarations go through `var(--font-heading|body|mono, <fallback>)`.
### Fonts policy (Wave-1)
- `fonts.json = []` (no bundled woff2s in this pass).
- `RECOMMENDED_FONTS.md` lists the spec §5 fonts as Google Fonts picker recommendations (Italiana, Cormorant Garamond, JetBrains Mono).
- Fallback stacks for `--font-heading`, `--font-body`, `--font-mono` are embedded inline in every template + utility.
## Build output
```
$ cd ~/src/blockninja/themes/art-deco && go mod tidy
(no output — clean)
$ /home/alex/go/bin/templ generate
(✓) Complete [ updates=13 duration=… ]
$ make
CGO_ENABLED=1 go build -buildmode=plugin -ldflags="-s -w" -o art-deco.so .
$ ls -lh art-deco.so
-rw-rw-r-- 1 alex alex 21M art-deco.so
```
## Safety check
```
$ cd ~/src/blockninja/check-safety && go run . ~/src/blockninja/themes/art-deco \
--plugin-dir ~/src/blockninja/themes/art-deco
=== Check 2c: Standalone plugin SDK import boundaries ===
OK: Standalone plugin imports and go.mod stay on SDK version v0.11.1
=== Check 3: Go code compiles and passes go fix, golangci-lint --fix, go vet, and strict lint ===
OK: Go lint pipeline clean for 1 module(s)
=== 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 ===
OK: No placeholder code found
exit=0
```
Only WARN remaining is Check 2e (`any` usage in `content map[string]any` signatures). This is the
exact same warning Gotham emits it is the standalone-plugin block signature contract and is
non-blocking.
## Open items / deferred
### Wave-1 deferrals (intentional, per `themes/docs/FONTS.md`)
- **No bundled woff2 fonts**. Theme ships `fonts.json = []` and a `RECOMMENDED_FONTS.md` for the
admin to pick Italiana / Cormorant Garamond / JetBrains Mono from the Google Fonts tab.
- **No `LICENSES.md`** nothing is bundled.
### Wave-2 follow-ups (out of scope for this pass)
- Commission a redistributable Art Deco display face (Italiana variants are limited) and bundle
via `fonts.json` if Italiana proves brittle at retina sizes. UAT §11 FOUT timing and the licence
audit only become checkable once a face is bundled.
- Capture the 6 UAT marketplace screenshots (`docs/uat/art-deco-shots/01..06.png`). Requires
a running CMS instance outside the autonomous build scope.
- Seed the "Maison Étoile" demo content (6 pages, 4 courses, 1 press quote, 9 gallery images).
Same constraint as above requires the instance container.
- Run the UAT browser-side gates: WCAG contrast pairs, focus-visible ring sweep, viewport
scroll-width checks at 360/768/1024/1440. The CSS is wired to satisfy them but the verification
is an instance-side workflow.
- Master-page UAT cell "Replacing `art-deco:footer` on `default-master` in admin produces a
ghost-free re-render" admin-side verification, not a build-side check.
### Not implemented (called out by spec but explicitly listed in BUILD_REPORT)
- `make rebuild` and the container deploy targets by build-instructions, `make rebuild` is
off-limits to the autonomous build pass. The Makefile here ships only `all`, `templ`, `clean`,
`help` production deploy is reserved for the gotham-style upstream Makefile and a human run.
- Real woff2 bundles in `assets/fonts/` deferred to Wave-2 per FONTS.md.
## File inventory
```
art-deco/
├── BUILD_REPORT.md ← this file
├── Makefile ← local-only build (make, make templ, make clean)
├── RECOMMENDED_FONTS.md ← Wave-1 Google Fonts picker guide
├── assets/style.css ← @layer utilities CSS (foil, frame, fan, grain, pill…)
├── button_override.{go,templ,_templ.go}
├── divider_fan.{go,templ,_templ.go}
├── email_wrapper.{templ,_templ.go}
├── embed.go ← //go:embed directives + CSSManifest hook
├── fonts.json ← []
├── footer.{go,templ,_templ.go}
├── gallery_fan.{go,templ,_templ.go}
├── go.{mod,sum} ← block/core v0.11.1, no replace directives
├── heading_override.{go,templ,_templ.go}
├── helpers.go ← getString / getInt / getSlice / getStringSlice / clampInt
├── image_override.{go,templ,_templ.go}
├── marquee_hero.{go,templ,_templ.go}
├── menu.{go,templ,_templ.go}
├── plugin.mod ← TOML metadata, kind=theme
├── presets.json ← 3 presets, 19 tokens each, HSL triples
├── press_quote.{go,templ,_templ.go}
├── press_quote_helpers.go
├── register.go ← Register(tr,br) + DefaultMasterPages()
├── registration.go ← var Registration plugin.PluginRegistration
├── reservation.{go,templ,_templ.go}
├── schemas/
│ ├── divider_fan.schema.json
│ ├── footer.schema.json
│ ├── gallery_fan.schema.json
│ ├── marquee_hero.schema.json
│ ├── menu.schema.json
│ ├── press_quote.schema.json
│ └── reservation.schema.json
├── template.templ + template_templ.go
└── text_override.{go,templ,_templ.go}
```