CTA

Ready to start?

Free for personal use, no card required.

Try Pro for 14 days.

Cancel anytime.

Going to production?

Get enterprise support, audit logs, and SSO.

Read the source

42 architectural principles in one document.

Left-aligned variant

Use when the surrounding flow is left-aligned content.


  
/*
 * CTA — section
 *
 * Hero-lite: title + lede + actions, designed for mid-page conversion
 * blocks ("Ready to start? Sign up.") rather than above-the-fold.
 *
 *   <section class="cta">
 *     <h2 class="cta__title">Ready to start?</h2>
 *     <p class="cta__lede">Free for personal use, no card required.</p>
 *     <div class="cta__actions">
 *       <a class="button -primary">Sign up</a>
 *       <a class="button -ghost">Talk to sales</a>
 *     </div>
 *   </section>
 */

@layer malevich.components {
  .cta {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: var(--space-gap-elements-m);

    padding-block: var(--space-inset-section-m);
    padding-inline: var(--space-inset-section-m);

    background-color: var(--color-surface-raised);
    color: var(--color-ink-strong);

    border-radius: var(--radius-section);

    inline-size: 100%;
    box-sizing: border-box;
  }

  .cta > * {
    max-inline-size: var(--hero-max-content);
  }

  .cta__title {
    margin: 0;
    color: var(--color-ink-strong);
    font-family: var(--font-display-statement-family);
    font-size: var(--font-display-statement-size);
    font-weight: var(--font-display-statement-weight);
    line-height: var(--font-display-statement-line-height);
    letter-spacing: var(--font-display-statement-letter-spacing);
  }

  .cta__lede {
    margin: 0;
    color: var(--color-ink-regular);
    font-family: var(--font-body-regular-family);
    font-size: var(--font-body-regular-size);
    font-weight: var(--font-body-regular-weight);
    line-height: var(--font-body-regular-line-height);
    letter-spacing: var(--font-body-regular-letter-spacing);
  }

  .cta__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: var(--space-gap-elements-s);
    margin-block-start: var(--space-gap-elements-s);
  }

  /* On-surface variants */

  .cta.-on-accent {
    background-color: var(--color-accent);
    color: var(--color-ink-inverse);
  }
  .cta.-on-accent .cta__title,
  .cta.-on-accent .cta__lede {
    color: var(--color-ink-inverse);
  }

  .cta.-on-inverse {
    background-color: var(--color-ink-strong);
    color: var(--color-ink-inverse);
  }
  .cta.-on-inverse .cta__title,
  .cta.-on-inverse .cta__lede {
    color: var(--color-ink-inverse);
  }

  .cta.-bordered {
    background-color: transparent;
    border: var(--border-width-hairline) solid var(--color-border-default);
  }

  /* Left-aligned variant */

  .cta.-start {
    align-items: flex-start;
    text-align: start;
  }

  .cta.-start .cta__actions {
    justify-content: flex-start;
  }
}
This component is pure CSS — no JavaScript required.
# Cta — AGENTS.md

> Auto-generated from `cta.docs.md` + the modifier applicability matrix.
> Edit those source files; this file regenerates on build.

## Identity

- **Component:** `cta`
- **Layer:** sections
- **Status:** stable
- **Last updated:** 2026-05-19

## Summary

- End-of-page conversion: "Ready to start?"

## Variants

| Variant     | Class                | Use for                                |
|-------------|----------------------|----------------------------------------|
| Default     | `.cta`               | Raised surface card, centered          |
| On accent   | `.cta.-on-accent`    | Accent-colored block                   |
| On inverse  | `.cta.-on-inverse`   | Inverse (dark) block                   |
| Bordered    | `.cta.-bordered`     | Outlined, no fill                      |
| Start       | `.cta.-start`        | Left-aligned (override of centered)    |

Variants compose: `.cta.-on-accent.-start`.

## Modifier applicability

Per ADR-0007. Modifiers attach via `data-{category}="value"` attributes.

| Category | Accepts |
|---|---|
| `background` | any value |
| `surface` | `flat`, `elevated` |
| `effect` | `grain`, `glow`, `ring` |
| `shader` | — |
| `rhythm` | `compact`, `regular`, `spacious` |
| `motion` | any value |
| `behavior` | — |

## Anatomy

```html
<section class="cta">
  <h2 class="cta__title">Ready to start?</h2>
  <p class="cta__lede">Free for personal use, no card required.</p>
  <div class="cta__actions">
    <a class="button -primary">Sign up</a>
    <a class="button -ghost">Talk to sales</a>
  </div>
</section>

<!-- Accent variant -->
<section class="cta -on-accent">
  <h2 class="cta__title">Try Pro for 14 days.</h2>
  <p class="cta__lede">Cancel anytime.</p>
  <div class="cta__actions">
    <a class="button -secondary">Start trial</a>
  </div>
</section>
```

## Tokens consumed

### From semantic tier
- `--color-surface-raised` — default background
- `--color-accent` — on-accent
- `--color-ink-strong` — on-inverse + default text
- `--color-ink-regular` — lede text
- `--color-ink-inverse` — on-accent / on-inverse text
- `--color-border-default` — bordered variant
- `--space-inset-section-m` — padding
- `--space-gap-elements-{s,m}` — gaps
- `--radius-section` — corner radius
- `--border-width-hairline`
- `--font-display-statement-*`, `--font-body-regular-*`

### Component-tier (shared with Hero)
- `--hero-max-content` — measure cap

## Accessibility contract

- Use `<section>` for the wrapper so the block participates in document
  outline.
- The title is an `<h2>` (or `<h3>` if the surrounding hierarchy is
  deeper). Don't reuse `<h1>` here — that's Hero's job.

## Guidance

### Do

- Use Hero for the top of the page, CTA for everything else that asks
  for action.
- Pair `.cta.-on-accent` with `.button.-secondary` so the button
  reads as the "other" surface.

### Don't

- Don't stack two CTAs back-to-back. One conversion ask per section.
- Don't put more than 2 buttons in `.cta__actions` — the call dilutes.