CTA pair

Offers the main action and its one alternative side by side: book a conversation, or start with the free health check.

Molecule · stable · v1.0.0 · owner: Site system lead · contract 19 of 20 points (open: figma)

Do and don't

Do: The standard pair

Rendered by cta-pair.ts

Open example: CTA pair · Do: The standard pair Use the full-page example to follow page links.

One commitment, one lighter step.

Do: A single action

Rendered by cta-pair.ts

Open example: CTA pair · Do: A single action Use the full-page example to follow page links.

A task page has one step.

Don't: Secondary first

Rendered by cta-pair.ts

Open example: CTA pair · Don't: Secondary first Use the full-page example to follow page links.

The main action always comes first and is the primary.

Don't: A vague primary

Rendered by cta-pair.ts

Open example: CTA pair · Don't: A vague primary Use the full-page example to follow page links.

Say what happens.

Rules

RuleWhyCheck
Primary first, secondary second, never more than two.The order tells the reader which step matters most.Automated: type of CtaPairProps (two fixed slots)
Labels come from the CTA content tokens.The same action is worded the same way on every page.Automated: glossary checker (CTA wording set)

API

PropTypeRequiredDefaultWhat it does
primaryobjectYes{ label, href } of the main action (content token cta.primary).
secondaryobjectNo{ label, href } of the alternative (cta.secondary).

Contract details

The full contract: identity, anatomy, variants, states, tokens, layout, motion, accessibility, content, tests

Identity

AspectValue
Idcta-pair
LayerMolecules
Categoryaction
OwnerSite system lead
Statusstable
Version1.0.0
Sourceapps/showcase/site/system/molecules/cta-pair.ts (private repository; the contract and the render function are this one module)
VersionDateChange
1.0.02026-10-26The page's main action with its one alternative, always in this order.

Use when

  • The end of a page or the hero. Why: The reader who is ready acts; the one who is not has a lighter next step.

Do not use when

  • With two primaries. Why: The pair is one primary and one secondary.
  • With three or more actions. Why: Choice beyond two slows the decision.

Anatomy

PartWhat it is
rootdiv.actions
primaryButton, primary, with the arrow.
secondaryButton, secondary.

Made of: button.

Used in: atomic-guide, cta-section, hero, page-head.

Variants

VariantWhenWhy
pairHero and closing CTA.Main action plus a low-commitment alternative.
singleA focused task page.One step only.

States

StateRendered or not applicable
defaultdefault
hoverNot applicable: Each button carries its own hover.
focus-visibleNot applicable: The pair does not take focus; links and buttons inside it carry their own focus ring.
activeNot applicable: The pair cannot be pressed.
disabledNot applicable: The pair has nothing to disable.
loadingNot applicable: The pair is rendered at build time and is complete when the page loads.
emptyNot applicable: The pair is not rendered without content; the builder skips it.
errorNot applicable: The pair shows no data that can fail.
currentNot applicable: The pair has no current item.
visitedNot applicable: The pair is not a link.
expandedNot applicable: The pair does not open or close.

Tokens

PartToken
root.gap--space-5
root.displayflex, wrap
--cta-pair-gap-space-5var(--space-5)
--cta-pair-margin-top-space-8var(--space-8)

Layout

AspectValue
breakpointsRequired review at 390, 768, 1024 and 1440 px; execution is not implied by this declaration; stacks full width under 600 px.
wrappingThe buttons wrap onto two rows.
textExpansionJapanese labels fit; long labels wrap inside each button.

Spacing and rhythm

AspectValue
internal--space-5 between buttons.
external--space-9 above in the hero (D-SITE-005).
rhythmLast in a hero or CTA section.

Motion

AspectValue
NoneThe buttons move; the pair does not.

Runtime and reuse

  1. Render-only element; mount any registered child enhancements separately. Preserve required Site CSS, fonts, responsive and locale rules from nativeElement.

Accessibility passport

AspectValue
rolegroup of links
nameEach button's label
keyboardTab from primary to secondary.
focusPrimary first.
screenReaderTwo links in order.
contrastAs the button.
target48 px each.

Content

SlotLengthVoice
primary.label4 wordscta.primary
secondary.label6 wordscta.secondary

Good: “Book a conversation / Start with a free health check”

Bad: “Contact / Learn more / Pricing”

Japanese: 相談する / 無料で診断する(約3分) from content tokens.

Icons and illustrations

  • icon:arrow-right

Decisions and exceptions

Usage

123 uses across the built pages (measured by npm run site:system).

Tests

AspectValue
unittests/unit/site-system.test.ts: renders every do and don't example through the registry and checks the classes it owns
visualNot yet: there are no screenshot baselines; the Library page is checked for sideways scroll at 1440 px by tests/site/system.spec.ts
a11ytests/site/system.spec.ts: axe (0 serious or critical) on the element's Library page in light and dark
parityscripts/site/system/check.ts: Library examples are rendered by the same render function the site imports

Figma

not-yet: The Loom Site Figma library is the last stage of the plan; this element is not mapped yet.

Open in Storybook

Start with a conversation about your system.

We read your system, show you what we found, and agree with your team what to fix first.