This directory contains comprehensive design specifications and implementation guides for the Credence Frontend application.
-
State Management ⭐ NEW
- Overview of SettingsContext, WalletContext, and ToastProvider
- State shape, public hooks, and persistence mechanisms
- Provider nesting order (load-bearing dependencies)
- Decision guide: when to add new state vs. local vs. URL params
- Mock patterns for testing
-
- How to run Vitest and generate coverage
- Render helpers, router wrapper, and mock patterns for matchMedia / localStorage / clipboard
- File naming conventions and coverage thresholds
-
QA Release-Gate Checklist ⭐ NEW
- Contributor-focused checklist of every gate a PR must pass before ship
- Automated gates (format, lint, build, test), functionality smoke tests, and manual checks
- Sections: code quality, accessibility, responsive layout, theme parity, API types, formatting utilities, security, widget cache
- PR evidence block to paste into every pull request description
-
Per-route document titles (
useDocumentTitle)src/hooks/useDocumentTitle.tskeepsdocument.titlein sync with the active route- Each page sets a distinct, branded title (e.g.
Bond · Credence); the 404 page usesPage Not Found · Credence - Why it matters: screen readers announce the title on navigation, and tabs, history, and bookmarks become distinguishable per page
- SSR-safe (
typeof documentguard), restores the previous title on unmount, and never double-applies the· Credencebrand suffix
import { useDocumentTitle } from '../hooks/useDocumentTitle' function Bond() { useDocumentTitle('Bond') // document.title === 'Bond · Credence' return <main>…</main> }
-
- Consolidated props, Storybook story paths and variants, accessibility notes, usage snippets, styling ownership, and
--credence-*token references for all public shared UI components - Documents severity/variant vocabularies and cross-links focused component docs
- Consolidated props, Storybook story paths and variants, accessibility notes, usage snippets, styling ownership, and
-
Component API Conventions ⭐ NEW
- Single-source for props conventions (naming rules, ordering rules, boolean-prop rules, and TypeScript interface contracts)
- Code examples and review checklist for consistent component APIs
-
- Complete guide for empty states, error states, and loading patterns
- Microcopy guidelines and tone recommendations
- When and how to use each state type
- Validation checklist
-
Error UI Pattern Guide ⭐ NEW
- Standardized guide for inline form errors, banners, toasts, and section/page error states
- Surface decision matrix, component contracts, accessibility guidelines, and concrete code snippets
-
Copy Tone Guide ⭐ NEW
- How we phrase success, error, empty, and loading UI copy
- Voice principles (friendly, clear, concise, helpful)
- State-by-state dos and don'ts with real codebase examples
- Capitalization, punctuation, and i18n conventions
- PR review checklist for user-facing strings
-
Form Validation Timing ⭐ NEW
- Practical guidance for when to use
onChange,onBlur, andonSubmit - Concrete examples for controlled input state, field-level validation, and final submission checks
- Recommended pattern for Credence forms and common mistakes to avoid
- Practical guidance for when to use
-
Form Inputs & Variants ⭐ NEW
- Standardized states (Default, Error, Disabled, Loading) for all input components
- Usage guidelines and accessibility contracts for
AddressInput,AmountInput, and controls
-
- Canonical
--credence-*CSS variable reference - Color, spacing, radius, typography, and motion scales
- Guidance for replacing one-off hex values in components
- Canonical
-
- Motion token strategy and reduced-motion defaults
- Best practices for animation and transitions
- Implementation examples for UI micro-interactions
- Visual design specifications
- Color palette and design tokens
- Layout measurements and spacing
- Animation specifications
- Responsive breakpoints
- Component organization structure
-
- Practical code examples for each page
- Reusable hooks and patterns
- Testing examples
- Accessibility guidelines
- Performance considerations
-
Prop Types Migration Guide ⭐ NEW
- Migration path from any-typed props to explicit TypeScript contracts
- Review checklist and component examples
- Guidance for replacing loose props with safer, documented interfaces
-
Mobile Navigation Pattern ⭐ NEW
- Hybrid responsive navigation (hamburger mobile + horizontal desktop)
- Complete implementation guide with code examples
- Accessibility requirements (WCAG 2.1 AA)
- Testing guide and troubleshooting
- Decision Matrix | Reconnaissance Report | Figma Rules
-
Keyboard Interactions Contract ⭐ NEW
- Developer-facing matrix of every interactive component and its expected keyboard behavior
- Covers
ConfirmDialog,TierLadder,Banner,Toggle,AddressInput, skip-link, and navigation - Focus-restore contract and checklist for new interactive components
-
Wallet Integration ⭐ NEW
useWalletAPI documentation- Connection state machine and UX contract for connection/network states
- Usage guide and network mismatch handling
-
Authentication Flows ⭐ NEW
- Sequence diagrams for login (connect wallet), logout (disconnect), and session refresh (re-authentication)
- Session timeout (inactivity logout) flow
- Key code paths and cross-references
-
- CSP policy, browser storage rules, third-party script posture, and dependency audit workflow
- Concrete review checklist for each security area Settings Auto-Save Indicator ⭐ NEW (closes #564)
- Debounced
PATCH /settingsflow withuseDebouncedAutoSavehook. <AutoSaveIndicator />token-driven pill showingSaving…/Saved just now/ "Couldn't save" with retry.- In-flight cancellation via
AbortControllerso stale PATCHes can't overwrite newer state.
-
Widget Cache & Per-Widget Refresh ⭐ NEW (closes #561)
- Shared in-app cache for dashboard widgets so a refresh button on one card only invalidates that card's key — others keep their state.
useWidgetCachehook +<WidgetRefreshButton />+ token-driven styling.- Coverage includes mount, key isolation, error surfacing, and reduced-motion.
-
API Client Policies ⭐ NEW
- Interceptors, retry policy, and error taxonomy for the API client
ApiErrorstructure and usage examples
-
Bundle Size Baseline ⭐ NEW
- Current production bundle size estimates and per-route breakdowns
- Top 10 heaviest dependencies ranked by gzipped size
- How to profile and compare bundle sizes with Vite, rollup-plugin-visualizer, and size-limit
- Contributor guidelines for keeping the bundle lean
-
- Privacy-first approach (no telemetry collected)
- No PII handling or third-party analytics
-
- What's cached (localStorage keys, in-memory widget cache), what's queued (pending transactions, auto-save retry), and what happens on cache miss
- Offline-aware hooks (
useQuery,useWidgetCache), offline banner, install prompt behaviour
-
- First-run onboarding concept for creating a bond
- Coach mark placement, copy, sequencing, and dismissal behavior
- Accessibility, responsive behavior, and visual QA checklist
To implement UI states in your components:
import { EmptyState, ErrorState, LoadingSkeleton } from '../components/states'
function MyComponent() {
const { data, isLoading, error } = useQuery()
if (isLoading) return <LoadingSkeleton variant="card" />
if (error) return <ErrorState type="network" />
if (!data) return <EmptyState title="No data" description="..." />
return <Content data={data} />
}-
State Components:
src/components/states/EmptyState.tsx- Empty state componentErrorState.tsx- Error state componentLoadingSkeleton.tsx- Loading skeleton componentindex.ts- Barrel export
-
Navigation Components:
src/components/navigation/(to be implemented)MobileNav.tsx- Hamburger menu + drawer (mobile)DesktopNav.tsx- Horizontal navigation (desktop)NavigationLinks.tsx- Shared navigation datauseMediaQuery.ts- Breakpoint detection hook
- User-First: Always prioritize user understanding and next actions
- Consistent: Use the same patterns across all views
- Helpful: Provide clear guidance and recovery options
- Accessible: Ensure all states work with assistive technologies
- Performant: Show loading states immediately, optimize transitions
When implementing components, check states in this order:
- Loading - Show immediately when data is being fetched
- Error - Show when something goes wrong
- Empty - Show when there's no data to display
- Content - Show the actual content
When adding new states or modifying existing ones:
- Update the relevant component in
src/components/states/ - Document the change in the appropriate guide
- Add implementation examples if needed
- Update Figma designs to match
- Test accessibility and responsiveness
Before shipping new states or UI changes, refer to the Design QA Checklist.
- Review against UI States Guide principles
- Validate microcopy with product team
- Test on mobile, tablet, and desktop
- Verify accessibility with screen readers
- Check loading state transitions
- Ensure error recovery flows work
- Figma File: [Link to be added]
- Component Storybook: Run
npm run storybooklocally - Accessibility Audit: [Link to be added]
For questions about UI states implementation, contact the design team or refer to the detailed guides in this directory.
- Storage key:
credence:settings— the settings context persists a JSON payload under this key inlocalStorage. - Fallback contract: on load the provider attempts to
JSON.parsethe value; if parsing fails or no key exists the provider falls back to built-in defaults (no exception is thrown).
The Credence frontend now has centralized formatting utilities to ensure consistency across all components.
This module is the single source of truth for all USDC formatting logic across the application.
Exported Functions:
// Format numeric USDC amount with "USDC" suffix
formatUsdc(amount: number): string
// Normalize user-entered string to consistent 2-decimal representation
normalizeUSDC(rawValue: string): string
// Format string for display with thousand separators
formatUSDC(rawValue: string): string
// UI display formatter (identical to formatUSDC)
formatUSDCDisplay(rawValue: string): string
// Sanitize user input while preserving valid decimal input
sanitizeUSDCInput(nextValue: string): stringUsage Examples:
import { formatUsdc, normalizeUSDC, formatUSDC, sanitizeUSDCInput } from '@/lib/format'
// Display formatting
formatUsdc(1234.5) // → "1,234.5 USDC"
// Input normalization
normalizeUSDC('1,234.5') // → "1234.50"
// Display formatting from strings
formatUSDC('1234.5') // → "1,234.50"
// Input sanitization
sanitizeUSDCInput('$1,000.50') // → "1000.50"Behavior Preservation:
- Thousands separators maintained for display
- Decimal precision fixed at 2 places
- Empty values handled gracefully
- Negative values clamped to 0 for normalization
- Invalid input returns empty string or original text for correction
This module is the single source of truth for all Stellar address validation and formatting.
Exported Functions:
// Validate Stellar public key format (56 chars, starts with 'G')
isValidStellarAddress(address: string | undefined | null): boolean
// Middle-truncate address for display: preserves both start and end
truncateAddress(address: string | undefined | null): string
// Format address according to display mode ('full' | 'short' | 'friendly')
formatAddressForDisplay(address: string | undefined | null, mode: AddressDisplayMode | string | undefined): stringMiddle Truncation Behavior:
truncateAddress performs middle truncation — instead of only showing the beginning of a
long string, it preserves both the start (first 12 characters) and the end (last 8 characters),
separated by .... Strings shorter than 20 characters are returned unchanged.
Usage Examples:
import { isValidStellarAddress, truncateAddress, formatAddressForDisplay } from '@/lib/stellar'
// Address validation
isValidStellarAddress('GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWNA') // → true
// Address truncation (middle truncation: start...end)
truncateAddress('GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWNA')
// → "GAAZI4TCR3TY...CCWNA"
// Format for display in different modes
formatAddressForDisplay('GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWNA', 'short')
// → "GAAZI4TCR3TY...CCWNA"
formatAddressForDisplay('GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWNA', 'friendly')
// → "GAAZI4…CCWNA"Behavior Preservation:
- Exact 56-character validation
- 'G' prefix requirement
- Uppercase alphanumeric characters only
- Middle truncation for long strings: first 12 +
...+ last 8 - Short addresses (≤20 chars) displayed unchanged
- Whitespace trimmed automatically
- Null/undefined values handled gracefully
All components should now import from these centralized modules instead of maintaining local implementations:
Before:
// In component files
export function normalizeUSDC(rawValue: string) { ... }
export function isValidStellarAddress(address: string) { ... }After:
// Import from centralized modules
import { normalizeUSDC } from '@/lib/format'
import { isValidStellarAddress } from '@/lib/stellar'Both utility modules have comprehensive test suites with ≥95% branch coverage:
src/lib/format.test.ts- USDC formatting testssrc/lib/stellar.test.ts- Stellar address tests
Run tests with:
npm test -- --run src/lib/format.test.ts src/lib/stellar.test.tsThe following components have been refactored to use the centralized utilities:
- AmountInput.tsx - USDC input formatting and sanitization
- AddressInput.tsx - Stellar address validation and truncation
- TrustScore.tsx - Stellar address validation
- useTrustScore.ts - Stellar address validation
- Bond.tsx - USDC display formatting
- Dashboard.tsx - USDC display formatting
- penalty.ts - USDC display formatting
- bondPenalty.ts - USDC display formatting
When adding new formatting or validation logic:
- Check if it belongs in the centralized modules
- Add comprehensive test coverage
- Update this documentation
- Refactor any existing duplicate implementations