themes-y2k/register.go
Alex Dunmow 49f9c90589 initial: theme plugin y2k
Bootstrapped during the 2026-06-06 BlockNinja consolidation. Was previously
an unversioned directory inside ~/src/blockninja-themes/y2k.

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

174 lines
5.7 KiB
Go

package main
import (
"context"
"github.com/a-h/templ"
"git.dev.alexdunmow.com/block/core/blocks"
"git.dev.alexdunmow.com/block/core/plugin"
"git.dev.alexdunmow.com/block/core/templates"
)
// wrap adapts a templ-returning render function to templates.TemplateFunc.
// templ.Component already implements templates.HTMLComponent via Render.
func wrap(f func(ctx context.Context, doc map[string]any) templ.Component) templates.TemplateFunc {
return func(ctx context.Context, doc map[string]any) templates.HTMLComponent {
return f(ctx, doc)
}
}
// Register registers the y2k system template, its four page templates, all
// theme-specific blocks, the four built-in overrides, and the email wrapper.
//
// Call order: page templates can be registered any time, but schemas MUST load
// before any block Register call.
func Register(tr templates.TemplateRegistry, br blocks.BlockRegistry) error {
// 1. System template
tr.RegisterSystemTemplate(templates.SystemTemplateMeta{
Key: "y2k",
Title: "Y2K",
Description: "Liquid-chrome Y2K revival theme for indie musicians, zines, and merch shops.",
})
// 2. Page templates (slots match the spec verbatim)
if err := tr.RegisterPageTemplate("y2k", templates.PageTemplateMeta{
Key: "default",
Title: "Default",
Description: "Chrome navbar + content + footer.",
Slots: []string{"header", "main", "footer"},
}, wrap(RenderY2K)); err != nil {
return err
}
if err := tr.RegisterPageTemplate("y2k", templates.PageTemplateMeta{
Key: "landing",
Title: "Landing",
Description: "Hero metaball + marquee + CTAs.",
Slots: []string{"hero", "marquee", "main", "cta", "footer"},
}, wrap(RenderY2KLanding)); err != nil {
return err
}
if err := tr.RegisterPageTemplate("y2k", templates.PageTemplateMeta{
Key: "article",
Title: "Article / Zine Page",
Description: "Narrow column with sidebar tracklist.",
Slots: []string{"header", "main", "aside", "footer"},
}, wrap(RenderY2KArticle)); err != nil {
return err
}
if err := tr.RegisterPageTemplate("y2k", templates.PageTemplateMeta{
Key: "full-width",
Title: "Full Width",
Description: "Edge-to-edge gradient mesh canvas.",
Slots: []string{"header", "main", "footer"},
}, wrap(RenderY2KFullWidth)); err != nil {
return err
}
// 3. Schemas first — must precede br.Register so they bind.
if err := br.LoadSchemasFromFS(Schemas()); err != nil {
return err
}
// 4. Theme-specific blocks (10 total per spec §11).
br.Register(ChromeNavbarBlockMeta, ChromeNavbarBlock)
br.Register(MetaballHeroBlockMeta, MetaballHeroBlock)
br.Register(WaveformPlayerBlockMeta, WaveformPlayerBlock)
br.Register(MarqueeBlockMeta, MarqueeBlock)
br.Register(TracklistBlockMeta, TracklistBlock)
br.Register(MerchCardBlockMeta, MerchCardBlock)
br.Register(WebringBadgeBlockMeta, WebringBadgeBlock)
br.Register(GlitterDividerBlockMeta, GlitterDividerBlock)
br.Register(FooterChromeBlockMeta, FooterChromeBlock)
br.Register(NFTGalleryBlockMeta, NFTGalleryBlock)
// 5. Built-in overrides (only apply when y2k is the active theme).
br.RegisterTemplateOverride("y2k", "heading", Y2KHeadingBlock)
br.RegisterTemplateOverride("y2k", "text", Y2KTextBlock)
br.RegisterTemplateOverride("y2k", "button", Y2KButtonBlock)
br.RegisterTemplateOverride("y2k", "image", Y2KImageBlock)
// 6. Email wrapper for branded transactional mail.
tr.RegisterEmailWrapper("y2k", Y2KEmailWrapper)
return nil
}
// DefaultMasterPages returns the master pages that y2k seeds on first load.
// Block keys reference theme-owned blocks as "y2k:<key>"; the built-in slot
// block uses {"slotName":"main"} per CLAUDE.md.
func DefaultMasterPages() []plugin.MasterPageDefinition {
return []plugin.MasterPageDefinition{
{
Key: "y2k:default-master",
Title: "Y2K Default Master",
PageTemplates: []string{"default", "article"},
Blocks: []plugin.MasterPageBlock{
{
BlockKey: "y2k:chrome_navbar",
Title: "Chrome Navbar",
Content: map[string]any{"menuName": "main", "logoText": "y2k.fm"},
Slot: "header",
SortOrder: 0,
},
{
BlockKey: "y2k:marquee",
Title: "Top Marquee",
Content: map[string]any{"items": []any{"new drop friday", "listen on bandcamp", "subscribe to the zine"}, "speed": "medium"},
Slot: "header",
SortOrder: 1,
},
{
BlockKey: "slot",
Title: "Main Slot",
Content: map[string]any{"slotName": "main"},
Slot: "main",
SortOrder: 0,
},
{
BlockKey: "y2k:footer_chrome",
Title: "Chrome Footer",
Content: map[string]any{"showWebring": true, "copyright": "(c) y2k.fm"},
Slot: "footer",
SortOrder: 0,
},
},
},
{
Key: "y2k:landing-master",
Title: "Y2K Landing Master",
PageTemplates: []string{"landing", "full-width"},
Blocks: []plugin.MasterPageBlock{
{
BlockKey: "y2k:metaball_hero",
Title: "Metaball Hero",
Content: map[string]any{"headline": "new EP out now", "sub": "liquid chrome dreams", "ctaLabel": "listen"},
Slot: "hero",
SortOrder: 0,
},
{
BlockKey: "y2k:marquee",
Title: "Marquee",
Content: map[string]any{"items": []any{"* new * new * new"}, "speed": "fast"},
Slot: "marquee",
SortOrder: 0,
},
{
BlockKey: "slot",
Title: "Main Slot",
Content: map[string]any{"slotName": "main"},
Slot: "main",
SortOrder: 0,
},
{
BlockKey: "y2k:footer_chrome",
Title: "Chrome Footer",
Content: map[string]any{"showWebring": true},
Slot: "footer",
SortOrder: 0,
},
},
},
}
}