Colors

Spruce CSS stores every color in one nested Sass map, $colors, and outputs each of them as a CSS custom property. That is what makes the dark mode and the design styles possible.

List of Colors

These are the default colors, grouped as in the $colors map. The swatches use the live custom properties, so they follow the theme mode and the style you pick in the header. Access any of them with the color() function: color('name', 'group').

Alert

danger $color-danger
info hsl(195deg 100% 42%)
success $color-success
warning hsl(48deg 89% 55%)

Base

background $color-white
blockquote-border $color-primary
border $color-gray-light
border-strong hsl(214deg 20% 78%)
code-background color.change($color-primary, $lightness: 97%)
code-foreground $color-black
focus-ring $color-primary
heading $color-black
kbd-border hsl(214deg 32% 84%)
link $color-primary
link-hover color.scale($color-primary, $lightness: -20%)
mark-background hsl(50deg 100% 80%)
mark-foreground $color-black
marker $color-primary
muted hsl(208deg 8% 46%)
overlay hsl(205deg 100% 2% / 55%)
pre-background color.change($color-primary, $lightness: 97%)
pre-foreground $color-black
primary $color-primary
secondary $color-secondary
strong $color-black
surface $color-white
surface-muted hsl(210deg 40% 98%)
text $color-gray

Btn

danger-background $color-danger
danger-background-hover color.adjust($color-danger, $lightness: -10%)
danger-foreground $color-white
primary-background $color-primary
primary-background-hover color.adjust($color-primary, $lightness: -10%)
primary-foreground $color-white
primary-shadow color.adjust($color-primary, $lightness: 35%)
secondary-background $color-secondary
secondary-background-hover color.adjust($color-secondary, $lightness: -10%)
secondary-foreground $color-white
secondary-shadow color.adjust($color-secondary, $lightness: 35%)

Form

background $color-white
background-disabled hsl(0deg 0% 95%)
border hsl(260deg 4% 70%)
border-disabled $color-gray-light
border-focus $color-primary
check-background $color-primary
check-focus-ring $color-primary
check-foreground $color-white
group-label-background hsl(210deg 60% 98%)
group-label-foreground $color-gray
invalid $color-danger
invalid-focus-ring color.adjust($color-danger, $alpha: -0.75)
label $color-black
legend $color-black
placeholder hsl(208deg 7% 40%)
progress-background $color-gray-light
progress-foreground $color-primary
range-thumb-background $color-primary
range-thumb-focus-ring $color-primary
range-track-background $color-gray-light
required $color-danger
ring-focus color.adjust($color-primary, $alpha: -0.75)
select-foreground $color-black
switch-background $color-primary
switch-focus-ring $color-primary
switch-foreground $color-white
text $color-gray
valid $color-success
valid-focus-ring color.adjust($color-success, $alpha: -0.75)

Selection

foreground $color-white
background $color-primary

Scrollbar

thumb-background hsl(0deg 0% 0% / 15%)
thumb-background-hover hsl(0deg 0% 0% / 25%)
track-background hsl(0deg 0% 0% / 5%)

Table

border $color-gray-light
caption $color-gray
heading $color-black
hover hsl(0deg 0% 0% / 5%)
stripe hsl(0deg 0% 0% / 2.5%)
text $color-gray

Some keys are null by default (like background-readonly or the table heading-background). Spruce doesn’t output anything for them until you give them a value.

Shared Colors

To avoid repetition, the map uses a few helper variables for the generic colors. Overwrite them to retheme Spruce quickly, or set the map values one by one.

@use 'sprucecss/scss/spruce' with (
  $color-black: hsl(205deg 100% 2%),
  $color-danger: hsl(0deg 71% 51%),
  $color-gray: hsl(208deg 9% 42%),
  $color-gray-light: hsl(215deg 63% 93%),
  $color-primary: hsl(262deg 71% 49%),
  $color-secondary: hsl(227deg 92% 55%),
  $color-success: hsl(150deg 100% 33%),
  $color-white: hsl(0deg 0% 100%),
);

