As a stylesheet grows, the same calculations tend to appear in multiple places. A spacing value derived from a base unit, a colour adjusted by a fixed amount, a class name built from a prefix and a component name. Copy-pasting those expressions works until something needs to change, at which point you are hunting through hundreds of lines to update every instance. SCSS functions solve this by letting you write the logic once, give it a name, and call it wherever you need it.
What a Function Does
An SCSS function takes one or more arguments, performs an operation on them, and returns a single computed value. That value can be a number, a colour, a string, or a list. The function itself does not output any CSS properties directly. It just produces a value that you use inside a property declaration, the same way you would use a raw value.
This is the distinction worth understanding early: SCSS also has mixins, which output blocks of CSS rules. Functions are narrower in scope. They compute and return, nothing more.
The Basic Structure
Functions are defined with the @function directive, followed by the function name, its parameters, and a block that must include a @return statement:
@function scale-value($n, $factor) { @return $n * $factor;}.card { padding: scale-value(8px, 1.5); // Returns 12px}
The @return line is where the computed result gets handed back to the caller. Without it, the function does nothing useful. Once defined, you call the function the same way you would use any built-in SCSS function, by writing the name followed by arguments in parentheses.
Working with Colours
One of the most practical uses for custom functions is wrapping colour operations so the logic behind your colour system lives in one place rather than being scattered across your stylesheet:
@function adjust-shade($base-color, $amount) { @return lighten($base-color, $amount);}.alert { background-color: adjust-shade(#e74c3c, 15%); // Lightens by 15%}.badge { background-color: adjust-shade(#2ecc71, 8%);}
The function wraps the built-in lighten call. This matters when your colour logic becomes more complex later. If you decide the system should darken instead of lighten, or apply a mix of adjustments, you change one function and every caller updates automatically.
Building Strings Dynamically
String interpolation in SCSS lets functions construct values that would otherwise require manual concatenation every time:
@function build-selector($component, $namespace) { @return "#{$namespace}__#{$component}";}.menu { content: build-selector("item", "nav"); // Returns "nav__item"}
This is particularly useful in design systems where class names follow a naming convention. Rather than manually typing the convention every time, you encode it in a function and call that instead.
Processing Lists
Functions can iterate over lists to compute aggregate values, which is useful for things like calculating spacing from a set of measurements or averaging values across a scale:
@function list-mean($values) { $total: 0; $count: length($values); @each $val in $values { $total: $total + $val; } @return $total / $count;}$spacing-scale: 4, 8, 16, 32;.section { margin-bottom: list-mean($spacing-scale); // Returns 15}
The function loops through the list with @each, accumulates the total, then divides by the count. The result feeds directly into a property value. This approach keeps magic numbers out of your declarations and makes the relationship between the scale and the derived values explicit.
A Few Principles Worth Following
Name functions after what they return, not what they do internally. adjusted-colour or base-spacing tells the caller what to expect. run-lightening-calculation does not.
Keep each function focused on one job. A function that adjusts colours should not also be building class name strings. When a function tries to do two things, it becomes harder to reuse and harder to debug.
Comment parameters on anything non-obvious. If someone reading the code six months from now cannot immediately tell what $amount means in context, a short comment saves them the time of tracing it back through the codebase.
Use @return as early as possible. If a condition is met that determines the output, return there rather than carrying the value through a chain of conditionals to a single return at the bottom. Earlier returns keep the logic path clear.
Why This Matters
The real value of SCSS functions is not any single example, it is the habit they encourage. When you write padding: 16px in three places and later need to change it to 14px, you change three lines. When you write padding: base-spacing(2) in three places and later need to change the base, you change one function. Functions push the decision-making into one place and let the rest of the stylesheet follow automatically.
That is maintainability in practice: not perfect code on the first pass, but code where changes are cheap and contained.
See you soon.
[…] variable is declared with a dollar sign, followed by the name, a colon, and the value. You then use the name […]
[…] variable name would be treated as literal text rather than substituted. It is the bridge between SCSS’s variables and the string-based parts of […]
[…] Functions: https://datalad.co.uk/2024/05/05/scss-functions/ […]
[…] Functions: https://datalad.co.uk/scss-functions/ […]
[…] CSS keyframe animations give you JavaScript-free motion with full control over every stage. Define the steps with @keyframes, apply them with the animation property, and tune the behaviour with duration, timing, delay, and iteration count. […]
[…] the full background, read the guide to SCSS functions. To practise, work through the 10 code-along […]
[…] variable name would be treated as literal text rather than substituted. It is the bridge between SCSS’s variables and the string-based parts of […]