OptionGrantCard
Hero example
When to use
Reach for OptionGrantCard when a Grant resource is in draft state and needs board approval. The card is the canonical surface for "here's the proposed equity grant — does this look right?" rendered either as an agent reply in the composer (after parsing an employment-letter request), inside the dashboard's equity inbox, or on the grant-detail page header before the resolution is signed. A tier_3 agent can propose the grant; a human or tier_4 agent approves it via the footer action.
Don't reach for OptionGrantCard when the grant is already approved and live on the cap table — that state belongs in a row of CapTableSnapshotCard, not its own card. Don't use it for warrant grants either; those carry different vesting and conversion mechanics that the four-year-cliff vesting bar mis-represents.
Anatomy
Six structural regions stacked top-to-bottom:
- Header.
Bolticon + "Option Grant — Draft" + right-aligned grant ID in mono (defaults toOPT-2026-014placeholder). - Recipient block. 36px circular avatar (auto-derived from recipient initials) + name + role.
- Stat grid. Three-column grid:
Shares,Strike,Plan. Shares and Strike render large (22px, tabular numerals), Plan renders smaller (14px) since plan names are text. - Vesting summary line. "4-year vest · 1-year cliff · monthly thereafter" +
Start {vestStart}mono right-aligned. - Vesting bar. 6px tall pill; 25% fill in
--action-createto indicate the cliff; vertical 2px tick in--gold-warmat the 25% boundary. Axis labels (Yr 0 → Yr 4) read in mono below. - Footer. Primary
Approve grant+ secondariesAdjustandOpen in Equity.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| grantId | string | "OPT-2026-014" | — |
| recipientrequired | string | — | — |
| rolerequired | string | — | — |
| sharesrequired | string | — | — |
| strikerequired | string | — | — |
| planrequired | string | — | — |
| vestStartrequired | string | — | — |
States
The card is single-state by design — it represents the draft proposal. Approval and revocation flip the surrounding context (the card is replaced by a CapTableSnapshotCard row or a revocation receipt), not the card's own state.
| State | Trigger | Visual change |
|---|---|---|
| Draft (default) | Grant.state = "draft" | As rendered above. |
| Skeleton | Grant data still resolving | Use CardSkeleton — do not render OptionGrantCard with placeholder strings. |
Density
In compact, the stat-grid gap drops from 18px to 12px and stat-value font size from 22px to 18px. The vesting bar stays 6px tall. The footer keeps full touch targets.
Themes
In dark, the vesting-bar track reads --ink-6 (which adapts to the dark surface) and the fill stays in the green-action family at adjusted lightness. Forced-colors maps the bar fill to Highlight and the cliff tick to ButtonText.
Tokens consumed
Vesting-bar fill reads --action-create. Cliff marker reads --gold-warm. Stat values inherit --fg; labels inherit --fg-soft. Avatar background reads --ink-6.
Accessibility
- Keyboard interactions. Tab order:
Approve grant→Adjust→Open in Equity. Enter / Space activate the focused button. - ARIA roles and properties. The vesting bar is
aria-hidden="true"— the textual vesting summary above it is the accessible description. The avatar isaria-hidden(decorative); the recipient name is the accessible identity. - Focus order. Header and stat grid are not focusable. Footer is the only focus surface.
- Screen-reader expectations. Reading order: "Option Grant — Draft,
{grant ID}", then "{recipient},{role}", then "Shares{value}", "Strike{value}", "Plan{value}", then "4-year vest, 1-year cliff, monthly thereafter, Start{date}", then footer buttons. - Reduced motion. No keyframes inside the card.
- Forced colors. Vesting bar collapses to a system-Highlight progress indicator. Avatar fill maps to
Highlight. - WIG rules. Tabular numerals via
font-variant-numeric: tabular-numson stat values (per WIG typography). Avatar carriesaria-hidden(decorative-icon rule). Hover lift on the footer respectsprefers-reduced-motion.
Do / Don't
shares and strike as pre-formatted strings ("80,000" / "$0.42"). The card renders them verbatim in tabular numerals.grantId from the API's Grant.id so it round-trips with the underlying resource.plan short ("2024 EIP"). It renders smaller than shares/strike for exactly this reason.Recipes
This card appears in:
- Equity recipe — the canonical recipe; drafted grants surface on the equity dashboard for board review.
- Docs corpus recipe — when an agent has parsed an employment-letter offer and is staging the grant.
Code example
import { OptionGrantCard } from "@matter/components";
export function DraftedGrantInline({ grant }: { grant: Grant }) {
return (
<OptionGrantCard
grantId={grant.id}
recipient={grant.recipient.full_name}
role={grant.recipient.title}
shares={grant.shares_authorized.toLocaleString()}
strike={`$${grant.strike_price_usd.toFixed(2)}`}
plan={grant.equity_plan.name}
vestStart={grant.vesting_start_date_display}
/>
);
}Source
packages/components/src/InlineCard/OptionGrantCardFilingStatusCard
Read-only summary of a single compliance filing — title, status pill, jurisdiction, due date, and a checklist of sub-items. Use when the dashboard or an agent needs to surface 'where does this filing stand right now.'
CapTableSnapshotCard
Live cap-table snapshot — SVG donut showing fully-diluted ownership, a holder list with role and FDS percentage, sync timestamp, and an open-cap-table footer. Use when a single glance at ownership is the user's question.