Docs/Theming

Theming

Learn how to customize and extend the ReacUI design system to match your brand

Overview

ReacUI is built with a flexible theming system that allows you to customize the look and feel of all components to match your brand's identity. The theming system is built on top of Tailwind CSS, making it easy to extend and customize.

Simple Configuration

Customize your theme with a simple configuration file that extends Tailwind CSS.

Design Tokens

Use design tokens to maintain consistency across your application's UI.

Dark Mode Support

Built-in dark mode support with seamless transitions between light and dark themes.

Pro Tip

The theming system is designed to work with Tailwind CSS out of the box. If you're already using Tailwind, you can seamlessly integrate ReacUI into your existing theme.

Theme Configuration

ReacUI's theming is powered by Tailwind CSS. To customize the theme, you'll need to modify your tailwind.config.js file. This allows you to change colors, typography, spacing, and more.

// tailwind.config.js
module.exports = {
  // Enable dark mode
  darkMode: 'class',
  
  theme: {
    extend: {
      colors: {
        // Primary colors (used for buttons, links, focus rings)
        primary: {
          50: '#f0f9ff',
          100: '#e0f2fe',
          200: '#bae6fd',
          300: '#7dd3fc',
          400: '#38bdf8',
          500: '#0ea5e9',
          600: '#0284c7',
          700: '#0369a1',
          800: '#075985',
          900: '#0c4a6e',
          950: '#082f49',
        },
        // Secondary colors (used for text, backgrounds, borders)
        secondary: {
          50: '#f8fafc',
          100: '#f1f5f9',
          200: '#e2e8f0',
          300: '#cbd5e1',
          400: '#94a3b8',
          500: '#64748b',
          600: '#475569',
          700: '#334155',
          800: '#1e293b',
          900: '#0f172a',
          950: '#020617',
        },
      },
      // Custom border radius
      borderRadius: {
        'sm': '0.125rem',
        DEFAULT: '0.25rem',
        'md': '0.375rem',
        'lg': '0.5rem',
        'xl': '0.75rem',
        '2xl': '1rem',
      },
      // Custom box shadows
      boxShadow: {
        'sm': '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
        DEFAULT: '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)',
        'md': '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)',
        'lg': '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
      },
    },
  },
  plugins: [],
}

Key Configuration Areas

Colors

Define your brand colors in the colors section. ReacUI uses these color scales:

  • primary: Brand colors, calls-to-action
  • secondary: UI text, borders, backgrounds
  • Semantic colors: success, warning, error, info

Typography

Customize fonts, sizes, weights, and line heights:

fontFamily: {
  sans: ['Inter', 'sans-serif'],
  mono: ['Fira Code', 'monospace'],
},
fontSize: {
  xs: ['0.75rem', { lineHeight: '1rem' }],
  sm: ['0.875rem', { lineHeight: '1.25rem' }],
  base: ['1rem', { lineHeight: '1.5rem' }],
  lg: ['1.125rem', { lineHeight: '1.75rem' }],
  xl: ['1.25rem', { lineHeight: '1.75rem' }],
  // ...etc
}

Spacing & Sizing

Control margins, padding, width, and height:

spacing: {
  px: '1px',
  0: '0',
  0.5: '0.125rem',
  1: '0.25rem',
  // ...continues with 1.5, 2, etc.
},

Breakpoints

Define responsive breakpoints:

screens: {
  sm: '640px',
  md: '768px',
  lg: '1024px',
  xl: '1280px',
  '2xl': '1536px',
},

Color System

ReacUI uses a systematic approach to colors. Each color in the palette includes a range of shades from 50 (lightest) to 950 (darkest), giving you fine-grained control over your UI.

Default Color Palette

Primary Colors

50
100
200
300
400
500
600
700
800
900
950

Secondary Colors

50
100
200
300
400
500
600
700
800
900
950

Success

Warning

Error

Customizing Colors

To customize the color palette, replace the default colors with your brand colors in the Tailwind configuration. It's recommended to use a complete scale for each color to ensure proper contrast in different UI states.

Custom Brand Colors

Purple Brand (Primary)

50
100
200
300
400
500
600
700
800
900
950

Default Button

Purple Button

Color Accessibility

When customizing colors, ensure they meet WCAG accessibility guidelines for contrast. Tools like Color Review can help you check contrast ratios.

Dark Mode

ReacUI has built-in support for dark mode using Tailwind's dark mode feature. The dark mode is implemented using the 'class' strategy, allowing you to toggle it programmatically.

Dark Mode Implementation

Light Mode

User Profile
JD
John Doe
john@example.com
Projects
Dashboard Redesign
Mobile App

Dark Mode

User Profile
JD
John Doe
john@example.com
Projects
Dashboard Redesign
Mobile App

Dark Mode Design Principles

Don't Just Invert Colors

Dark mode isn't just about swapping light for dark. Use slightly desaturated colors and reduce contrast for text and UI elements to reduce eye strain.

Test for Accessibility

Ensure your dark theme maintains sufficient contrast ratios for text and interactive elements. What works in light mode may not work in dark mode.

Avoid Pure Black

