Theming Guide

UI Lab provides a powerful theming system that allows you to customize every aspect of your components to match your brand identity.

Theme Structure

The theme system is built around design tokens that control:

  • Colors - Primary, secondary, semantic colors
  • Typography - Font families, sizes, weights, line heights
  • Spacing - Margins, padding, gaps
  • Borders - Radius, widths, styles
  • Shadows - Elevation levels
  • Motion - Transition durations and easings

Creating a Custom Theme

1. Define Your Design Tokens

// theme.config.js
export const theme = {
  colors: {
    primary: {
      50: '#fef2f2',
      100: '#fee2e2',
      500: '#ef4444',
      900: '#7f1d1d',
    },
    secondary: {
      50: '#f8fafc',
      100: '#f1f5f9',
      500: '#64748b',
      900: '#0f172a',
    },
    // Semantic colors
    success: '#10b981',
    warning: '#f59e0b',
    error: '#ef4444',
    info: '#3b82f6',
  },
  typography: {
    fontFamily: {
      sans: ['Inter', 'system-ui', 'sans-serif'],
      heading: ['Poppins', 'sans-serif'],
      mono: ['Fira Code', 'monospace'],
    },
    fontSize: {
      xs: '0.75rem',
      sm: '0.875rem',
      base: '1rem',
      lg: '1.125rem',
      xl: '1.25rem',
      '2xl': '1.5rem',
      '3xl': '1.875rem',
    },
    fontWeight: {
      normal: '400',
      medium: '500',
      semibold: '600',
      bold: '700',
    },
  },
  spacing: {
    xs: '0.25rem',
    sm: '0.5rem',
    md: '1rem',
    lg: '1.5rem',
    xl: '2rem',
    '2xl': '3rem',
  },
  borderRadius: {
    none: '0',
    sm: '0.25rem',
    md: '0.5rem',
    lg: '0.75rem',
    xl: '1rem',
    full: '9999px',
  },
};

2. Component-Specific Theming

export const componentThemes = {
  Button: {
    variants: {
      primary: {
        backgroundColor: 'var(--ui-color-primary-500)',
        color: 'white',
        '&:hover': {
          backgroundColor: 'var(--ui-color-primary-600)',
        },
      },
      secondary: {
        backgroundColor: 'var(--ui-color-secondary-100)',
        color: 'var(--ui-color-secondary-900)',
        '&:hover': {
          backgroundColor: 'var(--ui-color-secondary-200)',
        },
      },
    },
    sizes: {
      small: {
        padding: 'var(--ui-spacing-sm) var(--ui-spacing-md)',
        fontSize: 'var(--ui-text-sm)',
      },
      medium: {
        padding: 'var(--ui-spacing-md) var(--ui-spacing-lg)',
        fontSize: 'var(--ui-text-base)',
      },
      large: {
        padding: 'var(--ui-spacing-lg) var(--ui-spacing-xl)',
        fontSize: 'var(--ui-text-lg)',
      },
    },
  },
};

Dark Mode Theming

Automatic Dark Mode

export const darkTheme = {
  colors: {
    primary: {
      50: '#1e1b4b',
      100: '#312e81',
      500: '#6366f1',
      900: '#e0e7ff',
    },
    background: {
      primary: '#0f172a',
      secondary: '#1e293b',
      tertiary: '#334155',
    },
    text: {
      primary: '#f8fafc',
      secondary: '#cbd5e1',
      tertiary: '#94a3b8',
    },
  },
};

Manual Dark Mode Toggle

// Toggle between light and dark themes
const toggleTheme = () => {
  document.documentElement.classList.toggle('dark');

  // Store preference
  localStorage.setItem(
    'theme',
    document.documentElement.classList.contains('dark') ? 'dark' : 'light'
  );
};

CSS Custom Properties

UI Lab generates CSS custom properties for runtime theming:

:root {
  /* Colors */
  --ui-color-primary-50: #fef2f2;
  --ui-color-primary-500: #ef4444;
  --ui-color-primary-900: #7f1d1d;

  /* Typography */
  --ui-font-sans: 'Inter', system-ui, sans-serif;
  --ui-text-base: 1rem;
  --ui-font-medium: 500;

  /* Spacing */
  --ui-spacing-sm: 0.5rem;
  --ui-spacing-md: 1rem;
  --ui-spacing-lg: 1.5rem;

  /* Borders */
  --ui-radius-md: 0.5rem;
  --ui-border-width: 1px;

  /* Shadows */
  --ui-shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  --ui-shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
}

/* Dark mode overrides */
.dark {
  --ui-color-primary-500: #6366f1;
  --ui-color-background: #0f172a;
  --ui-color-text: #f8fafc;
}

Theme Application

Runtime Theme Changes

// Change theme colors dynamically
const changeTheme = (newColors) => {
  Object.entries(newColors).forEach(([key, value]) => {
    document.documentElement.style.setProperty(`--ui-color-${key}`, value);
  });
};

// Example usage
changeTheme({
  'primary-500': '#8b5cf6',
  'secondary-500': '#06b6d4',
});

Component-Level Customization

// Override specific component styles
<Button
  className="custom-button"
  style={{
    '--ui-button-bg': '#ff6b35',
    '--ui-button-text': 'white',
    '--ui-button-radius': '20px',
  }}
>
  Custom Styled Button
</Button>

Best Practices

  1. Start with Design Tokens - Define your color palette, typography scale, and spacing system first
  2. Use Semantic Naming - Name colors by their purpose (primary, danger) rather than their appearance (red, blue)
  3. Test in Both Modes - Always test your theme in both light and dark modes
  4. Maintain Contrast - Ensure sufficient color contrast for accessibility
  5. Document Your Theme - Keep a style guide documenting your design decisions

Examples

Brand Theme Example

// Spotify-inspired theme
export const spotifyTheme = {
  colors: {
    primary: {
      500: '#1db954', // Spotify green
    },
    secondary: {
      500: '#191414', // Dark gray
    },
    background: {
      primary: '#121212',
      secondary: '#181818',
    },
  },
  borderRadius: {
    md: '8px',
    lg: '12px',
  },
};

Ready to implement your custom theme? Check out our advanced customization guide for more details!