Spruce CSS has several Sass variables and maps that it can generate into CSS custom properties.
Our central concept is not to expose a CSS custom property that isn’t needed for later customization, but you can opt in to the full custom property mode.
By default, Spruce only generates CSS variables for the colors and the transition settings. If you set css-custom-properties to true, it also generates them for the typography, display, shadows, buttons, form elements, tables and more, with style hooks.
@use 'sprucecss/scss/spruce' with (
$settings: (
'css-custom-properties': true,
),
);Every map below is merged with your values, so you only pass the keys you change, at the first @use of Spruce:
@use 'sprucecss/scss/spruce' with (
$btn: (
'border-radius': 0,
'font-weight': 700,
),
$shadows: (
'md': 0 6px 20px hsl(0deg 0% 0% / 12%),
),
);The code blocks show the source of the installed version, with the default values. Read more about the difference between Sass and CSS variables, and see the colors, settings and styles on their own pages.
The breakpoints used by the breakpoint, breakpoint-down, breakpoint-between and container-query mixins.
$breakpoints: () !default;
$breakpoints: map.merge(
(
'xs': 32em,
'sm': 48em,
'md': 64em,
'lg': 80em,
'xl': 90em,
'xxl': 110em,
),
$breakpoints
);The button maps. The $btn-sm and $btn-lg maps only overwrite what differs from $btn. New in v3: box-shadow, disabled-opacity, letter-spacing, line-height.
$btn: () !default;
$btn: map.merge(
(
'border-radius': map.get($display, 'border-radius-sm'),
'border-width': 1px,
'box-shadow': null,
'disabled-opacity': 0.5,
'focus-ring-box-shadow-type': outside,
'focus-ring-offset': 2px,
'focus-ring-size': 2px,
'focus-ring-type': outline,
'font-family': null,
'font-size': map.get($typography, 'font-size-base'),
'font-style': null,
'font-weight': 500,
'gap': map.get($spacers, 'xs'),
'icon-padding': 0.75em,
'icon-size': 1em,
'letter-spacing': null,
'line-height': 1,
'padding': 0.75em 1em,
'shadow-size': 0.25rem,
'text-transform': null,
),
$btn
);
$btn-lg: () !default;
$btn-lg: map.merge(
(
'font-size': 1.15rem,
'gap': map.get($spacers, 'xs'),
'icon-padding': 0.9em,
'padding': 0.9em 1.15em,
),
$btn-lg
);
$btn-sm: () !default;
$btn-sm: map.merge(
(
'font-size': 0.8rem,
'gap': map.get($spacers, 'xxs'),
'icon-padding': 0.5em,
'icon-size': 0.8rem,
'padding': 0.5em 0.75em,
),
$btn-sm
);The characters the svg-escape() function escapes when an SVG is used as a data:image.
/// Characters to escape using SVG as data:image.
$escaping-characters: (
('<', '%3c'),
('>', '%3e'),
('#', '%23'),
('(', '%28'),
(')', '%29'),
) !default;New in v3. The details element options.
$details: () !default;
$details: map.merge(
(
'border-radius': map.get($display, 'border-radius-sm'),
'border-width': 1px,
'gap': map.get($spacers, 'xs'),
'icon-size': 1em,
'padding': map.get($spacers, 's'),
'summary-font-weight': 600,
),
$details
);New in v3. The dialog element options.
$dialog: () !default;
$dialog: map.merge(
(
'backdrop-filter': none,
'border-radius': map.get($display, 'border-radius-lg'),
'border-width': 1px,
'inline-size': 40rem,
'padding': map.get($spacers, 'm'),
),
$dialog
);Shared shape tokens. New in v3: border-radius-md, border-radius-pill and border-width-base. Get them with radius() or config(…, $display).
$display: () !default;
$display: map.merge(
(
'border-radius-lg': 0.925rem,
'border-radius-md': 0.675rem,
'border-radius-pill': 62.5rem,
'border-radius-sm': 0.425rem,
'border-width-base': 1px,
),
$display
);New in v3. The hr element options.
$divider: () !default;
$divider: map.merge(
(
'border-style': solid,
'border-width': 1px,
'margin-block': null,
),
$divider
);Checkboxes and radio buttons.
$form-check: () !default;
$form-check: map.merge(
(
'border-radius': map.get($display, 'border-radius-sm'),
'border-width': 1px,
'focus-ring-box-shadow-type': outside,
'focus-ring-offset': 2px,
'focus-ring-size': 2px,
'focus-ring-type': outline,
'font-size': 1.125rem,
'font-weight': map.get($form-label, 'font-weight'),
'line-height': map.get($typography, 'line-height-md'),
'margin-block': 0.1em,
'vertical-alignment': center,
),
$form-check
);
$form-check-lg: () !default;
$form-check-lg: map.merge(
(
'font-size': map.get($typography, 'font-size-lg'),
),
$form-check-lg
);
$form-check-sm: () !default;
$form-check-sm: map.merge(
(
'font-size': map.get($typography, 'font-size-base'),
),
$form-check-sm
);Inputs, selects and textareas, with the size and color input maps. New in v3: box-shadow, letter-spacing.
$form-control: () !default;
$form-control: map.merge(
(
'border-radius': map.get($display, 'border-radius-sm'),
'border-width': 1px,
'box-shadow': null,
'focus-ring-box-shadow-type': outside,
'focus-ring-offset': 2px,
'focus-ring-size': 0.25rem,
'focus-ring-type': box-shadow,
'font-family': null,
'font-size': map.get($typography, 'font-size-base'),
'font-weight': null,
'letter-spacing': null,
'line-height': 1.5,
'padding': 0.5em 0.75em,
'textarea-block-size': 6rem,
),
$form-control
);
$form-control-lg: () !default;
$form-control-lg: map.merge(
(
'font-size': map.get($typography, 'font-size-lg'),
'padding': 0.65em 1em,
),
$form-control-lg
);
$form-control-sm: () !default;
$form-control-sm: map.merge(
(
'border-radius': 0.35em,
'font-size': map.get($typography, 'font-size-sm'),
'padding': 0.25em 0.75em,
),
$form-control-sm
);
$form-control-color: () !default;
$form-control-color: map.merge(
(
'aspect-ratio': 1,
'block-size': 100%,
'inline-size': 2.625rem,
'padding': 0.5em,
),
$form-control-color
);
$form-control-color-lg: () !default;
$form-control-color-lg: map.merge(
(
'aspect-ratio': 1,
'block-size': 100%,
'inline-size': 3.204rem,
'padding': 0.5em,
),
$form-control-color-lg
);
$form-control-color-sm: () !default;
$form-control-color-sm: map.merge(
(
'aspect-ratio': 1,
'block-size': 100%,
'inline-size': 1.925rem,
'padding': 0.25em,
),
$form-control-color-sm
);The help text under the controls.
$form-description: () !default;
$form-description: map.merge(
(
'font-size': 1em,
'font-style': null,
'font-weight': 400,
),
$form-description
);Fieldsets and legends.
$form-fieldset: () !default;
$form-fieldset: map.merge(
(
'layout-gap': map.get($spacers, 's'),
'legend-font-family': null,
'legend-font-size': clamp(#{map.get($font-sizes, 'h5')}, 5vw, #{map.get($font-sizes, 'h4')}),
'legend-font-weight': 700,
),
$form-fieldset
);The file input button (background is the name of a button color variant).
$form-file: () !default;
$form-file: map.merge(
(
'background': 'primary',
),
$form-file
);The form group layouts.
$form-group: () !default;
$form-group: map.merge(
(
'gap': map.get($spacers, 'xs'),
),
$form-group
);
$form-group-check: () !default;
$form-group-check: map.merge(
(
'gap': map.get($spacers, 's'),
),
$form-group-check
);
$form-group-row: () !default;
$form-group-row: map.merge(
(
'container-inline-size': 38rem,
'gap': map.get($spacers, 'xxs') map.get($spacers, 's'),
'label-inline-size': 10rem,
'vertical-alignment': center,
),
$form-group-row
);The SVG icons of the form elements. #COLOR# is replaced with the related color.
$form-icon: () !default;
$form-icon: map.merge(
(
'checkbox-indeterminate': '<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>',
'checkbox': '<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>',
'invalid': '<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>',
'radio': '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="6" style="fill:#COLOR#;"/></svg>',
'select': '<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>',
'switch': '<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>',
'valid': '<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-icon
);New in v3: line-height, required-content, required-gap.
$form-label: () !default;
$form-label: map.merge(
(
'font-family': null,
'font-size': null,
'font-style': null,
'font-weight': null,
'line-height': null,
'required-content': '*',
'required-gap': 0.25em,
'text-align': start,
'text-transform': null,
),
$form-label
);New in v3: the $form-range-sm and $form-range-lg maps.
$form-range: () !default;
$form-range: map.merge(
(
'focus-ring-box-shadow-type': outside,
'focus-ring-offset': 2px,
'focus-ring-size': 2px,
'focus-ring-type': outline,
'thumb-block-size': 1rem,
'thumb-border-radius': 0.5rem,
'thumb-inline-size': 1rem,
'track-block-size': 0.25rem,
'track-border-radius': 0.15rem,
),
$form-range
);
$form-range-lg: () !default;
$form-range-lg: map.merge(
(
'thumb-block-size': 1.25rem,
'thumb-border-radius': 0.625rem,
'thumb-inline-size': 1.25rem,
'track-block-size': 0.375rem,
'track-border-radius': 0.1875rem,
),
$form-range-lg
);
$form-range-sm: () !default;
$form-range-sm: map.merge(
(
'thumb-block-size': 0.75rem,
'thumb-border-radius': 0.375rem,
'thumb-inline-size': 0.75rem,
'track-block-size': 0.1875rem,
'track-border-radius': 0.1rem,
),
$form-range-sm
);The .form-row--mixed layout.
$form-row: () !default;
$form-row: map.merge(
(
'inline-size': 20ch,
),
$form-row
);The select arrow and the validation icon placement.
$form-select: () !default;
$form-select: map.merge(
(
'icon-inline-size': 1.25em,
'icon-right-offset': 0.5em,
'padding-inline-end': calc(0.75em + 1.25em),
),
$form-select
);Switches.
$form-switch: () !default;
$form-switch: map.merge(
(
'border-width': 1px,
'font-size': 1.125rem,
'font-weight': map.get($form-label, 'font-weight'),
'line-height': map.get($typography, 'line-height-md'),
'margin-block': 0.15em,
'vertical-alignment': center,
),
$form-switch
);
$form-switch-lg: () !default;
$form-switch-lg: map.merge(
(
'font-size': map.get($typography, 'font-size-lead'),
),
$form-switch-lg
);
$form-switch-sm: () !default;
$form-switch-sm: map.merge(
(
'font-size': map.get($typography, 'font-size-base'),
),
$form-switch-sm
);Turn the style generation on or off by part. See the Generators page.
$generators: () !default;
$generators: map.deep-merge(
(
'content': (
'accessibility': true,
'dark-mode': false,
'default': true,
'details': true,
'dialog': true,
'display': true,
'divider': true,
'layout': true,
'media': true,
'normalize': true,
'print': true,
'progress': true,
'root': true,
'styles': false,
'table': true,
'typography': true,
'utilities': true,
),
'form': (
'btn': true,
'btn-group': true,
'file-btn': true,
'form-check': true,
'form-control': true,
'form-description': true,
'form-feedback': true,
'form-fieldset': true,
'form-group-label': true,
'form-group': true,
'form-label': true,
'form-range': true,
'form-row': true,
'form-switch': true,
),
),
$generators
);The default container width.
$layout: () !default;
$layout: map.merge(
(
'container-inline-size': 84rem,
),
$layout
);New in v3. Image and figure options.
$media: () !default;
$media: map.merge(
(
'figcaption-font-size': null,
'figcaption-font-style': null,
'figcaption-margin-block-start': map.get($spacers, 'xs'),
'figcaption-text-align': center,
'img-border-radius': null,
),
$media
);The print styles.
$print: () !default;
$print: map.merge(
(
'page-margin': 2cm,
'hidden-elements': 'header, footer, aside, nav, form, iframe, [class^="aspect-ratio"]',
),
$print
);New in v3. The progress and meter elements.
$progress: () !default;
$progress: map.merge(
(
'block-size': 0.5rem,
'border-radius': map.get($display, 'border-radius-pill'),
),
$progress
);New in v3. The shadow scale. Get them with shadow(); in custom property mode they are output as --spruce-shadow-*.
$shadows: () !default;
$shadows: map.merge(
(
'xs': 0 1px 2px hsl(0deg 0% 0% / 5%),
'sm': (0 1px 3px hsl(0deg 0% 0% / 10%), 0 1px 2px -1px hsl(0deg 0% 0% / 10%)),
'md': (0 4px 6px -1px hsl(0deg 0% 0% / 10%), 0 2px 4px -2px hsl(0deg 0% 0% / 10%)),
'lg': (0 10px 15px -3px hsl(0deg 0% 0% / 10%), 0 4px 6px -4px hsl(0deg 0% 0% / 10%)),
'xl': (0 20px 25px -5px hsl(0deg 0% 0% / 10%), 0 8px 10px -6px hsl(0deg 0% 0% / 10%)),
'inner': inset 0 2px 4px 0 hsl(0deg 0% 0% / 5%),
),
$shadows
);The spacers are generated from the $spacer value. Get them with spacer() and spacer-clamp().
$spacer: 1rem !default;
$spacers: () !default;
$spacers: map.merge(
(
'xxs': $spacer * 0.25,
'xs': $spacer * 0.5,
's': $spacer,
'm': $spacer * 1.5,
'l': $spacer * 3,
'xl': $spacer * 4.5,
'xxl': $spacer * 7,
'xxxl': $spacer * 10,
),
$spacers
);New in v3: border-width, caption-text-align, the heading-* keys and vertical-align.
$table: () !default;
$table: map.merge(
(
'border-width': 1px,
'caption-font-size': null,
'caption-font-style': null,
'caption-font-weight': null,
'caption-text-align': null,
'heading-font-size': null,
'heading-font-weight': null,
'heading-letter-spacing': null,
'heading-text-transform': null,
'line-height': map.get($typography, 'line-height-md'),
'padding': map.get($spacers, 's'),
'responsive-inline-size': 40rem,
'stripe': odd,
'vertical-align': null,
),
$table
);
$table-sm: () !default;
$table-sm: map.merge(
(
'padding': map.get($spacers, 'xs'),
),
$table-sm
);The default transition settings. In custom property mode, the duration is 0 for users who prefer reduced motion.
$transition: () !default;
$transition: map.merge(
(
'duration': 0.15s,
'timing-function': ease-in-out,
),
$transition
);The typography tokens and the heading scale. Get the sizes with font-size(). New in v3: blockquote-*, font-family-mono, font-size-code, font-weight-strong, letter-spacing-base, link-*, list-gap, pre-*, text-transform-heading, text-wrap-base, text-wrap-heading.
$typography: () !default;
$typography: map.merge(
(
'blockquote-border-width': 0.5rem,
'blockquote-font-size': null,
'blockquote-font-style': null,
'border-radius': map.get($display, 'border-radius-sm'),
'font-family-base': #{Seravek, 'Gill Sans Nova', Ubuntu, Calibri, 'DejaVu Sans', source-sans-pro, sans-serif},
'font-family-cursive': #{ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono', monospace},
'font-family-heading': #{Avenir, 'Avenir Next LT Pro', Montserrat, Corbel, 'URW Gothic', source-sans-pro, sans-serif},
'font-family-mono': #{ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono', monospace},
'font-size-base': 1rem,
'font-size-code': null,
'font-size-lead': clamp(1.15rem, 2vw, 1.35rem),
'font-size-lg': 1.125rem,
'font-size-ratio': 1.25,
'font-size-sm': 0.875rem,
'font-weight-base': null,
'font-weight-heading': 700,
'font-weight-strong': null,
'inline-padding': 0.1em 0.3em,
'letter-spacing-base': null,
'letter-spacing-heading': null,
'line-height-base': 1.5,
'line-height-heading': calc(2px + 2ex + 2px),
'line-height-lg': 1.8,
'line-height-md': 1.5,
'line-height-sm': 1.2,
'link-font-weight': null,
'link-text-decoration': underline,
'link-text-decoration-hover': null,
'link-text-decoration-thickness': null,
'link-text-underline-offset': null,
'list-gap': map.get($spacers, 'xxs'),
'pre-border-radius': map.get($display, 'border-radius-sm'),
'pre-padding': map.get($spacers, 's'),
'text-transform-heading': null,
'text-wrap-base': null,
'text-wrap-heading': balance,
),
$typography
);
$font-sizes: () !default;
$font-sizes: map.merge(
(
'h1': math.pow(map.get($typography, 'font-size-ratio'), 4) * map.get($typography, 'font-size-base'),
'h2': math.pow(map.get($typography, 'font-size-ratio'), 3) * map.get($typography, 'font-size-base'),
'h3': math.pow(map.get($typography, 'font-size-ratio'), 2) * map.get($typography, 'font-size-base'),
'h4': math.pow(map.get($typography, 'font-size-ratio'), 1) * map.get($typography, 'font-size-base'),
'h5': map.get($typography, 'font-size-base'),
'h6': map.get($typography, 'font-size-base'),
),
$font-sizes
);New in v3. The layer scale. Get the values with z-index().
$z-indexes: () !default;
$z-indexes: map.merge(
(
'dropdown': 1000,
'sticky': 1020,
'fixed': 1030,
'overlay': 1040,
'modal': 1050,
'popover': 1060,
'toast': 1070,
'tooltip': 1080,
),
$z-indexes
);

