The Blockchain Snapshots application uses a unified design system to ensure consistency across all UI components. This document outlines the design tokens, patterns, and best practices.
- Consistency First: Use predefined tokens instead of custom values
- Dark Mode Support: All components must work in both light and dark themes
- Accessibility: Follow WCAG 2.1 AA standards
- Performance: Minimize CSS bundle size through token reuse
- Mobile-First: Design for mobile devices first, then scale up
import { colors, getCompressionColor, getTierColor } from '@/lib/design-system';
// Brand colors
colors.brand.primary // blue-500
colors.brand.secondary // purple-600
// Status colors
colors.status.success // green-500
colors.status.error // red-500
// Tier-specific colors
getTierColor('premium') // Returns premium tier colors
getTierColor('free') // Returns free tier colors
// Compression type colors
getCompressionColor('lz4') // Returns LZ4 compression colorsimport { typography } from '@/lib/design-system';
// Headings
<h1 className={typography.h1}>Page Title</h1>
<h2 className={typography.h2}>Section Title</h2>
// Body text
<p className={typography.body.base}>Regular text</p>
<p className={typography.muted}>Muted text</p>
// Special text
<code className={typography.code}>code snippet</code>
<a className={typography.link.base}>Link text</a>import { spacing } from '@/lib/design-system';
// Page layout
<div className={cn(spacing.page.container, spacing.page.px)}>
<main className={spacing.page.maxWidth.lg}>
...
</main>
</div>
// Component spacing
<div className={spacing.component.stack.md}>
<Card className={spacing.component.card}>...</Card>
<Card className={spacing.component.card}>...</Card>
</div>
// Grid layouts
<div className={cn('grid', spacing.grid.cols[3], spacing.grid.gap.md)}>
...
</div>import { components } from '@/lib/design-system';
import { cn } from '@/lib/utils';
// Cards
<div className={components.card.base}>Basic card</div>
<div className={components.card.interactive}>Clickable card</div>
<div className={components.card.glassmorphic}>Glassmorphic card</div>
// Buttons
<button className={cn(
components.button.base,
components.button.variant.primary,
components.button.size.default
)}>
Primary Button
</button>
// Badges
<span className={cn(
components.badge.base,
components.badge.variant.success
)}>
Success
</span>
// Alerts
<div className={cn(
components.alert.base,
components.alert.variant.warning
)}>
Warning message
</div><div className={components.card.base}>
<div className="p-6">
<div className="flex items-center justify-between mb-4">
<h3 className={typography.h4}>Card Title</h3>
<button className={cn(
components.button.base,
components.button.variant.ghost,
components.button.size.sm
)}>
Action
</button>
</div>
<div className={typography.body.base}>
Card content goes here...
</div>
</div>
</div><form className={spacing.component.stack.md}>
<div>
<label className={typography.body.small}>Email</label>
<input type="email" className={components.input.base} />
</div>
<div>
<label className={typography.body.small}>Password</label>
<input type="password" className={components.input.base} />
</div>
<button className={cn(
components.button.base,
components.button.variant.primary,
components.button.size.default,
'w-full'
)}>
Submit
</button>
</form>// Download status badge
function getStatusBadge(status: string) {
const variant = status === 'active'
? components.badge.variant.success
: status === 'error'
? components.badge.variant.error
: components.badge.variant.default;
return (
<span className={cn(components.badge.base, variant)}>
{status}
</span>
);
}- Use design tokens for all styling decisions
- Compose utilities using the
cn()helper - Test in both themes (light and dark mode)
- Keep components small and composable
- Document deviations when custom styling is necessary
- Don't hardcode colors - use the color system
- Don't create one-off utilities - extend the design system
- Don't mix design systems - stick to our patterns
- Don't forget dark mode - all components must support it
- Don't ignore accessibility - use semantic HTML and ARIA
When updating existing components:
-
Import design system utilities:
import { components, typography, spacing, colors } from '@/lib/design-system'; import { cn } from '@/lib/utils';
-
Replace hardcoded classes with tokens:
// Before <div className="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6"> // After <div className={cn(components.card.base, 'p-6')}>
-
Use helper functions for dynamic styles:
// Before const color = type === 'premium' ? 'bg-purple-100 text-purple-800' : 'bg-gray-100 text-gray-800'; // After const { bg, text } = getTierColor(type);
To add new tokens or patterns:
- Add to the appropriate file in
/lib/design-system/ - Export from the index file
- Document the addition in this guide
- Update affected components
- Tailwind CSS: Our underlying utility framework
- Radix UI: Unstyled, accessible component primitives
- CVA: Class variance authority for component variants
- Design System Playground:
/testpage for testing components
- Color contrast ratio ≥ 4.5:1 for normal text
- Color contrast ratio ≥ 3:1 for large text
- Interactive elements have focus states
- All images have alt text
- Form inputs have labels
- Error messages are announced
- Keyboard navigation works
- Screen reader tested