Use dark grays (like #121212 or #1e1e1e) instead of pure black for backgrounds. Pure black can cause eye strain and create too much contrast.

Use Shadows Carefully

In dark mode, reduce shadow opacity and blur. Consider using lighter shadows on dark backgrounds for better depth perception.

Custom Themes

Create custom themes by extending the base theme with your own design tokens. This approach allows you to maintain consistency across your application while providing flexibility for different visual styles.

// themes.js - Theme configuration
export const themes = {
  default: {
    name: 'Default',
    colors: {
      primary: {
        50: '#f0f9ff',
        // ... other shades
        600: '#0284c7', // Primary action color
        // ... other shades
      },
      radius: {
        sm: '0.125rem',
        md: '0.25rem',
        lg: '0.5rem',
      }
    }
  },
  modern: {
    name: 'Modern',
    colors: {
      primary: {
        50: '#f5f3ff',
        // ... other shades
        600: '#7c3aed', // Purple primary
        // ... other shades
      },
      radius: {
        sm: '0.5rem',
        md: '0.75rem', 
        lg: '1rem',
      }
    }
  },
  minimal: {
    name: 'Minimal',
    colors: {
      primary: {
        50: '#f9fafb',
        // ... other shades
        600: '#374151', // Gray primary
        // ... other shades
      },
      radius: {
        sm: '0',
        md: '0.125rem',
        lg: '0.25rem',
      }
    }
  }
}

// ThemeContext.jsx
'use client'

import { createContext, useContext, useState, useEffect } from 'react'
import { themes } from './themes'

const ThemeContext = createContext({
  theme: 'default',
  colorMode: 'light',
  setTheme: () => {},
  toggleColorMode: () => {},
})

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('default')
  const [colorMode, setColorMode] = useState('light')
  
  const toggleColorMode = () => {
    const newMode = colorMode === 'light' ? 'dark' : 'light'
    setColorMode(newMode)
    
    if (newMode === 'dark') {
      document.documentElement.classList.add('dark')
    } else {
      document.documentElement.classList.remove('dark')
    }
    
    localStorage.setItem('colorMode', newMode)
  }
  
  const changeTheme = (newTheme) => {
    setTheme(newTheme)
    localStorage.setItem('theme', newTheme)
    
    // Apply theme-specific CSS variables
    const root = document.documentElement
    const currentTheme = themes[newTheme]
    
    // Apply primary color variables
    for (const [shade, value] of Object.entries(currentTheme.colors.primary)) {
      root.style.setProperty(`--color-primary-${shade}`, value)
    }
    
    // Apply border radius
    for (const [size, value] of Object.entries(currentTheme.colors.radius)) {
      root.style.setProperty(`--radius-${size}`, value)
    }
  }
  
  useEffect(() => {
    // Initialize on client side
    const savedTheme = localStorage.getItem('theme') || 'default'
    const savedColorMode = localStorage.getItem('colorMode')
    const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
    
    if (savedColorMode === 'dark' || (!savedColorMode && prefersDark)) {
      setColorMode('dark')
      document.documentElement.classList.add('dark')
    }
    
    changeTheme(savedTheme)
  }, [])
  
  return (
    <ThemeContext.Provider value={{ 
      theme, 
      colorMode, 
      setTheme: changeTheme, 
      toggleColorMode 
    }}>
      {children}
    </ThemeContext.Provider>
  )
}

export function useTheme() {
  return useContext(ThemeContext)
}

Theme Selector Component

Create a theme selector component to allow users to switch between different themes:

// ThemeSelector.jsx
import { useTheme } from '../context/ThemeContext'
import { themes } from '../themes'

export function ThemeSelector() {
  const { theme, setTheme } = useTheme()
  
  return (
    <div className="p-4 border rounded-lg">
      <h3 className="font-medium mb-3">Select Theme</h3>
      <div className="flex flex-wrap gap-2">
        {Object.keys(themes).map((themeKey) => (
          <button
            key={themeKey}
            className={`px-3 py-1.5 rounded-md ${
              theme === themeKey 
                ? 'bg-primary-600 text-white' 
                : 'bg-secondary-100 dark:bg-secondary-800 text-secondary-700 dark:text-secondary-300'
            }`}
            onClick={() => setTheme(themeKey)}
          >
            {themes[themeKey].name}
          </button>
        ))}
      </div>
    </div>
  )
}

Theme Preview

This is a message box

Remember my preferences

Component-Specific Styling

ReacUI components are designed to be easily customizable through props and class names. Here are some common patterns for customizing specific components.

Button Variants

// Custom button variants in your Button component
// components/Button.jsx

import { cva } from 'class-variance-authority'
import { cn } from '../lib/utils'

