Functions
|
This section documents Sass/SCSS as implemented by Dart Sass, the current official and actively maintained compiler — it is not tied to any specific book, build tool, or CSS framework (Bootstrap, Bulma, etc.). Unlike the other reference sections on this site, no single reference book underpins it: the content was generated with the assistance of AI from general knowledge of Dart Sass, and should be verified against the current official documentation at sass-lang.com before relying on it in production. Sass continues to evolve, so behaviour described here may lag the compiler you are actually running. This section’s bibliography lists the reference material consulted while preparing these pages. |
A Sass function takes arguments and returns a value. Unlike a mixin, it emits no CSS of its own — its result is used wherever a value is expected: a property value, a variable declaration, an argument to another function, or a condition.
Defining a function
@function declares one; @return produces its value.
@use "sass:math";
@function rem($px, $base: 16px) {
@return math.div($px, $base) * 1rem;
}
.title {
font-size: rem(24px); // 1.5rem
margin-bottom: rem(12px); // 0.75rem
}
.title {
font-size: 1.5rem;
margin-bottom: 0.75rem;
}
A function body may contain variables and control flow, but every path through it must reach a @return — falling off the end without returning is an error.
@use "sass:color";
@function contrast-colour($background) {
@if color.channel($background, "lightness", $space: hsl) > 55% {
@return #212121;
} @else {
@return #ffffff;
}
}
.badge {
background: #ffeb3b;
color: contrast-colour(#ffeb3b); // #212121
}
@return may only appear inside a function — it is not a general "exit" for mixins or stylesheets.
Arguments
Functions take arguments exactly the way mixins do: positionally or by name, with optional defaults, and with an optional trailing variadic parameter.
@use "sass:math";
@use "sass:list";
@function strip-unit($number) {
@if math.is-unitless($number) {
@return $number;
}
@return math.div($number, ($number * 0 + 1));
}
@function sum($numbers...) {
$total: 0;
@each $n in $numbers {
$total: $total + $n;
}
@return $total;
}
@function clamp-between($value, $min: 0, $max: 100) {
@return math.max($min, math.min($max, $value));
}
$a: strip-unit(24px); // 24
$b: sum(4px, 8px, 12px); // 24px
$c: clamp-between(150, $max: 64); // 64
Named arguments make a function call readable at the call site, and — as with mixins — renaming a parameter is a breaking change for callers who use its name.
Functions vs. mixins
They look similar and are easy to confuse. The distinction is what they produce:
| Function | Mixin | |
|---|---|---|
Declared with |
|
|
Invoked with |
|
|
Produces |
A single value, via |
CSS declarations and/or rules |
Emits CSS directly |
No |
Yes |
Can accept a block |
No |
Yes, via |
Typical use |
Compute a size, transform a colour, look up a token |
Apply a group of declarations, wrap a media query |
The rule of thumb: if you want a number, colour, string, list, or map back, write a function; if you want declarations to appear in the output, write a mixin. They compose naturally — a mixin’s body commonly calls functions to compute the values it emits.
@function rem($px, $base: 16px) { @return math.div($px, $base) * 1rem; }
@mixin type-scale($px, $line-height: 1.5) {
font-size: rem($px);
line-height: $line-height;
}
h1 { @include type-scale(32px); }
Functions are pure
A Sass function should be a pure computation: same arguments in, same value out, with no side effects. Sass does not enforce this — a function body can technically reassign an outer variable — but doing so makes stylesheets unpredictable, because the compiler is free to evaluate expressions in an order you did not write.
Practically, this means a function should not use !global, and should not rely on being called a particular
number of times.
Reporting problems: @error, @warn, @debug
Argument validation belongs in the function itself, so a mistake fails at compile time with a useful message rather than emitting nonsense CSS.
@use "sass:math";
@use "sass:meta";
@function rem($px, $base: 16px) {
@if meta.type-of($px) != number {
@error "rem() expects a number, got `#{$px}` (#{meta.type-of($px)}).";
}
@if math.is-unitless($px) {
@warn "rem() was given a unitless value `#{$px}`; assuming px.";
$px: $px * 1px;
}
@return math.div($px, $base) * 1rem;
}
-
@erroraborts compilation and prints the message with a stack trace. Use it for a genuine misuse. -
@warnprints a message and continues. Use it for deprecations and recoverable oddities. -
@debugprints a value for inspection during development; strip these before shipping.
Naming and namespacing
Custom functions live in a module like any other member (see Partials & Modules), so they are called through their module’s namespace:
// _units.scss
@use "sass:math";
@function rem($px, $base: 16px) { @return math.div($px, $base) * 1rem; }
@use "units";
.title { font-size: units.rem(24px); }
A function whose name starts with - or _ is private to its module.
Avoid naming a custom function after a plain CSS function (calc, min, max, clamp, translate,
url…). Sass has to decide whether such a call is yours or CSS’s, and shadowing one leads to surprising
output. Prefixing project functions (app-min(), or namespacing through a module) sidesteps the problem
entirely.
Built-in functions
Sass ships a substantial standard library — maths, colour manipulation, list and map operations, string
handling, selector introspection, and metaprogramming — organised into sass:* modules loaded with @use:
@use "sass:math";
@use "sass:color";
.button {
width: math.div(100%, 3);
background: color.adjust(#3f51b5, $lightness: -10%);
}
Those are documented on their own page rather than listed here — see
Built-in Modules for the module-by-module reference. Note that the older
global forms of these functions (darken(), map-get(), nth(), and so on) still work but are deprecated in
favour of the namespaced module versions.