Components
The canonical building blocks, live. Every snippet is copy-pasteable HTML — no JavaScript required except where noted. Styles come from src/styles/design-system.css.
Buttons
Base .btn with variants. Accent is reserved for the single primary action per view; danger only for destructive actions.
<button class="btn btn-primary">Primary</button>
<button class="btn">Secondary</button>
<button class="btn btn-ghost">Ghost</button>
<button class="btn btn-danger">Delete</button>
<button class="btn btn-sm">Small</button>
<button class="btn btn-lg">Large</button>
<button class="btn-icon" aria-label="Settings">…</button>Split-ops hero & terminal card
Public-only composition for ReachOut 2.0.2: status strip, hybrid display heading, flat console actions, and a machine-readable agent transcript.
Your agent runs your marketing
One operating layer for analytics, campaigns, contacts, and insights.
<section class="hero-console texture-dots">
<div class="console-status">…</div>
<div class="hero-console-grid">
<div class="hero-console-lead">…</div>
<div class="hero-console-session">
<div class="termcard">…</div>
</div>
</div>
</section>Console buttons
Public CTAs only. The sigil is visual; the approved CTA wording and authenticated state remain unchanged.
<a class="cbtn cbtn-primary" href="/signup">Get Started</a>
<a class="cbtn cbtn-ghost" href="/demo">See the live demo</a>Diff compare strip
A compact public comparison. Signs keep the meaning visible without relying on color.
<div class="diff">
<div class="diff-head">reachout vs legacy automation</div>
<div class="diff-row plus"><span class="sign">+</span><span class="diff-copy">MCP tool calls</span></div>
<div class="diff-row minus"><span class="sign">−</span><span class="diff-copy">visual builders</span></div>
</div>Pricing readout
Homepage pricing summary. The pricing page retains its fuller calculator and FAQ composition.
<div class="readout">
<div class="readout-row"><span class="readout-tier">Starter</span>…</div>
<div class="readout-row selected"><span class="readout-tier">Pro</span>…</div>
<div class="readout-row"><span class="readout-tier">Max</span>…</div>
</div>Capability card
Console-framed public feature card with a machine label, readable description, and quiet status footer.
<article class="capcard">
<div class="capcard-head"><span>Website analytics</span><span class="capcard-surface">studio</span></div>
<div class="capcard-body">First-party, identified, no aggregation</div>
<div class="capcard-foot">status: <span class="capcard-status">shipped</span></div>
</article>Dotted texture
Use .texture-dots only on the public hero and section dividers. It adapts automatically to both themes.
<div class="texture-dots ds-texture-preview"></div>Badges
Status at a glance — mono, uppercase, optional pulsing dot. Use .badge-live for running things, .badge-done for success, .badge-err for failures, .badge-warn for limits, .badge-draft for inactive.
<span class="badge badge-live"><span class="dot"></span>Live</span>
<span class="badge badge-done"><span class="dot"></span>Sent</span>
<span class="badge badge-err"><span class="dot"></span>Failed</span>
<span class="badge badge-warn"><span class="dot"></span>Near cap</span>
<span class="badge badge-draft">Draft</span>
<span class="badge">v1.0.1</span>Card
The base surface: 1px border, --bg-elev, --radius-lg, never a shadow. Compose with .card-head, .card-body, .card-foot.
Deliverability
Last 30 days across all campaigns
- Delivered
- 98.2%
- Bounced
- 1.1%
- Complaints
- 0.02%
<div class="card">
<div class="card-head">
<div class="card-head-main">
<h3 class="card-title">Deliverability</h3>
<p class="card-sub">Last 30 days across all campaigns</p>
</div>
<div class="card-head-actions"><button class="btn btn-sm">Export</button></div>
</div>
<div class="card-body">…</div>
<div class="card-foot"><span>Updated 4 min ago</span><span class="mono">src: ses-eu-1</span></div>
</div>Stat cards
The canonical metric tile: sans label, mono tabular value, semantic delta. Wrap in .stat-grid (4-up, responsive). Mark selectable cards with button.stat-card + .selected.
<div class="stat-grid">
<div class="stat-card">
<span class="stat-label">Emails sent <span class="delta up">↗ +18.2%</span></span>
<span class="stat-value">12,408</span>
<span class="stat-sub">vs 10,498 previous period</span>
</div>
…
</div>Delta
Signed change indicator. Semantic color, mono, arrow glyph. .delta-framed adds the bordered chip used in selectable stat cards.
<span class="delta positive">↗ +18.2%</span>
<span class="delta negative">↗ +0.3 pts</span>
<span class="delta neutral">→ 0.0%</span>
<span class="delta positive delta-framed">↗ +18.2%</span>Forms
.field composes label + control + hint/error. Checkboxes, radios and switches are native inputs, themed.
<div class="field">
<label class="field-label" for="name">Campaign name <span class="req">*</span></label>
<input id="name" type="text" placeholder="spring-launch" />
<span class="field-hint">Shown to your agent in tool calls.</span>
</div>
<div class="field has-error">
<label class="field-label" for="from">Sender</label>
<input id="from" type="email" value="not-an-email" />
<span class="field-error">Enter a valid sender address.</span>
</div>
<label class="check"><input type="checkbox" checked /> GDPR consent stored</label>
<label class="check radio"><input type="radio" name="r" checked /> Immediate send</label>
<label class="switch"><input type="checkbox" checked /> Live tracking</label>Segmented control & tabs
Segmented for switching a view within a panel (aria-pressed); tabs for page-level navigation.
<div class="segmented" role="group" aria-label="Range">
<button aria-pressed="false">7d</button>
<button aria-pressed="true">30d</button>
<button aria-pressed="false">3m</button>
</div>
<nav class="tabs" aria-label="Analytics sections">
<a href="#" class="active" aria-current="page">Overview</a>
<a href="#">Deliverability</a>
<a href="#">Engagement</a>
</nav>Alerts
Inline feedback. Info = accent, success = positive, warning = warning, danger = negative.
<div class="alert alert-info">…</div>
<div class="alert alert-success">…</div>
<div class="alert alert-warning">…</div>
<div class="alert alert-danger">…</div>Modal
The one canonical dialog: .modal-backdrop > .modal > head/body/foot. createModalController() owns focus, isolation, Escape/backdrop close and restoration.
Delete 2 contacts?
The contacts [email protected] and [email protected]will be removed from every segment and campaign. Event history is retained for 30 days.
<div class="modal-backdrop">
<div class="modal" role="dialog" aria-modal="true" aria-labelledby="m-title" tabindex="-1">
<div class="modal-head">
<div>
<div class="modal-kicker">contacts.delete</div>
<h3 id="m-title">Delete 2 contacts?</h3>
</div>
<button class="modal-close" aria-label="Close">✕</button>
</div>
<div class="modal-body">…</div>
<div class="modal-foot">
<span class="modal-foot-note">This cannot be undone.</span>
<button class="btn">Cancel</button>
<button class="btn btn-danger">Delete</button>
</div>
</div>
</div>Progress
Thin meters for usage and send progress. Default accent; semantic states for ok/warn/err.
<div class="progress"><span class="progress-fill" style="width: 64%"></span></div>
<div class="progress progress-ok">…</div>
<div class="progress progress-warn">…</div>
<div class="progress progress-err">…</div>
<div class="progress-labeled">
<div class="progress-head"><span>Emails this cycle</span><b>8,412 / 10,000</b></div>
<div class="progress progress-warn"><span class="progress-fill" style="width: 84%"></span></div>
</div>Loading progress & skeleton
The viewport bar confirms route progress; skeletons preserve the shape and hierarchy of unresolved data. Motion respects reduced-motion preferences.
<div class="studio-page-progress" aria-hidden="true">
<span class="studio-page-progress-fill"></span>
</div>
<div aria-hidden="true">
<div class="skeleton skeleton-heading"></div>
<div class="skeleton skeleton-line skeleton-line-wide"></div>
<div class="skeleton skeleton-chart"></div>
</div>Empty state
Every list and table needs one. Icon in a dashed frame, sans title, body copy, one primary action, optional mono hint for the agent path.
No segments yet
Segments target campaigns. Create one here, or ask your agent to build it from a natural-language rule.
<div class="empty-state">
<div class="empty-state-icon">⌁</div>
<h3>No segments yet</h3>
<p>Segments target campaigns. Create one here, or ask your agent.</p>
<button class="btn btn-primary">Create segment</button>
<div class="mono-note">mcp · segments.create</div>
</div>Key-value list
Dense metadata display — sans keys, mono tabular values, dashed separators.
- API key
- ro_live_9f2…c41
- Created
- 2026-07-14 09:32
- Calls this cycle
- 862 / 1,000
- Last used
- 4 min ago
<dl class="kv">
<div class="kv-row"><dt>API key</dt><dd>ro_live_9f2…c41</dd></div>
<div class="kv-row"><dt>Created</dt><dd>2026-07-14 09:32</dd></div>
<div class="kv-row"><dt>Calls this cycle</dt><dd>862 / 1,000</dd></div>
</dl>Code & terminal
.code-block with token colors (.tk-*). Machine content stays mono — this is the agent's native tongue.
$ reachout campaigns send --id spring-launch
✓ validated segment 3,102 contacts
✓ queued 12,408 emails # est. 4m 12s<div class="code-block">
<span class="code-lang">bash</span>
<pre><code><span class="tk-prompt">$</span> <span class="tk-cmd">reachout campaigns send</span> <span class="tk-flag">--id</span> spring-launch
<span class="tk-ok">✓</span> queued 12,408 emails <span class="tk-comment"># 4m 12s</span></code></pre>
</div>Mockup frames
Product presentation for public pages: .mockup (browser chrome) and .mockup-terminal. Fill the body with a screenshot, a live iframe, or — preferred — real components at reduced scale.
← { "contacts": 3102, "confidence": 0.94 }
<div class="mockup">
<div class="mockup-chrome">
<span class="mockup-dots"><i></i><i></i><i></i></span>
<span class="mockup-url"><span class="lock">●</span> studio.usereachout.com/analytics</span>
</div>
<div class="mockup-body">…screenshot, iframe or CSS miniature…</div>
</div>