24.1 Integration card
A directory tile for one third-party integration. The card composes a vendor logo + name + status pill in a flex head, an optional description body, and a consumer-supplied action footer pinned to the bottom via margin-top: auto so unequal description lengths don’t misalign the action row across the grid. The status pill (dot + label) maps four canonical states to colour pairs: is-connected uses --success-text for the live-and-wired meaning, is-disconnected uses the sunken neutral for the not-yet-wired meaning, is-error uses --error-text for the delivery-failure meaning, and is-pending uses --warning-text for the awaiting-OAuth meaning. .integration-card-grid is the canonical 3×2 grid wrapper — 3-up on desktop, 2-up at tablet, single-column on phone.
Six cards — Slack, GitHub, Stripe, Notion, Linear, Figma
.integration-cardSix integration cards arranged in the canonical 3×2 .integration-card-grid, one per canonical state. Slack (Productivity / connected / Manage) and GitHub (Developer / connected / Manage) demo the live-and-wired meaning; Stripe (Payments / error / Retry) demos the delivery-failure meaning; Notion (Productivity / disconnected / Connect) and Figma (Design / disconnected / Connect) demo the not-yet-wired meaning; Linear (Developer / pending / Cancel) demos the awaiting-OAuth meaning. Each card carries its own simple inline-SVG lettermark logo — a 40×40 tinted rect with a centred white initial — keeping the demo deterministic without external assets.
Send deal updates and pipeline notifications to a channel.
Open pull requests and surface review status on deal cards.
Pull invoice and subscription data into the billing tab.
Mirror meeting notes and project briefs into linked records.
Two-way sync issues to deals — awaiting OAuth confirmation.
Embed design file previews and version links on records.
<div class="integration-card-grid">
<div class="integration-card">
<div class="integration-card-head">
<span class="integration-card-logo">
<svg width="40" height="40" viewBox="0 0 40 40" aria-hidden="true">
<rect width="40" height="40" rx="8" fill="#611F69"/>
<text x="20" y="26" text-anchor="middle" font-size="16" font-weight="700" fill="#fff">S</text>
</svg>
</span>
<span class="integration-card-name">Slack</span>
<span class="integration-card-status is-connected">
<span class="integration-card-status-dot" aria-hidden="true"></span>
Connected
</span>
</div>
<p class="integration-card-desc">Send deal updates…</p>
<div class="integration-card-action">
<button type="button" class="btn btn-ghost">Manage</button>
</div>
</div>
…
</div>
.integration-card {
display: flex;
flex-direction: column;
gap: var(--s-3);
padding: var(--s-4);
border: 1px solid var(--hair);
border-radius: var(--r-md);
background: var(--bg-paper);
}
.integration-card-head {
display: flex;
align-items: center;
gap: var(--s-3);
}
.integration-card-logo {
width: 40px;
height: 40px;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--fg);
flex-shrink: 0;
}
.integration-card-name {
font: 600 14px/1.3 var(--f-body);
color: var(--fg);
}
.integration-card-status {
display: inline-flex;
align-items: center;
gap: 4px;
font: 500 11px/1 var(--f-mono);
text-transform: uppercase;
letter-spacing: 0.04em;
}
.integration-card-status.is-connected { color: var(--success-text); }
.integration-card-status.is-disconnected { color: var(--fg-dim); }
.integration-card-status.is-error { color: var(--error-text); }
.integration-card-status.is-pending { color: var(--warning-text); }
.integration-card-status-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: currentColor;
}
.integration-card-desc {
font: 400 13px/1.5 var(--f-body);
color: var(--fg-soft);
margin: 0;
}
.integration-card-action {
margin-top: auto;
}
.integration-card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--s-3);
}
@media (max-width: 720px) {
.integration-card-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 480px) {
.integration-card-grid { grid-template-columns: 1fr; }
.integration-card-action { width: 100%; }
.integration-card-action .btn { width: 100%; }
}
import { IntegrationCard } from "@magicblocksai/ui";
<div className="integration-card-grid">
<IntegrationCard
logo={<SlackLogo />}
name="Slack"
description="Send deal updates and pipeline notifications to a channel."
status="connected"
category="Productivity"
action={<button type="button" className="btn btn-ghost">Manage</button>}
/>
<IntegrationCard
logo={<GitHubLogo />}
name="GitHub"
description="Open pull requests and surface review status on deal cards."
status="connected"
category="Developer"
action={<button type="button" className="btn btn-ghost">Manage</button>}
/>
<IntegrationCard
logo={<StripeLogo />}
name="Stripe"
description="Pull invoice and subscription data into the billing tab."
status="error"
category="Payments"
action={<button type="button" className="btn btn-ghost">Retry</button>}
/>
<IntegrationCard
logo={<NotionLogo />}
name="Notion"
description="Mirror meeting notes and project briefs into linked records."
status="disconnected"
category="Productivity"
action={<button type="button" className="btn btn-primary">Connect</button>}
/>
<IntegrationCard
logo={<LinearLogo />}
name="Linear"
description="Two-way sync issues to deals — awaiting OAuth confirmation."
status="pending"
category="Developer"
action={<button type="button" className="btn btn-ghost">Cancel</button>}
/>
<IntegrationCard
logo={<FigmaLogo />}
name="Figma"
description="Embed design file previews and version links on records."
status="disconnected"
category="Design"
action={<button type="button" className="btn btn-primary">Connect</button>}
/>
</div>
24.2 Integrations grid
A wrapper that pairs a row of category filter chips with a responsive grid of integration cards. The chip row reuses the kit’s global .chip styling and toggles .is-active on the selected chip; the inner .integrations-grid-list is 3-up on desktop, 2-up at tablet, and single-column on phone — and the chip row switches to a horizontal scroller at the phone breakpoint so a longer category list never overflows. The wrapper itself doesn’t filter the children: filtering happens at the consumer’s data layer via the onCategoryChange callback, keeping the grid layout fully declarative.
Filtered grid — All / Productivity / Developer / Payments
.integrations-gridAn .integrations-grid with four filter chips above the canonical six-card grid from section 26.1. The first chip (All) is active; the chips render as <button class="chip"> with aria-pressed mirroring active state. Each card carries its own inline-SVG lettermark logo (40×40 tinted rect + centred white initial), keeping the demo deterministic without external assets.
Send deal updates and pipeline notifications to a channel.
Open pull requests and surface review status on deal cards.
Pull invoice and subscription data into the billing tab.
Mirror meeting notes and project briefs into linked records.
Two-way sync issues to deals — awaiting OAuth confirmation.
Embed design file previews and version links on records.
<div class="integrations-grid">
<div class="integrations-grid-filters" role="group" aria-label="Filter integrations by category">
<button type="button" class="chip is-active" aria-pressed="true">All</button>
<button type="button" class="chip" aria-pressed="false">Productivity</button>
<button type="button" class="chip" aria-pressed="false">Developer</button>
<button type="button" class="chip" aria-pressed="false">Payments</button>
</div>
<div class="integrations-grid-list">
<div class="integration-card">…</div>
…
</div>
</div>
.integrations-grid {
display: flex;
flex-direction: column;
gap: var(--s-4);
}
.integrations-grid-filters {
display: flex;
gap: var(--s-2);
flex-wrap: wrap;
}
.integrations-grid-list {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--s-3);
}
@media (max-width: 720px) {
.integrations-grid-list { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 480px) {
.integrations-grid-list { grid-template-columns: 1fr; }
.integrations-grid-filters { overflow-x: auto; flex-wrap: nowrap; }
}
import { IntegrationsGrid, IntegrationCard } from "@magicblocksai/ui";
<IntegrationsGrid
categories={["All", "Productivity", "Developer", "Payments"]}
defaultActiveCategory="All"
onCategoryChange={(cat) => setFilter(cat)}
>
<IntegrationCard
logo={<SlackLogo />}
name="Slack"
description="Send deal updates and pipeline notifications to a channel."
status="connected"
category="Productivity"
action={<button type="button" className="btn btn-ghost">Manage</button>}
/>
<IntegrationCard
logo={<GitHubLogo />}
name="GitHub"
description="Open pull requests and surface review status on deal cards."
status="connected"
category="Developer"
action={<button type="button" className="btn btn-ghost">Manage</button>}
/>
<IntegrationCard
logo={<StripeLogo />}
name="Stripe"
description="Pull invoice and subscription data into the billing tab."
status="error"
category="Payments"
action={<button type="button" className="btn btn-ghost">Retry</button>}
/>
<IntegrationCard
logo={<NotionLogo />}
name="Notion"
description="Mirror meeting notes and project briefs into linked records."
status="disconnected"
category="Productivity"
action={<button type="button" className="btn btn-primary">Connect</button>}
/>
<IntegrationCard
logo={<LinearLogo />}
name="Linear"
description="Two-way sync issues to deals — awaiting OAuth confirmation."
status="pending"
category="Developer"
action={<button type="button" className="btn btn-ghost">Cancel</button>}
/>
<IntegrationCard
logo={<FigmaLogo />}
name="Figma"
description="Embed design file previews and version links on records."
status="disconnected"
category="Design"
action={<button type="button" className="btn btn-primary">Connect</button>}
/>
</IntegrationsGrid>
24.3 Webhook console
A mono-font table of recent webhook deliveries — timestamp / endpoint / event / status / duration. Status codes are colour-coded by first digit so a glance reads the health of the queue without parsing every row: .is-2xx uses --success-text for the OK path, .is-4xx uses --warning-text for client-side errors (rate-limit, validation), and .is-5xx uses --error-text for server-side failures. The header row is always a <div>; each delivery row is rendered as a <button> when the consumer wires onDeliveryClick (typically to open a detail drawer) or a <div> when the console reads as a read-only log. Below 480px the five-column grid reflows to a two-row layout: timestamp + endpoint on the first row, event spanning both columns on the second row, status pinned to the right of the first row, and duration dropped to preserve the timestamp + status priority at the phone breakpoint.
Six deliveries — mixed status (200 / 200 / 200 / 429 / 500 / 204)
.webhook-consoleA read-only .webhook-console with six recent deliveries newest-first: three healthy 2xx responses (Slack lead.created, Stripe payment.succeeded, Zapier task.completed), one 429 rate-limit retry on the same Zapier endpoint (.is-4xx), one 500 server failure on Slack message.received (.is-5xx, 5012ms duration showing the slow timeout-then-fail path), and one 204 no-content on a Notion page.updated webhook. Status codes carry the colour-coded modifier; rows are static <div>s since no click handler is wired in the demo.
<div class="webhook-console">
<div class="webhook-console-row is-header" role="row">
<span class="webhook-console-timestamp">Time</span>
<span class="webhook-console-endpoint">Endpoint</span>
<span class="webhook-console-event">Event</span>
<span class="webhook-console-status">Status</span>
<span class="webhook-console-duration">Duration</span>
</div>
<div class="webhook-console-row">
<span class="webhook-console-timestamp">10:34:12</span>
<span class="webhook-console-endpoint">POST /webhooks/slack</span>
<span class="webhook-console-event">lead.created</span>
<span class="webhook-console-status is-2xx">200</span>
<span class="webhook-console-duration">142ms</span>
</div>
…
</div>
.webhook-console {
background: var(--bg-paper);
border: 1px solid var(--hair);
border-radius: var(--r-md);
overflow: hidden;
}
.webhook-console-row {
display: grid;
grid-template-columns: auto 1fr auto auto auto;
gap: var(--s-3);
padding: var(--s-3) var(--s-4);
border-bottom: 1px solid var(--hair);
font: 13px var(--f-mono);
align-items: center;
text-align: left;
width: 100%;
background: transparent;
}
.webhook-console-row:last-child { border-bottom: 0; }
button.webhook-console-row { border-left: 0; border-right: 0; border-top: 0; color: inherit; cursor: pointer; }
button.webhook-console-row:hover { background: var(--bg-sunk); }
.webhook-console-row.is-header {
background: var(--bg-sunk);
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--fg-faint);
}
.webhook-console-timestamp { color: var(--fg-dim); white-space: nowrap; }
.webhook-console-endpoint { color: var(--fg); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.webhook-console-event { color: var(--accent-text); }
.webhook-console-status { font-weight: 600; }
.webhook-console-status.is-2xx { color: var(--success-text); }
.webhook-console-status.is-4xx { color: var(--warning-text); }
.webhook-console-status.is-5xx { color: var(--error-text); }
.webhook-console-duration { color: var(--fg-dim); }
@media (max-width: 480px) {
.webhook-console-row { grid-template-columns: 1fr auto; grid-template-rows: auto auto; }
.webhook-console-event { grid-column: 1 / 3; grid-row: 2; font-size: 11px; }
.webhook-console-duration { display: none; }
}
import { WebhookConsole } from "@magicblocksai/ui";
<WebhookConsole
deliveries={[
{ id: "d1", timestamp: "10:34:12", endpoint: "POST /webhooks/slack",
eventType: "lead.created", statusCode: 200, durationMs: 142 },
{ id: "d2", timestamp: "10:31:54", endpoint: "POST /webhooks/stripe",
eventType: "payment.succeeded", statusCode: 200, durationMs: 89 },
{ id: "d3", timestamp: "10:28:22", endpoint: "POST /webhooks/zapier",
eventType: "task.completed", statusCode: 200, durationMs: 234 },
{ id: "d4", timestamp: "10:24:08", endpoint: "POST /webhooks/zapier",
eventType: "task.completed", statusCode: 429, durationMs: 0 },
{ id: "d5", timestamp: "10:18:43", endpoint: "POST /webhooks/slack",
eventType: "message.received", statusCode: 500, durationMs: 5012 },
{ id: "d6", timestamp: "10:12:01", endpoint: "POST /webhooks/notion",
eventType: "page.updated", statusCode: 204, durationMs: 67 },
]}
/>
24.4 OAuth callback banner
A page-level banner that confirms the outcome of an OAuth round-trip — tick + green on success, cross + red on failure, clock + amber on pending. The intent is carried on the root <aside> via data-intent, which drives both the colour pair (success → --success-*, failure → --error-*, pending → --warning-*) and the leading icon glyph. The actions slot lives to the right of the body block on desktop; at the phone breakpoint the banner stacks vertically and the slot stretches to full width so two buttons sit comfortably below the title. .oauth-callback-stack is the canonical wrapper for showing two or three banners in sequence (e.g. queueing OAuth outcomes that arrived in the same session).
Three banners stacked — success / failure / pending
.oauth-callback-bannerThree .oauth-callback-banners wrapped in .oauth-callback-stack, one per intent. Success demos a freshly-linked Slack workspace with a "View setup" link action; failure demos a rejected Stripe OAuth token with a "Retry" + "Contact support" action pair; pending demos a verification-email round-trip awaiting confirmation, no actions. Each banner carries its own inline SVG glyph (tick / cross / clock) keeping the demo deterministic without depending on the kit’s icon barrel.
<div class="oauth-callback-stack">
<aside class="oauth-callback-banner" data-intent="success">
<span class="oauth-callback-banner-icon">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6">
<circle cx="10" cy="10" r="8"/>
<path d="M6.5 10.5L9 13L14 7.5"/>
</svg>
</span>
<div class="oauth-callback-banner-text">
<span class="oauth-callback-banner-title">Slack connected</span>
<span class="oauth-callback-banner-desc">We’ve linked your workspace…</span>
</div>
<div class="oauth-callback-banner-actions">
<a class="btn btn-ghost" href="#">View setup</a>
</div>
</aside>
<aside class="oauth-callback-banner" data-intent="failure">…</aside>
<aside class="oauth-callback-banner" data-intent="pending">…</aside>
</div>
.oauth-callback-banner {
display: flex;
gap: var(--s-3);
padding: var(--s-3) var(--s-4);
border-radius: var(--r-md);
border: 1px solid;
align-items: flex-start;
}
.oauth-callback-banner[data-intent="success"] {
background: var(--success-soft);
border-color: var(--success);
color: var(--success-text);
}
.oauth-callback-banner[data-intent="failure"] {
background: var(--error-soft);
border-color: var(--error);
color: var(--error-text);
}
.oauth-callback-banner[data-intent="pending"] {
background: var(--warning-soft);
border-color: var(--warning);
color: var(--warning-text);
}
.oauth-callback-banner-icon {
width: 20px;
height: 20px;
flex-shrink: 0;
}
.oauth-callback-banner-text {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 2px;
}
.oauth-callback-banner-title { font: 600 14px/1.3 var(--f-body); }
.oauth-callback-banner-desc { font: 400 13px/1.5 var(--f-body); opacity: 0.85; }
.oauth-callback-banner-actions {
flex-shrink: 0;
display: flex;
gap: var(--s-2);
}
@media (max-width: 480px) {
.oauth-callback-banner { flex-direction: column; }
.oauth-callback-banner-actions { width: 100%; }
}
.oauth-callback-stack {
display: flex;
flex-direction: column;
gap: var(--s-3);
}
import { OAuthCallbackBanner } from "@magicblocksai/ui";
<div className="oauth-callback-stack">
<OAuthCallbackBanner
intent="success"
title="Slack connected"
description="We've linked your workspace. Webhooks will start firing in 30 seconds."
actions={<a className="btn btn-ghost" href="/settings/integrations/slack">View setup</a>}
/>
<OAuthCallbackBanner
intent="failure"
title="Connection failed"
description="Stripe rejected the OAuth token. Check your API credentials and try again."
actions={<>
<button type="button" className="btn btn-ghost" onClick={retry}>Retry</button>
<a className="btn btn-ghost" href="/support">Contact support</a>
</>}
/>
<OAuthCallbackBanner
intent="pending"
title="Awaiting confirmation"
description="We've sent a verification email to admin@acme.com. Click the link to finish the connection."
/>
</div>
24.5 Integrations page
A page-shaped wrapper composing the chapter’s four primitives into the canonical integrations-page layout: an optional <OAuthCallbackBanner> on top, then the <IntegrationsGrid> wrapping a flat list of <IntegrationCard> children, then the <WebhookConsole> beneath. The two lower sections each carry a small uppercase sub-header (.integrations-page-section-label) styled as a 16px var(--f-display) 600-weight label in var(--fg-soft); the banner section is unlabelled so the freshly-arrived OAuth outcome reads as the page’s lede. The wrapper is stateless glue — <IntegrationsGrid> owns its filter-chip state, <WebhookConsole> wires the consumer’s onDeliveryClick when interactivity is needed, and each composed primitive carries its own mobile reflow so the page surface reflows naturally at every breakpoint.
Realistic integrations page — banner + 6 cards + 4 deliveries
.integrations-pageA realistic post-OAuth view: a success banner confirms a freshly-linked Slack workspace, the integrations grid surfaces the six canonical states from 26.1 with the four category chips from 26.2 above, and the webhook-delivery console below shows four recent deliveries (three healthy 2xx responses + one 429 rate-limit retry) drawn from the section 26.3 dataset. The page wrapper just stacks the three sections; everything inside each section delegates to the primitive from 26.1–26.4.
Integrations
Send deal updates and pipeline notifications to a channel.
Open pull requests and surface review status on deal cards.
Pull invoice and subscription data into the billing tab.
Mirror meeting notes and project briefs into linked records.
Two-way sync issues to deals — awaiting OAuth confirmation.
Embed design file previews and version links on records.
Webhook deliveries
<div class="integrations-page">
<aside class="oauth-callback-banner" data-intent="success">…</aside>
<section class="integrations-page-section">
<h2 class="integrations-page-section-label">Integrations</h2>
<div class="integrations-grid">
<div class="integrations-grid-filters" role="group">…</div>
<div class="integrations-grid-list">
<div class="integration-card">…</div>
…
</div>
</div>
</section>
<section class="integrations-page-section">
<h2 class="integrations-page-section-label">Webhook deliveries</h2>
<div class="webhook-console">…</div>
</section>
</div>
.integrations-page {
display: flex;
flex-direction: column;
gap: var(--s-8);
}
.integrations-page-section {
display: flex;
flex-direction: column;
gap: var(--s-4);
}
.integrations-page-section-label {
margin: 0;
font: 600 16px/1.2 var(--f-display);
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--fg-soft);
}
import { IntegrationsPage } from "@magicblocksai/ui";
<IntegrationsPage
banner={{
intent: "success",
title: "Slack connected",
description: "We've linked your workspace. Webhooks will start firing in 30 seconds.",
actions: <a className="btn btn-ghost" href="/settings/integrations/slack">View setup</a>,
}}
categories={["All", "Productivity", "Developer", "Payments"]}
integrations={[
{ logo: <SlackLogo />, name: "Slack",
description: "Send deal updates and pipeline notifications to a channel.",
status: "connected", category: "Productivity",
action: <button type="button" className="btn btn-ghost">Manage</button> },
{ logo: <GitHubLogo />, name: "GitHub",
description: "Open pull requests and surface review status on deal cards.",
status: "connected", category: "Developer",
action: <button type="button" className="btn btn-ghost">Manage</button> },
{ logo: <StripeLogo />, name: "Stripe",
description: "Pull invoice and subscription data into the billing tab.",
status: "error", category: "Payments",
action: <button type="button" className="btn btn-ghost">Retry</button> },
{ logo: <NotionLogo />, name: "Notion",
description: "Mirror meeting notes and project briefs into linked records.",
status: "disconnected", category: "Productivity",
action: <button type="button" className="btn btn-primary">Connect</button> },
{ logo: <LinearLogo />, name: "Linear",
description: "Two-way sync issues to deals — awaiting OAuth confirmation.",
status: "pending", category: "Developer",
action: <button type="button" className="btn btn-ghost">Cancel</button> },
{ logo: <FigmaLogo />, name: "Figma",
description: "Embed design file previews and version links on records.",
status: "disconnected", category: "Design",
action: <button type="button" className="btn btn-primary">Connect</button> },
]}
webhookDeliveries={[
{ id: "d1", timestamp: "10:34:12", endpoint: "POST /webhooks/slack",
eventType: "lead.created", statusCode: 200, durationMs: 142 },
{ id: "d2", timestamp: "10:31:54", endpoint: "POST /webhooks/stripe",
eventType: "payment.succeeded", statusCode: 200, durationMs: 89 },
{ id: "d3", timestamp: "10:28:22", endpoint: "POST /webhooks/zapier",
eventType: "task.completed", statusCode: 200, durationMs: 234 },
{ id: "d4", timestamp: "10:24:08", endpoint: "POST /webhooks/zapier",
eventType: "task.completed", statusCode: 429, durationMs: 0 },
]}
/>