You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/src/uix/blocks/cta/README.md

8.2 KiB

Cta

Function

The panel that asks for the next step: a title, a supporting line and the buttons, on a finished surface that lifts it off the page. Either the closing beat of a page (center) or a mid-page nudge that must not stop the reading (justified).

Composition map

Slot Composes Notes
root bare <section aria-labelledby> landmark; id names it from the title the block renders
padding · measure Section + Container size / container
entrance Motion (trigger="viewport", scale-fade) the panel arrives WHOLE — it is one statement, not a list, so it does not stagger its parts
panel Surface (always solid · color · gradient · rounded) the look is the system's finish, not paint of the block's own
layout Stack (center) · Grid 2-col (justified) justified stacks on narrow, where one row would crush both halves
eyebrow — (app: Badge / text) above the title
title Heading at level (default 2) the block wraps the app's words and owns the landmark id; on-solid ink
description Text as="p" (60ch measure) on-solid ink. A span would ignore align — text-align is inert on an inline box
actions Flex (column → row at sm) the app drops Buttons / Links

Form: snippet slots, not sub-components

The tier's rule is that a compound API is earned when parts repeat (feature-grid.Item) or coordinate (pricing.Switch ↔ PlanPrice). A CTA's parts do neither: they are fixed positional slots the root arranges. So it takes the same shape as hero. (The plan sketched .Title/.Description/.Actions; this is the same registered deviation as hero.)

Coordination

Position under the 2026-07-31 doctrine (architecture/blocks.md §«Coordination»).

Owns nothing. A closing panel places copy and actions; there is no state, no data shape and no word of its own. Layout blocks do not coordinate, and giving one a machine would be the opposite mistake.

Decisions

2026-07-29 — reference floor (dossier §P1: Tailwind Plus «CTA 11» · PrimeBlocks «CTA 12» · Untitled UI):

  • Adopted: the centred panel (parity) and the justified arrangement — copy at the inline-start, actions at the end — which the dossier names as the gap to close.
  • The finish is the system's: a CTA is distinguished by treatment, and treatment is already framework vocabulary. Surface with gradient on by default (the one section that earns it), so a theme or palette swap carries the panel along. The block paints nothing of its own.
  • Ink follows the panel: the title and description take on-solid, the contrast ink the palette guarantees for that canvas. No hand-picked colour, so a color or theme swap carries them along.
  • It reveals whole: one Motion, no stagger. A CTA is a single statement; dealing its three parts in one by one would read as hesitation.
  • No variant prop — the panel is always solid. It shipped as 'solid' | 'soft' and the soft state was measured out in the browser: the primary track lands at oklch(0.9932 …) against a --color-surface-default of oklch(0.9911 …) — 0.002 L apart, i.e. no panel at all in light mode, and Surface has no border to fall back on. Cutting the prop is cheaper than shipping a state that vanishes.
  • The demo offers only dark canvases (primary · indigo · plum). Measured body-copy contrast on the panel: 5.18 · 5.21 · 4.75 in both modes, versus neutral 3.32 / secondary 3.30 / slate 3.30 / teal 3.07 in light. The palette's contrast slot is #ffffff for every solid step, so a mid-lightness canvas cannot carry body copy at AA. The block forwards any color the app passes — it just does not advertise the ones that fail.

Demo

web/routes/blocks/cta/ — the block full-bleed on the page, with live control of layout, colour, finish and direction, and the device widths served from preview/. Mini-page in CtaSite.svelte, shared by both surfaces.

Gaps

Gap Disposition
Split with media (panel with a screenshot beside the copy) deferred — recurs in the refs; it is a third arrangement, and Mockup already exists for the media. Enters when a demo asks
Full-bleed panel (edge to edge, no container) app-land — the app sets container="full"
Dismissible / sticky CTA out — that is the banner block's job (F2.11), not this one
A quiet BOUNDED panel canon — Surface soft does not bound (0.002 L from the page in light) and has no border; Card outline bounds but takes no gradient finish. No primitive covers "quiet CTA panel with an edge"
contrast slot is white on every solid step canon — so a mid-L solid canvas (neutral 3.32 · secondary 3.30 · slate 3.30 · teal 3.07 in light) puts body copy below AA. The pairing guarantee holds for the dark canvases only. Measured with a luminance probe, not eyeballed
On-solid chip treatment canon — Badge has no on-solid variant; the demo uses variant="soft", whose near-white track happens to read against a saturated canvas. A Badge that knows it sits on a solid panel would be the real answer

Found while composing

  • Text align is inert by default. Text renders a span; text-align does nothing on an inline box, so align="center" silently kept the copy left-aligned inside the centred layout. Fixed here with as="p" (which is what a paragraph is anyway), but the prop advertises an effect it does not have until the consumer changes the element. Canon candidate.
  • Surface soft cannot bound a panel — measured above; see Gaps.
  • Group does not stack. The actions row needed Flex direction={{ base: 'column', sm: 'row' }}: in a Group at 420px the secondary action's label breaks mid-phrase against the primary button. hero composes its actions with Group too, so it has the same narrow behaviour — noted for its next pass.

Powered by TurnKey Linux.