Canvas Design System
Main Site Tokens

CTA Section

Closes a marketing page with one conversion prompt: eyebrow, headline, subtitle, and up to two buttons.

When to Use

Use when: A marketing, landing, or solutions page ends and the reader should do something next. One per page, at the very bottom — it consolidates CTA markup that was previously hand-rolled on each page.
Don't use when: You're opening a page — that's Hero. For a prompt mid-content use Banner, and inside app screens skip it entirely; product UI shouldn't carry marketing CTAs.

Variants

Full — eyebrow, subtitle, two buttons

The complete form. Two buttons work when the second is a genuinely lower-commitment path, not a rewording of the first.

Ready to start?

Plan your workshop today

Build your agenda in 3 minutes with the drag-and-drop planner.

<?= ws_cta_section('Plan your workshop today', [
    'eyebrow'      => 'Ready to start?',
    'subtitle'     => 'Build your agenda in 3 minutes with the drag-and-drop planner.',
    'primaryBtn'   => ['text' => 'Get Started',        'href' => '/planner/'],
    'secondaryBtn' => ['text' => 'Browse the Library', 'href' => '/library/exercises/'],
    'color'        => 'learn',
]) ?>

Minimal — title and one button

Often stronger. When the page has already made the argument, the CTA only needs to name the action.

Discover your Facilitator DNA

<?= ws_cta_section('Discover your Facilitator DNA', [
    'primaryBtn' => ['text' => 'Take the Quiz', 'href' => '/dna/'],
    'color'      => 'reflect',
]) ?>

Colour

Five keys. Since Red Unification the four phase keys resolve to the same red, so the practical choice is phase versus neutral — neutral being the muted variant for pages that shouldn't shout.

A quieter close

The neutral key, for pages where a full-colour panel would be too loud.

<?= ws_cta_section('A quieter close', ['color' => 'neutral', /* … */]) ?>

States

StateBehavior
DefaultFull-bleed panel with the content constrained to --container-max. Static — the section itself has no interactive state.
Button statesHover, focus, and disabled all belong to Button, which both slots render through.
Omitted partsEyebrow, subtitle, and secondary button each render only when supplied. No empty space is reserved.
No buttonsRenders headline-only. Almost certainly a mistake — a CTA section without a call to action is just a heading.
Button defaultsPassing an empty array for a slot still renders a button, with the fallback text "Get Started" or "Learn More". Omit the key entirely to leave a slot out.

Real-World Usage

The end of a solutions page, with the CTA gated on auth state — a signed-in reader gets taken to the product, a visitor gets asked to sign up.

Start free

Run a workshop people actually remember

The library, the planner, and the AI coach &mdash; no card required.

<?= ws_cta_section('Run a workshop people actually remember', [
    'eyebrow'      => 'Start free',
    'subtitle'     => 'The library, the planner, and the AI coach — no card required.',
    'primaryBtn'   => $isLoggedIn
        ? ['text' => 'Open the Planner', 'href' => '/planner/']
        : ['text' => 'Create your first workshop', 'href' => '/signup',
           'icon' => 'arrow_forward', 'iconPosition' => 'right'],
    'secondaryBtn' => ['text' => 'See how it works', 'href' => '/about/'],
]) ?>

Options

OptionTypeDefaultPurpose
$titlestring''Positional headline. Make it the outcome, not the product.
eyebrowstringnullSmall label above the title
subtitlestringnullOne supporting sentence
primaryBtnarraynullForwarded to ws_button(). Text defaults to "Get Started" if the array is present but has no text.
secondaryBtnarraynullSame, defaulting to "Learn More"
colorstring'learn'learn | plan | facilitate | reflect | neutral
classstring''Additional CSS classes on the section
attrsarray[]Extra HTML attributes on the section element

Accessibility

ConcernBehavior
SemanticsRenders a <section>. It has no accessible name of its own — add attrs => ['aria-labelledby' => …] if the page has several landmarks and this one needs distinguishing.
Heading levelThe title is styled display text rather than a heading element, so it doesn't join the page outline. Deliberate for a closing panel, but it means screen-reader users browsing by heading won't land here — the buttons are how they'll find it.
KeyboardOnly the buttons are focusable, and they inherit Button's native behaviour. As the last thing on the page, they're the final tab stops.
Link textThe buttons are usually links. Name the destination: "Create your first workshop" beats "Get Started", which is meaningless in a screen reader's link list.
EyebrowRead before the title in document order, so the two should make sense together — "Ready to start? Plan your workshop today".
ContrastEach colour key pairs a tested background with --color-ink or its muted variants. Don't override one side alone.

Tokens

TokenUsed for
--phase-learn / --phase-plan / --phase-facilitate / --phase-reflect (and -light pairs)Panel background per colour key
--bg-muted / --bg-page-warmNeutral key background
--color-borderPanel edge
--color-ink / --color-ink-secondary / --color-ink-mutedTitle, subtitle, and eyebrow text
--container-maxInner content width
--text-h2 / --text-ui / --text-metaTitle, subtitle, and eyebrow type scale
--font-heading / --font-body / --font-boldFraunces title, Inter subtitle
--space-2 through --space-16Internal rhythm and the panel's generous vertical padding

CSS Classes

ClassPurpose
.ws-cta-sectionFull-bleed section
.ws-cta-section--learn / --plan / --facilitate / --reflect / --neutralColour keys
.ws-cta-section__containerMax-width wrapper
.ws-cta-section__contentText column
.ws-cta-section__eyebrowSmall label above the title
.ws-cta-section__titleDisplay headline
.ws-cta-section__subtitleSupporting paragraph
.ws-cta-section__actionsButton row

Files

FilePurpose
includes/components/helpers.phpws_cta_section() helper function
includes/components/cta-section.phpTemplate — layout and the button calls
includes/components/components.cssStyles (.ws-cta-section rules)