CapTableSnapshotCard
Hero example
When to use
Reach for CapTableSnapshotCard when the canonical question is "what does the cap table look like right now?" — surfaced as an agent reply to "show me ownership," in the equity-dashboard hero row, or as a static snapshot embedded in an investor update. The card reads from the live CapTable view; the syncedAgo prop carries the freshness signal so readers know whether the percentages reflect the most recent grant or are stale.
Don't reach for CapTableSnapshotCard for proposed (not yet approved) changes — those belong in OptionGrantCard for grants or in dedicated round-modeling surfaces for financings. Don't use it for a single-holder query either — for "what does Jane own?" the right surface is a focused detail page, not the full donut.
Anatomy
Three structural regions stacked top-to-bottom:
- Header.
Charticon + "Cap Table — Live" + right-alignedSynced {syncedAgo}(default12s ago). - Body grid (2 columns).
- Donut. 180px-wide SVG. Background ring in
--ink-5, segments colored perCapTableRow.color, centre text readsFDS/ total (default10.0M). Role on the donut is"img"witharia-label="Cap-table donut". - Holder list. One row per
CapTableRow. Row layout: 9px square swatch + name + role + right-aligned percentage in mono with tabular numerals.
- Donut. 180px-wide SVG. Background ring in
- Footer. Primary
Open Cap Table+ secondaryExport to Excel.
The donut math: total circumference 2π × 38, each segment computes its dash array from pct, segments are drawn clockwise from 12 o'clock via a -90° rotation.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| rows | CapTableRow[] | DEFAULT_ROWS | — |
| fdsLabel | string | "10.0M" | — |
| syncedAgo | string | "12s ago" | — |
The default rows set illustrates a typical seed-stage cap table (Jane, Mike, Silverstine, Goldilocks, Option Pool, Other) so the design-site preview renders meaningfully without a consumer wiring up data. In production, pass real rows derived from the CapTable view.
States
The card is single-state by design — it reflects a snapshot. The only variation is row count.
| Row count | Behaviour |
|---|---|
| 1–8 rows (typical) | Renders inline; no scroll. |
| 9+ rows | Holder list scrolls inside the card body. Donut stays full size. |
| 0 rows | Renders the default 6-row demo set. Don't ship the default to users — handle the empty case in the wrapping screen. |
Density
In compact, the body grid drops to a single column (donut above list) at viewport widths < 480px. Row vertical padding drops from 8 to 4px. The donut stays 180px (legibility floor).
Themes
In dark, the donut background ring reads --ink-5 (which adapts to the dark surface) and segment colors retain their hue. Forced-colors collapses segments to alternating Highlight / Canvas stripes — semantic colors don't survive but ordering does.
Tokens consumed
Default segment colors pull from the token surface — --action-create (founder green), --action-mutate (purple), --alert-dot (red), --status-sage (sage), --gold-warm (option pool), --ink-18 (other). Consumers can override any of them via CapTableRow.color.
Accessibility
- Keyboard interactions. Tab order:
Open Cap Table→Export to Excel. The donut and holder list are not focusable. - ARIA roles and properties. The donut SVG carries
role="img"andaria-label="Cap-table donut". Decorative swatches in the list arearia-hidden. - Focus order. Footer only.
- Screen-reader expectations. Reading order: "Cap Table — Live, Synced {time ago}", "Cap-table donut" (alt), then each holder row as "{name}, {role}, {percentage}%".
- Reduced motion. No keyframes in this card. Even if you add a mount animation, the canonical motion contract requires it to collapse to opacity under
prefers-reduced-motion: reduce. - Forced colors. Donut segments → alternating
Highlight/Canvas. Swatches →ButtonTextfilled squares. - WIG rules. Tabular numerals on percentages (WIG typography). SVG carries
role="img"andaria-label(WIG image rule). Color is never the sole differentiator — every segment has both color and a labelled list row, so colour-blind readers still get the data.
Do / Don't
rows by pct descending so the donut and list read in the same order.syncedAgo (e.g. "12s ago", "5m ago"). It's the user's freshness signal."Other (N)" rows above ~8 visible holders. The donut tail becomes illegible past that.Recipes
This card appears in:
- Equity recipe — the equity dashboard's hero card.
- Docs corpus recipe — inline agent replies about ownership.
Code example
import { CapTableSnapshotCard } from "@matter/components";
export function CapTableSnapshotInline({ entity }: { entity: Entity }) {
return (
<CapTableSnapshotCard
rows={entity.cap_table.holders.map((h) => ({
name: h.display_name,
role: h.role,
pct: h.fds_percent,
color: h.brand_color,
}))}
fdsLabel={entity.cap_table.fds_display}
syncedAgo={entity.cap_table.synced_relative}
/>
);
}Source
packages/components/src/InlineCard/CapTableSnapshotCardOptionGrantCard
Proposed option grant in draft — recipient avatar, shares/strike/plan grid, four-year vesting bar with cliff marker, approve-grant footer. Use when an agent or founder is staging an equity grant before board approval.
PlanCard
Ordered checklist of plan steps with a `completed` cursor — title, x/n progress, list of items with done / active / pending states. Use when an agent proposes a multi-step plan and the user needs to track progress as steps complete.