Skip to content

Components / Sections

Store CTA

The call to get the app: a panel of the app's colour block with the headline, the store buttons and, on a wide screen, a QR code to the app, and a phone rising out of the panel's lower edge as the panel comes into view, its lower third cut by the panel. The rise is a view timeline; where there is none, or with reduced motion, the phone stands where the rise ends. The store buttons invert on the panel, the block's text as their ground. Zero JavaScript.

Live demo

Start tomorrow morning

Free for three habits. Every habit, the widgets and the watch for $3.99 a month (a sample price).

Point your phone's camera here to open this page

Live, from the library itself, at full size: scroll, hover, navigate away and back. The demo is never gated.

Variants · each row writes the attribute the prop writes; the section's own CSS answers

tone
density

Measured

JavaScript of its own

0 B

Uses Reveal and the shared runtime (1.9 kB raw, once per site). With those included: 3.0 kB raw.

CSS 29.7 kB raw including the base tokens. Measured from a production build, and again in CI for every change that can move it.

Browser support

Baseline · widely available

Chrome 105 · Firefox 110 · Safari 16; needs container-units. Elsewhere: without view timelines the phone stands still where the rise ends.

Install Pro

The demo above is never gated; the source is. With a key in components.json (see account), the registry serves it like any other item:

terminal
npx shadcn@latest add @moonarc-pro/store-cta
src/pages/index.astro
---
import StoreCta from '@/components/moonarc/pro/StoreCta.astro';
---
<StoreCta
  copy="Free for three habits. Every habit for $3.99 a month."
  stores={[
    { href: 'https://apps.apple.com/app/id000000000', note: 'Download on the', name: 'App Store' },
    { href: 'https://play.google.com/store/apps/details?id=com.example', note: 'Get it on', name: 'Google Play' },
  ]}
  qr={{ value: 'https://example.com/get', caption: 'Scan to get the app' }}
  screen={{ title: 'Today', label: 'The today screen', rows: [{ kind: 'ring', value: 0.75, text: '3 of 4', label: 'done' }] }}
>
  <span slot="headline">Start <em>tomorrow</em> morning</span>
</StoreCta>

Without a key the registry answers 401 with an upgrade message. Everything this section composes is free: phone-frame, app-screen, reveal.

Props

PropTypeDefaultDescription
stores{ href: string; name: string; note?: string }[]noneThe stores as text buttons. Slot "actions" replaces them and the QR code.
screenAppScreen propsnoneThe screen in the phone; a label names it for a screen reader.
qr{ value: string; caption?: string; label?: string }noneA QR code to the app, on a wide screen with a pointer.
headlinestringnoneThe heading. Slot "headline" wins and may hold an <em>.
eyebrowstringnoneA short line above the heading.
copystringnoneA line under the heading.
notestringnoneA short line under the buttons.
block1 | 2 | 3 | 4 | 51The panel's colour block.
model'island' | 'notch' | 'punch''island'The phone's cut-out.
level2 | 32Heading level of the heading.
tone'surface' | 'muted' | 'ink' | 'brand'unset (paints nothing)The ground around the panel.
density'compact' | 'cozy' | 'roomy''cozy'Block padding ×0.8 / ×1 / ×1.25 through --ma-density.

Theming and overrides

The section reads the theme contract (shadcn's variable names with the base tokens as fallback), so a pasted theme or one --ma-brand recolours it. Its class names are the override surface and stay stable; a named slot replaces the matching prop with your own markup.

Classes

.ma-scta.ma-scta__actions.ma-scta__copy.ma-scta__eyebrow.ma-scta__headline.ma-scta__media.ma-scta__note.ma-scta__outer.ma-scta__panel.ma-scta__phone

Slots

slot="headline"slot="actions"

Reduced motion

The phone stands still where the rise ends.

With ClientRouter

CSS and Reveal.

Why it is built this way

Replaces: App download banners with store badges and a phone mockup. See the migration table.

A generic phone frame around a screenshot or any content, with three cut-outs (island, notch, punch), a home bar, and no logo, buttons or brand.

every browseralways

A QR code for a URL or a short text, encoded at build and drawn as one SVG path inside the quiet zone a reader needs.

every browseralways

A hero for a mobile app: the headline, the rating line (stars, the average, how many rated and where) and the store buttons beside a phone on a disc of the app's colour.

every browserload

The closing call on a colour block: a short line, a large heading that ends on a phrase in the emphasis face, a sentence and two pill actions, on one of the palette's blocks with a drawing (Motif) rising out of its lower corner.

every browserscrollhover

A closing call to action: a deep brand panel with an aurora and film grain behind it, a headline that catches a shine, a magnetic primary action, a secondary one, and a typed command.

every browserscrollpointer

Also: view transitions · how costs are measured · browser support · accessibility policy · five-minute setup · MCP for agents · this page as Markdown