The sass:color module
A design rarely uses one colour alone: a button needs a darker shade for hover, a light one for the background, a transparent one for the shadow. The built-in sass:color module calculates these shades from one source colour, so a change of the brand colour updates all of them.
Connecting the module
Built-in modules are loaded with @use and the sass: prefix. Their namespace is the part after the colon — here color. Add the shades to styles/abstracts/_variables.scss:
// styles/abstracts/_variables.scss
@use "sass:color";
$primary: hsl(220, 80%, 50%);
$primary-dark: color.adjust($primary, $lightness: -10%);The colour is written in hsl() — hue, saturation, lightness — because shades are easiest to think about in terms of lightness.
color.adjust — shift by an amount
color.adjust() adds or subtracts a fixed amount from a channel: $lightness, $saturation, $hue, $alpha and others:
// styles/components/_buttons.scss
@use "sass:color";
$primary: hsl(220, 80%, 50%);
.btn {
background: $primary;
&:hover {
background: color.adjust($primary, $lightness: -10%);
}
}// styles/main.scss
@use "components/buttons";.btn {
background: hsl(220, 80%, 50%);
}
.btn:hover {
background: hsl(220, 80%, 40%);
}<style>
.btn {
background: hsl(220, 80%, 50%);
}
.btn:hover {
background: hsl(220, 80%, 40%);
}
</style>
<button class="btn" style="color: white; padding: 8px 16px; border: 0">Hover me</button>Lightness went from 50% to 40%. Sass writes the result in the same colour space as the source colour — here hsl().
color.scale — shift proportionally
color.scale() moves a channel a percentage of the way to its limit. $lightness: 40% means "40% of the way from the current lightness to white":
// styles/main.scss
@use "sass:color";
$primary: hsl(220, 80%, 50%);
.badge {
background: color.scale($primary, $lightness: 40%);
}.badge {
background: hsl(220, 80%, 70%);
}Lightness 50% plus 40% of the remaining 50% gives 70%. scale never "overshoots": however far you go, the colour stays a colour, while adjust with a large number can turn it into pure white.
color.change — set a value
color.change() replaces a channel with an exact value. Most often it makes a transparent version of a colour:
// styles/main.scss
@use "sass:color";
$primary: hsl(220, 80%, 50%);
.card {
box-shadow: 0 4px 12px color.change($primary, $alpha: 0.2);
}.card {
box-shadow: 0 4px 12px hsla(220, 80%, 50%, 0.2);
}color.mix — mix two colours
color.mix() mixes two colours; the third argument is the share of the first one:
// styles/main.scss
@use "sass:color";
$primary: #2864dc;
.notice {
color: color.mix($primary, black, 80%);
background: color.mix($primary, white, 20%);
}.notice {
color: #2050b0;
background: #d4e0f8;
}Mixing with white gives a tint, with black — a shade. When the result has fractional channels, Sass prints it as rgb() with percentages instead of a hex code; it is still the same correct colour.
Common mistake
Old tutorials use global functions like darken($primary, 10%) and lighten(). They are deprecated: Sass prints a warning for each call, and Dart Sass 3 removes them. Use color.adjust($primary, $lightness: -10%) from the module instead — the result is the same.