Skip to content

Latest commit

 

History

History
581 lines (416 loc) · 37.9 KB

File metadata and controls

581 lines (416 loc) · 37.9 KB

Shared Components Catalog

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.

Styling ownership snapshot

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.

Shared vocabularies

BadgeVariant

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.

BannerSeverity

Source: Banner.tsx

'info' | 'success' | 'warning' | 'critical'

warning and critical render urgent role="alert"; info and success render role="status".

ToastSeverity

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.

TIER_CONFIG

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

Button

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>

PinWidgetButton

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 identifier
  • isPinned: boolean
  • onToggle: (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.

Badge

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

Banner

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>

Toast and ToastProvider

Sources: src/components/Toast.tsx, src/components/ToastProvider.tsx. Focused docs: notifications.

Toast props

Prop Type Default
toast { id: string; severity: ToastSeverity; message: string } Required
onDismiss (id: string) => void Required

ToastProvider API

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>
}

ConfirmDialog

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

AddressInput

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/AddressInputDefault · Filled · Invalid · Disabled · Loading.

AmountInput

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/AmountInputDefault · Filled · OverBalance · Error · Disabled · Loading.

TrustGauge

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

TierLadder

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

ActivityTimeline

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

FormField

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/FormFieldDefault · WithHint · WithError · WithHintAndError.

controls/Select

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/SelectDefault · Error · Disabled · Loading.

controls/Toggle

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/ToggleOff · On · Error · Disabled · Loading.

states/EmptyState

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

states/ErrorState

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

states/LoadingSkeleton

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

SessionTimeoutModal

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

BottomNav

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

Progress

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

RepoAvatar

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