The Full Map

Below is the full declaration of the $colors map (from the installed version).

$colors: () !default;
$colors: map.deep-merge(
  (
    'alert': (
      'danger': $color-danger,
      'info': hsl(195deg 100% 42%),
      'success': $color-success,
      'warning': hsl(48deg 89% 55%),
    ),
    'base': (
      'background': $color-white,
      'blockquote-border': $color-primary,
      'border': $color-gray-light,
      'border-strong': hsl(214deg 20% 78%),
      'code-background': color.change($color-primary, $lightness: 97%),
      'code-foreground': $color-black,
      'focus-ring': $color-primary,
      'heading': $color-black,
      'kbd-border': hsl(214deg 32% 84%),
      'link': $color-primary,
      'link-hover': color.scale($color-primary, $lightness: -20%),
      'mark-background': hsl(50deg 100% 80%),
      'mark-foreground': $color-black,
      'marker': $color-primary,
      'muted': hsl(208deg 8% 46%),
      'overlay': hsl(205deg 100% 2% / 55%),
      'pre-background': color.change($color-primary, $lightness: 97%),
      'pre-foreground': $color-black,
      'primary': $color-primary,
      'secondary': $color-secondary,
      'strong': $color-black,
      'surface': $color-white,
      'surface-muted': hsl(210deg 40% 98%),
      'text': $color-gray,
    ),
    'btn': (
      'danger-background': $color-danger,
      'danger-background-hover': color.adjust($color-danger, $lightness: -10%),
      'danger-foreground': $color-white,
      'primary-background': $color-primary,
      'primary-background-hover': color.adjust($color-primary, $lightness: -10%),
      'primary-foreground': $color-white,
      'primary-shadow': color.adjust($color-primary, $lightness: 35%),
      'secondary-background': $color-secondary,
      'secondary-background-hover': color.adjust($color-secondary, $lightness: -10%),
      'secondary-foreground': $color-white,
      'secondary-shadow': color.adjust($color-secondary, $lightness: 35%),
    ),
    'form': (
      'background': $color-white,
      'background-disabled': hsl(0deg 0% 95%),
      'background-readonly': null,
      'border': hsl(260deg 4% 70%),
      'border-disabled': $color-gray-light,
      'border-focus': $color-primary,
      'check-background': $color-primary,
      'check-focus-ring': $color-primary,
      'check-foreground': $color-white,
      'group-label-background': hsl(210deg 60% 98%),
      'group-label-foreground': $color-gray,
      'invalid': $color-danger,
      'invalid-focus-ring': color.adjust($color-danger, $alpha: -0.75),
      'label': $color-black,
      'legend': $color-black,
      'placeholder': hsl(208deg 7% 40%),
      'progress-background': $color-gray-light,
      'progress-foreground': $color-primary,
      'range-thumb-background': $color-primary,
      'range-thumb-focus-ring': $color-primary,
      'range-track-background': $color-gray-light,
      'required': $color-danger,
      'ring-focus': color.adjust($color-primary, $alpha: -0.75),
      'select-foreground': $color-black,
      'switch-background': $color-primary,
      'switch-focus-ring': $color-primary,
      'switch-foreground': $color-white,
      'text': $color-gray,
      'valid': $color-success,
      'valid-focus-ring': color.adjust($color-success, $alpha: -0.75),
    ),
    'selection': (
      'foreground': $color-white,
      'background': $color-primary,
    ),
    'scrollbar': (
      'thumb-background': hsl(0deg 0% 0% / 15%),
      'thumb-background-hover': hsl(0deg 0% 0% / 25%),
      'track-background': hsl(0deg 0% 0% / 5%),
    ),
    'table': (
      'border': $color-gray-light,
      'caption': $color-gray,
      'heading': $color-black,
      'heading-background': null,
      'hover': hsl(0deg 0% 0% / 5%),
      'stripe': hsl(0deg 0% 0% / 2.5%),
      'text': $color-gray,
    ),
  ),
  $colors
);

