This catalog is the source-facing reference for shared UI under src/components/. It documents current TypeScript props, accessibility contracts, styling ownership, and the --credence-* design tokens each component consumes. Keep this page in sync whenever component props or CSS tokens change.
Related focused docs: button system, notifications, design tokens, dark mode, focus patterns, UI states, TrustGauge quick reference, and tier thresholds.
Per-route SEO metadata — Use the
useSeohook (documented in HOOKS.md) to setdocument.titleand<meta name="description">on a per-route basis. Every route-level page component should calluseSeowith a descriptivedescriptionso search engines and social-card scrapers receive page-specific context rather than the static fallback inindex.html.
| Component | Styling owner | Inline-style migration note |
|---|---|---|
| Button | src/components/Button.css |
None. |
| Badge | src/components/Badge.css |
None. |
| Banner | src/components/Banner.css |
None. |
| Toast / ToastProvider | src/components/Toast.css |
None. |
| ConfirmDialog | src/components/ConfirmDialog.css |
None. |
| AddressInput | src/components/AddressInput.css + FormField.css |
None. |
| AmountInput | src/components/AmountInput.css |
None. |
| TrustGauge | src/components/TrustGauge.css |
Uses inline CSS custom properties for dynamic progress, marker, thumb, and legend-dot colors; keep scoped until migrated. |
| TierLadder | src/components/TierLadder.css + Badge.css |
None. |
| ActivityTimeline | src/components/ActivityTimeline.css + EmptyState inline styles for empty fallback |
Empty fallback inherits EmptyState inline styles; migrate with states components. |
| FormField | src/components/forms/FormField.css |
None. |
| controls/Select | src/components/controls/controls.css |
None. |
| controls/Toggle | src/components/controls/controls.css |
None. |
| states/EmptyState | Inline styles in src/components/states/EmptyState.tsx |
Owns inline styles and should be migrated to CSS. |
| states/ErrorState | Inline styles in src/components/states/ErrorState.tsx |
Owns inline styles and should be migrated to CSS. |
| states/LoadingSkeleton | Inline styles in src/components/states/LoadingSkeleton.tsx |
Owns inline styles and should be migrated to CSS. |
| SessionTimeoutModal | Inline styles in src/components/SessionTimeoutModal.tsx |
Uses ConfirmDialog primitive with internal warning styles. |
Source: Badge.tsx
'bronze' | 'silver' | 'gold' | 'platinum' | 'active' | 'locked' | 'slashed' | 'grace-period' | 'unknown'
Unknown runtime strings normalize to the unknown visual style while preserving the supplied string as a fallback label only when no known label exists.
Source: Banner.tsx
'info' | 'success' | 'warning' | 'critical'
warning and critical render urgent role="alert"; info and success render role="status".
Source: Toast.tsx and ToastProvider.tsx
'info' | 'success' | 'warning' | 'danger'
Default auto-dismiss timeouts are 5s for info and success, 8s for warning, and persistent for danger unless settings override auto-dismiss.
Source: TrustGauge.tsx
| Tier | Range | Label | Tokens referenced by config |
|---|---|---|---|
bronze |
0-250 | Bronze | --credence-color-bronze-border, --credence-color-bronze-surface, --credence-color-bronze-text |
silver |
250-500 | Silver | --credence-color-silver-border, --credence-color-silver-surface, --credence-color-silver-text |
gold |
500-750 | Gold | --credence-color-gold-border, --credence-color-gold-surface, --credence-color-gold-text |
platinum |
750-1000 | Platinum | --credence-color-platinum-border, --credence-color-platinum-surface, --credence-color-platinum-text |
Source: src/components/Button.tsx. Focused docs: button system.
| Prop | Type | Default |
|---|---|---|
variant |
'primary' | 'secondary' | 'ghost' | 'danger' |
'primary' |
isLoading |
boolean |
false |
fullWidth |
boolean |
false |
children |
ReactNode |
Required |
| Native button props | ButtonHTMLAttributes<HTMLButtonElement> |
Forwarded; type defaults to 'button' |
Accessibility: renders a native <button>, disables interaction while disabled or isLoading, sets aria-busy for loading state, hides spinner SVG from assistive tech, and inherits keyboard activation/focus behavior from the platform. Primary CTAs (variant="primary") automatically receive data-testid="primary-cta" for test stability, unless overridden via props.
Tokens: --credence-border-default, --credence-color-danger-*, --credence-color-info-surface, --credence-color-primary*, --credence-color-slate-*, --credence-color-white, --credence-focus-ring, font, line-height, radius, spacing, surface, and text tokens.
<Button variant="primary" isLoading={isSaving} onClick={saveBond}>
Save bond
</Button>Source: src/components/Badge.tsx. Contrast audit: badge-contrast-audit.md.
| Prop | Type | Default |
|---|---|---|
variant |
BadgeVariant | string |
Required |
label |
string |
Known variant label |
className |
string |
'' |
srPrefix |
string |
— |
srPrefix renders an .sr-only <span> before the visible label so assistive technology can announce the badge in context (e.g. srPrefix="Bond status:" causes a screen reader to read "Bond status: Slashed" rather than just "Slashed"). No extra DOM is inserted when the prop is omitted.
Accessibility: renders text in a <span> with a title attribute matching the display label (provides a tooltip on truncation). Status badges (slashed, grace-period, locked) carry safety-relevant meaning — the visible label is always non-empty so meaning is never communicated by color alone. Use srPrefix when a badge appears inside a list row or table cell where a screen reader needs additional context to interpret the label.
Tokens: tier/status color tokens, --credence-font-size-xs, --credence-font-weight-semibold, --credence-radius-full, --credence-space-2.
<Badge variant="gold" />
<Badge variant="grace-period" label="Grace" />
<Badge variant="slashed" srPrefix="Bond status:" />Source: src/components/Banner.tsx. Focused docs: notifications.
| Prop | Type | Default |
|---|---|---|
severity |
BannerSeverity |
Required |
children |
ReactNode |
Required |
title |
string |
undefined |
dismissible |
boolean |
undefined |
onDismiss |
() => void |
undefined |
action |
{ label: string; href?: string; onClick?: () => void } |
undefined |
returnFocusRef |
React.RefObject<HTMLElement> |
document.body fallback |
Accessibility: severity maps to role="alert" for warning/critical and role="status" for info/success. The root has an aria label such as “Warning banner”. Dismiss buttons have aria-label="Dismiss banner", support Escape while focused, and return focus to returnFocusRef or document.body after dismissal. Icons are aria-hidden.
Tokens: motion duration/easing tokens in CSS; severity color styling is component-owned CSS values and should be reviewed during token migrations.
<Banner severity="warning" title="Review required" dismissible onDismiss={closeBanner}>
Your bond evidence needs one more attestation.
</Banner>Sources: src/components/Toast.tsx, src/components/ToastProvider.tsx. Focused docs: notifications.
| Prop | Type | Default |
|---|---|---|
toast |
{ id: string; severity: ToastSeverity; message: string } |
Required |
onDismiss |
(id: string) => void |
Required |
| API | Type | Default |
|---|---|---|
children prop |
ReactNode |
Required |
useToast().addToast |
(severity: ToastSeverity, message: string) => void |
Context value |
useToast().removeToast |
(id: string) => void |
Context value |
useToast().removeAllToasts |
() => void |
Context value |
Accessibility: individual danger toasts use role="alert"; other severities use role="status". Provider separates polite notifications into aria-live="polite" and danger notifications into aria-live="assertive" regions, each with a region label. Dismiss buttons have severity-specific accessible names.
Tokens: --credence-font-size-*, --credence-line-height-base, motion duration/easing, --credence-radius-md, --credence-shadow-toast, spacing, --credence-surface-card, --credence-text-primary.
function SaveButton() {
const { addToast } = useToast()
return <Button onClick={() => addToast('success', 'Bond saved')}>Save</Button>
}Source: src/components/ConfirmDialog.tsx. Focused docs: focus patterns.
| Prop | Type | Default |
|---|---|---|
open |
boolean |
Required |
title |
string |
Required |
subtitle |
string |
undefined |
breakdown |
{ bondAmount: string; penaltyAmount: string; penaltyPercent: number; resultingBalance: string } |
Required |
onConfirm |
() => void |
Required |
onCancel |
() => void |
Required |
returnFocusRef |
RefObject<HTMLElement | null> |
undefined |
confirmLabel |
string |
'Withdraw bond' |
Accessibility: renders in a portal with role="dialog", aria-modal="true", generated aria-labelledby/aria-describedby, focus trap, initial focus on Cancel, Escape and backdrop cancellation, body scroll lock, and optional focus restoration. The destructive action is disabled until the user types CONFIRM; assertive sr-only announcements describe state changes.
Tokens: danger color tokens, font family/size/weight, line-height, motion, radius, spacing, surface, and text tokens.
<ConfirmDialog
open={isOpen}
title="Withdraw bond?"
breakdown={{
bondAmount: '100 USDC',
penaltyAmount: '5 USDC',
penaltyPercent: 5,
resultingBalance: '95 USDC',
}}
onConfirm={withdraw}
onCancel={close}
/>Source: src/components/AddressInput.tsx.
| Prop | Type | Default |
|---|---|---|
id |
string |
Required |
label |
string |
'Stellar Address' |
value |
string |
Required |
onChange |
(value: string) => void |
Required |
onValidationChange |
(isValid: boolean) => void |
undefined |
disabled |
boolean |
false |
className |
string |
'' |
Accessibility: composes FormField, so label, hint, and error IDs wire through htmlFor, aria-describedby, and aria-invalid. Paste and copy controls are native buttons with explicit aria labels and hidden SVGs. Validation requires a 56-character Stellar public key starting with G; invalid feedback is exposed by the FormField alert.
Tokens: border, danger, primary, slate, success, focus, font, line-height, motion, radius, spacing, surface, and text tokens.
<AddressInput
id="recipient"
value={address}
onChange={setAddress}
onValidationChange={setAddressValid}
/>Source: src/components/AmountInput.tsx. Focused docs: USDC amount input.
| Prop | Type | Default |
|---|---|---|
value |
string |
Required |
onChange |
(value: string) => void |
Required |
balance |
number |
Required |
presets |
number[] |
[100, 500, 1000] |
currencyLabel |
string |
'USDC' |
error |
string |
undefined |
| Native input props | Omit<InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'inputMode'> |
Forwarded |
Accessibility: uses a native input with inputMode="decimal", disables browser autocomplete, exposes invalid state when error or aria-invalid="true" is supplied, hides the currency adornment, and gives Max/preset buttons descriptive aria labels. Presets above balance and Max at zero balance are disabled.
Tokens: border, danger-border, slate, focus, font, motion, radius, spacing, surface, and text tokens.
<AmountInput value={amount} onChange={setAmount} balance={availableUsdc} error={amountError} />Source: src/components/TrustGauge.tsx. Focused docs: TrustGauge quick reference, accessibility report.
| Prop | Type | Default |
|---|---|---|
score |
number |
Required |
tier |
'bronze' | 'silver' | 'gold' | 'platinum' |
Required |
className |
string |
'' |
id |
string |
'trust-gauge' |
Accessibility: includes visible heading/description, a role="progressbar" with aria-valuenow, aria-valuemin="0", aria-valuemax="1000", and an aria label summarizing score and tier. Decorative fills, thumb, and legend dots are presentational or aria-hidden.
Tokens: tier color tokens, --credence-color-primary, slate, focus, font, line-height, motion, radius, and spacing tokens. Dynamic inline CSS custom properties set progress width, marker position, thumb position, and legend-dot color.
<TrustGauge score={640} tier="gold" />Source: src/components/TierLadder.tsx. Focused docs: tier thresholds.
| Prop | Type | Default |
|---|---|---|
className |
string |
'' |
defaultOpen |
boolean |
false |
Accessibility: root section is labelled by an sr-only heading. Trigger is a native button with aria-expanded and aria-controls; the panel uses hidden when collapsed. Decorative rail and chevron are aria-hidden. Tier content is structured as an ordered list with nested headings and benefit lists.
Tokens: border, tier color tokens, slate, focus, font, line-height, motion, radius, spacing, surface, and text tokens.
<TierLadder defaultOpen />Source: src/components/ActivityTimeline.tsx. Focused docs: activity surface concept.
| Prop | Type | Default |
|---|---|---|
compact |
boolean |
false |
items |
ActivityItem[] |
Built-in sample events |
ActivityItem is { id: string; timestamp: string; title: string; description: string; actor: string; statusLabel: string; tone: 'success' | 'warning' | 'info'; meta: string }.
Accessibility: renders a labelled section and a labelled timeline list. Decorative rails/nodes are aria-hidden. Empty data delegates to EmptyState with activity illustration and explanatory copy.
Tokens: border, info/success/warning color tokens, primary, font, line-height, radius, spacing, surface, and text tokens.
<ActivityTimeline compact items={events} />Source: src/components/forms/FormField.tsx.
| Prop | Type | Default |
|---|---|---|
id |
string |
Required |
label |
string |
Required |
hint |
string |
undefined |
error |
string |
undefined |
srOnlyLabel |
boolean |
false |
children |
React.ReactElement |
Required |
Accessibility: renders a <label htmlFor={id}>, optional hint, clones the child to inject id, merged aria-describedby, and aria-invalid when an error exists. Error text has role="alert". Set srOnlyLabel when the visible UI relies on a placeholder or icon-only affordance but a programmatic label is still required for assistive technology.
Tokens: --credence-color-danger-text, --credence-font-size-sm, --credence-font-weight-semibold, --credence-space-2, --credence-text-secondary.
<FormField id="amount" label="Bond amount" hint="Enter USDC" error={error}>
<input value={amount} onChange={handleAmountChange} />
</FormField>Placeholder-only layouts should still expose an accessible name:
<FormField id="search" label="Search attestations" srOnlyLabel>
<input placeholder="Search attestations…" />
</FormField>Source: src/components/controls/Select.tsx.
| Prop | Type | Default |
|---|---|---|
id |
string |
undefined |
value |
string |
Required |
onChange |
(v: string) => void |
Required |
options |
{ value: string; label: string }[] |
Required |
ariaLabel |
string |
undefined |
Accessibility: renders a native <select> with optional id and aria-label; pair with a visible <label> via id when possible. Native keyboard behavior is preserved.
Tokens: shared control CSS consumes border, primary, white, focus, font, line-height, motion, radius, spacing, surface, and text tokens.
<Select
id="tier-filter"
value={tier}
onChange={setTier}
ariaLabel="Filter by tier"
options={[{ value: 'gold', label: 'Gold' }]}
/>Source: src/components/controls/Toggle.tsx.
| Prop | Type | Default |
|---|---|---|
id |
string |
undefined |
checked |
boolean |
Required |
onChange |
(next: boolean) => void |
Required |
ariaLabel |
string |
undefined |
Accessibility: renders a native button with role="switch" and aria-checked; label it with ariaLabel or external labelling. Click toggles state; Space/Enter activation comes from button semantics.
Tokens: shared control CSS consumes border, primary, white, focus, font, line-height, motion, radius, spacing, surface, and text tokens.
<Toggle checked={toastsEnabled} onChange={setToastsEnabled} ariaLabel="Enable notifications" />Source: src/components/states/EmptyState.tsx. Focused docs: UI states guide, zero states copy.
| Prop | Type | Default |
|---|---|---|
icon |
ReactNode |
undefined |
title |
string |
Required |
description |
string |
Required |
action |
{ label: string; onClick: () => void; variant?: 'primary' | 'secondary' } |
undefined |
illustration |
'bond' | 'trust' | 'dispute' | 'attestation' | 'activity' |
undefined |
Accessibility: illustration SVGs are aria-hidden; the accessible name comes from the visible title and description. Optional action is a native button. There is no landmark role; place inside a labelled region when context is needed.
Tokens: inline styles consume illustration color tokens, radius, spacing, font, line-height, text, border, primary, and white tokens. Inline styles are flagged for migration.
<EmptyState
title="No bonds yet"
description="Create a bond to start earning trust."
action={{ label: 'Create bond', onClick: start }}
/>Source: src/components/states/ErrorState.tsx. Focused docs: UI states guide.
| Prop | Type | Default |
|---|---|---|
type |
'network' | 'backend' | 'validation' | 'generic' |
'generic' |
title |
string |
Type-specific title |
message |
string |
Type-specific message |
action |
{ label: string; onClick: () => void } |
undefined |
icon |
ReactNode |
Type-specific emoji |
Accessibility: visible title/message describe the error; optional action is a native button. No role="alert" is set, so add surrounding live-region behavior when errors are asynchronous and need announcement.
Tokens: inline styles consume danger surface/text/action, white, radius, spacing, font, line-height, and border tokens. Inline styles are flagged for migration.
<ErrorState type="network" action={{ label: 'Retry', onClick: refetch }} />Source: src/components/states/LoadingSkeleton.tsx. Focused docs: UI states guide.
| Prop | Type | Default |
|---|---|---|
variant |
'text' | 'card' | 'form' | 'table' | 'dashboard' |
'text' |
rows |
number |
3 |
width |
string |
'100%' |
height |
string |
undefined |
Accessibility: purely visual placeholder with no aria attributes. Pair with aria-busy, status text, or route-level loading announcements when loading state needs to be exposed to assistive technologies.
Tokens: inline styles consume --credence-skeleton-gradient, --credence-motion-skeleton, border, radius, spacing, and layout values. Inline styles are flagged for migration.
<LoadingSkeleton variant="card" rows={2} />Source: src/components/SessionTimeoutModal.tsx.
| Prop | Type | Default |
|---|---|---|
open |
boolean |
Required |
onStayLoggedIn |
() => void |
Required |
onLogout |
() => void |
Required |
timeLeftSeconds |
number |
Required |
Accessibility: uses ConfirmDialog primitive.
Tokens: warning color tokens, spacing, radius.
<SessionTimeoutModal
open={showWarning}
timeLeftSeconds={60}
onStayLoggedIn={stay}
onLogout={logout}
/>