Bootstrapped during the 2026-06-06 BlockNinja consolidation. Was previously an unversioned directory inside ~/src/blockninja-themes/kindergarten. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
182 lines
6.1 KiB
Go
182 lines
6.1 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 satisfies 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 wires the Kindergarten theme into the host registries. The call
|
|
// order matters: the system template + page templates are registered first,
|
|
// then schemas are loaded before any block registrations so the schema
|
|
// bindings resolve.
|
|
func Register(tr templates.TemplateRegistry, br blocks.BlockRegistry) error {
|
|
tr.RegisterSystemTemplate(templates.SystemTemplateMeta{
|
|
Key: "kindergarten",
|
|
Title: "Kindergarten",
|
|
Description: "Primary-colored, hand-lettered theme for schools, daycares, toy shops, museums, and kids' publishers.",
|
|
})
|
|
|
|
if err := tr.RegisterPageTemplate("kindergarten", templates.PageTemplateMeta{
|
|
Key: "default",
|
|
Title: "Default",
|
|
Description: "Standard rounded layout with header, main, footer",
|
|
Slots: []string{"header", "main", "footer"},
|
|
}, wrap(RenderKindergarten)); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := tr.RegisterPageTemplate("kindergarten", templates.PageTemplateMeta{
|
|
Key: "landing",
|
|
Title: "Landing",
|
|
Description: "Mascot-led hero plus stacked feature sections",
|
|
Slots: []string{"hero", "main", "cta", "footer"},
|
|
}, wrap(RenderKindergartenLanding)); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := tr.RegisterPageTemplate("kindergarten", templates.PageTemplateMeta{
|
|
Key: "article",
|
|
Title: "Story Page",
|
|
Description: "Picture-book reading layout for blog posts and stories",
|
|
Slots: []string{"header", "main", "aside", "footer"},
|
|
}, wrap(RenderKindergartenArticle)); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := tr.RegisterPageTemplate("kindergarten", templates.PageTemplateMeta{
|
|
Key: "full-width",
|
|
Title: "Full Width",
|
|
Description: "Edge-to-edge gallery / classroom showcase",
|
|
Slots: []string{"header", "main", "footer"},
|
|
}, wrap(RenderKindergartenFullWidth)); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Schemas must be loaded before block registrations so each Register()
|
|
// call can bind to its declared schema.
|
|
if err := br.LoadSchemasFromFS(Schemas()); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Theme-owned blocks (eight per spec §8). Registered unqualified; the host
|
|
// addresses them as "kindergarten:<key>" downstream.
|
|
br.Register(MascotHeroBlockMeta, MascotHeroBlock)
|
|
br.Register(AlphabetStripBlockMeta, AlphabetStripBlock)
|
|
br.Register(ScheduleBlockMeta, ScheduleBlock)
|
|
br.Register(GalleryOfArtBlockMeta, GalleryOfArtBlock)
|
|
br.Register(NumbersCounterBlockMeta, NumbersCounterBlock)
|
|
br.Register(StorybookQuoteBlockMeta, StorybookQuoteBlock)
|
|
br.Register(BigCTABlockMeta, BigCTABlock)
|
|
br.Register(FooterBlockMeta, FooterBlock)
|
|
|
|
// Built-in overrides (spec §9). Each is scoped to the kindergarten template,
|
|
// so they only apply when this theme is active.
|
|
br.RegisterTemplateOverride("kindergarten", "heading", KindergartenHeadingBlock)
|
|
br.RegisterTemplateOverride("kindergarten", "text", KindergartenTextBlock)
|
|
br.RegisterTemplateOverride("kindergarten", "button", KindergartenButtonBlock)
|
|
br.RegisterTemplateOverride("kindergarten", "card", KindergartenCardBlock)
|
|
|
|
// Branded email wrapper.
|
|
tr.RegisterEmailWrapper("kindergarten", KindergartenEmailWrapper)
|
|
|
|
return nil
|
|
}
|
|
|
|
// DefaultMasterPages returns the default master pages that Kindergarten ships.
|
|
// Per spec §7, two masters: default-master (used by default + article page
|
|
// templates) and landing-master (used by landing + full-width).
|
|
func DefaultMasterPages() []plugin.MasterPageDefinition {
|
|
return []plugin.MasterPageDefinition{
|
|
{
|
|
Key: "kindergarten:default-master",
|
|
Title: "Kindergarten Default Master",
|
|
PageTemplates: []string{"default", "article"},
|
|
Blocks: []plugin.MasterPageBlock{
|
|
{
|
|
BlockKey: "navbar",
|
|
Title: "Crayon Nav",
|
|
Content: map[string]any{"menuName": "main"},
|
|
Slot: "header",
|
|
SortOrder: 0,
|
|
},
|
|
{
|
|
BlockKey: "kindergarten:alphabet_strip",
|
|
Title: "ABC Strip",
|
|
Content: map[string]any{"letters": "ABCDEFGHIJ", "colorMode": "rainbow"},
|
|
Slot: "header",
|
|
SortOrder: 1,
|
|
},
|
|
{
|
|
BlockKey: "slot",
|
|
Title: "Main Slot",
|
|
Content: map[string]any{"slotName": "main", "placeholder": "Page content"},
|
|
Slot: "main",
|
|
SortOrder: 0,
|
|
},
|
|
{
|
|
BlockKey: "kindergarten:footer",
|
|
Title: "Friendly Footer",
|
|
Content: map[string]any{"showSignup": "true", "mascotName": "Pip"},
|
|
Slot: "footer",
|
|
SortOrder: 0,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Key: "kindergarten:landing-master",
|
|
Title: "Kindergarten Landing Master",
|
|
PageTemplates: []string{"landing", "full-width"},
|
|
Blocks: []plugin.MasterPageBlock{
|
|
{
|
|
BlockKey: "navbar",
|
|
Title: "Crayon Nav",
|
|
Content: map[string]any{"menuName": "main"},
|
|
Slot: "header",
|
|
SortOrder: 0,
|
|
},
|
|
{
|
|
BlockKey: "kindergarten:mascot_hero",
|
|
Title: "Mascot Hero",
|
|
Content: map[string]any{"mascot": "pip", "headline": "Welcome to school!", "tagline": "Big fun, small humans."},
|
|
Slot: "hero",
|
|
SortOrder: 0,
|
|
},
|
|
{
|
|
BlockKey: "slot",
|
|
Title: "Main Slot",
|
|
Content: map[string]any{"slotName": "main"},
|
|
Slot: "main",
|
|
SortOrder: 0,
|
|
},
|
|
{
|
|
BlockKey: "kindergarten:big_cta",
|
|
Title: "Big Yellow CTA",
|
|
Content: map[string]any{"label": "Enrol today", "href": "/enrol", "colorVariant": "yellow"},
|
|
Slot: "cta",
|
|
SortOrder: 0,
|
|
},
|
|
{
|
|
BlockKey: "kindergarten:footer",
|
|
Title: "Friendly Footer",
|
|
Content: map[string]any{"showSignup": "true"},
|
|
Slot: "footer",
|
|
SortOrder: 0,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|