AstroNova
Design 5 min read

Building a Modern Design System: From Light to Dark Mode Excellence

Explore the comprehensive design system powering this blog, featuring seamless dark mode transitions, accessibility-first components, and pixel-perfect typography.

Building a Modern Design System: From Light to Dark Mode Excellence

In an era where user preferences reign supreme, a robust design system isn’t just about aesthetics—it’s about creating inclusive, adaptable experiences. This blog showcases how modern design principles can create seamless transitions between light and dark modes while maintaining accessibility and visual hierarchy.

The Philosophy: Design for Everyone

Our design system is built on three core principles:

  • Accessibility First: Every color, spacing, and interaction is designed with WCAG 2.1 AA compliance
  • User Preference Respect: Automatic theme switching based on system preferences
  • Performance Conscious: Zero layout shifts during theme transitions

Dark Mode Architecture

CSS Custom Properties System

We leverage CSS custom properties for dynamic theming:

:root {
  /* Light mode colors */
  --color-bg-primary: #ffffff;
  --color-bg-secondary: #f9fafb;
  --color-text-primary: #111827;
  --color-text-secondary: #6b7280;
  --color-border: #e5e7eb;
  
  /* Semantic colors */
  --color-accent: #3b82f6;
  --color-success: #10b981;
  --color-warning: #f59e0b;
  --color-error: #ef4444;
}

[data-theme="dark"] {
  /* Dark mode colors */
  --color-bg-primary: #111827;
  --color-bg-secondary: #1f2937;
  --color-text-primary: #f9fafb;
  --color-text-secondary: #d1d5db;
  --color-border: #374151;
  
  /* Adjusted semantic colors for dark mode */
  --color-accent: #60a5fa;
  --color-success: #34d399;
  --color-warning: #fbbf24;
  --color-error: #f87171;
}

Automatic Theme Detection

The system automatically detects and applies user preferences:

// Theme detection utility
const detectTheme = (): void => {
  const savedTheme = localStorage.getItem('theme')
  const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
  
  const theme = savedTheme || systemTheme
  document.documentElement.setAttribute('data-theme', theme)
}

Smooth Transitions

All theme changes use smooth transitions to prevent jarring user experiences:

* {
  transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
}

Typography System

Scale and Hierarchy

Our typography system uses a modular scale for consistent visual rhythm:

--font-size-xs: 0.75rem;    /* 12px */
--font-size-sm: 0.875rem;   /* 14px */
--font-size-base: 1rem;     /* 16px */
--font-size-lg: 1.125rem;   /* 18px */
--font-size-xl: 1.25rem;    /* 20px */
--font-size-2xl: 1.5rem;    /* 24px */
--font-size-3xl: 1.875rem;   /* 30px */
--font-size-4xl: 2.25rem;    /* 36px */

Font Loading Strategy

We use variable fonts for optimal performance:

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">

Component Architecture

Button Component Example

Our button component adapts seamlessly to both themes:

interface ButtonProps {
  variant: 'primary' | 'secondary' | 'ghost'
  size: 'sm' | 'md' | 'lg'
  disabled?: boolean
}

const Button: React.FC<ButtonProps> = ({ variant, size, children, ...props }) => {
  const baseClasses = "inline-flex items-center justify-center font-medium rounded-md transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2"
  
  const variantClasses = {
    primary: "bg-accent text-white hover:bg-accent/90 focus:ring-accent",
    secondary: "bg-secondary text-text-primary hover:bg-secondary/80 focus:ring-secondary",
    ghost: "text-text-primary hover:bg-secondary/50 focus:ring-secondary"
  }
  
  const sizeClasses = {
    sm: "px-3 py-1.5 text-sm",
    md: "px-4 py-2 text-base",
    lg: "px-6 py-3 text-lg"
  }
  
  return (
    <button 
      className={`${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]}`}
      {...props}
    >
      {children}
    </button>
  )
}

Color System

Semantic Color Tokens

Our color system uses semantic naming for consistency:

TokenLight ModeDark ModeUsage
--color-bg-primary#ffffff#111827Main background
--color-bg-secondary#f9fafb#1f2937Card backgrounds
--color-text-primary#111827#f9fafbPrimary text
--color-text-secondary#6b7280#d1d5dbSecondary text
--color-border#e5e7eb#374151Borders and dividers

Accessibility Testing

Every color combination is tested for accessibility:

  • WCAG 2.1 AA compliance for normal text (4.5:1 contrast ratio)
  • WCAG 2.1 AA compliance for large text (3:1 contrast ratio)
  • Focus indicators visible in both themes
  • Error states clearly distinguishable

Responsive Design

Breakpoint System

--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
--breakpoint-2xl: 1536px;

Fluid Typography

Using CSS clamp() for responsive text:

.heading {
  font-size: clamp(1.5rem, 4vw, 2.25rem);
  line-height: 1.2;
}

Interactive Elements

Theme Toggle Component

The theme toggle provides smooth transitions:

const ThemeToggle: React.FC = () => {
  const [theme, setTheme] = useState<'light' | 'dark'>('light')
  
  const toggleTheme = () => {
    const newTheme = theme === 'light' ? 'dark' : 'light'
    setTheme(newTheme)
    document.documentElement.setAttribute('data-theme', newTheme)
    localStorage.setItem('theme', newTheme)
  }
  
  return (
    <button 
      onClick={toggleTheme}
      className="p-2 rounded-md hover:bg-secondary transition-colors"
      aria-label="Toggle theme"
    >
      {theme === 'light' ? <MoonIcon /> : <SunIcon />}
    </button>
  )
}

Implementation Results

Performance Impact

Our design system delivers exceptional performance:

  • Zero layout shifts during theme transitions
  • First Contentful Paint: < 0.8s on mobile
  • Cumulative Layout Shift: < 0.1
  • Perfect accessibility scores across all pages

User Experience Benefits

  • Seamless theme switching without page reloads
  • Consistent visual hierarchy across all screen sizes
  • Accessibility compliance for all users
  • Developer-friendly component system

Future Enhancements

Planned Improvements

  • High contrast mode for users with visual impairments
  • Reduced motion preferences for accessibility
  • Custom theme builder for user personalization
  • System color scheme synchronization

Key Takeaways

Building a modern design system is about creating adaptive, inclusive experiences that respect user preferences while maintaining performance. This blog demonstrates that thoughtful design decisions can create beautiful, accessible interfaces that work for everyone.

The dark mode implementation isn’t just about inverting colors—it’s about creating a cohesive experience that enhances readability, reduces eye strain, and provides visual comfort in any lighting condition.


Explore our design system documentation and accessibility guidelines to learn more about inclusive design practices.

Comments