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

8.7 KiB
Raw Blame History

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, articlenavbar (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-widthnavbar (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}