Bootstrapped during the 2026-06-06 BlockNinja consolidation. Was previously an unversioned directory inside ~/src/blockninja-themes/brutalist. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
188 lines
5.6 KiB
Go
188 lines
5.6 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 a 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 installs the Brutalist system template, its four page templates,
|
|
// theme-specific blocks, block overrides, and the email wrapper.
|
|
func Register(tr templates.TemplateRegistry, br blocks.BlockRegistry) error {
|
|
tr.RegisterSystemTemplate(templates.SystemTemplateMeta{
|
|
Key: "brutalist",
|
|
Title: "Brutalist",
|
|
Description: "Concrete, oversized type, hard 12-col grid theme for design studios, architecture firms and art galleries.",
|
|
})
|
|
|
|
if err := tr.RegisterPageTemplate("brutalist", templates.PageTemplateMeta{
|
|
Key: "default",
|
|
Title: "Default",
|
|
Description: "Standard masthead + 12-col body + footer",
|
|
Slots: []string{"header", "main", "footer"},
|
|
}, wrap(RenderBrutalist)); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := tr.RegisterPageTemplate("brutalist", templates.PageTemplateMeta{
|
|
Key: "landing",
|
|
Title: "Index Sheet",
|
|
Description: "Oversized headline + project ledger",
|
|
Slots: []string{"hero", "ledger", "footer"},
|
|
}, wrap(RenderBrutalistLanding)); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := tr.RegisterPageTemplate("brutalist", templates.PageTemplateMeta{
|
|
Key: "article",
|
|
Title: "Case Study",
|
|
Description: "Long-form project page with metadata strip",
|
|
Slots: []string{"header", "meta", "main", "footer"},
|
|
}, wrap(RenderBrutalistArticle)); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := tr.RegisterPageTemplate("brutalist", templates.PageTemplateMeta{
|
|
Key: "full-width",
|
|
Title: "Full Bleed",
|
|
Description: "Edge-to-edge gallery layout",
|
|
Slots: []string{"header", "main", "footer"},
|
|
}, wrap(RenderBrutalistFullWidth)); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Load schemas BEFORE any block registration so the registry can bind
|
|
// content shapes to the block keys.
|
|
if err := br.LoadSchemasFromFS(Schemas()); err != nil {
|
|
return err
|
|
}
|
|
|
|
br.Register(MastheadBlockMeta, MastheadBlock)
|
|
br.Register(ProjectLedgerBlockMeta, ProjectLedgerBlock)
|
|
br.Register(ConcreteHeroBlockMeta, ConcreteHeroBlock)
|
|
br.Register(MetaStripBlockMeta, MetaStripBlock)
|
|
br.Register(CaptionImageBlockMeta, CaptionImageBlock)
|
|
br.Register(PullQuoteBlockMeta, PullQuoteBlock)
|
|
br.Register(ColophonBlockMeta, ColophonBlock)
|
|
|
|
br.RegisterTemplateOverride("brutalist", "heading", BrutalistHeadingBlock)
|
|
br.RegisterTemplateOverride("brutalist", "text", BrutalistTextBlock)
|
|
br.RegisterTemplateOverride("brutalist", "button", BrutalistButtonBlock)
|
|
br.RegisterTemplateOverride("brutalist", "image", BrutalistImageBlock)
|
|
|
|
tr.RegisterEmailWrapper("brutalist", BrutalistEmailWrapper)
|
|
|
|
return nil
|
|
}
|
|
|
|
// DefaultMasterPages provisions the Brutalist default master pages on first
|
|
// theme activation. Three masters cover the four page templates per
|
|
// spec section 7.
|
|
func DefaultMasterPages() []plugin.MasterPageDefinition {
|
|
return []plugin.MasterPageDefinition{
|
|
{
|
|
Key: "brutalist:default-master",
|
|
Title: "Brutalist Default Master",
|
|
PageTemplates: []string{"default", "article"},
|
|
Blocks: []plugin.MasterPageBlock{
|
|
{
|
|
BlockKey: "navbar",
|
|
Title: "Masthead Nav",
|
|
Content: map[string]any{"menuName": "main"},
|
|
Slot: "header",
|
|
SortOrder: 0,
|
|
},
|
|
{
|
|
BlockKey: "brutalist:masthead",
|
|
Title: "Studio Masthead",
|
|
Content: map[string]any{"studioName": "STUDIO"},
|
|
Slot: "header",
|
|
SortOrder: 1,
|
|
},
|
|
{
|
|
BlockKey: "slot",
|
|
Title: "Main Content",
|
|
Content: map[string]any{"slotName": "main"},
|
|
Slot: "main",
|
|
SortOrder: 0,
|
|
},
|
|
{
|
|
BlockKey: "brutalist:colophon",
|
|
Title: "Colophon Footer",
|
|
Content: map[string]any{"showAddress": true},
|
|
Slot: "footer",
|
|
SortOrder: 0,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Key: "brutalist:index-master",
|
|
Title: "Brutalist Index Master",
|
|
PageTemplates: []string{"landing"},
|
|
Blocks: []plugin.MasterPageBlock{
|
|
{
|
|
BlockKey: "brutalist:masthead",
|
|
Title: "Studio Masthead",
|
|
Content: map[string]any{"studioName": "STUDIO"},
|
|
Slot: "hero",
|
|
SortOrder: 0,
|
|
},
|
|
{
|
|
BlockKey: "slot",
|
|
Title: "Ledger Slot",
|
|
Content: map[string]any{"slotName": "ledger"},
|
|
Slot: "ledger",
|
|
SortOrder: 0,
|
|
},
|
|
{
|
|
BlockKey: "brutalist:colophon",
|
|
Title: "Colophon Footer",
|
|
Content: map[string]any{"showAddress": true},
|
|
Slot: "footer",
|
|
SortOrder: 0,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Key: "brutalist:fullbleed-master",
|
|
Title: "Brutalist Full Bleed Master",
|
|
PageTemplates: []string{"full-width"},
|
|
Blocks: []plugin.MasterPageBlock{
|
|
{
|
|
BlockKey: "navbar",
|
|
Title: "Masthead Nav",
|
|
Content: map[string]any{"menuName": "main"},
|
|
Slot: "header",
|
|
SortOrder: 0,
|
|
},
|
|
{
|
|
BlockKey: "slot",
|
|
Title: "Full Bleed Slot",
|
|
Content: map[string]any{"slotName": "main"},
|
|
Slot: "main",
|
|
SortOrder: 0,
|
|
},
|
|
{
|
|
BlockKey: "brutalist:colophon",
|
|
Title: "Colophon Footer",
|
|
Content: map[string]any{"showAddress": false},
|
|
Slot: "footer",
|
|
SortOrder: 0,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|