loader

Theming with SCSS

What is Theming in SCSS?

Theming in SCSS refers to creating a system of variables and styles that can be easily switched or customized to change the visual appearance of a website or application. This approach allows for consistent styling across components and enables features like dark/light mode switching.

Key components of a theme system:

  • Variables: Define colors, fonts, spacing, and other design tokens
  • Maps: Organize related variables into structured collections
  • Mixins: Create reusable patterns that apply theme variables
  • Functions: Manipulate theme values programmatically
  • Component Styles: Apply theme variables consistently across components

Creating a Basic Theme System

Start by defining your theme variables in a dedicated file:


// _theme-variables.scss

// Primary colors
$primary-color: #3498db;
$secondary-color: #2ecc71;
$accent-color: #e74c3c;

// Neutral colors
$text-color: #333333;
$background-color: #ffffff;
$border-color: #dddddd;

// Typography
$font-family-base: 'Roboto', sans-serif;
$font-size-base: 16px;
$line-height-base: 1.5;

// Spacing
$spacing-unit: 8px;
$spacing-small: $spacing-unit;
$spacing-medium: $spacing-unit * 2;
$spacing-large: $spacing-unit * 3;

// Border radius
$border-radius: 4px;
                        

Then use these variables throughout your application:


// _button.scss
.button {
  background-color: $primary-color;
  color: $background-color;
  font-family: $font-family-base;
  padding: $spacing-small $spacing-medium;
  border-radius: $border-radius;
  border: none;
  
  &:hover {
    background-color: darken($primary-color, 10%);
  }
  
  &.secondary {
    background-color: $secondary-color;
    
    &:hover {
      background-color: darken($secondary-color, 10%);
    }
  }
}
                        

Advanced Theming with Maps

Using SCSS maps allows for more structured and flexible theme systems:


// _theme-maps.scss

// Define theme colors as maps
$theme-colors: (
  'primary': #3498db,
  'secondary': #2ecc71,
  'success': #27ae60,
  'danger': #e74c3c,
  'warning': #f39c12,
  'info': #3498db,
  'light': #ecf0f1,
  'dark': #2c3e50
);

// Access theme colors with a function
@function theme-color($key) {
  @return map-get($theme-colors, $key);
}

// Usage
.alert-success {
  background-color: theme-color('success');
  color: white;
}
                        

Creating Multiple Themes

You can define multiple themes using nested maps:


// Define multiple themes
$themes: (
  'light': (
    'background': #ffffff,
    'text': #333333,
    'border': #dddddd,
    'primary': #3498db,
    'secondary': #2ecc71
  ),
  'dark': (
    'background': #2c3e50,
    'text': #ecf0f1,
    'border': #7f8c8d,
    'primary': #3498db,
    'secondary': #2ecc71
  )
);

// Theme mixin
@mixin themed() {
  @each $theme, $map in $themes {
    .theme-#{$theme} & {
      $theme-map: () !global;
      @each $key, $value in $map {
        $theme-map: map-merge($theme-map, ($key: $value)) !global;
      }
      @content;
      $theme-map: null !global;
    }
  }
}

// Access the current theme
@function t($key) {
  @return map-get($theme-map, $key);
}

// Usage
.card {
  @include themed() {
    background-color: t('background');
    color: t('text');
    border: 1px solid t('border');
  }
}
                        

Implementing Dark/Light Mode

A common use case for theming is implementing dark and light modes:

1. Define theme variables


// _theme-variables.scss

// Light theme (default)
:root {
  --color-background: #ffffff;
  --color-text: #333333;
  --color-primary: #3498db;
  --color-secondary: #2ecc71;
  --color-border: #dddddd;
}

// Dark theme
.dark-theme {
  --color-background: #1a1a1a;
  --color-text: #f5f5f5;
  --color-primary: #3498db;
  --color-secondary: #2ecc71;
  --color-border: #444444;
}
                        

2. Use CSS variables in your SCSS


body {
  background-color: var(--color-background);
  color: var(--color-text);
}

.button {
  background-color: var(--color-primary);
  color: white;
  border: 1px solid var(--color-border);
}
                        

3. Add JavaScript for theme switching


// theme-switcher.js
document.getElementById('theme-toggle').addEventListener('click', function() {
  document.body.classList.toggle('dark-theme');
  
  // Optional: Save preference to localStorage
  const isDarkMode = document.body.classList.contains('dark-theme');
  localStorage.setItem('darkMode', isDarkMode);
});

// Check for saved theme preference
document.addEventListener('DOMContentLoaded', function() {
  const isDarkMode = localStorage.getItem('darkMode') === 'true';
  if (isDarkMode) {
    document.body.classList.add('dark-theme');
  }
});
                        
Pro Tip: You can also respect the user's system preference using the prefers-color-scheme media query in your SCSS:
@media (prefers-color-scheme: dark) {
  :root {
    --color-background: #1a1a1a;
    --color-text: #f5f5f5;
    // Other dark theme variables
  }
}

Best Practices for Theme Systems

  • Consistent naming: Use a clear naming convention for all theme variables
  • Semantic variables: Name variables by their purpose, not their value (e.g., $primary-color not $blue)
  • Layer your variables: Create base tokens and derived tokens
  • Document your theme: Create a style guide showing all theme components
  • Test all themes: Ensure all UI components work in all theme variations
  • Consider accessibility: Ensure sufficient contrast in all themes

Example: Layered Variables


// Base tokens (primitive values)
$color-blue-500: #3498db;
$color-green-500: #2ecc71;
$color-gray-100: #f5f5f5;
$color-gray-900: #1a1a1a;

// Semantic tokens (usage-based)
$color-primary: $color-blue-500;
$color-secondary: $color-green-500;
$color-background-light: $color-gray-100;
$color-background-dark: $color-gray-900;

// Component tokens (component-specific)
$button-background: $color-primary;
$button-text: white;
$card-background: $color-background-light;