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.

scss
.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 overwriteable, expandable with @use's with option.

scss
@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.

Colors

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.

scss
$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: (
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: -20%),
border: $color-gray-light,
mark-background: hsl(50 100% 80%),
mark-foreground: $color-black,
code-background: hsl(261 52% 95%),
code-foreground: $color-black,
blockquote-border: $color-primary
),
selection: (
foreground: $color-white,
background: $color-primary
),
alert: (
danger: $color-danger,
info: hsl(195 100% 42%),
success: $color-success,
warning: hsl(48 89% 55%)
),
btn: (
primary-background: $color-primary,
primary-foreground: $color-white,
primary-shadow-focus: color.adjust($color-primary, $alpha: -0.75),
secondary-background: $color-secondary,
secondary-foreground: $color-white,
secondary-shadow-focus: color.adjust($color-secondary, $alpha: -0.75)
),
form: (
background: $color-white,
background-disabled: hsl(0 0% 95%),
border: $color-gray-light,
border-disabled: $color-gray-light,
border-focus: $color-primary,
shadow-focus: color.adjust($color-primary, $alpha: -0.75),
check-foreground: $color-white,
check-background: $color-primary,
invalid: $color-danger,
invalid-shadow: color.adjust($color-danger, $alpha: -0.75),
label: $color-black,
legend: $color-black,
placeholder: hsl(208 7% 40%),
text: $color-gray,
select-foreground: $color-primary,
valid: $color-success,
valid-shadow: color.adjust($color-success, $alpha: -0.75)
),
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)
),
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)
)
);

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().

scss
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.

scss
@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.

scss
$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.

scss
@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

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

scss
$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.

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

Overwrite Spacers

You can easily overwrite each value or add new ones.

scss
@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:

scss
$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.

Display

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

Typography

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

Font Size

scss
$font-size-ratio: 1.333;
$font-size-base: 1rem;
$font-size-sm: 0.9rem;
$font-size-lg: 1.1rem;
$font-size-lead: math.pow($font-size-ratio, 1) * $font-size-base;
$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-sizes
);

Font Family

scss
$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

scss
$heading-font-weight: 700;

Line Height

scss
$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

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

Buttons

scss
$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-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;

Forms

Label

scss
$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

scss
$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;

Description

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

Control

scss
$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-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;

File

scss
$form-file-background: primary;

Range

scss
$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;

Check

scss
$form-check-border-width: 1px;
$form-check-font-size: $font-size-base;
$form-check-font-size-sm: $font-size-sm;
$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-bottom: -0.1em;

Switch

scss
$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

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

Icons

scss
$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>';

Other

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

Tables

scss
$table-responsive-width: 40rem;
$table-line-height: $line-height-md;
$table-padding: map.get($spacers, s);
$table-padding-sm: map.get($spacers, xxs) map.get($spacers, xs);

Layout

scss
$container-width: 84rem;

Transition and Animation

scss
$transition-duration: 0.15s;
$transition-timing-function: ease-in-out;

Character escaping

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

Breakpoints

scss
$breakpoints: (
xs: 32em,
sm: 48em,
md: 64em,
lg: 80em,
xl: 90em,
xxl: 110em
);

Print

scss
$print-page-margin: 2cm !default;
$print-hidden-elements: 'header, footer, aside, nav, form, iframe, [class^="aspect-ratio"]';