sass-site/source/documentation/at-rules/function.liquid

365 lines
12 KiB
Plaintext
Raw Normal View History

---
title: "@function"
table_of_contents: true
introduction: >
Functions allow you to define complex operations on [SassScript
2023-05-30 17:15:42 +02:00
values](/documentation/values) that you can re-use throughout your stylesheet.
They make it easy to abstract out common formulas and behaviors in a readable
way.
---
2023-05-30 17:15:42 +02:00
{% markdown %}
2023-05-30 23:23:34 +02:00
Functions are defined using the `@function` at-rule, which is written
`@function <name>(<arguments...>) { ... }`. 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]: /documentation/syntax/structure#universal-statements
[`@return` at-rule]: #return
{% endmarkdown %}
{% codeExample 'functions' %}
2023-05-30 23:23:34 +02:00
@function pow($base, $exponent) {
$result: 1;
@for $_ from 1 through $exponent {
$result: $result * $base;
}
@return $result;
}
2023-05-30 23:23:34 +02:00
.sidebar {
float: left;
margin-left: pow(4, 3) * 1px;
}
===
@function pow($base, $exponent)
$result: 1
@for $_ from 1 through $exponent
$result: $result * $base
2023-05-30 23:23:34 +02:00
@return $result
2023-05-30 23:23:34 +02:00
.sidebar
float: left
margin-left: pow(4, 3) * 1px
{% endcodeExample %}
{% funFact %}
2023-05-30 23:23:34 +02:00
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.
{% endfunFact %}
{% headsUp %}
2023-05-30 23:23:34 +02:00
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.
2023-05-30 23:23:34 +02:00
[global variables]: /documentation/variables#scope
[mixins]: /documentation/at-rules/mixin
{% endheadsUp %}
{% markdown %}
2023-05-30 23:23:34 +02:00
## Arguments
2023-05-30 23:23:34 +02:00
{% comment %}
When changing this section, don't forget to change the mixin arguments
section as well!
{% endcomment %}
2023-05-30 23:23:34 +02:00
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.
2023-05-30 23:23:34 +02:00
[SassScript expressions]: /documentation/syntax/structure#expressions
2023-05-30 17:15:42 +02:00
{% endmarkdown %}
{% funFact %}
2023-05-30 23:23:34 +02:00
Argument lists can also have trailing commas! This makes it easier to avoid
syntax errors when refactoring your stylesheets.
{% endfunFact %}
2023-05-30 17:15:42 +02:00
{% markdown %}
2023-05-30 23:23:34 +02:00
### Optional Arguments
2023-05-30 23:23:34 +02:00
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.
2023-05-30 23:23:34 +02:00
[variable declarations]: /documentation/variables
[SassScript expression]: /documentation/syntax/structure#expressions
{% endmarkdown %}
{% codeExample 'optional-arguments' %}
2023-05-30 23:23:34 +02:00
@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%)
{% endcodeExample %}
{% funFact %}
2023-05-30 23:23:34 +02:00
Default values can be any SassScript expression, and they can even refer to
earlier arguments!
{% endfunFact %}
{% markdown %}
2023-05-30 23:23:34 +02:00
### 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][].
[variable declarations]: /documentation/variables
[boolean]: /documentation/values/booleans
[optional arguments]: #optional-arguments
{% endmarkdown %}
{% codeExample 'keyword-arguments' %}
2023-05-30 23:23:34 +02:00
$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%)
{% endcodeExample %}
{% headsUp %}
2023-05-30 23:23:34 +02:00
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.
2023-05-30 23:23:34 +02:00
[optional argument]: #optional-arguments
[warning]: /documentation/at-rules/warn
{% endheadsUp %}
{% markdown %}
2023-05-30 23:23:34 +02:00
### Taking Arbitrary Arguments
2023-05-30 23:23:34 +02:00
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][].
2023-05-30 23:23:34 +02:00
[list]: /documentation/values/lists
[argument list]: /documentation/values/lists#argument-lists
{% endmarkdown %}
{% codeExample 'taking-arbitrary-arguments' %}
2023-05-30 23:23:34 +02:00
@function sum($numbers...) {
$sum: 0;
@each $number in $numbers {
$sum: $sum + $number;
}
@return $sum;
}
2023-05-30 23:23:34 +02:00
.micro {
width: sum(50px, 30px, 100px);
}
===
@function sum($numbers...)
$sum: 0
@each $number in $numbers
$sum: $sum + $number
2023-05-30 23:23:34 +02:00
@return $sum
2023-05-30 17:15:42 +02:00
2023-05-30 23:23:34 +02:00
.micro
width: sum(50px, 30px, 100px)
{% endcodeExample %}
{% markdown %}
2023-05-30 23:23:34 +02:00
#### Taking Arbitrary Keyword Arguments
2023-05-30 23:23:34 +02:00
Argument lists can also be used to take arbitrary keyword arguments. The
[`meta.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.
2023-05-30 23:23:34 +02:00
[`meta.keywords()` function]: /documentation/modules/meta#keywords
[map]: /documentation/values/maps
2023-05-30 17:15:42 +02:00
{% endmarkdown %}
2023-05-30 17:15:42 +02:00
{% funFact %}
2023-05-30 23:23:34 +02:00
If you don't ever pass an argument list to the [`meta.keywords()` function][],
that argument list won't allow extra keyword arguments. This helps callers of
your function make sure they haven't accidentally misspelled any argument
names.
2023-05-30 23:23:34 +02:00
[`meta.keywords()` function]: /documentation/modules/meta#keywords
{% endfunFact %}
2023-05-30 17:15:42 +02:00
{% markdown %}
2023-05-30 23:23:34 +02:00
#### 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!
{% endmarkdown %}
{% codeExample 'passing-arbitrary-arguments' %}
2023-05-30 23:23:34 +02:00
$widths: 50px, 30px, 100px;
.micro {
width: min($widths...);
}
===
$widths: 50px, 30px, 100px
.micro
width: min($widths...)
{% endcodeExample %}
2023-05-30 17:15:42 +02:00
{% funFact false %}
2023-05-30 23:23:34 +02:00
{% markdown %}
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]: /documentation/values/lists#argument-lists
{% endmarkdown %}
{% codeExample 'passing-arbitrary-arguments-fun-fact' %}
@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...)
{% endcodeExample %}
{% endfunFact %}
{% markdown %}
2023-05-30 23:23:34 +02:00
## `@return`
2023-05-30 23:23:34 +02:00
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`.
2023-05-30 23:23:34 +02:00
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][].
2023-05-30 23:23:34 +02:00
[`@else` block]: /documentation/at-rules/control/if#else
{% endmarkdown %}
{% codeExample 'return', false %}
2023-05-30 23:23:34 +02:00
@use "sass:string";
2023-05-30 23:23:34 +02:00
@function str-insert($string, $insert, $index) {
// Avoid making new strings if we don't need to.
@if string.length($string) == 0 {
@return $insert;
}
2023-05-30 23:23:34 +02:00
$before: string.slice($string, 0, $index);
$after: string.slice($string, $index);
@return $before + $insert + $after;
}
===
@use "sass:string"
2023-05-30 23:23:34 +02:00
@function str-insert($string, $insert, $index)
// Avoid making new strings if we don't need to.
@if string.length($string) == 0
@return $insert
2023-05-30 23:23:34 +02:00
$before: string.slice($string, 0, $index)
$after: string.slice($string, $index)
@return $before + $insert + $after
{% endcodeExample %}
{% markdown %}
2023-05-30 23:23:34 +02:00
## 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]: /documentation/modules
[custom functions]: /documentation/js-api/interfaces/LegacySharedOptions#functions
[plain CSS functions]: #plain-css-functions
[weird syntax]: /documentation/syntax/special-functions
### Plain CSS Functions
Any function call that's not either a user-defined or
[built-in](/documentation/modules) function is compiled to a plain CSS
function (unless it uses [Sass argument
syntax](/documentation/at-rules/function#arguments)). The arguments will be
compiled to CSS and included as-is in the function call. This ensures that
Sass supports all CSS functions without needing to release new versions every
time a new one is added.
{% endmarkdown %}
2023-05-30 17:15:42 +02:00
{% codeExample 'plain-css-functions', false %}
2023-05-30 23:23:34 +02:00
@debug var(--main-bg-color); // var(--main-bg-color)
2023-05-30 23:23:34 +02:00
$primary: #f2ece4;
$accent: #e1d7d2;
@debug radial-gradient($primary, $accent); // radial-gradient(#f2ece4, #e1d7d2)
===
@debug var(--main-bg-color) // var(--main-bg-color)
2023-05-30 23:23:34 +02:00
$primary: #f2ece4
$accent: #e1d7d2
@debug radial-gradient($primary, $accent) // radial-gradient(#f2ece4, #e1d7d2)
{% endcodeExample %}
{% headsUp %}
2023-05-30 23:23:34 +02:00
Because any unknown function will be compiled to CSS, it's easy to miss when
you typo a function name. Consider running a [CSS linter][] on your
stylesheet's output to be notified when this happens!
2023-05-30 23:23:34 +02:00
[CSS linter]: https://stylelint.io/
{% endheadsUp %}
{% funFact %}
2023-05-30 23:23:34 +02:00
Some CSS functions, like `calc()` and `element()` have unusual syntax. Sass
[parses these functions specially][] as [unquoted strings][].
2023-05-30 23:23:34 +02:00
[parses these functions specially]: /documentation/syntax/special-functions
[unquoted strings]: /documentation/values/strings#unquoted
{% endfunFact %}