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.
Storybook: Components that have stories are listed with their Storybook path and variant names. Run npm run storybook (defaults to port 6006) to browse and interact with them. Components without a Storybook entry have no story file yet.
| Component | Styling owner | Inline-style migration note |
|---|---|---|
| BottomNav | src/components/navigation/BottomNav.css |
None. |
| Progress | src/components/Progress.css |
None. |
| 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. |
| ActionCard | Inline styles in src/components/ActionCard.tsx |
Owns all inline styles; migrate to a CSS file when a module is added. |
| Disclaimer | src/components/Disclaimer.css |
None. |
| ThemeToggle | src/components/ThemeToggle.css |
None. |
| Kbd | src/components/Kbd.css |
None. |
| KeyboardShortcutsDialog | src/components/KeyboardShortcutsDialog.css |
None. |
| AttestationForm | Delegates to AddressInput, Select, FormField, Button |
No dedicated CSS file; inherits from composing components. |
| CreateBondFlow | src/components/CreateBondFlow.css |
None. |
| ErrorBoundary | Delegates to states/ErrorState |
No dedicated CSS file. |
| RepoAvatar | src/components/RepoAvatar.css |
None. |
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.
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>Toggle button rendered on each dashboard widget card, allowing the user
to pin/unpin it to the top row. Pinned state persists in localStorage
under credence:pinnedWidgets (see src/config/pinnedWidgets.ts), capped
at MAX_PINNED_WIDGETS.
Props
slug: string— widget identifierisPinned: booleanonToggle: (slug: string) => void
Styling: uses --spacing-xs, --radius-sm, --color-text-secondary,
--color-bg-hover, --color-focus-ring design tokens. No hard-coded values.
Accessibility: aria-pressed reflects pin state; aria-label announces
the pin/unpin action.
Source: src/components/Badge.tsx.
| Prop | Type | Default |
|---|---|---|
variant |
BadgeVariant | string |
Required |
label |
string |
Known variant label |
className |
string |
'' |
Accessibility: renders text in a <span>; consumers should provide surrounding context when the badge alone is not descriptive.
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" />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 |
ConfirmDialogPenaltyBreakdown |
undefined |
description |
React.ReactNode |
undefined |
children |
React.ReactNode |
undefined |
onConfirm |
() => void |
Required |
onCancel |
() => void |
Required |
returnFocusRef |
RefObject<HTMLElement | null> |
undefined |
confirmLabel |
string |
'Withdraw bond' |
confirmInputLabel |
React.ReactNode |
undefined |
confirmInputHint |
React.ReactNode |
undefined |
variant |
'danger' | 'info' |
'danger' |
confirmPhrase |
string |
'CONFIRM' |
confirmHint |
string |
Wallet/funds irreversibility hint |
ConfirmDialogPenaltyBreakdown is { bondAmount: string; penaltyAmount: string; penaltyPercent: number; resultingBalance: string }. When breakdown is omitted, the description prop or children slot is rendered in its place.
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 confirm button is disabled until the user types the value of confirmPhrase (default: 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}
/>Storybook: Components/Forms/AddressInput — Default · Filled · Invalid · Disabled · Loading.
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} />Storybook: Components/Forms/AmountInput — Default · Filled · OverBalance · Error · Disabled · Loading.
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 |
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".
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>Storybook: Components/Forms/FormField — Default · WithHint · WithError · WithHintAndError.
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' }]}
/>Storybook: Components/Controls/Select — Default · Error · Disabled · Loading.
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" />Storybook: Components/Controls/Toggle — Off · On · Error · Disabled · Loading.
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}
/>Source: src/components/navigation/BottomNav.tsx.
Fixed bottom navigation bar showing the 5 primary routes. Visible on viewports ≤ BREAKPOINTS.MD (768 px); hidden at wider widths via display: none in CSS.
| Prop | Type | Default |
|---|---|---|
| (none) | — | — |
The component accepts no props. Route state is derived internally via React Router's useLocation.
Rendered markup: <nav aria-label="Bottom navigation"> containing a <ul role="list"> of 5 <li> items, each wrapping a React Router NavLink. NavLink automatically sets aria-current="page" on the active route's <a> element.
Accessibility: all tabs are native <a> elements reachable by Tab key in document order; active state is communicated both visually (primary-color border and text) and semantically (aria-current="page"); focus indicator uses var(--credence-focus-ring); touch targets are ≥ 56 px tall (44 px WCAG minimum). Transitions are suppressed under prefers-reduced-motion.
Tokens: --credence-surface-card (background), --credence-border-default (top border), --credence-color-primary (active tab indicator), --credence-text-secondary (inactive label), --credence-text-primary (hover), --credence-space-1, --credence-space-2 (padding), --credence-font-size-xs (label size), --credence-font-weight-semibold (label weight), --credence-font-weight-bold (active label), --credence-motion-duration-fast, --credence-motion-easing-standard (transitions), --credence-focus-ring.
// BottomNav is rendered by Layout — no props needed.
// It reads the current route via React Router context automatically.
<BottomNav />Source: src/components/Progress.tsx.
| Prop | Type | Default |
|---|---|---|
value |
number |
undefined |
min |
number |
0 |
max |
number |
100 |
aria-label |
string |
Required |
className |
string |
'' |
size |
'sm' | 'md' | 'lg' |
'md' |
When value is supplied the bar is determinate: aria-valuenow, aria-valuemin, and aria-valuemax are set and the fill width reflects the completion percentage. When value is omitted the bar is indeterminate: none of the aria-value* attributes are set, which signals to assistive technology that the completion amount is unknown. Values outside [min, max] are clamped silently.
Accessibility: role="progressbar" on the root; aria-label is required. The inner track and fill divs are aria-hidden. Indeterminate animation is suppressed under prefers-reduced-motion.
Tokens: --credence-color-primary (fill), --credence-color-slate-200 (track background), --credence-radius-full, --credence-space-1/2/3 (track heights), --credence-motion-duration-base, --credence-motion-duration-slow, --credence-motion-easing-standard.
{/* Determinate */}
<Progress value={60} max={100} aria-label="Bond creation: step 3 of 5" />
{/* Indeterminate */}
<Progress aria-label="Loading trust score" />Source: src/components/RepoAvatar.tsx. Config: src/config/avatar.ts.
Repository avatar component supporting tokenised sizing presets (sm, md, lg) tied directly to --credence-* design tokens, image error fallback handling, and accessible ARIA attributes.
| Prop | Type | Default | Description |
|---|---|---|---|
src |
string |
undefined |
URL for the avatar image |
name |
string |
undefined |
Repository or organization name, used for fallback initials generation |
size |
'sm' | 'md' | 'lg' |
'md' |
Preset tokenised size variant |
alt |
string |
undefined |
Image alt text override |
className |
string |
'' |
Extra CSS class names |
Accessibility: renders role="img" with descriptive aria-label. When src is missing or fails to load, falls back cleanly to uppercase initials derived from name or a default repository icon.
Tokens: --credence-avatar-size-sm, --credence-avatar-size-md, --credence-avatar-size-lg, --credence-space-6, --credence-space-8, --credence-space-12, --credence-radius-md, --credence-surface-card, --credence-border-default, --credence-text-primary, --credence-font-size-xs, --credence-font-size-sm, --credence-font-size-base.
{/* Default medium size with name fallback */}
<RepoAvatar name="CredenceOrg/Credence-Frontend" />
{/* Small size with image URL */}
<RepoAvatar size="sm" src="https://example.com/avatar.png" name="CredenceOrg/Credence-Frontend" />