We declare the colors with the modern HSL syntax; Sass converts them in the output.

New Colors in v3

GroupKeyUsed by
baseborder-strongStronger dividers, empty states.
basefocus-ringThe summary focus ring and the components.
basekbd-borderThe kbd element.
basemutedSecondary text (.text-muted, descriptions).
baseoverlayThe dialog backdrop.
basepre-background, pre-foregroundCode blocks.
basesurface, surface-mutedCards and panels (follows the styles).
btndanger-*The danger button variants.
formbackground-readonlyRead-only form controls (null by default).
formprogress-background, progress-foregroundprogress and meter.
formrequiredThe .form-label--required marker.
tableheading-backgroundThe table header row (null by default).

Get Color Values

Spruce generates prefixed CSS variables at :root by group from the nested map. You can access a generated variable by name, but it is easier to use the getter function, color(). For the raw Sass value, use color-value().

a {
  color: color('primary'); // var(--spruce-base-color-primary)
}

.control {
  background-color: color('primary-background', 'btn');
}

.overlay {
  // Runtime mixes that follow the theme and the style:
  background-color: color-alpha('heading', 0.6);
  border-color: color-tint('primary', 80%);
}

Overwrite, Add New Colors

Because the colors are in a Sass map, it is easy to overwrite them or add new values and groups.

@use 'sprucecss/scss/spruce' with (
  $colors: (
    'base': (
      'primary': hsl(0deg 0% 15%),
      'tertiary': hsl(332deg 49% 29%),
    ),
    'my-custom-component': (
      'background': hsl(0deg 0% 100%),
    ),
  ),
);

A new group is output as --spruce-my-custom-component-color-background, and you get it with color('background', 'my-custom-component').

Dark Colors

The $dark-colors map holds your dark mode colors. It is deep-merged into the built-in $default-dark-colors palette by the dark mode generator, so you only set what differs.

The default dark palette
$dark-color-background: hsl(206deg 100% 7%) !default;
$dark-color-border: hsl(0deg 0% 100% / 10%) !default;
$dark-color-heading: hsl(0deg 0% 95%) !default;
$dark-color-primary: hsl(261deg 54% 70%) !default;
$dark-color-secondary: hsl(227deg 92% 65%) !default;
$dark-color-text: hsl(210deg 14% 80%) !default;

$default-dark-colors: () !default;

