diff --git a/PORT-NOTES.md b/PORT-NOTES.md new file mode 100644 index 0000000..cdb294a --- /dev/null +++ b/PORT-NOTES.md @@ -0,0 +1,74 @@ +# Gotham — templ → codeless `.bnp` port notes + +This theme was ported from the bundled Go/templ `gotham` plugin (source kept in +`port-source/` for reference) to a **codeless** `.bnp` (no Go, no `plugin.wasm`), +matching the `art-deco` reference theme's structure. Every block, page template, +override and the email wrapper is now a host-rendered `.ninjatpl`. + +## What maps 1:1 (faithful, no behavior change) + +| Source (templ) | Codeless equivalent | Notes | +|---|---|---| +| `RenderGotham` (default) | `templates/gotham/default.ninjatpl` | `bn.Head`/`bn.BodyEnd`/`bn.AdminBypassBanner` replaced by host-injected `{{ head_html }}` / `{{ body_end_html }}` / `{{ admin_banner_html }}`. `` hardcoded exactly as source (Gotham is always dark). | +| `RenderGothamLanding` | `templates/gotham/landing.ninjatpl` | hero/main/cta/footer slots preserved. | +| `RenderGothamFullWidth` | `templates/gotham/full-width.ninjatpl` | | +| `RenderGothamCentered` | `templates/gotham/centered.ninjatpl` | | +| `FeaturesBlock` + `featuresComponent` | `blocks/features.ninjatpl` | Inline icon SVG switch (chart/users/clock/star/default) ported verbatim as `{% if %}` chains. `columns` grid logic preserved. | +| `FooterBlock` + `footerComponent` | `blocks/footer.ninjatpl` | Column grid + link-URL logic preserved (see PageID note below). | +| `StatsBlock` + `statsComponent` / `statsEmptyComponent` | `blocks/stats.ninjatpl` | Uses the `items` content array (the only path `StatsBlock` ever took). Empty-state placeholder preserved. | +| `GothamHeadingBlock` override | `templates/overrides/gotham/heading.ninjatpl` | Per-level base classes + `text-accent` preserved. | +| `GothamTextBlock` override | `templates/overrides/gotham/text.ninjatpl` | `prose prose-invert prose-amber` preserved. | +| `GothamEmailWrapper` | `templates/email/gotham.ninjatpl` | Var names (`site_name`, `site_url`, `colors.background/card/foreground/muted/mutedForeground/border/primary`, `preview_text`, `logo_url`, `unsubscribe_url`) match the host email-context contract — identical to the `art-deco` reference wrapper. | +| `assets/style.css` | `assets/style.css` | Byte-identical (`gotham-accent`, `gotham-accent-bg`, `gotham-card`, `gotham-glow`, …). Host serves it as a static and links it via `head_html`. | +| `DefaultMasterPages()` | `master_pages.json` | `gotham:default-master` with navbar / slot / `gotham:footer`. | +| `presets.json`, `fonts.json` | unchanged | Carried over as manifest JSON refs. | + +## Behavior gaps / decisions the parent should review + +1. **`hero` block was NOT registered in the source.** `port-source/hero.go` / + `hero.templ` / `schemas/hero.schema.json` exist, but `register.go` never calls + `br.Register(HeroBlockMeta, ...)`, and `HeroBlock` has the CMS-internal + builtin signature `(ctx, content, _ []string)` — it was **dead code**. Because + the task explicitly asked for a hero port and the `landing` template exposes a + `hero` slot, I ported it faithfully to `blocks/hero.ninjatpl` + + `hero.schema.json` and registered it as `gotham:hero`. **If the parent wants to + match the original registered surface exactly, delete those two files and the + `gotham:hero` entry in `blocks/blocks.yaml`** — nothing else references it. + +2. **`stat_item` block dropped (intentionally).** `port-source/stat_item.go` + (`StatItemBlockMeta`, `Hidden: true`) and `statItemComponent` were also never + registered in `register.go`, and `StatsBlock` renders its items inline (never + via a `stat_item` child block). The child-block container path + (`statsContainerComponent` / `gridCols(childCount)`) was likewise unused. No + codeless equivalent was created; the inline stats rendering is fully preserved. + +3. **Footer internal-page links are a placeholder, same as source.** The templ + `getLinkURL` emitted `/pages/` for a link with `page_id` set, with a + source comment "In production, this would be resolved to the actual page + slug." The codeless template reproduces `/pages/{{ link.page_id }}` verbatim — + it does **not** resolve `page_id` → real slug (the codeless block declares no + data providers, and the source didn't resolve it either). Behavior is + identical to the original; flagging only because it was already a known + limitation. + +4. **No computing logic required code.** All templ helper functions + (`gridCols`, `featureGridCols`, `footerGridCols`, `gothamHeadingBaseClass`, + `parseHeadingLevel`, the email `gotham*Color` fallbacks) are pure + presentational branching and were fully expressed with `{% if %}` / + `|default:` in ninjatpl. Nothing in the source needed a wasm guest, so the + artifact is genuinely codeless. + +## Not ported (obsolete) + +`port-source/` also contains `embed.go`, `registration.go`, `register.go`, +`text_override.go`, `heading_override.go` — Go glue for the bundled/`.so` era. +None have a codeless equivalent (their concerns are now declarations in +`manifest.yaml` / `blocks.yaml` / `master_pages.json`). Delete `port-source/` +once the port is verified. + +## Scope / version + +- `version` starts at **1.0.0** (graduation from bundled). +- `scope` set to **@ninja** per the port task. NOTE: the sibling reference themes + (`art-deco`, `brutalist`) use `@themes`, and the dev registry has both scopes. + Change `plugin.mod` if `@themes` is preferred. diff --git a/blocks/blocks.yaml b/blocks/blocks.yaml index d41494c..9501a62 100644 --- a/blocks/blocks.yaml +++ b/blocks/blocks.yaml @@ -4,6 +4,11 @@ # codeless path (unlike the wasm/templ path). The qualified keys match the # master_pages.json references. blocks: + - key: gotham:hero + title: Hero Section + description: Large hero section with headline and optional CTA + schema: hero.schema.json + template: hero.ninjatpl - key: gotham:stats title: Stats Counter description: Display statistics with labels and values diff --git a/blocks/hero.ninjatpl b/blocks/hero.ninjatpl new file mode 100644 index 0000000..7ff61c5 --- /dev/null +++ b/blocks/hero.ninjatpl @@ -0,0 +1 @@ +
{% if background_url %}
{% endif %}

{{ headline }}

{% if subheadline %}

{{ subheadline }}

{% endif %}{% if cta_text and cta_url %}{{ cta_text }}{% endif %}
diff --git a/blocks/hero.schema.json b/blocks/hero.schema.json new file mode 100644 index 0000000..8a15d18 --- /dev/null +++ b/blocks/hero.schema.json @@ -0,0 +1,39 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Hero Block", + "description": "Large hero section with headline and optional CTA", + "type": "object", + "properties": { + "headline": { + "type": "string", + "title": "Headline", + "description": "Main headline text", + "x-editor": "text" + }, + "subheadline": { + "type": "string", + "title": "Subheadline", + "description": "Supporting text below the headline", + "x-editor": "textarea" + }, + "background_url": { + "type": "string", + "title": "Background Image URL", + "description": "URL for the background image", + "x-editor": "url" + }, + "cta_text": { + "type": "string", + "title": "CTA Button Text", + "description": "Text for the call-to-action button", + "x-editor": "text" + }, + "cta_url": { + "type": "string", + "title": "CTA Button URL", + "description": "Link for the call-to-action button", + "x-editor": "url" + } + }, + "required": ["headline"] +}