Spruce uses CSS custom properties to handle the theming. It means that you can easily overwrite the colors if needed (like in a case of a dark theme mode).

Detect Dark Mode

Achieving a dark or any color mode can be different project by project. Spruce CSS gives you the structure to handle themes, but it’s on you to manage end detect them.

Our method usually adds a data-theme-mode attribute on the HTML element and does the styling based on its value. We detect the prefers-color-scheme and/or the user settings, then add the dark value if needed.

<html lang="en" data-theme-mode="dark">

Set Up the Dark Colors

Setting up any theme mode is simple but can be tricky and complicated because of the many values (from the $colors map). For redeclaring dark mode, we have a predefined empty $dark-colors map which you can use when importing Spruce (but you can use any object you like).

First, declare the new colors, and keep in mind you have to use the same key names because we will overwrite them.

The following values are the ones that we use for this site’s dark mode.

@use 'sass:color';
$color-primary: hsl(261 54% 70%);
$color-secondary: hsl(227 92% 55%);
$color-black: hsl(206 100% 7%);
$color-white: hsl(0 0% 95%);
$color-gray: hsl(0 0% 97%);
$color-gray-dark: hsl(0 0% 100% / 8%);
$color-danger: hsl(0 71% 51%);
$color-success: hsl(150 100% 33%);
$colors: (
base: (
card-border: hsl(207 90% 13%),
primary: $color-primary,
secondary: $color-secondary,
background: $color-black,
heading: $color-white,
text: $color-gray,
link: $color-primary,
link-hover: color.scale($color-primary, $lightness: -20%),
border: $color-gray-dark,
mark-background: hsl(50 100% 80%),
mark-foreground: $color-black,
marker: $color-primary,
code-background: hsl(207 64% 18%),
code-foreground: $color-white,
blockquote-border: $color-primary,
footer-background: hsl(0 0% 0% / 0.15)
btn: (
primary-background: hsl(261 52% 59%),
primary-background-hover: hsl(261 52% 65%),
primary-foreground: $color-white,
secondary-background: $color-secondary,
secondary-background-hover: color.adjust($color-secondary, $lightness: 5%),
secondary-foreground: $color-white
form: (
background: color.scale($color-black, $lightness: 5%),
background-disabled: $color-black,
border: $color-gray-dark,
border-disabled: $color-gray-dark,
border-focus: $color-primary,
ring-focus: color.adjust($color-primary, $alpha: -0.75),
check-foreground: $color-black,
check-background: $color-primary,
check-focus-ring: $color-primary,
invalid: $color-danger,
invalid-focus-ring: color.adjust($color-danger, $alpha: -0.75),
label: $color-white,
legend: $color-white,
placeholder: hsl(0 0% 60%),
range-thumb-background: $color-primary,
range-thumb-focus-ring: $color-primary,
range-track-background: $color-gray-dark,
text: $color-gray,
select-foreground: hsl(0 0% 100%),
valid: $color-success,
valid-focus-ring: color.adjust($color-success, $alpha: -0.75)
selection: (
foreground: $color-white,
background: $color-primary
scrollbar: (
thumb-background: hsl(0 0% 100% / 0.15),
thumb-background-hover: hsl(0 0% 100% / 0.25),
track-background: hsl(0 0% 100% / 0.05)
table: (
border: $color-gray-dark,
caption: $color-gray,
heading: $color-white,
hover: hsl(0 0% 100% / 0.05),
text: $color-gray,
stripe: hsl(0 0% 100% / 0.025)

Generate the Variables

Use the generate-color-variables to generate the color variables under a specified selector.

@use '~sprucecss/scss/spruce';
@include spruce.generate-color-variables(

Set Up Other Necessities

Usually, setting up a theme is not just mean overwriting the color variables. We want to do more.

For example, we want to set the color-scheme to dark to discolor the scrollbars. Also, in the case of Spruce CSS, we may wish to change the select image too.

[data-theme-mode="dark"] {
color-scheme: dark;
select.form-control:not([multiple]):not([size]) {
@include spruce.field-icon(
spruce.color(select-foreground, form, true, spruce.$dark-colors)