feat: convert to codeless .bnp — templates + manifest, Go/templ deleted (WO-WZ-021)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
ab4a5e633a
commit
989f1fb515
40
Makefile
40
Makefile
@ -1,26 +1,27 @@
|
|||||||
# magazine-bold — build & publish helpers (wasm .bnp workflow)
|
# magazine-bold — build & publish helpers (CODELESS .bnp workflow, WO-WZ-021)
|
||||||
#
|
#
|
||||||
# The theme compiles to a wasip1 reactor-mode wasm module packed into a .bnp
|
# This theme is a CODELESS .bnp: pure declaration the host runs — blocks
|
||||||
# artifact (`ninja plugin build`). The legacy .so path is retired (WO-WZ-015):
|
# (blocks/blocks.yaml + .ninjatpl), page/system templates, template overrides,
|
||||||
# the CMS is wasm-native and can no longer load .so plugins. Distribution is
|
# an email wrapper, master pages, presets, fonts and CSS in manifest.yaml. No
|
||||||
# publish-to-registry -> InstallFromRegistry (hot-load, no restart).
|
# Go, no templ, no plugin.wasm, no block/core dependency. `ninja plugin build`
|
||||||
|
# classifies the repo as codeless (no Go at the root) and packs the artifact
|
||||||
|
# with manifest.pb (codeless: true) and NO plugin.wasm.
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# make build # compile wasm + pack magazine-bold-<version>.bnp
|
# make build # pack magazine-bold-<version>.bnp (codeless — no wasm)
|
||||||
# make verify # validate the built .bnp (layout/abi/name/size)
|
# make verify # validate the built .bnp (loader-identical checks)
|
||||||
# make archive-check # prove a clean `git archive HEAD` compiles to wasm
|
# make archive-check # prove a clean `git archive HEAD` still builds codeless
|
||||||
# make publish # ninja plugin publish --bnp (to the active registry)
|
# make publish # ninja plugin publish --bnp (to the active registry)
|
||||||
# make templ # regenerate templ Go files
|
|
||||||
# make clean # remove build artefacts
|
# make clean # remove build artefacts
|
||||||
|
|
||||||
.PHONY: build verify archive-check publish templ clean help
|
.PHONY: build verify archive-check publish clean help
|
||||||
|
|
||||||
PLUGIN_NAME := magazine-bold
|
PLUGIN_NAME := magazine-bold
|
||||||
NINJA := ninja
|
NINJA := ninja
|
||||||
BNP := $(PLUGIN_NAME)-$(shell grep '^version' plugin.mod | sed 's/.*"\(.*\)"/\1/').bnp
|
BNP := $(PLUGIN_NAME)-$(shell grep '^version' plugin.mod | sed 's/.*"\(.*\)"/\1/').bnp
|
||||||
|
|
||||||
# Compile to wasm and pack the .bnp (GOOS=wasip1 GOARCH=wasm, DESCRIBE probe,
|
# Pack the codeless .bnp (manifest.pb synthesized from plugin.mod + manifest.yaml
|
||||||
# tar.zst of plugin.wasm + plugin.mod + manifest.pb + schemas/ + assets/).
|
# + blocks/ + templates/; no wasm compile, no DESCRIBE probe).
|
||||||
build:
|
build:
|
||||||
$(NINJA) plugin build --dir .
|
$(NINJA) plugin build --dir .
|
||||||
|
|
||||||
@ -28,12 +29,12 @@ build:
|
|||||||
verify:
|
verify:
|
||||||
$(NINJA) plugin verify $(BNP)
|
$(NINJA) plugin verify $(BNP)
|
||||||
|
|
||||||
# Prove a clean `git archive HEAD` (what publish ships) compiles to wasm.
|
# Prove a clean `git archive HEAD` (what publish ships) still packs codeless.
|
||||||
archive-check:
|
archive-check:
|
||||||
@T=$$(mktemp -d /tmp/archive-check-$(PLUGIN_NAME).XXXXXX) && \
|
@T=$$(mktemp -d /tmp/archive-check-$(PLUGIN_NAME).XXXXXX) && \
|
||||||
trap 'rm -rf "$$T"' EXIT && \
|
trap 'rm -rf "$$T"' EXIT && \
|
||||||
git archive HEAD | tar -x -C "$$T" && \
|
git archive HEAD | tar -x -C "$$T" && \
|
||||||
cd "$$T" && GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o "$$T/check.wasm" . && \
|
cd "$$T" && $(NINJA) plugin build --dir . && \
|
||||||
echo "archive-check OK"
|
echo "archive-check OK"
|
||||||
|
|
||||||
# Publish the prebuilt .bnp to the active registry.
|
# Publish the prebuilt .bnp to the active registry.
|
||||||
@ -41,19 +42,14 @@ archive-check:
|
|||||||
publish: build
|
publish: build
|
||||||
$(NINJA) plugin publish --bnp $(BNP)
|
$(NINJA) plugin publish --bnp $(BNP)
|
||||||
|
|
||||||
# Regenerate templ Go files locally.
|
|
||||||
templ:
|
|
||||||
templ generate
|
|
||||||
|
|
||||||
# Remove build artefacts.
|
# Remove build artefacts.
|
||||||
clean:
|
clean:
|
||||||
rm -f $(PLUGIN_NAME)-*.bnp $(PLUGIN_NAME).so plugin.wasm
|
rm -f $(PLUGIN_NAME)-*.bnp plugin.wasm
|
||||||
|
|
||||||
help:
|
help:
|
||||||
@echo "Targets:"
|
@echo "Targets:"
|
||||||
@echo " build Compile wasm + pack the .bnp"
|
@echo " build Pack the codeless .bnp (no wasm)"
|
||||||
@echo " verify Validate the built .bnp (loader-identical checks)"
|
@echo " verify Validate the built .bnp (loader-identical checks)"
|
||||||
@echo " archive-check Prove a clean git archive HEAD compiles to wasm"
|
@echo " archive-check Prove a clean git archive HEAD packs codeless"
|
||||||
@echo " publish Publish the .bnp to the active registry"
|
@echo " publish Publish the .bnp to the active registry"
|
||||||
@echo " templ Regenerate templ Go files"
|
|
||||||
@echo " clean Remove build artefacts"
|
@echo " clean Remove build artefacts"
|
||||||
|
|||||||
42
blocks/blocks.yaml
Normal file
42
blocks/blocks.yaml
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# Magazine Bold theme block definitions (codeless .bnp, WO-WZ-021).
|
||||||
|
# Keys are fully qualified (magazine-bold:<key>) because the codeless loader
|
||||||
|
# registers definitions verbatim — there is no PluginBlockRegistry prefixing
|
||||||
|
# in the codeless path (unlike the wasm/templ path). The qualified keys match
|
||||||
|
# the hardcoded data-block attributes and the master_pages.json references.
|
||||||
|
blocks:
|
||||||
|
- key: magazine-bold:masthead
|
||||||
|
title: Issue Masthead
|
||||||
|
description: Wordmark, issue number and tagline strip — sits at the top of every page.
|
||||||
|
category: layout
|
||||||
|
schema: masthead.schema.json
|
||||||
|
template: masthead.ninjatpl
|
||||||
|
- key: magazine-bold:cover_story
|
||||||
|
title: Cover Story
|
||||||
|
description: 12-column asymmetric hero with kicker, oversized display headline, deck and cover image.
|
||||||
|
category: content
|
||||||
|
schema: cover_story.schema.json
|
||||||
|
template: cover_story.ninjatpl
|
||||||
|
- key: magazine-bold:photo_essay
|
||||||
|
title: Photo Essay
|
||||||
|
description: Numbered photo-essay with asymmetric frame spans (half / full / tall) and mono captions.
|
||||||
|
category: content
|
||||||
|
schema: photo_essay.schema.json
|
||||||
|
template: photo_essay.ninjatpl
|
||||||
|
- key: magazine-bold:pull_quote
|
||||||
|
title: Pull Quote
|
||||||
|
description: Color-block pull quote with rotating accent (pink / blue / lime).
|
||||||
|
category: content
|
||||||
|
schema: pull_quote.schema.json
|
||||||
|
template: pull_quote.ninjatpl
|
||||||
|
- key: magazine-bold:issue_archive
|
||||||
|
title: Issue Archive
|
||||||
|
description: Numbered list of back-issues driven by a bucket query.
|
||||||
|
category: navigation
|
||||||
|
schema: issue_archive.schema.json
|
||||||
|
template: issue_archive.ninjatpl
|
||||||
|
- key: magazine-bold:colophon
|
||||||
|
title: Colophon
|
||||||
|
description: Mono caption strip with credits and an optional social row.
|
||||||
|
category: layout
|
||||||
|
schema: colophon.schema.json
|
||||||
|
template: colophon.ninjatpl
|
||||||
4
blocks/colophon.ninjatpl
Normal file
4
blocks/colophon.ninjatpl
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<div data-block="magazine-bold:colophon" class="py-8 font-mono mb-caption">
|
||||||
|
{% if credits %}<div class="mb-4">{{ credits|safe }}</div>{% endif %}
|
||||||
|
{% if showSocial == "true" or showSocial == true %}<ul class="flex items-center gap-6 mb-hairline-top pt-4 mt-4"><li><a href="#" class="hover:text-foreground">Instagram</a></li><li><a href="#" class="hover:text-foreground">Twitter</a></li><li><a href="#" class="hover:text-foreground">RSS</a></li></ul>{% endif %}
|
||||||
|
</div>
|
||||||
11
blocks/cover_story.ninjatpl
Normal file
11
blocks/cover_story.ninjatpl
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<section data-block="magazine-bold:cover_story" class="w-full py-12">
|
||||||
|
<div class="max-w-6xl mx-auto px-6">
|
||||||
|
<div class="mb-grid-asym-cover">
|
||||||
|
<div class="mb-col-folio">{% if kicker %}<div class="font-mono text-kicker text-muted-foreground mb-4">{{ kicker }}</div>{% endif %}</div>
|
||||||
|
<div class="mb-col-headline"><h1 class="font-display text-folio text-foreground">{% if headline %}{{ headline|safe }}{% else %}Cover story{% endif %}</h1></div>
|
||||||
|
<div class="mb-col-deck">{% if deck %}<div class="font-sans text-deck text-muted-foreground">{{ deck|safe }}</div>{% endif %}</div>
|
||||||
|
</div>
|
||||||
|
{% if image %}<div class="mt-10"><img src="{{ image }}" alt="" class="w-full h-auto block" loading="lazy"></div>{% endif %}
|
||||||
|
{% if link %}<div class="mt-6"><a href="{{ link }}" class="mb-button">Read the story →</a></div>{% endif %}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
4
blocks/issue_archive.ninjatpl
Normal file
4
blocks/issue_archive.ninjatpl
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<nav data-block="magazine-bold:issue_archive" class="py-6">
|
||||||
|
<div class="font-mono text-kicker text-muted-foreground mb-6">Issue Archive</div>
|
||||||
|
{% set rows = items %}{% if not rows %}{% set rows = bucket_data %}{% endif %}{% set eff = limit|default:12|integer %}{% if rows %}<ol class="grid grid-cols-1 md:grid-cols-2 gap-x-12 gap-y-4">{% for item in rows %}{% if forloop.Counter0 < eff %}<li class="flex items-baseline gap-4 mb-hairline-bottom py-3"><span class="font-mono text-kicker text-muted-foreground w-10">{{ forloop.Counter|stringformat:"%02d" }}</span><span class="font-display flex-1 text-foreground">{% if item.title %}{{ item.title }}{% elif item.name %}{{ item.name }}{% else %}Untitled issue{% endif %}</span>{% if item.date %}<span class="font-mono mb-caption">{{ item.date }}</span>{% endif %}</li>{% endif %}{% endfor %}</ol>{% else %}<p class="font-mono mb-caption">No issues yet.</p>{% endif %}
|
||||||
|
</nav>
|
||||||
7
blocks/masthead.ninjatpl
Normal file
7
blocks/masthead.ninjatpl
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<div data-block="magazine-bold:masthead" class="py-6 mb-hairline-bottom flex items-end justify-between gap-6">
|
||||||
|
<div class="flex items-baseline gap-4">
|
||||||
|
{% if issueNo %}<span class="font-mono text-kicker">No. {{ issueNo }}</span>{% endif %}
|
||||||
|
{% if tagline %}<span class="font-mono text-kicker text-muted-foreground">{{ tagline }}</span>{% endif %}
|
||||||
|
</div>
|
||||||
|
{% if date %}<span class="font-mono text-kicker text-muted-foreground">{{ date }}</span>{% endif %}
|
||||||
|
</div>
|
||||||
6
blocks/photo_essay.ninjatpl
Normal file
6
blocks/photo_essay.ninjatpl
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<section data-block="magazine-bold:photo_essay" class="w-full py-12">
|
||||||
|
<div class="max-w-6xl mx-auto px-6">
|
||||||
|
{% if title %}<h2 class="font-display text-deck text-foreground mb-8">{{ title }}</h2>{% endif %}
|
||||||
|
{% if frames %}<div class="mb-photo-grid">{% for frame in frames %}{% if frame.image %}<figure class="{% if frame.span == "full" %}mb-frame-full{% elif frame.span == "tall" %}mb-frame-tall{% else %}mb-frame-half{% endif %}"><img src="{{ frame.image }}" alt="{{ frame.caption }}" class="w-full h-auto block" loading="lazy"><figcaption class="mb-caption font-mono mt-2 flex items-baseline gap-3"><span class="mb-image-folio">{{ forloop.Counter|stringformat:"%02d" }}</span>{% if frame.caption %}<span>{{ frame.caption }}</span>{% endif %}</figcaption></figure>{% endif %}{% endfor %}</div>{% else %}<div class="font-mono mb-caption py-12">No frames yet.</div>{% endif %}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
7
blocks/pull_quote.ninjatpl
Normal file
7
blocks/pull_quote.ninjatpl
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<aside data-block="magazine-bold:pull_quote" data-mb-accent="{% if accent == "blue" %}blue{% elif accent == "lime" %}lime{% else %}pink{% endif %}" class="my-8 grid grid-cols-12 gap-4 items-start">
|
||||||
|
<div class="mb-quote-bar col-span-1 h-full min-h-[3rem]"></div>
|
||||||
|
<blockquote class="col-span-11 mb-quote-text">
|
||||||
|
<p class="font-display text-deck">{% if quote %}{{ quote|safe }}{% endif %}</p>
|
||||||
|
{% if attribution %}<cite class="block mt-4 font-mono text-kicker text-muted-foreground not-italic">— {{ attribution }}</cite>{% endif %}
|
||||||
|
</blockquote>
|
||||||
|
</aside>
|
||||||
@ -1,33 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ButtonOverrideBlock renders the built-in button block with the Magazine Bold
|
|
||||||
// squared-ink treatment and accent hover.
|
|
||||||
//
|
|
||||||
// Built-in button content shape: {"label": "...", "url": "...", "variant": "primary|secondary|destructive"}
|
|
||||||
func ButtonOverrideBlock(ctx context.Context, content map[string]any) string {
|
|
||||||
label := getString(content, "label")
|
|
||||||
if label == "" {
|
|
||||||
// fall back to "text" — some older button blocks used that key.
|
|
||||||
label = getString(content, "text")
|
|
||||||
}
|
|
||||||
url := getString(content, "url")
|
|
||||||
if url == "" {
|
|
||||||
url = getString(content, "href")
|
|
||||||
}
|
|
||||||
variant := getString(content, "variant")
|
|
||||||
switch variant {
|
|
||||||
case "secondary", "destructive", "primary":
|
|
||||||
// ok
|
|
||||||
default:
|
|
||||||
variant = "primary"
|
|
||||||
}
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
_ = mbButtonComponent(label, url, variant).Render(ctx, &buf)
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
// mbButtonComponent renders a squared-ink button with accent hover.
|
|
||||||
// All variants get the visible 2px ring on :focus-visible from .mb-button.
|
|
||||||
templ mbButtonComponent(label, url, variant string) {
|
|
||||||
if label != "" {
|
|
||||||
<a
|
|
||||||
href={ templ.SafeURL(url) }
|
|
||||||
class={ "mb-button", "mb-button-" + variant }
|
|
||||||
data-variant={ variant }
|
|
||||||
>{ label }</a>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,101 +0,0 @@
|
|||||||
// Code generated by templ - DO NOT EDIT.
|
|
||||||
|
|
||||||
// templ: version: v0.3.1020
|
|
||||||
package main
|
|
||||||
|
|
||||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
|
||||||
|
|
||||||
import "github.com/a-h/templ"
|
|
||||||
import templruntime "github.com/a-h/templ/runtime"
|
|
||||||
|
|
||||||
// mbButtonComponent renders a squared-ink button with accent hover.
|
|
||||||
// All variants get the visible 2px ring on :focus-visible from .mb-button.
|
|
||||||
func mbButtonComponent(label, url, variant string) templ.Component {
|
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
|
||||||
return templ_7745c5c3_CtxErr
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
|
||||||
if !templ_7745c5c3_IsBuffer {
|
|
||||||
defer func() {
|
|
||||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err == nil {
|
|
||||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
ctx = templ.InitializeContext(ctx)
|
|
||||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
|
||||||
if templ_7745c5c3_Var1 == nil {
|
|
||||||
templ_7745c5c3_Var1 = templ.NopComponent
|
|
||||||
}
|
|
||||||
ctx = templ.ClearChildren(ctx)
|
|
||||||
if label != "" {
|
|
||||||
var templ_7745c5c3_Var2 = []any{"mb-button", "mb-button-" + variant}
|
|
||||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var2...)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<a href=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var3 templ.SafeURL
|
|
||||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(url))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button_override.templ`, Line: 8, Col: 28}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\" class=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var4 string
|
|
||||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var2).String())
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button_override.templ`, Line: 1, Col: 0}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\" data-variant=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var5 string
|
|
||||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.ResolveAttributeValue(variant)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button_override.templ`, Line: 10, Col: 25}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var5)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var6 string
|
|
||||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(label)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button_override.templ`, Line: 11, Col: 10}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</a>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ = templruntime.GeneratedTemplate
|
|
||||||
35
colophon.go
35
colophon.go
@ -1,35 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"git.dev.alexdunmow.com/block/core/blocks"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ColophonBlockMeta is the mono caption strip with credits and optional social row.
|
|
||||||
var ColophonBlockMeta = blocks.BlockMeta{
|
|
||||||
Key: "colophon",
|
|
||||||
Title: "Colophon",
|
|
||||||
Description: "Mono caption strip with credits and an optional social row.",
|
|
||||||
Source: "magazine-bold",
|
|
||||||
Category: blocks.CategoryLayout,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ColophonData is what the templ component consumes.
|
|
||||||
type ColophonData struct {
|
|
||||||
Credits string
|
|
||||||
ShowSocial bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// ColophonBlock renders the colophon strip.
|
|
||||||
// content keys: credits (richtext), showSocial (select "true"/"false").
|
|
||||||
func ColophonBlock(ctx context.Context, content map[string]any) string {
|
|
||||||
data := ColophonData{
|
|
||||||
Credits: getString(content, "credits"),
|
|
||||||
ShowSocial: getBool(content, "showSocial", false),
|
|
||||||
}
|
|
||||||
var buf bytes.Buffer
|
|
||||||
_ = colophonComponent(data).Render(ctx, &buf)
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
// colophonComponent renders the mono colophon strip.
|
|
||||||
templ colophonComponent(data ColophonData) {
|
|
||||||
<div data-block="magazine-bold:colophon" class="py-8 font-mono mb-caption">
|
|
||||||
if data.Credits != "" {
|
|
||||||
<div class="mb-4">
|
|
||||||
@templ.Raw(data.Credits)
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
if data.ShowSocial {
|
|
||||||
<ul class="flex items-center gap-6 mb-hairline-top pt-4 mt-4">
|
|
||||||
<li><a href="#" class="hover:text-foreground">Instagram</a></li>
|
|
||||||
<li><a href="#" class="hover:text-foreground">Twitter</a></li>
|
|
||||||
<li><a href="#" class="hover:text-foreground">RSS</a></li>
|
|
||||||
</ul>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
@ -1,65 +0,0 @@
|
|||||||
// Code generated by templ - DO NOT EDIT.
|
|
||||||
|
|
||||||
// templ: version: v0.3.1020
|
|
||||||
package main
|
|
||||||
|
|
||||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
|
||||||
|
|
||||||
import "github.com/a-h/templ"
|
|
||||||
import templruntime "github.com/a-h/templ/runtime"
|
|
||||||
|
|
||||||
// colophonComponent renders the mono colophon strip.
|
|
||||||
func colophonComponent(data ColophonData) templ.Component {
|
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
|
||||||
return templ_7745c5c3_CtxErr
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
|
||||||
if !templ_7745c5c3_IsBuffer {
|
|
||||||
defer func() {
|
|
||||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err == nil {
|
|
||||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
ctx = templ.InitializeContext(ctx)
|
|
||||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
|
||||||
if templ_7745c5c3_Var1 == nil {
|
|
||||||
templ_7745c5c3_Var1 = templ.NopComponent
|
|
||||||
}
|
|
||||||
ctx = templ.ClearChildren(ctx)
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div data-block=\"magazine-bold:colophon\" class=\"py-8 font-mono mb-caption\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
if data.Credits != "" {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"mb-4\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templ.Raw(data.Credits).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if data.ShowSocial {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<ul class=\"flex items-center gap-6 mb-hairline-top pt-4 mt-4\"><li><a href=\"#\" class=\"hover:text-foreground\">Instagram</a></li><li><a href=\"#\" class=\"hover:text-foreground\">Twitter</a></li><li><a href=\"#\" class=\"hover:text-foreground\">RSS</a></li></ul>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ = templruntime.GeneratedTemplate
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"git.dev.alexdunmow.com/block/core/blocks"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CoverStoryBlockMeta is the 12-column asymmetric hero anchoring the issue landing.
|
|
||||||
var CoverStoryBlockMeta = blocks.BlockMeta{
|
|
||||||
Key: "cover_story",
|
|
||||||
Title: "Cover Story",
|
|
||||||
Description: "12-column asymmetric hero with kicker, oversized display headline, deck and cover image.",
|
|
||||||
Source: "magazine-bold",
|
|
||||||
Category: blocks.CategoryContent,
|
|
||||||
}
|
|
||||||
|
|
||||||
// CoverStoryData is what the templ component consumes.
|
|
||||||
type CoverStoryData struct {
|
|
||||||
Kicker string
|
|
||||||
Headline string
|
|
||||||
Deck string
|
|
||||||
Image string
|
|
||||||
Link string
|
|
||||||
}
|
|
||||||
|
|
||||||
// CoverStoryBlock renders the cover-story hero.
|
|
||||||
// content keys: kicker, headline (richtext), deck (richtext), image (media), link.
|
|
||||||
func CoverStoryBlock(ctx context.Context, content map[string]any) string {
|
|
||||||
data := CoverStoryData{
|
|
||||||
Kicker: getString(content, "kicker"),
|
|
||||||
Headline: getString(content, "headline"),
|
|
||||||
Deck: getString(content, "deck"),
|
|
||||||
Image: blocks.ResolveMediaPath(getString(content, "image")),
|
|
||||||
Link: getString(content, "link"),
|
|
||||||
}
|
|
||||||
var buf bytes.Buffer
|
|
||||||
_ = coverStoryComponent(data).Render(ctx, &buf)
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
@ -1,46 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
// coverStoryComponent renders the 12-column asymmetric cover hero.
|
|
||||||
// Note: the spec calls for an oversized display headline; this consumes
|
|
||||||
// `--font-heading` via .font-display and clamps via .text-folio.
|
|
||||||
templ coverStoryComponent(data CoverStoryData) {
|
|
||||||
<section data-block="magazine-bold:cover_story" class="w-full py-12">
|
|
||||||
<div class="max-w-6xl mx-auto px-6">
|
|
||||||
<div class="mb-grid-asym-cover">
|
|
||||||
<div class="mb-col-folio">
|
|
||||||
if data.Kicker != "" {
|
|
||||||
<div class="font-mono text-kicker text-muted-foreground mb-4">{ data.Kicker }</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<div class="mb-col-headline">
|
|
||||||
<h1 class="font-display text-folio text-foreground">
|
|
||||||
if data.Headline != "" {
|
|
||||||
@templ.Raw(data.Headline)
|
|
||||||
} else {
|
|
||||||
Cover story
|
|
||||||
}
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
<div class="mb-col-deck">
|
|
||||||
if data.Deck != "" {
|
|
||||||
<div class="font-sans text-deck text-muted-foreground">
|
|
||||||
@templ.Raw(data.Deck)
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
if data.Image != "" {
|
|
||||||
<div class="mt-10">
|
|
||||||
<img src={ data.Image } alt="" class="w-full h-auto block" loading="lazy"/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
if data.Link != "" {
|
|
||||||
<div class="mt-6">
|
|
||||||
<a href={ templ.SafeURL(data.Link) } class="mb-button">
|
|
||||||
Read the story →
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
}
|
|
||||||
@ -1,141 +0,0 @@
|
|||||||
// Code generated by templ - DO NOT EDIT.
|
|
||||||
|
|
||||||
// templ: version: v0.3.1020
|
|
||||||
package main
|
|
||||||
|
|
||||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
|
||||||
|
|
||||||
import "github.com/a-h/templ"
|
|
||||||
import templruntime "github.com/a-h/templ/runtime"
|
|
||||||
|
|
||||||
// coverStoryComponent renders the 12-column asymmetric cover hero.
|
|
||||||
// Note: the spec calls for an oversized display headline; this consumes
|
|
||||||
// `--font-heading` via .font-display and clamps via .text-folio.
|
|
||||||
func coverStoryComponent(data CoverStoryData) templ.Component {
|
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
|
||||||
return templ_7745c5c3_CtxErr
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
|
||||||
if !templ_7745c5c3_IsBuffer {
|
|
||||||
defer func() {
|
|
||||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err == nil {
|
|
||||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
ctx = templ.InitializeContext(ctx)
|
|
||||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
|
||||||
if templ_7745c5c3_Var1 == nil {
|
|
||||||
templ_7745c5c3_Var1 = templ.NopComponent
|
|
||||||
}
|
|
||||||
ctx = templ.ClearChildren(ctx)
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<section data-block=\"magazine-bold:cover_story\" class=\"w-full py-12\"><div class=\"max-w-6xl mx-auto px-6\"><div class=\"mb-grid-asym-cover\"><div class=\"mb-col-folio\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
if data.Kicker != "" {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"font-mono text-kicker text-muted-foreground mb-4\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var2 string
|
|
||||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(data.Kicker)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `cover_story.templ`, Line: 12, Col: 81}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</div><div class=\"mb-col-headline\"><h1 class=\"font-display text-folio text-foreground\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
if data.Headline != "" {
|
|
||||||
templ_7745c5c3_Err = templ.Raw(data.Headline).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "Cover story")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</h1></div><div class=\"mb-col-deck\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
if data.Deck != "" {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<div class=\"font-sans text-deck text-muted-foreground\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templ.Raw(data.Deck).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</div></div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
if data.Image != "" {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<div class=\"mt-10\"><img src=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var3 string
|
|
||||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.Image)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `cover_story.templ`, Line: 34, Col: 26}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\" alt=\"\" class=\"w-full h-auto block\" loading=\"lazy\"></div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if data.Link != "" {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<div class=\"mt-6\"><a href=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var4 templ.SafeURL
|
|
||||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(data.Link))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `cover_story.templ`, Line: 39, Col: 39}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\" class=\"mb-button\">Read the story →</a></div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</div></section>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ = templruntime.GeneratedTemplate
|
|
||||||
191
css.go
191
css.go
@ -1,191 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
// magazineBoldUtilityCSS is appended into the host Tailwind input. It carries
|
|
||||||
// the editorial display-type utilities, font-slot classes, the hairline rule
|
|
||||||
// motif, the asymmetric grid utilities, and the per-accent pull-quote variants.
|
|
||||||
//
|
|
||||||
// All font-family references go through CSS variables per docs/FONTS.md.
|
|
||||||
// All color references go through `hsl(var(--token))` consumers of the 19
|
|
||||||
// theme tokens — no hardcoded hex, rgb(), or named colors. Per-accent
|
|
||||||
// pull-quote variants intentionally key off the `--accent` token (which the
|
|
||||||
// active preset rotates between hot pink, electric blue and lime).
|
|
||||||
//
|
|
||||||
// The `data-mb-accent` attribute selectors let a pull-quote pin a specific
|
|
||||||
// accent independently of the preset — they map back into the theme token
|
|
||||||
// space using the HSL triples documented in the spec §4 accent rows.
|
|
||||||
const magazineBoldUtilityCSS = `
|
|
||||||
/* ===========================================================
|
|
||||||
* Magazine Bold — theme utilities
|
|
||||||
* Appended to the Tailwind input via CSSManifest.InputCSSAppend.
|
|
||||||
* =========================================================== */
|
|
||||||
|
|
||||||
/* --- Font-slot classes (consume CMS-managed font variables) --- */
|
|
||||||
.font-display {
|
|
||||||
font-family: var(--font-heading, "Playfair Display", "Cormorant Garamond", Georgia, "Times New Roman", serif);
|
|
||||||
font-feature-settings: "ss01", "ss02", "kern", "liga";
|
|
||||||
font-optical-sizing: auto;
|
|
||||||
}
|
|
||||||
.font-serif-sub {
|
|
||||||
font-family: var(--font-heading, "Fraunces", "Playfair Display", Georgia, "Times New Roman", serif);
|
|
||||||
}
|
|
||||||
.font-sans {
|
|
||||||
font-family: var(--font-body, "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);
|
|
||||||
}
|
|
||||||
.font-mono {
|
|
||||||
font-family: var(--font-mono, "JetBrains Mono", "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Display-size utilities ------------------------------- */
|
|
||||||
/* text-folio: 220pt-equivalent oversized folio number / cover hit.
|
|
||||||
The clamp resolves to ~220px (~165pt) at 1440px, scaling down to 80px on mobile. */
|
|
||||||
.text-folio {
|
|
||||||
font-family: var(--font-heading, "Playfair Display", Georgia, serif);
|
|
||||||
font-size: clamp(80px, 18vw, 240px);
|
|
||||||
line-height: 0.85;
|
|
||||||
letter-spacing: -0.04em;
|
|
||||||
font-weight: 200;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
.text-deck {
|
|
||||||
font-family: var(--font-body, "Inter", system-ui, sans-serif);
|
|
||||||
font-size: clamp(28px, 3.2vw, 48px);
|
|
||||||
line-height: 1.15;
|
|
||||||
letter-spacing: -0.01em;
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
.text-kicker {
|
|
||||||
font-family: var(--font-mono, "JetBrains Mono", monospace);
|
|
||||||
font-size: 12px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.18em;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Hairline rule motif ---------------------------------- */
|
|
||||||
.mb-hairline { border-color: hsl(var(--border)); border-width: 1px; }
|
|
||||||
.mb-hairline-top { border-top: 1px solid hsl(var(--border)); }
|
|
||||||
.mb-hairline-bottom { border-bottom: 1px solid hsl(var(--border)); }
|
|
||||||
|
|
||||||
/* --- Asymmetric editorial grid ---------------------------- */
|
|
||||||
.mb-grid-12 {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(12, minmax(0, 1fr));
|
|
||||||
gap: 1.5rem;
|
|
||||||
}
|
|
||||||
.mb-grid-asym-cover {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(12, minmax(0, 1fr));
|
|
||||||
gap: 2rem;
|
|
||||||
align-items: end;
|
|
||||||
}
|
|
||||||
.mb-col-folio { grid-column: 1 / span 2; }
|
|
||||||
.mb-col-headline { grid-column: 3 / span 7; }
|
|
||||||
.mb-col-deck { grid-column: 10 / span 3; }
|
|
||||||
.mb-col-image-wide { grid-column: 1 / -1; }
|
|
||||||
|
|
||||||
@media (max-width: 1024px) {
|
|
||||||
.mb-grid-asym-cover { grid-template-columns: 1fr; gap: 1.25rem; }
|
|
||||||
.mb-col-folio, .mb-col-headline, .mb-col-deck { grid-column: 1 / -1; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Photo essay frame spans ------------------------------ */
|
|
||||||
.mb-photo-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(12, minmax(0, 1fr));
|
|
||||||
gap: 1.25rem;
|
|
||||||
}
|
|
||||||
.mb-frame-half { grid-column: span 6 / span 6; }
|
|
||||||
.mb-frame-full { grid-column: 1 / -1; }
|
|
||||||
.mb-frame-tall { grid-column: span 5 / span 5; grid-row: span 2; }
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.mb-photo-grid { grid-template-columns: 1fr; }
|
|
||||||
.mb-frame-half, .mb-frame-full, .mb-frame-tall { grid-column: 1 / -1; grid-row: auto; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Color-block accent panels ---------------------------- */
|
|
||||||
.colorblock-accent {
|
|
||||||
background-color: hsl(var(--accent));
|
|
||||||
color: hsl(var(--accent-foreground));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Per-accent pull-quote variants ----------------------- *
|
|
||||||
* These let a pull_quote override the preset accent on a per-instance basis.
|
|
||||||
* They use literal HSL triples that match the spec §4 accent values so a
|
|
||||||
* "pink" pull-quote always reads pink even on the ink-blue preset.
|
|
||||||
*/
|
|
||||||
[data-mb-accent="pink"] { --mb-accent-h: 330; --mb-accent-s: 95%; --mb-accent-l: 58%; }
|
|
||||||
[data-mb-accent="blue"] { --mb-accent-h: 220; --mb-accent-s: 100%; --mb-accent-l: 56%; }
|
|
||||||
[data-mb-accent="lime"] { --mb-accent-h: 78; --mb-accent-s: 90%; --mb-accent-l: 50%; }
|
|
||||||
[data-mb-accent] .mb-quote-bar {
|
|
||||||
background-color: hsl(var(--mb-accent-h) var(--mb-accent-s) var(--mb-accent-l));
|
|
||||||
}
|
|
||||||
[data-mb-accent] .mb-quote-text { color: hsl(var(--foreground)); }
|
|
||||||
|
|
||||||
/* --- Drop-cap (heading override) -------------------------- */
|
|
||||||
.mb-dropcap::first-letter {
|
|
||||||
font-family: var(--font-heading, "Playfair Display", Georgia, serif);
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 200;
|
|
||||||
float: left;
|
|
||||||
font-size: 5.5em;
|
|
||||||
line-height: 0.85;
|
|
||||||
padding-right: 0.08em;
|
|
||||||
margin-top: 0.04em;
|
|
||||||
color: hsl(var(--foreground));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Body text override (Inter + margin folios) ----------- */
|
|
||||||
.mb-body {
|
|
||||||
font-family: var(--font-body, "Inter", system-ui, sans-serif);
|
|
||||||
font-size: clamp(16px, 1.05vw, 18px);
|
|
||||||
line-height: 1.55;
|
|
||||||
letter-spacing: 0.005em;
|
|
||||||
color: hsl(var(--foreground));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Squared ink button ----------------------------------- */
|
|
||||||
.mb-button {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
background-color: hsl(var(--primary));
|
|
||||||
color: hsl(var(--primary-foreground));
|
|
||||||
font-family: var(--font-mono, "JetBrains Mono", monospace);
|
|
||||||
font-size: 12px;
|
|
||||||
letter-spacing: 0.14em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
padding: 0.875rem 1.5rem;
|
|
||||||
border: 1px solid hsl(var(--primary));
|
|
||||||
border-radius: 0;
|
|
||||||
transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease;
|
|
||||||
}
|
|
||||||
.mb-button:hover {
|
|
||||||
background-color: hsl(var(--accent));
|
|
||||||
color: hsl(var(--accent-foreground));
|
|
||||||
border-color: hsl(var(--accent));
|
|
||||||
}
|
|
||||||
.mb-button:focus-visible {
|
|
||||||
outline: 2px solid hsl(var(--ring));
|
|
||||||
outline-offset: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Caption (mono, used by image override + photo essay) -- */
|
|
||||||
.mb-caption {
|
|
||||||
font-family: var(--font-mono, "JetBrains Mono", monospace);
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 1.45;
|
|
||||||
letter-spacing: 0.01em;
|
|
||||||
color: hsl(var(--muted-foreground));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Full-bleed image with numbered folio ----------------- */
|
|
||||||
.mb-fullbleed { width: 100vw; max-width: 100vw; margin-left: 50%; transform: translateX(-50%); }
|
|
||||||
.mb-image-folio {
|
|
||||||
font-family: var(--font-mono, "JetBrains Mono", monospace);
|
|
||||||
font-size: 11px;
|
|
||||||
letter-spacing: 0.16em;
|
|
||||||
color: hsl(var(--muted-foreground));
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
100
email_wrapper.go
100
email_wrapper.go
@ -1,100 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.dev.alexdunmow.com/block/core/templates"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MagazineBoldEmailWrapper wraps body content in the Magazine Bold email template.
|
|
||||||
// The template is a 600px paper-toned table with a PP Editorial New wordmark,
|
|
||||||
// an ink hairline, body in Inter 16/24, and a mono colophon strip.
|
|
||||||
func MagazineBoldEmailWrapper(body string, emailCtx templates.EmailContext) string {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
_ = magazineBoldEmailTemplate(emailCtx, body).Render(context.Background(), &buf)
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hex defaults map onto the paper-pink light preset so wallets / clients that
|
|
||||||
// don't ship custom colours still resolve to the brand's paper tone.
|
|
||||||
// Spec §4 paper-pink light: background `38 30% 96%` → ~#F7F2EB
|
|
||||||
// foreground `0 0% 8%` → ~#141414
|
|
||||||
// border `0 0% 88%` → ~#E0E0E0
|
|
||||||
// muted `38 20% 92%`→ ~#EEE9E0
|
|
||||||
// mutedFg `0 0% 40%` → ~#666666
|
|
||||||
// accent `330 95% 58%` → ~#F0298A
|
|
||||||
func mbEmailBg(ctx templates.EmailContext) string {
|
|
||||||
if ctx.Colors.Background != "" {
|
|
||||||
return ctx.Colors.Background
|
|
||||||
}
|
|
||||||
return "#F7F2EB"
|
|
||||||
}
|
|
||||||
|
|
||||||
func mbEmailFg(ctx templates.EmailContext) string {
|
|
||||||
if ctx.Colors.Foreground != "" {
|
|
||||||
return ctx.Colors.Foreground
|
|
||||||
}
|
|
||||||
return "#141414"
|
|
||||||
}
|
|
||||||
|
|
||||||
func mbEmailMuted(ctx templates.EmailContext) string {
|
|
||||||
if ctx.Colors.Muted != "" {
|
|
||||||
return ctx.Colors.Muted
|
|
||||||
}
|
|
||||||
return "#EEE9E0"
|
|
||||||
}
|
|
||||||
|
|
||||||
func mbEmailMutedFg(ctx templates.EmailContext) string {
|
|
||||||
if ctx.Colors.MutedForeground != "" {
|
|
||||||
return ctx.Colors.MutedForeground
|
|
||||||
}
|
|
||||||
return "#666666"
|
|
||||||
}
|
|
||||||
|
|
||||||
func mbEmailBorder(ctx templates.EmailContext) string {
|
|
||||||
if ctx.Colors.Border != "" {
|
|
||||||
return ctx.Colors.Border
|
|
||||||
}
|
|
||||||
return "#E0E0E0"
|
|
||||||
}
|
|
||||||
|
|
||||||
func mbEmailCard(ctx templates.EmailContext) string {
|
|
||||||
if ctx.Colors.Card != "" {
|
|
||||||
return ctx.Colors.Card
|
|
||||||
}
|
|
||||||
return "#FFFFFF"
|
|
||||||
}
|
|
||||||
|
|
||||||
// mbEmailDisplayStack is the serif fallback the wordmark falls back to in
|
|
||||||
// clients that strip @font-face (Outlook 365 web). System serifs only.
|
|
||||||
const mbEmailDisplayStack = `'PP Editorial New', Georgia, 'Times New Roman', serif`
|
|
||||||
|
|
||||||
// mbEmailBodyStack is the sans fallback used for body copy in clients that
|
|
||||||
// strip @font-face.
|
|
||||||
const mbEmailBodyStack = `'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif`
|
|
||||||
|
|
||||||
// mbEmailMonoStack is the mono fallback used for the colophon strip.
|
|
||||||
const mbEmailMonoStack = `'JetBrains Mono', 'Courier New', Courier, monospace`
|
|
||||||
|
|
||||||
// mbEmailStyleBody styles the outer body tag.
|
|
||||||
func mbEmailStyleBody(ctx templates.EmailContext) string {
|
|
||||||
return fmt.Sprintf("background-color: %s; margin: 0; padding: 0; font-family: %s;", mbEmailBg(ctx), mbEmailBodyStack)
|
|
||||||
}
|
|
||||||
|
|
||||||
func mbEmailStyleHeader(ctx templates.EmailContext) string {
|
|
||||||
return fmt.Sprintf("padding: 32px 40px; background-color: %s; border-bottom: 1px solid %s;", mbEmailCard(ctx), mbEmailBorder(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
func mbEmailStyleWordmark(ctx templates.EmailContext) string {
|
|
||||||
return fmt.Sprintf("margin: 0; font-family: %s; font-style: italic; font-weight: 200; font-size: 42px; line-height: 1; letter-spacing: -0.02em; color: %s;", mbEmailDisplayStack, mbEmailFg(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
func mbEmailStyleBodyContent(ctx templates.EmailContext) string {
|
|
||||||
return fmt.Sprintf("padding: 40px 48px; background-color: %s; color: %s; font-family: %s; font-size: 16px; line-height: 24px;", mbEmailCard(ctx), mbEmailFg(ctx), mbEmailBodyStack)
|
|
||||||
}
|
|
||||||
|
|
||||||
func mbEmailStyleColophon(ctx templates.EmailContext) string {
|
|
||||||
return fmt.Sprintf("padding: 24px 48px; background-color: %s; border-top: 1px solid %s; font-family: %s; font-size: 12px; line-height: 18px; color: %s; letter-spacing: 0.08em; text-transform: uppercase;", mbEmailMuted(ctx), mbEmailBorder(ctx), mbEmailMonoStack, mbEmailMutedFg(ctx))
|
|
||||||
}
|
|
||||||
@ -1,83 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"git.dev.alexdunmow.com/block/core/templates"
|
|
||||||
)
|
|
||||||
|
|
||||||
// magazineBoldEmailTemplate is the 600px paper-toned editorial email wrapper.
|
|
||||||
// It uses inlined hex defaults that map onto the paper-pink light preset so the
|
|
||||||
// layout reads correctly in clients that strip server CSS.
|
|
||||||
templ magazineBoldEmailTemplate(emailCtx templates.EmailContext, body string) {
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8"/>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
|
||||||
<meta name="x-apple-disable-message-reformatting"/>
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
|
||||||
<title>{ emailCtx.SiteSettings.SiteName }</title>
|
|
||||||
<style type="text/css">
|
|
||||||
body, table, td, p, a, li, blockquote { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
|
|
||||||
table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
|
|
||||||
img { -ms-interpolation-mode: bicubic; border: 0; height: auto; line-height: 100%; outline: none; text-decoration: none; }
|
|
||||||
body { margin: 0 !important; padding: 0 !important; width: 100% !important; }
|
|
||||||
@media only screen and (max-width: 620px) {
|
|
||||||
.mb-email-container { width: 100% !important; max-width: 100% !important; }
|
|
||||||
.mb-email-pad { padding-left: 24px !important; padding-right: 24px !important; }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body style={ mbEmailStyleBody(emailCtx) }>
|
|
||||||
if emailCtx.PreviewText != "" {
|
|
||||||
<div style="display:none;max-height:0;overflow:hidden;mso-hide:all;">
|
|
||||||
{ emailCtx.PreviewText }
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
|
|
||||||
<tr>
|
|
||||||
<td align="center" style={ fmt.Sprintf("padding: 48px 10px; background-color: %s;", mbEmailBg(emailCtx)) }>
|
|
||||||
<table role="presentation" class="mb-email-container" width="600" cellspacing="0" cellpadding="0" border="0" style={ fmt.Sprintf("max-width: 600px; background-color: %s; border: 1px solid %s;", mbEmailCard(emailCtx), mbEmailBorder(emailCtx)) }>
|
|
||||||
<!-- Wordmark / masthead -->
|
|
||||||
<tr>
|
|
||||||
<td align="left" class="mb-email-pad" style={ mbEmailStyleHeader(emailCtx) }>
|
|
||||||
if emailCtx.SiteSettings.LogoURL != "" {
|
|
||||||
<img src={ emailCtx.SiteSettings.LogoURL } alt={ emailCtx.SiteSettings.SiteName } style="max-height:48px;width:auto;display:block;"/>
|
|
||||||
} else if emailCtx.SiteSettings.SiteName != "" {
|
|
||||||
<h1 style={ mbEmailStyleWordmark(emailCtx) }>{ emailCtx.SiteSettings.SiteName }</h1>
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<!-- Ink hairline -->
|
|
||||||
<tr>
|
|
||||||
<td style={ fmt.Sprintf("height: 1px; line-height: 1px; background-color: %s;", mbEmailFg(emailCtx)) }> </td>
|
|
||||||
</tr>
|
|
||||||
<!-- Body -->
|
|
||||||
<tr>
|
|
||||||
<td class="mb-email-pad" style={ mbEmailStyleBodyContent(emailCtx) }>
|
|
||||||
@templ.Raw(body)
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<!-- Mono colophon -->
|
|
||||||
<tr>
|
|
||||||
<td class="mb-email-pad" style={ mbEmailStyleColophon(emailCtx) }>
|
|
||||||
<div>{ emailCtx.SiteSettings.SiteName }</div>
|
|
||||||
if emailCtx.SiteSettings.SiteURL != "" {
|
|
||||||
<div style="margin-top:8px;">
|
|
||||||
<a href={ templ.SafeURL(emailCtx.SiteSettings.SiteURL) } style={ fmt.Sprintf("color: %s; text-decoration: underline;", mbEmailMutedFg(emailCtx)) }>{ emailCtx.SiteSettings.SiteURL }</a>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
if emailCtx.UnsubscribeURL != "" {
|
|
||||||
<div style="margin-top:8px;">
|
|
||||||
<a href={ templ.SafeURL(emailCtx.UnsubscribeURL) } style={ fmt.Sprintf("color: %s; text-decoration: underline;", mbEmailMutedFg(emailCtx)) }>Unsubscribe</a>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
}
|
|
||||||
@ -1,344 +0,0 @@
|
|||||||
// Code generated by templ - DO NOT EDIT.
|
|
||||||
|
|
||||||
// templ: version: v0.3.1020
|
|
||||||
package main
|
|
||||||
|
|
||||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
|
||||||
|
|
||||||
import "github.com/a-h/templ"
|
|
||||||
import templruntime "github.com/a-h/templ/runtime"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"git.dev.alexdunmow.com/block/core/templates"
|
|
||||||
)
|
|
||||||
|
|
||||||
// magazineBoldEmailTemplate is the 600px paper-toned editorial email wrapper.
|
|
||||||
// It uses inlined hex defaults that map onto the paper-pink light preset so the
|
|
||||||
// layout reads correctly in clients that strip server CSS.
|
|
||||||
func magazineBoldEmailTemplate(emailCtx templates.EmailContext, body string) templ.Component {
|
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
|
||||||
return templ_7745c5c3_CtxErr
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
|
||||||
if !templ_7745c5c3_IsBuffer {
|
|
||||||
defer func() {
|
|
||||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err == nil {
|
|
||||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
ctx = templ.InitializeContext(ctx)
|
|
||||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
|
||||||
if templ_7745c5c3_Var1 == nil {
|
|
||||||
templ_7745c5c3_Var1 = templ.NopComponent
|
|
||||||
}
|
|
||||||
ctx = templ.ClearChildren(ctx)
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:o=\"urn:schemas-microsoft-com:office:office\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><meta name=\"x-apple-disable-message-reformatting\"><meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"><title>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var2 string
|
|
||||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(emailCtx.SiteSettings.SiteName)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 19, Col: 42}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</title><style type=\"text/css\">\n\t\t\t\tbody, table, td, p, a, li, blockquote { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }\n\t\t\t\ttable, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; }\n\t\t\t\timg { -ms-interpolation-mode: bicubic; border: 0; height: auto; line-height: 100%; outline: none; text-decoration: none; }\n\t\t\t\tbody { margin: 0 !important; padding: 0 !important; width: 100% !important; }\n\t\t\t\t@media only screen and (max-width: 620px) {\n\t\t\t\t\t.mb-email-container { width: 100% !important; max-width: 100% !important; }\n\t\t\t\t\t.mb-email-pad { padding-left: 24px !important; padding-right: 24px !important; }\n\t\t\t\t}\n\t\t\t</style></head><body style=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var3 string
|
|
||||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(mbEmailStyleBody(emailCtx))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 31, Col: 42}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
if emailCtx.PreviewText != "" {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<div style=\"display:none;max-height:0;overflow:hidden;mso-hide:all;\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var4 string
|
|
||||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(emailCtx.PreviewText)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 34, Col: 27}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<table role=\"presentation\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\"><tr><td align=\"center\" style=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var5 string
|
|
||||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(fmt.Sprintf("padding: 48px 10px; background-color: %s;", mbEmailBg(emailCtx)))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 39, Col: 109}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "\"><table role=\"presentation\" class=\"mb-email-container\" width=\"600\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" style=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var6 string
|
|
||||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(fmt.Sprintf("max-width: 600px; background-color: %s; border: 1px solid %s;", mbEmailCard(emailCtx), mbEmailBorder(emailCtx)))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 40, Col: 247}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "\"><!-- Wordmark / masthead --><tr><td align=\"left\" class=\"mb-email-pad\" style=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var7 string
|
|
||||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(mbEmailStyleHeader(emailCtx))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 43, Col: 82}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
if emailCtx.SiteSettings.LogoURL != "" {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<img src=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var8 string
|
|
||||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.ResolveAttributeValue(emailCtx.SiteSettings.LogoURL)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 45, Col: 50}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var8)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\" alt=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var9 string
|
|
||||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.ResolveAttributeValue(emailCtx.SiteSettings.SiteName)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 45, Col: 89}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var9)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "\" style=\"max-height:48px;width:auto;display:block;\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
} else if emailCtx.SiteSettings.SiteName != "" {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<h1 style=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var10 string
|
|
||||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(mbEmailStyleWordmark(emailCtx))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 47, Col: 52}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var11 string
|
|
||||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(emailCtx.SiteSettings.SiteName)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 47, Col: 87}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</h1>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</td></tr><!-- Ink hairline --><tr><td style=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var12 string
|
|
||||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(fmt.Sprintf("height: 1px; line-height: 1px; background-color: %s;", mbEmailFg(emailCtx)))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 53, Col: 108}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "\"> </td></tr><!-- Body --><tr><td class=\"mb-email-pad\" style=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var13 string
|
|
||||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(mbEmailStyleBodyContent(emailCtx))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 57, Col: 74}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templ.Raw(body).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</td></tr><!-- Mono colophon --><tr><td class=\"mb-email-pad\" style=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var14 string
|
|
||||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(mbEmailStyleColophon(emailCtx))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 63, Col: 71}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "\"><div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var15 string
|
|
||||||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(emailCtx.SiteSettings.SiteName)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 64, Col: 46}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "</div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
if emailCtx.SiteSettings.SiteURL != "" {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "<div style=\"margin-top:8px;\"><a href=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var16 templ.SafeURL
|
|
||||||
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(emailCtx.SiteSettings.SiteURL))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 67, Col: 65}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\" style=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var17 string
|
|
||||||
templ_7745c5c3_Var17, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(fmt.Sprintf("color: %s; text-decoration: underline;", mbEmailMutedFg(emailCtx)))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 67, Col: 155}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var18 string
|
|
||||||
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(emailCtx.SiteSettings.SiteURL)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 67, Col: 189}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "</a></div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if emailCtx.UnsubscribeURL != "" {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<div style=\"margin-top:8px;\"><a href=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var19 templ.SafeURL
|
|
||||||
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(emailCtx.UnsubscribeURL))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 72, Col: 59}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\" style=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var20 string
|
|
||||||
templ_7745c5c3_Var20, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(fmt.Sprintf("color: %s; text-decoration: underline;", mbEmailMutedFg(emailCtx)))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 72, Col: 149}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "\">Unsubscribe</a></div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</td></tr></table></td></tr></table></body></html>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ = templruntime.GeneratedTemplate
|
|
||||||
49
embed.go
49
embed.go
@ -1,49 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"embed"
|
|
||||||
"io/fs"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed assets/*
|
|
||||||
var assetsFS embed.FS
|
|
||||||
|
|
||||||
//go:embed schemas/*
|
|
||||||
var schemasFS embed.FS
|
|
||||||
|
|
||||||
//go:embed presets.json
|
|
||||||
var presetsData []byte
|
|
||||||
|
|
||||||
//go:embed fonts.json
|
|
||||||
var fontsData []byte
|
|
||||||
|
|
||||||
//go:embed plugin.mod
|
|
||||||
var pluginModBytes []byte
|
|
||||||
|
|
||||||
// Assets returns the embedded assets filesystem.
|
|
||||||
func Assets() fs.FS {
|
|
||||||
sub, _ := fs.Sub(assetsFS, "assets")
|
|
||||||
return sub
|
|
||||||
}
|
|
||||||
|
|
||||||
// Schemas returns the embedded schemas filesystem.
|
|
||||||
func Schemas() fs.FS {
|
|
||||||
sub, _ := fs.Sub(schemasFS, "schemas")
|
|
||||||
return sub
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssetsHandler returns an http.Handler that serves the embedded assets.
|
|
||||||
func AssetsHandler() http.Handler {
|
|
||||||
return http.FileServer(http.FS(Assets()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ThemePresets returns the embedded theme presets JSON.
|
|
||||||
func ThemePresets() []byte {
|
|
||||||
return presetsData
|
|
||||||
}
|
|
||||||
|
|
||||||
// BundledFonts returns the embedded fonts manifest JSON.
|
|
||||||
func BundledFonts() []byte {
|
|
||||||
return fontsData
|
|
||||||
}
|
|
||||||
20
go.mod
20
go.mod
@ -1,20 +0,0 @@
|
|||||||
module git.dev.alexdunmow.com/block/themes/magazine-bold
|
|
||||||
|
|
||||||
go 1.26.4
|
|
||||||
|
|
||||||
require (
|
|
||||||
git.dev.alexdunmow.com/block/core v0.15.2
|
|
||||||
github.com/a-h/templ v0.3.1020
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
connectrpc.com/connect v1.20.0 // indirect
|
|
||||||
github.com/BurntSushi/toml v1.6.0 // indirect
|
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
|
||||||
github.com/jackc/pgx/v5 v5.9.2 // indirect
|
|
||||||
golang.org/x/mod v0.34.0 // indirect
|
|
||||||
golang.org/x/text v0.36.0 // indirect
|
|
||||||
google.golang.org/protobuf v1.36.11 // indirect
|
|
||||||
)
|
|
||||||
46
go.sum
46
go.sum
@ -1,46 +0,0 @@
|
|||||||
connectrpc.com/connect v1.20.0 h1:6TNDAB+WeNd2uolWNlYczB5E0KNNaVMNUEx8JEUsPmQ=
|
|
||||||
connectrpc.com/connect v1.20.0/go.mod h1:A2ygJrukXwWy32vkCAAHNVguZrqZ+jeZ9rGRnGR4dN4=
|
|
||||||
git.dev.alexdunmow.com/block/core v0.15.2 h1:qTjJxa1pClxwehkdTxXiJDpHQLr5DoXBUCa40QRO0L8=
|
|
||||||
git.dev.alexdunmow.com/block/core v0.15.2/go.mod h1:A+mgA9oeWoPhXruv4Mjpywwl2A9B5I3cKzlJjalBdlE=
|
|
||||||
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
|
|
||||||
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
|
||||||
github.com/a-h/templ v0.3.1020 h1:ypAT/L5ySWEnZ6Zft/5yfoWXYYkhFNvEFOeeqecg4tw=
|
|
||||||
github.com/a-h/templ v0.3.1020/go.mod h1:A2DlK61v+K+NRoGnhmYbNYVmtYHcFO5/AisMvBdDxTM=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
|
||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
|
||||||
github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw=
|
|
||||||
github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
|
|
||||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
|
||||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
|
||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
|
||||||
github.com/tetratelabs/wazero v1.12.0 h1:DuWcpNu/FzgEXgGBDp8J1Spc+CWOvvtvVyjKlaZopYU=
|
|
||||||
github.com/tetratelabs/wazero v1.12.0/go.mod h1:LvKtzl2RqO4gyF27BiXU+nKAjcV8f38U+kP/q2vgxh0=
|
|
||||||
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
|
|
||||||
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
|
|
||||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
|
||||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
|
||||||
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
|
|
||||||
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
|
||||||
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
|
||||||
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
|
||||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
|
||||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
@ -1,63 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// HeadingOverrideBlock renders headings with the Magazine Bold display-serif scale
|
|
||||||
// and an optional drop-cap on h1.
|
|
||||||
//
|
|
||||||
// Built-in heading content shape: {"text": "...", "level": 1-6, "textClass": "..."}
|
|
||||||
func HeadingOverrideBlock(ctx context.Context, content map[string]any) string {
|
|
||||||
text := getString(content, "text")
|
|
||||||
textClass := getString(content, "textClass")
|
|
||||||
level := parseHeadingLevel(content)
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
_ = mbHeadingComponent(level, text, textClass).Render(ctx, &buf)
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseHeadingLevel parses the level from content, defaulting to 2.
|
|
||||||
func parseHeadingLevel(content map[string]any) int {
|
|
||||||
if level, ok := content["level"].(float64); ok {
|
|
||||||
l := int(level)
|
|
||||||
if l >= 1 && l <= 6 {
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if level, ok := content["level"].(int); ok {
|
|
||||||
if level >= 1 && level <= 6 {
|
|
||||||
return level
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if level, ok := content["level"].(string); ok {
|
|
||||||
if l, err := strconv.Atoi(level); err == nil && l >= 1 && l <= 6 {
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
|
|
||||||
// mbHeadingBaseClass returns the Magazine Bold class for each heading level.
|
|
||||||
// h1 picks up the .text-folio clamp + drop-cap behaviour.
|
|
||||||
func mbHeadingBaseClass(level int) string {
|
|
||||||
switch level {
|
|
||||||
case 1:
|
|
||||||
return "font-display text-folio text-foreground mb-dropcap"
|
|
||||||
case 2:
|
|
||||||
return "font-display text-deck text-foreground"
|
|
||||||
case 3:
|
|
||||||
return "font-display text-3xl text-foreground"
|
|
||||||
case 4:
|
|
||||||
return "font-serif-sub text-2xl text-foreground"
|
|
||||||
case 5:
|
|
||||||
return "font-serif-sub text-xl text-foreground"
|
|
||||||
case 6:
|
|
||||||
return "font-mono text-kicker text-muted-foreground"
|
|
||||||
default:
|
|
||||||
return "font-display text-deck text-foreground"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
// mbHeadingComponent renders a heading at the requested level with Magazine Bold scale.
|
|
||||||
templ mbHeadingComponent(level int, text, textClass string) {
|
|
||||||
switch level {
|
|
||||||
case 1:
|
|
||||||
<h1 class={ mbHeadingBaseClass(1), textClass }>{ text }</h1>
|
|
||||||
case 2:
|
|
||||||
<h2 class={ mbHeadingBaseClass(2), textClass }>{ text }</h2>
|
|
||||||
case 3:
|
|
||||||
<h3 class={ mbHeadingBaseClass(3), textClass }>{ text }</h3>
|
|
||||||
case 4:
|
|
||||||
<h4 class={ mbHeadingBaseClass(4), textClass }>{ text }</h4>
|
|
||||||
case 5:
|
|
||||||
<h5 class={ mbHeadingBaseClass(5), textClass }>{ text }</h5>
|
|
||||||
case 6:
|
|
||||||
<h6 class={ mbHeadingBaseClass(6), textClass }>{ text }</h6>
|
|
||||||
default:
|
|
||||||
<h2 class={ mbHeadingBaseClass(2), textClass }>{ text }</h2>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,291 +0,0 @@
|
|||||||
// Code generated by templ - DO NOT EDIT.
|
|
||||||
|
|
||||||
// templ: version: v0.3.1020
|
|
||||||
package main
|
|
||||||
|
|
||||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
|
||||||
|
|
||||||
import "github.com/a-h/templ"
|
|
||||||
import templruntime "github.com/a-h/templ/runtime"
|
|
||||||
|
|
||||||
// mbHeadingComponent renders a heading at the requested level with Magazine Bold scale.
|
|
||||||
func mbHeadingComponent(level int, text, textClass string) templ.Component {
|
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
|
||||||
return templ_7745c5c3_CtxErr
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
|
||||||
if !templ_7745c5c3_IsBuffer {
|
|
||||||
defer func() {
|
|
||||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err == nil {
|
|
||||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
ctx = templ.InitializeContext(ctx)
|
|
||||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
|
||||||
if templ_7745c5c3_Var1 == nil {
|
|
||||||
templ_7745c5c3_Var1 = templ.NopComponent
|
|
||||||
}
|
|
||||||
ctx = templ.ClearChildren(ctx)
|
|
||||||
switch level {
|
|
||||||
case 1:
|
|
||||||
var templ_7745c5c3_Var2 = []any{mbHeadingBaseClass(1), textClass}
|
|
||||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var2...)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<h1 class=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var3 string
|
|
||||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var2).String())
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 1, Col: 0}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var4 string
|
|
||||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(text)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 7, Col: 56}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</h1>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
case 2:
|
|
||||||
var templ_7745c5c3_Var5 = []any{mbHeadingBaseClass(2), textClass}
|
|
||||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var5...)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<h2 class=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var6 string
|
|
||||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var5).String())
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 1, Col: 0}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var6)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var7 string
|
|
||||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(text)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 9, Col: 56}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</h2>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
case 3:
|
|
||||||
var templ_7745c5c3_Var8 = []any{mbHeadingBaseClass(3), textClass}
|
|
||||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var8...)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<h3 class=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var9 string
|
|
||||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var8).String())
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 1, Col: 0}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var9)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var10 string
|
|
||||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(text)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 11, Col: 56}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</h3>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
case 4:
|
|
||||||
var templ_7745c5c3_Var11 = []any{mbHeadingBaseClass(4), textClass}
|
|
||||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var11...)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<h4 class=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var12 string
|
|
||||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var11).String())
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 1, Col: 0}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var12)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var13 string
|
|
||||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(text)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 13, Col: 56}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</h4>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
case 5:
|
|
||||||
var templ_7745c5c3_Var14 = []any{mbHeadingBaseClass(5), textClass}
|
|
||||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var14...)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<h5 class=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var15 string
|
|
||||||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var14).String())
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 1, Col: 0}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var15)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var16 string
|
|
||||||
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(text)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 15, Col: 56}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</h5>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
case 6:
|
|
||||||
var templ_7745c5c3_Var17 = []any{mbHeadingBaseClass(6), textClass}
|
|
||||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var17...)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<h6 class=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var18 string
|
|
||||||
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var17).String())
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 1, Col: 0}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var18)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var19 string
|
|
||||||
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(text)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 17, Col: 56}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</h6>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
var templ_7745c5c3_Var20 = []any{mbHeadingBaseClass(2), textClass}
|
|
||||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var20...)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<h2 class=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var21 string
|
|
||||||
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var20).String())
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 1, Col: 0}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var21)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var22 string
|
|
||||||
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(text)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 19, Col: 56}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "</h2>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ = templruntime.GeneratedTemplate
|
|
||||||
71
helpers.go
71
helpers.go
@ -1,71 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
// getString extracts a string value from content map.
|
|
||||||
func getString(content map[string]any, key string) string {
|
|
||||||
if v, ok := content[key].(string); ok {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// getInt extracts an int value from content map (handles float64 from JSON).
|
|
||||||
func getInt(content map[string]any, key string, defaultVal int) int {
|
|
||||||
if v, ok := content[key].(float64); ok {
|
|
||||||
return int(v)
|
|
||||||
}
|
|
||||||
if v, ok := content[key].(int); ok {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
return defaultVal
|
|
||||||
}
|
|
||||||
|
|
||||||
// getBool extracts a bool from content (handles "true"/"false" strings).
|
|
||||||
func getBool(content map[string]any, key string, defaultVal bool) bool {
|
|
||||||
if v, ok := content[key].(bool); ok {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
if v, ok := content[key].(string); ok {
|
|
||||||
switch v {
|
|
||||||
case "true", "TRUE", "True", "1", "yes", "on":
|
|
||||||
return true
|
|
||||||
case "false", "FALSE", "False", "0", "no", "off", "":
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return defaultVal
|
|
||||||
}
|
|
||||||
|
|
||||||
// getSlice extracts a slice of maps from content.
|
|
||||||
func getSlice(content map[string]any, key string) []map[string]any {
|
|
||||||
if v, ok := content[key].([]any); ok {
|
|
||||||
result := make([]map[string]any, 0, len(v))
|
|
||||||
for _, item := range v {
|
|
||||||
if m, ok := item.(map[string]any); ok {
|
|
||||||
result = append(result, m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// normalizeAccent maps the schema accent enum into a CSS class suffix.
|
|
||||||
// Unknown values fall back to "pink" (the default-preset accent slot).
|
|
||||||
func normalizeAccent(raw string) string {
|
|
||||||
switch raw {
|
|
||||||
case "pink", "blue", "lime":
|
|
||||||
return raw
|
|
||||||
default:
|
|
||||||
return "pink"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// normalizeSpan maps the photo-essay frame span enum.
|
|
||||||
func normalizeSpan(raw string) string {
|
|
||||||
switch raw {
|
|
||||||
case "half", "full", "tall":
|
|
||||||
return raw
|
|
||||||
default:
|
|
||||||
return "half"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"git.dev.alexdunmow.com/block/core/blocks"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ImageOverrideBlock renders the built-in image block with the Magazine Bold
|
|
||||||
// treatment: optional full-bleed mode + a mono caption folio.
|
|
||||||
//
|
|
||||||
// Built-in image content shape: {"src": "...", "alt": "...", "caption": "...", "fullBleed": true/false}
|
|
||||||
func ImageOverrideBlock(ctx context.Context, content map[string]any) string {
|
|
||||||
src := blocks.ResolveMediaPath(getString(content, "src"))
|
|
||||||
alt := getString(content, "alt")
|
|
||||||
caption := getString(content, "caption")
|
|
||||||
fullBleed := getBool(content, "fullBleed", false)
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
_ = mbImageComponent(src, alt, caption, fullBleed).Render(ctx, &buf)
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
// mbImageComponent renders an image with optional full-bleed mode and a numbered caption.
|
|
||||||
// Skips rendering if no src — guarantees zero broken <img>.
|
|
||||||
templ mbImageComponent(src, alt, caption string, fullBleed bool) {
|
|
||||||
if src != "" {
|
|
||||||
<figure class={ "my-8", templ.KV("mb-fullbleed", fullBleed) }>
|
|
||||||
<img src={ src } alt={ alt } class="w-full h-auto block" loading="lazy"/>
|
|
||||||
if caption != "" {
|
|
||||||
<figcaption class="mb-caption font-mono mt-2 px-6">{ caption }</figcaption>
|
|
||||||
}
|
|
||||||
</figure>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,111 +0,0 @@
|
|||||||
// Code generated by templ - DO NOT EDIT.
|
|
||||||
|
|
||||||
// templ: version: v0.3.1020
|
|
||||||
package main
|
|
||||||
|
|
||||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
|
||||||
|
|
||||||
import "github.com/a-h/templ"
|
|
||||||
import templruntime "github.com/a-h/templ/runtime"
|
|
||||||
|
|
||||||
// mbImageComponent renders an image with optional full-bleed mode and a numbered caption.
|
|
||||||
// Skips rendering if no src — guarantees zero broken <img>.
|
|
||||||
func mbImageComponent(src, alt, caption string, fullBleed bool) templ.Component {
|
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
|
||||||
return templ_7745c5c3_CtxErr
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
|
||||||
if !templ_7745c5c3_IsBuffer {
|
|
||||||
defer func() {
|
|
||||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err == nil {
|
|
||||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
ctx = templ.InitializeContext(ctx)
|
|
||||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
|
||||||
if templ_7745c5c3_Var1 == nil {
|
|
||||||
templ_7745c5c3_Var1 = templ.NopComponent
|
|
||||||
}
|
|
||||||
ctx = templ.ClearChildren(ctx)
|
|
||||||
if src != "" {
|
|
||||||
var templ_7745c5c3_Var2 = []any{"my-8", templ.KV("mb-fullbleed", fullBleed)}
|
|
||||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var2...)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<figure class=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var3 string
|
|
||||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var2).String())
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `image_override.templ`, Line: 1, Col: 0}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\"><img src=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var4 string
|
|
||||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(src)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `image_override.templ`, Line: 8, Col: 17}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\" alt=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var5 string
|
|
||||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.ResolveAttributeValue(alt)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `image_override.templ`, Line: 8, Col: 29}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var5)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\" class=\"w-full h-auto block\" loading=\"lazy\"> ")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
if caption != "" {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<figcaption class=\"mb-caption font-mono mt-2 px-6\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var6 string
|
|
||||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(caption)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `image_override.templ`, Line: 10, Col: 64}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</figcaption>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</figure>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ = templruntime.GeneratedTemplate
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"git.dev.alexdunmow.com/block/core/blocks"
|
|
||||||
)
|
|
||||||
|
|
||||||
// IssueArchiveBlockMeta is the numbered list of back-issues.
|
|
||||||
var IssueArchiveBlockMeta = blocks.BlockMeta{
|
|
||||||
Key: "issue_archive",
|
|
||||||
Title: "Issue Archive",
|
|
||||||
Description: "Numbered list of back-issues driven by a bucket query.",
|
|
||||||
Source: "magazine-bold",
|
|
||||||
Category: blocks.CategoryNavigation,
|
|
||||||
}
|
|
||||||
|
|
||||||
// IssueArchiveData is what the templ component consumes.
|
|
||||||
type IssueArchiveData struct {
|
|
||||||
BucketSlug string
|
|
||||||
Limit int
|
|
||||||
}
|
|
||||||
|
|
||||||
// IssueArchiveBlock renders the issue archive list.
|
|
||||||
// content keys: bucketSlug (bucket-picker), limit (number).
|
|
||||||
//
|
|
||||||
// NOTE: bucket-row resolution is handled by the host at render-time via
|
|
||||||
// `bucket_data` injection — this block renders the scaffold and an empty
|
|
||||||
// state if the host hasn't supplied items yet.
|
|
||||||
func IssueArchiveBlock(ctx context.Context, content map[string]any) string {
|
|
||||||
data := IssueArchiveData{
|
|
||||||
BucketSlug: getString(content, "bucketSlug"),
|
|
||||||
Limit: getInt(content, "limit", 12),
|
|
||||||
}
|
|
||||||
items := getSlice(content, "items")
|
|
||||||
if items == nil {
|
|
||||||
items = getSlice(content, "bucket_data")
|
|
||||||
}
|
|
||||||
var buf bytes.Buffer
|
|
||||||
_ = issueArchiveComponent(data, items).Render(ctx, &buf)
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
// issueArchiveComponent renders a numbered archive list.
|
|
||||||
// Unknown / empty bucket → renders an empty-state message, no broken markup.
|
|
||||||
templ issueArchiveComponent(data IssueArchiveData, items []map[string]any) {
|
|
||||||
<nav data-block="magazine-bold:issue_archive" class="py-6">
|
|
||||||
<div class="font-mono text-kicker text-muted-foreground mb-6">Issue Archive</div>
|
|
||||||
if len(items) == 0 {
|
|
||||||
<p class="font-mono mb-caption">No issues yet.</p>
|
|
||||||
} else {
|
|
||||||
<ol class="grid grid-cols-1 md:grid-cols-2 gap-x-12 gap-y-4">
|
|
||||||
for i, item := range items {
|
|
||||||
if i >= max(1, data.Limit) {
|
|
||||||
{{ break }}
|
|
||||||
}
|
|
||||||
<li class="flex items-baseline gap-4 mb-hairline-bottom py-3">
|
|
||||||
<span class="font-mono text-kicker text-muted-foreground w-10">{ fmt.Sprintf("%02d", i+1) }</span>
|
|
||||||
<span class="font-display flex-1 text-foreground">
|
|
||||||
if title, ok := item["title"].(string); ok {
|
|
||||||
{ title }
|
|
||||||
} else if name, ok := item["name"].(string); ok {
|
|
||||||
{ name }
|
|
||||||
} else {
|
|
||||||
Untitled issue
|
|
||||||
}
|
|
||||||
</span>
|
|
||||||
if date, ok := item["date"].(string); ok && date != "" {
|
|
||||||
<span class="font-mono mb-caption">{ date }</span>
|
|
||||||
}
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
</ol>
|
|
||||||
}
|
|
||||||
</nav>
|
|
||||||
}
|
|
||||||
@ -1,138 +0,0 @@
|
|||||||
// Code generated by templ - DO NOT EDIT.
|
|
||||||
|
|
||||||
// templ: version: v0.3.1020
|
|
||||||
package main
|
|
||||||
|
|
||||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
|
||||||
|
|
||||||
import "github.com/a-h/templ"
|
|
||||||
import templruntime "github.com/a-h/templ/runtime"
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
// issueArchiveComponent renders a numbered archive list.
|
|
||||||
// Unknown / empty bucket → renders an empty-state message, no broken markup.
|
|
||||||
func issueArchiveComponent(data IssueArchiveData, items []map[string]any) templ.Component {
|
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
|
||||||
return templ_7745c5c3_CtxErr
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
|
||||||
if !templ_7745c5c3_IsBuffer {
|
|
||||||
defer func() {
|
|
||||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err == nil {
|
|
||||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
ctx = templ.InitializeContext(ctx)
|
|
||||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
|
||||||
if templ_7745c5c3_Var1 == nil {
|
|
||||||
templ_7745c5c3_Var1 = templ.NopComponent
|
|
||||||
}
|
|
||||||
ctx = templ.ClearChildren(ctx)
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<nav data-block=\"magazine-bold:issue_archive\" class=\"py-6\"><div class=\"font-mono text-kicker text-muted-foreground mb-6\">Issue Archive</div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
if len(items) == 0 {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<p class=\"font-mono mb-caption\">No issues yet.</p>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<ol class=\"grid grid-cols-1 md:grid-cols-2 gap-x-12 gap-y-4\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
for i, item := range items {
|
|
||||||
if i >= max(1, data.Limit) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, " <li class=\"flex items-baseline gap-4 mb-hairline-bottom py-3\"><span class=\"font-mono text-kicker text-muted-foreground w-10\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var2 string
|
|
||||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%02d", i+1))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `issue_archive.templ`, Line: 19, Col: 95}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</span> <span class=\"font-display flex-1 text-foreground\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
if title, ok := item["title"].(string); ok {
|
|
||||||
var templ_7745c5c3_Var3 string
|
|
||||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(title)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `issue_archive.templ`, Line: 22, Col: 15}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
} else if name, ok := item["name"].(string); ok {
|
|
||||||
var templ_7745c5c3_Var4 string
|
|
||||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(name)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `issue_archive.templ`, Line: 24, Col: 14}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "Untitled issue")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</span> ")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
if date, ok := item["date"].(string); ok && date != "" {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<span class=\"font-mono mb-caption\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var5 string
|
|
||||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(date)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `issue_archive.templ`, Line: 30, Col: 48}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</span>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</li>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</ol>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</nav>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ = templruntime.GeneratedTemplate
|
|
||||||
17
main.go
17
main.go
@ -1,17 +0,0 @@
|
|||||||
//go:build wasip1
|
|
||||||
|
|
||||||
// Package main compiles the magazine-bold theme to a wasip1 reactor-mode wasm guest
|
|
||||||
// (WO-WZ-015). The .so build path is retired; `ninja plugin build` produces the
|
|
||||||
// .bnp artifact the registry serves.
|
|
||||||
//
|
|
||||||
// Reactor mode: the host runs `_initialize` once per pooled instance, which
|
|
||||||
// runs this init func (hence wasmguest.Serve) before any bn_invoke. Serve is
|
|
||||||
// non-blocking; all theme work then arrives over the ABI hook calls. main is
|
|
||||||
// never called.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import "git.dev.alexdunmow.com/block/core/plugin/wasmguest"
|
|
||||||
|
|
||||||
func init() { wasmguest.Serve(Registration) }
|
|
||||||
|
|
||||||
func main() {} // never called — reactor mode
|
|
||||||
218
manifest.yaml
Normal file
218
manifest.yaml
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
# Magazine Bold theme — codeless .bnp manifest (WO-WZ-021 Phase B1).
|
||||||
|
# Synthesized into manifest.pb by `ninja plugin build` (no plugin.wasm).
|
||||||
|
theme_presets: presets.json
|
||||||
|
bundled_fonts: fonts.json
|
||||||
|
master_pages: master_pages.json
|
||||||
|
|
||||||
|
system_templates:
|
||||||
|
- key: magazine-bold
|
||||||
|
title: Magazine Bold
|
||||||
|
description: "Editorial-print theme with oversized display type, asymmetric grids and rotating color-block accents for fashion, streetwear and culture publications."
|
||||||
|
|
||||||
|
page_templates:
|
||||||
|
- system: magazine-bold
|
||||||
|
key: default
|
||||||
|
title: Default
|
||||||
|
description: "Standard editorial page with masthead, asymmetric main, footer colophon."
|
||||||
|
slots: [masthead, main, colophon]
|
||||||
|
- system: magazine-bold
|
||||||
|
key: landing
|
||||||
|
title: Issue Landing
|
||||||
|
description: "Cover-story hero plus issue navigation strip."
|
||||||
|
slots: [cover, secondary, main, colophon]
|
||||||
|
- system: magazine-bold
|
||||||
|
key: article
|
||||||
|
title: Article / Feature
|
||||||
|
description: "Long-form feature with deck, byline rail and pull-quote gutter."
|
||||||
|
slots: [masthead, deck, main, colophon]
|
||||||
|
- system: magazine-bold
|
||||||
|
key: full-width
|
||||||
|
title: Full Bleed
|
||||||
|
description: "Edge-to-edge photo-essay layout."
|
||||||
|
slots: [masthead, main, colophon]
|
||||||
|
|
||||||
|
template_overrides:
|
||||||
|
- { template: magazine-bold, block: heading }
|
||||||
|
- { template: magazine-bold, block: text }
|
||||||
|
- { template: magazine-bold, block: image }
|
||||||
|
- { template: magazine-bold, block: button }
|
||||||
|
|
||||||
|
email_wrappers:
|
||||||
|
- magazine-bold
|
||||||
|
|
||||||
|
css:
|
||||||
|
input_css_append: |
|
||||||
|
/* ===========================================================
|
||||||
|
* Magazine Bold — theme utilities
|
||||||
|
* Appended to the Tailwind input via CSSManifest.InputCSSAppend.
|
||||||
|
* =========================================================== */
|
||||||
|
|
||||||
|
/* --- Font-slot classes (consume CMS-managed font variables) --- */
|
||||||
|
.font-display {
|
||||||
|
font-family: var(--font-heading, "Playfair Display", "Cormorant Garamond", Georgia, "Times New Roman", serif);
|
||||||
|
font-feature-settings: "ss01", "ss02", "kern", "liga";
|
||||||
|
font-optical-sizing: auto;
|
||||||
|
}
|
||||||
|
.font-serif-sub {
|
||||||
|
font-family: var(--font-heading, "Fraunces", "Playfair Display", Georgia, "Times New Roman", serif);
|
||||||
|
}
|
||||||
|
.font-sans {
|
||||||
|
font-family: var(--font-body, "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);
|
||||||
|
}
|
||||||
|
.font-mono {
|
||||||
|
font-family: var(--font-mono, "JetBrains Mono", "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Display-size utilities ------------------------------- */
|
||||||
|
/* text-folio: 220pt-equivalent oversized folio number / cover hit.
|
||||||
|
The clamp resolves to ~220px (~165pt) at 1440px, scaling down to 80px on mobile. */
|
||||||
|
.text-folio {
|
||||||
|
font-family: var(--font-heading, "Playfair Display", Georgia, serif);
|
||||||
|
font-size: clamp(80px, 18vw, 240px);
|
||||||
|
line-height: 0.85;
|
||||||
|
letter-spacing: -0.04em;
|
||||||
|
font-weight: 200;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
.text-deck {
|
||||||
|
font-family: var(--font-body, "Inter", system-ui, sans-serif);
|
||||||
|
font-size: clamp(28px, 3.2vw, 48px);
|
||||||
|
line-height: 1.15;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
.text-kicker {
|
||||||
|
font-family: var(--font-mono, "JetBrains Mono", monospace);
|
||||||
|
font-size: 12px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Hairline rule motif ---------------------------------- */
|
||||||
|
.mb-hairline { border-color: hsl(var(--border)); border-width: 1px; }
|
||||||
|
.mb-hairline-top { border-top: 1px solid hsl(var(--border)); }
|
||||||
|
.mb-hairline-bottom { border-bottom: 1px solid hsl(var(--border)); }
|
||||||
|
|
||||||
|
/* --- Asymmetric editorial grid ---------------------------- */
|
||||||
|
.mb-grid-12 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(12, minmax(0, 1fr));
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
.mb-grid-asym-cover {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(12, minmax(0, 1fr));
|
||||||
|
gap: 2rem;
|
||||||
|
align-items: end;
|
||||||
|
}
|
||||||
|
.mb-col-folio { grid-column: 1 / span 2; }
|
||||||
|
.mb-col-headline { grid-column: 3 / span 7; }
|
||||||
|
.mb-col-deck { grid-column: 10 / span 3; }
|
||||||
|
.mb-col-image-wide { grid-column: 1 / -1; }
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.mb-grid-asym-cover { grid-template-columns: 1fr; gap: 1.25rem; }
|
||||||
|
.mb-col-folio, .mb-col-headline, .mb-col-deck { grid-column: 1 / -1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Photo essay frame spans ------------------------------ */
|
||||||
|
.mb-photo-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(12, minmax(0, 1fr));
|
||||||
|
gap: 1.25rem;
|
||||||
|
}
|
||||||
|
.mb-frame-half { grid-column: span 6 / span 6; }
|
||||||
|
.mb-frame-full { grid-column: 1 / -1; }
|
||||||
|
.mb-frame-tall { grid-column: span 5 / span 5; grid-row: span 2; }
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.mb-photo-grid { grid-template-columns: 1fr; }
|
||||||
|
.mb-frame-half, .mb-frame-full, .mb-frame-tall { grid-column: 1 / -1; grid-row: auto; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Color-block accent panels ---------------------------- */
|
||||||
|
.colorblock-accent {
|
||||||
|
background-color: hsl(var(--accent));
|
||||||
|
color: hsl(var(--accent-foreground));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Per-accent pull-quote variants ----------------------- *
|
||||||
|
* These let a pull_quote override the preset accent on a per-instance basis.
|
||||||
|
* They use literal HSL triples that match the spec §4 accent values so a
|
||||||
|
* "pink" pull-quote always reads pink even on the ink-blue preset.
|
||||||
|
*/
|
||||||
|
[data-mb-accent="pink"] { --mb-accent-h: 330; --mb-accent-s: 95%; --mb-accent-l: 58%; }
|
||||||
|
[data-mb-accent="blue"] { --mb-accent-h: 220; --mb-accent-s: 100%; --mb-accent-l: 56%; }
|
||||||
|
[data-mb-accent="lime"] { --mb-accent-h: 78; --mb-accent-s: 90%; --mb-accent-l: 50%; }
|
||||||
|
[data-mb-accent] .mb-quote-bar {
|
||||||
|
background-color: hsl(var(--mb-accent-h) var(--mb-accent-s) var(--mb-accent-l));
|
||||||
|
}
|
||||||
|
[data-mb-accent] .mb-quote-text { color: hsl(var(--foreground)); }
|
||||||
|
|
||||||
|
/* --- Drop-cap (heading override) -------------------------- */
|
||||||
|
.mb-dropcap::first-letter {
|
||||||
|
font-family: var(--font-heading, "Playfair Display", Georgia, serif);
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 200;
|
||||||
|
float: left;
|
||||||
|
font-size: 5.5em;
|
||||||
|
line-height: 0.85;
|
||||||
|
padding-right: 0.08em;
|
||||||
|
margin-top: 0.04em;
|
||||||
|
color: hsl(var(--foreground));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Body text override (Inter + margin folios) ----------- */
|
||||||
|
.mb-body {
|
||||||
|
font-family: var(--font-body, "Inter", system-ui, sans-serif);
|
||||||
|
font-size: clamp(16px, 1.05vw, 18px);
|
||||||
|
line-height: 1.55;
|
||||||
|
letter-spacing: 0.005em;
|
||||||
|
color: hsl(var(--foreground));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Squared ink button ----------------------------------- */
|
||||||
|
.mb-button {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
background-color: hsl(var(--primary));
|
||||||
|
color: hsl(var(--primary-foreground));
|
||||||
|
font-family: var(--font-mono, "JetBrains Mono", monospace);
|
||||||
|
font-size: 12px;
|
||||||
|
letter-spacing: 0.14em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
padding: 0.875rem 1.5rem;
|
||||||
|
border: 1px solid hsl(var(--primary));
|
||||||
|
border-radius: 0;
|
||||||
|
transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease;
|
||||||
|
}
|
||||||
|
.mb-button:hover {
|
||||||
|
background-color: hsl(var(--accent));
|
||||||
|
color: hsl(var(--accent-foreground));
|
||||||
|
border-color: hsl(var(--accent));
|
||||||
|
}
|
||||||
|
.mb-button:focus-visible {
|
||||||
|
outline: 2px solid hsl(var(--ring));
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Caption (mono, used by image override + photo essay) -- */
|
||||||
|
.mb-caption {
|
||||||
|
font-family: var(--font-mono, "JetBrains Mono", monospace);
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.45;
|
||||||
|
letter-spacing: 0.01em;
|
||||||
|
color: hsl(var(--muted-foreground));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Full-bleed image with numbered folio ----------------- */
|
||||||
|
.mb-fullbleed { width: 100vw; max-width: 100vw; margin-left: 50%; transform: translateX(-50%); }
|
||||||
|
.mb-image-folio {
|
||||||
|
font-family: var(--font-mono, "JetBrains Mono", monospace);
|
||||||
|
font-size: 11px;
|
||||||
|
letter-spacing: 0.16em;
|
||||||
|
color: hsl(var(--muted-foreground));
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
25
master_pages.json
Normal file
25
master_pages.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"key": "magazine-bold:default-master",
|
||||||
|
"title": "Magazine Bold — Default",
|
||||||
|
"page_templates": ["default", "article"],
|
||||||
|
"blocks": [
|
||||||
|
{ "block_key": "navbar", "title": "Masthead Nav", "content": { "menuName": "main" }, "slot": "masthead", "sort_order": 0 },
|
||||||
|
{ "block_key": "magazine-bold:masthead", "title": "Issue Masthead", "content": { "issueNo": "01", "tagline": "Vol. 1 / Spring" }, "slot": "masthead", "sort_order": 1 },
|
||||||
|
{ "block_key": "slot", "title": "Main Slot", "content": { "slotName": "main" }, "slot": "main", "sort_order": 0 },
|
||||||
|
{ "block_key": "magazine-bold:colophon", "title": "Colophon", "content": { "showSocial": "true" }, "slot": "colophon", "sort_order": 0 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "magazine-bold:landing-master",
|
||||||
|
"title": "Magazine Bold — Issue Landing",
|
||||||
|
"page_templates": ["landing", "full-width"],
|
||||||
|
"blocks": [
|
||||||
|
{ "block_key": "navbar", "title": "Masthead Nav", "content": { "menuName": "main" }, "slot": "masthead", "sort_order": 0 },
|
||||||
|
{ "block_key": "magazine-bold:cover_story", "title": "Cover Story", "content": { "issueNo": "01" }, "slot": "cover", "sort_order": 0 },
|
||||||
|
{ "block_key": "magazine-bold:issue_archive", "title": "Issue Archive", "content": { "bucketSlug": "issues" }, "slot": "secondary", "sort_order": 0 },
|
||||||
|
{ "block_key": "slot", "title": "Main Slot", "content": { "slotName": "main" }, "slot": "main", "sort_order": 0 },
|
||||||
|
{ "block_key": "magazine-bold:colophon", "title": "Colophon", "content": { "showSocial": "true" }, "slot": "colophon", "sort_order": 0 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
37
masthead.go
37
masthead.go
@ -1,37 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"git.dev.alexdunmow.com/block/core/blocks"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MastheadBlockMeta is the issue-masthead (wordmark + folio bar) at the top of the page.
|
|
||||||
var MastheadBlockMeta = blocks.BlockMeta{
|
|
||||||
Key: "masthead",
|
|
||||||
Title: "Issue Masthead",
|
|
||||||
Description: "Wordmark, issue number and tagline strip — sits at the top of every page.",
|
|
||||||
Source: "magazine-bold",
|
|
||||||
Category: blocks.CategoryLayout,
|
|
||||||
}
|
|
||||||
|
|
||||||
// MastheadData is what the templ component consumes.
|
|
||||||
type MastheadData struct {
|
|
||||||
IssueNo string
|
|
||||||
Tagline string
|
|
||||||
Date string
|
|
||||||
}
|
|
||||||
|
|
||||||
// MastheadBlock renders the issue masthead.
|
|
||||||
// content keys: issueNo, tagline, date — all optional, no panic on empty.
|
|
||||||
func MastheadBlock(ctx context.Context, content map[string]any) string {
|
|
||||||
data := MastheadData{
|
|
||||||
IssueNo: getString(content, "issueNo"),
|
|
||||||
Tagline: getString(content, "tagline"),
|
|
||||||
Date: getString(content, "date"),
|
|
||||||
}
|
|
||||||
var buf bytes.Buffer
|
|
||||||
_ = mastheadComponent(data).Render(ctx, &buf)
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
// mastheadComponent renders the wordmark + folio strip.
|
|
||||||
templ mastheadComponent(data MastheadData) {
|
|
||||||
<div data-block="magazine-bold:masthead" class="py-6 mb-hairline-bottom flex items-end justify-between gap-6">
|
|
||||||
<div class="flex items-baseline gap-4">
|
|
||||||
if data.IssueNo != "" {
|
|
||||||
<span class="font-mono text-kicker">No. { data.IssueNo }</span>
|
|
||||||
}
|
|
||||||
if data.Tagline != "" {
|
|
||||||
<span class="font-mono text-kicker text-muted-foreground">{ data.Tagline }</span>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
if data.Date != "" {
|
|
||||||
<span class="font-mono text-kicker text-muted-foreground">{ data.Date }</span>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
@ -1,106 +0,0 @@
|
|||||||
// Code generated by templ - DO NOT EDIT.
|
|
||||||
|
|
||||||
// templ: version: v0.3.1020
|
|
||||||
package main
|
|
||||||
|
|
||||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
|
||||||
|
|
||||||
import "github.com/a-h/templ"
|
|
||||||
import templruntime "github.com/a-h/templ/runtime"
|
|
||||||
|
|
||||||
// mastheadComponent renders the wordmark + folio strip.
|
|
||||||
func mastheadComponent(data MastheadData) templ.Component {
|
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
|
||||||
return templ_7745c5c3_CtxErr
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
|
||||||
if !templ_7745c5c3_IsBuffer {
|
|
||||||
defer func() {
|
|
||||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err == nil {
|
|
||||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
ctx = templ.InitializeContext(ctx)
|
|
||||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
|
||||||
if templ_7745c5c3_Var1 == nil {
|
|
||||||
templ_7745c5c3_Var1 = templ.NopComponent
|
|
||||||
}
|
|
||||||
ctx = templ.ClearChildren(ctx)
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div data-block=\"magazine-bold:masthead\" class=\"py-6 mb-hairline-bottom flex items-end justify-between gap-6\"><div class=\"flex items-baseline gap-4\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
if data.IssueNo != "" {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<span class=\"font-mono text-kicker\">No. ")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var2 string
|
|
||||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(data.IssueNo)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `masthead.templ`, Line: 8, Col: 58}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</span> ")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if data.Tagline != "" {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<span class=\"font-mono text-kicker text-muted-foreground\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var3 string
|
|
||||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.Tagline)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `masthead.templ`, Line: 11, Col: 76}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</span>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
if data.Date != "" {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<span class=\"font-mono text-kicker text-muted-foreground\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var4 string
|
|
||||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(data.Date)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `masthead.templ`, Line: 15, Col: 72}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</span>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ = templruntime.GeneratedTemplate
|
|
||||||
@ -1,70 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"git.dev.alexdunmow.com/block/core/blocks"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PhotoEssayBlockMeta is the numbered photo-essay scaffolding.
|
|
||||||
var PhotoEssayBlockMeta = blocks.BlockMeta{
|
|
||||||
Key: "photo_essay",
|
|
||||||
Title: "Photo Essay",
|
|
||||||
Description: "Numbered photo-essay with asymmetric frame spans (half / full / tall) and mono captions.",
|
|
||||||
Source: "magazine-bold",
|
|
||||||
Category: blocks.CategoryContent,
|
|
||||||
}
|
|
||||||
|
|
||||||
// PhotoFrame is a single image in the photo essay.
|
|
||||||
type PhotoFrame struct {
|
|
||||||
Number int
|
|
||||||
Image string
|
|
||||||
Caption string
|
|
||||||
Span string
|
|
||||||
}
|
|
||||||
|
|
||||||
// PhotoEssayData is what the templ component consumes.
|
|
||||||
type PhotoEssayData struct {
|
|
||||||
Title string
|
|
||||||
Frames []PhotoFrame
|
|
||||||
}
|
|
||||||
|
|
||||||
// PhotoEssayBlock renders the photo essay scaffolding.
|
|
||||||
// content keys: title (text), frames (collection of {image, caption, span}).
|
|
||||||
func PhotoEssayBlock(ctx context.Context, content map[string]any) string {
|
|
||||||
rawFrames := getSlice(content, "frames")
|
|
||||||
frames := make([]PhotoFrame, 0, len(rawFrames))
|
|
||||||
for i, item := range rawFrames {
|
|
||||||
img := blocks.ResolveMediaPath(getString(item, "image"))
|
|
||||||
// Skip frames that have no image (malformed-content safety).
|
|
||||||
if img == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
frames = append(frames, PhotoFrame{
|
|
||||||
Number: i + 1,
|
|
||||||
Image: img,
|
|
||||||
Caption: getString(item, "caption"),
|
|
||||||
Span: normalizeSpan(getString(item, "span")),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
data := PhotoEssayData{
|
|
||||||
Title: getString(content, "title"),
|
|
||||||
Frames: frames,
|
|
||||||
}
|
|
||||||
var buf bytes.Buffer
|
|
||||||
_ = photoEssayComponent(data).Render(ctx, &buf)
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// photoFrameSpanClass maps a span string to its CSS utility class.
|
|
||||||
func photoFrameSpanClass(span string) string {
|
|
||||||
switch span {
|
|
||||||
case "full":
|
|
||||||
return "mb-frame-full"
|
|
||||||
case "tall":
|
|
||||||
return "mb-frame-tall"
|
|
||||||
default:
|
|
||||||
return "mb-frame-half"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
// photoEssayComponent renders a numbered photo-essay grid.
|
|
||||||
templ photoEssayComponent(data PhotoEssayData) {
|
|
||||||
<section data-block="magazine-bold:photo_essay" class="w-full py-12">
|
|
||||||
<div class="max-w-6xl mx-auto px-6">
|
|
||||||
if data.Title != "" {
|
|
||||||
<h2 class="font-display text-deck text-foreground mb-8">{ data.Title }</h2>
|
|
||||||
}
|
|
||||||
if len(data.Frames) == 0 {
|
|
||||||
<div class="font-mono mb-caption py-12">No frames yet.</div>
|
|
||||||
} else {
|
|
||||||
<div class="mb-photo-grid">
|
|
||||||
for _, frame := range data.Frames {
|
|
||||||
<figure class={ photoFrameSpanClass(frame.Span) }>
|
|
||||||
<img src={ frame.Image } alt={ frame.Caption } class="w-full h-auto block" loading="lazy"/>
|
|
||||||
<figcaption class="mb-caption font-mono mt-2 flex items-baseline gap-3">
|
|
||||||
<span class="mb-image-folio">{ fmt.Sprintf("%02d", frame.Number) }</span>
|
|
||||||
if frame.Caption != "" {
|
|
||||||
<span>{ frame.Caption }</span>
|
|
||||||
}
|
|
||||||
</figcaption>
|
|
||||||
</figure>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
}
|
|
||||||
@ -1,167 +0,0 @@
|
|||||||
// Code generated by templ - DO NOT EDIT.
|
|
||||||
|
|
||||||
// templ: version: v0.3.1020
|
|
||||||
package main
|
|
||||||
|
|
||||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
|
||||||
|
|
||||||
import "github.com/a-h/templ"
|
|
||||||
import templruntime "github.com/a-h/templ/runtime"
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
// photoEssayComponent renders a numbered photo-essay grid.
|
|
||||||
func photoEssayComponent(data PhotoEssayData) templ.Component {
|
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
|
||||||
return templ_7745c5c3_CtxErr
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
|
||||||
if !templ_7745c5c3_IsBuffer {
|
|
||||||
defer func() {
|
|
||||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err == nil {
|
|
||||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
ctx = templ.InitializeContext(ctx)
|
|
||||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
|
||||||
if templ_7745c5c3_Var1 == nil {
|
|
||||||
templ_7745c5c3_Var1 = templ.NopComponent
|
|
||||||
}
|
|
||||||
ctx = templ.ClearChildren(ctx)
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<section data-block=\"magazine-bold:photo_essay\" class=\"w-full py-12\"><div class=\"max-w-6xl mx-auto px-6\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
if data.Title != "" {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<h2 class=\"font-display text-deck text-foreground mb-8\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var2 string
|
|
||||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(data.Title)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `photo_essay.templ`, Line: 10, Col: 72}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</h2>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(data.Frames) == 0 {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<div class=\"font-mono mb-caption py-12\">No frames yet.</div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<div class=\"mb-photo-grid\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
for _, frame := range data.Frames {
|
|
||||||
var templ_7745c5c3_Var3 = []any{photoFrameSpanClass(frame.Span)}
|
|
||||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var3...)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<figure class=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var4 string
|
|
||||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var3).String())
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `photo_essay.templ`, Line: 1, Col: 0}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "\"><img src=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var5 string
|
|
||||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.ResolveAttributeValue(frame.Image)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `photo_essay.templ`, Line: 18, Col: 29}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var5)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "\" alt=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var6 string
|
|
||||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.ResolveAttributeValue(frame.Caption)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `photo_essay.templ`, Line: 18, Col: 51}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var6)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "\" class=\"w-full h-auto block\" loading=\"lazy\"><figcaption class=\"mb-caption font-mono mt-2 flex items-baseline gap-3\"><span class=\"mb-image-folio\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var7 string
|
|
||||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%02d", frame.Number))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `photo_essay.templ`, Line: 20, Col: 72}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</span> ")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
if frame.Caption != "" {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<span>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var8 string
|
|
||||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(frame.Caption)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `photo_essay.templ`, Line: 22, Col: 30}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</span>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</figcaption></figure>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</div></section>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ = templruntime.GeneratedTemplate
|
|
||||||
@ -2,11 +2,8 @@
|
|||||||
name = "magazine-bold"
|
name = "magazine-bold"
|
||||||
display_name = "Magazine Bold"
|
display_name = "Magazine Bold"
|
||||||
scope = "@themes"
|
scope = "@themes"
|
||||||
version = "0.1.2"
|
version = "0.2.0"
|
||||||
description = "Editorial-print theme with oversized display type, asymmetric grids and rotating color-block accents for fashion, streetwear and culture publications."
|
description = "Editorial-print theme with oversized display type, asymmetric grids and rotating color-block accents for fashion, streetwear and culture publications."
|
||||||
kind = "theme"
|
kind = "theme"
|
||||||
categories = ["templates"]
|
categories = ["templates"]
|
||||||
tags = ["fashion", "lifestyle", "streetwear", "magazine", "bold", "display", "music", "editorial", "art"]
|
tags = ["fashion", "lifestyle", "streetwear", "magazine", "bold", "display", "music", "editorial", "art"]
|
||||||
|
|
||||||
[compatibility]
|
|
||||||
block_core = ">=0.15.0 <0.16.0"
|
|
||||||
|
|||||||
@ -1,37 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"git.dev.alexdunmow.com/block/core/blocks"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PullQuoteBlockMeta is the color-block pull quote.
|
|
||||||
var PullQuoteBlockMeta = blocks.BlockMeta{
|
|
||||||
Key: "pull_quote",
|
|
||||||
Title: "Pull Quote",
|
|
||||||
Description: "Color-block pull quote with rotating accent (pink / blue / lime).",
|
|
||||||
Source: "magazine-bold",
|
|
||||||
Category: blocks.CategoryContent,
|
|
||||||
}
|
|
||||||
|
|
||||||
// PullQuoteData is what the templ component consumes.
|
|
||||||
type PullQuoteData struct {
|
|
||||||
Quote string
|
|
||||||
Attribution string
|
|
||||||
Accent string
|
|
||||||
}
|
|
||||||
|
|
||||||
// PullQuoteBlock renders the color-block pull quote.
|
|
||||||
// content keys: quote (richtext), attribution (text), accent (select).
|
|
||||||
func PullQuoteBlock(ctx context.Context, content map[string]any) string {
|
|
||||||
data := PullQuoteData{
|
|
||||||
Quote: getString(content, "quote"),
|
|
||||||
Attribution: getString(content, "attribution"),
|
|
||||||
Accent: normalizeAccent(getString(content, "accent")),
|
|
||||||
}
|
|
||||||
var buf bytes.Buffer
|
|
||||||
_ = pullQuoteComponent(data).Render(ctx, &buf)
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
// pullQuoteComponent renders a pull quote anchored by an accent color bar.
|
|
||||||
// The accent value drives the data-mb-accent attribute, which the appended
|
|
||||||
// CSS uses to colour the .mb-quote-bar regardless of the active preset.
|
|
||||||
templ pullQuoteComponent(data PullQuoteData) {
|
|
||||||
<aside data-block="magazine-bold:pull_quote" data-mb-accent={ data.Accent } class="my-8 grid grid-cols-12 gap-4 items-start">
|
|
||||||
<div class="mb-quote-bar col-span-1 h-full min-h-[3rem]"></div>
|
|
||||||
<blockquote class="col-span-11 mb-quote-text">
|
|
||||||
<p class="font-display text-deck">
|
|
||||||
if data.Quote != "" {
|
|
||||||
@templ.Raw(data.Quote)
|
|
||||||
}
|
|
||||||
</p>
|
|
||||||
if data.Attribution != "" {
|
|
||||||
<cite class="block mt-4 font-mono text-kicker text-muted-foreground not-italic">— { data.Attribution }</cite>
|
|
||||||
}
|
|
||||||
</blockquote>
|
|
||||||
</aside>
|
|
||||||
}
|
|
||||||
@ -1,89 +0,0 @@
|
|||||||
// Code generated by templ - DO NOT EDIT.
|
|
||||||
|
|
||||||
// templ: version: v0.3.1020
|
|
||||||
package main
|
|
||||||
|
|
||||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
|
||||||
|
|
||||||
import "github.com/a-h/templ"
|
|
||||||
import templruntime "github.com/a-h/templ/runtime"
|
|
||||||
|
|
||||||
// pullQuoteComponent renders a pull quote anchored by an accent color bar.
|
|
||||||
// The accent value drives the data-mb-accent attribute, which the appended
|
|
||||||
// CSS uses to colour the .mb-quote-bar regardless of the active preset.
|
|
||||||
func pullQuoteComponent(data PullQuoteData) templ.Component {
|
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
|
||||||
return templ_7745c5c3_CtxErr
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
|
||||||
if !templ_7745c5c3_IsBuffer {
|
|
||||||
defer func() {
|
|
||||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err == nil {
|
|
||||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
ctx = templ.InitializeContext(ctx)
|
|
||||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
|
||||||
if templ_7745c5c3_Var1 == nil {
|
|
||||||
templ_7745c5c3_Var1 = templ.NopComponent
|
|
||||||
}
|
|
||||||
ctx = templ.ClearChildren(ctx)
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<aside data-block=\"magazine-bold:pull_quote\" data-mb-accent=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var2 string
|
|
||||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.Accent)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pull_quote.templ`, Line: 7, Col: 74}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\" class=\"my-8 grid grid-cols-12 gap-4 items-start\"><div class=\"mb-quote-bar col-span-1 h-full min-h-[3rem]\"></div><blockquote class=\"col-span-11 mb-quote-text\"><p class=\"font-display text-deck\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
if data.Quote != "" {
|
|
||||||
templ_7745c5c3_Err = templ.Raw(data.Quote).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</p>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
if data.Attribution != "" {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<cite class=\"block mt-4 font-mono text-kicker text-muted-foreground not-italic\">— ")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var3 string
|
|
||||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.Attribution)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pull_quote.templ`, Line: 16, Col: 106}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</cite>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</blockquote></aside>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ = templruntime.GeneratedTemplate
|
|
||||||
172
register.go
172
register.go
@ -1,172 +0,0 @@
|
|||||||
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.
|
|
||||||
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 is the plugin entry point that registers the Magazine Bold system
|
|
||||||
// template, page templates, blocks, overrides and email wrapper.
|
|
||||||
func Register(tr templates.TemplateRegistry, br blocks.BlockRegistry) error {
|
|
||||||
tr.RegisterSystemTemplate(templates.SystemTemplateMeta{
|
|
||||||
Key: "magazine-bold",
|
|
||||||
Title: "Magazine Bold",
|
|
||||||
Description: "Editorial-print theme with oversized display type, asymmetric grids and rotating color-block accents for fashion, streetwear and culture publications.",
|
|
||||||
})
|
|
||||||
|
|
||||||
// Page templates — slots per spec §6 / UAT §3.
|
|
||||||
if err := tr.RegisterPageTemplate("magazine-bold", templates.PageTemplateMeta{
|
|
||||||
Key: "default",
|
|
||||||
Title: "Default",
|
|
||||||
Description: "Standard editorial page with masthead, asymmetric main, footer colophon.",
|
|
||||||
Slots: []string{"masthead", "main", "colophon"},
|
|
||||||
}, wrap(RenderMagazineBold)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := tr.RegisterPageTemplate("magazine-bold", templates.PageTemplateMeta{
|
|
||||||
Key: "landing",
|
|
||||||
Title: "Issue Landing",
|
|
||||||
Description: "Cover-story hero plus issue navigation strip.",
|
|
||||||
Slots: []string{"cover", "secondary", "main", "colophon"},
|
|
||||||
}, wrap(RenderMagazineBoldLanding)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := tr.RegisterPageTemplate("magazine-bold", templates.PageTemplateMeta{
|
|
||||||
Key: "article",
|
|
||||||
Title: "Article / Feature",
|
|
||||||
Description: "Long-form feature with deck, byline rail and pull-quote gutter.",
|
|
||||||
Slots: []string{"masthead", "deck", "main", "colophon"},
|
|
||||||
}, wrap(RenderMagazineBoldArticle)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := tr.RegisterPageTemplate("magazine-bold", templates.PageTemplateMeta{
|
|
||||||
Key: "full-width",
|
|
||||||
Title: "Full Bleed",
|
|
||||||
Description: "Edge-to-edge photo-essay layout.",
|
|
||||||
Slots: []string{"masthead", "main", "colophon"},
|
|
||||||
}, wrap(RenderMagazineBoldFullWidth)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Schemas BEFORE blocks (per UAT §3).
|
|
||||||
if err := br.LoadSchemasFromFS(Schemas()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Theme blocks.
|
|
||||||
br.Register(MastheadBlockMeta, MastheadBlock)
|
|
||||||
br.Register(CoverStoryBlockMeta, CoverStoryBlock)
|
|
||||||
br.Register(PhotoEssayBlockMeta, PhotoEssayBlock)
|
|
||||||
br.Register(PullQuoteBlockMeta, PullQuoteBlock)
|
|
||||||
br.Register(IssueArchiveBlockMeta, IssueArchiveBlock)
|
|
||||||
br.Register(ColophonBlockMeta, ColophonBlock)
|
|
||||||
|
|
||||||
// Built-in overrides — only active when this theme is selected.
|
|
||||||
br.RegisterTemplateOverride("magazine-bold", "heading", HeadingOverrideBlock)
|
|
||||||
br.RegisterTemplateOverride("magazine-bold", "text", TextOverrideBlock)
|
|
||||||
br.RegisterTemplateOverride("magazine-bold", "image", ImageOverrideBlock)
|
|
||||||
br.RegisterTemplateOverride("magazine-bold", "button", ButtonOverrideBlock)
|
|
||||||
|
|
||||||
// Branded email wrapper.
|
|
||||||
tr.RegisterEmailWrapper("magazine-bold", MagazineBoldEmailWrapper)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultMasterPages returns the default master pages Magazine Bold seeds.
|
|
||||||
func DefaultMasterPages() []plugin.MasterPageDefinition {
|
|
||||||
return []plugin.MasterPageDefinition{
|
|
||||||
{
|
|
||||||
Key: "magazine-bold:default-master",
|
|
||||||
Title: "Magazine Bold — Default",
|
|
||||||
PageTemplates: []string{"default", "article"},
|
|
||||||
Blocks: []plugin.MasterPageBlock{
|
|
||||||
{
|
|
||||||
BlockKey: "navbar",
|
|
||||||
Title: "Masthead Nav",
|
|
||||||
Content: map[string]any{"menuName": "main"},
|
|
||||||
Slot: "masthead",
|
|
||||||
SortOrder: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
BlockKey: "magazine-bold:masthead",
|
|
||||||
Title: "Issue Masthead",
|
|
||||||
Content: map[string]any{"issueNo": "01", "tagline": "Vol. 1 / Spring"},
|
|
||||||
Slot: "masthead",
|
|
||||||
SortOrder: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
BlockKey: "slot",
|
|
||||||
Title: "Main Slot",
|
|
||||||
Content: map[string]any{"slotName": "main"},
|
|
||||||
Slot: "main",
|
|
||||||
SortOrder: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
BlockKey: "magazine-bold:colophon",
|
|
||||||
Title: "Colophon",
|
|
||||||
Content: map[string]any{"showSocial": "true"},
|
|
||||||
Slot: "colophon",
|
|
||||||
SortOrder: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Key: "magazine-bold:landing-master",
|
|
||||||
Title: "Magazine Bold — Issue Landing",
|
|
||||||
PageTemplates: []string{"landing", "full-width"},
|
|
||||||
Blocks: []plugin.MasterPageBlock{
|
|
||||||
{
|
|
||||||
BlockKey: "navbar",
|
|
||||||
Title: "Masthead Nav",
|
|
||||||
Content: map[string]any{"menuName": "main"},
|
|
||||||
Slot: "masthead",
|
|
||||||
SortOrder: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
BlockKey: "magazine-bold:cover_story",
|
|
||||||
Title: "Cover Story",
|
|
||||||
Content: map[string]any{"issueNo": "01"},
|
|
||||||
Slot: "cover",
|
|
||||||
SortOrder: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
BlockKey: "magazine-bold:issue_archive",
|
|
||||||
Title: "Issue Archive",
|
|
||||||
Content: map[string]any{"bucketSlug": "issues"},
|
|
||||||
Slot: "secondary",
|
|
||||||
SortOrder: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
BlockKey: "slot",
|
|
||||||
Title: "Main Slot",
|
|
||||||
Content: map[string]any{"slotName": "main"},
|
|
||||||
Slot: "main",
|
|
||||||
SortOrder: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
BlockKey: "magazine-bold:colophon",
|
|
||||||
Title: "Colophon",
|
|
||||||
Content: map[string]any{"showSocial": "true"},
|
|
||||||
Slot: "colophon",
|
|
||||||
SortOrder: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/fs"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"git.dev.alexdunmow.com/block/core/blocks"
|
|
||||||
"git.dev.alexdunmow.com/block/core/plugin"
|
|
||||||
"git.dev.alexdunmow.com/block/core/templates"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Registration is the compile-time plugin registration for the Magazine Bold template.
|
|
||||||
var Registration = plugin.PluginRegistration{
|
|
||||||
Name: "magazine-bold",
|
|
||||||
Version: plugin.ParseModVersion(pluginModBytes),
|
|
||||||
Register: func(tr templates.TemplateRegistry, br blocks.BlockRegistry) error {
|
|
||||||
return Register(tr, br)
|
|
||||||
},
|
|
||||||
Assets: func() http.Handler { return AssetsHandler() },
|
|
||||||
Schemas: func() fs.FS { return Schemas() },
|
|
||||||
ThemePresets: func() []byte { return ThemePresets() },
|
|
||||||
BundledFonts: func() []byte { return BundledFonts() },
|
|
||||||
MasterPages: func() []plugin.MasterPageDefinition { return DefaultMasterPages() },
|
|
||||||
CSSManifest: func() *plugin.CSSManifest { return ThemeCSSManifest() },
|
|
||||||
}
|
|
||||||
|
|
||||||
// ThemeCSSManifest returns the CSS manifest for Magazine Bold. The
|
|
||||||
// InputCSSAppend payload is appended into the host Tailwind input so the
|
|
||||||
// theme's display-type utilities (`.text-folio`, `.text-deck`,
|
|
||||||
// `.font-display`, etc.) and hairline rules ship with the compiled CSS.
|
|
||||||
//
|
|
||||||
// NOTE: per docs/FONTS.md, this pass does NOT inject @font-face declarations.
|
|
||||||
// The CMS emits @font-face automatically for fonts assigned via the picker.
|
|
||||||
// We only inject custom utility CSS here.
|
|
||||||
func ThemeCSSManifest() *plugin.CSSManifest {
|
|
||||||
return &plugin.CSSManifest{
|
|
||||||
InputCSSAppend: magazineBoldUtilityCSS,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
246
template.templ
246
template.templ
@ -1,246 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"git.dev.alexdunmow.com/block/core/templates/bn"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PageData carries the data the Magazine Bold page templates render.
|
|
||||||
type PageData struct {
|
|
||||||
Title string
|
|
||||||
Slots map[string]string
|
|
||||||
ThemeMode string
|
|
||||||
ThemeCSS string
|
|
||||||
SiteSettings bn.SiteSettingsData
|
|
||||||
PageMeta bn.PageMeta
|
|
||||||
StructuredData string
|
|
||||||
CSSHash string
|
|
||||||
PageviewNonce string
|
|
||||||
EngagementConfig bn.EngagementConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseMagazineBoldPageData(doc map[string]any) PageData {
|
|
||||||
title := "Untitled"
|
|
||||||
if t, ok := doc["title"].(string); ok {
|
|
||||||
title = t
|
|
||||||
}
|
|
||||||
|
|
||||||
slots := make(map[string]string)
|
|
||||||
if s, ok := doc["slots"].(map[string]string); ok {
|
|
||||||
slots = s
|
|
||||||
}
|
|
||||||
|
|
||||||
themeCSS := ""
|
|
||||||
if tc, ok := doc["theme_css"].(string); ok {
|
|
||||||
themeCSS = tc
|
|
||||||
}
|
|
||||||
|
|
||||||
structuredData := ""
|
|
||||||
if sd, ok := doc["structured_data"].(string); ok {
|
|
||||||
structuredData = sd
|
|
||||||
}
|
|
||||||
|
|
||||||
cssHash := ""
|
|
||||||
if ch, ok := doc["css_hash"].(string); ok {
|
|
||||||
cssHash = ch
|
|
||||||
}
|
|
||||||
|
|
||||||
pageviewNonce := ""
|
|
||||||
if pn, ok := doc["pageview_nonce"].(string); ok {
|
|
||||||
pageviewNonce = pn
|
|
||||||
}
|
|
||||||
|
|
||||||
themeMode := "light"
|
|
||||||
if tm, ok := doc["theme_mode"].(string); ok && tm != "" {
|
|
||||||
themeMode = tm
|
|
||||||
}
|
|
||||||
|
|
||||||
siteSettings := bn.ParseSiteSettings(doc)
|
|
||||||
pageMeta := bn.ParsePageMeta(doc)
|
|
||||||
engagementConfig := bn.ParseEngagementConfig(doc)
|
|
||||||
|
|
||||||
return PageData{
|
|
||||||
Title: title,
|
|
||||||
Slots: slots,
|
|
||||||
ThemeMode: themeMode,
|
|
||||||
ThemeCSS: themeCSS,
|
|
||||||
SiteSettings: siteSettings,
|
|
||||||
PageMeta: pageMeta,
|
|
||||||
StructuredData: structuredData,
|
|
||||||
CSSHash: cssHash,
|
|
||||||
PageviewNonce: pageviewNonce,
|
|
||||||
EngagementConfig: engagementConfig,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// mbHead reuses the core head component with the magazine-bold plugin style sheet.
|
|
||||||
templ mbHead(data PageData) {
|
|
||||||
@bn.Head(bn.HeadData{
|
|
||||||
Title: data.Title,
|
|
||||||
Settings: data.SiteSettings,
|
|
||||||
PageMeta: data.PageMeta,
|
|
||||||
ThemeMode: data.ThemeMode,
|
|
||||||
ThemeCSS: data.ThemeCSS,
|
|
||||||
PluginStyles: []string{"/templates/magazine-bold/style.css"},
|
|
||||||
StructuredData: data.StructuredData,
|
|
||||||
CSSHash: data.CSSHash,
|
|
||||||
PageviewNonce: data.PageviewNonce,
|
|
||||||
EngagementConfig: data.EngagementConfig,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// MagazineBold — default page template.
|
|
||||||
// Slots: masthead, main, colophon.
|
|
||||||
templ MagazineBold(data PageData) {
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
@mbHead(data)
|
|
||||||
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col font-sans">
|
|
||||||
@bn.AdminBypassBanner(data.SiteSettings)
|
|
||||||
<header class="w-full mb-hairline-bottom">
|
|
||||||
<div class="max-w-6xl mx-auto px-6">
|
|
||||||
@templ.Raw(data.Slots["masthead"])
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<main class="flex-grow w-full">
|
|
||||||
<div class="max-w-6xl mx-auto px-6 py-12">
|
|
||||||
if main, ok := data.Slots["main"]; ok && main != "" {
|
|
||||||
@templ.Raw(main)
|
|
||||||
} else {
|
|
||||||
<div class="py-20 text-center font-mono mb-caption">No content blocks assigned to this page.</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
<footer class="w-full mt-auto mb-hairline-top">
|
|
||||||
<div class="max-w-6xl mx-auto px-6">
|
|
||||||
@templ.Raw(data.Slots["colophon"])
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
@bn.BodyEnd(data.SiteSettings)
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
}
|
|
||||||
|
|
||||||
// MagazineBoldLanding — issue landing page template.
|
|
||||||
// Slots: cover, secondary, main, colophon.
|
|
||||||
templ MagazineBoldLanding(data PageData) {
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
@mbHead(data)
|
|
||||||
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col font-sans">
|
|
||||||
@bn.AdminBypassBanner(data.SiteSettings)
|
|
||||||
<section class="w-full">
|
|
||||||
@templ.Raw(data.Slots["cover"])
|
|
||||||
</section>
|
|
||||||
<section class="w-full border-t border-b mb-hairline">
|
|
||||||
<div class="max-w-6xl mx-auto px-6 py-10">
|
|
||||||
@templ.Raw(data.Slots["secondary"])
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<main class="flex-grow w-full">
|
|
||||||
<div class="max-w-6xl mx-auto px-6 py-12">
|
|
||||||
if main, ok := data.Slots["main"]; ok && main != "" {
|
|
||||||
@templ.Raw(main)
|
|
||||||
} else {
|
|
||||||
<div class="py-20 text-center font-mono mb-caption">No content blocks assigned to this page.</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
<footer class="w-full mt-auto mb-hairline-top">
|
|
||||||
<div class="max-w-6xl mx-auto px-6">
|
|
||||||
@templ.Raw(data.Slots["colophon"])
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
@bn.BodyEnd(data.SiteSettings)
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
}
|
|
||||||
|
|
||||||
// MagazineBoldArticle — long-form feature template with deck and pull-quote gutter.
|
|
||||||
// Slots: masthead, deck, main, colophon.
|
|
||||||
templ MagazineBoldArticle(data PageData) {
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
@mbHead(data)
|
|
||||||
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col font-sans">
|
|
||||||
@bn.AdminBypassBanner(data.SiteSettings)
|
|
||||||
<header class="w-full mb-hairline-bottom">
|
|
||||||
<div class="max-w-6xl mx-auto px-6">
|
|
||||||
@templ.Raw(data.Slots["masthead"])
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<section class="w-full">
|
|
||||||
<div class="max-w-6xl mx-auto px-6 py-10">
|
|
||||||
@templ.Raw(data.Slots["deck"])
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<main class="flex-grow w-full">
|
|
||||||
<div class="max-w-4xl mx-auto px-6 py-12">
|
|
||||||
if main, ok := data.Slots["main"]; ok && main != "" {
|
|
||||||
<article class="mb-body">
|
|
||||||
@templ.Raw(main)
|
|
||||||
</article>
|
|
||||||
} else {
|
|
||||||
<div class="py-20 text-center font-mono mb-caption">No content blocks assigned to this page.</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
<footer class="w-full mt-auto mb-hairline-top">
|
|
||||||
<div class="max-w-6xl mx-auto px-6">
|
|
||||||
@templ.Raw(data.Slots["colophon"])
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
@bn.BodyEnd(data.SiteSettings)
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
}
|
|
||||||
|
|
||||||
// MagazineBoldFullWidth — edge-to-edge photo-essay template.
|
|
||||||
// Slots: masthead, main, colophon.
|
|
||||||
templ MagazineBoldFullWidth(data PageData) {
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
@mbHead(data)
|
|
||||||
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col font-sans">
|
|
||||||
@bn.AdminBypassBanner(data.SiteSettings)
|
|
||||||
<header class="w-full mb-hairline-bottom">
|
|
||||||
<div class="max-w-6xl mx-auto px-6">
|
|
||||||
@templ.Raw(data.Slots["masthead"])
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<main class="flex-grow w-full">
|
|
||||||
if main, ok := data.Slots["main"]; ok && main != "" {
|
|
||||||
@templ.Raw(main)
|
|
||||||
} else {
|
|
||||||
<div class="max-w-4xl mx-auto py-20 px-6 text-center font-mono mb-caption">No content blocks assigned to this page.</div>
|
|
||||||
}
|
|
||||||
</main>
|
|
||||||
<footer class="w-full mt-auto mb-hairline-top">
|
|
||||||
<div class="max-w-6xl mx-auto px-6">
|
|
||||||
@templ.Raw(data.Slots["colophon"])
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
@bn.BodyEnd(data.SiteSettings)
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
}
|
|
||||||
|
|
||||||
// RenderMagazineBold renders the default page.
|
|
||||||
func RenderMagazineBold(ctx context.Context, doc map[string]any) templ.Component {
|
|
||||||
return MagazineBold(parseMagazineBoldPageData(doc))
|
|
||||||
}
|
|
||||||
|
|
||||||
// RenderMagazineBoldLanding renders the issue-landing page.
|
|
||||||
func RenderMagazineBoldLanding(ctx context.Context, doc map[string]any) templ.Component {
|
|
||||||
return MagazineBoldLanding(parseMagazineBoldPageData(doc))
|
|
||||||
}
|
|
||||||
|
|
||||||
// RenderMagazineBoldArticle renders the long-form article page.
|
|
||||||
func RenderMagazineBoldArticle(ctx context.Context, doc map[string]any) templ.Component {
|
|
||||||
return MagazineBoldArticle(parseMagazineBoldPageData(doc))
|
|
||||||
}
|
|
||||||
|
|
||||||
// RenderMagazineBoldFullWidth renders the edge-to-edge photo-essay page.
|
|
||||||
func RenderMagazineBoldFullWidth(ctx context.Context, doc map[string]any) templ.Component {
|
|
||||||
return MagazineBoldFullWidth(parseMagazineBoldPageData(doc))
|
|
||||||
}
|
|
||||||
@ -1,513 +0,0 @@
|
|||||||
// Code generated by templ - DO NOT EDIT.
|
|
||||||
|
|
||||||
// templ: version: v0.3.1020
|
|
||||||
package main
|
|
||||||
|
|
||||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
|
||||||
|
|
||||||
import "github.com/a-h/templ"
|
|
||||||
import templruntime "github.com/a-h/templ/runtime"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"git.dev.alexdunmow.com/block/core/templates/bn"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PageData carries the data the Magazine Bold page templates render.
|
|
||||||
type PageData struct {
|
|
||||||
Title string
|
|
||||||
Slots map[string]string
|
|
||||||
ThemeMode string
|
|
||||||
ThemeCSS string
|
|
||||||
SiteSettings bn.SiteSettingsData
|
|
||||||
PageMeta bn.PageMeta
|
|
||||||
StructuredData string
|
|
||||||
CSSHash string
|
|
||||||
PageviewNonce string
|
|
||||||
EngagementConfig bn.EngagementConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseMagazineBoldPageData(doc map[string]any) PageData {
|
|
||||||
title := "Untitled"
|
|
||||||
if t, ok := doc["title"].(string); ok {
|
|
||||||
title = t
|
|
||||||
}
|
|
||||||
|
|
||||||
slots := make(map[string]string)
|
|
||||||
if s, ok := doc["slots"].(map[string]string); ok {
|
|
||||||
slots = s
|
|
||||||
}
|
|
||||||
|
|
||||||
themeCSS := ""
|
|
||||||
if tc, ok := doc["theme_css"].(string); ok {
|
|
||||||
themeCSS = tc
|
|
||||||
}
|
|
||||||
|
|
||||||
structuredData := ""
|
|
||||||
if sd, ok := doc["structured_data"].(string); ok {
|
|
||||||
structuredData = sd
|
|
||||||
}
|
|
||||||
|
|
||||||
cssHash := ""
|
|
||||||
if ch, ok := doc["css_hash"].(string); ok {
|
|
||||||
cssHash = ch
|
|
||||||
}
|
|
||||||
|
|
||||||
pageviewNonce := ""
|
|
||||||
if pn, ok := doc["pageview_nonce"].(string); ok {
|
|
||||||
pageviewNonce = pn
|
|
||||||
}
|
|
||||||
|
|
||||||
themeMode := "light"
|
|
||||||
if tm, ok := doc["theme_mode"].(string); ok && tm != "" {
|
|
||||||
themeMode = tm
|
|
||||||
}
|
|
||||||
|
|
||||||
siteSettings := bn.ParseSiteSettings(doc)
|
|
||||||
pageMeta := bn.ParsePageMeta(doc)
|
|
||||||
engagementConfig := bn.ParseEngagementConfig(doc)
|
|
||||||
|
|
||||||
return PageData{
|
|
||||||
Title: title,
|
|
||||||
Slots: slots,
|
|
||||||
ThemeMode: themeMode,
|
|
||||||
ThemeCSS: themeCSS,
|
|
||||||
SiteSettings: siteSettings,
|
|
||||||
PageMeta: pageMeta,
|
|
||||||
StructuredData: structuredData,
|
|
||||||
CSSHash: cssHash,
|
|
||||||
PageviewNonce: pageviewNonce,
|
|
||||||
EngagementConfig: engagementConfig,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// mbHead reuses the core head component with the magazine-bold plugin style sheet.
|
|
||||||
func mbHead(data PageData) templ.Component {
|
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
|
||||||
return templ_7745c5c3_CtxErr
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
|
||||||
if !templ_7745c5c3_IsBuffer {
|
|
||||||
defer func() {
|
|
||||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err == nil {
|
|
||||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
ctx = templ.InitializeContext(ctx)
|
|
||||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
|
||||||
if templ_7745c5c3_Var1 == nil {
|
|
||||||
templ_7745c5c3_Var1 = templ.NopComponent
|
|
||||||
}
|
|
||||||
ctx = templ.ClearChildren(ctx)
|
|
||||||
templ_7745c5c3_Err = bn.Head(bn.HeadData{
|
|
||||||
Title: data.Title,
|
|
||||||
Settings: data.SiteSettings,
|
|
||||||
PageMeta: data.PageMeta,
|
|
||||||
ThemeMode: data.ThemeMode,
|
|
||||||
ThemeCSS: data.ThemeCSS,
|
|
||||||
PluginStyles: []string{"/templates/magazine-bold/style.css"},
|
|
||||||
StructuredData: data.StructuredData,
|
|
||||||
CSSHash: data.CSSHash,
|
|
||||||
PageviewNonce: data.PageviewNonce,
|
|
||||||
EngagementConfig: data.EngagementConfig,
|
|
||||||
}).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// MagazineBold — default page template.
|
|
||||||
// Slots: masthead, main, colophon.
|
|
||||||
func MagazineBold(data PageData) templ.Component {
|
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
|
||||||
return templ_7745c5c3_CtxErr
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
|
||||||
if !templ_7745c5c3_IsBuffer {
|
|
||||||
defer func() {
|
|
||||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err == nil {
|
|
||||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
ctx = templ.InitializeContext(ctx)
|
|
||||||
templ_7745c5c3_Var2 := templ.GetChildren(ctx)
|
|
||||||
if templ_7745c5c3_Var2 == nil {
|
|
||||||
templ_7745c5c3_Var2 = templ.NopComponent
|
|
||||||
}
|
|
||||||
ctx = templ.ClearChildren(ctx)
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = mbHead(data).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<body class=\"bg-background text-foreground antialiased min-h-screen flex flex-col font-sans\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = bn.AdminBypassBanner(data.SiteSettings).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<header class=\"w-full mb-hairline-bottom\"><div class=\"max-w-6xl mx-auto px-6\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templ.Raw(data.Slots["masthead"]).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</div></header><main class=\"flex-grow w-full\"><div class=\"max-w-6xl mx-auto px-6 py-12\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
if main, ok := data.Slots["main"]; ok && main != "" {
|
|
||||||
templ_7745c5c3_Err = templ.Raw(main).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<div class=\"py-20 text-center font-mono mb-caption\">No content blocks assigned to this page.</div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</div></main><footer class=\"w-full mt-auto mb-hairline-top\"><div class=\"max-w-6xl mx-auto px-6\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templ.Raw(data.Slots["colophon"]).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</div></footer>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = bn.BodyEnd(data.SiteSettings).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</body></html>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// MagazineBoldLanding — issue landing page template.
|
|
||||||
// Slots: cover, secondary, main, colophon.
|
|
||||||
func MagazineBoldLanding(data PageData) templ.Component {
|
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
|
||||||
return templ_7745c5c3_CtxErr
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
|
||||||
if !templ_7745c5c3_IsBuffer {
|
|
||||||
defer func() {
|
|
||||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err == nil {
|
|
||||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
ctx = templ.InitializeContext(ctx)
|
|
||||||
templ_7745c5c3_Var3 := templ.GetChildren(ctx)
|
|
||||||
if templ_7745c5c3_Var3 == nil {
|
|
||||||
templ_7745c5c3_Var3 = templ.NopComponent
|
|
||||||
}
|
|
||||||
ctx = templ.ClearChildren(ctx)
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<!doctype html><html lang=\"en\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = mbHead(data).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<body class=\"bg-background text-foreground antialiased min-h-screen flex flex-col font-sans\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = bn.AdminBypassBanner(data.SiteSettings).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<section class=\"w-full\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templ.Raw(data.Slots["cover"]).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</section><section class=\"w-full border-t border-b mb-hairline\"><div class=\"max-w-6xl mx-auto px-6 py-10\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templ.Raw(data.Slots["secondary"]).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</div></section><main class=\"flex-grow w-full\"><div class=\"max-w-6xl mx-auto px-6 py-12\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
if main, ok := data.Slots["main"]; ok && main != "" {
|
|
||||||
templ_7745c5c3_Err = templ.Raw(main).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<div class=\"py-20 text-center font-mono mb-caption\">No content blocks assigned to this page.</div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</div></main><footer class=\"w-full mt-auto mb-hairline-top\"><div class=\"max-w-6xl mx-auto px-6\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templ.Raw(data.Slots["colophon"]).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</div></footer>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = bn.BodyEnd(data.SiteSettings).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</body></html>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// MagazineBoldArticle — long-form feature template with deck and pull-quote gutter.
|
|
||||||
// Slots: masthead, deck, main, colophon.
|
|
||||||
func MagazineBoldArticle(data PageData) templ.Component {
|
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
|
||||||
return templ_7745c5c3_CtxErr
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
|
||||||
if !templ_7745c5c3_IsBuffer {
|
|
||||||
defer func() {
|
|
||||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err == nil {
|
|
||||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
ctx = templ.InitializeContext(ctx)
|
|
||||||
templ_7745c5c3_Var4 := templ.GetChildren(ctx)
|
|
||||||
if templ_7745c5c3_Var4 == nil {
|
|
||||||
templ_7745c5c3_Var4 = templ.NopComponent
|
|
||||||
}
|
|
||||||
ctx = templ.ClearChildren(ctx)
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "<!doctype html><html lang=\"en\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = mbHead(data).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<body class=\"bg-background text-foreground antialiased min-h-screen flex flex-col font-sans\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = bn.AdminBypassBanner(data.SiteSettings).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<header class=\"w-full mb-hairline-bottom\"><div class=\"max-w-6xl mx-auto px-6\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templ.Raw(data.Slots["masthead"]).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "</div></header><section class=\"w-full\"><div class=\"max-w-6xl mx-auto px-6 py-10\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templ.Raw(data.Slots["deck"]).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "</div></section><main class=\"flex-grow w-full\"><div class=\"max-w-4xl mx-auto px-6 py-12\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
if main, ok := data.Slots["main"]; ok && main != "" {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<article class=\"mb-body\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templ.Raw(main).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "</article>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<div class=\"py-20 text-center font-mono mb-caption\">No content blocks assigned to this page.</div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</div></main><footer class=\"w-full mt-auto mb-hairline-top\"><div class=\"max-w-6xl mx-auto px-6\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templ.Raw(data.Slots["colophon"]).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "</div></footer>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = bn.BodyEnd(data.SiteSettings).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</body></html>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// MagazineBoldFullWidth — edge-to-edge photo-essay template.
|
|
||||||
// Slots: masthead, main, colophon.
|
|
||||||
func MagazineBoldFullWidth(data PageData) templ.Component {
|
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
|
||||||
return templ_7745c5c3_CtxErr
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
|
||||||
if !templ_7745c5c3_IsBuffer {
|
|
||||||
defer func() {
|
|
||||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err == nil {
|
|
||||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
ctx = templ.InitializeContext(ctx)
|
|
||||||
templ_7745c5c3_Var5 := templ.GetChildren(ctx)
|
|
||||||
if templ_7745c5c3_Var5 == nil {
|
|
||||||
templ_7745c5c3_Var5 = templ.NopComponent
|
|
||||||
}
|
|
||||||
ctx = templ.ClearChildren(ctx)
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<!doctype html><html lang=\"en\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = mbHead(data).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<body class=\"bg-background text-foreground antialiased min-h-screen flex flex-col font-sans\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = bn.AdminBypassBanner(data.SiteSettings).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<header class=\"w-full mb-hairline-bottom\"><div class=\"max-w-6xl mx-auto px-6\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templ.Raw(data.Slots["masthead"]).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "</div></header><main class=\"flex-grow w-full\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
if main, ok := data.Slots["main"]; ok && main != "" {
|
|
||||||
templ_7745c5c3_Err = templ.Raw(main).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "<div class=\"max-w-4xl mx-auto py-20 px-6 text-center font-mono mb-caption\">No content blocks assigned to this page.</div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</main><footer class=\"w-full mt-auto mb-hairline-top\"><div class=\"max-w-6xl mx-auto px-6\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templ.Raw(data.Slots["colophon"]).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "</div></footer>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = bn.BodyEnd(data.SiteSettings).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "</body></html>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// RenderMagazineBold renders the default page.
|
|
||||||
func RenderMagazineBold(ctx context.Context, doc map[string]any) templ.Component {
|
|
||||||
return MagazineBold(parseMagazineBoldPageData(doc))
|
|
||||||
}
|
|
||||||
|
|
||||||
// RenderMagazineBoldLanding renders the issue-landing page.
|
|
||||||
func RenderMagazineBoldLanding(ctx context.Context, doc map[string]any) templ.Component {
|
|
||||||
return MagazineBoldLanding(parseMagazineBoldPageData(doc))
|
|
||||||
}
|
|
||||||
|
|
||||||
// RenderMagazineBoldArticle renders the long-form article page.
|
|
||||||
func RenderMagazineBoldArticle(ctx context.Context, doc map[string]any) templ.Component {
|
|
||||||
return MagazineBoldArticle(parseMagazineBoldPageData(doc))
|
|
||||||
}
|
|
||||||
|
|
||||||
// RenderMagazineBoldFullWidth renders the edge-to-edge photo-essay page.
|
|
||||||
func RenderMagazineBoldFullWidth(ctx context.Context, doc map[string]any) templ.Component {
|
|
||||||
return MagazineBoldFullWidth(parseMagazineBoldPageData(doc))
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ = templruntime.GeneratedTemplate
|
|
||||||
53
templates/email/magazine-bold.ninjatpl
Normal file
53
templates/email/magazine-bold.ninjatpl
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta name="x-apple-disable-message-reformatting">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<title>{{ site_name }}</title>
|
||||||
|
<style type="text/css">
|
||||||
|
body, table, td, p, a, li, blockquote { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
|
||||||
|
table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
|
||||||
|
img { -ms-interpolation-mode: bicubic; border: 0; height: auto; line-height: 100%; outline: none; text-decoration: none; }
|
||||||
|
body { margin: 0 !important; padding: 0 !important; width: 100% !important; }
|
||||||
|
@media only screen and (max-width: 620px) {
|
||||||
|
.mb-email-container { width: 100% !important; max-width: 100% !important; }
|
||||||
|
.mb-email-pad { padding-left: 24px !important; padding-right: 24px !important; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body style="background-color: {{ colors.background|default:"#F7F2EB" }}; margin: 0; padding: 0; font-family: &#39;Inter&#39;, -apple-system, BlinkMacSystemFont, &#39;Segoe UI&#39;, Roboto, sans-serif;">
|
||||||
|
{% if preview_text %}<div style="display:none;max-height:0;overflow:hidden;mso-hide:all;">{{ preview_text }}</div>{% endif %}
|
||||||
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
|
||||||
|
<tr>
|
||||||
|
<td align="center" style="padding: 48px 10px; background-color: {{ colors.background|default:"#F7F2EB" }};">
|
||||||
|
<table role="presentation" class="mb-email-container" width="600" cellspacing="0" cellpadding="0" border="0" style="max-width: 600px; background-color: {{ colors.card|default:"#FFFFFF" }}; border: 1px solid {{ colors.border|default:"#E0E0E0" }};">
|
||||||
|
<!-- Wordmark / masthead -->
|
||||||
|
<tr>
|
||||||
|
<td align="left" class="mb-email-pad" style="padding: 32px 40px; background-color: {{ colors.card|default:"#FFFFFF" }}; border-bottom: 1px solid {{ colors.border|default:"#E0E0E0" }};">
|
||||||
|
{% if logo_url %}<img src="{{ logo_url }}" alt="{{ site_name }}" style="max-height:48px;width:auto;display:block;">{% elif site_name %}<h1 style="margin: 0; font-family: &#39;PP Editorial New&#39;, Georgia, &#39;Times New Roman&#39;, serif; font-style: italic; font-weight: 200; font-size: 42px; line-height: 1; letter-spacing: -0.02em; color: {{ colors.foreground|default:"#141414" }};">{{ site_name }}</h1>{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<!-- Ink hairline -->
|
||||||
|
<tr>
|
||||||
|
<td style="height: 1px; line-height: 1px; background-color: {{ colors.foreground|default:"#141414" }};"> </td>
|
||||||
|
</tr>
|
||||||
|
<!-- Body -->
|
||||||
|
<tr>
|
||||||
|
<td class="mb-email-pad" style="padding: 40px 48px; background-color: {{ colors.card|default:"#FFFFFF" }}; color: {{ colors.foreground|default:"#141414" }}; font-family: &#39;Inter&#39;, -apple-system, BlinkMacSystemFont, &#39;Segoe UI&#39;, Roboto, sans-serif; font-size: 16px; line-height: 24px;">{{ body|safe }}</td>
|
||||||
|
</tr>
|
||||||
|
<!-- Mono colophon -->
|
||||||
|
<tr>
|
||||||
|
<td class="mb-email-pad" style="padding: 24px 48px; background-color: {{ colors.muted|default:"#EEE9E0" }}; border-top: 1px solid {{ colors.border|default:"#E0E0E0" }}; font-family: &#39;JetBrains Mono&#39;, &#39;Courier New&#39;, Courier, monospace; font-size: 12px; line-height: 18px; color: {{ colors.mutedForeground|default:"#666666" }}; letter-spacing: 0.08em; text-transform: uppercase;">
|
||||||
|
<div>{{ site_name }}</div>
|
||||||
|
{% if site_url %}<div style="margin-top:8px;"><a href="{{ site_url }}" style="color: {{ colors.mutedForeground|default:"#666666" }}; text-decoration: underline;">{{ site_url }}</a></div>{% endif %}
|
||||||
|
{% if unsubscribe_url %}<div style="margin-top:8px;"><a href="{{ unsubscribe_url }}" style="color: {{ colors.mutedForeground|default:"#666666" }}; text-decoration: underline;">Unsubscribe</a></div>{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
20
templates/magazine-bold/article.ninjatpl
Normal file
20
templates/magazine-bold/article.ninjatpl
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
{{ head_html|safe }}
|
||||||
|
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col font-sans">
|
||||||
|
{{ admin_banner_html|safe }}
|
||||||
|
<header class="w-full mb-hairline-bottom">
|
||||||
|
<div class="max-w-6xl mx-auto px-6">{{ slots.masthead|safe }}</div>
|
||||||
|
</header>
|
||||||
|
<section class="w-full">
|
||||||
|
<div class="max-w-6xl mx-auto px-6 py-10">{{ slots.deck|safe }}</div>
|
||||||
|
</section>
|
||||||
|
<main class="flex-grow w-full">
|
||||||
|
<div class="max-w-4xl mx-auto px-6 py-12">{% if slots.main %}<article class="mb-body">{{ slots.main|safe }}</article>{% else %}<div class="py-20 text-center font-mono mb-caption">No content blocks assigned to this page.</div>{% endif %}</div>
|
||||||
|
</main>
|
||||||
|
<footer class="w-full mt-auto mb-hairline-top">
|
||||||
|
<div class="max-w-6xl mx-auto px-6">{{ slots.colophon|safe }}</div>
|
||||||
|
</footer>
|
||||||
|
{{ body_end_html|safe }}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
17
templates/magazine-bold/default.ninjatpl
Normal file
17
templates/magazine-bold/default.ninjatpl
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
{{ head_html|safe }}
|
||||||
|
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col font-sans">
|
||||||
|
{{ admin_banner_html|safe }}
|
||||||
|
<header class="w-full mb-hairline-bottom">
|
||||||
|
<div class="max-w-6xl mx-auto px-6">{{ slots.masthead|safe }}</div>
|
||||||
|
</header>
|
||||||
|
<main class="flex-grow w-full">
|
||||||
|
<div class="max-w-6xl mx-auto px-6 py-12">{% if slots.main %}{{ slots.main|safe }}{% else %}<div class="py-20 text-center font-mono mb-caption">No content blocks assigned to this page.</div>{% endif %}</div>
|
||||||
|
</main>
|
||||||
|
<footer class="w-full mt-auto mb-hairline-top">
|
||||||
|
<div class="max-w-6xl mx-auto px-6">{{ slots.colophon|safe }}</div>
|
||||||
|
</footer>
|
||||||
|
{{ body_end_html|safe }}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
17
templates/magazine-bold/full-width.ninjatpl
Normal file
17
templates/magazine-bold/full-width.ninjatpl
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
{{ head_html|safe }}
|
||||||
|
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col font-sans">
|
||||||
|
{{ admin_banner_html|safe }}
|
||||||
|
<header class="w-full mb-hairline-bottom">
|
||||||
|
<div class="max-w-6xl mx-auto px-6">{{ slots.masthead|safe }}</div>
|
||||||
|
</header>
|
||||||
|
<main class="flex-grow w-full">
|
||||||
|
{% if slots.main %}{{ slots.main|safe }}{% else %}<div class="max-w-4xl mx-auto py-20 px-6 text-center font-mono mb-caption">No content blocks assigned to this page.</div>{% endif %}
|
||||||
|
</main>
|
||||||
|
<footer class="w-full mt-auto mb-hairline-top">
|
||||||
|
<div class="max-w-6xl mx-auto px-6">{{ slots.colophon|safe }}</div>
|
||||||
|
</footer>
|
||||||
|
{{ body_end_html|safe }}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
18
templates/magazine-bold/landing.ninjatpl
Normal file
18
templates/magazine-bold/landing.ninjatpl
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
{{ head_html|safe }}
|
||||||
|
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col font-sans">
|
||||||
|
{{ admin_banner_html|safe }}
|
||||||
|
<section class="w-full">{{ slots.cover|safe }}</section>
|
||||||
|
<section class="w-full border-t border-b mb-hairline">
|
||||||
|
<div class="max-w-6xl mx-auto px-6 py-10">{{ slots.secondary|safe }}</div>
|
||||||
|
</section>
|
||||||
|
<main class="flex-grow w-full">
|
||||||
|
<div class="max-w-6xl mx-auto px-6 py-12">{% if slots.main %}{{ slots.main|safe }}{% else %}<div class="py-20 text-center font-mono mb-caption">No content blocks assigned to this page.</div>{% endif %}</div>
|
||||||
|
</main>
|
||||||
|
<footer class="w-full mt-auto mb-hairline-top">
|
||||||
|
<div class="max-w-6xl mx-auto px-6">{{ slots.colophon|safe }}</div>
|
||||||
|
</footer>
|
||||||
|
{{ body_end_html|safe }}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
1
templates/overrides/magazine-bold/button.ninjatpl
Normal file
1
templates/overrides/magazine-bold/button.ninjatpl
Normal file
@ -0,0 +1 @@
|
|||||||
|
{% set _label = label|default:text %}{% set _url = url|default:href %}{% if _label %}<a href="{{ _url }}" class="mb-button mb-button-{% if variant == "secondary" %}secondary{% elif variant == "destructive" %}destructive{% else %}primary{% endif %}" data-variant="{% if variant == "secondary" %}secondary{% elif variant == "destructive" %}destructive{% else %}primary{% endif %}">{{ _label }}</a>{% endif %}
|
||||||
1
templates/overrides/magazine-bold/heading.ninjatpl
Normal file
1
templates/overrides/magazine-bold/heading.ninjatpl
Normal file
@ -0,0 +1 @@
|
|||||||
|
{% if level|integer == 1 %}<h1 class="font-display text-folio text-foreground mb-dropcap {{ textClass }}">{{ text }}</h1>{% elif level|integer == 3 %}<h3 class="font-display text-3xl text-foreground {{ textClass }}">{{ text }}</h3>{% elif level|integer == 4 %}<h4 class="font-serif-sub text-2xl text-foreground {{ textClass }}">{{ text }}</h4>{% elif level|integer == 5 %}<h5 class="font-serif-sub text-xl text-foreground {{ textClass }}">{{ text }}</h5>{% elif level|integer == 6 %}<h6 class="font-mono text-kicker text-muted-foreground {{ textClass }}">{{ text }}</h6>{% else %}<h2 class="font-display text-deck text-foreground {{ textClass }}">{{ text }}</h2>{% endif %}
|
||||||
1
templates/overrides/magazine-bold/image.ninjatpl
Normal file
1
templates/overrides/magazine-bold/image.ninjatpl
Normal file
@ -0,0 +1 @@
|
|||||||
|
{% if src %}<figure class="my-8{% if fullBleed == true or fullBleed == "true" %} mb-fullbleed{% endif %}"><img src="{{ src }}" alt="{{ alt }}" class="w-full h-auto block" loading="lazy">{% if caption %}<figcaption class="mb-caption font-mono mt-2 px-6">{{ caption }}</figcaption>{% endif %}</figure>{% endif %}
|
||||||
1
templates/overrides/magazine-bold/text.ninjatpl
Normal file
1
templates/overrides/magazine-bold/text.ninjatpl
Normal file
@ -0,0 +1 @@
|
|||||||
|
<div class="mb-body font-sans {{ class }}">{{ text|safe }}</div>
|
||||||
@ -1,19 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TextOverrideBlock renders body text with the Magazine Bold Inter cadence
|
|
||||||
// and a margin folio.
|
|
||||||
//
|
|
||||||
// Built-in text content shape: {"text": "...", "class": "..."}
|
|
||||||
func TextOverrideBlock(ctx context.Context, content map[string]any) string {
|
|
||||||
text := getString(content, "text")
|
|
||||||
class := getString(content, "class")
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
_ = mbTextComponent(text, class).Render(ctx, &buf)
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
// mbTextComponent renders a text run in Magazine Bold body cadence (Inter, 16–18px tight leading).
|
|
||||||
templ mbTextComponent(text string, class string) {
|
|
||||||
<div class={ "mb-body font-sans", class }>
|
|
||||||
@templ.Raw(text)
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
@ -1,67 +0,0 @@
|
|||||||
// Code generated by templ - DO NOT EDIT.
|
|
||||||
|
|
||||||
// templ: version: v0.3.1020
|
|
||||||
package main
|
|
||||||
|
|
||||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
|
||||||
|
|
||||||
import "github.com/a-h/templ"
|
|
||||||
import templruntime "github.com/a-h/templ/runtime"
|
|
||||||
|
|
||||||
// mbTextComponent renders a text run in Magazine Bold body cadence (Inter, 16–18px tight leading).
|
|
||||||
func mbTextComponent(text string, class string) templ.Component {
|
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
|
||||||
return templ_7745c5c3_CtxErr
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
|
||||||
if !templ_7745c5c3_IsBuffer {
|
|
||||||
defer func() {
|
|
||||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err == nil {
|
|
||||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
ctx = templ.InitializeContext(ctx)
|
|
||||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
|
||||||
if templ_7745c5c3_Var1 == nil {
|
|
||||||
templ_7745c5c3_Var1 = templ.NopComponent
|
|
||||||
}
|
|
||||||
ctx = templ.ClearChildren(ctx)
|
|
||||||
var templ_7745c5c3_Var2 = []any{"mb-body font-sans", class}
|
|
||||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var2...)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var3 string
|
|
||||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var2).String())
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `text_override.templ`, Line: 1, Col: 0}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templ.Raw(text).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ = templruntime.GeneratedTemplate
|
|
||||||
Loading…
x
Reference in New Issue
Block a user