Skip to content

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.

Do — render OptionGrantCard when an agent says "I've drafted an option grant for the new VP of Engineering — 80,000 shares at $0.42."
Don't — render OptionGrantCard for non-equity compensation (cash bonus, RSU). It's specifically for options.

Anatomy

Six structural regions stacked top-to-bottom:

  1. Header. Bolt icon + "Option Grant — Draft" + right-aligned grant ID in mono (defaults to OPT-2026-014 placeholder).
  2. Recipient block. 36px circular avatar (auto-derived from recipient initials) + name + role.
  3. 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.
  4. Vesting summary line. "4-year vest · 1-year cliff · monthly thereafter" + Start {vestStart} mono right-aligned.
  5. Vesting bar. 6px tall pill; 25% fill in --action-create to indicate the cliff; vertical 2px tick in --gold-warm at the 25% boundary. Axis labels (Yr 0 → Yr 4) read in mono below.
  6. Footer. Primary Approve grant + secondaries Adjust and Open in Equity.

Props

PropTypeDefaultDescription
grantIdstring"OPT-2026-014"
recipientrequiredstring
rolerequiredstring
sharesrequiredstring
strikerequiredstring
planrequiredstring
vestStartrequiredstring

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.

StateTriggerVisual change
Draft (default)Grant.state = "draft"As rendered above.
SkeletonGrant data still resolvingUse 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

No tokens match.
colors2 tokensv2.3.0
#F5F3EE
matter
src ↗
#f5f3ee
brand
src ↗
No tokens match.

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 grantAdjustOpen 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 is aria-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-nums on stat values (per WIG typography). Avatar carries aria-hidden (decorative-icon rule). Hover lift on the footer respects prefers-reduced-motion.

Do / Don't

Do — pass shares and strike as pre-formatted strings ("80,000" / "$0.42"). The card renders them verbatim in tabular numerals.
Don't — pass raw numbers. The card has no locale-aware number formatter — that's the consumer's job.
Do — derive grantId from the API's Grant.id so it round-trips with the underlying resource.
Don't — synthesize a grant ID at the UI layer. The placeholder default is for the design site preview only.
Do — keep plan short ("2024 EIP"). It renders smaller than shares/strike for exactly this reason.
Don't — render the card for non-standard vesting schedules. The bar assumes 4-year, 1-year cliff, monthly. Use a custom layout for backloaded or milestone vesting.

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/OptionGrantCard

On this page