initial: theme plugin art-deco
Bootstrapped during the 2026-06-06 BlockNinja consolidation. Was previously an unversioned directory inside ~/src/blockninja-themes/art-deco. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
commit
9fbedf5ba1
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
*.so
|
||||||
|
*.test
|
||||||
|
tmp/
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
168
BUILD_REPORT.md
Normal file
168
BUILD_REPORT.md
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
# Art Deco — Build Report
|
||||||
|
|
||||||
|
Status: **SHIPPED**.
|
||||||
|
|
||||||
|
Compiled `.so` produced and the safety check exits 0.
|
||||||
|
|
||||||
|
## What landed
|
||||||
|
|
||||||
|
### Plugin metadata (`plugin.mod`)
|
||||||
|
- `kind = "theme"`, `scope = "@themes"`, `version = "0.1.0"`.
|
||||||
|
- `categories = ["templates"]` (single whitelist entry).
|
||||||
|
- `tags = ["luxury", "gold", "hospitality", "hotel", "wedding", "jewellery", "vintage", "glam", "fine-dining"]` (9 tags, within the 5–9 UAT range).
|
||||||
|
- `[compatibility].block_core = ">=0.11.0 <0.12.0"`.
|
||||||
|
|
||||||
|
### System & page templates
|
||||||
|
- System template registered with `Key: "art-deco"`.
|
||||||
|
- 4 page templates registered:
|
||||||
|
- `default` → slots `header, main, footer`.
|
||||||
|
- `landing` → slots `marquee, main, cta, footer`.
|
||||||
|
- `article` → slots `header, main, aside, footer`.
|
||||||
|
- `full-width` → slots `header, main, footer`.
|
||||||
|
|
||||||
|
### Blocks (7, all `Source: "art-deco"`)
|
||||||
|
| Key | Schema | Notes |
|
||||||
|
|---|---|---|
|
||||||
|
| `reservation` | `schemas/reservation.schema.json` | Sticky gold-on-black strip with phone, OpenTable CTA, hours. |
|
||||||
|
| `menu` | `schemas/menu.schema.json` | Symmetric menu card with rich-text course descriptions and prices. |
|
||||||
|
| `gallery_fan` | `schemas/gallery_fan.schema.json` | Mirrored gallery framed by sunburst, 2/3/4 columns, collapses to 1 col <640px. |
|
||||||
|
| `divider_fan` | `schemas/divider_fan.schema.json` | Pure-CSS sunburst / scallop / ziggurat divider — falls back to sunburst on unknown variant. |
|
||||||
|
| `footer` | `schemas/footer.schema.json` | Centered footer with gold rule, address, reservations email, social row, menu name. |
|
||||||
|
| `marquee_hero` | `schemas/marquee_hero.schema.json` | Wide hero with stepped ziggurat frame and sunburst overlay. |
|
||||||
|
| `press_quote` | `schemas/press_quote.schema.json` | Italic Cormorant pull-quote with stars (clamped 0–5). |
|
||||||
|
|
||||||
|
Schemas are loaded BEFORE any `br.Register(...)` call (line ordering enforced in `register.go`).
|
||||||
|
|
||||||
|
### Template overrides (4)
|
||||||
|
- `heading` — caps tracking + hairline gold underline, Italiana display family.
|
||||||
|
- `text` — Cormorant body with italic drop-cap on first paragraph.
|
||||||
|
- `button` — black pill with gold border, stamped inset shadow on hover/focus.
|
||||||
|
- `image` — stepped ziggurat frame variant (also scallop / plain).
|
||||||
|
|
||||||
|
### Email wrapper
|
||||||
|
- `tr.RegisterEmailWrapper("art-deco", ArtDecoEmailWrapper)` wired.
|
||||||
|
- 600px centered column, ivory body, jet-black masthead.
|
||||||
|
- Masthead uses a flat-gold SVG `<img>` (not a CSS gradient) so Outlook renders it correctly.
|
||||||
|
- Reservation phone / `SupportEmail` auto-injected as italic postscript line.
|
||||||
|
|
||||||
|
### Master pages (2)
|
||||||
|
- `art-deco:default-master` covers `default`, `article` — `navbar` (header,0), `art-deco:divider_fan` (header,1,sunburst), `slot` (main,0,slotName=main), `art-deco:footer` (footer,0).
|
||||||
|
- `art-deco:marquee-master` covers `landing`, `full-width` — `navbar` (marquee,0), `art-deco:reservation` (marquee,1), `slot` (main,0,slotName=main), `art-deco:divider_fan` (cta,0,scallop), `art-deco:footer` (footer,0).
|
||||||
|
|
||||||
|
### Presets (`presets.json`, 3)
|
||||||
|
- `champagne-noir` (`mode: both`, lightColors + darkColors, all 19 tokens each).
|
||||||
|
- `ivory-rose` (`mode: light`, lightColors only, all 19 tokens).
|
||||||
|
- `velvet-onyx` (`mode: dark`, darkColors only, all 19 tokens).
|
||||||
|
- Every value is an HSL triple `H S% L%` — no `hsl(...)` wrappers, no hex.
|
||||||
|
|
||||||
|
### CSS manifest
|
||||||
|
- `CSSManifest.InputCSSAppend` registered via `ThemeCSSManifest()` in `embed.go`/`registration.go`.
|
||||||
|
- The injected CSS declares `@layer utilities` blocks for `.deco-rule`, `.deco-frame`, `.deco-step`, `.deco-grain`, plus aesthetic helpers `.deco-foil`, `.deco-sunburst`, `.deco-scallop`, `.deco-ziggurat`, `.deco-caps`, `.deco-dropcap`, `.deco-underline`, `.deco-pill`, `.deco-link`.
|
||||||
|
- All colour values consume `hsl(var(--token))`; all `font-family` declarations go through `var(--font-heading|body|mono, <fallback>)`.
|
||||||
|
|
||||||
|
### Fonts policy (Wave-1)
|
||||||
|
- `fonts.json = []` (no bundled woff2s in this pass).
|
||||||
|
- `RECOMMENDED_FONTS.md` lists the spec §5 fonts as Google Fonts picker recommendations (Italiana, Cormorant Garamond, JetBrains Mono).
|
||||||
|
- Fallback stacks for `--font-heading`, `--font-body`, `--font-mono` are embedded inline in every template + utility.
|
||||||
|
|
||||||
|
## Build output
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cd ~/src/blockninja/themes/art-deco && go mod tidy
|
||||||
|
(no output — clean)
|
||||||
|
$ /home/alex/go/bin/templ generate
|
||||||
|
(✓) Complete [ updates=13 duration=… ]
|
||||||
|
$ make
|
||||||
|
CGO_ENABLED=1 go build -buildmode=plugin -ldflags="-s -w" -o art-deco.so .
|
||||||
|
$ ls -lh art-deco.so
|
||||||
|
-rw-rw-r-- 1 alex alex 21M art-deco.so
|
||||||
|
```
|
||||||
|
|
||||||
|
## Safety check
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cd ~/src/blockninja/check-safety && go run . ~/src/blockninja/themes/art-deco \
|
||||||
|
--plugin-dir ~/src/blockninja/themes/art-deco
|
||||||
|
…
|
||||||
|
=== Check 2c: Standalone plugin SDK import boundaries ===
|
||||||
|
OK: Standalone plugin imports and go.mod stay on SDK version v0.11.1
|
||||||
|
=== Check 3: Go code compiles and passes go fix, golangci-lint --fix, go vet, and strict lint ===
|
||||||
|
OK: Go lint pipeline clean for 1 module(s)
|
||||||
|
=== Check 6: No hardcoded colors in frontend (use theme tokens) ===
|
||||||
|
OK: No hardcoded colors in .templ files
|
||||||
|
=== Check 11: No placeholder code; only shipped features ===
|
||||||
|
OK: No placeholder code found
|
||||||
|
…
|
||||||
|
exit=0
|
||||||
|
```
|
||||||
|
|
||||||
|
Only WARN remaining is Check 2e (`any` usage in `content map[string]any` signatures). This is the
|
||||||
|
exact same warning Gotham emits — it is the standalone-plugin block signature contract and is
|
||||||
|
non-blocking.
|
||||||
|
|
||||||
|
## Open items / deferred
|
||||||
|
|
||||||
|
### Wave-1 deferrals (intentional, per `themes/docs/FONTS.md`)
|
||||||
|
- **No bundled woff2 fonts**. Theme ships `fonts.json = []` and a `RECOMMENDED_FONTS.md` for the
|
||||||
|
admin to pick Italiana / Cormorant Garamond / JetBrains Mono from the Google Fonts tab.
|
||||||
|
- **No `LICENSES.md`** — nothing is bundled.
|
||||||
|
|
||||||
|
### Wave-2 follow-ups (out of scope for this pass)
|
||||||
|
- Commission a redistributable Art Deco display face (Italiana variants are limited) and bundle
|
||||||
|
via `fonts.json` if Italiana proves brittle at retina sizes. UAT §11 FOUT timing and the licence
|
||||||
|
audit only become checkable once a face is bundled.
|
||||||
|
- Capture the 6 UAT marketplace screenshots (`docs/uat/art-deco-shots/01..06.png`). Requires
|
||||||
|
a running CMS instance — outside the autonomous build scope.
|
||||||
|
- Seed the "Maison Étoile" demo content (6 pages, 4 courses, 1 press quote, 9 gallery images).
|
||||||
|
Same constraint as above — requires the instance container.
|
||||||
|
- Run the UAT browser-side gates: WCAG contrast pairs, focus-visible ring sweep, viewport
|
||||||
|
scroll-width checks at 360/768/1024/1440. The CSS is wired to satisfy them but the verification
|
||||||
|
is an instance-side workflow.
|
||||||
|
- Master-page UAT cell "Replacing `art-deco:footer` on `default-master` in admin produces a
|
||||||
|
ghost-free re-render" — admin-side verification, not a build-side check.
|
||||||
|
|
||||||
|
### Not implemented (called out by spec but explicitly listed in BUILD_REPORT)
|
||||||
|
- `make rebuild` and the container deploy targets — by build-instructions, `make rebuild` is
|
||||||
|
off-limits to the autonomous build pass. The Makefile here ships only `all`, `templ`, `clean`,
|
||||||
|
`help` — production deploy is reserved for the gotham-style upstream Makefile and a human run.
|
||||||
|
- Real woff2 bundles in `assets/fonts/` — deferred to Wave-2 per FONTS.md.
|
||||||
|
|
||||||
|
## File inventory
|
||||||
|
|
||||||
|
```
|
||||||
|
art-deco/
|
||||||
|
├── BUILD_REPORT.md ← this file
|
||||||
|
├── Makefile ← local-only build (make, make templ, make clean)
|
||||||
|
├── RECOMMENDED_FONTS.md ← Wave-1 Google Fonts picker guide
|
||||||
|
├── assets/style.css ← @layer utilities CSS (foil, frame, fan, grain, pill…)
|
||||||
|
├── button_override.{go,templ,_templ.go}
|
||||||
|
├── divider_fan.{go,templ,_templ.go}
|
||||||
|
├── email_wrapper.{templ,_templ.go}
|
||||||
|
├── embed.go ← //go:embed directives + CSSManifest hook
|
||||||
|
├── fonts.json ← []
|
||||||
|
├── footer.{go,templ,_templ.go}
|
||||||
|
├── gallery_fan.{go,templ,_templ.go}
|
||||||
|
├── go.{mod,sum} ← block/core v0.11.1, no replace directives
|
||||||
|
├── heading_override.{go,templ,_templ.go}
|
||||||
|
├── helpers.go ← getString / getInt / getSlice / getStringSlice / clampInt
|
||||||
|
├── image_override.{go,templ,_templ.go}
|
||||||
|
├── marquee_hero.{go,templ,_templ.go}
|
||||||
|
├── menu.{go,templ,_templ.go}
|
||||||
|
├── plugin.mod ← TOML metadata, kind=theme
|
||||||
|
├── presets.json ← 3 presets, 19 tokens each, HSL triples
|
||||||
|
├── press_quote.{go,templ,_templ.go}
|
||||||
|
├── press_quote_helpers.go
|
||||||
|
├── register.go ← Register(tr,br) + DefaultMasterPages()
|
||||||
|
├── registration.go ← var Registration plugin.PluginRegistration
|
||||||
|
├── reservation.{go,templ,_templ.go}
|
||||||
|
├── schemas/
|
||||||
|
│ ├── divider_fan.schema.json
|
||||||
|
│ ├── footer.schema.json
|
||||||
|
│ ├── gallery_fan.schema.json
|
||||||
|
│ ├── marquee_hero.schema.json
|
||||||
|
│ ├── menu.schema.json
|
||||||
|
│ ├── press_quote.schema.json
|
||||||
|
│ └── reservation.schema.json
|
||||||
|
├── template.templ + template_templ.go
|
||||||
|
└── text_override.{go,templ,_templ.go}
|
||||||
|
```
|
||||||
32
Makefile
Normal file
32
Makefile
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# Art Deco — local build helpers (.so plugin workflow).
|
||||||
|
#
|
||||||
|
# `make` produces art-deco.so via CGO go build -buildmode=plugin. This is the
|
||||||
|
# local-only build used by check-safety. The full `rebuild` target intentionally
|
||||||
|
# does NOT ship here — production deployment goes through the upstream gotham-style
|
||||||
|
# Makefile, which is reserved for human-run cycles, not autonomous agents.
|
||||||
|
|
||||||
|
.PHONY: all clean templ help
|
||||||
|
|
||||||
|
PLUGIN_NAME := art-deco
|
||||||
|
TEMPL_BIN := $(HOME)/go/bin/templ
|
||||||
|
|
||||||
|
# Default target: build the .so locally.
|
||||||
|
all: $(PLUGIN_NAME).so
|
||||||
|
|
||||||
|
# Local plugin build (no container).
|
||||||
|
$(PLUGIN_NAME).so: $(wildcard *.go) plugin.mod go.mod
|
||||||
|
CGO_ENABLED=1 go build -buildmode=plugin -ldflags="-s -w" -o $(PLUGIN_NAME).so .
|
||||||
|
|
||||||
|
# Regenerate templ Go files from .templ sources.
|
||||||
|
templ:
|
||||||
|
$(TEMPL_BIN) generate
|
||||||
|
|
||||||
|
# Remove build artefacts.
|
||||||
|
clean:
|
||||||
|
rm -f $(PLUGIN_NAME).so
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "Targets:"
|
||||||
|
@echo " all Build $(PLUGIN_NAME).so locally (default)"
|
||||||
|
@echo " templ Regenerate templ Go files"
|
||||||
|
@echo " clean Remove build artefacts"
|
||||||
53
RECOMMENDED_FONTS.md
Normal file
53
RECOMMENDED_FONTS.md
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# Recommended Fonts — Art Deco
|
||||||
|
|
||||||
|
This theme ships `fonts.json = []` (Wave-1 policy, see `themes/docs/FONTS.md`).
|
||||||
|
The intended typographic identity is achieved by an admin assigning the
|
||||||
|
following Google Fonts to the theme's typography slots after install.
|
||||||
|
|
||||||
|
## Picker workflow
|
||||||
|
|
||||||
|
1. Sign in as an admin and open **Settings → Typography**.
|
||||||
|
2. Switch to the **Google Fonts** tab in the font picker.
|
||||||
|
3. Search for the family, click **Add**, then assign it to the listed slot.
|
||||||
|
|
||||||
|
## Display heading — Italiana
|
||||||
|
|
||||||
|
- **Source**: `google:Italiana`
|
||||||
|
- **Slot**: Heading
|
||||||
|
- **Why**: The spec calls for stamped Art Deco titles in tight tracking. Italiana
|
||||||
|
is the closest curated Google Fonts match to the high-contrast 1920s display
|
||||||
|
cuts the brief describes.
|
||||||
|
- **Fallback already in CSS**: `var(--font-heading, "Italiana", "Cinzel", Georgia, serif)`.
|
||||||
|
|
||||||
|
### Alternative
|
||||||
|
|
||||||
|
- **Source**: `google:Cinzel` (also acceptable per spec §3)
|
||||||
|
Pick this if Italiana feels too thin at small sizes.
|
||||||
|
|
||||||
|
## Body — Cormorant Garamond
|
||||||
|
|
||||||
|
- **Source**: `google:Cormorant Garamond`
|
||||||
|
- **Slot**: Body
|
||||||
|
- **Why**: Classical italic and book weights for long-form prose, exactly the
|
||||||
|
"Cormorant for long-form prose with classical italics" pairing the spec calls
|
||||||
|
out.
|
||||||
|
- **Fallback already in CSS**: `var(--font-body, "Cormorant Garamond", "Cormorant", Georgia, serif)`.
|
||||||
|
|
||||||
|
## Mono — JetBrains Mono
|
||||||
|
|
||||||
|
- **Source**: `google:JetBrains Mono`
|
||||||
|
- **Slot**: Mono
|
||||||
|
- **Why**: Spec wants a precise monospace for reservation IDs and room numbers.
|
||||||
|
JetBrains Mono ships across all curated Google Fonts and is the cleanest
|
||||||
|
utilitarian option.
|
||||||
|
- **Fallback already in CSS**: `var(--font-mono, "JetBrains Mono", ui-monospace, monospace)`.
|
||||||
|
|
||||||
|
## Notes for Wave-2
|
||||||
|
|
||||||
|
- If Italiana proves too brittle on retina at large sizes, commission a
|
||||||
|
redistributable Art Deco display face and bundle it via `fonts.json`. The
|
||||||
|
rest of the CSS is already variable-driven and will pick the new family up
|
||||||
|
without further edits.
|
||||||
|
- Body italic for the drop-cap relies on the body family having an italic
|
||||||
|
variant; both Cormorant Garamond and Cormorant ship italics in the curated
|
||||||
|
Google Fonts list — no extra picker action needed.
|
||||||
0
assets/.gitkeep
Normal file
0
assets/.gitkeep
Normal file
212
assets/style.css
Normal file
212
assets/style.css
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
/* Art Deco theme — utility CSS for foil, frames, fans and grain.
|
||||||
|
*
|
||||||
|
* Color is always consumed via hsl(var(--token)) — the tokens carry HSL triples.
|
||||||
|
* Font families always come from var(--font-heading|body|mono, <fallback>).
|
||||||
|
*/
|
||||||
|
|
||||||
|
@layer utilities {
|
||||||
|
/* deco-rule — hairline gold rule, 1px ascending to 2px on accent lines. */
|
||||||
|
.deco-rule {
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid hsl(var(--primary));
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.deco-rule-thick {
|
||||||
|
border: none;
|
||||||
|
border-top: 2px solid hsl(var(--primary));
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* deco-frame — stepped ziggurat frame, layered borders + clipped corners. */
|
||||||
|
.deco-frame {
|
||||||
|
position: relative;
|
||||||
|
border: 1px solid hsl(var(--primary));
|
||||||
|
padding: 1.25rem;
|
||||||
|
}
|
||||||
|
.deco-frame::before,
|
||||||
|
.deco-frame::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: -6px;
|
||||||
|
right: -6px;
|
||||||
|
height: 4px;
|
||||||
|
background: hsl(var(--primary));
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
.deco-frame::before { top: -6px; }
|
||||||
|
.deco-frame::after { bottom: -6px; }
|
||||||
|
|
||||||
|
/* deco-step — stepped corner mask suitable for image frames. */
|
||||||
|
.deco-step {
|
||||||
|
position: relative;
|
||||||
|
padding: 4px;
|
||||||
|
background:
|
||||||
|
linear-gradient(hsl(var(--primary)), hsl(var(--primary))) top left / 14px 1px no-repeat,
|
||||||
|
linear-gradient(hsl(var(--primary)), hsl(var(--primary))) top left / 1px 14px no-repeat,
|
||||||
|
linear-gradient(hsl(var(--primary)), hsl(var(--primary))) top right / 14px 1px no-repeat,
|
||||||
|
linear-gradient(hsl(var(--primary)), hsl(var(--primary))) top right / 1px 14px no-repeat,
|
||||||
|
linear-gradient(hsl(var(--primary)), hsl(var(--primary))) bottom left / 14px 1px no-repeat,
|
||||||
|
linear-gradient(hsl(var(--primary)), hsl(var(--primary))) bottom left / 1px 14px no-repeat,
|
||||||
|
linear-gradient(hsl(var(--primary)), hsl(var(--primary))) bottom right / 14px 1px no-repeat,
|
||||||
|
linear-gradient(hsl(var(--primary)), hsl(var(--primary))) bottom right / 1px 14px no-repeat;
|
||||||
|
}
|
||||||
|
.deco-step > * {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* deco-grain — 3% film-grain noise, dark surfaces only. */
|
||||||
|
.deco-grain {
|
||||||
|
position: relative;
|
||||||
|
isolation: isolate;
|
||||||
|
}
|
||||||
|
.deco-grain::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: -1;
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.03;
|
||||||
|
background-image:
|
||||||
|
repeating-radial-gradient(circle at 17% 23%, hsl(var(--foreground)) 0 1px, transparent 1px 3px),
|
||||||
|
repeating-radial-gradient(circle at 73% 61%, hsl(var(--foreground)) 0 1px, transparent 1px 3px);
|
||||||
|
mix-blend-mode: overlay;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* deco-foil — inset gold "stamp" shadow used on hover states. */
|
||||||
|
.deco-foil {
|
||||||
|
box-shadow:
|
||||||
|
inset 0 0 0 1px hsl(var(--primary)),
|
||||||
|
inset 0 1px 0 hsl(var(--primary) / 0.6),
|
||||||
|
inset 0 -1px 0 hsl(var(--primary) / 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* deco-sunburst — conic-gradient sunburst rays. Used by divider_fan & marquee_hero. */
|
||||||
|
.deco-sunburst {
|
||||||
|
background-image:
|
||||||
|
conic-gradient(
|
||||||
|
from 180deg at 50% 100%,
|
||||||
|
transparent 0deg,
|
||||||
|
hsl(var(--primary) / 0.7) 5deg,
|
||||||
|
transparent 10deg,
|
||||||
|
transparent 20deg,
|
||||||
|
hsl(var(--primary) / 0.55) 25deg,
|
||||||
|
transparent 30deg,
|
||||||
|
transparent 40deg,
|
||||||
|
hsl(var(--primary) / 0.7) 45deg,
|
||||||
|
transparent 50deg,
|
||||||
|
transparent 60deg,
|
||||||
|
hsl(var(--primary) / 0.55) 65deg,
|
||||||
|
transparent 70deg,
|
||||||
|
transparent 80deg,
|
||||||
|
hsl(var(--primary) / 0.7) 85deg,
|
||||||
|
transparent 90deg,
|
||||||
|
transparent 100deg,
|
||||||
|
hsl(var(--primary) / 0.55) 105deg,
|
||||||
|
transparent 110deg,
|
||||||
|
transparent 120deg,
|
||||||
|
hsl(var(--primary) / 0.7) 125deg,
|
||||||
|
transparent 130deg,
|
||||||
|
transparent 140deg,
|
||||||
|
hsl(var(--primary) / 0.55) 145deg,
|
||||||
|
transparent 150deg,
|
||||||
|
transparent 160deg,
|
||||||
|
hsl(var(--primary) / 0.7) 165deg,
|
||||||
|
transparent 170deg,
|
||||||
|
transparent 180deg
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* deco-scallop — scalloped fan crest via radial gradient. */
|
||||||
|
.deco-scallop {
|
||||||
|
background-image:
|
||||||
|
radial-gradient(circle at 50% 100%, hsl(var(--primary) / 0.7) 0 2px, transparent 3px),
|
||||||
|
repeating-linear-gradient(
|
||||||
|
to right,
|
||||||
|
transparent 0,
|
||||||
|
transparent 12px,
|
||||||
|
hsl(var(--primary) / 0.55) 12px,
|
||||||
|
hsl(var(--primary) / 0.55) 13px,
|
||||||
|
transparent 13px,
|
||||||
|
transparent 24px
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* deco-ziggurat — stepped ziggurat divider stripes. */
|
||||||
|
.deco-ziggurat {
|
||||||
|
background-image:
|
||||||
|
linear-gradient(to bottom, transparent 0 30%, hsl(var(--primary)) 30% 32%, transparent 32% 50%, hsl(var(--primary)) 50% 52%, transparent 52% 70%, hsl(var(--primary)) 70% 72%, transparent 72% 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* deco-caps — caps tracking helper for Italiana display headings. */
|
||||||
|
.deco-caps {
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* deco-dropcap — classical italic dropcap on first paragraph. */
|
||||||
|
.deco-dropcap[data-deco-dropcap]::first-letter {
|
||||||
|
font-family: var(--font-body, "Cormorant Garamond", "Cormorant", Georgia, serif);
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 3.2em;
|
||||||
|
line-height: 0.95;
|
||||||
|
float: left;
|
||||||
|
padding: 0.05em 0.12em 0 0;
|
||||||
|
color: hsl(var(--primary));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* deco-underline — hairline gold underline on headings. */
|
||||||
|
.deco-underline {
|
||||||
|
position: relative;
|
||||||
|
padding-bottom: 0.35em;
|
||||||
|
}
|
||||||
|
.deco-underline::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
height: 1px;
|
||||||
|
background: hsl(var(--primary));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* deco-pill — black pill button base for the button override. */
|
||||||
|
.deco-pill {
|
||||||
|
border-radius: 9999px;
|
||||||
|
border-width: 1px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: hsl(var(--primary));
|
||||||
|
background-color: hsl(var(--foreground));
|
||||||
|
color: hsl(var(--background));
|
||||||
|
padding: 0.65rem 1.6rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
transition: box-shadow 200ms ease, background-color 200ms ease;
|
||||||
|
}
|
||||||
|
.deco-pill:hover,
|
||||||
|
.deco-pill:focus-visible {
|
||||||
|
box-shadow:
|
||||||
|
inset 0 0 0 1px hsl(var(--primary)),
|
||||||
|
inset 0 1px 0 hsl(var(--primary) / 0.6),
|
||||||
|
inset 0 -1px 0 hsl(var(--primary) / 0.4);
|
||||||
|
}
|
||||||
|
.deco-pill:focus-visible {
|
||||||
|
outline: 2px solid hsl(var(--ring));
|
||||||
|
outline-offset: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* deco-link — gold underline link state. */
|
||||||
|
.deco-link {
|
||||||
|
color: hsl(var(--primary));
|
||||||
|
text-decoration: none;
|
||||||
|
border-bottom: 1px solid hsl(var(--primary));
|
||||||
|
transition: color 200ms ease, border-color 200ms ease;
|
||||||
|
}
|
||||||
|
.deco-link:hover,
|
||||||
|
.deco-link:focus-visible {
|
||||||
|
color: hsl(var(--accent));
|
||||||
|
border-bottom-color: hsl(var(--accent));
|
||||||
|
}
|
||||||
|
}
|
||||||
41
button_override.go
Normal file
41
button_override.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ButtonOverrideData is the typed view for the button override component.
|
||||||
|
type ButtonOverrideData struct {
|
||||||
|
Label string
|
||||||
|
URL string
|
||||||
|
Variant string
|
||||||
|
}
|
||||||
|
|
||||||
|
// normaliseButtonVariant returns a known variant or "primary" as default.
|
||||||
|
func normaliseButtonVariant(v string) string {
|
||||||
|
switch v {
|
||||||
|
case "primary", "secondary", "ghost":
|
||||||
|
return v
|
||||||
|
default:
|
||||||
|
return "primary"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArtDecoButtonBlock overrides the built-in "button" block when the Art Deco theme is active.
|
||||||
|
// Content: {"label": "...", "url": "...", "variant": "primary|secondary|ghost"}
|
||||||
|
func ArtDecoButtonBlock(ctx context.Context, content map[string]any) string {
|
||||||
|
data := ButtonOverrideData{
|
||||||
|
Label: getString(content, "label"),
|
||||||
|
URL: getString(content, "url"),
|
||||||
|
Variant: normaliseButtonVariant(getString(content, "variant")),
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.Label == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = artDecoButtonComponent(data).Render(ctx, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
36
button_override.templ
Normal file
36
button_override.templ
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// buttonVariantStyle returns inline style overrides per variant.
|
||||||
|
func buttonVariantStyle(variant string) string {
|
||||||
|
switch variant {
|
||||||
|
case "secondary":
|
||||||
|
return "background-color: hsl(var(--secondary)); color: hsl(var(--secondary-foreground)); border-color: hsl(var(--primary));"
|
||||||
|
case "ghost":
|
||||||
|
return "background-color: transparent; color: hsl(var(--foreground)); border-color: hsl(var(--primary));"
|
||||||
|
default:
|
||||||
|
return "background-color: hsl(var(--foreground)); color: hsl(var(--background)); border-color: hsl(var(--primary));"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// artDecoButtonComponent renders the black-pill button with stamped hover state.
|
||||||
|
templ artDecoButtonComponent(data ButtonOverrideData) {
|
||||||
|
if data.URL != "" {
|
||||||
|
<a
|
||||||
|
data-block-override="art-deco:button"
|
||||||
|
href={ templ.SafeURL(data.URL) }
|
||||||
|
class="deco-pill inline-block"
|
||||||
|
style={ buttonVariantStyle(data.Variant) }
|
||||||
|
>
|
||||||
|
{ data.Label }
|
||||||
|
</a>
|
||||||
|
} else {
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
data-block-override="art-deco:button"
|
||||||
|
class="deco-pill"
|
||||||
|
style={ buttonVariantStyle(data.Variant) }
|
||||||
|
>
|
||||||
|
{ data.Label }
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
}
|
||||||
125
button_override_templ.go
Normal file
125
button_override_templ.go
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
// 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"
|
||||||
|
|
||||||
|
// buttonVariantStyle returns inline style overrides per variant.
|
||||||
|
func buttonVariantStyle(variant string) string {
|
||||||
|
switch variant {
|
||||||
|
case "secondary":
|
||||||
|
return "background-color: hsl(var(--secondary)); color: hsl(var(--secondary-foreground)); border-color: hsl(var(--primary));"
|
||||||
|
case "ghost":
|
||||||
|
return "background-color: transparent; color: hsl(var(--foreground)); border-color: hsl(var(--primary));"
|
||||||
|
default:
|
||||||
|
return "background-color: hsl(var(--foreground)); color: hsl(var(--background)); border-color: hsl(var(--primary));"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// artDecoButtonComponent renders the black-pill button with stamped hover state.
|
||||||
|
func artDecoButtonComponent(data ButtonOverrideData) 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 data.URL != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<a data-block-override=\"art-deco:button\" href=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var2 templ.SafeURL
|
||||||
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(data.URL))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button_override.templ`, Line: 20, Col: 33}
|
||||||
|
}
|
||||||
|
_, 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, "\" class=\"deco-pill inline-block\" style=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var3 string
|
||||||
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(buttonVariantStyle(data.Variant))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button_override.templ`, Line: 22, Col: 43}
|
||||||
|
}
|
||||||
|
_, 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
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 string
|
||||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(data.Label)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button_override.templ`, Line: 24, Col: 15}
|
||||||
|
}
|
||||||
|
_, 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, 4, "</a>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<button type=\"button\" data-block-override=\"art-deco:button\" class=\"deco-pill\" style=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var5 string
|
||||||
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(buttonVariantStyle(data.Variant))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button_override.templ`, Line: 31, Col: 43}
|
||||||
|
}
|
||||||
|
_, 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, 6, "\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var6 string
|
||||||
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(data.Label)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button_override.templ`, Line: 33, Col: 15}
|
||||||
|
}
|
||||||
|
_, 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, 7, "</button>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
43
divider_fan.go
Normal file
43
divider_fan.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/core/blocks"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DividerFanBlockMeta defines the fan divider block.
|
||||||
|
var DividerFanBlockMeta = blocks.BlockMeta{
|
||||||
|
Key: "divider_fan",
|
||||||
|
Title: "Fan Divider",
|
||||||
|
Description: "Pure-CSS gold geometric divider — sunburst, scallop or ziggurat.",
|
||||||
|
Source: "art-deco",
|
||||||
|
}
|
||||||
|
|
||||||
|
// DividerFanData carries the variant for the divider component.
|
||||||
|
type DividerFanData struct {
|
||||||
|
Variant string
|
||||||
|
}
|
||||||
|
|
||||||
|
// normaliseVariant clamps to the supported set; unknown values fall back to sunburst.
|
||||||
|
func normaliseVariant(v string) string {
|
||||||
|
switch v {
|
||||||
|
case "scallop", "ziggurat":
|
||||||
|
return v
|
||||||
|
default:
|
||||||
|
return "sunburst"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DividerFanBlock renders a CSS-only geometric divider.
|
||||||
|
// Content: {"variant": "sunburst" | "scallop" | "ziggurat"}
|
||||||
|
func DividerFanBlock(ctx context.Context, content map[string]any) string {
|
||||||
|
data := DividerFanData{
|
||||||
|
Variant: normaliseVariant(getString(content, "variant")),
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = dividerFanComponent(data).Render(ctx, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
26
divider_fan.templ
Normal file
26
divider_fan.templ
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// dividerVariantClass maps the variant to the corresponding utility class.
|
||||||
|
func dividerVariantClass(variant string) string {
|
||||||
|
switch variant {
|
||||||
|
case "scallop":
|
||||||
|
return "deco-scallop"
|
||||||
|
case "ziggurat":
|
||||||
|
return "deco-ziggurat"
|
||||||
|
default:
|
||||||
|
return "deco-sunburst"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// dividerFanComponent renders a pure-CSS gold geometric divider.
|
||||||
|
templ dividerFanComponent(data DividerFanData) {
|
||||||
|
<div
|
||||||
|
data-block="art-deco:divider_fan"
|
||||||
|
data-variant={ data.Variant }
|
||||||
|
class="w-full py-6 flex justify-center"
|
||||||
|
role="presentation"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<div class={ "h-10 w-full max-w-xl", dividerVariantClass(data.Variant) }></div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
88
divider_fan_templ.go
Normal file
88
divider_fan_templ.go
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
// 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"
|
||||||
|
|
||||||
|
// dividerVariantClass maps the variant to the corresponding utility class.
|
||||||
|
func dividerVariantClass(variant string) string {
|
||||||
|
switch variant {
|
||||||
|
case "scallop":
|
||||||
|
return "deco-scallop"
|
||||||
|
case "ziggurat":
|
||||||
|
return "deco-ziggurat"
|
||||||
|
default:
|
||||||
|
return "deco-sunburst"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// dividerFanComponent renders a pure-CSS gold geometric divider.
|
||||||
|
func dividerFanComponent(data DividerFanData) 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=\"art-deco:divider_fan\" data-variant=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var2 string
|
||||||
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.Variant)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `divider_fan.templ`, Line: 19, Col: 29}
|
||||||
|
}
|
||||||
|
_, 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=\"w-full py-6 flex justify-center\" role=\"presentation\" aria-hidden=\"true\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var3 = []any{"h-10 w-full max-w-xl", dividerVariantClass(data.Variant)}
|
||||||
|
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, 3, "<div 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: `divider_fan.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, 4, "\"></div></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
178
email_wrapper.templ
Normal file
178
email_wrapper.templ
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/core/templates"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ArtDecoEmailWrapper wraps body content in the Art Deco email layout:
|
||||||
|
// ivory background, 600px column, jet-black masthead with flat-gold PNG sunburst,
|
||||||
|
// Cormorant body, gold hairline above footer, reservation phone postscript.
|
||||||
|
func ArtDecoEmailWrapper(body string, emailCtx templates.EmailContext) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = artDecoEmailTemplate(emailCtx, body).Render(context.Background(), &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// edColor returns a color from the email context or a hex fallback for the named token.
|
||||||
|
func edColor(value, fallback string) string {
|
||||||
|
if value != "" {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
// edBg ivory background.
|
||||||
|
func edBg(emailCtx templates.EmailContext) string {
|
||||||
|
return edColor(emailCtx.Colors.Background, "#f6efe2")
|
||||||
|
}
|
||||||
|
|
||||||
|
// edCard jet-black masthead surface (uses Foreground as the dark masthead colour).
|
||||||
|
func edCardDark(emailCtx templates.EmailContext) string {
|
||||||
|
return edColor(emailCtx.Colors.Foreground, "#191510")
|
||||||
|
}
|
||||||
|
|
||||||
|
// edFg foreground text on the ivory body.
|
||||||
|
func edFg(emailCtx templates.EmailContext) string {
|
||||||
|
return edColor(emailCtx.Colors.Foreground, "#191510")
|
||||||
|
}
|
||||||
|
|
||||||
|
// edPrimary champagne-gold accent (used for the hairline and links).
|
||||||
|
func edPrimary(emailCtx templates.EmailContext) string {
|
||||||
|
return edColor(emailCtx.Colors.Primary, "#c9a14a")
|
||||||
|
}
|
||||||
|
|
||||||
|
// edMutedFg muted footer text.
|
||||||
|
func edMutedFg(emailCtx templates.EmailContext) string {
|
||||||
|
return edColor(emailCtx.Colors.MutedForeground, "#6c5b35")
|
||||||
|
}
|
||||||
|
|
||||||
|
// edBorder hairline gold border colour.
|
||||||
|
func edBorder(emailCtx templates.EmailContext) string {
|
||||||
|
return edColor(emailCtx.Colors.Border, "#c9a14a")
|
||||||
|
}
|
||||||
|
|
||||||
|
// flatGoldSunburstSVG is the inlined data: URI for the flat-gold sunburst image
|
||||||
|
// used as a fallback for the Outlook masthead (CSS conic-gradient is flattened by Outlook).
|
||||||
|
// We render via string concatenation so the URL-encoded %xx tokens are not misread as fmt verbs.
|
||||||
|
func flatGoldSunburstSVG(gold string) string {
|
||||||
|
g := stripHash(gold)
|
||||||
|
prefix := "data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20600%2080%22%3E%3Crect%20width%3D%22600%22%20height%3D%2280%22%20fill%3D%22%23191510%22%2F%3E%3Cg%20stroke%3D%22%23"
|
||||||
|
rays := "%22%20stroke-width%3D%221%22%20fill%3D%22none%22%3E%3Cpath%20d%3D%22M300%2080%20L%20100%2010%22%2F%3E%3Cpath%20d%3D%22M300%2080%20L%20180%2010%22%2F%3E%3Cpath%20d%3D%22M300%2080%20L%20260%2010%22%2F%3E%3Cpath%20d%3D%22M300%2080%20L%20340%2010%22%2F%3E%3Cpath%20d%3D%22M300%2080%20L%20420%2010%22%2F%3E%3Cpath%20d%3D%22M300%2080%20L%20500%2010%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E"
|
||||||
|
return prefix + g + rays
|
||||||
|
}
|
||||||
|
|
||||||
|
// stripHash removes a leading '#' if present (defensive for the data URI).
|
||||||
|
func stripHash(s string) string {
|
||||||
|
if len(s) > 0 && s[0] == '#' {
|
||||||
|
return s[1:]
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// artDecoEmailTemplate renders the Art Deco email wrapper.
|
||||||
|
templ artDecoEmailTemplate(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"/>
|
||||||
|
<!--[if mso]>
|
||||||
|
<noscript>
|
||||||
|
<xml>
|
||||||
|
<o:OfficeDocumentSettings>
|
||||||
|
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||||
|
</o:OfficeDocumentSettings>
|
||||||
|
</xml>
|
||||||
|
</noscript>
|
||||||
|
<![endif]-->
|
||||||
|
<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; }
|
||||||
|
a[x-apple-data-detectors] { color: inherit !important; text-decoration: none !important; }
|
||||||
|
@media only screen and (max-width: 620px) {
|
||||||
|
.email-container { width: 100% !important; max-width: 100% !important; }
|
||||||
|
.content-padding { padding-left: 24px !important; padding-right: 24px !important; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body style={ fmt.Sprintf("background-color: %s; margin: 0; padding: 0; font-family: 'Cormorant Garamond', 'Cormorant', Georgia, serif;", edBg(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: 32px 10px; background-color: %s;", edBg(emailCtx)) }>
|
||||||
|
<table role="presentation" class="email-container" width="600" cellspacing="0" cellpadding="0" border="0" style={ fmt.Sprintf("max-width: 600px; background-color: %s; border: 1px solid %s;", edBg(emailCtx), edBorder(emailCtx)) }>
|
||||||
|
<tr>
|
||||||
|
<td align="center" style={ fmt.Sprintf("padding: 0; background-color: %s;", edCardDark(emailCtx)) }>
|
||||||
|
<img
|
||||||
|
src={ flatGoldSunburstSVG(edPrimary(emailCtx)) }
|
||||||
|
alt=""
|
||||||
|
width="600"
|
||||||
|
height="80"
|
||||||
|
style={ fmt.Sprintf("display: block; width: 100%%; max-width: 600px; height: 80px; background-color: %s;", edCardDark(emailCtx)) }
|
||||||
|
/>
|
||||||
|
if emailCtx.SiteSettings.SiteName != "" {
|
||||||
|
<h1 style={ fmt.Sprintf("margin: 0; padding: 24px 16px; font-family: 'Italiana', 'Cinzel', Georgia, serif; font-size: 28px; text-transform: uppercase; letter-spacing: 0.18em; color: %s;", edPrimary(emailCtx)) }>
|
||||||
|
{ emailCtx.SiteSettings.SiteName }
|
||||||
|
</h1>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="content-padding" style={ fmt.Sprintf("padding: 40px 48px; color: %s; font-size: 16px; line-height: 1.7;", edFg(emailCtx)) }>
|
||||||
|
@templ.Raw(body)
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style={ fmt.Sprintf("padding: 0 48px;") }>
|
||||||
|
<hr style={ fmt.Sprintf("border: none; border-top: 1px solid %s; margin: 0;", edPrimary(emailCtx)) }/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center" style={ fmt.Sprintf("padding: 24px 48px; color: %s; font-size: 13px;", edMutedFg(emailCtx)) }>
|
||||||
|
<p style={ fmt.Sprintf("margin: 0 0 8px; font-family: 'Italiana', 'Cinzel', Georgia, serif; text-transform: uppercase; letter-spacing: 0.18em; color: %s;", edFg(emailCtx)) }>
|
||||||
|
{ emailCtx.SiteSettings.SiteName }
|
||||||
|
</p>
|
||||||
|
if emailCtx.SiteSettings.SiteURL != "" {
|
||||||
|
<p style="margin: 0 0 8px;">
|
||||||
|
<a href={ templ.SafeURL(emailCtx.SiteSettings.SiteURL) } style={ fmt.Sprintf("color: %s; text-decoration: none; border-bottom: 1px solid %s;", edPrimary(emailCtx), edPrimary(emailCtx)) }>
|
||||||
|
{ emailCtx.SiteSettings.SiteURL }
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
if emailCtx.SiteSettings.SupportEmail != "" {
|
||||||
|
<p style={ fmt.Sprintf("margin: 12px 0 0; font-style: italic; color: %s;", edMutedFg(emailCtx)) }>
|
||||||
|
P.S. Reservations & enquiries: { emailCtx.SiteSettings.SupportEmail }
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
if emailCtx.UnsubscribeURL != "" {
|
||||||
|
<p style="margin: 16px 0 0; font-size: 11px;">
|
||||||
|
<a href={ templ.SafeURL(emailCtx.UnsubscribeURL) } style={ fmt.Sprintf("color: %s; text-decoration: none;", edMutedFg(emailCtx)) }>
|
||||||
|
Unsubscribe
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
}
|
||||||
463
email_wrapper_templ.go
Normal file
463
email_wrapper_templ.go
Normal file
@ -0,0 +1,463 @@
|
|||||||
|
// 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 (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/core/templates"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ArtDecoEmailWrapper wraps body content in the Art Deco email layout:
|
||||||
|
// ivory background, 600px column, jet-black masthead with flat-gold PNG sunburst,
|
||||||
|
// Cormorant body, gold hairline above footer, reservation phone postscript.
|
||||||
|
func ArtDecoEmailWrapper(body string, emailCtx templates.EmailContext) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = artDecoEmailTemplate(emailCtx, body).Render(context.Background(), &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// edColor returns a color from the email context or a hex fallback for the named token.
|
||||||
|
func edColor(value, fallback string) string {
|
||||||
|
if value != "" {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
// edBg ivory background.
|
||||||
|
func edBg(emailCtx templates.EmailContext) string {
|
||||||
|
return edColor(emailCtx.Colors.Background, "#f6efe2")
|
||||||
|
}
|
||||||
|
|
||||||
|
// edCard jet-black masthead surface (uses Foreground as the dark masthead colour).
|
||||||
|
func edCardDark(emailCtx templates.EmailContext) string {
|
||||||
|
return edColor(emailCtx.Colors.Foreground, "#191510")
|
||||||
|
}
|
||||||
|
|
||||||
|
// edFg foreground text on the ivory body.
|
||||||
|
func edFg(emailCtx templates.EmailContext) string {
|
||||||
|
return edColor(emailCtx.Colors.Foreground, "#191510")
|
||||||
|
}
|
||||||
|
|
||||||
|
// edPrimary champagne-gold accent (used for the hairline and links).
|
||||||
|
func edPrimary(emailCtx templates.EmailContext) string {
|
||||||
|
return edColor(emailCtx.Colors.Primary, "#c9a14a")
|
||||||
|
}
|
||||||
|
|
||||||
|
// edMutedFg muted footer text.
|
||||||
|
func edMutedFg(emailCtx templates.EmailContext) string {
|
||||||
|
return edColor(emailCtx.Colors.MutedForeground, "#6c5b35")
|
||||||
|
}
|
||||||
|
|
||||||
|
// edBorder hairline gold border colour.
|
||||||
|
func edBorder(emailCtx templates.EmailContext) string {
|
||||||
|
return edColor(emailCtx.Colors.Border, "#c9a14a")
|
||||||
|
}
|
||||||
|
|
||||||
|
// flatGoldSunburstSVG is the inlined data: URI for the flat-gold sunburst image
|
||||||
|
// used as a fallback for the Outlook masthead (CSS conic-gradient is flattened by Outlook).
|
||||||
|
// We render via string concatenation so the URL-encoded %xx tokens are not misread as fmt verbs.
|
||||||
|
func flatGoldSunburstSVG(gold string) string {
|
||||||
|
g := stripHash(gold)
|
||||||
|
prefix := "data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20600%2080%22%3E%3Crect%20width%3D%22600%22%20height%3D%2280%22%20fill%3D%22%23191510%22%2F%3E%3Cg%20stroke%3D%22%23"
|
||||||
|
rays := "%22%20stroke-width%3D%221%22%20fill%3D%22none%22%3E%3Cpath%20d%3D%22M300%2080%20L%20100%2010%22%2F%3E%3Cpath%20d%3D%22M300%2080%20L%20180%2010%22%2F%3E%3Cpath%20d%3D%22M300%2080%20L%20260%2010%22%2F%3E%3Cpath%20d%3D%22M300%2080%20L%20340%2010%22%2F%3E%3Cpath%20d%3D%22M300%2080%20L%20420%2010%22%2F%3E%3Cpath%20d%3D%22M300%2080%20L%20500%2010%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E"
|
||||||
|
return prefix + g + rays
|
||||||
|
}
|
||||||
|
|
||||||
|
// stripHash removes a leading '#' if present (defensive for the data URI).
|
||||||
|
func stripHash(s string) string {
|
||||||
|
if len(s) > 0 && s[0] == '#' {
|
||||||
|
return s[1:]
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// artDecoEmailTemplate renders the Art Deco email wrapper.
|
||||||
|
func artDecoEmailTemplate(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\"><!--[if mso]>\n\t\t\t<noscript>\n\t\t\t\t<xml>\n\t\t\t\t\t<o:OfficeDocumentSettings>\n\t\t\t\t\t\t<o:PixelsPerInch>96</o:PixelsPerInch>\n\t\t\t\t\t</o:OfficeDocumentSettings>\n\t\t\t\t</xml>\n\t\t\t</noscript>\n\t\t\t<![endif]--><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: 94, 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 {\n\t\t\t\t\t-webkit-text-size-adjust: 100%;\n\t\t\t\t\t-ms-text-size-adjust: 100%;\n\t\t\t\t}\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\ta[x-apple-data-detectors] { color: inherit !important; text-decoration: none !important; }\n\t\t\t\t@media only screen and (max-width: 620px) {\n\t\t\t\t\t.email-container { width: 100% !important; max-width: 100% !important; }\n\t\t\t\t\t.content-padding { 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(fmt.Sprintf("background-color: %s; margin: 0; padding: 0; font-family: 'Cormorant Garamond', 'Cormorant', Georgia, serif;", edBg(emailCtx)))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 110, Col: 155}
|
||||||
|
}
|
||||||
|
_, 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: 113, 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: 32px 10px; background-color: %s;", edBg(emailCtx)))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 118, Col: 104}
|
||||||
|
}
|
||||||
|
_, 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=\"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;", edBg(emailCtx), edBorder(emailCtx)))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 119, Col: 232}
|
||||||
|
}
|
||||||
|
_, 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, "\"><tr><td align=\"center\" style=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var7 string
|
||||||
|
templ_7745c5c3_Var7, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(fmt.Sprintf("padding: 0; background-color: %s;", edCardDark(emailCtx)))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 121, Col: 105}
|
||||||
|
}
|
||||||
|
_, 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, "\"><img src=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var8 string
|
||||||
|
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.ResolveAttributeValue(flatGoldSunburstSVG(edPrimary(emailCtx)))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 123, Col: 56}
|
||||||
|
}
|
||||||
|
_, 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, 10, "\" alt=\"\" width=\"600\" height=\"80\" style=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var9 string
|
||||||
|
templ_7745c5c3_Var9, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(fmt.Sprintf("display: block; width: 100%%; max-width: 600px; height: 80px; background-color: %s;", edCardDark(emailCtx)))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 127, Col: 138}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||||
|
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
|
||||||
|
}
|
||||||
|
if emailCtx.SiteSettings.SiteName != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<h1 style=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var10 string
|
||||||
|
templ_7745c5c3_Var10, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(fmt.Sprintf("margin: 0; padding: 24px 16px; font-family: 'Italiana', 'Cinzel', Georgia, serif; font-size: 28px; text-transform: uppercase; letter-spacing: 0.18em; color: %s;", edPrimary(emailCtx)))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 130, Col: 218}
|
||||||
|
}
|
||||||
|
_, 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, 13, "\">")
|
||||||
|
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: 131, Col: 43}
|
||||||
|
}
|
||||||
|
_, 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, 14, "</h1>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</td></tr><tr><td class=\"content-padding\" 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("padding: 40px 48px; color: %s; font-size: 16px; line-height: 1.7;", edFg(emailCtx)))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 137, Col: 140}
|
||||||
|
}
|
||||||
|
_, 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, 16, "\">")
|
||||||
|
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, 17, "</td></tr><tr><td style=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var13 string
|
||||||
|
templ_7745c5c3_Var13, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(fmt.Sprintf("padding: 0 48px;"))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 142, Col: 51}
|
||||||
|
}
|
||||||
|
_, 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, "\"><hr style=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var14 string
|
||||||
|
templ_7745c5c3_Var14, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(fmt.Sprintf("border: none; border-top: 1px solid %s; margin: 0;", edPrimary(emailCtx)))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 143, Col: 107}
|
||||||
|
}
|
||||||
|
_, 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, 19, "\"></td></tr><tr><td align=\"center\" style=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var15 string
|
||||||
|
templ_7745c5c3_Var15, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(fmt.Sprintf("padding: 24px 48px; color: %s; font-size: 13px;", edMutedFg(emailCtx)))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 147, Col: 118}
|
||||||
|
}
|
||||||
|
_, 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, 20, "\"><p style=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var16 string
|
||||||
|
templ_7745c5c3_Var16, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(fmt.Sprintf("margin: 0 0 8px; font-family: 'Italiana', 'Cinzel', Georgia, serif; text-transform: uppercase; letter-spacing: 0.18em; color: %s;", edFg(emailCtx)))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 148, Col: 180}
|
||||||
|
}
|
||||||
|
_, 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, 21, "\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var17 string
|
||||||
|
templ_7745c5c3_Var17, 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: 149, Col: 42}
|
||||||
|
}
|
||||||
|
_, 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, 22, "</p>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if emailCtx.SiteSettings.SiteURL != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<p style=\"margin: 0 0 8px;\"><a href=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var18 templ.SafeURL
|
||||||
|
templ_7745c5c3_Var18, 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: 153, Col: 65}
|
||||||
|
}
|
||||||
|
_, 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, 24, "\" style=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var19 string
|
||||||
|
templ_7745c5c3_Var19, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(fmt.Sprintf("color: %s; text-decoration: none; border-bottom: 1px solid %s;", edPrimary(emailCtx), edPrimary(emailCtx)))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 153, Col: 195}
|
||||||
|
}
|
||||||
|
_, 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, 25, "\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var20 string
|
||||||
|
templ_7745c5c3_Var20, 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: 154, Col: 43}
|
||||||
|
}
|
||||||
|
_, 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, 26, "</a></p>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if emailCtx.SiteSettings.SupportEmail != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<p style=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var21 string
|
||||||
|
templ_7745c5c3_Var21, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(fmt.Sprintf("margin: 12px 0 0; font-style: italic; color: %s;", edMutedFg(emailCtx)))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 159, Col: 105}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "\">P.S. Reservations & enquiries: ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var22 string
|
||||||
|
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(emailCtx.SiteSettings.SupportEmail)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 160, Col: 82}
|
||||||
|
}
|
||||||
|
_, 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, 29, "</p>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if emailCtx.UnsubscribeURL != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<p style=\"margin: 16px 0 0; font-size: 11px;\"><a href=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var23 templ.SafeURL
|
||||||
|
templ_7745c5c3_Var23, 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: 165, Col: 59}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "\" style=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var24 string
|
||||||
|
templ_7745c5c3_Var24, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(fmt.Sprintf("color: %s; text-decoration: none;", edMutedFg(emailCtx)))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 165, Col: 139}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "\">Unsubscribe</a></p>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "</td></tr></table></td></tr></table></body></html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
58
embed.go
Normal file
58
embed.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"io/fs"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/core/plugin"
|
||||||
|
)
|
||||||
|
|
||||||
|
//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 rooted at assets/.
|
||||||
|
func Assets() fs.FS {
|
||||||
|
sub, _ := fs.Sub(assetsFS, "assets")
|
||||||
|
return sub
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schemas returns the embedded schemas filesystem rooted at schemas/.
|
||||||
|
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 ([] in this pass).
|
||||||
|
func BundledFonts() []byte { return fontsData }
|
||||||
|
|
||||||
|
// ThemeCSSManifest exposes the Art Deco utility CSS to the host Tailwind input.
|
||||||
|
func ThemeCSSManifest() *plugin.CSSManifest {
|
||||||
|
css, err := assetsFS.ReadFile("assets/style.css")
|
||||||
|
if err != nil {
|
||||||
|
return &plugin.CSSManifest{}
|
||||||
|
}
|
||||||
|
return &plugin.CSSManifest{
|
||||||
|
InputCSSAppend: string(css),
|
||||||
|
}
|
||||||
|
}
|
||||||
1
fonts.json
Normal file
1
fonts.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
47
footer.go
Normal file
47
footer.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/core/blocks"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FooterBlockMeta defines the Art Deco footer block.
|
||||||
|
var FooterBlockMeta = blocks.BlockMeta{
|
||||||
|
Key: "footer",
|
||||||
|
Title: "Footer",
|
||||||
|
Description: "Centered Art Deco footer with gold rule, address, reservations email and optional social row.",
|
||||||
|
Source: "art-deco",
|
||||||
|
}
|
||||||
|
|
||||||
|
// FooterData is the typed view for the footer component.
|
||||||
|
type FooterData struct {
|
||||||
|
Address string
|
||||||
|
ReservationsEmail string
|
||||||
|
ShowSocial bool
|
||||||
|
MenuName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// FooterBlock renders the centered Art Deco footer.
|
||||||
|
// Content: {"address": "...", "reservationsEmail": "...", "showSocial": "true|false", "menuName": "..."}
|
||||||
|
func FooterBlock(ctx context.Context, content map[string]any) string {
|
||||||
|
// showSocial is stored as a string select ("true"/"false"); also accept bool for flexibility.
|
||||||
|
showSocial := true
|
||||||
|
if b, ok := content["showSocial"].(bool); ok {
|
||||||
|
showSocial = b
|
||||||
|
} else if s := getString(content, "showSocial"); s != "" {
|
||||||
|
showSocial = s != "false"
|
||||||
|
}
|
||||||
|
|
||||||
|
data := FooterData{
|
||||||
|
Address: getString(content, "address"),
|
||||||
|
ReservationsEmail: getString(content, "reservationsEmail"),
|
||||||
|
ShowSocial: showSocial,
|
||||||
|
MenuName: getString(content, "menuName"),
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = footerComponent(data).Render(ctx, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
59
footer.templ
Normal file
59
footer.templ
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// addressLines splits a multi-line address into individual lines for centered rendering.
|
||||||
|
func addressLines(addr string) []string {
|
||||||
|
if addr == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
lines := strings.Split(addr, "\n")
|
||||||
|
out := make([]string, 0, len(lines))
|
||||||
|
for _, line := range lines {
|
||||||
|
l := strings.TrimSpace(line)
|
||||||
|
if l != "" {
|
||||||
|
out = append(out, l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// footerComponent renders the centered Art Deco footer.
|
||||||
|
templ footerComponent(data FooterData) {
|
||||||
|
<section data-block="art-deco:footer" class="py-12 deco-grain">
|
||||||
|
<div class="max-w-3xl mx-auto px-4 text-center">
|
||||||
|
<hr class="deco-rule mb-8"/>
|
||||||
|
if len(addressLines(data.Address)) > 0 {
|
||||||
|
<address class="not-italic mb-4" style="color: hsl(var(--muted-foreground));">
|
||||||
|
for _, line := range addressLines(data.Address) {
|
||||||
|
<div>{ line }</div>
|
||||||
|
}
|
||||||
|
</address>
|
||||||
|
}
|
||||||
|
if data.ReservationsEmail != "" {
|
||||||
|
<p class="mb-4">
|
||||||
|
<a href={ templ.SafeURL("mailto:" + data.ReservationsEmail) } class="deco-link">{ data.ReservationsEmail }</a>
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
if data.MenuName != "" {
|
||||||
|
<nav
|
||||||
|
aria-label="Footer"
|
||||||
|
class="deco-caps text-xs mb-4"
|
||||||
|
style="font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--foreground));"
|
||||||
|
>
|
||||||
|
<span>{ data.MenuName }</span>
|
||||||
|
</nav>
|
||||||
|
}
|
||||||
|
if data.ShowSocial {
|
||||||
|
<div class="flex justify-center gap-4 mb-4" style="color: hsl(var(--primary));">
|
||||||
|
<span class="deco-caps text-xs" aria-hidden="true">Instagram</span>
|
||||||
|
<span aria-hidden="true">·</span>
|
||||||
|
<span class="deco-caps text-xs" aria-hidden="true">Facebook</span>
|
||||||
|
<span aria-hidden="true">·</span>
|
||||||
|
<span class="deco-caps text-xs" aria-hidden="true">LinkedIn</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<hr class="deco-rule mt-8"/>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
149
footer_templ.go
Normal file
149
footer_templ.go
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
// 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 "strings"
|
||||||
|
|
||||||
|
// addressLines splits a multi-line address into individual lines for centered rendering.
|
||||||
|
func addressLines(addr string) []string {
|
||||||
|
if addr == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
lines := strings.Split(addr, "\n")
|
||||||
|
out := make([]string, 0, len(lines))
|
||||||
|
for _, line := range lines {
|
||||||
|
l := strings.TrimSpace(line)
|
||||||
|
if l != "" {
|
||||||
|
out = append(out, l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// footerComponent renders the centered Art Deco footer.
|
||||||
|
func footerComponent(data FooterData) 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=\"art-deco:footer\" class=\"py-12 deco-grain\"><div class=\"max-w-3xl mx-auto px-4 text-center\"><hr class=\"deco-rule mb-8\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if len(addressLines(data.Address)) > 0 {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<address class=\"not-italic mb-4\" style=\"color: hsl(var(--muted-foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
for _, line := range addressLines(data.Address) {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var2 string
|
||||||
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(line)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `footer.templ`, Line: 29, Col: 17}
|
||||||
|
}
|
||||||
|
_, 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, 4, "</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</address>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if data.ReservationsEmail != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<p class=\"mb-4\"><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("mailto:" + data.ReservationsEmail))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `footer.templ`, Line: 35, Col: 64}
|
||||||
|
}
|
||||||
|
_, 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, 7, "\" class=\"deco-link\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 string
|
||||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(data.ReservationsEmail)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `footer.templ`, Line: 35, Col: 109}
|
||||||
|
}
|
||||||
|
_, 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, "</a></p>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if data.MenuName != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<nav aria-label=\"Footer\" class=\"deco-caps text-xs mb-4\" style=\"font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--foreground));\"><span>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var5 string
|
||||||
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(data.MenuName)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `footer.templ`, Line: 44, Col: 26}
|
||||||
|
}
|
||||||
|
_, 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, 10, "</span></nav>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if data.ShowSocial {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<div class=\"flex justify-center gap-4 mb-4\" style=\"color: hsl(var(--primary));\"><span class=\"deco-caps text-xs\" aria-hidden=\"true\">Instagram</span> <span aria-hidden=\"true\">·</span> <span class=\"deco-caps text-xs\" aria-hidden=\"true\">Facebook</span> <span aria-hidden=\"true\">·</span> <span class=\"deco-caps text-xs\" aria-hidden=\"true\">LinkedIn</span></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<hr class=\"deco-rule mt-8\"></div></section>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
48
gallery_fan.go
Normal file
48
gallery_fan.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/core/blocks"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GalleryFanBlockMeta defines the mirrored fan gallery block.
|
||||||
|
var GalleryFanBlockMeta = blocks.BlockMeta{
|
||||||
|
Key: "gallery_fan",
|
||||||
|
Title: "Fan Gallery",
|
||||||
|
Description: "Mirrored gallery framed by sunburst — collapses to single column under 640px.",
|
||||||
|
Source: "art-deco",
|
||||||
|
}
|
||||||
|
|
||||||
|
// GalleryImage is a single image in the gallery.
|
||||||
|
type GalleryImage struct {
|
||||||
|
Src string
|
||||||
|
Caption string
|
||||||
|
}
|
||||||
|
|
||||||
|
// GalleryFanData is the typed view for the gallery component.
|
||||||
|
type GalleryFanData struct {
|
||||||
|
Images []GalleryImage
|
||||||
|
Columns int
|
||||||
|
}
|
||||||
|
|
||||||
|
// GalleryFanBlock renders a mirrored fan gallery.
|
||||||
|
// Content: {"images": [{"src": "...", "caption": "..."}], "columns": 3}
|
||||||
|
func GalleryFanBlock(ctx context.Context, content map[string]any) string {
|
||||||
|
items := getSlice(content, "images")
|
||||||
|
images := make([]GalleryImage, 0, len(items))
|
||||||
|
for _, item := range items {
|
||||||
|
images = append(images, GalleryImage{
|
||||||
|
Src: getString(item, "src"),
|
||||||
|
Caption: getString(item, "caption"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
cols := getInt(content, "columns", 3)
|
||||||
|
cols = clampInt(cols, 2, 4)
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = galleryFanComponent(GalleryFanData{Images: images, Columns: cols}).Render(ctx, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
54
gallery_fan.templ
Normal file
54
gallery_fan.templ
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// galleryFanGridCols maps the column count to a tailwind grid class with single-column collapse below 640px.
|
||||||
|
func galleryFanGridCols(cols int) string {
|
||||||
|
switch cols {
|
||||||
|
case 2:
|
||||||
|
return "grid-cols-1 sm:grid-cols-2"
|
||||||
|
case 4:
|
||||||
|
return "grid-cols-1 sm:grid-cols-2 lg:grid-cols-4"
|
||||||
|
default:
|
||||||
|
return "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallbackImage returns a tiny transparent svg data URI used when no image src is supplied.
|
||||||
|
func fallbackImage() string {
|
||||||
|
return "data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2032%2032%22%3E%3Crect%20width%3D%2232%22%20height%3D%2232%22%20fill%3D%22%23222%22%2F%3E%3C%2Fsvg%3E"
|
||||||
|
}
|
||||||
|
|
||||||
|
// galleryFanComponent renders the mirrored gallery framed by sunburst rays.
|
||||||
|
templ galleryFanComponent(data GalleryFanData) {
|
||||||
|
<section data-block="art-deco:gallery_fan" class="py-16">
|
||||||
|
<div class="max-w-6xl mx-auto px-4">
|
||||||
|
<div class="relative deco-grain">
|
||||||
|
<div class="absolute inset-x-0 -top-2 h-12 pointer-events-none deco-sunburst" aria-hidden="true"></div>
|
||||||
|
<div class={ "grid gap-4 pt-12", galleryFanGridCols(data.Columns) }>
|
||||||
|
if len(data.Images) == 0 {
|
||||||
|
<div class="col-span-full text-center text-sm py-12" style="color: hsl(var(--muted-foreground));">
|
||||||
|
Add images to populate the fan gallery.
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
for _, img := range data.Images {
|
||||||
|
<figure class="deco-step">
|
||||||
|
if img.Src != "" {
|
||||||
|
<img src={ img.Src } alt={ img.Caption } class="block w-full h-auto object-cover" style="background-color: hsl(var(--muted));"/>
|
||||||
|
} else {
|
||||||
|
<img src={ fallbackImage() } alt="" class="block w-full h-auto" style="background-color: hsl(var(--muted));"/>
|
||||||
|
}
|
||||||
|
if img.Caption != "" {
|
||||||
|
<figcaption
|
||||||
|
class="mt-2 text-center italic text-sm"
|
||||||
|
style="color: hsl(var(--muted-foreground));"
|
||||||
|
>
|
||||||
|
{ img.Caption }
|
||||||
|
</figcaption>
|
||||||
|
}
|
||||||
|
</figure>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="absolute inset-x-0 -bottom-2 h-12 pointer-events-none deco-sunburst rotate-180" aria-hidden="true"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
169
gallery_fan_templ.go
Normal file
169
gallery_fan_templ.go
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
// 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"
|
||||||
|
|
||||||
|
// galleryFanGridCols maps the column count to a tailwind grid class with single-column collapse below 640px.
|
||||||
|
func galleryFanGridCols(cols int) string {
|
||||||
|
switch cols {
|
||||||
|
case 2:
|
||||||
|
return "grid-cols-1 sm:grid-cols-2"
|
||||||
|
case 4:
|
||||||
|
return "grid-cols-1 sm:grid-cols-2 lg:grid-cols-4"
|
||||||
|
default:
|
||||||
|
return "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallbackImage returns a tiny transparent svg data URI used when no image src is supplied.
|
||||||
|
func fallbackImage() string {
|
||||||
|
return "data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2032%2032%22%3E%3Crect%20width%3D%2232%22%20height%3D%2232%22%20fill%3D%22%23222%22%2F%3E%3C%2Fsvg%3E"
|
||||||
|
}
|
||||||
|
|
||||||
|
// galleryFanComponent renders the mirrored gallery framed by sunburst rays.
|
||||||
|
func galleryFanComponent(data GalleryFanData) 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=\"art-deco:gallery_fan\" class=\"py-16\"><div class=\"max-w-6xl mx-auto px-4\"><div class=\"relative deco-grain\"><div class=\"absolute inset-x-0 -top-2 h-12 pointer-events-none deco-sunburst\" aria-hidden=\"true\"></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var2 = []any{"grid gap-4 pt-12", galleryFanGridCols(data.Columns)}
|
||||||
|
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, 2, "<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: `gallery_fan.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, 3, "\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if len(data.Images) == 0 {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<div class=\"col-span-full text-center text-sm py-12\" style=\"color: hsl(var(--muted-foreground));\">Add images to populate the fan gallery.</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, img := range data.Images {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<figure class=\"deco-step\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if img.Src != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<img src=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 string
|
||||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(img.Src)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `gallery_fan.templ`, Line: 35, Col: 26}
|
||||||
|
}
|
||||||
|
_, 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, "\" alt=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var5 string
|
||||||
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.ResolveAttributeValue(img.Caption)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `gallery_fan.templ`, Line: 35, Col: 46}
|
||||||
|
}
|
||||||
|
_, 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, "\" class=\"block w-full h-auto object-cover\" style=\"background-color: hsl(var(--muted));\"> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<img src=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var6 string
|
||||||
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.ResolveAttributeValue(fallbackImage())
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `gallery_fan.templ`, Line: 37, Col: 34}
|
||||||
|
}
|
||||||
|
_, 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, 10, "\" alt=\"\" class=\"block w-full h-auto\" style=\"background-color: hsl(var(--muted));\"> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if img.Caption != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<figcaption class=\"mt-2 text-center italic text-sm\" style=\"color: hsl(var(--muted-foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var7 string
|
||||||
|
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(img.Caption)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `gallery_fan.templ`, Line: 44, Col: 22}
|
||||||
|
}
|
||||||
|
_, 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, 12, "</figcaption>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</figure>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</div><div class=\"absolute inset-x-0 -bottom-2 h-12 pointer-events-none deco-sunburst rotate-180\" aria-hidden=\"true\"></div></div></div></section>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
20
go.mod
Normal file
20
go.mod
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
module git.dev.alexdunmow.com/block/themes/art-deco
|
||||||
|
|
||||||
|
go 1.26.4
|
||||||
|
|
||||||
|
require (
|
||||||
|
git.dev.alexdunmow.com/block/core v0.11.1
|
||||||
|
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
|
||||||
|
)
|
||||||
42
go.sum
Normal file
42
go.sum
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
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.11.1 h1:5b3Ps9CLor2FGyxw/Qovt27AGZKR5Xi1JZGi/TfliTA=
|
||||||
|
git.dev.alexdunmow.com/block/core v0.11.1/go.mod h1:ZwzEOxRDLDfrhQGqo6hLw01/C1z/aS4Dm9ljQMl0Bg4=
|
||||||
|
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=
|
||||||
|
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/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=
|
||||||
44
heading_override.go
Normal file
44
heading_override.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// parseHeadingLevel reads "level" from a content map, defaulting to 2 and clamping to 1..6.
|
||||||
|
func parseHeadingLevel(content map[string]any) int {
|
||||||
|
if v, ok := content["level"].(float64); ok {
|
||||||
|
return clampInt(int(v), 1, 6)
|
||||||
|
}
|
||||||
|
if v, ok := content["level"].(int); ok {
|
||||||
|
return clampInt(v, 1, 6)
|
||||||
|
}
|
||||||
|
if v, ok := content["level"].(string); ok {
|
||||||
|
if i, err := strconv.Atoi(v); err == nil {
|
||||||
|
return clampInt(i, 1, 6)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArtDecoHeadingBlock overrides the built-in "heading" block when the Art Deco theme is active.
|
||||||
|
// Content: {"text": "...", "level": 1-6, "textClass": "optional-utility-classes"}
|
||||||
|
func ArtDecoHeadingBlock(ctx context.Context, content map[string]any) string {
|
||||||
|
data := HeadingData{
|
||||||
|
Text: getString(content, "text"),
|
||||||
|
Level: parseHeadingLevel(content),
|
||||||
|
ExtraCls: getString(content, "textClass"),
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = artDecoHeadingComponent(data).Render(ctx, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeadingData is the typed view for the heading override component.
|
||||||
|
type HeadingData struct {
|
||||||
|
Text string
|
||||||
|
Level int
|
||||||
|
ExtraCls string
|
||||||
|
}
|
||||||
69
heading_override.templ
Normal file
69
heading_override.templ
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// headingSizeClass returns the responsive size utility for a given heading level.
|
||||||
|
func headingSizeClass(level int) string {
|
||||||
|
switch level {
|
||||||
|
case 1:
|
||||||
|
return "text-4xl md:text-5xl"
|
||||||
|
case 2:
|
||||||
|
return "text-3xl md:text-4xl"
|
||||||
|
case 3:
|
||||||
|
return "text-2xl md:text-3xl"
|
||||||
|
case 4:
|
||||||
|
return "text-xl md:text-2xl"
|
||||||
|
case 5:
|
||||||
|
return "text-lg md:text-xl"
|
||||||
|
case 6:
|
||||||
|
return "text-base md:text-lg"
|
||||||
|
default:
|
||||||
|
return "text-3xl md:text-4xl"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// artDecoHeadingComponent renders an Art Deco heading with caps tracking and hairline gold underline.
|
||||||
|
templ artDecoHeadingComponent(data HeadingData) {
|
||||||
|
switch data.Level {
|
||||||
|
case 1:
|
||||||
|
<h1
|
||||||
|
data-block-override="art-deco:heading"
|
||||||
|
class={ "deco-caps deco-underline", headingSizeClass(1), data.ExtraCls }
|
||||||
|
style="font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--foreground));"
|
||||||
|
>{ data.Text }</h1>
|
||||||
|
case 2:
|
||||||
|
<h2
|
||||||
|
data-block-override="art-deco:heading"
|
||||||
|
class={ "deco-caps deco-underline", headingSizeClass(2), data.ExtraCls }
|
||||||
|
style="font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--foreground));"
|
||||||
|
>{ data.Text }</h2>
|
||||||
|
case 3:
|
||||||
|
<h3
|
||||||
|
data-block-override="art-deco:heading"
|
||||||
|
class={ "deco-caps deco-underline", headingSizeClass(3), data.ExtraCls }
|
||||||
|
style="font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--foreground));"
|
||||||
|
>{ data.Text }</h3>
|
||||||
|
case 4:
|
||||||
|
<h4
|
||||||
|
data-block-override="art-deco:heading"
|
||||||
|
class={ "deco-caps deco-underline", headingSizeClass(4), data.ExtraCls }
|
||||||
|
style="font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--foreground));"
|
||||||
|
>{ data.Text }</h4>
|
||||||
|
case 5:
|
||||||
|
<h5
|
||||||
|
data-block-override="art-deco:heading"
|
||||||
|
class={ "deco-caps deco-underline", headingSizeClass(5), data.ExtraCls }
|
||||||
|
style="font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--foreground));"
|
||||||
|
>{ data.Text }</h5>
|
||||||
|
case 6:
|
||||||
|
<h6
|
||||||
|
data-block-override="art-deco:heading"
|
||||||
|
class={ "deco-caps deco-underline", headingSizeClass(6), data.ExtraCls }
|
||||||
|
style="font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--foreground));"
|
||||||
|
>{ data.Text }</h6>
|
||||||
|
default:
|
||||||
|
<h2
|
||||||
|
data-block-override="art-deco:heading"
|
||||||
|
class={ "deco-caps deco-underline", headingSizeClass(2), data.ExtraCls }
|
||||||
|
style="font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--foreground));"
|
||||||
|
>{ data.Text }</h2>
|
||||||
|
}
|
||||||
|
}
|
||||||
311
heading_override_templ.go
Normal file
311
heading_override_templ.go
Normal file
@ -0,0 +1,311 @@
|
|||||||
|
// 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"
|
||||||
|
|
||||||
|
// headingSizeClass returns the responsive size utility for a given heading level.
|
||||||
|
func headingSizeClass(level int) string {
|
||||||
|
switch level {
|
||||||
|
case 1:
|
||||||
|
return "text-4xl md:text-5xl"
|
||||||
|
case 2:
|
||||||
|
return "text-3xl md:text-4xl"
|
||||||
|
case 3:
|
||||||
|
return "text-2xl md:text-3xl"
|
||||||
|
case 4:
|
||||||
|
return "text-xl md:text-2xl"
|
||||||
|
case 5:
|
||||||
|
return "text-lg md:text-xl"
|
||||||
|
case 6:
|
||||||
|
return "text-base md:text-lg"
|
||||||
|
default:
|
||||||
|
return "text-3xl md:text-4xl"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// artDecoHeadingComponent renders an Art Deco heading with caps tracking and hairline gold underline.
|
||||||
|
func artDecoHeadingComponent(data HeadingData) 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 data.Level {
|
||||||
|
case 1:
|
||||||
|
var templ_7745c5c3_Var2 = []any{"deco-caps deco-underline", headingSizeClass(1), data.ExtraCls}
|
||||||
|
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 data-block-override=\"art-deco:heading\" 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, "\" style=\"font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 string
|
||||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(data.Text)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 31, Col: 15}
|
||||||
|
}
|
||||||
|
_, 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{"deco-caps deco-underline", headingSizeClass(2), data.ExtraCls}
|
||||||
|
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 data-block-override=\"art-deco:heading\" 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, "\" style=\"font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var7 string
|
||||||
|
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(data.Text)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 37, Col: 15}
|
||||||
|
}
|
||||||
|
_, 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{"deco-caps deco-underline", headingSizeClass(3), data.ExtraCls}
|
||||||
|
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 data-block-override=\"art-deco:heading\" 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, "\" style=\"font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var10 string
|
||||||
|
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(data.Text)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 43, Col: 15}
|
||||||
|
}
|
||||||
|
_, 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{"deco-caps deco-underline", headingSizeClass(4), data.ExtraCls}
|
||||||
|
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 data-block-override=\"art-deco:heading\" 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, "\" style=\"font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var13 string
|
||||||
|
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(data.Text)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 49, Col: 15}
|
||||||
|
}
|
||||||
|
_, 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{"deco-caps deco-underline", headingSizeClass(5), data.ExtraCls}
|
||||||
|
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 data-block-override=\"art-deco:heading\" 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, "\" style=\"font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var16 string
|
||||||
|
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(data.Text)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 55, Col: 15}
|
||||||
|
}
|
||||||
|
_, 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{"deco-caps deco-underline", headingSizeClass(6), data.ExtraCls}
|
||||||
|
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 data-block-override=\"art-deco:heading\" 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, "\" style=\"font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var19 string
|
||||||
|
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(data.Text)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 61, Col: 15}
|
||||||
|
}
|
||||||
|
_, 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{"deco-caps deco-underline", headingSizeClass(2), data.ExtraCls}
|
||||||
|
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 data-block-override=\"art-deco:heading\" 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, "\" style=\"font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var22 string
|
||||||
|
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(data.Text)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 67, Col: 15}
|
||||||
|
}
|
||||||
|
_, 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
|
||||||
59
helpers.go
Normal file
59
helpers.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// getString extracts a string value from a 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 a 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSlice extracts a slice of maps from a content map.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// getStringSlice extracts a slice of strings from a content map.
|
||||||
|
func getStringSlice(content map[string]any, key string) []string {
|
||||||
|
if v, ok := content[key].([]any); ok {
|
||||||
|
result := make([]string, 0, len(v))
|
||||||
|
for _, item := range v {
|
||||||
|
if s, ok := item.(string); ok {
|
||||||
|
result = append(result, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// clampInt clamps an int into [min, max] inclusive.
|
||||||
|
func clampInt(v, min, max int) int {
|
||||||
|
if v < min {
|
||||||
|
return min
|
||||||
|
}
|
||||||
|
if v > max {
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
43
image_override.go
Normal file
43
image_override.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ImageOverrideData is the typed view for the image override component.
|
||||||
|
type ImageOverrideData struct {
|
||||||
|
Src string
|
||||||
|
Alt string
|
||||||
|
Caption string
|
||||||
|
Frame string
|
||||||
|
}
|
||||||
|
|
||||||
|
// normaliseImageFrame clamps the frame option to the supported set.
|
||||||
|
func normaliseImageFrame(v string) string {
|
||||||
|
switch v {
|
||||||
|
case "stepped", "scallop", "plain":
|
||||||
|
return v
|
||||||
|
default:
|
||||||
|
return "stepped"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArtDecoImageBlock overrides the built-in "image" block when the Art Deco theme is active.
|
||||||
|
// Content: {"src": "...", "alt": "...", "caption": "...", "frame": "stepped|scallop|plain"}
|
||||||
|
func ArtDecoImageBlock(ctx context.Context, content map[string]any) string {
|
||||||
|
data := ImageOverrideData{
|
||||||
|
Src: getString(content, "src"),
|
||||||
|
Alt: getString(content, "alt"),
|
||||||
|
Caption: getString(content, "caption"),
|
||||||
|
Frame: normaliseImageFrame(getString(content, "frame")),
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.Src == "" {
|
||||||
|
data.Src = fallbackImage()
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = artDecoImageComponent(data).Render(ctx, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
30
image_override.templ
Normal file
30
image_override.templ
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// imageFrameClass returns the utility class for an image frame option.
|
||||||
|
func imageFrameClass(frame string) string {
|
||||||
|
switch frame {
|
||||||
|
case "scallop":
|
||||||
|
return "deco-scallop"
|
||||||
|
case "plain":
|
||||||
|
return ""
|
||||||
|
default:
|
||||||
|
return "deco-step"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// artDecoImageComponent renders an image wrapped in the stepped ziggurat frame option.
|
||||||
|
templ artDecoImageComponent(data ImageOverrideData) {
|
||||||
|
<figure data-block-override="art-deco:image" class="my-8">
|
||||||
|
<div class={ imageFrameClass(data.Frame) }>
|
||||||
|
<img src={ data.Src } alt={ data.Alt } class="block w-full h-auto"/>
|
||||||
|
</div>
|
||||||
|
if data.Caption != "" {
|
||||||
|
<figcaption
|
||||||
|
class="mt-2 italic text-center text-sm"
|
||||||
|
style="color: hsl(var(--muted-foreground));"
|
||||||
|
>
|
||||||
|
{ data.Caption }
|
||||||
|
</figcaption>
|
||||||
|
}
|
||||||
|
</figure>
|
||||||
|
}
|
||||||
124
image_override_templ.go
Normal file
124
image_override_templ.go
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
// 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"
|
||||||
|
|
||||||
|
// imageFrameClass returns the utility class for an image frame option.
|
||||||
|
func imageFrameClass(frame string) string {
|
||||||
|
switch frame {
|
||||||
|
case "scallop":
|
||||||
|
return "deco-scallop"
|
||||||
|
case "plain":
|
||||||
|
return ""
|
||||||
|
default:
|
||||||
|
return "deco-step"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// artDecoImageComponent renders an image wrapped in the stepped ziggurat frame option.
|
||||||
|
func artDecoImageComponent(data ImageOverrideData) 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, "<figure data-block-override=\"art-deco:image\" class=\"my-8\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var2 = []any{imageFrameClass(data.Frame)}
|
||||||
|
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, 2, "<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: `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, 3, "\"><img src=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 string
|
||||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.Src)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `image_override.templ`, Line: 19, Col: 22}
|
||||||
|
}
|
||||||
|
_, 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, 4, "\" alt=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var5 string
|
||||||
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.Alt)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `image_override.templ`, Line: 19, Col: 39}
|
||||||
|
}
|
||||||
|
_, 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, 5, "\" class=\"block w-full h-auto\"></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if data.Caption != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<figcaption class=\"mt-2 italic text-center text-sm\" style=\"color: hsl(var(--muted-foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var6 string
|
||||||
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(data.Caption)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `image_override.templ`, Line: 26, Col: 18}
|
||||||
|
}
|
||||||
|
_, 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, 7, "</figcaption>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</figure>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
43
marquee_hero.go
Normal file
43
marquee_hero.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/core/blocks"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MarqueeHeroBlockMeta defines the marquee hero block.
|
||||||
|
var MarqueeHeroBlockMeta = blocks.BlockMeta{
|
||||||
|
Key: "marquee_hero",
|
||||||
|
Title: "Marquee Hero",
|
||||||
|
Description: "Wide hero with stepped ziggurat frame and sunburst overlay.",
|
||||||
|
Source: "art-deco",
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarqueeHeroData is the typed view for the marquee hero component.
|
||||||
|
type MarqueeHeroData struct {
|
||||||
|
Eyebrow string
|
||||||
|
Title string
|
||||||
|
Subtitle string
|
||||||
|
CTALabel string
|
||||||
|
CTAHref string
|
||||||
|
Image string
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarqueeHeroBlock renders the wide hero with stepped frame and sunburst overlay.
|
||||||
|
// Content: {"eyebrow": "...", "title": "...", "subtitle": "...", "ctaLabel": "...", "ctaHref": "...", "image": "..."}
|
||||||
|
func MarqueeHeroBlock(ctx context.Context, content map[string]any) string {
|
||||||
|
data := MarqueeHeroData{
|
||||||
|
Eyebrow: getString(content, "eyebrow"),
|
||||||
|
Title: getString(content, "title"),
|
||||||
|
Subtitle: getString(content, "subtitle"),
|
||||||
|
CTALabel: getString(content, "ctaLabel"),
|
||||||
|
CTAHref: getString(content, "ctaHref"),
|
||||||
|
Image: getString(content, "image"),
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = marqueeHeroComponent(data).Render(ctx, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
42
marquee_hero.templ
Normal file
42
marquee_hero.templ
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// marqueeHeroComponent renders the wide hero with stepped ziggurat frame and sunburst overlay.
|
||||||
|
templ marqueeHeroComponent(data MarqueeHeroData) {
|
||||||
|
<section data-block="art-deco:marquee_hero" class="relative w-full overflow-hidden deco-grain" style="background-color: hsl(var(--foreground)); color: hsl(var(--background));">
|
||||||
|
if data.Image != "" {
|
||||||
|
<div class="absolute inset-0 z-0" aria-hidden="true">
|
||||||
|
<img src={ data.Image } alt="" class="w-full h-full object-cover" style="opacity: 0.5;"/>
|
||||||
|
<div class="absolute inset-0" style="background-color: hsl(var(--foreground) / 0.55);"></div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="absolute inset-x-0 top-0 h-24 z-10 pointer-events-none deco-sunburst" aria-hidden="true"></div>
|
||||||
|
<div class="relative z-20 max-w-4xl mx-auto px-4 py-32 text-center">
|
||||||
|
<div class="deco-frame inline-block px-12 py-10" style="background-color: hsl(var(--foreground) / 0.6); border-color: hsl(var(--primary));">
|
||||||
|
if data.Eyebrow != "" {
|
||||||
|
<p class="deco-caps mb-4 text-xs" style="color: hsl(var(--primary));">{ data.Eyebrow }</p>
|
||||||
|
}
|
||||||
|
if data.Title != "" {
|
||||||
|
<h1
|
||||||
|
class="deco-caps deco-underline mb-6 text-5xl md:text-6xl lg:text-7xl"
|
||||||
|
style="font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--background));"
|
||||||
|
>
|
||||||
|
{ data.Title }
|
||||||
|
</h1>
|
||||||
|
}
|
||||||
|
if data.Subtitle != "" {
|
||||||
|
<div
|
||||||
|
class="text-lg md:text-xl mb-8 max-w-2xl mx-auto"
|
||||||
|
style="color: hsl(var(--background) / 0.9);"
|
||||||
|
>
|
||||||
|
@templ.Raw(data.Subtitle)
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
if data.CTALabel != "" && data.CTAHref != "" {
|
||||||
|
<a href={ templ.SafeURL(data.CTAHref) } class="deco-pill" style="background-color: hsl(var(--primary)); color: hsl(var(--primary-foreground)); border-color: hsl(var(--primary));">
|
||||||
|
{ data.CTALabel }
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
152
marquee_hero_templ.go
Normal file
152
marquee_hero_templ.go
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
// 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"
|
||||||
|
|
||||||
|
// marqueeHeroComponent renders the wide hero with stepped ziggurat frame and sunburst overlay.
|
||||||
|
func marqueeHeroComponent(data MarqueeHeroData) 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=\"art-deco:marquee_hero\" class=\"relative w-full overflow-hidden deco-grain\" style=\"background-color: hsl(var(--foreground)); color: hsl(var(--background));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if data.Image != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"absolute inset-0 z-0\" aria-hidden=\"true\"><img src=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var2 string
|
||||||
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.Image)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `marquee_hero.templ`, Line: 8, Col: 25}
|
||||||
|
}
|
||||||
|
_, 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, 3, "\" alt=\"\" class=\"w-full h-full object-cover\" style=\"opacity: 0.5;\"><div class=\"absolute inset-0\" style=\"background-color: hsl(var(--foreground) / 0.55);\"></div></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<div class=\"absolute inset-x-0 top-0 h-24 z-10 pointer-events-none deco-sunburst\" aria-hidden=\"true\"></div><div class=\"relative z-20 max-w-4xl mx-auto px-4 py-32 text-center\"><div class=\"deco-frame inline-block px-12 py-10\" style=\"background-color: hsl(var(--foreground) / 0.6); border-color: hsl(var(--primary));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if data.Eyebrow != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<p class=\"deco-caps mb-4 text-xs\" style=\"color: hsl(var(--primary));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var3 string
|
||||||
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.Eyebrow)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `marquee_hero.templ`, Line: 16, Col: 89}
|
||||||
|
}
|
||||||
|
_, 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, 6, "</p>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if data.Title != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<h1 class=\"deco-caps deco-underline mb-6 text-5xl md:text-6xl lg:text-7xl\" style=\"font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--background));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 string
|
||||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(data.Title)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `marquee_hero.templ`, Line: 23, Col: 18}
|
||||||
|
}
|
||||||
|
_, 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, "</h1>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if data.Subtitle != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<div class=\"text-lg md:text-xl mb-8 max-w-2xl mx-auto\" style=\"color: hsl(var(--background) / 0.9);\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(data.Subtitle).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if data.CTALabel != "" && data.CTAHref != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<a href=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var5 templ.SafeURL
|
||||||
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(data.CTAHref))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `marquee_hero.templ`, Line: 35, Col: 42}
|
||||||
|
}
|
||||||
|
_, 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, 12, "\" class=\"deco-pill\" style=\"background-color: hsl(var(--primary)); color: hsl(var(--primary-foreground)); border-color: hsl(var(--primary));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var6 string
|
||||||
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(data.CTALabel)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `marquee_hero.templ`, Line: 36, Col: 21}
|
||||||
|
}
|
||||||
|
_, 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, 13, "</a>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</div></div></section>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
52
menu.go
Normal file
52
menu.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/core/blocks"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MenuBlockMeta defines the tasting menu block.
|
||||||
|
var MenuBlockMeta = blocks.BlockMeta{
|
||||||
|
Key: "menu",
|
||||||
|
Title: "Tasting Menu",
|
||||||
|
Description: "Symmetric two-column menu card listing courses with description and price.",
|
||||||
|
Source: "art-deco",
|
||||||
|
}
|
||||||
|
|
||||||
|
// MenuCourse is a single course on the menu.
|
||||||
|
type MenuCourse struct {
|
||||||
|
Name string
|
||||||
|
Description string
|
||||||
|
Price string
|
||||||
|
}
|
||||||
|
|
||||||
|
// MenuData is the typed view for the menu component.
|
||||||
|
type MenuData struct {
|
||||||
|
Title string
|
||||||
|
Courses []MenuCourse
|
||||||
|
}
|
||||||
|
|
||||||
|
// MenuBlock renders a tasting menu card.
|
||||||
|
// Content: {"title": "...", "courses": [{"name": "...", "description": "...", "price": "..."}]}
|
||||||
|
func MenuBlock(ctx context.Context, content map[string]any) string {
|
||||||
|
items := getSlice(content, "courses")
|
||||||
|
courses := make([]MenuCourse, 0, len(items))
|
||||||
|
for _, item := range items {
|
||||||
|
courses = append(courses, MenuCourse{
|
||||||
|
Name: getString(item, "name"),
|
||||||
|
Description: getString(item, "description"),
|
||||||
|
Price: getString(item, "price"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
data := MenuData{
|
||||||
|
Title: getString(content, "title"),
|
||||||
|
Courses: courses,
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = menuComponent(data).Render(ctx, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
50
menu.templ
Normal file
50
menu.templ
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// menuComponent renders the symmetric two-column menu card.
|
||||||
|
templ menuComponent(data MenuData) {
|
||||||
|
<section data-block="art-deco:menu" class="py-16">
|
||||||
|
<div class="max-w-3xl mx-auto px-4">
|
||||||
|
<div class="deco-frame text-center" style="background-color: hsl(var(--card)); color: hsl(var(--card-foreground));">
|
||||||
|
if data.Title != "" {
|
||||||
|
<h2
|
||||||
|
class="deco-caps deco-underline mb-8 text-3xl md:text-4xl"
|
||||||
|
style="font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--foreground));"
|
||||||
|
>
|
||||||
|
{ data.Title }
|
||||||
|
</h2>
|
||||||
|
}
|
||||||
|
if len(data.Courses) == 0 {
|
||||||
|
<p class="text-muted-foreground" style="color: hsl(var(--muted-foreground));">Add courses to start composing this menu.</p>
|
||||||
|
} else {
|
||||||
|
<ul class="space-y-6 text-left">
|
||||||
|
for _, course := range data.Courses {
|
||||||
|
<li class="flex items-start justify-between gap-6 border-b" style="border-color: hsl(var(--border));">
|
||||||
|
<div class="flex-1">
|
||||||
|
<h3
|
||||||
|
class="deco-caps text-lg"
|
||||||
|
style="font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--foreground));"
|
||||||
|
>
|
||||||
|
{ course.Name }
|
||||||
|
</h3>
|
||||||
|
if course.Description != "" {
|
||||||
|
<div class="mt-1 italic" style="color: hsl(var(--muted-foreground));">
|
||||||
|
@templ.Raw(course.Description)
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
if course.Price != "" {
|
||||||
|
<span
|
||||||
|
class="deco-caps text-base"
|
||||||
|
style="font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace); color: hsl(var(--primary));"
|
||||||
|
>
|
||||||
|
{ course.Price }
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
139
menu_templ.go
Normal file
139
menu_templ.go
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
// 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"
|
||||||
|
|
||||||
|
// menuComponent renders the symmetric two-column menu card.
|
||||||
|
func menuComponent(data MenuData) 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=\"art-deco:menu\" class=\"py-16\"><div class=\"max-w-3xl mx-auto px-4\"><div class=\"deco-frame text-center\" style=\"background-color: hsl(var(--card)); color: hsl(var(--card-foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if data.Title != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<h2 class=\"deco-caps deco-underline mb-8 text-3xl md:text-4xl\" style=\"font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--foreground));\">")
|
||||||
|
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: `menu.templ`, Line: 13, Col: 18}
|
||||||
|
}
|
||||||
|
_, 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.Courses) == 0 {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<p class=\"text-muted-foreground\" style=\"color: hsl(var(--muted-foreground));\">Add courses to start composing this menu.</p>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<ul class=\"space-y-6 text-left\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
for _, course := range data.Courses {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<li class=\"flex items-start justify-between gap-6 border-b\" style=\"border-color: hsl(var(--border));\"><div class=\"flex-1\"><h3 class=\"deco-caps text-lg\" style=\"font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var3 string
|
||||||
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(course.Name)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `menu.templ`, Line: 27, Col: 23}
|
||||||
|
}
|
||||||
|
_, 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, 7, "</h3>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if course.Description != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<div class=\"mt-1 italic\" style=\"color: hsl(var(--muted-foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(course.Description).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if course.Price != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<span class=\"deco-caps text-base\" style=\"font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace); color: hsl(var(--primary));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 string
|
||||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(course.Price)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `menu.templ`, Line: 40, Col: 24}
|
||||||
|
}
|
||||||
|
_, 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, 12, "</span>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</li>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</ul>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</div></div></section>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
12
plugin.mod
Normal file
12
plugin.mod
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[plugin]
|
||||||
|
name = "art-deco"
|
||||||
|
display_name = "Art Deco"
|
||||||
|
scope = "@themes"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Luxury Art Deco theme with champagne gold gradients, jet black, ivory and geometric fan motifs for hotels, fine dining, weddings and jewellers."
|
||||||
|
kind = "theme"
|
||||||
|
categories = ["templates"]
|
||||||
|
tags = ["luxury", "gold", "hospitality", "hotel", "wedding", "jewellery", "vintage", "glam", "fine-dining"]
|
||||||
|
|
||||||
|
[compatibility]
|
||||||
|
block_core = ">=0.11.0 <0.12.0"
|
||||||
110
presets.json
Normal file
110
presets.json
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "champagne-noir",
|
||||||
|
"name": "Champagne Noir",
|
||||||
|
"description": "Jet black lifted by champagne gold — the default Art Deco palette.",
|
||||||
|
"theme": {
|
||||||
|
"mode": "both",
|
||||||
|
"lightColors": {
|
||||||
|
"background": "40 30% 96%",
|
||||||
|
"foreground": "40 20% 10%",
|
||||||
|
"card": "40 25% 98%",
|
||||||
|
"cardForeground": "40 20% 10%",
|
||||||
|
"popover": "40 25% 98%",
|
||||||
|
"popoverForeground": "40 20% 10%",
|
||||||
|
"primary": "42 55% 52%",
|
||||||
|
"primaryForeground": "40 25% 8%",
|
||||||
|
"secondary": "40 15% 90%",
|
||||||
|
"secondaryForeground": "40 20% 12%",
|
||||||
|
"muted": "40 12% 92%",
|
||||||
|
"mutedForeground": "40 12% 38%",
|
||||||
|
"accent": "36 70% 60%",
|
||||||
|
"accentForeground": "40 30% 8%",
|
||||||
|
"destructive": "0 70% 45%",
|
||||||
|
"destructiveForeground": "40 25% 96%",
|
||||||
|
"border": "42 30% 78%",
|
||||||
|
"input": "42 25% 82%",
|
||||||
|
"ring": "42 65% 55%"
|
||||||
|
},
|
||||||
|
"darkColors": {
|
||||||
|
"background": "40 8% 6%",
|
||||||
|
"foreground": "42 35% 92%",
|
||||||
|
"card": "40 10% 10%",
|
||||||
|
"cardForeground": "42 35% 92%",
|
||||||
|
"popover": "40 10% 10%",
|
||||||
|
"popoverForeground": "42 35% 92%",
|
||||||
|
"primary": "42 65% 58%",
|
||||||
|
"primaryForeground": "40 25% 8%",
|
||||||
|
"secondary": "40 12% 14%",
|
||||||
|
"secondaryForeground": "42 30% 88%",
|
||||||
|
"muted": "40 10% 12%",
|
||||||
|
"mutedForeground": "40 12% 62%",
|
||||||
|
"accent": "36 80% 62%",
|
||||||
|
"accentForeground": "40 30% 8%",
|
||||||
|
"destructive": "0 72% 52%",
|
||||||
|
"destructiveForeground": "40 25% 96%",
|
||||||
|
"border": "42 30% 22%",
|
||||||
|
"input": "42 20% 18%",
|
||||||
|
"ring": "42 65% 58%"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ivory-rose",
|
||||||
|
"name": "Ivory & Rose Gold",
|
||||||
|
"description": "Soft ivory with rose-gold flourishes for weddings and jewellers.",
|
||||||
|
"theme": {
|
||||||
|
"mode": "light",
|
||||||
|
"lightColors": {
|
||||||
|
"background": "36 40% 97%",
|
||||||
|
"foreground": "20 15% 14%",
|
||||||
|
"card": "36 35% 99%",
|
||||||
|
"cardForeground": "20 15% 14%",
|
||||||
|
"popover": "36 35% 99%",
|
||||||
|
"popoverForeground": "20 15% 14%",
|
||||||
|
"primary": "18 55% 58%",
|
||||||
|
"primaryForeground": "36 40% 97%",
|
||||||
|
"secondary": "30 35% 92%",
|
||||||
|
"secondaryForeground": "20 15% 14%",
|
||||||
|
"muted": "30 25% 94%",
|
||||||
|
"mutedForeground": "20 12% 40%",
|
||||||
|
"accent": "15 65% 65%",
|
||||||
|
"accentForeground": "20 20% 8%",
|
||||||
|
"destructive": "0 70% 45%",
|
||||||
|
"destructiveForeground": "36 40% 97%",
|
||||||
|
"border": "25 30% 82%",
|
||||||
|
"input": "25 25% 86%",
|
||||||
|
"ring": "18 55% 58%"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "velvet-onyx",
|
||||||
|
"name": "Velvet Onyx",
|
||||||
|
"description": "Deep velvet onyx with gold and rose stage-lights for theatre and after-dark dining.",
|
||||||
|
"theme": {
|
||||||
|
"mode": "dark",
|
||||||
|
"darkColors": {
|
||||||
|
"background": "260 20% 6%",
|
||||||
|
"foreground": "42 40% 92%",
|
||||||
|
"card": "260 20% 10%",
|
||||||
|
"cardForeground": "42 40% 92%",
|
||||||
|
"popover": "260 20% 10%",
|
||||||
|
"popoverForeground": "42 40% 92%",
|
||||||
|
"primary": "42 70% 60%",
|
||||||
|
"primaryForeground": "260 25% 8%",
|
||||||
|
"secondary": "260 18% 14%",
|
||||||
|
"secondaryForeground": "42 30% 88%",
|
||||||
|
"muted": "260 15% 12%",
|
||||||
|
"mutedForeground": "42 15% 60%",
|
||||||
|
"accent": "320 35% 55%",
|
||||||
|
"accentForeground": "42 40% 95%",
|
||||||
|
"destructive": "0 72% 52%",
|
||||||
|
"destructiveForeground": "42 40% 95%",
|
||||||
|
"border": "260 20% 22%",
|
||||||
|
"input": "260 18% 18%",
|
||||||
|
"ring": "42 70% 60%"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
39
press_quote.go
Normal file
39
press_quote.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/core/blocks"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PressQuoteBlockMeta defines the press quote block.
|
||||||
|
var PressQuoteBlockMeta = blocks.BlockMeta{
|
||||||
|
Key: "press_quote",
|
||||||
|
Title: "Press Quote",
|
||||||
|
Description: "Italic Cormorant pull-quote between fan dividers, optional star rating.",
|
||||||
|
Source: "art-deco",
|
||||||
|
}
|
||||||
|
|
||||||
|
// PressQuoteData is the typed view for the press quote component.
|
||||||
|
type PressQuoteData struct {
|
||||||
|
Quote string
|
||||||
|
Source string
|
||||||
|
Stars int
|
||||||
|
}
|
||||||
|
|
||||||
|
// PressQuoteBlock renders the italic pull-quote framed by fan dividers.
|
||||||
|
// Content: {"quote": "...", "source": "...", "stars": 5}
|
||||||
|
func PressQuoteBlock(ctx context.Context, content map[string]any) string {
|
||||||
|
stars := clampInt(getInt(content, "stars", 0), 0, 5)
|
||||||
|
|
||||||
|
data := PressQuoteData{
|
||||||
|
Quote: getString(content, "quote"),
|
||||||
|
Source: getString(content, "source"),
|
||||||
|
Stars: stars,
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = pressQuoteComponent(data).Render(ctx, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
39
press_quote.templ
Normal file
39
press_quote.templ
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// pressQuoteComponent renders the italic Cormorant pull-quote between fan dividers.
|
||||||
|
templ pressQuoteComponent(data PressQuoteData) {
|
||||||
|
<section data-block="art-deco:press_quote" class="py-16">
|
||||||
|
<div class="max-w-3xl mx-auto px-4 text-center">
|
||||||
|
<div class="h-6 mb-6 deco-sunburst" aria-hidden="true"></div>
|
||||||
|
<blockquote
|
||||||
|
class="italic text-2xl md:text-3xl leading-snug"
|
||||||
|
style="font-family: var(--font-body, "Cormorant Garamond", "Cormorant", Georgia, serif); font-style: italic; color: hsl(var(--foreground));"
|
||||||
|
>
|
||||||
|
if data.Quote != "" {
|
||||||
|
@templ.Raw(data.Quote)
|
||||||
|
} else {
|
||||||
|
<span style="color: hsl(var(--muted-foreground));">Add a press quote to populate this block.</span>
|
||||||
|
}
|
||||||
|
</blockquote>
|
||||||
|
if data.Stars > 0 {
|
||||||
|
<div class="mt-4 flex justify-center gap-1" aria-label="Star rating" data-stars={ starsAttr(data.Stars) }>
|
||||||
|
for i := 0; i < data.Stars; i++ {
|
||||||
|
<span data-deco-star="filled" aria-hidden="true" style="color: hsl(var(--primary));">★</span>
|
||||||
|
}
|
||||||
|
for i := data.Stars; i < 5; i++ {
|
||||||
|
<span data-deco-star="empty" aria-hidden="true" style="color: hsl(var(--muted-foreground));">☆</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
if data.Source != "" {
|
||||||
|
<p
|
||||||
|
class="mt-6 deco-caps text-xs"
|
||||||
|
style="font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--muted-foreground));"
|
||||||
|
>
|
||||||
|
{ data.Source }
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
<div class="h-6 mt-6 deco-scallop" aria-hidden="true"></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
8
press_quote_helpers.go
Normal file
8
press_quote_helpers.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
// starsAttr renders an int into a stable data-stars attribute string.
|
||||||
|
func starsAttr(n int) string {
|
||||||
|
return strconv.Itoa(n)
|
||||||
|
}
|
||||||
114
press_quote_templ.go
Normal file
114
press_quote_templ.go
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
// 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"
|
||||||
|
|
||||||
|
// pressQuoteComponent renders the italic Cormorant pull-quote between fan dividers.
|
||||||
|
func pressQuoteComponent(data PressQuoteData) 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=\"art-deco:press_quote\" class=\"py-16\"><div class=\"max-w-3xl mx-auto px-4 text-center\"><div class=\"h-6 mb-6 deco-sunburst\" aria-hidden=\"true\"></div><blockquote class=\"italic text-2xl md:text-3xl leading-snug\" style=\"font-family: var(--font-body, "Cormorant Garamond", "Cormorant", Georgia, serif); font-style: italic; color: hsl(var(--foreground));\">")
|
||||||
|
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
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<span style=\"color: hsl(var(--muted-foreground));\">Add a press quote to populate this block.</span>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</blockquote>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if data.Stars > 0 {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<div class=\"mt-4 flex justify-center gap-1\" aria-label=\"Star rating\" data-stars=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var2 string
|
||||||
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.ResolveAttributeValue(starsAttr(data.Stars))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `press_quote.templ`, Line: 19, Col: 107}
|
||||||
|
}
|
||||||
|
_, 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, 5, "\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
for i := 0; i < data.Stars; i++ {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<span data-deco-star=\"filled\" aria-hidden=\"true\" style=\"color: hsl(var(--primary));\">★</span> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := data.Stars; i < 5; i++ {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<span data-deco-star=\"empty\" aria-hidden=\"true\" style=\"color: hsl(var(--muted-foreground));\">☆</span>")
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if data.Source != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<p class=\"mt-6 deco-caps text-xs\" style=\"font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--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.Source)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `press_quote.templ`, Line: 33, Col: 18}
|
||||||
|
}
|
||||||
|
_, 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, 10, "</p>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<div class=\"h-6 mt-6 deco-scallop\" aria-hidden=\"true\"></div></div></section>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
174
register.go
Normal file
174
register.go
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
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 into a 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 wires up the Art Deco system template, its page templates, blocks,
|
||||||
|
// template overrides and email wrapper.
|
||||||
|
func Register(tr templates.TemplateRegistry, br blocks.BlockRegistry) error {
|
||||||
|
// Schemas must be loaded BEFORE any br.Register call so block schemas bind.
|
||||||
|
if err := br.LoadSchemasFromFS(Schemas()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// System template metadata.
|
||||||
|
tr.RegisterSystemTemplate(templates.SystemTemplateMeta{
|
||||||
|
Key: "art-deco",
|
||||||
|
Title: "Art Deco",
|
||||||
|
Description: "Luxury Art Deco theme with champagne gold gradients, jet black, ivory and geometric fan motifs for hotels, fine dining, weddings and jewellers.",
|
||||||
|
})
|
||||||
|
|
||||||
|
// Page templates.
|
||||||
|
if err := tr.RegisterPageTemplate("art-deco", templates.PageTemplateMeta{
|
||||||
|
Key: "default",
|
||||||
|
Title: "Default",
|
||||||
|
Description: "Centered symmetric layout with gold rule header and footer.",
|
||||||
|
Slots: []string{"header", "main", "footer"},
|
||||||
|
}, wrap(RenderArtDecoDefault)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tr.RegisterPageTemplate("art-deco", templates.PageTemplateMeta{
|
||||||
|
Key: "landing",
|
||||||
|
Title: "Marquee Landing",
|
||||||
|
Description: "Wide hero with sunburst overlay, reservation strip, CTA.",
|
||||||
|
Slots: []string{"marquee", "main", "cta", "footer"},
|
||||||
|
}, wrap(RenderArtDecoLanding)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tr.RegisterPageTemplate("art-deco", templates.PageTemplateMeta{
|
||||||
|
Key: "article",
|
||||||
|
Title: "Editorial",
|
||||||
|
Description: "Narrow editorial column for press, menus and stories.",
|
||||||
|
Slots: []string{"header", "main", "aside", "footer"},
|
||||||
|
}, wrap(RenderArtDecoArticle)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tr.RegisterPageTemplate("art-deco", templates.PageTemplateMeta{
|
||||||
|
Key: "full-width",
|
||||||
|
Title: "Grand Ballroom",
|
||||||
|
Description: "Edge-to-edge gallery layout for venues and lookbooks.",
|
||||||
|
Slots: []string{"header", "main", "footer"},
|
||||||
|
}, wrap(RenderArtDecoFullWidth)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Theme-specific blocks.
|
||||||
|
br.Register(ReservationBlockMeta, ReservationBlock)
|
||||||
|
br.Register(MenuBlockMeta, MenuBlock)
|
||||||
|
br.Register(GalleryFanBlockMeta, GalleryFanBlock)
|
||||||
|
br.Register(DividerFanBlockMeta, DividerFanBlock)
|
||||||
|
br.Register(FooterBlockMeta, FooterBlock)
|
||||||
|
br.Register(MarqueeHeroBlockMeta, MarqueeHeroBlock)
|
||||||
|
br.Register(PressQuoteBlockMeta, PressQuoteBlock)
|
||||||
|
|
||||||
|
// Template overrides — active only while the Art Deco system template is selected.
|
||||||
|
br.RegisterTemplateOverride("art-deco", "heading", ArtDecoHeadingBlock)
|
||||||
|
br.RegisterTemplateOverride("art-deco", "text", ArtDecoTextBlock)
|
||||||
|
br.RegisterTemplateOverride("art-deco", "button", ArtDecoButtonBlock)
|
||||||
|
br.RegisterTemplateOverride("art-deco", "image", ArtDecoImageBlock)
|
||||||
|
|
||||||
|
// Branded email wrapper.
|
||||||
|
tr.RegisterEmailWrapper("art-deco", ArtDecoEmailWrapper)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultMasterPages returns the master pages provisioned the first time the plugin loads.
|
||||||
|
func DefaultMasterPages() []plugin.MasterPageDefinition {
|
||||||
|
return []plugin.MasterPageDefinition{
|
||||||
|
{
|
||||||
|
Key: "art-deco:default-master",
|
||||||
|
Title: "Art Deco Default Master",
|
||||||
|
PageTemplates: []string{"default", "article"},
|
||||||
|
Blocks: []plugin.MasterPageBlock{
|
||||||
|
{
|
||||||
|
BlockKey: "navbar",
|
||||||
|
Title: "Brass Navigation",
|
||||||
|
Content: map[string]any{"menuName": "main"},
|
||||||
|
Slot: "header",
|
||||||
|
SortOrder: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
BlockKey: "art-deco:divider_fan",
|
||||||
|
Title: "Header Fan Divider",
|
||||||
|
Content: map[string]any{"variant": "sunburst"},
|
||||||
|
Slot: "header",
|
||||||
|
SortOrder: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
BlockKey: "slot",
|
||||||
|
Title: "Main Content",
|
||||||
|
Content: map[string]any{"slotName": "main", "placeholder": "Page content"},
|
||||||
|
Slot: "main",
|
||||||
|
SortOrder: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
BlockKey: "art-deco:footer",
|
||||||
|
Title: "Site Footer",
|
||||||
|
Content: map[string]any{"address": "", "reservationsEmail": "", "showSocial": "true"},
|
||||||
|
Slot: "footer",
|
||||||
|
SortOrder: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "art-deco:marquee-master",
|
||||||
|
Title: "Art Deco Marquee Master",
|
||||||
|
PageTemplates: []string{"landing", "full-width"},
|
||||||
|
Blocks: []plugin.MasterPageBlock{
|
||||||
|
{
|
||||||
|
BlockKey: "navbar",
|
||||||
|
Title: "Brass Navigation",
|
||||||
|
Content: map[string]any{"menuName": "main"},
|
||||||
|
Slot: "marquee",
|
||||||
|
SortOrder: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
BlockKey: "art-deco:reservation",
|
||||||
|
Title: "Reservation Strip",
|
||||||
|
Content: map[string]any{"phone": "", "openTable": ""},
|
||||||
|
Slot: "marquee",
|
||||||
|
SortOrder: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
BlockKey: "slot",
|
||||||
|
Title: "Main Content",
|
||||||
|
Content: map[string]any{"slotName": "main"},
|
||||||
|
Slot: "main",
|
||||||
|
SortOrder: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
BlockKey: "art-deco:divider_fan",
|
||||||
|
Title: "CTA Fan Divider",
|
||||||
|
Content: map[string]any{"variant": "scallop"},
|
||||||
|
Slot: "cta",
|
||||||
|
SortOrder: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
BlockKey: "art-deco:footer",
|
||||||
|
Title: "Site Footer",
|
||||||
|
Content: map[string]any{"showSocial": "true"},
|
||||||
|
Slot: "footer",
|
||||||
|
SortOrder: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
25
registration.go
Normal file
25
registration.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
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 Art Deco theme.
|
||||||
|
var Registration = plugin.PluginRegistration{
|
||||||
|
Name: "art-deco",
|
||||||
|
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() },
|
||||||
|
}
|
||||||
37
reservation.go
Normal file
37
reservation.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/core/blocks"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReservationBlockMeta defines the reservation strip block.
|
||||||
|
var ReservationBlockMeta = blocks.BlockMeta{
|
||||||
|
Key: "reservation",
|
||||||
|
Title: "Reservation Strip",
|
||||||
|
Description: "Sticky gold-on-black reservation strip with phone, OpenTable CTA and trading hours.",
|
||||||
|
Source: "art-deco",
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReservationData is the typed view used by the templ component.
|
||||||
|
type ReservationData struct {
|
||||||
|
Phone string
|
||||||
|
OpenTable string
|
||||||
|
Hours []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReservationBlock renders a reservation strip.
|
||||||
|
// Content: {"phone": "...", "openTable": "...", "hours": ["Tue–Sat · 6pm–late", ...]}
|
||||||
|
func ReservationBlock(ctx context.Context, content map[string]any) string {
|
||||||
|
data := ReservationData{
|
||||||
|
Phone: getString(content, "phone"),
|
||||||
|
OpenTable: getString(content, "openTable"),
|
||||||
|
Hours: getStringSlice(content, "hours"),
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = reservationComponent(data).Render(ctx, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
36
reservation.templ
Normal file
36
reservation.templ
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// reservationComponent renders the sticky reservation strip.
|
||||||
|
// Empty inputs render a shell with a "Reservations" label so the strip is never blank.
|
||||||
|
templ reservationComponent(data ReservationData) {
|
||||||
|
<div
|
||||||
|
data-block="art-deco:reservation"
|
||||||
|
class="sticky top-0 z-30 w-full deco-grain"
|
||||||
|
style="background-color: hsl(var(--foreground)); color: hsl(var(--background)); border-bottom: 1px solid hsl(var(--primary));"
|
||||||
|
>
|
||||||
|
<div class="max-w-5xl mx-auto px-4 py-2 flex flex-wrap items-center justify-between gap-2 text-sm">
|
||||||
|
<div class="flex items-center gap-3 deco-caps" style="font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--primary));">
|
||||||
|
<span>Reservations</span>
|
||||||
|
if data.Phone != "" {
|
||||||
|
<span aria-hidden="true" style="color: hsl(var(--primary) / 0.5);">·</span>
|
||||||
|
<a href={ templ.SafeURL("tel:" + data.Phone) } class="deco-link" style="border-bottom-color: hsl(var(--primary));">{ data.Phone }</a>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
if len(data.Hours) > 0 {
|
||||||
|
<div class="flex flex-wrap items-center gap-3" style="color: hsl(var(--background));">
|
||||||
|
for i, line := range data.Hours {
|
||||||
|
if i > 0 {
|
||||||
|
<span aria-hidden="true" style="color: hsl(var(--primary) / 0.5);">·</span>
|
||||||
|
}
|
||||||
|
<span>{ line }</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
if data.OpenTable != "" {
|
||||||
|
<a href={ templ.SafeURL(data.OpenTable) } class="deco-pill" style="background-color: hsl(var(--primary)); color: hsl(var(--primary-foreground)); border-color: hsl(var(--primary));">
|
||||||
|
Book a Table
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
136
reservation_templ.go
Normal file
136
reservation_templ.go
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
// 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"
|
||||||
|
|
||||||
|
// reservationComponent renders the sticky reservation strip.
|
||||||
|
// Empty inputs render a shell with a "Reservations" label so the strip is never blank.
|
||||||
|
func reservationComponent(data ReservationData) 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=\"art-deco:reservation\" class=\"sticky top-0 z-30 w-full deco-grain\" style=\"background-color: hsl(var(--foreground)); color: hsl(var(--background)); border-bottom: 1px solid hsl(var(--primary));\"><div class=\"max-w-5xl mx-auto px-4 py-2 flex flex-wrap items-center justify-between gap-2 text-sm\"><div class=\"flex items-center gap-3 deco-caps\" style=\"font-family: var(--font-heading, "Italiana", "Cinzel", Georgia, serif); color: hsl(var(--primary));\"><span>Reservations</span> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if data.Phone != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<span aria-hidden=\"true\" style=\"color: hsl(var(--primary) / 0.5);\">·</span> <a href=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var2 templ.SafeURL
|
||||||
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("tel:" + data.Phone))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `reservation.templ`, Line: 16, Col: 49}
|
||||||
|
}
|
||||||
|
_, 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, "\" class=\"deco-link\" style=\"border-bottom-color: hsl(var(--primary));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var3 string
|
||||||
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.Phone)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `reservation.templ`, Line: 16, Col: 132}
|
||||||
|
}
|
||||||
|
_, 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, 4, "</a>")
|
||||||
|
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
|
||||||
|
}
|
||||||
|
if len(data.Hours) > 0 {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<div class=\"flex flex-wrap items-center gap-3\" style=\"color: hsl(var(--background));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
for i, line := range data.Hours {
|
||||||
|
if i > 0 {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<span aria-hidden=\"true\" style=\"color: hsl(var(--primary) / 0.5);\">·</span>")
|
||||||
|
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
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 string
|
||||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(line)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `reservation.templ`, Line: 25, Col: 18}
|
||||||
|
}
|
||||||
|
_, 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, 9, "</span>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if data.OpenTable != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<a href=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var5 templ.SafeURL
|
||||||
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(data.OpenTable))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `reservation.templ`, Line: 30, Col: 43}
|
||||||
|
}
|
||||||
|
_, 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, 12, "\" class=\"deco-pill\" style=\"background-color: hsl(var(--primary)); color: hsl(var(--primary-foreground)); border-color: hsl(var(--primary));\">Book a Table</a>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</div></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
16
schemas/divider_fan.schema.json
Normal file
16
schemas/divider_fan.schema.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "Fan Divider",
|
||||||
|
"description": "Pure-CSS geometric gold divider — sunburst, scallop or ziggurat.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"variant": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Variant",
|
||||||
|
"description": "Choose the divider motif.",
|
||||||
|
"x-editor": "select",
|
||||||
|
"default": "sunburst",
|
||||||
|
"enum": ["sunburst", "scallop", "ziggurat"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
schemas/footer.schema.json
Normal file
37
schemas/footer.schema.json
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "Footer",
|
||||||
|
"description": "Centered Art Deco footer with gold rule, address, reservations email and optional social row.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"address": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Address",
|
||||||
|
"description": "Multi-line postal address.",
|
||||||
|
"x-editor": "textarea",
|
||||||
|
"default": ""
|
||||||
|
},
|
||||||
|
"reservationsEmail": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Reservations email",
|
||||||
|
"description": "Public email shown in the footer for reservations enquiries.",
|
||||||
|
"x-editor": "text",
|
||||||
|
"default": ""
|
||||||
|
},
|
||||||
|
"showSocial": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Show social row",
|
||||||
|
"description": "Whether to render the social-icon row.",
|
||||||
|
"x-editor": "select",
|
||||||
|
"default": "true",
|
||||||
|
"enum": ["true", "false"]
|
||||||
|
},
|
||||||
|
"menuName": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Footer menu",
|
||||||
|
"description": "Name of the menu to render in the footer.",
|
||||||
|
"x-editor": "menu-select",
|
||||||
|
"default": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
schemas/gallery_fan.schema.json
Normal file
38
schemas/gallery_fan.schema.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "Fan Gallery",
|
||||||
|
"description": "Mirrored gallery framed by sunburst rays; collapses to single column under 640px.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"images": {
|
||||||
|
"type": "array",
|
||||||
|
"title": "Images",
|
||||||
|
"description": "Images to mirror across the fan.",
|
||||||
|
"x-editor": "array",
|
||||||
|
"default": [],
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"src": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Image",
|
||||||
|
"x-editor": "media"
|
||||||
|
},
|
||||||
|
"caption": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Caption",
|
||||||
|
"x-editor": "text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"columns": {
|
||||||
|
"type": "integer",
|
||||||
|
"title": "Columns",
|
||||||
|
"description": "Columns on desktop (2, 3 or 4).",
|
||||||
|
"x-editor": "select",
|
||||||
|
"default": 3,
|
||||||
|
"enum": [2, 3, 4]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
47
schemas/marquee_hero.schema.json
Normal file
47
schemas/marquee_hero.schema.json
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "Marquee Hero",
|
||||||
|
"description": "Wide hero with stepped ziggurat frame and sunburst overlay.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"eyebrow": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Eyebrow",
|
||||||
|
"description": "Small caps line above the title.",
|
||||||
|
"x-editor": "text",
|
||||||
|
"default": ""
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Title",
|
||||||
|
"description": "Display headline in Italiana caps.",
|
||||||
|
"x-editor": "text",
|
||||||
|
"default": ""
|
||||||
|
},
|
||||||
|
"subtitle": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Subtitle",
|
||||||
|
"description": "Long-form supporting copy (rich text).",
|
||||||
|
"x-editor": "richtext",
|
||||||
|
"default": ""
|
||||||
|
},
|
||||||
|
"ctaLabel": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "CTA label",
|
||||||
|
"x-editor": "text",
|
||||||
|
"default": ""
|
||||||
|
},
|
||||||
|
"ctaHref": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "CTA URL",
|
||||||
|
"x-editor": "link",
|
||||||
|
"default": ""
|
||||||
|
},
|
||||||
|
"image": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Background image",
|
||||||
|
"x-editor": "media",
|
||||||
|
"default": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
45
schemas/menu.schema.json
Normal file
45
schemas/menu.schema.json
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "Tasting Menu",
|
||||||
|
"description": "Symmetric two-column menu card listing courses with description and price.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"title": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Menu title",
|
||||||
|
"description": "e.g. 'Chef's Tasting' or 'Cellar List'.",
|
||||||
|
"x-editor": "text",
|
||||||
|
"default": ""
|
||||||
|
},
|
||||||
|
"courses": {
|
||||||
|
"type": "array",
|
||||||
|
"title": "Courses",
|
||||||
|
"description": "Ordered list of courses on the menu.",
|
||||||
|
"x-editor": "collection",
|
||||||
|
"default": [],
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Course name",
|
||||||
|
"x-editor": "text"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Description",
|
||||||
|
"description": "Tasting note for the course.",
|
||||||
|
"x-editor": "richtext"
|
||||||
|
},
|
||||||
|
"price": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Price",
|
||||||
|
"description": "Plain string — e.g. '$48' or 'Market'.",
|
||||||
|
"x-editor": "text"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["name"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
schemas/press_quote.schema.json
Normal file
31
schemas/press_quote.schema.json
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "Press Quote",
|
||||||
|
"description": "Italic Cormorant pull-quote between fan dividers.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"quote": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Quote",
|
||||||
|
"description": "The press quotation (rich text).",
|
||||||
|
"x-editor": "richtext",
|
||||||
|
"default": ""
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Source",
|
||||||
|
"description": "Attribution line — e.g. 'The Daily, March 2024'.",
|
||||||
|
"x-editor": "text",
|
||||||
|
"default": ""
|
||||||
|
},
|
||||||
|
"stars": {
|
||||||
|
"type": "integer",
|
||||||
|
"title": "Stars",
|
||||||
|
"description": "Filled-star count, clamped to 0–5.",
|
||||||
|
"x-editor": "number",
|
||||||
|
"default": 0,
|
||||||
|
"minimum": 0,
|
||||||
|
"maximum": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
33
schemas/reservation.schema.json
Normal file
33
schemas/reservation.schema.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "Reservation Strip",
|
||||||
|
"description": "Sticky gold-on-black reservation strip with phone, OpenTable CTA, and trading hours.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"phone": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Reservations phone",
|
||||||
|
"description": "International-format phone number for the reservations desk.",
|
||||||
|
"x-editor": "text",
|
||||||
|
"default": ""
|
||||||
|
},
|
||||||
|
"openTable": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "OpenTable URL",
|
||||||
|
"description": "Deep link to the OpenTable widget or booking page.",
|
||||||
|
"x-editor": "link",
|
||||||
|
"default": ""
|
||||||
|
},
|
||||||
|
"hours": {
|
||||||
|
"type": "array",
|
||||||
|
"title": "Trading hours",
|
||||||
|
"description": "Free-form lines such as 'Tue–Sat · 6pm–late'.",
|
||||||
|
"x-editor": "array",
|
||||||
|
"default": [],
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"x-editor": "text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
270
template.templ
Normal file
270
template.templ
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/core/templates/bn"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PageData captures the inputs needed to render an Art Deco page template.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// parsePageData reads a page-render doc into the typed PageData used by templ.
|
||||||
|
func parsePageData(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 := "dark"
|
||||||
|
if tm, ok := doc["theme_mode"].(string); ok && tm != "" {
|
||||||
|
themeMode = tm
|
||||||
|
}
|
||||||
|
|
||||||
|
return PageData{
|
||||||
|
Title: title,
|
||||||
|
Slots: slots,
|
||||||
|
ThemeMode: themeMode,
|
||||||
|
ThemeCSS: themeCSS,
|
||||||
|
SiteSettings: bn.ParseSiteSettings(doc),
|
||||||
|
PageMeta: bn.ParsePageMeta(doc),
|
||||||
|
StructuredData: structuredData,
|
||||||
|
CSSHash: cssHash,
|
||||||
|
PageviewNonce: pageviewNonce,
|
||||||
|
EngagementConfig: bn.ParseEngagementConfig(doc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// htmlClassForMode returns the html-element class hint that flips dark mode.
|
||||||
|
func htmlClassForMode(mode string) string {
|
||||||
|
if mode == "dark" {
|
||||||
|
return "dark"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArtDecoDefault — centered symmetric layout with gold rule header and footer.
|
||||||
|
templ ArtDecoDefault(data PageData) {
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" class={ htmlClassForMode(data.ThemeMode) }>
|
||||||
|
@bn.Head(bn.HeadData{
|
||||||
|
Title: data.Title,
|
||||||
|
Settings: data.SiteSettings,
|
||||||
|
PageMeta: data.PageMeta,
|
||||||
|
ThemeMode: data.ThemeMode,
|
||||||
|
ThemeCSS: data.ThemeCSS,
|
||||||
|
PluginStyles: []string{"/templates/art-deco/style.css"},
|
||||||
|
StructuredData: data.StructuredData,
|
||||||
|
CSSHash: data.CSSHash,
|
||||||
|
PageviewNonce: data.PageviewNonce,
|
||||||
|
EngagementConfig: data.EngagementConfig,
|
||||||
|
})
|
||||||
|
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col" style="font-family: var(--font-body, "Cormorant Garamond", "Cormorant", Georgia, serif);">
|
||||||
|
@bn.AdminBypassBanner(data.SiteSettings)
|
||||||
|
<header class="w-full">
|
||||||
|
<div class="max-w-5xl mx-auto px-4">
|
||||||
|
@templ.Raw(data.Slots["header"])
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<main class="flex-grow max-w-3xl mx-auto w-full px-4 py-12 text-center">
|
||||||
|
if main, ok := data.Slots["main"]; ok && main != "" {
|
||||||
|
@templ.Raw(main)
|
||||||
|
} else {
|
||||||
|
<div class="py-20">
|
||||||
|
<p class="text-muted-foreground">No content blocks assigned to this page.</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</main>
|
||||||
|
<footer class="w-full mt-auto">
|
||||||
|
<div class="max-w-5xl mx-auto px-4">
|
||||||
|
@templ.Raw(data.Slots["footer"])
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
@bn.BodyEnd(data.SiteSettings)
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArtDecoLanding — Marquee Landing: wide hero, reservation strip, CTA.
|
||||||
|
templ ArtDecoLanding(data PageData) {
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" class={ htmlClassForMode(data.ThemeMode) }>
|
||||||
|
@bn.Head(bn.HeadData{
|
||||||
|
Title: data.Title,
|
||||||
|
Settings: data.SiteSettings,
|
||||||
|
PageMeta: data.PageMeta,
|
||||||
|
ThemeMode: data.ThemeMode,
|
||||||
|
ThemeCSS: data.ThemeCSS,
|
||||||
|
PluginStyles: []string{"/templates/art-deco/style.css"},
|
||||||
|
StructuredData: data.StructuredData,
|
||||||
|
CSSHash: data.CSSHash,
|
||||||
|
PageviewNonce: data.PageviewNonce,
|
||||||
|
EngagementConfig: data.EngagementConfig,
|
||||||
|
})
|
||||||
|
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col" style="font-family: var(--font-body, "Cormorant Garamond", "Cormorant", Georgia, serif);">
|
||||||
|
@bn.AdminBypassBanner(data.SiteSettings)
|
||||||
|
<section class="w-full">
|
||||||
|
@templ.Raw(data.Slots["marquee"])
|
||||||
|
</section>
|
||||||
|
<main class="flex-grow">
|
||||||
|
if main, ok := data.Slots["main"]; ok && main != "" {
|
||||||
|
<div class="max-w-5xl mx-auto px-4 py-16 text-center">
|
||||||
|
@templ.Raw(main)
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</main>
|
||||||
|
<section class="w-full">
|
||||||
|
@templ.Raw(data.Slots["cta"])
|
||||||
|
</section>
|
||||||
|
<footer class="w-full mt-auto">
|
||||||
|
<div class="max-w-5xl mx-auto px-4">
|
||||||
|
@templ.Raw(data.Slots["footer"])
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
@bn.BodyEnd(data.SiteSettings)
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArtDecoArticle — narrow editorial column for press, menus and stories.
|
||||||
|
templ ArtDecoArticle(data PageData) {
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" class={ htmlClassForMode(data.ThemeMode) }>
|
||||||
|
@bn.Head(bn.HeadData{
|
||||||
|
Title: data.Title,
|
||||||
|
Settings: data.SiteSettings,
|
||||||
|
PageMeta: data.PageMeta,
|
||||||
|
ThemeMode: data.ThemeMode,
|
||||||
|
ThemeCSS: data.ThemeCSS,
|
||||||
|
PluginStyles: []string{"/templates/art-deco/style.css"},
|
||||||
|
StructuredData: data.StructuredData,
|
||||||
|
CSSHash: data.CSSHash,
|
||||||
|
PageviewNonce: data.PageviewNonce,
|
||||||
|
EngagementConfig: data.EngagementConfig,
|
||||||
|
})
|
||||||
|
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col" style="font-family: var(--font-body, "Cormorant Garamond", "Cormorant", Georgia, serif);">
|
||||||
|
@bn.AdminBypassBanner(data.SiteSettings)
|
||||||
|
<header class="w-full">
|
||||||
|
<div class="max-w-3xl mx-auto px-4">
|
||||||
|
@templ.Raw(data.Slots["header"])
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div class="flex-grow grid gap-12 max-w-5xl mx-auto w-full px-4 py-12 md:grid-cols-[2fr_1fr]">
|
||||||
|
<main class="prose max-w-none">
|
||||||
|
if main, ok := data.Slots["main"]; ok && main != "" {
|
||||||
|
<article class="deco-dropcap" data-deco-dropcap="true">
|
||||||
|
@templ.Raw(main)
|
||||||
|
</article>
|
||||||
|
} else {
|
||||||
|
<div class="py-20 text-center">
|
||||||
|
<p class="text-muted-foreground">No content blocks assigned to this page.</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</main>
|
||||||
|
<aside class="w-full">
|
||||||
|
@templ.Raw(data.Slots["aside"])
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
<footer class="w-full mt-auto">
|
||||||
|
<div class="max-w-5xl mx-auto px-4">
|
||||||
|
@templ.Raw(data.Slots["footer"])
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
@bn.BodyEnd(data.SiteSettings)
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArtDecoFullWidth — Grand Ballroom: edge-to-edge gallery layout.
|
||||||
|
templ ArtDecoFullWidth(data PageData) {
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" class={ htmlClassForMode(data.ThemeMode) }>
|
||||||
|
@bn.Head(bn.HeadData{
|
||||||
|
Title: data.Title,
|
||||||
|
Settings: data.SiteSettings,
|
||||||
|
PageMeta: data.PageMeta,
|
||||||
|
ThemeMode: data.ThemeMode,
|
||||||
|
ThemeCSS: data.ThemeCSS,
|
||||||
|
PluginStyles: []string{"/templates/art-deco/style.css"},
|
||||||
|
StructuredData: data.StructuredData,
|
||||||
|
CSSHash: data.CSSHash,
|
||||||
|
PageviewNonce: data.PageviewNonce,
|
||||||
|
EngagementConfig: data.EngagementConfig,
|
||||||
|
})
|
||||||
|
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col" style="font-family: var(--font-body, "Cormorant Garamond", "Cormorant", Georgia, serif);">
|
||||||
|
@bn.AdminBypassBanner(data.SiteSettings)
|
||||||
|
<header class="w-full">
|
||||||
|
@templ.Raw(data.Slots["header"])
|
||||||
|
</header>
|
||||||
|
<main class="flex-grow w-full">
|
||||||
|
if main, ok := data.Slots["main"]; ok && main != "" {
|
||||||
|
@templ.Raw(main)
|
||||||
|
} else {
|
||||||
|
<div class="py-20 text-center">
|
||||||
|
<p class="text-muted-foreground">No content blocks assigned to this page.</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</main>
|
||||||
|
<footer class="w-full mt-auto">
|
||||||
|
<div class="max-w-5xl mx-auto px-4">
|
||||||
|
@templ.Raw(data.Slots["footer"])
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
@bn.BodyEnd(data.SiteSettings)
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Page-render funcs adapt typed PageData into templ.Component values.
|
||||||
|
|
||||||
|
func RenderArtDecoDefault(ctx context.Context, doc map[string]any) templ.Component {
|
||||||
|
return ArtDecoDefault(parsePageData(doc))
|
||||||
|
}
|
||||||
|
|
||||||
|
func RenderArtDecoLanding(ctx context.Context, doc map[string]any) templ.Component {
|
||||||
|
return ArtDecoLanding(parsePageData(doc))
|
||||||
|
}
|
||||||
|
|
||||||
|
func RenderArtDecoArticle(ctx context.Context, doc map[string]any) templ.Component {
|
||||||
|
return ArtDecoArticle(parsePageData(doc))
|
||||||
|
}
|
||||||
|
|
||||||
|
func RenderArtDecoFullWidth(ctx context.Context, doc map[string]any) templ.Component {
|
||||||
|
return ArtDecoFullWidth(parsePageData(doc))
|
||||||
|
}
|
||||||
607
template_templ.go
Normal file
607
template_templ.go
Normal file
@ -0,0 +1,607 @@
|
|||||||
|
// 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 captures the inputs needed to render an Art Deco page template.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// parsePageData reads a page-render doc into the typed PageData used by templ.
|
||||||
|
func parsePageData(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 := "dark"
|
||||||
|
if tm, ok := doc["theme_mode"].(string); ok && tm != "" {
|
||||||
|
themeMode = tm
|
||||||
|
}
|
||||||
|
|
||||||
|
return PageData{
|
||||||
|
Title: title,
|
||||||
|
Slots: slots,
|
||||||
|
ThemeMode: themeMode,
|
||||||
|
ThemeCSS: themeCSS,
|
||||||
|
SiteSettings: bn.ParseSiteSettings(doc),
|
||||||
|
PageMeta: bn.ParsePageMeta(doc),
|
||||||
|
StructuredData: structuredData,
|
||||||
|
CSSHash: cssHash,
|
||||||
|
PageviewNonce: pageviewNonce,
|
||||||
|
EngagementConfig: bn.ParseEngagementConfig(doc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// htmlClassForMode returns the html-element class hint that flips dark mode.
|
||||||
|
func htmlClassForMode(mode string) string {
|
||||||
|
if mode == "dark" {
|
||||||
|
return "dark"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArtDecoDefault — centered symmetric layout with gold rule header and footer.
|
||||||
|
func ArtDecoDefault(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 = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var2 = []any{htmlClassForMode(data.ThemeMode)}
|
||||||
|
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, 2, "<html lang=\"en\" 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: `template.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, 3, "\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
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/art-deco/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
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<body class=\"bg-background text-foreground antialiased min-h-screen flex flex-col\" style=\"font-family: var(--font-body, "Cormorant Garamond", "Cormorant", Georgia, serif);\">")
|
||||||
|
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, 5, "<header class=\"w-full\"><div class=\"max-w-5xl mx-auto px-4\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(data.Slots["header"]).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</div></header><main class=\"flex-grow max-w-3xl mx-auto w-full px-4 py-12 text-center\">")
|
||||||
|
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, 7, "<div class=\"py-20\"><p class=\"text-muted-foreground\">No content blocks assigned to this page.</p></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</main><footer class=\"w-full mt-auto\"><div class=\"max-w-5xl mx-auto px-4\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(data.Slots["footer"]).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</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, 10, "</body></html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArtDecoLanding — Marquee Landing: wide hero, reservation strip, CTA.
|
||||||
|
func ArtDecoLanding(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, 11, "<!doctype html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var5 = []any{htmlClassForMode(data.ThemeMode)}
|
||||||
|
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, 12, "<html lang=\"en\" 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: `template.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, 13, "\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
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/art-deco/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
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<body class=\"bg-background text-foreground antialiased min-h-screen flex flex-col\" style=\"font-family: var(--font-body, "Cormorant Garamond", "Cormorant", Georgia, serif);\">")
|
||||||
|
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, 15, "<section class=\"w-full\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(data.Slots["marquee"]).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</section><main class=\"flex-grow\">")
|
||||||
|
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, 17, "<div class=\"max-w-5xl mx-auto px-4 py-16 text-center\">")
|
||||||
|
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, 18, "</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</main><section class=\"w-full\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(data.Slots["cta"]).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</section><footer class=\"w-full mt-auto\"><div class=\"max-w-5xl mx-auto px-4\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(data.Slots["footer"]).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "</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, 22, "</body></html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArtDecoArticle — narrow editorial column for press, menus and stories.
|
||||||
|
func ArtDecoArticle(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_Var7 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var7 == nil {
|
||||||
|
templ_7745c5c3_Var7 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<!doctype html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var8 = []any{htmlClassForMode(data.ThemeMode)}
|
||||||
|
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, 24, "<html lang=\"en\" 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: `template.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, 25, "\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
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/art-deco/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
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<body class=\"bg-background text-foreground antialiased min-h-screen flex flex-col\" style=\"font-family: var(--font-body, "Cormorant Garamond", "Cormorant", Georgia, serif);\">")
|
||||||
|
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, 27, "<header class=\"w-full\"><div class=\"max-w-3xl mx-auto px-4\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(data.Slots["header"]).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</div></header><div class=\"flex-grow grid gap-12 max-w-5xl mx-auto w-full px-4 py-12 md:grid-cols-[2fr_1fr]\"><main class=\"prose max-w-none\">")
|
||||||
|
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, 29, "<article class=\"deco-dropcap\" data-deco-dropcap=\"true\">")
|
||||||
|
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, 30, "</article>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<div class=\"py-20 text-center\"><p class=\"text-muted-foreground\">No content blocks assigned to this page.</p></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "</main><aside class=\"w-full\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(data.Slots["aside"]).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "</aside></div><footer class=\"w-full mt-auto\"><div class=\"max-w-5xl mx-auto px-4\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(data.Slots["footer"]).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</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, 35, "</body></html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArtDecoFullWidth — Grand Ballroom: edge-to-edge gallery layout.
|
||||||
|
func ArtDecoFullWidth(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_Var10 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var10 == nil {
|
||||||
|
templ_7745c5c3_Var10 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "<!doctype html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var11 = []any{htmlClassForMode(data.ThemeMode)}
|
||||||
|
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, 37, "<html lang=\"en\" 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: `template.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, 38, "\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
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/art-deco/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
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "<body class=\"bg-background text-foreground antialiased min-h-screen flex flex-col\" style=\"font-family: var(--font-body, "Cormorant Garamond", "Cormorant", Georgia, serif);\">")
|
||||||
|
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, 40, "<header class=\"w-full\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(data.Slots["header"]).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</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, 42, "<div class=\"py-20 text-center\"><p class=\"text-muted-foreground\">No content blocks assigned to this page.</p></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</main><footer class=\"w-full mt-auto\"><div class=\"max-w-5xl mx-auto px-4\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(data.Slots["footer"]).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</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, 45, "</body></html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Page-render funcs adapt typed PageData into templ.Component values.
|
||||||
|
|
||||||
|
func RenderArtDecoDefault(ctx context.Context, doc map[string]any) templ.Component {
|
||||||
|
return ArtDecoDefault(parsePageData(doc))
|
||||||
|
}
|
||||||
|
|
||||||
|
func RenderArtDecoLanding(ctx context.Context, doc map[string]any) templ.Component {
|
||||||
|
return ArtDecoLanding(parsePageData(doc))
|
||||||
|
}
|
||||||
|
|
||||||
|
func RenderArtDecoArticle(ctx context.Context, doc map[string]any) templ.Component {
|
||||||
|
return ArtDecoArticle(parsePageData(doc))
|
||||||
|
}
|
||||||
|
|
||||||
|
func RenderArtDecoFullWidth(ctx context.Context, doc map[string]any) templ.Component {
|
||||||
|
return ArtDecoFullWidth(parsePageData(doc))
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
26
text_override.go
Normal file
26
text_override.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ArtDecoTextBlock overrides the built-in "text" block when the Art Deco theme is active.
|
||||||
|
// Renders prose in Cormorant body with a classical italic drop-cap on the first paragraph.
|
||||||
|
// Content: {"text": "<rich HTML>", "class": "optional-extra-class"}
|
||||||
|
func ArtDecoTextBlock(ctx context.Context, content map[string]any) string {
|
||||||
|
data := TextOverrideData{
|
||||||
|
Text: getString(content, "text"),
|
||||||
|
Class: getString(content, "class"),
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = artDecoTextComponent(data).Render(ctx, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TextOverrideData is the typed view for the text override component.
|
||||||
|
type TextOverrideData struct {
|
||||||
|
Text string
|
||||||
|
Class string
|
||||||
|
}
|
||||||
13
text_override.templ
Normal file
13
text_override.templ
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// artDecoTextComponent renders rich text body copy with a classical italic dropcap.
|
||||||
|
templ artDecoTextComponent(data TextOverrideData) {
|
||||||
|
<div
|
||||||
|
data-block-override="art-deco:text"
|
||||||
|
data-deco-dropcap="true"
|
||||||
|
class={ "deco-dropcap prose max-w-none", data.Class }
|
||||||
|
style="font-family: var(--font-body, "Cormorant Garamond", "Cormorant", Georgia, serif); color: hsl(var(--foreground));"
|
||||||
|
>
|
||||||
|
@templ.Raw(data.Text)
|
||||||
|
</div>
|
||||||
|
}
|
||||||
67
text_override_templ.go
Normal file
67
text_override_templ.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// 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"
|
||||||
|
|
||||||
|
// artDecoTextComponent renders rich text body copy with a classical italic dropcap.
|
||||||
|
func artDecoTextComponent(data TextOverrideData) 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{"deco-dropcap prose max-w-none", data.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 data-block-override=\"art-deco:text\" data-deco-dropcap=\"true\" 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, "\" style=\"font-family: var(--font-body, "Cormorant Garamond", "Cormorant", Georgia, serif); color: hsl(var(--foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(data.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