Spruce CSS comes with a bunch of Sass variables. It also uses CSS custom properties for colors and transitions.

Our central concept is not to expose a CSS custom property that isn’t needed for later customization, like the colors (for prefers-color-scheme or any later overwrite) or the transitions (for prefers-reduced-motion). Also, this is a framework for better customization Sass variables are needed (even if we generate CSS custom properties based on them). Like most things, this can change in the future depending on the use cases.

More information about the difference between Sass variables and CSS variables.

View Source on GitHub

Get Any Variable

To get a variable, you have to use one of the getters or the variable name with the spruce namespace (or without one if you imported the code that way).

To see the complete list of the getter functions, please visit the functions page.

.component {
border-radius: spruce.$border-radius-lg;
color: spruce.color(secondary);
font-size: spruce.font-size(h2);
padding-block: spruce.spacer(m);

Override Variables

There are two types of variables: single one and map. All of them are overwritable, expandable with @use's with option.

@use '~sprucecss/scss/spruce' with (
$colors: (
base: (
primary-darkest: hsl(272, 100%, 9%),
heading: hsl(271deg, 100%, 9%)
btn: (
new-background: hsl(0, 0%, 95%), // Expanded new color values for buttons.
new-foreground: hsl(0, 0%, 10%)
$font-size-base: 1.063rem,
$font-size-ratio: 1.250,
$container-width: 86rem

You must do the overwrite (of any of the variables or maps) at the first use of @use in your code — more information about the newer imports in Sass.


We store the color values in a central map named $color. It is a nested object separated by components.

To eliminate the repetition, we use some helper variables for the more generic colors (which we use more than once). You can overwrite these directly to make the theming easier or from the object one by one.

You can access any color with the color() helper function.

$color-primary: hsl(261 52% 59%);
$color-secondary: hsl(227 92% 55%);
$color-black: hsl(205 100% 2%);
$color-white: hsl(0 0% 100%);
$color-gray-light: hsl(0 0% 90%);
$color-gray: hsl(208 7% 30%);
$color-danger: hsl(0 71% 51%);
$color-success: hsl(150 100% 33%);
$colors: (
alert: (
danger: $color-danger,
info: hsl(195 100% 42%),
success: $color-success,
warning: hsl(48 89% 55%)
base: (
primary: $color-primary,
secondary: $color-secondary,
background: $color-white,
heading: $color-black,
text: $color-gray,
link: $color-primary,
link-hover: color.scale($color-primary, $lightness: 10%),
border: $color-gray-light,
mark-background: hsl(50 100% 80%),
mark-foreground: $color-black,
marker: $color-primary,
code-background: color.change($color-primary, $lightness: 95%),
code-foreground: $color-black,
blockquote-border: $color-primary
btn: (
primary-background: $color-primary,
primary-foreground: $color-white,
secondary-background: $color-secondary,
secondary-foreground: $color-white
form: (
background: $color-white,
background-disabled: hsl(0 0% 95%),
border: hsl(260 4% 70%),
border-disabled: $color-gray-light,
border-focus: $color-primary,
ring-focus: color.adjust($color-primary, $alpha: -0.75),
check-foreground: $color-white,
check-background: $color-primary,
check-focus-ring: $color-primary,
invalid: $color-danger,
invalid-focus-ring: color.adjust($color-danger, $alpha: -0.75),
label: $color-black,
legend: $color-black,
placeholder: hsl(208 7% 40%),
range-thumb-background: $color-primary,
range-thumb-focus-ring: $color-primary,
range-track-background: $color-gray-light,
text: $color-gray,
select-foreground: $color-black,
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% 0% / 0.15),
thumb-background-hover: hsl(0 0% 0% / 0.25),
track-background: hsl(0 0% 0% / 0.05)
table: (
border: $color-gray-light,
caption: $color-gray,
heading: $color-black,
hover: hsl(0 0% 0% / 0.05),
text: $color-gray,
stripe: hsl(0 0% 0% / 0.025)

We declare the colors with the modern HSL syntax. The generated values are given and converted by Sass by default.

Get Color Values

Spruce generates prefixed CSS variables at :root by group from the nested map above.

You can always access a generated variable by name, but it is easier to use the custom getter function named color().

a {
color: spruce.color(primary);

Overwrite, Add New Color

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

@use '~sprucecss/scss/spruce' with (
$colors: (
base: (
primary: rgb(75 38% 110%),
tertiary: hsl(332 49% 29%)
my-custom-component: (
background: hsl(0 0% 100%)

Dark Colors

Spruce CSS supports dark-mode, which we mainly achieve through CSS Custom Properties. You can use the $dark-colors map to set up your dark colors.

$dark-colors: ();

List Colors

In this example, you can see how you can generate a component - an alert - using Spruce’s colors with Sass’s @each and map.get.

@use 'sass:color';
@use 'sass:map';
@use '~sprucecss/scss/spruce';
.alert {
border: 1px solid;
border-left: 0.35rem solid;
border-radius: spruce.$border-radius-lg;
font-weight: 500;
gap: spruce.spacer(m);
justify-content: space-between;
padding: 0.65em 1em;
@each $name, $value in map.get(spruce.$colors, alert) {
@at-root .alert--#{$name} {
background-color: color.scale($value, $lightness: 90%);
color: color.scale($value, $lightness: -30%);


Spacers are generated by a predefined $spacer value, and the $spacers map with added multiplication.

$spacer: 1rem;
$spacers: (
xxs: $spacer * 0.25,
xs: $spacer * 0.5,
s: $spacer,
m: $spacer * 1.5,
l: $spacer * 3,
xl: $spacer * 4.5,
xxl: $spacer * 5,
xxxl: $spacer * 10

Get Spacers

Using the spacer() helper function we can get any value by key name from the $spacers map.

.component {
padding-inline: spruce.spacer(m);

Overwrite Spacers

You can easily overwrite each value or add new ones.

@use '~sprucecss/scss/spruce' with (
$spacer: 2rem,
$spacers: (
xl: 10rem

Overwriting the values using @use’s with can be tricky because we can’t reference other values under the with block. So in our new declaration under $spacers map, we can’t access the $spacer map inside Spruce.

A solution for this problem looks like something like this:

$spacer: 1.5rem
@use '~sprucecss/scss/spruce' with (
$spacer: $spacer,
$spacers: (
xl: $spacer * 6

We create our own $spacer variable passing it down to Spruce.


$border-radius-sm: 0.25rem;
$border-radius-lg: 0.5rem;


You can access any font size with the font-size() helper function.

Font Size

$font-size-ratio: 1.333;
$font-size-base: 1rem;
$font-size-sm: 0.9rem;
$font-size-lg: 1.1rem;
$font-size-lead: clamp(#{$font-size-lg}, 2vw, #{map.get($font-sizes, h4)});
$font-sizes: ();
$font-sizes: map.merge(
h1: math.pow($font-size-ratio, 4) * $font-size-base,
h2: math.pow($font-size-ratio, 3) * $font-size-base,
h3: math.pow($font-size-ratio, 2) * $font-size-base,
h4: math.pow($font-size-ratio, 1) * $font-size-base,
h5: $font-size-base,
h6: $font-size-base

Font Weight

$font-weight-base: null;

Font Family

$font-family-base: #{system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', 'Liberation Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'};
$font-family-heading: $font-family-base;
$font-family-cursive: #{SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace};


$heading-font-weight: 700;

Line Height

The line-height is always an interesting property when we set up the base typography. In Spruce, we use the:

  • $line-height-base, which is set on the body element,
  • $line-height-heading, which is set on the headings. The other variables (*-sm, *-md, *-lg) are generic helpers because there aren’t too many good answers here.

We use the formula for $line-height-heding from Jesús Ricarte; give it a read!

View on GitHub

Name Value
$line-height-sm 1.2
$line-height-md 1.5
$line-height-lg 1.8
$line-height-base $line-height-lg
$line-height-heading calc(2px + 2ex + 2px)
@use '~sprucecss/scss/spruce' with (
$line-height-sm: 1.2,
$line-height-md: 1.5,
$line-height-lg: 1.8,
$line-height-base: $line-height-lg,
$line-height-heading: calc(2px + 2ex + 2px)

Inline Elements

$inline-text-element-padding: 0.1em 0.3em;
$inline-text-element-border-radius: $border-radius-sm;


$btn-border-radius: $border-radius-sm;
$btn-font-family: null;
$btn-font-style: null;
$btn-font-weight: 500;
$btn-font-size: $font-size-base;
$btn-font-size-sm: 0.8rem;
$btn-font-size-lg: 1.15rem;
$btn-shadow-width: 0.25rem;
$btn-outline-width: 1px;
$btn-padding: 0.75em 1em;
$btn-padding-sm: 0.5em;
$btn-padding-lg: 0.9em 1.15em;
$btn-icon-padding: 0.75em;
$btn-icon-padding-sm: 0.5em;
$btn-icon-padding-lg: 0.9em;
$btn-icon-size-sm: 0.85em;
$btn-icon-size: 1em;
$btn-focus-ring-type: outline;
$btn-focus-ring-box-shadow-type: outside;
$btn-focus-ring-width: 2px;
$btn-focus-ring-offset: 2px;



$form-label-font-size: null;
$form-label-font-size-sm: $font-size-sm;
$form-label-font-size-lg: $font-size-lg;
$form-label-font-style: null;
$form-label-font-weight: null;


$fieldset-gap-size: map.get($spacers, s);
$legend-font-size: clamp(#{map.get($font-sizes, 'h5')}, 5vw, #{map.get($font-sizes, 'h4')});
$legend-font-weight: 700;


$form-description-font-size: $font-size-base;
$form-description-font-style: null;
$form-description-font-weight: null;


$form-control-font-size: $font-size-base;
$form-control-font-size-sm: $font-size-sm;
$form-control-font-size-lg: $font-size-lg;
$form-control-font-weight: null;
$form-control-line-height: 1.5;
$form-control-padding: 0.5em 0.75em;
$form-control-padding-sm: 0.25em 0.5em;
$form-control-padding-lg: 0.65em 1em;
$form-control-border-width: 1px;
$form-control-border-radius: $border-radius-sm;
$form-control-focus-ring-type: box-shadow;
$form-control-focus-ring-box-shadow-type: outside;
$form-control-focus-ring-width: 0.25rem;
$form-control-focus-ring-offset: 2px;
$form-control-color-padding: 0.5em;
$form-control-color-padding-sm: 0.25em;
$form-control-color-padding-lg: 0.75em;
$form-control-color-height: calc(#{$form-control-line-height}em + ( #{$form-control-color-padding} + #{$form-control-border-width} ) * 2);
$form-control-color-height-sm: calc(#{$form-control-line-height}em + ( #{$form-control-color-padding-sm} + #{$form-control-border-width} ) * 2);
$form-control-color-height-lg: calc(#{$form-control-line-height}em + ( #{$form-control-color-padding-lg} + #{$form-control-border-width} ) * 2);
$form-control-textarea-height: 6rem;


$form-file-background: primary;


$form-range-track-height: 0.25rem;
$form-range-track-broder-radius: 0.15rem;
$form-range-thumb-height: 1rem;
$form-range-thumb-width: 1rem;
$form-range-thumb-broder-radius: 0.25rem;
$form-range-focus-ring-type: outline;
$form-range-focus-ring-box-shadow-type: outside;
$form-range-focus-ring-width: 2px;
$form-range-focus-ring-offset: 2px;


$form-check-border-width: 1px;
$form-check-checkbox-border-radius: $border-radius-sm;
$form-check-font-size: 1.125rem;
$form-check-font-size-sm: $font-size-base;
$form-check-font-size-lg: $font-size-lg;
$form-check-font-weight: $form-label-font-weight;
$form-check-line-height: $line-height-heading;
$form-check-margin-block-start: 0.1em;
$form-check-vertical-alignment: center;
$form-check-focus-ring-type: outline;
$form-check-focus-ring-box-shadow-type: outside;
$form-check-focus-ring-width: 2px;
$form-check-focus-ring-offset: 2px;


$form-switch-border-width: 1px;
$form-switch-font-size: 1.125rem;
$form-switch-font-size-sm: $font-size-base;
$form-switch-font-size-lg: $font-size-lg;
$form-switch-font-weight: $form-label-font-weight;
$form-switch-line-height: $line-height-heading;
$form-switch-margin-bottom: $form-check-margin-bottom;


$select-icon-width: 1.25em;
$select-icon-right-offset: 0.5em;
$select-padding-right: calc(0.75em + $select-icon-width);


$select-icon: '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12,12.507l-3.816,-3.815c-0.171,-0.172 -0.45,-0.172 -0.622,-0l-0.933,0.933c-0.172,0.172 -0.172,0.451 0,0.623l5.06,5.06c0.172,0.172 0.45,0.172 0.622,0l5.06,-5.06c0.172,-0.172 0.172,-0.451 -0,-0.623l-0.933,-0.933c-0.172,-0.172 -0.451,-0.172 -0.622,-0l-3.816,3.815Z" style="fill:#COLOR#;"/></svg>';
$form-radio-icon: '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="6" style="fill:#COLOR#;"/></svg>';
$form-checkbox-icon: '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M9.525,13.777l-2.411,-2.412c-0.234,-0.233 -0.613,-0.233 -0.846,0l-1.27,1.27c-0.233,0.233 -0.233,0.612 0,0.846l4.104,4.103c0.116,0.117 0.269,0.175 0.422,0.175l0.003,0c0.152,0 0.305,-0.058 0.421,-0.175l9.054,-9.053c0.233,-0.234 0.233,-0.613 -0,-0.846l-1.27,-1.269c-0.233,-0.234 -0.612,-0.234 -0.846,-0l-7.361,7.361Z" style="fill:#COLOR#;"/></svg>';
$form-checkbox-indeterminate-icon: '<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M19.5,11.1c-0,-0.331 -0.269,-0.6 -0.6,-0.6l-13.8,0c-0.331,0 -0.6,0.269 -0.6,0.6l0,1.8c0,0.331 0.269,0.6 0.6,0.6l13.8,0c0.331,0 0.6,-0.269 0.6,-0.6l-0,-1.8Z" style="fill:#COLOR#;"/></svg>';
$form-switch-icon: '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="8.5" style="fill:#COLOR#;"/></svg>';
$form-valid-icon: '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><path d="M12,2c5.519,0 10,4.481 10,10c0,5.519 -4.481,10 -10,10c-5.519,0 -10,-4.481 -10,-10c0,-5.519 4.481,-10 10,-10Zm0,1.667c4.599,-0 8.333,3.734 8.333,8.333c0,4.599 -3.734,8.333 -8.333,8.333c-4.599,0 -8.333,-3.734 -8.333,-8.333c-0,-4.599 3.734,-8.333 8.333,-8.333Zm-1.476,10.182l-2.984,-2.984c-0.065,-0.065 -0.17,-0.065 -0.235,0l-0.943,0.943c-0.065,0.065 -0.065,0.171 -0,0.236l4.043,4.042c0.033,0.033 0.076,0.05 0.119,0.049c0.044,0.001 0.087,-0.016 0.12,-0.049l6.994,-6.994c0.065,-0.065 0.065,-0.17 0,-0.235l-0.943,-0.943c-0.065,-0.065 -0.17,-0.065 -0.235,-0l-5.936,5.935Z" style="fill:#COLOR#;"/></svg>';
$form-invalid-icon: '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><path d="M12,2c5.519,0 10,4.481 10,10c0,5.519 -4.481,10 -10,10c-5.519,0 -10,-4.481 -10,-10c0,-5.519 4.481,-10 10,-10Zm0,1.667c4.599,-0 8.333,3.734 8.333,8.333c0,4.599 -3.734,8.333 -8.333,8.333c-4.599,0 -8.333,-3.734 -8.333,-8.333c-0,-4.599 3.734,-8.333 8.333,-8.333Zm0.813,11.649c-0,-0.081 -0.065,-0.146 -0.146,-0.146l-1.334,0c-0.081,0 -0.146,0.065 -0.146,0.146l0,1.169c0,0.08 0.065,0.146 0.146,0.146l1.334,-0c0.081,-0 0.146,-0.066 0.146,-0.146l-0,-1.169Zm-0,-7.784c-0,-0.09 -0.073,-0.163 -0.163,-0.163l-1.3,0c-0.09,0 -0.163,0.073 -0.163,0.163l0,6.351c0,0.09 0.073,0.163 0.163,0.163l1.3,-0c0.09,-0 0.163,-0.073 0.163,-0.163l-0,-6.351Z" style="fill:#COLOR#;"/></svg>';


$form-row-col-width: 20ch;
$form-input-shadow-width: 0.25rem;


$table-responsive-width: 40rem;
$table-line-height: $line-height-md;
$table-padding: map.get($spacers, s);
$table-padding-sm: map.get($spacers, xs);
$table-caption-font-size: null;
$table-caption-font-style: null;
$table-caption-font-weight: null;
$table-stripe: odd;


$container-width: 84rem;

Transition and Animation

Name Value
$transition-duration 0.15s
$transition-timing-function ease-in-out
@use '~sprucecss/scss/spruce' with (
$transition-duration: 0.15s,
$transition-timing-function: ease-in-out

Character escaping

We use the $escaping-characters under the svg-escape function to neutralize the characters when we use inline SVG through data:image.

View on GitHub

$escaping-characters: (
('<', '%3c'),
('>', '%3e'),
('#', '%23'),
('(', '%28'),
(')', '%29'),


We store the breakpoints in the $breakpoints map, which we use in the breakpoint mixin.

View on GitHub

Name Value
xs 32em
sm 48em
md 64em
lg 80em
xl 90em
xxl 110em
@use '~sprucecss/scss/spruce' with (
$breakpoints: (
xs: 32em,
sm: 48em,
md: 64em,
lg: 80em,
xl: 90em,
xxl: 110em


To learn more about the print option, please visit the related page.

View on GitHub

Name Value
$print-page-margin 2cm
$print-hidden-elements 'header, footer, aside, nav, form, iframe, [class^="aspect-ratio"]'
@use '~sprucecss/scss/spruce' with (
$print-page-margin: 2cm,
$print-hidden-elements: 'header, footer, aside, nav, form, iframe, [class^="aspect-ratio"]'