feat: cta core element — full-width tinted call-to-action strip #101

Merged
dominik.polakovics merged 2 commits from afk/91 into main 2026-07-18 03:28:07 +02:00

Closes #91

What

A new cta core element: a full-width tinted call-to-action strip with centered content, distinct from hero (which remains the page lead).

  • Sidecar scaffold/editor/elements/cta.yaml — exactly as specified in the issue (rte body with allow: [bold, italic, link], allowedInline: [button], allowedIn: [page], optional heading text param), plus the conventional icon: (lucide:megaphone, verified present in the web app's lucide set).
  • Shortcode scaffold/themes/deckle/layouts/shortcodes/cta.html<section class="ce-cta"> with ce/attrs; the optional heading renders as an h3 (a step below hero's h2, per the issue), guarded by with so an empty/absent heading emits no heading tag at all.
  • Theme CSS — viewport-spanning tinted strip (--color-accent-tint, same AA-passing pairing hero uses) via breakout margins from inside .container, centered content in a --measure column. Two hardening rules the strip forced: a contained-strip fallback inside multi-column layout slots (.ce-layout__cols .ce-cta — the two-col main/sidebar cells have no elements restriction, and a viewport bleed there would smear across the neighboring column) and overflow-x: clip on body (classic scrollbars make 100vw overshoot by the bar width, which would break the theme's "no horizontal overflow" promise).
  • Theme README — element-table row, --color-accent-tint note (hero/cta background), heading-hierarchy paragraph extended for the h3.
  • Fixtures + test — kitchen-sink gains a headed cta with a primary button chip and a button-only (no heading) cta; new TestCtaRenderingInProduction pins the rendered contract on a real Hugo build (2 strips, exactly one heading tag, both button chips).

scaffold/site/AGENTS.md is untouched on purpose: cta introduces no new field type, body policy, or rte token, and the manual documents the schema without enumerating elements (the agentsdoc drift test passes).

Acceptance criteria — verified

  • Sidecar as specified; shortcode renders heading + rte body with button chips
  • Full-width tinted strip, centered content, AA contrast (--color-text on --color-accent-tint ≈ 13.7:1)
  • Appears in the insert picker's content group; server rejects placement inside a container (422: element "cta" is not allowed inside "accordion")
  • Empty-heading cta emits no heading tag (both for an omitted param and a cleared one — CoerceValues drops the empty text param on save)
  • Full editor round-trip proven against the real harness binary: GET /api/elements lists cta → insert at page level → save heading + body with {{< button >}} chip → canonical shortcode on disk → publish (real Hugo pre-flight build) → published HTML shows the strip with the button, and no <h3> in the headless variant

Tests

web vitest: 40 files / 792 tests passed. go test ./...: all packages ok (includes sidecar↔template parity, attrs, content round-trip, production hygiene/anchors, AGENTS.md drift, and the new cta rendering test), re-run uncached after the final CSS change.

🤖 Generated with Claude Code

Closes #91 ## What A new `cta` core element: a full-width tinted call-to-action strip with centered content, distinct from `hero` (which remains the page lead). - **Sidecar** `scaffold/editor/elements/cta.yaml` — exactly as specified in the issue (rte body with `allow: [bold, italic, link]`, `allowedInline: [button]`, `allowedIn: [page]`, optional `heading` text param), plus the conventional `icon:` (`lucide:megaphone`, verified present in the web app's lucide set). - **Shortcode** `scaffold/themes/deckle/layouts/shortcodes/cta.html` — `<section class="ce-cta">` with `ce/attrs`; the optional heading renders as an **h3** (a step below hero's h2, per the issue), guarded by `with` so an empty/absent heading emits **no heading tag at all**. - **Theme CSS** — viewport-spanning tinted strip (`--color-accent-tint`, same AA-passing pairing hero uses) via breakout margins from inside `.container`, centered content in a `--measure` column. Two hardening rules the strip forced: a contained-strip fallback inside multi-column layout slots (`.ce-layout__cols .ce-cta` — the two-col main/sidebar cells have no `elements` restriction, and a viewport bleed there would smear across the neighboring column) and `overflow-x: clip` on `body` (classic scrollbars make 100vw overshoot by the bar width, which would break the theme's "no horizontal overflow" promise). - **Theme README** — element-table row, `--color-accent-tint` note (`hero/cta background`), heading-hierarchy paragraph extended for the h3. - **Fixtures + test** — kitchen-sink gains a headed cta with a primary button chip and a button-only (no heading) cta; new `TestCtaRenderingInProduction` pins the rendered contract on a real Hugo build (2 strips, exactly one heading tag, both button chips). `scaffold/site/AGENTS.md` is untouched on purpose: cta introduces no new field type, body policy, or rte token, and the manual documents the schema without enumerating elements (the `agentsdoc` drift test passes). ## Acceptance criteria — verified - [x] Sidecar as specified; shortcode renders heading + rte body with button chips - [x] Full-width tinted strip, centered content, AA contrast (`--color-text` on `--color-accent-tint` ≈ 13.7:1) - [x] Appears in the insert picker's content group; server rejects placement inside a container (`422: element "cta" is not allowed inside "accordion"`) - [x] Empty-heading cta emits no heading tag (both for an omitted param and a cleared one — `CoerceValues` drops the empty text param on save) - [x] Full editor round-trip proven against the real `harness` binary: `GET /api/elements` lists cta → insert at page level → save heading + body with `{{< button >}}` chip → canonical shortcode on disk → publish (real Hugo pre-flight build) → published HTML shows the strip with the button, and no `<h3>` in the headless variant ## Tests `web` vitest: 40 files / 792 tests passed. `go test ./...`: all packages ok (includes sidecar↔template parity, attrs, content round-trip, production hygiene/anchors, AGENTS.md drift, and the new cta rendering test), re-run uncached after the final CSS change. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat: cta core element — full-width tinted call-to-action strip
All checks were successful
ci / build-and-test (pull_request) Successful in 9m15s
1e43561534
A new page-level content element, distinct from hero (which stays the
page lead): sidecar with a restricted rte body (bold/italic/link,
button chips) and an optional heading that renders as an h3 — or not
at all when empty.

The deckle theme renders it as a viewport-spanning tinted strip via
breakout margins, with a contained fallback inside multi-column layout
slots (the bleed would cross the neighboring column) and an
overflow-x clip on body guarding the classic-scrollbar 100vw
overshoot. Theme README gains the element-table row, tint-token note,
and heading-hierarchy update; the kitchen-sink fixture carries a
headed and a button-only cta, pinned by a production rendering test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Merge remote-tracking branch 'origin/main' into afk/91
All checks were successful
ci / build-and-test (pull_request) Successful in 8m56s
4186fad13c
# Conflicts:
#	internal/theme/theme_test.go
#	scaffold/themes/deckle/README.md
Author
Owner

This was generated by AI while landing a PR.

Landing review — verdict: PASS (merge conflict resolved).

  • CI: ci / build-and-test green (run #82, 9m15s) on the PR head — predates main advancing, so the resolution below was re-verified locally.
  • AFK contract: Closes #91 present.
  • Convention: Conventional Commits title ✓; AGENTS.md correctly untouched (no new field type / body policy / rte token; drift test passes).
  • Diff vs issue #91: sidecar matches spec (rte allow: [bold, italic, link], allowedInline: [button], allowedIn: [page], optional heading); shortcode renders a tinted <section class="ce-cta"> with an optional h3 heading (empty heading emits no tag) and a button-chip rte body. Pinned by TestCtaRenderingInProduction.

Merge conflict (main advanced via the #99 + #100 merges):

  1. internal/theme/theme_test.goTestCtaRenderingInProduction (this PR) and TestCardsInProduction (from #99, now on main) tangled on identical opening boilerplate. Resolved as a union: both test functions kept in full, gofmt-clean.
  2. scaffold/themes/deckle/README.md — kept this PR's new CTA-h3 heading-hierarchy sentence and used main's current icon prose (built-in stroke SVGs), dropping this PR's now-stale "neutral placeholder glyph" wording (superseded by #100).

Re-verification on the resolved tree (merge commit 4186fad, pushed to afk/91): go build ./... + go vet clean; full go test ./internal/theme/ passes, including the real-Hugo build of the merged kitchen-sink fixture rendering CTA, Cards, and icons together.

No blockers. Awaiting the maintainer's free-text go-ahead to merge.

> *This was generated by AI while landing a PR.* **Landing review — verdict: PASS (merge conflict resolved).** - **CI**: `ci / build-and-test` green (run #82, 9m15s) on the PR head — predates `main` advancing, so the resolution below was re-verified locally. - **AFK contract**: `Closes #91` present. - **Convention**: Conventional Commits title ✓; AGENTS.md correctly untouched (no new field type / body policy / rte token; drift test passes). - **Diff vs issue #91**: sidecar matches spec (rte `allow: [bold, italic, link]`, `allowedInline: [button]`, `allowedIn: [page]`, optional heading); shortcode renders a tinted `<section class="ce-cta">` with an optional **h3** heading (empty heading emits no tag) and a button-chip rte body. Pinned by `TestCtaRenderingInProduction`. **Merge conflict** (`main` advanced via the #99 + #100 merges): 1. `internal/theme/theme_test.go` — `TestCtaRenderingInProduction` (this PR) and `TestCardsInProduction` (from #99, now on main) tangled on identical opening boilerplate. Resolved as a **union**: both test functions kept in full, gofmt-clean. 2. `scaffold/themes/deckle/README.md` — kept this PR's new CTA-h3 heading-hierarchy sentence and used main's current icon prose (built-in stroke SVGs), dropping this PR's now-stale "neutral placeholder glyph" wording (superseded by #100). **Re-verification on the resolved tree** (merge commit `4186fad`, pushed to `afk/91`): `go build ./...` + `go vet` clean; full `go test ./internal/theme/` passes, including the real-Hugo build of the merged kitchen-sink fixture rendering CTA, Cards, and icons together. No blockers. Awaiting the maintainer's free-text go-ahead to merge.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/deckle!101
No description provided.