video-to-prose pipeline · 3 services
Patterns
Canonical compositions for the shapes you actually build.
The Guides page covers tier rules; the Components catalogue lists primitives. This page closes the loop with five canonical compositions — the patterns operators reach for when assembling a screen. Each pattern shows the live render, the annotated source, and the tokens it depends on. Copy the source; the result will match a hundred other gyrum surfaces.
Page layout
The default shape for every operator surface: hero on top, KPI strip
below, content body, optional sidebar. Composes GyOverview (template) with primitive content in the named
slots. The template owns max-width + gutter + gap; you only fill
slots.
Operator dashboard
What needs you
Three open items would unblock the distill release.
What just shipped
Recent merges across the fleet.
Source
<div style="display: flex; flex-direction: column; gap: var(--space-5);">
<h2>Operator dashboard</h2>
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem;">
<KpiCard value="23" label="PRs merged" />
<KpiCard value="4" label="agents in flight" />
<KpiCard value="0" label="outages 24h" />
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-5);">
<div>
<GySectionH><h4>What needs you</h4></GySectionH>
<p>Three open items would unblock the distill release.</p>
</div>
<div>
<GySectionH><h4>What just shipped</h4></GySectionH>
<p>Recent merges across the fleet.</p>
</div>
</div>
</div>Tokens used: --space-5 between bands, --font-size-h2 for the
hero title (template-owned in production via GyOverview). Primitives: KpiCard, GySectionH. (Production code can compose these inside GyOverview for the
max-width + gutter + named-slot contract; this pattern shows the inner
geometry without the template wrapper.)
Form
Section heading + form-field rows + footer action row. The form-field
atom carries label / hint / error in named slots so consumers don't
re-style validation copy. Submit-style buttons use GyActionButton when the action is irreversible (deploy, delete, ship) and GyButton otherwise.
Source
<form>
<GySectionH><h4>New service</h4></GySectionH>
<GyFormField label="Slug" description="lowercase, dashes only">
<input type="text" />
</GyFormField>
<GyFormField label="Intent" description="one line">
<input type="text" />
</GyFormField>
<div class="form-actions">
<GyButton variant="ghost">Cancel</GyButton>
<GyActionButton variant="primary"
confirmText="Click again to provision"
confirmMs={3000}>Provision</GyActionButton>
</div>
</form>Tokens used: --space-4 between rows, --space-3 between actions, --space-2 --space-3 for input padding. Primitives: GySectionH, GyFormField, GyButton, GyActionButton (use when the action is irreversible).
Card grid
Multiple related entities at the same level — projects, services, agents, tickets. Auto-fit grid with a minmax floor so rows fill the available width on wide viewports and stack on narrow. Each card carries its own tone (GyDot) + lifecycle indicator (GyBadge).
agent work queue · 1 service
demo product · 0 services
Source
<div class="example-grid">
<article class="example-tile">
<header>
<GyDot tone="ok" />
<strong>distill</strong>
<GyBadge variant="success">live</GyBadge>
</header>
<p>video-to-prose pipeline · 3 services</p>
</article>
<!-- repeat per entity -->
</div>
<style>
.example-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: var(--space-3);
}
.example-tile {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: var(--space-4);
}
</style>Tokens used: --surface, --border, --radius, --space-3 --space-4, --text-muted. Primitives: GyDot, GyBadge. Anti-pattern: don't render long lists as a card grid — that's the list-row pattern (use GyListRow).
Status display
Three pieces — tone dot, label, optional value chip — that appear in every operator surface that reports state (deploy status, build health, runner liveness, agent activity). The dot conveys severity at a glance; the label says what; the chip adds the count or last-event detail.
Source
<div class="status-row">
<GyDot tone="ok" />
<strong>warp</strong>
<span class="status-row__meta">deployed 5h ago</span>
<GyBadge variant="success">v0.18.521</GyBadge>
</div>
<style>
.status-row {
display: flex;
align-items: center;
gap: var(--space-2);
}
.status-row__meta {
color: var(--text-muted);
margin-left: auto;
}
</style>Tone-to-dot mapping: ok → green (deployed, healthy), warn → amber (drift, stale, behind), bad → red (failed, outage), unknown → grey (no signal yet). Pair the dot tone with the
badge tone so the two read consistently.
Stepped flow
Multi-step processes — onboarding wizards, deploy stages, decompose flows. Step indicator at the top showing progress, step body in the middle, footer-actions on the bottom. Each step's "active" / "complete" / "pending" state maps to a tone.
New service
Five steps to draft a fleet-service manifest.
Body of the active step renders here — typically a form (see the Form pattern above).
Source
<div class="stepped-flow">
<header>
<GySectionH><h4>New service</h4></GySectionH>
<p class="stepped-flow__lead">Five steps to draft a fleet-service manifest.</p>
<nav class="stepped-flow__indicator">
<GyBadge variant="success">1 Intent</GyBadge>
<span>→</span>
<GyBadge variant="neutral">2 Shape</GyBadge>
<!-- ...remaining steps... -->
</nav>
</header>
<div class="stepped-flow__body">
<!-- the active step's form / picker / review -->
</div>
<footer class="stepped-flow__actions">
<GyButton variant="ghost">Cancel</GyButton>
<GyButton variant="primary">Next →</GyButton>
</footer>
</div>Step state-to-tone mapping: success for completed steps (operator can go back), primary/accent for the active step (current focus), neutral for upcoming steps (not yet reachable), danger for a failed step (validation error, retry needed). Primitives: GySectionH, GyBadge, GyButton.
When you reach for a new pattern
These five cover most operator surfaces. When you find yourself composing something that doesn't fit any of them, that's a signal worth honouring — either you've discovered a sixth canonical pattern (open a PR adding a section here), or the primitives don't yet support the shape (open an ADR proposing a new primitive or token, per the Contributing recipe).
Don't invent a one-off composition without surfacing it. The whole point of the canon is that the next surface to build gets the benefit of every prior surface's lessons. A new pattern you don't share leaves that lesson stranded in your own component.