Design System
The SUDIGITAL Design System provides a comprehensive foundation for building consistent, accessible, and beautiful user interfaces across all platform applications.
Overview
Our design system is built on the principles of simplicity, consistency, and scalability. It provides a unified language and shared components that enable teams to build products faster while maintaining high quality and coherent user experiences.
Philosophy
Design Principles
Simplicity First
- Remove complexity wherever possible
- Focus on essential features and interactions
- Provide clear visual hierarchy and information architecture
Consistency Everywhere
- Unified patterns across all touchpoints
- Predictable behaviors and interactions
- Standardized visual language and terminology
Accessibility by Default
- WCAG 2.1 AA compliance as a baseline
- Inclusive design for all users and abilities
- Keyboard navigation and screen reader support
Performance Minded
- Optimized assets and efficient rendering
- Progressive enhancement strategies
- Mobile-first responsive implementations
Design Tokens
Design tokens are the foundational elements that define the visual characteristics of our design system. They ensure consistency across all platforms and make it easy to maintain and evolve our design language.
Color Palette
Primary Colors
:root {
/* Primary Brand Colors */
--color-primary-50: #f0f9ff;
--color-primary-100: #e0f2fe;
--color-primary-200: #bae6fd;
--color-primary-300: #7dd3fc;
--color-primary-400: #38bdf8;
--color-primary-500: #0ea5e9;
--color-primary-600: #0284c7;
--color-primary-700: #0369a1;
--color-primary-800: #075985;
--color-primary-900: #0c4a6e;
}Semantic Colors
:root {
/* Success */
--color-success-50: #f0fdf4;
--color-success-500: #22c55e;
--color-success-600: #16a34a;
/* Warning */
--color-warning-50: #fffbeb;
--color-warning-500: #f59e0b;
--color-warning-600: #d97706;
/* Error */
--color-error-50: #fef2f2;
--color-error-500: #ef4444;
--color-error-600: #dc2626;
/* Info */
--color-info-50: #f0f9ff;
--color-info-500: #3b82f6;
--color-info-600: #2563eb;
}Neutral Colors
:root {
/* Grays */
--color-gray-50: #f9fafb;
--color-gray-100: #f3f4f6;
--color-gray-200: #e5e7eb;
--color-gray-300: #d1d5db;
--color-gray-400: #9ca3af;
--color-gray-500: #6b7280;
--color-gray-600: #4b5563;
--color-gray-700: #374151;
--color-gray-800: #1f2937;
--color-gray-900: #111827;
}Typography
Font Families
:root {
--font-family-sans: 'Inter', system-ui, -apple-system, sans-serif;
--font-family-mono: 'JetBrains Mono', 'Fira Code', monospace;
--font-family-display: 'Inter Display', system-ui, sans-serif;
}Font Sizes
:root {
/* Text Sizes */
--text-xs: 0.75rem; /* 12px */
--text-sm: 0.875rem; /* 14px */
--text-base: 1rem; /* 16px */
--text-lg: 1.125rem; /* 18px */
--text-xl: 1.25rem; /* 20px */
--text-2xl: 1.5rem; /* 24px */
--text-3xl: 1.875rem; /* 30px */
--text-4xl: 2.25rem; /* 36px */
--text-5xl: 3rem; /* 48px */
--text-6xl: 3.75rem; /* 60px */
}Font Weights
:root {
--font-weight-thin: 100;
--font-weight-light: 300;
--font-weight-normal: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
--font-weight-extrabold: 800;
}Line Heights
:root {
--line-height-none: 1;
--line-height-tight: 1.25;
--line-height-snug: 1.375;
--line-height-normal: 1.5;
--line-height-relaxed: 1.625;
--line-height-loose: 2;
}Spacing
:root {
/* Spacing Scale */
--space-0: 0;
--space-1: 0.25rem; /* 4px */
--space-2: 0.5rem; /* 8px */
--space-3: 0.75rem; /* 12px */
--space-4: 1rem; /* 16px */
--space-5: 1.25rem; /* 20px */
--space-6: 1.5rem; /* 24px */
--space-8: 2rem; /* 32px */
--space-10: 2.5rem; /* 40px */
--space-12: 3rem; /* 48px */
--space-16: 4rem; /* 64px */
--space-20: 5rem; /* 80px */
--space-24: 6rem; /* 96px */
--space-32: 8rem; /* 128px */
}Border Radius
:root {
--radius-none: 0;
--radius-sm: 0.125rem; /* 2px */
--radius-default: 0.25rem; /* 4px */
--radius-md: 0.375rem; /* 6px */
--radius-lg: 0.5rem; /* 8px */
--radius-xl: 0.75rem; /* 12px */
--radius-2xl: 1rem; /* 16px */
--radius-3xl: 1.5rem; /* 24px */
--radius-full: 9999px;
}Shadows
:root {
/* Elevation Shadows */
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-default:
0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--shadow-lg:
0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
--shadow-xl:
0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
--shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25);
--shadow-inner: inset 0 2px 4px 0 rgb(0 0 0 / 0.05);
}Component Architecture
Component Categories
1. Foundation Components
- Typography - Headings, body text, captions
- Colors - Palettes, swatches, semantic colors
- Icons - Icon library and usage guidelines
- Spacing - Margin, padding, and layout utilities
2. Basic Components
- Button - Primary, secondary, text, icon buttons
- Input - Text fields, textareas, search inputs
- Select - Dropdowns, multi-select, autocomplete
- Checkbox - Single and group checkboxes
- Radio - Radio buttons and radio groups
- Switch - Toggle switches and controls
3. Composite Components
- Form - Complete form layouts and validation
- Card - Content containers with various layouts
- Modal - Dialogs, alerts, and overlays
- Navigation - Headers, sidebars, breadcrumbs
- Table - Data tables with sorting and filtering
- Pagination - Page navigation controls
4. Layout Components
- Container - Page and section containers
- Grid - Responsive grid systems
- Stack - Vertical and horizontal stacking
- Divider - Section separators and spacing
- Spacer - Flexible spacing utilities
Component Naming Convention
// Component naming follows PascalCase
export const Button: React.FC<ButtonProps> = ({ ... }) => { ... }
export const FormField: React.FC<FormFieldProps> = ({ ... }) => { ... }
export const DataTable: React.FC<DataTableProps> = ({ ... }) => { ... }
// Props interfaces follow ComponentNameProps pattern
interface ButtonProps {
variant: 'primary' | 'secondary' | 'outline' | 'ghost';
size: 'sm' | 'md' | 'lg';
disabled?: boolean;
loading?: boolean;
children: React.ReactNode;
onClick?: () => void;
}Component Variants
Each component supports multiple variants to handle different use cases:
// Size variants
type ComponentSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
// Color variants
type ComponentVariant =
| 'primary'
| 'secondary'
| 'success'
| 'warning'
| 'error'
// State variants
type ComponentState = 'default' | 'hover' | 'active' | 'focus' | 'disabled'Layout System
Responsive Breakpoints
:root {
/* Breakpoints */
--breakpoint-xs: 0px;
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
--breakpoint-2xl: 1536px;
}Grid System
.grid {
display: grid;
gap: var(--space-4);
}
.grid-cols-1 {
grid-template-columns: repeat(1, minmax(0, 1fr));
}
.grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.grid-cols-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.grid-cols-4 {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.grid-cols-12 {
grid-template-columns: repeat(12, minmax(0, 1fr));
}
/* Responsive grid */
@media (min-width: 768px) {
.md\:grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.md\:grid-cols-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}Container System
.container {
width: 100%;
margin-left: auto;
margin-right: auto;
padding-left: var(--space-4);
padding-right: var(--space-4);
}
@media (min-width: 640px) {
.container {
max-width: 640px;
}
}
@media (min-width: 768px) {
.container {
max-width: 768px;
}
}
@media (min-width: 1024px) {
.container {
max-width: 1024px;
}
}
@media (min-width: 1280px) {
.container {
max-width: 1280px;
}
}Iconography
Icon Library
We use a curated set of icons from Heroicons and custom SUDIGITAL icons.
Icon Sizes
.icon-xs {
width: 12px;
height: 12px;
}
.icon-sm {
width: 16px;
height: 16px;
}
.icon-md {
width: 20px;
height: 20px;
}
.icon-lg {
width: 24px;
height: 24px;
}
.icon-xl {
width: 32px;
height: 32px;
}Icon Usage
import { ChevronDownIcon, UserIcon, CogIcon } from '@heroicons/react/24/outline';
import { HeartIcon } from '@heroicons/react/24/solid';
// Icon component with consistent sizing
const Icon: React.FC<IconProps> = ({
icon: IconComponent,
size = 'md',
className
}) => (
<IconComponent
className={`icon-${size} ${className}`}
aria-hidden="true"
/>
);Accessibility
WCAG 2.1 AA Compliance
All components meet WCAG 2.1 AA accessibility standards:
Color Contrast
- Minimum 4.5:1 ratio for normal text
- Minimum 3:1 ratio for large text
- Minimum 3:1 ratio for UI components
Keyboard Navigation
- All interactive elements are keyboard accessible
- Logical tab order and focus management
- Custom components support arrow key navigation
Screen Reader Support
- Semantic HTML elements
- Proper ARIA labels and descriptions
- Screen reader testing with VoiceOver and NVDA
Focus Management
- Visible focus indicators
- Focus trapping in modals
- Skip links for main content
Accessibility Guidelines
// Always include proper ARIA attributes
<button
aria-label="Close dialog"
aria-expanded={isOpen}
aria-controls="dialog-content"
onClick={onClose}
>
<CloseIcon aria-hidden="true" />
</button>
// Use semantic HTML elements
<nav aria-label="Main navigation">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
// Provide alternative text for images
<img
src="chart.png"
alt="Revenue growth chart showing 25% increase over last quarter"
/>Animation & Motion
Motion Principles
- Purposeful - Every animation should have a clear purpose
- Performant - Use transform and opacity for smooth animations
- Respectful - Honor user preferences for reduced motion
- Subtle - Avoid distracting or excessive animations
Animation Tokens
:root {
/* Duration */
--duration-fast: 150ms;
--duration-normal: 300ms;
--duration-slow: 500ms;
/* Easing */
--ease-in: cubic-bezier(0.4, 0, 1, 1);
--ease-out: cubic-bezier(0, 0, 0.2, 1);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
--ease-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
}Common Animations
/* Fade transitions */
.fade-enter {
opacity: 0;
transform: translateY(8px);
}
.fade-enter-active {
opacity: 1;
transform: translateY(0);
transition:
opacity var(--duration-normal) var(--ease-out),
transform var(--duration-normal) var(--ease-out);
}
/* Slide transitions */
.slide-enter {
transform: translateX(-100%);
}
.slide-enter-active {
transform: translateX(0);
transition: transform var(--duration-normal) var(--ease-out);
}
/* Respect reduced motion preferences */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}Implementation Guidelines
CSS Architecture
// 1. Design tokens (variables)
@import 'tokens/colors';
@import 'tokens/typography';
@import 'tokens/spacing';
// 2. Base styles
@import 'base/reset';
@import 'base/typography';
// 3. Layout utilities
@import 'layout/container';
@import 'layout/grid';
// 4. Components
@import 'components/button';
@import 'components/form';
@import 'components/card';
// 5. Utilities
@import 'utilities/spacing';
@import 'utilities/colors';Component Development
// Use TypeScript for all components
interface ButtonProps {
variant?: 'primary' | 'secondary' | 'outline' | 'ghost';
size?: 'sm' | 'md' | 'lg';
disabled?: boolean;
loading?: boolean;
children: React.ReactNode;
className?: string;
onClick?: () => void;
}
export const Button: React.FC<ButtonProps> = ({
variant = 'primary',
size = 'md',
disabled = false,
loading = false,
children,
className = '',
onClick,
...rest
}) => {
const baseClasses = 'btn';
const variantClasses = `btn--${variant}`;
const sizeClasses = `btn--${size}`;
const stateClasses = {
'btn--disabled': disabled,
'btn--loading': loading,
};
const classes = clsx(
baseClasses,
variantClasses,
sizeClasses,
stateClasses,
className
);
return (
<button
className={classes}
disabled={disabled || loading}
onClick={onClick}
aria-disabled={disabled || loading}
{...rest}
>
{loading && <Spinner size="sm" />}
{children}
</button>
);
};Testing Components
import { render, screen, fireEvent } from '@testing-library/react';
import { Button } from './Button';
describe('Button', () => {
test('renders with correct text', () => {
render(<Button>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
test('calls onClick when clicked', () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick}>Click me</Button>);
fireEvent.click(screen.getByRole('button'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
test('is disabled when loading', () => {
render(<Button loading>Click me</Button>);
expect(screen.getByRole('button')).toBeDisabled();
});
test('has correct accessibility attributes', () => {
render(<Button disabled>Click me</Button>);
expect(screen.getByRole('button')).toHaveAttribute('aria-disabled', 'true');
});
});Tools & Resources
Development Tools
- Storybook - Component development and documentation
- Figma - Design files and component specifications
- VS Code Extensions - Snippets and IntelliSense support
- ESLint/Prettier - Code formatting and best practices
Browser Support
| Browser | Version |
|---|---|
| Chrome | Latest 2 versions |
| Firefox | Latest 2 versions |
| Safari | Latest 2 versions |
| Edge | Latest 2 versions |
Performance Targets
- First Contentful Paint: < 1.5s
- Largest Contentful Paint: < 2.5s
- Cumulative Layout Shift: < 0.1
- First Input Delay: < 100ms
Getting Started
Installation
# Install the design system package
bun add @sudigital/design-system
# Install peer dependencies
bun add react react-domBasic Usage
import '@sudigital/design-system/styles.css';
import { Button, Card, Text } from '@sudigital/design-system';
function App() {
return (
<Card>
<Text variant="h2">Welcome to SUDIGITAL</Text>
<Button variant="primary" size="lg">
Get Started
</Button>
</Card>
);
}Customization
/* Override design tokens */
:root {
--color-primary-500: #your-brand-color;
--font-family-sans: 'Your Custom Font', sans-serif;
}Related Resources
- Component Library - Individual component documentation
- Pattern Library - Common UI patterns and layouts
- Figma Files - Design specifications
- GitHub Repository - Source code and issues