--- title: "@function" table_of_contents: true introduction: > Functions allow you to define complex operations on [SassScript values](../values) that you can re-use throughout your stylesheet. They make it easy to abstract out common formulas and behaviors in a readable way. --- Functions are defined using the `@function` at-rule, which is written `@function () { ... }`. A function's name can be any Sass identifier. It can only contain [universal statements][], as well as the [`@return` at-rule][] which indicates the value to use as the result of the function call. Functions are called using the normal CSS function syntax. [universal statements]: ../syntax/structure#universal-statements [`@return` at-rule]: #return <% example do %> @function pow($base, $exponent) { $result: 1; @for $_ from 1 through $exponent { $result: $result * $base; } @return $result; } .sidebar { float: left; margin-left: pow(4, 3) * 1px; } === @function pow($base, $exponent) $result: 1 @for $_ from 1 through $exponent $result: $result * $base @return $result .sidebar float: left margin-left: pow(4, 3) * 1px <% end %> <% fun_fact do %> Function names, like all Sass identifiers, treat hyphens and underscores as identical. This means that `scale-color` and `scale_color` both refer to the same function. This is a historical holdover from the very early days of Sass, when it *only* allowed underscores in identifier names. Once Sass added support for hyphens to match CSS's syntax, the two were made equivalent to make migration easier. <% end %> <% heads_up do %> While it's technically possible for functions to have side-effects like setting [global variables][], this is strongly discouraged. Use [mixins][] for side-effects, and use functions just to compute values. [global variables]: ../variables#scope [mixins]: mixin <% end %> ## Arguments <%# When changing this section, don't forget to change the mixin arguments section as well! %> Arguments allow functions' behavior to be customized each time they're called. The arguments are specified in the `@function` rule after the function's name, as a list of variable names surrounded by parentheses. The function must be called with the same number of arguments in the form of [SassScript expressions][]. The values of these expression are available within the function's body as the corresponding variables. [SassScript expressions]: ../syntax/structure#expressions <% fun_fact do %> Argument lists can also have trailing commas! This can makes it easier to avoid syntax errors when refactoring your stylesheets. <% end %> ### Optional Arguments Normally, every argument a function declares must be passed when that function is included. However, you can make an argument optional by defining a *default value* which will be used if that arguments isn't passed. Default values use the same syntax as [variable declarations][]: the variable name, followed by a colon and a [SassScript expression][]. This makes it easy to define flexible function APIs that can be used in simple or complex ways. [variable declarations]: ../variables [SassScript expression]: ../syntax/structure#expressions <% example do %> @function invert($color, $amount: 100%) { $inverse: change-color($color, $hue: hue($color) + 180); @return mix($inverse, $color, $amount); } $primary-color: #036; .header { background-color: invert($primary-color, 80%); } === @function invert($color, $amount: 100%) $inverse: change-color($color, $hue: hue($color) + 180) @return mix($inverse, $color, $amount) $primary-color: #036 .header background-color: invert($primary-color, 80%) <% end %> <% fun_fact do %> Default values can be any SassScript expression, and they can even refer to earlier arguments! <% end %> ### Keyword Arguments When a function is called, arguments can be passed by name in addition to passing them by their position in the argument list. This is especially useful for functions with multiple optional arguments, or with [boolean][] arguments whose meanings aren't obvious without a name to go with them. Keyword arguments use the same syntax as [variable declarations][] and [optional arguments][]. [boolean]: ../values/booleans [optional arguments]: #optional-arguments <% example do %> $primary-color: #036; .banner { background-color: $primary-color; color: scale-color($primary-color, $lightness: +40%); } === $primary-color: #036 .banner background-color: $primary-color color: scale-color($primary-color, $lightness: +40%) <% end %> <% heads_up do %> Because *any* argument can be passed by name, be careful when renaming a function's arguments... it might break your users! It can be helpful to keep the old name around as an [optional argument][] for a while and printing a [warning][] if anyone passes it, so they know to migrate to the new argument. [optional argument]: #optional-arguments [warning]: warn <% end %> ### Taking Arbitrary Arguments Sometimes it's useful for a function to be able to take any number of arguments. If the last argument in a `@function` declaration ends in `...`, then all extra arguments to that function are passed to that argument as a [list][]. This argument is known as an [argument list][]. [list]: ../values/lists [argument list]: ../values/lists#argument-lists <% example do %> @function min($numbers...) { $min: null; @each $number in $numbers { @if $min == null or $number < $min { $min: $number; } } @return $min; } .micro { width: min(50px, 30px, 100px); } === @function min($numbers...) $min: null @each $number in $numbers @if $min == null or $number < $min $min: $number @return $min .micro width: min(50px, 30px, 100px) <% end %> #### Taking Arbitrary Keyword Arguments Argument lists can also be used to take arbitrary keyword arguments. The [`keywords()` function][] takes an argument list and returns any extra keywords that were passed to the function as a [map][] from argument names (not including `$`) to those arguments' values. [`keywords()` function]: ../functions/map#keywords [map]: ../values/maps <% fun_fact do %> If you don't ever pass an argument list to the [`keywords()` function][], that argument list won't allow extra keyword arguments. This helps callers of your function make sure they haven't accidentally written any typos in their argument names. [`keywords()` function]: ../functions/map#keywords <% end %> #### Passing Arbitrary Arguments Just like argument lists allow functions to take arbitrary positional or keyword arguments, the same syntax can be used to *pass* positional and keyword arguments to a function. If you pass a list followed by `...` as the last argument of a function call, its elements will be treated as additional positional arguments. Similarly, a map followed by `...` will be treated as additional keyword arguments. You can even pass both at once! <% example do %> $widths: 50px, 30px, 100px; .micro { width: min($widths...); } === $widths: 50px, 30px, 100px .micro width: min($widths...) <% end %> <% fun_fact do %> Because an [argument list][] keeps track of both positional and keyword arguments, you use it to pass both at once to another function. That makes it super easy to define an alias for a function! [argument list]: ../values/lists#argument-lists <% example do %> @function fg($args...) { @warn "The fg() function is deprecated. Call foreground() instead."; @return foreground($args...); } === @function fg($args...) @warn "The fg() function is deprecated. Call foreground() instead." @return foreground($args...) <% end %> <% end %> ## `@return` The `@return` at-rule indicates the value to use as the result of calling a function. It's only allowed within a `@function` body, and each `@function` must end with a `@return`. When a `@return` is encountered, it immediately ends the function and returns its result. Returning early can be useful for handling edge-cases or cases where a more efficient algorithm is available without wrapping the entire function in an [`@else` block][]. [`@else` block]: control/if#else <% example do %> @function str-insert($string, $insert, $index) { // Avoid making new strings if we don't need to. @if str-length($string) == 0 { @return $insert; } $before: str-slice($string, 0, $index); $after: str-slice($string, $index); @return $before + $insert + $after; } === @function str-insert($string, $insert, $index) // Avoid making new strings if we don't need to. @if str-length($string) == 0 @return $insert $before: str-slice($string, 0, $index) $after: str-slice($string, $index) @return $before + $insert + $after <% end %> ## Other Functions In addition to user-defined function, Sass provides a substantial [core library][] of built-in functions that are always available to use. Sass implementations also make it possible to define [custom functions][] in the host language. And of course, you can always call [plain CSS functions][] (even ones with [weird syntax][]). [core library]: ../functions [custom functions]: ../js-api#functions [plain CSS functions]: ../functions/css [weird syntax]: ../syntax/special-functions