Home / Snippets / Layout /

CTA block with CQ

Responsive call-to-action block that adapts layout via container queries — no JavaScript needed.

Ready to get started?

Join thousands of developers building better web experiences.

Unlock premium features

Upgrade today and get access to exclusive tools.

Widely supported
layoutno-js

Quick implementation

/* HTML: 
<div class="cta-wrapper">
  <div class="cta-block">
    <div class="cta-content">
      <h3>Title</h3>
      <p>Description</p>
    </div>
    <div class="cta-actions">
      <button class="cta-btn primary">Primary</button>
      <button class="cta-btn secondary">Secondary</button>
    </div>
  </div>
</div>
*/

.cta-wrapper {
  container-type: inline-size;
  container-name: cta;
}

.cta-block {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 1.5rem;
  border-radius: var(--radius);
  background: oklch(0.22 0.03 260);
}

.cta-content h3 {
  margin: 0;
  font-size: 1.25rem;
}

.cta-actions {
  display: flex;
  gap: 0.75rem;
}

.cta-btn {
  flex: 1;
  padding: 0.65rem 1rem;
  border-radius: var(--radius);
  font-weight: 600;
  cursor: pointer;
}

.cta-btn--primary {
  background: var(--accent);
  border: 1px solid var(--accent);
  color: white;
}

.cta-btn--secondary {
  background: transparent;
  border: 1px solid var(--card-border);
  color: var(--text);
}

/* Wide layout (container >= 20rem) */
@container cta (min-width: 20rem) {
  .cta-block {
    flex-direction: row;
    align-items: center;
  }
  .cta-content {
    flex: 1;
  }
  .cta-actions {
    flex: 0 0 auto;
    min-width: 12rem;
  }
}

/* Narrow layout (container < 20rem) */
@container cta (max-width: 19.99rem) {
  .cta-block {
    flex-direction: column;
    align-items: stretch;
  }
  .cta-content {
    text-align: center;
  }
  .cta-actions {
    width: 100%;
  }
}

Prompt this to your LLM

Includes role, constraints, two framework variants, and edge cases to handle.

You are a senior frontend engineer specializing in modern CSS layout.

Goal: Build a call-to-action (CTA) block component that adapts its layout based on available container width — no JavaScript.

Technical constraints:
- Use container-type: inline-size and @container queries to detect wrapper width.
- Structure: content area (heading + description) + action buttons (primary + secondary).
- Use oklch() for all colors — no hex or rgba().
- Layout shift: horizontal (side-by-side) when wide, vertical (stacked) when narrow.
- Include focus-visible styles for interactive elements (buttons).

Framework variant (pick one):
A) Vanilla HTML + CSS only.
B) React component — accept `title`, `description`, `primaryAction` (text + onClick), `secondaryAction` (text + onClick), `className` props; include CSS as a CSS module.

Edge cases to handle:
- Disabled state: add .disabled class with opacity: 0.6 and cursor: not-allowed on buttons.
- What happens if container queries are not supported? Provide a media query fallback.
- ARIA: ensure buttons have proper accessible names; add role="region" with aria-label if the CTA is standalone.

Return HTML + CSS, clearly separated.

Why this matters in 2026

CTA blocks appear everywhere — hero sections, sidebars, modals, email footers. Each context has different width constraints. Container queries let a single CTA component adapt: stacked in a narrow sidebar, horizontal in a wide hero. No more duplicate components or JS-driven class toggles.

This is component-driven responsive design at its best: the component knows its context and styles accordingly. Build once, embed anywhere.

The logic

container-type: inline-size on the wrapper establishes a sizing context. The @container cta (min-width: 20rem) rule checks that context, not the viewport. When the wrapper crosses the 20rem threshold, the block switches from stacked to side-by-side via flex-direction.

The breakpoint: 20rem ensures enough horizontal space for the content and buttons to sit comfortably side-by-side. Below that, stacking prevents cramped text and buttons.

Why container queries over media queries? A media query would respond to viewport width — useless when the same CTA appears in a narrow sidebar vs. a wide hero. Container queries make the CTA self-aware of its embedding context.

Accessibility & performance

CTA buttons must be keyboard-accessible with visible focus rings (:focus-visible). Use semantic HTML: wrap the CTA in <section> with aria-label if it's standalone. For performance, container queries run on the compositor thread — no layout thrashing. Avoid animating flex-direction (it's not animatable); instead, use @media (prefers-reduced-motion: reduce) to skip any transition effects.

Browser support: Container queries landed in all major browsers in 2023. For older browsers, provide a @media (min-width: 20rem) fallback that assumes the wide layout.