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

23 PRs merged
4 agents in flight
0 outages 24h

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.

New service

lowercase, dashes only

one line

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).

distill live

video-to-prose pipeline · 3 services

warp 5 behind

agent work queue · 1 service

buzzy archived

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.

warp deployed 5h ago v0.18.521
ai-frontend 3 PRs behind drift
distill-staging last deploy failed red
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.