From f481e8fabb4f2260353ca758e834835c2c5d6e06 Mon Sep 17 00:00:00 2001 From: Sana Javed Date: Fri, 26 May 2023 16:47:25 +0200 Subject: [PATCH] Porting Documentation - At-Rules - function --- source/documentation/at-rules/function.liquid | 354 ++++++++++++++++++ 1 file changed, 354 insertions(+) create mode 100644 source/documentation/at-rules/function.liquid diff --git a/source/documentation/at-rules/function.liquid b/source/documentation/at-rules/function.liquid new file mode 100644 index 0000000..4b250a6 --- /dev/null +++ b/source/documentation/at-rules/function.liquid @@ -0,0 +1,354 @@ +--- +title: "@function" +table_of_contents: true +introduction: > + Functions allow you to define complex operations on [SassScript + 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. +--- +{% markdown %} +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]: /documentation/syntax/structure#universal-statements +[`@return` at-rule]: #return +{% endmarkdown %} + +{% codeExample 'functions' %} +@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 +{% endcodeExample %} + +{% funFact %} +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 %} +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]: /documentation/variables#scope +[mixins]: /documentation/at-rules/mixin +{% endheadsUp %} + +{% markdown %} +## Arguments + +{% comment %} +When changing this section, don't forget to change the mixin arguments +section as well! +{% endcomment %} + +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]: /documentation/syntax/structure#expressions + +{% funFact %} +Argument lists can also have trailing commas! This makes it easier to +avoid syntax errors when refactoring your stylesheets. +{% endfunFact %} + +### 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]: /documentation/variables +[SassScript expression]: /documentation/syntax/structure#expressions +{% endmarkdown %} + +{% codeExample 'optional-arguments' %} +@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 %} +Default values can be any SassScript expression, and they can even refer to +earlier arguments! +{% endfunFact %} + +{% markdown %} +### 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' %} +$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 %} +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]: /documentation/at-rules/warn +{% endheadsUp %} + +{% markdown %} +### 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]: /documentation/values/lists +[argument list]: /documentation/values/lists#argument-lists +{% endmarkdown %} + +{% codeExample 'taking-arbitrary-arguments' %} +@function sum($numbers...) { + $sum: 0; + @each $number in $numbers { + $sum: $sum + $number; + } + @return $sum; +} + +.micro { + width: sum(50px, 30px, 100px); +} +=== +@function sum($numbers...) + $sum: 0 + @each $number in $numbers + $sum: $sum + $number + + @return $sum + +.micro + width: sum(50px, 30px, 100px) +{% endcodeExample %} + +{% markdown %} +#### Taking Arbitrary Keyword Arguments + +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. + +[`meta.keywords()` function]: /documentation/modules/meta#keywords +[map]: /documentation/values/maps + +{% funFact %} +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. + +[`meta.keywords()` function]: /documentation/modules/meta#keywords +{% endfunFact %} + +#### 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' %} +$widths: 50px, 30px, 100px; +.micro { + width: min($widths...); +} +=== +$widths: 50px, 30px, 100px +.micro + width: min($widths...) +{% endcodeExample %} + +{% funFact, false %} +{% 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 %} +## `@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]: /documentation/at-rules/control/if#else +{% endmarkdown %} + +{% codeExample 'return', false %} +@use "sass:string"; + +@function str-insert($string, $insert, $index) { + // Avoid making new strings if we don't need to. + @if string.length($string) == 0 { + @return $insert; + } + + $before: string.slice($string, 0, $index); + $after: string.slice($string, $index); + @return $before + $insert + $after; +} +=== +@use "sass:string" + +@function str-insert($string, $insert, $index) + // Avoid making new strings if we don't need to. + @if string.length($string) == 0 + @return $insert + + + $before: string.slice($string, 0, $index) + $after: string.slice($string, $index) + @return $before + $insert + $after +{% endcodeExample %} + +{% markdown %} +## 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 %} + +{% codeExample 'plain-css-functions' %} +@debug var(--main-bg-color); // var(--main-bg-color) + +$primary: #f2ece4; +$accent: #e1d7d2; +@debug radial-gradient($primary, $accent); // radial-gradient(#f2ece4, #e1d7d2) +=== +@debug var(--main-bg-color) // var(--main-bg-color) + +$primary: #f2ece4 +$accent: #e1d7d2 +@debug radial-gradient($primary, $accent) // radial-gradient(#f2ece4, #e1d7d2) +{% endcodeExample %} + +{% headsUp %} +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! + +[CSS linter]: https://stylelint.io/ +{% endheadsUp %} + +{% funFact %} +Some CSS functions, like `calc()` and `element()` have unusual syntax. Sass +[parses these functions specially][] as [unquoted strings][]. + +[parses these functions specially]: /documentation/syntax/special-functions +[unquoted strings]: /documentation/values/strings#unquoted +{% endfunFact %}