$default-dark-colors: () !default;
$default-dark-colors: map.deep-merge(
  (
    'alert': (
      'danger': hsl(0deg 80% 62%),
      'info': hsl(195deg 90% 55%),
      'success': hsl(150deg 70% 45%),
      'warning': hsl(48deg 89% 60%),
    ),
    'base': (
      'background': $dark-color-background,
      'blockquote-border': $dark-color-primary,
      'border': $dark-color-border,
      'border-strong': hsl(0deg 0% 100% / 22%),
      'code-background': hsl(207deg 64% 16%),
      'code-foreground': $dark-color-heading,
      'focus-ring': $dark-color-primary,
      'heading': $dark-color-heading,
      'kbd-border': hsl(0deg 0% 100% / 18%),
      'link': $dark-color-primary,
      'link-hover': color.scale($dark-color-primary, $lightness: 25%),
      'mark-background': hsl(50deg 100% 80%),
      'mark-foreground': $dark-color-background,
      'marker': $dark-color-primary,
      'muted': hsl(210deg 10% 62%),
      'overlay': hsl(0deg 0% 0% / 70%),
      'pre-background': hsl(207deg 64% 12%),
      'pre-foreground': $dark-color-heading,
      'primary': $dark-color-primary,
      'secondary': $dark-color-secondary,
      'strong': $dark-color-heading,
      'surface': color.scale($dark-color-background, $lightness: 4%),
      'surface-muted': color.scale($dark-color-background, $lightness: 2%),
      'text': $dark-color-text,
    ),
    'btn': (
      'danger-background': hsl(0deg 70% 55%),
      'danger-background-hover': hsl(0deg 70% 62%),
      'danger-foreground': hsl(0deg 0% 100%),
      'primary-background': hsl(261deg 52% 59%),
      'primary-background-hover': hsl(261deg 52% 65%),
      'primary-foreground': hsl(0deg 0% 100%),
      'primary-shadow': hsl(261deg 52% 25%),
      'secondary-background': hsl(227deg 80% 58%),
      'secondary-background-hover': hsl(227deg 80% 64%),
      'secondary-foreground': hsl(0deg 0% 100%),
      'secondary-shadow': hsl(227deg 80% 25%),
    ),
    'form': (
      'background': color.scale($dark-color-background, $lightness: 5%),
      'background-disabled': $dark-color-background,
      'border': hsl(0deg 0% 100% / 16%),
      'border-disabled': $dark-color-border,
      'border-focus': $dark-color-primary,
      'check-background': $dark-color-primary,
      'check-focus-ring': $dark-color-primary,
      'check-foreground': $dark-color-background,
      'group-label-background': color.scale($dark-color-background, $lightness: 3%),
      'group-label-foreground': $dark-color-text,
      'invalid': hsl(0deg 80% 62%),
      'invalid-focus-ring': hsl(0deg 80% 62% / 25%),
      'label': $dark-color-heading,
      'legend': $dark-color-heading,
      'placeholder': hsl(0deg 0% 60%),
      'progress-background': hsl(0deg 0% 100% / 12%),
      'progress-foreground': $dark-color-primary,
      'range-thumb-background': $dark-color-primary,
      'range-thumb-focus-ring': $dark-color-primary,
      'range-track-background': hsl(0deg 0% 100% / 12%),
      'required': hsl(0deg 80% 62%),
      'ring-focus': hsl(261deg 54% 70% / 25%),
      'select-foreground': hsl(0deg 0% 100%),
      'switch-background': $dark-color-primary,
      'switch-focus-ring': $dark-color-primary,
      'switch-foreground': $dark-color-background,
      'text': $dark-color-text,
      'valid': hsl(150deg 70% 45%),
      'valid-focus-ring': hsl(150deg 70% 45% / 25%),
    ),
    'scrollbar': (
      'thumb-background': hsl(0deg 0% 100% / 15%),
      'thumb-background-hover': hsl(0deg 0% 100% / 25%),
      'track-background': hsl(0deg 0% 100% / 5%),
    ),
    'selection': (
      'background': $dark-color-primary,
      'foreground': $dark-color-background,
    ),
    'table': (
      'border': $dark-color-border,
      'caption': $dark-color-text,
      'heading': $dark-color-heading,
      'hover': hsl(0deg 0% 100% / 5%),
      'stripe': hsl(0deg 0% 100% / 2.5%),
      'text': $dark-color-text,
    ),
  ),
  $default-dark-colors
);

List Colors

In this example, you can see how to generate a component (an alert) from the alert colors with Sass’s @each and map.get.

@use 'sass:color';
@use 'sass:map';
@use 'sprucecss/scss/spruce' as *;

.alert {
  border: 1px solid;
  border-inline-start-width: 0.35rem;
  border-radius: radius('lg');
  padding: 0.65em 1em;

  @each $name, $value in map.get($colors, 'alert') {
    &--#{$name} {
      background-color: color-tint($name, 90%, 'alert');
      color: color.scale($value, $lightness: -30%);
    }
  }
}

Documentation

Learn about Spruce CSS through our extensive documentation.

Components

Explore our UI library of accessible components built with Spruce CSS.

Blocks and Pages

Full-width page blocks and complete page templates, ready to copy.

Find us on GitHub

Did you find a bug? Have an idea or a question? Please open an issue to help us develop the project.