Skip to content

BoardConsentCard

Hero example

When to use

Reach for BoardConsentCard when a Resolution of type board_written_consent is in partially_signed state and the founder, board secretary, or an agent on a tier_3 token needs to see signing progress at a glance — who has signed, who hasn't, what the document is. The card belongs in three surfaces: the dashboard's board-consent inbox row, an inline reply from an agent in the composer ("Here's the status of the SAFE-authorization consent"), and the consent-detail page header.

Don't reach for BoardConsentCard when the consent is already executed (Document.execution_status: executed) — at that point the signing flow is history and the right surface is a static SignatureStack showing the audit trail, not a live tracker. Don't use it for non-board signatures either; investor side-letters and indemnification agreements have their own document state machine and belong in dedicated cards.

Do — render BoardConsentCard inline as an agent response when a tier_3 agent answers "what's the status of the next-round SAFE authorization?"
Don't — render BoardConsentCard for a fully executed consent. Use a static SignatureStack instead.

Anatomy

Five structural parts:

  1. Header. Doc icon + "Board Consent — {title}" + the awaiting-signatures Pill. The pill flips to tone="green" when every signer has signed (consumer responsibility — pass the right signers array).
  2. Signer rows. One per BoardSigner. Each row: 24px circular initials avatar (color from the signer's color prop or --fg-soft fallback), name + role stacked, signed/pending pill aligned right.
  3. Avatar. Initials only. The color prop accepts any CSS color string; if absent, the avatar falls back to --fg-soft. Avatars are decorative (aria-hidden); the signer's name carries the accessible identity.
  4. Per-signer pill. tone="green" for signed: true, tone="amber" for the pending default. Geometry matches the announcement-pill shape — 20px height, mono font, 6px dot.
  5. Footer. CardsKit footer — primary Send reminder action plus a secondary View document link.

Props

PropTypeDefaultDescription
titlerequiredstring
signersrequiredBoardSigner[]

States

The card has three semantic states driven by the signers array:

StateSigner conditionHeader pill
Partially signedSome signed: true, some signed: falseAwaiting signatures (amber)
Fully signedEvery signer signed: trueExecuted (green) — consumer flips header pill
Awaiting first signatureEvery signer signed: falseAwaiting signatures (amber)

The internal pill for each row reads directly from signer.signed — no consumer logic needed for the per-row state.

Density

In compact, signer-row padding drops from 10/14px to 6/10px and the row gap from 8px to 4px. The avatar stays 24px (touch target) and the per-row pill stays full size (legibility floor).

Themes

The card surface uses .glass-card, which composes the Glass backdrop in light and a flat translucent panel in dark. In forced-colors, the glass collapses to Canvas and the row borders inherit ButtonBorder.

Tokens consumed

colors2 tokensv2.3.0
#F5F3EE
matter
src ↗
#f5f3ee
brand
src ↗
colors17 tokensv2.3.0
#3FBF6E
matter
src ↗
#1b7a47
matter
src ↗
#E8A845
matter
src ↗
#8a5e1c
matter
src ↗
#8C8C8C
matter
src ↗
#5A5A5A
matter
src ↗
#6E8AD8
matter
src ↗
#3a5cb8
matter
src ↗
#1f8a5b
brand
src ↗
rgba(31, 138, 91, 0.1)
brand
src ↗
rgba(31, 138, 91, 0.35)
brand
src ↗
#6b7280
brand
src ↗
rgba(107, 114, 128, 0.1)
brand
src ↗
rgba(107, 114, 128, 0.3)
brand
src ↗
#b0322b
brand
src ↗
rgba(176, 50, 43, 0.1)
brand
src ↗
rgba(176, 50, 43, 0.35)
brand
src ↗

The signer-row background reads --paper-50 (or fully transparent if absent), the row border reads --ink-5, and each pill reads its tone family (--status-green-bg / --status-amber-bg and matching foregrounds).

Accessibility

  • Keyboard interactions. The primary Send reminder button is the first focusable element; View document follows. Signer rows themselves are non-interactive — Tab skips past them.
  • ARIA roles and properties. The card is a generic region; no role override. Each per-row pill uses no role and relies on its text content for the accessible name.
  • Focus order. Footer primary → footer secondary. There is no focusable element within the signer list by design — the live signing flow happens on the document detail page, not inline.
  • Screen-reader expectations. Reading order: card title, "awaiting signatures", then each row in DOM order as "name, role, signed | pending". Avatars are aria-hidden.
  • Reduced motion. No keyframes inside this card. The footer button inherits the Matter motion contract — hover lift collapses to opacity when prefers-reduced-motion: reduce.
  • Forced colors. Avatar fill maps to Highlight, pill dot to ButtonText. Glass surface collapses to Canvas.
  • WIG rules. Footer button respects icon-only aria-label, the card is a <div> (not abusing semantic elements), and the Doc icon carries aria-hidden="true" per the decorative-icon rule.

Do / Don't

Do — pass real initials (two characters max) and a color derived from a stable hash of the signer's email so the avatar is consistent across renders.
Don't — pass full names into initials. The avatar caps at 24px and clips beyond two characters.
Do — render the card inline when an agent reports "the SAFE-authorization consent has two of four signatures."
Don't — render the card with an empty signers array as a placeholder. Use a CardSkeleton until the data resolves.
Do — keep the title short (≤ 60 chars). It composes with the "Board Consent — " prefix and should fit on one line at desktop widths.
Don't — duplicate the awaiting-signatures pill in the body. The header pill is canonical.

Recipes

This card appears in:

  • Board recipe — the full board surface with pending consents, recent resolutions, and the next-meeting agenda.
  • Docs corpus recipe — when a consent is rendered inline as part of an agent reply.

Code example

import { BoardConsentCard } from "@matter/components";

export function PendingConsentInline({ resolution }: { resolution: Resolution }) {
  return (
    <BoardConsentCard
      title={resolution.title}
      signers={resolution.authorized_signers.map((s) => ({
        name: s.full_name,
        role: s.board_role,
        initials: s.initials,
        color: s.avatar_color,
        signed: s.signature_state === "signed",
      }))}
    />
  );
}

A complete worked example, including the API call that produces resolution, lives in the Board recipe.

Source

packages/components/src/InlineCard/BoardConsentCard

On this page