const buttonVariants = cva(
  "inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
  {
    variants: {
      variant: {
        default: "bg-primary-600 text-white hover:bg-primary-700 focus-visible:ring-primary-500",
        secondary: "bg-secondary-200 text-secondary-700 dark:bg-secondary-700 dark:text-secondary-200 hover:bg-secondary-300 dark:hover:bg-secondary-600",
        outline: "border border-secondary-300 dark:border-secondary-600 bg-transparent hover:bg-secondary-50 dark:hover:bg-secondary-800",
        ghost: "bg-transparent hover:bg-secondary-100 dark:hover:bg-secondary-800 text-secondary-700 dark:text-secondary-300",
        danger: "bg-red-600 text-white hover:bg-red-700 focus-visible:ring-red-500",
        // Add your custom variants
        gradient: "bg-gradient-to-r from-primary-600 to-purple-600 text-white hover:from-primary-700 hover:to-purple-700",
      },
      size: {
        sm: "h-8 px-3 text-xs",
        md: "h-10 px-4 py-2",
        lg: "h-12 px-6 text-lg",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "md",
    },
  }
)

export function Button({
  className,
  variant,
  size,
  children,
  ...props
}) {
  return (
    <button
      className={cn(buttonVariants({ variant, size }), className)}
      {...props}
    >
      {children}
    </button>
  )
}

// Usage:
<Button variant="gradient" size="lg">
  Sign Up Now
</Button>

Button Style Examples

Default Variant

Custom Gradient

Size Variants

Custom Classes

Card Component Styling

// Card component with variants
// components/Card.jsx

import { cva } from 'class-variance-authority'
import { cn } from '../lib/utils'

const cardVariants = cva(
  "rounded-lg border overflow-hidden",
  {
    variants: {
      variant: {
        default: "bg-white dark:bg-secondary-800 border-secondary-200 dark:border-secondary-700",
        outline: "bg-transparent border-secondary-200 dark:border-secondary-700",
        filled: "bg-secondary-100 dark:bg-secondary-800 border-secondary-100 dark:border-secondary-800",
        // Custom variant
        primary: "bg-primary-50 dark:bg-primary-900/20 border-primary-200 dark:border-primary-700",
      },
      shadow: {
        none: "",
        sm: "shadow-sm",
        md: "shadow-md",
        lg: "shadow-lg",
      }
    },
    defaultVariants: {
      variant: "default",
      shadow: "none",
    },
  }
)

export function Card({
  className,
  variant,
  shadow,
  children,
  ...props
}) {
  return (
    <div
      className={cn(cardVariants({ variant, shadow }), className)}
      {...props}
    >
      {children}
    </div>
  )
}

export function CardHeader({ className, ...props }) {
  return (
    <div
      className={cn("px-6 py-4 border-b border-secondary-200 dark:border-secondary-700", className)}
      {...props}
    />
  )
}

export function CardContent({ className, ...props }) {
  return (
    <div
      className={cn("px-6 py-4", className)}
      {...props}
    />
  )
}

export function CardFooter({ className, ...props }) {
  return (
    <div
      className={cn("px-6 py-4 border-t border-secondary-200 dark:border-secondary-700", className)}
      {...props}
    />
  )
}

// Usage:
<Card variant="primary" shadow="md" className="max-w-md">
  <CardHeader>
    <h3 className="text-lg font-semibold">Card Title</h3>
  </CardHeader>
  <CardContent>
    <p>Card content goes here</p>
  </CardContent>
  <CardFooter>
    <Button>Action</Button>
  </CardFooter>
</Card>

Card Style Examples

Default Card

Default card with standard styling

Primary Card

Custom primary themed card with shadow

Best Practices

Use Design Tokens for Consistency

Define all your colors, spacing, typography, and other design variables as tokens in your theme configuration. This ensures consistency across your application and makes it easier to update your design system.

Create Component Abstractions

Build component abstractions that encapsulate styling logic. Use utility libraries like class-variance-authority (cva) to create reusable component variants rather than repeating the same class combinations.

// Good: Encapsulated button styles
<Button variant="primary" size="lg" />

// Avoid: Repeating class combinations
<button className="px-4 py-2 bg-primary-600 text-white rounded-md hover:bg-primary-700" />

Test All Color Modes

Always test your components in both light and dark mode. A design that looks good in light mode might have contrast or readability issues in dark mode, and vice versa.

Design for Accessibility First

Ensure your theme meets WCAG accessibility guidelines. Pay special attention to color contrast ratios, font sizes, and interactive states to ensure your UI is accessible to all users.

Common Pitfalls to Avoid

Hardcoding Colors

Avoid using hardcoded color values like #3b82f6 directly in your components. Always use theme tokens (bg-primary-600) instead.

Overriding Core Components

Avoid directly modifying core component files. Instead, create wrapper components or use composition to extend functionality while keeping the original components intact.

Ignoring Dark Mode

Don't assume everyone uses light mode. Always design and test for both light and dark themes to ensure a good user experience for all users.

Too Many Custom Variants

Don't create a unique variant for every special case. Stick to a reasonable number of reusable variants, and use composition or local customization for edge cases.

Theme Configuration Checklist

  • Define a coherent color palette with primary, secondary, and semantic colors
  • Configure typography with consistent font families, sizes, weights, and line heights
  • Set up a consistent spacing system for margins, padding, and layout
  • Define border radius values that match your design language
  • Configure shadows for elevation and depth
  • Test all components in both light and dark mode
  • Verify accessibility compliance for all theme variations