diff --git a/eleventy.config.js b/eleventy.config.js index 11a24dfe7..c38ed5a9b 100644 --- a/eleventy.config.js +++ b/eleventy.config.js @@ -16,6 +16,7 @@ const datesPlugin = require('./source/helpers/dates.ts').default; const { liquidEngine, markdownEngine } = require('./source/helpers/engines.ts'); const pagesPlugin = require('./source/helpers/pages.ts').default; const typePlugin = require('./source/helpers/type.ts').default; +const functionPlugin = require('./source/helpers/function.ts').default; /** @param {import('@11ty/eleventy').UserConfig} eleventyConfig */ module.exports = (eleventyConfig) => { @@ -43,6 +44,7 @@ module.exports = (eleventyConfig) => { eleventyConfig.addPlugin(datesPlugin); eleventyConfig.addPlugin(pagesPlugin); eleventyConfig.addPlugin(typePlugin); + eleventyConfig.addPlugin(functionPlugin); // rss plugin eleventyConfig.addLiquidFilter('absoluteUrl', absoluteUrl); diff --git a/source/code-snippets/example-advanced-nesting.liquid b/source/_includes/code_snippets/example-advanced-nesting.liquid similarity index 100% rename from source/code-snippets/example-advanced-nesting.liquid rename to source/_includes/code_snippets/example-advanced-nesting.liquid diff --git a/source/code-snippets/example-each-list.liquid b/source/_includes/code_snippets/example-each-list.liquid similarity index 100% rename from source/code-snippets/example-each-list.liquid rename to source/_includes/code_snippets/example-each-list.liquid diff --git a/source/code-snippets/example-each-map.liquid b/source/_includes/code_snippets/example-each-map.liquid similarity index 100% rename from source/code-snippets/example-each-map.liquid rename to source/_includes/code_snippets/example-each-map.liquid diff --git a/source/_includes/code_snippets/example-first-class-function.liquid b/source/_includes/code_snippets/example-first-class-function.liquid new file mode 100644 index 000000000..2681d856a --- /dev/null +++ b/source/_includes/code_snippets/example-first-class-function.liquid @@ -0,0 +1,59 @@ +{% comment -%} + TODO(nweiz): auto-generate this CSS once we're compiling with Dart Sass +{%- endcomment -%} +{% codeExample 'first-class-function' %} + @use "sass:list"; + @use "sass:meta"; + @use "sass:string"; + + /// Return a copy of $list with all elements for which $condition returns `true` + /// removed. + @function remove-where($list, $condition) { + $new-list: (); + $separator: list.separator($list); + @each $element in $list { + @if not meta.call($condition, $element) { + $new-list: list.append($new-list, $element, $separator: $separator); + } + } + @return $new-list; + } + + $fonts: Tahoma, Geneva, "Helvetica Neue", Helvetica, Arial, sans-serif; + + content { + @function contains-helvetica($string) { + @return string.index($string, "Helvetica"); + } + font-family: remove-where($fonts, meta.get-function("contains-helvetica")); + } + === + @use "sass:list" + @use "sass:meta" + @use "sass:string" + + /// Return a copy of $list with all elements for which $condition returns `true` + /// removed. + @function remove-where($list, $condition) + $new-list: () + $separator: list.separator($list) + @each $element in $list + @if not meta.call($condition, $element) + $new-list: list.append($new-list, $element, $separator: $separator) + + + @return $new-list + + + $fonts: Tahoma, Geneva, "Helvetica Neue", Helvetica, Arial, sans-serif + + .content + @function contains-helvetica($string) + @return string.index($string, "Helvetica") + + font-family: remove-where($fonts, meta.get-function("contains-helvetica")) + === + .content { + font-family: Tahoma, Geneva, Arial, sans-serif; + } +{% endcodeExample %} diff --git a/source/code-snippets/example-if-parent-selector.liquid b/source/_includes/code_snippets/example-if-parent-selector.liquid similarity index 100% rename from source/code-snippets/example-if-parent-selector.liquid rename to source/_includes/code_snippets/example-if-parent-selector.liquid diff --git a/source/code-snippets/example-if.liquid b/source/_includes/code_snippets/example-if.liquid similarity index 100% rename from source/code-snippets/example-if.liquid rename to source/_includes/code_snippets/example-if.liquid diff --git a/source/_includes/code_snippets/example-list-index.liquid b/source/_includes/code_snippets/example-list-index.liquid new file mode 100644 index 000000000..01bbc168d --- /dev/null +++ b/source/_includes/code_snippets/example-list-index.liquid @@ -0,0 +1,9 @@ +{% codeExample 'example-list-index', false %} + @debug list.index(1px solid red, 1px); // 1 + @debug list.index(1px solid red, solid); // 2 + @debug list.index(1px solid red, dashed); // null + === + @debug list.index(1px solid red, 1px) // 1 + @debug list.index(1px solid red, solid) // 2 + @debug list.index(1px solid red, dashed) // null +{% endcodeExample %} diff --git a/source/_includes/code_snippets/example-list-nth.liquid b/source/_includes/code_snippets/example-list-nth.liquid new file mode 100644 index 000000000..5c404864b --- /dev/null +++ b/source/_includes/code_snippets/example-list-nth.liquid @@ -0,0 +1,7 @@ +{% codeExample 'example-list-nth', false %} + @debug list.nth(10px 12px 16px, 2); // 12px + @debug list.nth([line1, line2, line3], -1); // line3 + === + @debug list.nth(10px 12px 16px, 2) // 12px + @debug list.nth([line1, line2, line3], -1) // line3 +{% endcodeExample %} diff --git a/source/_includes/code_snippets/example-map-get.liquid b/source/_includes/code_snippets/example-map-get.liquid new file mode 100644 index 000000000..6d3d17097 --- /dev/null +++ b/source/_includes/code_snippets/example-map-get.liquid @@ -0,0 +1,11 @@ +{% codeExample 'map-get', false %} + $font-weights: ("regular": 400, "medium": 500, "bold": 700); + + @debug map.get($font-weights, "medium"); // 500 + @debug map.get($font-weights, "extra-bold"); // null + === + $font-weights: ("regular": 400, "medium": 500, "bold": 700) + + @debug map.get($font-weights, "medium") // 500 + @debug map.get($font-weights, "extra-bold") // null +{% endcodeExample %} diff --git a/source/code-snippets/example-mixin-arbitrary-keyword-arguments.liquid b/source/_includes/code_snippets/example-mixin-arbitrary-keyword-arguments.liquid similarity index 95% rename from source/code-snippets/example-mixin-arbitrary-keyword-arguments.liquid rename to source/_includes/code_snippets/example-mixin-arbitrary-keyword-arguments.liquid index f42a1ee65..987359b1f 100644 --- a/source/code-snippets/example-mixin-arbitrary-keyword-arguments.liquid +++ b/source/_includes/code_snippets/example-mixin-arbitrary-keyword-arguments.liquid @@ -1,4 +1,4 @@ -{% codeExample 'mixin-arbitrary-keyword' %} +{% codeExample 'mixin-arbitrary-kwargs' %} @use "sass:meta"; @mixin syntax-colors($args...) { diff --git a/source/code-snippets/example-module-migrator.liquid b/source/_includes/code_snippets/example-module-migrator.liquid similarity index 100% rename from source/code-snippets/example-module-migrator.liquid rename to source/_includes/code_snippets/example-module-migrator.liquid diff --git a/source/code-snippets/example-nesting.liquid b/source/_includes/code_snippets/example-nesting.liquid similarity index 100% rename from source/code-snippets/example-nesting.liquid rename to source/_includes/code_snippets/example-nesting.liquid diff --git a/source/code-snippets/example-placeholder.liquid b/source/_includes/code_snippets/example-placeholder.liquid similarity index 100% rename from source/code-snippets/example-placeholder.liquid rename to source/_includes/code_snippets/example-placeholder.liquid diff --git a/source/code-snippets/example-use-with.liquid b/source/_includes/code_snippets/example-use-with.liquid similarity index 100% rename from source/code-snippets/example-use-with.liquid rename to source/_includes/code_snippets/example-use-with.liquid diff --git a/source/_includes/doc_snippets/built-in-module-status.liquid b/source/_includes/doc_snippets/built-in-module-status.liquid new file mode 100644 index 000000000..e7bfb1c17 --- /dev/null +++ b/source/_includes/doc_snippets/built-in-module-status.liquid @@ -0,0 +1,4 @@ +{% compatibility 'dart: "1.23.0"', 'libsass: false', 'ruby: false' %} + Only Dart Sass currently supports loading built-in modules with `@use`. Users + of other implementations must call functions using their global names instead. +{% endcompatibility %} diff --git a/source/_includes/doc_snippets/call-impl-status.liquid b/source/_includes/doc_snippets/call-impl-status.liquid new file mode 100644 index 000000000..c716ce472 --- /dev/null +++ b/source/_includes/doc_snippets/call-impl-status.liquid @@ -0,0 +1,11 @@ +{% compatibility 'dart: true', 'libsass: "3.5.0"', 'ruby: "3.5.0"', 'feature: "Argument Type"' %} + In older versions of LibSass and Ruby Sass, the [`call()` function][] took a + string representing a function's name. This was changed to take a function + value instead in preparation for a new module system where functions are no + longer global and so a given name may not always refer to the same function. + + [`call()` function]: /documentation/modules/meta#call + + Passing a string to `call()` still works in all implementations, but it's + deprecated and will be disallowed in future versions. +{% endcompatibility %} diff --git a/source/_includes/doc_snippets/module-system-function-status.liquid b/source/_includes/doc_snippets/module-system-function-status.liquid new file mode 100644 index 000000000..c7ef22abc --- /dev/null +++ b/source/_includes/doc_snippets/module-system-function-status.liquid @@ -0,0 +1,3 @@ +{% compatibility 'dart: "1.23.0"', 'libsass: false', 'ruby: false' %} + Only Dart Sass currently supports this function. +{% endcompatibility %} diff --git a/source/documentation/snippets/module-system-status.liquid b/source/_includes/doc_snippets/module-system-status.liquid similarity index 100% rename from source/documentation/snippets/module-system-status.liquid rename to source/_includes/doc_snippets/module-system-status.liquid diff --git a/source/documentation/snippets/operator-list.liquid b/source/_includes/doc_snippets/operator-list.liquid similarity index 100% rename from source/documentation/snippets/operator-list.liquid rename to source/_includes/doc_snippets/operator-list.liquid diff --git a/source/documentation/snippets/silence-deprecations.liquid b/source/_includes/doc_snippets/silence-deprecations.liquid similarity index 100% rename from source/documentation/snippets/silence-deprecations.liquid rename to source/_includes/doc_snippets/silence-deprecations.liquid diff --git a/source/documentation/snippets/source-maps.liquid b/source/_includes/doc_snippets/source-maps.liquid similarity index 100% rename from source/documentation/snippets/source-maps.liquid rename to source/_includes/doc_snippets/source-maps.liquid diff --git a/source/documentation/snippets/truthiness-and-falsiness.liquid b/source/_includes/doc_snippets/truthiness-and-falsiness.liquid similarity index 100% rename from source/documentation/snippets/truthiness-and-falsiness.liquid rename to source/_includes/doc_snippets/truthiness-and-falsiness.liquid diff --git a/source/_includes/function.liquid b/source/_includes/function.liquid new file mode 100644 index 000000000..264025584 --- /dev/null +++ b/source/_includes/function.liquid @@ -0,0 +1,5 @@ +{% for name in names offset: 1 %}
{% endfor -%} +
{{ signatures }}{% if returns %} //=> {{ returns }}{% endif %}
+ {{ content }} +
+{%- for name in names offset: 1 %}
{% endfor %} diff --git a/source/documentation/at-rules/at-root.liquid b/source/documentation/at-rules/at-root.liquid index 05f2827f9..7dadf17e6 100644 --- a/source/documentation/at-rules/at-root.liquid +++ b/source/documentation/at-rules/at-root.liquid @@ -10,7 +10,7 @@ introduction: > [selector functions](/documentation/modules/selector). --- -{% render 'code-snippets/example-advanced-nesting' %} +{% render 'code_snippets/example-advanced-nesting' %} {% markdown %} The `@at-root` rule is necessary here because Sass doesn't know what diff --git a/source/documentation/at-rules/control/each.liquid b/source/documentation/at-rules/control/each.liquid index 0957b176e..d31f65a8a 100644 --- a/source/documentation/at-rules/control/each.liquid +++ b/source/documentation/at-rules/control/each.liquid @@ -11,7 +11,7 @@ introduction: > the given variable name. --- -{% render 'code-snippets/example-each-list' %} +{% render 'code_snippets/example-each-list' %} {% markdown %} ## With Maps @@ -22,7 +22,7 @@ introduction: > second. {% endmarkdown %} -{% render 'code-snippets/example-each-map' %} +{% render 'code_snippets/example-each-map' %} {% markdown %} ## Destructuring diff --git a/source/documentation/at-rules/control/if.liquid b/source/documentation/at-rules/control/if.liquid index e921eb336..9ca2bf9d8 100644 --- a/source/documentation/at-rules/control/if.liquid +++ b/source/documentation/at-rules/control/if.liquid @@ -10,7 +10,7 @@ introduction: > it’s not. --- -{% render 'code-snippets/example-if' %} +{% render 'code_snippets/example-if' %} {% markdown %} ## `@else` @@ -146,4 +146,4 @@ introduction: > } {% endcodeExample %} -{% render 'documentation/snippets/truthiness-and-falsiness' %} +{% render 'doc_snippets/truthiness-and-falsiness' %} diff --git a/source/documentation/at-rules/control/while.liquid b/source/documentation/at-rules/control/while.liquid index 8fe572482..0778329f6 100644 --- a/source/documentation/at-rules/control/while.liquid +++ b/source/documentation/at-rules/control/while.liquid @@ -52,4 +52,4 @@ introduction: > [`@for`]: /documentation/at-rules/control/for {% endheadsUp %} -{% render 'documentation/snippets/truthiness-and-falsiness' %} +{% render 'doc_snippets/truthiness-and-falsiness' %} diff --git a/source/documentation/at-rules/extend.liquid b/source/documentation/at-rules/extend.liquid index 2910f2147..20a9af74d 100644 --- a/source/documentation/at-rules/extend.liquid +++ b/source/documentation/at-rules/extend.liquid @@ -204,7 +204,7 @@ introduction: > [placeholder selectors]: /documentation/style-rules/placeholder-selectors {% endmarkdown %} -{% render 'code-snippets/example-placeholder' %} +{% render 'code_snippets/example-placeholder' %} {% markdown %} ### Private Placeholders diff --git a/source/documentation/at-rules/import.liquid b/source/documentation/at-rules/import.liquid index f60495929..543d17188 100644 --- a/source/documentation/at-rules/import.liquid +++ b/source/documentation/at-rules/import.liquid @@ -484,7 +484,7 @@ introduction: > {% markdown %} ## Import and Modules - {% render 'documentation/snippets/module-system-status' %} + {% render 'doc_snippets/module-system-status' %} Sass's [module system][] integrates seamlessly with `@import`, whether you're importing a file that contains `@use` rules or loading a file that contains diff --git a/source/documentation/at-rules/mixin.liquid b/source/documentation/at-rules/mixin.liquid index 9a2502b49..b7ef1905d 100644 --- a/source/documentation/at-rules/mixin.liquid +++ b/source/documentation/at-rules/mixin.liquid @@ -277,11 +277,12 @@ introduction: > [`meta.keywords()` function]: /documentation/modules/meta#keywords [map]: /documentation/values/maps - - {% endmarkdown %} -{% render 'code-snippets/example-mixin-arbitrary-keyword-arguments' %} +{% comment -%} + TODO(nweiz): auto-generate this CSS once we're compiling with Dart Sass +{%- endcomment -%} +{% render 'code_snippets/example-mixin-arbitrary-keyword-arguments' %} {% funFact %} If you don't ever pass an argument list to the [`meta.keywords()` function][], @@ -408,7 +409,9 @@ introduction: > [map]: /documentation/values/maps {% endheadsUp %} - +{% comment -%} + TODO(nweiz): auto-generate this CSS once we're compiling with Dart Sass +{%- endcomment -%} {% codeExample 'passing-arguments-to-content-blocks', false %} @mixin media($types...) { @each $type in $types { diff --git a/source/documentation/at-rules/use.liquid b/source/documentation/at-rules/use.liquid index d41f71f82..ac1a115b2 100644 --- a/source/documentation/at-rules/use.liquid +++ b/source/documentation/at-rules/use.liquid @@ -3,7 +3,7 @@ title: "@use" table_of_contents: true eleventyComputed: before_introduction: > - {% render 'documentation/snippets/module-system-status' %} + {% render 'doc_snippets/module-system-status' %} introduction: > The `@use` rule loads [mixins](/documentation/at-rules/mixin), [functions](/documentation/at-rules/function), and @@ -298,7 +298,7 @@ introduction: > [`!default` flag]: /documentation/variables#default-values {% endmarkdown %} -{% render 'code-snippets/example-use-with' %} +{% render 'code_snippets/example-use-with' %} {% markdown %} ### With Mixins diff --git a/source/documentation/breaking-changes/bogus-combinators.liquid b/source/documentation/breaking-changes/bogus-combinators.liquid index a037f137f..37cd2ce79 100644 --- a/source/documentation/breaking-changes/bogus-combinators.liquid +++ b/source/documentation/breaking-changes/bogus-combinators.liquid @@ -57,7 +57,7 @@ introduction: > leading or trailing combinators that end up in selectors after nesting is resolved. - {% render 'documentation/snippets/silence-deprecations' %} + {% render 'doc_snippets/silence-deprecations' %} In addition, we'll immediately start omitting selectors that we know to be invalid CSS from the compiled CSS, with one exception: we _won't_ omit diff --git a/source/documentation/breaking-changes/slash-div.liquid b/source/documentation/breaking-changes/slash-div.liquid index 611d42882..c6d359c06 100644 --- a/source/documentation/breaking-changes/slash-div.liquid +++ b/source/documentation/breaking-changes/slash-div.liquid @@ -61,7 +61,7 @@ introduction: > instead. {% endmarkdown %} -{% render 'documentation/snippets/silence-deprecations' %} +{% render 'doc_snippets/silence-deprecations' %} {% codeExample 'math-div', false %} @use "sass:math"; diff --git a/source/documentation/breaking-changes/strict-unary.liquid b/source/documentation/breaking-changes/strict-unary.liquid index 59b02c8d8..8776734e5 100644 --- a/source/documentation/breaking-changes/strict-unary.liquid +++ b/source/documentation/breaking-changes/strict-unary.liquid @@ -51,7 +51,7 @@ introduction: > We'll make this an error in Dart Sass 2.0.0, but until then it'll just emit a deprecation warning. - {% render 'documentation/snippets/silence-deprecations' %} + {% render 'doc_snippets/silence-deprecations' %} ## Automatic Migration diff --git a/source/documentation/cli/dart-sass.md b/source/documentation/cli/dart-sass.md index 69385753a..7761fa443 100644 --- a/source/documentation/cli/dart-sass.md +++ b/source/documentation/cli/dart-sass.md @@ -223,7 +223,7 @@ Compiled themes/light.scss to public/css/light.css. {% compatibility 'dart: "1.3.0"' %}{% endcompatibility %} -{% render 'documentation/snippets/source-maps' %} +{% render 'doc_snippets/source-maps' %} Dart Sass generates source maps by default for every CSS file it emits. diff --git a/source/documentation/cli/migrator.md b/source/documentation/cli/migrator.md index 9d77497f9..58b98040d 100644 --- a/source/documentation/cli/migrator.md +++ b/source/documentation/cli/migrator.md @@ -33,7 +33,7 @@ see what changes will be made without actually saving them, you can pass [--dry-run]: #dry-run [--verbose]: #verbose -{% render 'code-snippets/example-module-migrator' %} +{% render 'code_snippets/example-module-migrator' %} ## Installation @@ -259,7 +259,7 @@ before, including: [`--migrate-deps` option]: #migrate-deps {% endheadsUp %} -{% render 'code-snippets/example-module-migrator' %} +{% render 'code_snippets/example-module-migrator' %} #### Loading Dependencies diff --git a/source/documentation/modules/color.liquid b/source/documentation/modules/color.liquid new file mode 100644 index 000000000..488e8ce85 --- /dev/null +++ b/source/documentation/modules/color.liquid @@ -0,0 +1,925 @@ +--- +title: sass:color +--- + +{% render 'doc_snippets/built-in-module-status' %} + +{% capture color_adjust %} + color.adjust($color, + $red: null, $green: null, $blue: null, + $hue: null, $saturation: null, $lightness: null, + $whiteness: null, $blackness: null, + $alpha: null) +{% endcapture %} + +{% function color_adjust, 'adjust-color(...)', 'returns:color' %} + {% compatibility 'dart: "1.28.0"', 'libsass: false', 'ruby: false', 'feature: "$whiteness and $blackness"' %}{% endcompatibility %} + + {% markdown %} + Increases or decreases one or more properties of `$color` by fixed amounts. + + Adds the value passed for each keyword argument to the corresponding + property of the color, and returns the adjusted color. It's an error to + specify an RGB property (`$red`, `$green`, and/or `$blue`) at the same time + as an HSL property (`$hue`, `$saturation`, and/or `$lightness`), or either + of those at the same time as an [HWB][] property (`$hue`, `$whiteness`, + and/or `$blackness`). + + [HWB]: https://en.wikipedia.org/wiki/HWB_color_model + + All optional arguments must be numbers. The `$red`, `$green`, and `$blue` + arguments must be [unitless][] and between -255 and 255 (inclusive). The + `$hue` argument must have either the unit `deg` or no unit. The + `$saturation`, `$lightness`, `$whiteness`, and `$blackness` arguments must + be between `-100%` and `100%` (inclusive), and may not be unitless. The + `$alpha` argument must be unitless and between -1 and 1 (inclusive). + + [unitless]: /documentation/values/numbers#units + + See also: + + * [`color.scale()`](#scale) for fluidly scaling a color's properties. + * [`color.change()`](#change) for setting a color's properties. + {% endmarkdown %} + + {% codeExample 'adjust-color', false %} + @debug color.adjust(#6b717f, $red: 15); // #7a717f + @debug color.adjust(#d2e1dd, $red: -10, $blue: 10); // #c8e1e7 + @debug color.adjust(#998099, $lightness: -30%, $alpha: -0.4); // rgba(71, 57, 71, 0.6) + === + @debug color.adjust(#6b717f, $red: 15) // #7a717f + @debug color.adjust(#d2e1dd, $red: -10, $blue: 10) // #c8e1e7 + @debug color.adjust(#998099, $lightness: -30%, $alpha: -0.4) // rgba(71, 57, 71, 0.6) + {% endcodeExample %} +{% endfunction %} + +{% function 'adjust-hue($color, $degrees)', 'returns:color' %} + {% markdown %} + Increases or decreases `$color`'s hue. + + The `$hue` must be a number between `-360deg` and `360deg` (inclusive) to + add to `$color`'s hue. It may be [unitless][] but it may not have any unit + other than `deg`. + + [unitless]: /documentation/values/numbers#units + + See also [`color.adjust()`](#adjust), which can adjust any property of a + color. + {% endmarkdown %} + + {% headsUp %} + Because `adjust-hue()` is redundant with [`adjust()`](#adjust), it's not + included directly in the new module system. Instead of `adjust-hue($color, + $amount)`, you can write [`color.adjust($color, $hue: $amount)`](#adjust). + {% endheadsUp %} + + {% codeExample 'adjust-hue', false %} + // Hue 222deg becomes 282deg. + @debug adjust-hue(#6b717f, 60deg); // #796b7f + + // Hue 164deg becomes 104deg. + @debug adjust-hue(#d2e1dd, -60deg); // #d6e1d2 + + // Hue 210deg becomes 255deg. + @debug adjust-hue(#036, 45); // #1a0066 + === + // Hue 222deg becomes 282deg. + @debug adjust-hue(#6b717f, 60deg) // #796b7f + + // Hue 164deg becomes 104deg. + @debug adjust-hue(#d2e1dd, -60deg) // #d6e1d2 + + // Hue 210deg becomes 255deg. + @debug adjust-hue(#036, 45) // #1a0066 + {% endcodeExample %} +{% endfunction %} + +{% function 'color.alpha($color)', 'alpha($color)', 'opacity($color)', 'returns:number' %} + {% markdown %} + Returns the alpha channel of `$color` as a number between 0 and 1. + + As a special case, this supports the Internet Explorer syntax + `alpha(opacity=20)`, for which it returns an [unquoted string][]. + + [unquoted string]: /documentation/values/strings#unquoted + + See also: + + * [`color.red()`](#red) for getting a color's red channel. + * [`color.green()`](#green) for getting a color's green channel. + * [`color.blue()`](#blue) for getting a color's blue channel. + * [`color.hue()`](#hue) for getting a color's hue. + * [`color.saturation()`](#saturation) for getting a color's saturation. + * [`color.lightness()`](#lightness) for getting a color's lightness. + {% endmarkdown %} + + {% codeExample 'color-alpha', false %} + @debug color.alpha(#e1d7d2); // 1 + @debug color.opacity(rgb(210, 225, 221, 0.4)); // 0.4 + @debug alpha(opacity=20); // alpha(opacity=20) + === + @debug color.alpha(#e1d7d2) // 1 + @debug color.opacity(rgb(210, 225, 221, 0.4)) // 0.4 + @debug alpha(opacity=20) // alpha(opacity=20) + {% endcodeExample %} +{% endfunction %} + +{% function 'color.blackness($color)', 'returns:number' %} + {% compatibility 'dart: "1.28.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + Returns the [HWB][] blackness of `$color` as a number between `0%` and + `100%`. + + [HWB]: https://en.wikipedia.org/wiki/HWB_color_model + + See also: + + * [`color.red()`](#red) for getting a color's red channel. + * [`color.green()`](#green) for getting a color's green channel. + * [`color.hue()`](#hue) for getting a color's hue. + * [`color.saturation()`](#saturation) for getting a color's saturation. + * [`color.lightness()`](#lightness) for getting a color's lightness. + * [`color.whiteness()`](#whiteness) for getting a color's whiteness. + * [`color.alpha()`](#alpha) for getting a color's alpha channel. + {% endmarkdown %} + + {% codeExample 'color-blackness', false %} + @debug color.blackness(#e1d7d2); // 11.7647058824% + @debug color.blackness(white); // 0% + @debug color.blackness(black); // 100% + === + @debug color.blackness(#e1d7d2) // 11.7647058824% + @debug color.blackness(white) // 0% + @debug color.blackness(black) // 100% + {% endcodeExample %} +{% endfunction %} + +{% function 'color.blue($color)', 'blue($color)', 'returns:number' %} + {% markdown %} + Returns the blue channel of `$color` as a number between 0 and 255. + + See also: + + * [`color.red()`](#red) for getting a color's red channel. + * [`color.green()`](#green) for getting a color's green channel. + * [`color.hue()`](#hue) for getting a color's hue. + * [`color.saturation()`](#saturation) for getting a color's saturation. + * [`color.lightness()`](#lightness) for getting a color's lightness. + * [`color.whiteness()`](#whiteness) for getting a color's whiteness. + * [`color.blackness()`](#blackness) for getting a color's blackness. + * [`color.alpha()`](#alpha) for getting a color's alpha channel. + {% endmarkdown %} + + {% codeExample 'color-blue', false %} + @debug color.blue(#e1d7d2); // 210 + @debug color.blue(white); // 255 + @debug color.blue(black); // 0 + === + @debug color.blue(#e1d7d2) // 210 + @debug color.blue(white) // 255 + @debug color.blue(black) // 0 + {% endcodeExample %} +{% endfunction %} + +{% capture color_change %} + color.change($color, + $red: null, $green: null, $blue: null, + $hue: null, $saturation: null, $lightness: null, + $whiteness: null, $blackness: null, + $alpha: null) +{% endcapture %} + +{% function color_change, 'change-color(...)', 'returns:color' %} + {% compatibility 'dart: "1.28.0"', 'libsass: false', 'ruby: false', 'feature: "$whiteness and $blackness"' %}{% endcompatibility %} + + {% markdown %} + Sets one or more properties of a color to new values. + + Uses the value passed for each keyword argument in place of the + corresponding property of the color, and returns the changed color. It's an + error to specify an RGB property (`$red`, `$green`, and/or `$blue`) at the + same time as an HSL property (`$hue`, `$saturation`, and/or `$lightness`), + or either of those at the same time as an [HWB][] property (`$hue`, + `$whiteness`, and/or `$blackness`). + + [HWB]: https://en.wikipedia.org/wiki/HWB_color_model + + All optional arguments must be numbers. The `$red`, `$green`, and `$blue` + arguments must be [unitless][] and between 0 and 255 (inclusive). The `$hue` + argument must have either the unit `deg` or no unit. The `$saturation`, + `$lightness`, `$whiteness`, and `$blackness` arguments must be between `0%` + and `100%` (inclusive), and may not be unitless. The `$alpha` argument must + be unitless and between 0 and 1 (inclusive). + + [unitless]: /documentation/values/numbers#units + + See also: + + * [`color.scale()`](#scale) for fluidly scaling a color's properties. + * [`color.adjust()`](#adjust) for adjusting a color's properties by fixed + amounts. + {% endmarkdown %} + + {% codeExample 'color-change', false %} + @debug color.change(#6b717f, $red: 100); // #64717f + @debug color.change(#d2e1dd, $red: 100, $blue: 50); // #64e132 + @debug color.change(#998099, $lightness: 30%, $alpha: 0.5); // rgba(85, 68, 85, 0.5) + === + @debug color.change(#6b717f, $red: 100) // #64717f + @debug color.change(#d2e1dd, $red: 100, $blue: 50) // #64e132 + @debug color.change(#998099, $lightness: 30%, $alpha: 0.5) // rgba(85, 68, 85, 0.5) + {% endcodeExample %} +{% endfunction %} + +{% function 'color.complement($color)', 'complement($color)', 'returns:color' %} + {% markdown %} + Returns the RGB [complement][] of `$color`. + + This is identical to [`color.adjust($color, $hue: 180deg)`](#adjust). + + [complement]: https://en.wikipedia.org/wiki/Complementary_colors + {% endmarkdown %} + + {% codeExample 'color-complement', false %} + // Hue 222deg becomes 42deg. + @debug color.complement(#6b717f); // #7f796b + + // Hue 164deg becomes 344deg. + @debug color.complement(#d2e1dd); // #e1d2d6 + + // Hue 210deg becomes 30deg. + @debug color.complement(#036); // #663300 + === + // Hue 222deg becomes 42deg. + @debug color.complement(#6b717f) // #7f796b + + // Hue 164deg becomes 344deg. + @debug color.complement(#d2e1dd) // #e1d2d6 + + // Hue 210deg becomes 30deg. + @debug color.complement(#036) // #663300 + {% endcodeExample %} +{% endfunction %} + +{% function 'darken($color, $amount)', 'returns:color' %} + {% markdown %} + Makes `$color` darker. + + The `$amount` must be a number between `0%` and `100%` (inclusive). + Decreases the HSL lightness of `$color` by that amount. + {% endmarkdown %} + + {% headsUp false %} + {% markdown %} + The `darken()` function decreases lightness by a fixed amount, which is + often not the desired effect. To make a color a certain percentage darker + than it was before, use [`color.scale()`](#scale) instead. + + Because `darken()` is usually not the best way to make a color darker, + it's not included directly in the new module system. However, if you have + to preserve the existing behavior, `darken($color, $amount)` can be + written [`color.adjust($color, $lightness: -$amount)`](#adjust). + {% endmarkdown %} + + {% codeExample 'color-darken', false %} + // #036 has lightness 20%, so when darken() subtracts 30% it just returns black. + @debug darken(#036, 30%); // black + + // scale() instead makes it 30% darker than it was originally. + @debug color.scale(#036, $lightness: -30%); // #002447 + === + // #036 has lightness 20%, so when darken() subtracts 30% it just returns black. + @debug darken(#036, 30%) // black + + // scale() instead makes it 30% darker than it was originally. + @debug color.scale(#036, $lightness: -30%) // #002447 + {% endcodeExample %} + {% endheadsUp %} + + {% codeExample 'color-darken-2', false %} + // Lightness 92% becomes 72%. + @debug darken(#b37399, 20%); // #7c4465 + + // Lightness 85% becomes 45%. + @debug darken(#f2ece4, 40%); // #b08b5a + + // Lightness 20% becomes 0%. + @debug darken(#036, 30%); // black + === + // Lightness 92% becomes 72%. + @debug darken(#b37399, 20%) // #7c4465 + + // Lightness 85% becomes 45%. + @debug darken(#f2ece4, 40%) // #b08b5a + + // Lightness 20% becomes 0%. + @debug darken(#036, 30%) // black + {% endcodeExample %} +{% endfunction %} + +{% function 'desaturate($color, $amount)', 'returns:color' %} + {% markdown %} + Makes `$color` less saturated. + + The `$amount` must be a number between `0%` and `100%` (inclusive). + Decreases the HSL saturation of `$color` by that amount. + {% endmarkdown %} + + {% headsUp false %} + {% markdown %} + The `desaturate()` function decreases saturation by a fixed amount, which + is often not the desired effect. To make a color a certain percentage less + saturated than it was before, use [`color.scale()`](#scale) instead. + + Because `desaturate()` is usually not the best way to make a color less + saturated, it's not included directly in the new module system. However, + if you have to preserve the existing behavior, `desaturate($color, + $amount)` can be written [`color.adjust($color, $saturation: + -$amount)`](#adjust). + {% endmarkdown %} + + {% codeExample 'color-desaturate', false %} + // #d2e1dd has saturation 20%, so when desaturate() subtracts 30% it just + // returns gray. + @debug desaturate(#d2e1dd, 30%); // #dadada + + // scale() instead makes it 30% less saturated than it was originally. + @debug color.scale(#6b717f, $saturation: -30%); // #6e727c + === + // #6b717f has saturation 20%, so when desaturate() subtracts 30% it just + // returns gray. + @debug desaturate(#d2e1dd, 30%) // #dadada + + // scale() instead makes it 30% less saturated than it was originally. + @debug color.scale(#6b717f, $saturation: -30%) // #6e727c + {% endcodeExample %} + {% endheadsUp %} + + {% codeExample 'color-desaturate-2', false %} + // Saturation 100% becomes 80%. + @debug desaturate(#036, 20%); // #0a335c + + // Saturation 35% becomes 15%. + @debug desaturate(#f2ece4, 20%); // #eeebe8 + + // Saturation 20% becomes 0%. + @debug desaturate(#d2e1dd, 30%); // #dadada + === + // Saturation 100% becomes 80%. + @debug desaturate(#036, 20%) // #0a335c + + // Saturation 35% becomes 15%. + @debug desaturate(#f2ece4, 20%) // #eeebe8 + + // Saturation 20% becomes 0%. + @debug desaturate(#d2e1dd, 30%) // #dadada + {% endcodeExample %} +{% endfunction %} + +{% function 'color.grayscale($color)', 'grayscale($color)', 'returns:color' %} + {% markdown %} + Returns a gray color with the same lightness as `$color`. + + This is identical to [`color.change($color, $saturation: 0%)`](#change). + {% endmarkdown %} + + {% codeExample 'color-grayscale', false %} + @debug color.grayscale(#6b717f); // #757575 + @debug color.grayscale(#d2e1dd); // #dadada + @debug color.grayscale(#036); // #333333 + === + @debug color.grayscale(#6b717f) // #757575 + @debug color.grayscale(#d2e1dd) // #dadada + @debug color.grayscale(#036) // #333333 + {% endcodeExample %} +{% endfunction %} + +{% function 'color.green($color)', 'green($color)', 'returns:number' %} + {% markdown %} + Returns the green channel of `$color` as a number between 0 and 255. + + See also: + + * [`color.red()`](#red) for getting a color's red channel. + * [`color.blue()`](#blue) for getting a color's blue channel. + * [`color.hue()`](#hue) for getting a color's hue. + * [`color.saturation()`](#saturation) for getting a color's saturation. + * [`color.lightness()`](#lightness) for getting a color's lightness. + * [`color.whiteness()`](#whiteness) for getting a color's whiteness. + * [`color.blackness()`](#blackness) for getting a color's blackness. + * [`color.alpha()`](#alpha) for getting a color's alpha channel. + {% endmarkdown %} + + {% codeExample 'color-green', false %} + @debug color.green(#e1d7d2); // 215 + @debug color.green(white); // 255 + @debug color.green(black); // 0 + === + @debug color.green(#e1d7d2) // 215 + @debug color.green(white) // 255 + @debug color.green(black) // 0 + {% endcodeExample %} +{% endfunction %} + +{% function 'color.hue($color)', 'hue($color)', 'returns:number' %} + {% markdown %} + Returns the hue of `$color` as a number between `0deg` and `360deg`. + + See also: + + * [`color.red()`](#red) for getting a color's red channel. + * [`color.green()`](#green) for getting a color's green channel. + * [`color.blue()`](#blue) for getting a color's blue channel. + * [`color.saturation()`](#saturation) for getting a color's saturation. + * [`color.lightness()`](#lightness) for getting a color's lightness. + * [`color.whiteness()`](#whiteness) for getting a color's whiteness. + * [`color.blackness()`](#blackness) for getting a color's blackness. + * [`color.alpha()`](#alpha) for getting a color's alpha channel. + {% endmarkdown %} + + {% codeExample 'color-hue', false %} + @debug color.hue(#e1d7d2); // 20deg + @debug color.hue(#f2ece4); // 34.2857142857deg + @debug color.hue(#dadbdf); // 228deg + === + @debug color.hue(#e1d7d2) // 20deg + @debug color.hue(#f2ece4) // 34.2857142857deg + @debug color.hue(#dadbdf) // 228deg + {% endcodeExample %} +{% endfunction %} + +{% function 'color.hwb($hue $whiteness $blackness)', 'color.hwb($hue $whiteness $blackness / $alpha)', 'color.hwb($hue, $whiteness, $blackness, $alpha: 1)', 'returns:color' %} + {% compatibility 'dart: "1.28.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + Returns a color with the given [hue, whiteness, and blackness][] and the + given alpha channel. + + [hue, whiteness, and blackness]: https://en.wikipedia.org/wiki/HWB_color_model + + The hue is a number between `0deg` and `360deg` (inclusive). The whiteness + and blackness are numbers between `0%` and `100%` (inclusive). The hue may + be [unitless][], but the whiteness and blackness must have unit `%`. The + alpha channel can be specified as either a unitless number between 0 and 1 + (inclusive), or a percentage between `0%` and `100%` (inclusive). + + [unitless]: /documentation/values/numbers#units + {% endmarkdown %} + + {% headsUp %} + Sass's [special parsing rules][] for slash-separated values make it + difficult to pass variables for `$blackness` or `$alpha` when using the + `color.hwb($hue $whiteness $blackness / $alpha)` signature. Consider using + `color.hwb($hue, $whiteness, $blackness, $alpha)` instead. + + [special parsing rules]: /documentation/operators/numeric#slash-separated-values + {% endheadsUp %} + + {% codeExample 'color-hwb', false %} + @debug color.hwb(210, 0%, 60%); // #036 + @debug color.hwb(34, 89%, 5%); // #f2ece4 + @debug color.hwb(210 0% 60% / 0.5); // rgba(0, 51, 102, 0.5) + === + @debug color.hwb(210, 0%, 60%) // #036 + @debug color.hwb(34, 89%, 5%) // #f2ece4 + @debug color.hwb(210 0% 60% / 0.5) // rgba(0, 51, 102, 0.5) + {% endcodeExample %} +{% endfunction %} + +{% function 'color.ie-hex-str($color)', 'ie-hex-str($color)', 'returns:unquoted string' %} + {% markdown %} + Returns an unquoted string that represents `$color` in the `#AARRGGBB` + format expected by Internet Explorer's [`-ms-filter`][] property. + + [`-ms-filter`]: https://developer.mozilla.org/en-US/docs/Web/CSS/-ms-filter + {% endmarkdown %} + + {% codeExample 'color-ie-hex-str', false %} + @debug color.ie-hex-str(#b37399); // #FFB37399 + @debug color.ie-hex-str(#808c99); // #FF808C99 + @debug color.ie-hex-str(rgba(242, 236, 228, 0.6)); // #99F2ECE4 + === + @debug color.ie-hex-str(#b37399); // #FFB37399 + @debug color.ie-hex-str(#808c99); // #FF808C99 + @debug color.ie-hex-str(rgba(242, 236, 228, 0.6)); // #99F2ECE4 + {% endcodeExample %} +{% endfunction %} + +{% function 'color.invert($color, $weight: 100%)', 'invert($color, $weight: 100%)', 'returns:color' %} + {% markdown %} + Returns the inverse or [negative][] of `$color`. + + [negative]: https://en.wikipedia.org/wiki/Negative_(photography) + + The `$weight` must be a number between `0%` and `100%` (inclusive). A higher + weight means the result will be closer to the negative, and a lower weight + means it will be closer to `$color`. Weight `50%` will always produce + `#808080`. + {% endmarkdown %} + + {% codeExample 'color-invert', false %} + @debug color.invert(#b37399); // #4c8c66 + @debug color.invert(black); // white + @debug color.invert(#550e0c, 20%); // #663b3a + === + @debug color.invert(#b37399) // #4c8c66 + @debug color.invert(black) // white + @debug color.invert(#550e0c, 20%) // #663b3a + {% endcodeExample %} +{% endfunction %} + +{% function 'lighten($color, $amount)', 'returns:color' %} + {% markdown %} + Makes `$color` lighter. + + The `$amount` must be a number between `0%` and `100%` (inclusive). + Increases the HSL lightness of `$color` by that amount. + {% endmarkdown %} + + {% headsUp false %} + {% markdown %} + The `lighten()` function increases lightness by a fixed amount, which is + often not the desired effect. To make a color a certain percentage lighter + than it was before, use [`scale()`](#scale) instead. + + Because `lighten()` is usually not the best way to make a color lighter, + it's not included directly in the new module system. However, if you have + to preserve the existing behavior, `lighten($color, $amount)` can be + written [`adjust($color, $lightness: $amount)`](#adjust). + {% endmarkdown %} + + {% codeExample 'color-lighten', false %} + // #e1d7d2 has lightness 85%, so when lighten() adds 30% it just returns white. + @debug lighten(#e1d7d2, 30%); // white + + // scale() instead makes it 30% lighter than it was originally. + @debug color.scale(#e1d7d2, $lightness: 30%); // #eae3e0 + === + // #e1d7d2 has lightness 85%, so when lighten() adds 30% it just returns white. + @debug lighten(#e1d7d2, 30%) // white + + // scale() instead makes it 30% lighter than it was originally. + @debug color.scale(#e1d7d2, $lightness: 30%) // #eae3e0 + {% endcodeExample %} + {% endheadsUp %} + + {% codeExample 'color-lighten-2', false %} + // Lightness 46% becomes 66%. + @debug lighten(#6b717f, 20%); // #a1a5af + + // Lightness 20% becomes 80%. + @debug lighten(#036, 60%); // #99ccff + + // Lightness 85% becomes 100%. + @debug lighten(#e1d7d2, 30%); // white + === + // Lightness 46% becomes 66%. + @debug lighten(#6b717f, 20%) // #a1a5af + + // Lightness 20% becomes 80%. + @debug lighten(#036, 60%) // #99ccff + + // Lightness 85% becomes 100%. + @debug lighten(#e1d7d2, 30%) // white + {% endcodeExample %} +{% endfunction %} + +{% function 'color.lightness($color)', 'lightness($color)', 'returns:number' %} + {% markdown %} + Returns the HSL lightness of `$color` as a number between `0%` and `100%`. + + See also: + + * [`color.red()`](#red) for getting a color's red channel. + * [`color.green()`](#green) for getting a color's green channel. + * [`color.blue()`](#blue) for getting a color's blue channel. + * [`color.hue()`](#hue) for getting a color's hue. + * [`color.saturation()`](#saturation) for getting a color's saturation. + * [`color.whiteness()`](#whiteness) for getting a color's whiteness. + * [`color.blackness()`](#blackness) for getting a color's blackness. + * [`color.alpha()`](#alpha) for getting a color's alpha channel. + {% endmarkdown %} + + {% codeExample 'color-lightness', false %} + @debug color.lightness(#e1d7d2); // 85.2941176471% + @debug color.lightness(#f2ece4); // 92.1568627451% + @debug color.lightness(#dadbdf); // 86.4705882353% + === + @debug color.lightness(#e1d7d2) // 85.2941176471% + @debug color.lightness(#f2ece4) // 92.1568627451% + @debug color.lightness(#dadbdf) // 86.4705882353% + {% endcodeExample %} +{% endfunction %} + +{% function 'color.mix($color1, $color2, $weight: 50%)', 'mix($color1, $color2, $weight: 50%)', 'returns:color' %} + {% markdown %} + Returns a color that's a mixture of `$color1` and `$color2`. + + Both the `$weight` and the relative opacity of each color determines how + much of each color is in the result. The `$weight` must be a number between + `0%` and `100%` (inclusive). A larger weight indicates that more of + `$color1` should be used, and a smaller weight indicates that more of + `$color2` should be used. + {% endmarkdown %} + + {% codeExample 'color-mix', false %} + @debug color.mix(#036, #d2e1dd); // #698aa2 + @debug color.mix(#036, #d2e1dd, 75%); // #355f84 + @debug color.mix(#036, #d2e1dd, 25%); // #9eb6bf + @debug color.mix(rgba(242, 236, 228, 0.5), #6b717f); // rgba(141, 144, 152, 0.75) + === + @debug color.mix(#036, #d2e1dd) // #698aa2 + @debug color.mix(#036, #d2e1dd, 75%) // #355f84 + @debug color.mix(#036, #d2e1dd, 25%) // #9eb6bf + @debug color.mix(rgba(242, 236, 228, 0.5), #6b717f) // rgba(141, 144, 152, 0.75) + {% endcodeExample %} +{% endfunction %} + +{% function 'opacify($color, $amount)', 'fade-in($color, $amount)', 'returns:color' %} + {% markdown %} + Makes `$color` more opaque. + + The `$amount` must be a number between `0` and `1` (inclusive). Increases + the alpha channel of `$color` by that amount. + {% endmarkdown %} + + {% headsUp false %} + {% markdown %} + The `opacify()` function increases the alpha channel by a fixed amount, + which is often not the desired effect. To make a color a certain + percentage more opaque than it was before, use [`scale()`](#scale) + instead. + + Because `opacify()` is usually not the best way to make a color more + opaque, it's not included directly in the new module system. However, if + you have to preserve the existing behavior, `opacify($color, $amount)` can + be written [`adjust($color, $alpha: -$amount)`](#adjust). + {% endmarkdown %} + + {% codeExample 'color-opacify', false %} + // rgba(#036, 0.7) has alpha 0.7, so when opacify() adds 0.3 it returns a fully + // opaque color. + @debug opacify(rgba(#036, 0.7), 0.3); // #036 + + // scale() instead makes it 30% more opaque than it was originally. + @debug color.scale(rgba(#036, 0.7), $alpha: 30%); // rgba(0, 51, 102, 0.79) + === + // rgba(#036, 0.7) has alpha 0.7, so when opacify() adds 0.3 it returns a fully + // opaque color. + @debug opacify(rgba(#036, 0.7), 0.3) // #036 + + // scale() instead makes it 30% more opaque than it was originally. + @debug color.scale(rgba(#036, 0.7), $alpha: 30%) // rgba(0, 51, 102, 0.79) + {% endcodeExample %} + {% endheadsUp %} + + {% codeExample 'color-opacify-2', false %} + @debug opacify(rgba(#6b717f, 0.5), 0.2); // rgba(107, 113, 127, 0.7) + @debug fade-in(rgba(#e1d7d2, 0.5), 0.4); // rgba(225, 215, 210, 0.9) + @debug opacify(rgba(#036, 0.7), 0.3); // #036 + === + @debug opacify(rgba(#6b717f, 0.5), 0.2) // rgba(107, 113, 127, 0.7) + @debug fade-in(rgba(#e1d7d2, 0.5), 0.4) // rgba(225, 215, 210, 0.9) + @debug opacify(rgba(#036, 0.7), 0.3) // #036 + {% endcodeExample %} +{% endfunction %} + +{% function 'color.red($color)', 'red($color)', 'returns:number' %} + {% markdown %} + Returns the red channel of `$color` as a number between 0 and 255. + + See also: + + * [`color.green()`](#green) for getting a color's green channel. + * [`color.blue()`](#blue) for getting a color's blue channel. + * [`color.hue()`](#hue) for getting a color's hue. + * [`color.saturation()`](#saturation) for getting a color's saturation. + * [`color.lightness()`](#lightness) for getting a color's lightness. + * [`color.whiteness()`](#whiteness) for getting a color's whiteness. + * [`color.blackness()`](#blackness) for getting a color's blackness. + * [`color.alpha()`](#alpha) for getting a color's alpha channel. + {% endmarkdown %} + + {% codeExample 'color-red', false %} + @debug color.red(#e1d7d2); // 225 + @debug color.red(white); // 255 + @debug color.red(black); // 0 + === + @debug color.red(#e1d7d2) // 225 + @debug color.red(white) // 255 + @debug color.red(black) // 0 + {% endcodeExample %} +{% endfunction %} + +{% function 'color.saturate($color, $amount)', 'saturate($color, $amount)', 'returns:color' %} + {% markdown %} + Makes `$color` more saturated. + + The `$amount` must be a number between `0%` and `100%` (inclusive). + Increases the HSL saturation of `$color` by that amount. + {% endmarkdown %} + + {% headsUp false %} + {% markdown %} + The `saturate()` function increases saturation by a fixed amount, which is + often not the desired effect. To make a color a certain percentage more + saturated than it was before, use [`scale()`](#scale) instead. + + Because `saturate()` is usually not the best way to make a color more + saturated, it's not included directly in the new module system. However, + if you have to preserve the existing behavior, `saturate($color, $amount)` + can be written [`adjust($color, $saturation: $amount)`](#adjust). + {% endmarkdown %} + + {% codeExample 'color-saturate', false %} + // #0e4982 has saturation 80%, so when saturate() adds 30% it just becomes + // fully saturated. + @debug saturate(#0e4982, 30%); // #004990 + + // scale() instead makes it 30% more saturated than it was originally. + @debug color.scale(#0e4982, $saturation: 30%); // #0a4986 + === + // #0e4982 has saturation 80%, so when saturate() adds 30% it just becomes + // fully saturated. + @debug saturate(#0e4982, 30%) // #004990 + + // scale() instead makes it 30% more saturated than it was originally. + @debug color.scale(#0e4982, $saturation: 30%) // #0a4986 + {% endcodeExample %} + {% endheadsUp %} + + {% codeExample 'color-saturate-2', false %} + // Saturation 50% becomes 70%. + @debug saturate(#c69, 20%); // #e05299 + + // Saturation 35% becomes 85%. + @debug desaturate(#f2ece4, 50%); // #ebebeb + + // Saturation 80% becomes 100%. + @debug saturate(#0e4982, 30%) // #004990 + === + // Saturation 50% becomes 70%. + @debug saturate(#c69, 20%); // #e05299 + + // Saturation 35% becomes 85%. + @debug desaturate(#f2ece4, 50%); // #ebebeb + + // Saturation 80% becomes 100%. + @debug saturate(#0e4982, 30%) // #004990 + {% endcodeExample %} +{% endfunction %} + +{% function 'color.saturation($color)', 'saturation($color)', 'returns:number' %} + {% markdown %} + Returns the HSL saturation of `$color` as a number between `0%` and `100%`. + + See also: + + * [`color.red()`](#red) for getting a color's red channel. + * [`color.green()`](#green) for getting a color's green channel. + * [`color.blue()`](#blue) for getting a color's blue channel. + * [`color.hue()`](#hue) for getting a color's hue. + * [`color.lightness()`](#lightness) for getting a color's lightness. + * [`color.whiteness()`](#whiteness) for getting a color's whiteness. + * [`color.blackness()`](#blackness) for getting a color's blackness. + * [`color.alpha()`](#alpha) for getting a color's alpha channel. + {% endmarkdown %} + + {% codeExample 'color-saturation', false %} + @debug color.saturation(#e1d7d2); // 20% + @debug color.saturation(#f2ece4); // 30% + @debug color.saturation(#dadbdf); // 7.2463768116% + === + @debug color.saturation(#e1d7d2) // 20% + @debug color.saturation(#f2ece4) // 30% + @debug color.saturation(#dadbdf) // 7.2463768116% + {% endcodeExample %} +{% endfunction %} + +{% capture color_scale %} + color.scale($color, + $red: null, $green: null, $blue: null, + $saturation: null, $lightness: null, + $whiteness: null, $blackness: null, + $alpha: null) +{% endcapture %} + +{% function color_scale, 'scale-color(...)', 'returns:color' %} + {% compatibility 'dart: "1.28.0"', 'libsass: false', 'ruby: false', 'feature: "$whiteness and $blackness"' %}{% endcompatibility %} + + {% markdown %} + Fluidly scales one or more properties of `$color`. + + Each keyword argument must be a number between `-100%` and `100%` + (inclusive). This indicates how far the corresponding property should be + moved from its original position towards the maximum (if the argument is + positive) or the minimum (if the argument is negative). This means that, for + example, `$lightness: 50%` will make all colors `50%` closer to maximum + lightness without making them fully white. + + It's an error to specify an RGB property (`$red`, `$green`, and/or `$blue`) + at the same time as an HSL property (`$saturation`, and/or `$lightness`), or + either of those at the same time as an [HWB][] property (`$whiteness`, + and/or `$blackness`). + + [HWB]: https://en.wikipedia.org/wiki/HWB_color_model + + See also: + + * [`color.adjust()`](#adjust) for changing a color's properties by fixed + amounts. + * [`color.change()`](#change) for setting a color's properties. + {% endmarkdown %} + + {% codeExample 'color-scale', false %} + @debug color.scale(#6b717f, $red: 15%); // #81717f + @debug color.scale(#d2e1dd, $lightness: -10%, $saturation: 10%); // #b3d4cb + @debug color.scale(#998099, $alpha: -40%); // rgba(153, 128, 153, 0.6) + === + @debug color.scale(#6b717f, $red: 15%) // #81717f + @debug color.scale(#d2e1dd, $lightness: -10%, $saturation: 10%) // #b3d4cb + @debug color.scale(#998099, $alpha: -40%) // rgba(153, 128, 153, 0.6) + {% endcodeExample %} +{% endfunction %} + +{% function 'transparentize($color, $amount)', 'fade-out($color, $amount)', 'returns:color' %} + {% markdown %} + Makes `$color` more transparent. + + The `$amount` must be a number between `0` and `1` (inclusive). Decreases + the alpha channel of `$color` by that amount. + {% endmarkdown %} + + {% headsUp false %} + {% markdown %} + The `transparentize()` function decreases the alpha channel by a fixed + amount, which is often not the desired effect. To make a color a certain + percentage more transparent than it was before, use + [`color.scale()`](#scale) instead. + + Because `transparentize()` is usually not the best way to make a color + more transparent, it's not included directly in the new module system. + However, if you have to preserve the existing behavior, + `transparentize($color, $amount)` can be written [`color.adjust($color, + $alpha: -$amount)`](#adjust). + {% endmarkdown %} + + {% codeExample 'transparentize', false %} + // rgba(#036, 0.3) has alpha 0.3, so when transparentize() subtracts 0.3 it + // returns a fully transparent color. + @debug transparentize(rgba(#036, 0.3), 0.3); // rgba(0, 51, 102, 0) + + // scale() instead makes it 30% more transparent than it was originally. + @debug color.scale(rgba(#036, 0.3), $alpha: -30%); // rgba(0, 51, 102, 0.21) + === + // rgba(#036, 0.3) has alpha 0.3, so when transparentize() subtracts 0.3 it + // returns a fully transparent color. + @debug transparentize(rgba(#036, 0.3), 0.3) // rgba(0, 51, 102, 0) + + // scale() instead makes it 30% more transparent than it was originally. + @debug color.scale(rgba(#036, 0.3), $alpha: -30%) // rgba(0, 51, 102, 0.21) + {% endcodeExample %} + {% endheadsUp %} + + {% codeExample 'transparentize-2', false %} + @debug transparentize(rgba(#6b717f, 0.5), 0.2) // rgba(107, 113, 127, 0.3) + @debug fade-out(rgba(#e1d7d2, 0.5), 0.4) // rgba(225, 215, 210, 0.1) + @debug transparentize(rgba(#036, 0.3), 0.3) // rgba(0, 51, 102, 0) + === + @debug transparentize(rgba(#6b717f, 0.5), 0.2) // rgba(107, 113, 127, 0.3) + @debug fade-out(rgba(#e1d7d2, 0.5), 0.4) // rgba(225, 215, 210, 0.1) + @debug transparentize(rgba(#036, 0.3), 0.3) // rgba(0, 51, 102, 0) + {% endcodeExample %} +{% endfunction %} + +{% function 'color.whiteness($color)', 'returns:number' %} + {% compatibility 'dart: "1.28.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + Returns the [HWB][] whiteness of `$color` as a number between `0%` and + `100%`. + + [HWB]: https://en.wikipedia.org/wiki/HWB_color_model + + See also: + + * [`color.red()`](#red) for getting a color's red channel. + * [`color.green()`](#green) for getting a color's green channel. + * [`color.hue()`](#hue) for getting a color's hue. + * [`color.saturation()`](#saturation) for getting a color's saturation. + * [`color.lightness()`](#lightness) for getting a color's lightness. + * [`color.blackness()`](#blackness) for getting a color's blackness. + * [`color.alpha()`](#alpha) for getting a color's alpha channel. + {% endmarkdown %} + + {% codeExample 'color-whiteness', false %} + @debug color.whiteness(#e1d7d2); // 82.3529411765% + @debug color.whiteness(white); // 100% + @debug color.whiteness(black); // 0% + === + @debug color.whiteness(#e1d7d2) // 82.3529411765% + @debug color.whiteness(white) // 100% + @debug color.whiteness(black) // 0% + {% endcodeExample %} +{% endfunction %} diff --git a/source/documentation/modules/index.liquid b/source/documentation/modules/index.liquid new file mode 100644 index 000000000..47c3e74bb --- /dev/null +++ b/source/documentation/modules/index.liquid @@ -0,0 +1,272 @@ +--- +title: Built-In Modules +eleventyComputed: + before_introduction: > + {% render 'doc_snippets/built-in-module-status' %} +introduction: > + Sass provides many built-in modules which contain useful functions (and the + occasional mixin). These modules can be loaded with the [`@use` + rule](/documentation/at-rules/use) like any user-defined stylesheet, and their + functions can be called [like any other module + member](/documentation/at-rules/use#loading-members). All built-in module URLs + begin with `sass:` to indicate that they're part of Sass itself. +--- + +{% headsUp %} + Before the Sass module system was introduced, all Sass functions were globally + available at all times. Many functions still have global aliases (these are + listed in their documentation). The Sass team discourages their use and will + eventually deprecate them, but for now they remain available for compatibility + with older Sass versions and with LibSass (which doesn't support the module + system yet). + + [A few functions][] are *only* available globally even in the new module + system, either because they have special evaluation behavior ([`if()`][]) or + because they add extra behavior on top of built-in CSS functions ([`rgb()`][] + and [`hsl()`][]). These will not be deprecated and can be used freely. + + [a few functions]: #global-functions + [`if()`]: #if + [`rgb()`]: #rgb + [`hsl()`]: #hsl +{% endheadsUp %} + +{% codeExample 'modules' %} + @use "sass:color"; + + .button { + $primary-color: #6b717f; + color: $primary-color; + border: 1px solid color.scale($primary-color, $lightness: 20%); + } + === + @use "sass:color" + + .button + $primary-color: #6b717f + color: $primary-color + border: 1px solid color.scale($primary-color, $lightness: 20%) + === + .button { + color: #6b717f; + border: 1px solid #878d9a; + } +{% endcodeExample %} + +{% markdown %} + Sass provides the following built-in modules: + + * The [`sass:math` module][] provides functions that operate on [numbers][]. + + * The [`sass:string` module][] makes it easy to combine, search, or split + apart [strings][]. + + * The [`sass:color` module][] generates new [colors][] based on existing ones, + making it easy to build color themes. + + * The [`sass:list` module][] lets you access and modify values in [lists][]. + + * The [`sass:map` module][] makes it possible to look up the value associated + with a key in a [map][], and much more. + + * The [`sass:selector` module][] provides access to Sass's powerful selector + engine. + + * The [`sass:meta` module][] exposes the details of Sass's inner workings. + + [`sass:math` module]: /documentation/modules/math + [numbers]: /documentation/values/numbers + [`sass:string` module]: /documentation/modules/string + [strings]: /documentation/values/strings + [`sass:color` module]: /documentation/modules/color + [colors]: /documentation/values/colors + [`sass:list` module]: /documentation/modules/list + [lists]: /documentation/values/lists + [`sass:map` module]: /documentation/modules/map + [map]: /documentation/values/maps + [`sass:selector` module]: /documentation/modules/selector + [`sass:meta` module]: /documentation/modules/meta + + ## Global Functions +{% endmarkdown %} + +{% function 'hsl($hue $saturation $lightness)', 'hsl($hue $saturation $lightness / $alpha)', 'hsl($hue, $saturation, $lightness, $alpha: 1)', 'hsla($hue $saturation $lightness)', 'hsla($hue $saturation $lightness / $alpha)', 'hsla($hue, $saturation, $lightness, $alpha: 1)', 'returns:color' %} + {% compatibility 'dart: "1.15.0"', 'libsass: false', 'ruby: false', 'feature: "Level 4 Syntax"' %} + LibSass and Ruby Sass only support the following signatures: + + * `hsl($hue, $saturation, $lightness)` + * `hsla($hue, $saturation, $lightness, $alpha)` + + Note that for these implementations, the `$alpha` argument is *required* if + the function name `hsla()` is used, and *forbidden* if the function name + `hsl()` is used. + {% endcompatibility %} + + {% compatibility 'dart: true', 'libsass: false', 'ruby: "3.7.0"', 'feature: "Percent Alpha"' %} + LibSass and older versions of Ruby Sass don't support alpha values specified + as percentages. + {% endcompatibility %} + + {% markdown %} + Returns a color with the given [hue, saturation, and lightness][] and the + given alpha channel. + + [hue, saturation, and lightness]: https://en.wikipedia.org/wiki/HSL_and_HSV + + The hue is a number between `0deg` and `360deg` (inclusive) and may be + unitless. The saturation and lightness are numbers between `0%` and `100%` + (inclusive) and may *not* be unitless. The alpha channel can be specified as + either a unitless number between 0 and 1 (inclusive), or a percentage + between `0%` and `100%` (inclusive). + {% endmarkdown %} + + {% funFact false %} + {% markdown %} + You can pass [special functions][] like `calc()` or `var()` in place of + any argument to `hsl()`. You can even use `var()` in place of multiple + arguments, since it might be replaced by multiple values! When a color + function is called this way, it returns an unquoted string using the same + signature it was called with. + + [special functions]: /documentation/syntax/special-functions + {% endmarkdown %} + + {% codeExample 'hsl-special', false %} + @debug hsl(210deg 100% 20% / var(--opacity)); // hsl(210deg 100% 20% / var(--opacity)) + @debug hsla(var(--peach), 20%); // hsla(var(--peach), 20%) + === + @debug hsl(210deg 100% 20% / var(--opacity)) // hsl(210deg 100% 20% / var(--opacity)) + @debug hsla(var(--peach), 20%) // hsla(var(--peach), 20%) + {% endcodeExample %} + {% endfunFact %} + + {% headsUp %} + Sass's [special parsing rules][] for slash-separated values make it + difficult to pass variables for `$lightness` or `$alpha` when using the + `hsl($hue $saturation $lightness / $alpha)` signature. Consider using + `hsl($hue, $saturation, $lightness, $alpha)` instead. + + [special parsing rules]: /documentation/operators/numeric#slash-separated-values + {% endheadsUp %} + + {% codeExample 'hsl', false %} + @debug hsl(210deg 100% 20%); // #036 + @debug hsl(34, 35%, 92%); // #f2ece4 + @debug hsl(210deg 100% 20% / 50%); // rgba(0, 51, 102, 0.5) + @debug hsla(34, 35%, 92%, 0.2); // rgba(242, 236, 228, 0.2) + === + @debug hsl(210deg 100% 20%) // #036 + @debug hsl(34, 35%, 92%) // #f2ece4 + @debug hsl(210deg 100% 20% / 50%) // rgba(0, 51, 102, 0.5) + @debug hsla(34, 35%, 92%, 0.2) // rgba(242, 236, 228, 0.2) + {% endcodeExample %} +{% endfunction %} + +{% function 'if($condition, $if-true, $if-false)' %} + {% markdown %} + Returns `$if-true` if `$condition` is [truthy][], and `$if-false` otherwise. + + This function is special in that it doesn't even evaluate the argument that + isn't returned, so it's safe to call even if the unused argument would throw + an error. + + [truthy]: /documentation/at-rules/control/if#truthiness-and-falsiness + {% endmarkdown %} + + {% codeExample 'debug', false %} + @debug if(true, 10px, 15px); // 10px + @debug if(false, 10px, 15px); // 15px + @debug if(variable-defined($var), $var, null); // null + === + @debug if(true, 10px, 15px) // 10px + @debug if(false, 10px, 15px) // 15px + @debug if(variable-defined($var), $var, null) // null + {% endcodeExample %} +{% endfunction %} + +{% function 'rgb($red $green $blue)', 'rgb($red $green $blue / $alpha)', 'rgb($red, $green, $blue, $alpha: 1)', 'rgb($color, $alpha)', 'rgba($red $green $blue)', 'rgba($red $green $blue / $alpha)', 'rgba($red, $green, $blue, $alpha: 1)', 'rgba($color, $alpha)', 'returns:color' %} + {% compatibility 'dart: "1.15.0"', 'libsass: false', 'ruby: false', 'feature: "Level 4 Syntax"' %} + LibSass and Ruby Sass only support the following signatures: + + * `rgb($red, $green, $blue)` + * `rgba($red, $green, $blue, $alpha)` + * `rgba($color, $alpha)` + + Note that for these implementations, the `$alpha` argument is *required* if + the function name `rgba()` is used, and *forbidden* if the function name + `rgb()` is used. + {% endcompatibility %} + + {% compatibility 'dart: true', 'libsass: false', 'ruby: "3.7.0"', 'feature: "Percent Alpha"' %} + LibSass and older versions of Ruby Sass don't support alpha values specified + as percentages. + {% endcompatibility %} + + {% markdown %} + If `$red`, `$green`, `$blue`, and optionally `$alpha` are passed, returns a + color with the given red, green, blue, and alpha channels. + + Each channel can be specified as either a [unitless][] number between 0 and + 255 (inclusive), or a percentage between `0%` and `100%` (inclusive). The + alpha channel can be specified as either a unitless number between 0 and 1 + (inclusive), or a percentage between `0%` and `100%` (inclusive). + + [unitless]: /documentation/values/numbers#units + {% endmarkdown %} + + {% funFact false %} + {% markdown %} + You can pass [special functions][] like `calc()` or `var()` in place of + any argument to `rgb()`. You can even use `var()` in place of multiple + arguments, since it might be replaced by multiple values! When a color + function is called this way, it returns an unquoted string using the same + signature it was called with. + + [special functions]: /documentation/syntax/special-functions + {% endmarkdown %} + + {% codeExample 'rgb-special', false %} + @debug rgb(0 51 102 / var(--opacity)); // rgb(0 51 102 / var(--opacity)) + @debug rgba(var(--peach), 0.2); // rgba(var(--peach), 0.2) + === + @debug rgb(0 51 102 / var(--opacity)) // rgb(0 51 102 / var(--opacity)) + @debug rgba(var(--peach), 0.2) // rgba(var(--peach), 0.2) + {% endcodeExample %} + {% endfunFact %} + + {% headsUp %} + Sass's [special parsing rules][] for slash-separated values make it + difficult to pass variables for `$blue` or `$alpha` when using the + `rgb($red $green $blue / $alpha)` signature. Consider using + `rgb($red, $green, $blue, $alpha)` instead. + + [special parsing rules]: /documentation/operators/numeric#slash-separated-values + {% endheadsUp %} + + {% codeExample 'rgb', false %} + @debug rgb(0 51 102); // #036 + @debug rgb(95%, 92.5%, 89.5%); // #f2ece4 + @debug rgb(0 51 102 / 50%); // rgba(0, 51, 102, 0.5) + @debug rgba(95%, 92.5%, 89.5%, 0.2); // rgba(242, 236, 228, 0.2) + === + @debug rgb(0 51 102) // #036 + @debug rgb(95%, 92.5%, 89.5%) // #f2ece4 + @debug rgb(0 51 102 / 50%) // rgba(0, 51, 102, 0.5) + @debug rgba(95%, 92.5%, 89.5%, 0.2) // rgba(242, 236, 228, 0.2) + {% endcodeExample %} + + {{ '---' | markdown }} + + {% markdown %} + If `$color` and `$alpha` are passed, this returns `$color` with the given + `$alpha` channel instead of its original alpha channel. + {% endmarkdown %} + + {% codeExample 'color-and-alpha', false %} + @debug rgb(#f2ece4, 50%); // rgba(242, 236, 228, 0.5); + @debug rgba(rgba(0, 51, 102, 0.5), 1); // #003366 + === + @debug rgb(#f2ece4, 50%) // rgba(242, 236, 228, 0.5) + @debug rgba(rgba(0, 51, 102, 0.5), 1) // #003366 + {% endcodeExample %} +{% endfunction %} diff --git a/source/documentation/modules/list.liquid b/source/documentation/modules/list.liquid new file mode 100644 index 000000000..3efaaf993 --- /dev/null +++ b/source/documentation/modules/list.liquid @@ -0,0 +1,242 @@ +--- +title: sass:list +--- + +{% render 'doc_snippets/built-in-module-status' %} + +{% funFact %} + In Sass, every [map][] counts as a list that contains a two-element list for + each key/value pair. For example, `(1: 2, 3: 4)` counts as `(1 2, 3 4)`. So + all these functions work for maps as well! + + [map]: /documentation/values/maps + + Individual values also count as lists. All these functions treat `1px` as a + list that contains the value `1px`. +{% endfunFact %} + +{% function 'list.append($list, $val, $separator: auto)', 'append($list, $val, $separator: auto)', 'returns:list' %} + {% markdown %} + Returns a copy of `$list` with `$val` added to the end. + + If `$separator` is `comma`, `space`, or `slash`, the returned list is + comma-separated, space-separated, or slash-separated, respectively. If it's + `auto` (the default), the returned list will use the same separator as + `$list` (or `space` if `$list` doesn't have a separator). Other values + aren't allowed. + + [separator]: /documentation/values/lists + + Note that unlike [`list.join()`](#join), if `$val` is a list it's nested + within the returned list rather than having all its elements added to the + returned list. + {% endmarkdown %} + + {% codeExample 'list-append', false %} + @debug list.append(10px 20px, 30px); // 10px 20px 30px + @debug list.append((blue, red), green); // blue, red, green + @debug list.append(10px 20px, 30px 40px); // 10px 20px (30px 40px) + @debug list.append(10px, 20px, $separator: comma); // 10px, 20px + @debug list.append((blue, red), green, $separator: space); // blue red green + === + @debug list.append(10px 20px, 30px) // 10px 20px 30px + @debug list.append((blue, red), green) // blue, red, green + @debug list.append(10px 20px, 30px 40px) // 10px 20px (30px 40px) + @debug list.append(10px, 20px, $separator: comma) // 10px, 20px + @debug list.append((blue, red), green, $separator: space) // blue red green + {% endcodeExample %} +{% endfunction %} + +{% function 'list.index($list, $value)', 'index($list, $value)', 'returns:number | null' %} + {% markdown %} + Returns the [index][] of `$value` in `$list`. + + [index]: /documentation/values/lists#indexes + + If `$value` doesn't appear in `$list`, this returns [`null`][]. If `$value` + appears multiple times in `$list`, this returns the index of its first + appearance. + + [`null`]: /documentation/values/null + {% endmarkdown %} + + {% render 'code_snippets/example-list-index' %} +{% endfunction %} + +{% function 'list.is-bracketed($list)', 'is-bracketed($list)', 'returns:boolean' %} + {% markdown %} + Returns whether `$list` has square brackets. + {% endmarkdown %} + + {% codeExample 'list-is-bracketed', false %} + @debug list.is-bracketed(1px 2px 3px); // false + @debug list.is-bracketed([1px, 2px, 3px]); // true + === + @debug list.is-bracketed(1px 2px 3px) // false + @debug list.is-bracketed([1px, 2px, 3px]) // true + {% endcodeExample %} +{% endfunction %} + +{% function 'list.join($list1, $list2, $separator: auto, $bracketed: auto)', 'join($list1, $list2, $separator: auto, $bracketed: auto)', 'returns:list' %} + {% markdown %} + Returns a new list containing the elements of `$list1` followed by the + elements of `$list2`. + {% endmarkdown %} + + {% headsUp %} + Because individual values count as single-element lists, it's possible to + use `list.join()` to add a value to the end of a list. However, *this is not + recommended*, since if that value is a list it will be concatenated, which + is probably not what you're expecting. + + Use [`list.append()`](#append) instead to add a single value to a list. Only + use `list.join()` to combine two lists together into one. + {% endheadsUp %} + + {% markdown %} + If `$separator` is `comma`, `space`, or `slash`, the returned list is + comma-separated, space-separated, or slash-separated, respectively. If it's + `auto` (the default), the returned list will use the same separator as + `$list1` if it has a separator, or else `$list2` if it has a separator, or + else `space`. Other values aren't allowed. + + If `$bracketed` is `auto` (the default), the returned list will be bracketed + if `$list1` is. Otherwise, the returned list will have square brackets if + `$bracketed` is [truthy] and no brackets if `$bracketed` is falsey. + + [truthy]: /documentation/values/booleans#truthiness-and-falsiness + {% endmarkdown %} + + {% codeExample 'list-join', false %} + @debug list.join(10px 20px, 30px 40px); // 10px 20px 30px 40px + @debug list.join((blue, red), (#abc, #def)); // blue, red, #abc, #def + @debug list.join(10px, 20px); // 10px 20px + @debug list.join(10px, 20px, $separator: comma); // 10px, 20px + @debug list.join((blue, red), (#abc, #def), $separator: space); // blue red #abc #def + @debug list.join([10px], 20px); // [10px 20px] + @debug list.join(10px, 20px, $bracketed: true); // [10px 20px] + === + @debug list.join(10px 20px, 30px 40px) // 10px 20px 30px 40px + @debug list.join((blue, red), (#abc, #def)) // blue, red, #abc, #def + @debug list.join(10px, 20px) // 10px 20px + @debug list.join(10px, 20px, comma) // 10px, 20px + @debug list.join((blue, red), (#abc, #def), space) // blue red #abc #def + @debug list.join([10px], 20px) // [10px 20px] + @debug list.join(10px, 20px, $bracketed: true) // [10px 20px] + {% endcodeExample %} +{% endfunction %} + +{% function 'list.length($list)', 'length($list)', 'returns:number' %} + {% markdown %} + Returns the length of `$list`. + + This can also return the number of pairs in a map. + {% endmarkdown %} + + {% codeExample 'list-length', false %} + @debug list.length(10px); // 1 + @debug list.length(10px 20px 30px); // 3 + @debug list.length((width: 10px, height: 20px)); // 2 + === + @debug list.length(10px) // 1 + @debug list.length(10px 20px 30px) // 3 + @debug list.length((width: 10px, height: 20px)) // 2 + {% endcodeExample %} +{% endfunction %} + +{% function 'list.separator($list)', 'list-separator($list)', 'returns:unquoted string' %} + {% markdown %} + Returns the name of the separator used by `$list`, either `space`, `comma`, + or `slash`. + + If `$list` doesn't have a separator, returns `space`. + {% endmarkdown %} + + {% codeExample 'list-separator', false %} + @debug list.separator(1px 2px 3px); // space + @debug list.separator(1px, 2px, 3px); // comma + @debug list.separator('Helvetica'); // space + @debug list.separator(()); // space + === + @debug list.separator(1px 2px 3px) // space + @debug list.separator(1px, 2px, 3px) // comma + @debug list.separator('Helvetica') // space + @debug list.separator(()) // space + {% endcodeExample %} +{% endfunction %} + +{% function 'list.nth($list, $n)', 'nth($list, $n)' %} + {% markdown %} + Returns the element of `$list` at [index][] `$n`. + + [index]: /documentation/values/lists#indexes + + If `$n` is negative, it counts from the end of `$list`. Throws an error if + there is no element at index `$n`. + {% endmarkdown %} + + {% render 'code_snippets/example-list-nth' %} +{% endfunction %} + +{% function 'list.set-nth($list, $n, $value)', 'set-nth($list, $n, $value)', 'returns:list' %} + {% markdown %} + Returns a copy of `$list` with the element at [index][] `$n` replaced with + `$value`. + + [index]: /documentation/values/lists#indexes + + If `$n` is negative, it counts from the end of `$list`. Throws an error if + there is no existing element at index `$n`. + {% endmarkdown %} + + {% codeExample 'list-set-nth', false %} + @debug list.set-nth(10px 20px 30px, 1, 2em); // 2em 20px 30px + @debug list.set-nth(10px 20px 30px, -1, 8em); // 10px, 20px, 8em + @debug list.set-nth((Helvetica, Arial, sans-serif), 3, Roboto); // Helvetica, Arial, Roboto + === + @debug list.set-nth(10px 20px 30px, 1, 2em); // 2em 20px 30px + @debug list.set-nth(10px 20px 30px, -1, 8em); // 10px, 20px, 8em + @debug list.set-nth((Helvetica, Arial, sans-serif), 3, Roboto); // Helvetica, Arial, Roboto + {% endcodeExample %} +{% endfunction %} + +{% function 'list.slash($elements...)', 'returns:list' %} + {% markdown %} + Returns a slash-separated list that contains `$elements`. + {% endmarkdown %} + + {% headsUp %} + This function is a temporary solution for creating slash-separated lists. + Eventually, they'll be written literally with slashes, as in + `1px / 2px / solid`, but for the time being [slashes are used for division] + so Sass can't use them for new syntax until the old syntax is removed. + + [slashes are used for division]: /documentation/breaking-changes/slash-div + {% endheadsUp %} + + {% codeExample 'list-slash', false %} + @debug list.slash(1px, 50px, 100px); // 1px / 50px / 100px + === + @debug list.slash(1px, 50px, 100px) // 1px / 50px / 100px + {% endcodeExample %} +{% endfunction %} + +{% function 'list.zip($lists...)', 'zip($lists...)', 'returns:list' %} + {% markdown %} + Combines every list in `$lists` into a single list of sub-lists. + + Each element in the returned list contains all the elements at that position + in `$lists`. The returned list is as long as the shortest list in `$lists`. + + The returned list is always comma-separated and the sub-lists are always + space-separated. + {% endmarkdown %} + + {% codeExample 'list-zip', false %} + @debug list.zip(10px 50px 100px, short mid long); // 10px short, 50px mid, 100px long + @debug list.zip(10px 50px 100px, short mid); // 10px short, 50px mid + === + @debug list.zip(10px 50px 100px, short mid long) // 10px short, 50px mid, 100px long + @debug list.zip(10px 50px 100px, short mid) // 10px short, 50px mid + {% endcodeExample %} +{% endfunction %} diff --git a/source/documentation/modules/map.liquid b/source/documentation/modules/map.liquid new file mode 100644 index 000000000..bc1f99040 --- /dev/null +++ b/source/documentation/modules/map.liquid @@ -0,0 +1,484 @@ +--- +title: sass:map +--- + +{% render 'doc_snippets/built-in-module-status' %} + +{% funFact false %} + {% markdown %} + Sass libraries and design systems tend to share and override configurations + that are represented as nested maps (maps that contain maps that contain + maps). + + To help you work with nested maps, some map functions support deep + operations. For example, if you pass multiple keys to `map.get()`, it will + follow those keys to find the desired nested map: + {% endmarkdown %} + + {% codeExample 'map', false %} + $config: (a: (b: (c: d))); + @debug map.get($config, a, b, c); // d + === + $config: (a: (b: (c: d))) + @debug map.get($config, a, b, c) // d + {% endcodeExample %} +{% endfunFact %} + +{% function 'map.deep-merge($map1, $map2)', 'returns:map' %} + {% compatibility 'dart: "1.27.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + Identical to [`map.merge()`](#merge), except that nested map values are + *also* recursively merged. + {% endmarkdown %} + + {% codeExample 'map-deep-merge', false %} + $helvetica-light: ( + "weights": ( + "lightest": 100, + "light": 300 + ) + ); + $helvetica-heavy: ( + "weights": ( + "medium": 500, + "bold": 700 + ) + ); + + @debug map.deep-merge($helvetica-light, $helvetica-heavy); + // ( + // "weights": ( + // "lightest": 100, + // "light": 300, + // "medium": 500, + // "bold": 700 + // ) + // ) + @debug map.merge($helvetica-light, $helvetica-heavy); + // ( + // "weights": ( + // "medium: 500, + // "bold": 700 + // ) + // ) + === + $helvetica-light: ("weights": ("lightest": 100, "light": 300)) + $helvetica-heavy: ("weights": ("medium": 500, "bold": 700)) + + @debug map.deep-merge($helvetica-light, $helvetica-heavy) + // ( + // "weights": ( + // "lightest": 100, + // "light": 300, + // "medium": 500, + // "bold": 700 + // ) + // ) + @debug map.merge($helvetica-light, $helvetica-heavy); + // ( + // "weights": ( + // "medium: 500, + // "bold": 700 + // ) + // ) + {% endcodeExample %} +{% endfunction %} + +{% function 'map.deep-remove($map, $key, $keys...)', 'returns:map' %} + {% compatibility 'dart: "1.27.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + If `$keys` is empty, returns a copy of `$map` without a value associated + with `$key`. + {% endmarkdown %} + + {% codeExample 'map-deep-remove', false %} + $font-weights: ("regular": 400, "medium": 500, "bold": 700); + + @debug map.deep-remove($font-weights, "regular"); + // ("medium": 500, "bold": 700) + === + $font-weights: ("regular": 400, "medium": 500, "bold": 700) + + @debug map.deep-remove($font-weights, "regular") + // ("medium": 500, "bold": 700) + {% endcodeExample %} + + {{ '---' | markdown }} + + {% markdown %} + If `$keys` is not empty, follows the set of keys including `$key` and + excluding the last key in `$keys`, from left to right, to find the nested + map targeted for updating. + + Returns a copy of `$map` where the targeted map does not have a value + associated with the last key in `$keys`. + {% endmarkdown %} + + {% codeExample 'map-deep-remove-2', false %} + $fonts: ( + "Helvetica": ( + "weights": ( + "regular": 400, + "medium": 500, + "bold": 700 + ) + ) + ); + + @debug map.deep-remove($fonts, "Helvetica", "weights", "regular"); + // ( + // "Helvetica": ( + // "weights: ( + // "medium": 500, + // "bold": 700 + // ) + // ) + // ) + === + $fonts: ("Helvetica": ("weights": ("regular": 400, "medium": 500, "bold": 700))) + + @debug map.deep-remove($fonts, "Helvetica", "weights", "regular") + // ( + // "Helvetica": ( + // "weights: ( + // "medium": 500, + // "bold": 700 + // ) + // ) + // ) + {% endcodeExample %} +{% endfunction %} + +{% function 'map.get($map, $key, $keys...)', 'map-get($map, $key, $keys...)' %} + {% markdown %} + If `$keys` is empty, returns the value in `$map` associated with `$key`. + + If `$map` doesn't have a value associated with `$key`, returns [`null`][]. + + [`null`]: /documentation/values/null + {% endmarkdown %} + + {% render 'code_snippets/example-map-get' %} + + {{ '---' | markdown }} + + {% compatibility 'dart: "1.27.0"', 'libsass: false', 'ruby: false' %} + Only Dart Sass supports calling `map.get()` with more than two arguments. + {% endcompatibility %} + + {% markdown %} + If `$keys` is not empty, follows the set of keys including `$key` and + excluding the last key in `$keys`, from left to right, to find the nested + map targeted for searching. + + Returns the value in the targeted map associated with the last key in + `$keys`. + + Returns [`null`][] if the map does not have a value associated with the key, + or if any key in `$keys` is missing from a map or references a value that is + not a map. + + [`null`]: /documentation/values/null + {% endmarkdown %} + + {% codeExample 'map-deep-remove-2', false %} + $fonts: ( + "Helvetica": ( + "weights": ( + "regular": 400, + "medium": 500, + "bold": 700 + ) + ) + ); + + @debug map.get($fonts, "Helvetica", "weights", "regular"); // 400 + @debug map.get($fonts, "Helvetica", "colors"); // null + === + $fonts: ("Helvetica": ("weights": ("regular": 400, "medium": 500, "bold": 700))) + + @debug map.get($fonts, "Helvetica", "weights", "regular") // 400 + @debug map.get($fonts, "Helvetica", "colors") // null + {% endcodeExample %} +{% endfunction %} + +{% function 'map.has-key($map, $key, $keys...)', 'map-has-key($map, $key, $keys...)', 'returns:boolean' %} + {% markdown %} + If `$keys` is empty, returns whether `$map` contains a value associated with + `$key`. + {% endmarkdown %} + + {% codeExample 'map-has-key', false %} + $font-weights: ("regular": 400, "medium": 500, "bold": 700); + + @debug map.has-key($font-weights, "regular"); // true + @debug map.has-key($font-weights, "bolder"); // false + === + $font-weights: ("regular": 400, "medium": 500, "bold": 700) + + @debug map.has-key($font-weights, "regular") // true + @debug map.has-key($font-weights, "bolder") // false + {% endcodeExample %} + + {{ '---' | markdown }} + + {% compatibility 'dart: "1.27.0"', 'libsass: false', 'ruby: false' %} + Only Dart Sass supports calling `map.has-key()` with more than two + arguments. + {% endcompatibility %} + + {% markdown %} + If `$keys` is not empty, follows the set of keys including `$key` and + excluding the last key in `$keys`, from left to right, to find the nested + map targeted for searching. + + Returns true if the targeted map contains a value associated with the last + key in `$keys`. + + Returns false if it does not, or if any key in `$keys` is missing from a map + or references a value that is not a map. + {% endmarkdown %} + + {% codeExample 'map-has-key-2', false %} + $fonts: ( + "Helvetica": ( + "weights": ( + "regular": 400, + "medium": 500, + "bold": 700 + ) + ) + ); + + @debug map.has-key($fonts, "Helvetica", "weights", "regular"); // true + @debug map.has-key($fonts, "Helvetica", "colors"); // false + === + $fonts: ("Helvetica": ("weights": ("regular": 400, "medium": 500, "bold": 700))) + + @debug map.has-key($fonts, "Helvetica", "weights", "regular") // true + @debug map.has-key($fonts, "Helvetica", "colors") // false + {% endcodeExample %} +{% endfunction %} + +{% function 'map.keys($map)', 'map-keys($map)', 'returns:list' %} + {% markdown %} + Returns a comma-separated list of all the keys in `$map`. + {% endmarkdown %} + + {% codeExample 'map-keys', false %} + $font-weights: ("regular": 400, "medium": 500, "bold": 700); + + @debug map.keys($font-weights); // "regular", "medium", "bold" + === + $font-weights: ("regular": 400, "medium": 500, "bold": 700) + + @debug map.keys($font-weights) // "regular", "medium", "bold" + {% endcodeExample %} +{% endfunction %} + +{% function 'map.merge($map1, $map2)', 'map-merge($map1, $map2)', 'map.merge($map1, $keys..., $map2)', 'map-merge($map1, $keys..., $map2)', 'returns:map' %} + {% headsUp %} + In practice, the actual arguments to `map.merge($map1, $keys..., $map2)` are + passed as `map.merge($map1, $args...)`. They are described here as `$map1, + $keys..., $map2` for explanation purposes only. + {% endheadsUp %} + + {% markdown %} + If no `$keys` are passed, returns a new map with all the keys and values + from both `$map1` and `$map2`. + + If both `$map1` and `$map2` have the same key, `$map2`'s value takes + precedence. + + All keys in the returned map that also appear in `$map1` have the same order + as in `$map1`. New keys from `$map2` appear at the end of the map. + {% endmarkdown %} + + {% codeExample 'map-merge', false %} + $light-weights: ("lightest": 100, "light": 300); + $heavy-weights: ("medium": 500, "bold": 700); + + @debug map.merge($light-weights, $heavy-weights); + // ("lightest": 100, "light": 300, "medium": 500, "bold": 700) + === + $light-weights: ("lightest": 100, "light": 300) + $heavy-weights: ("medium": 500, "bold": 700) + + @debug map.merge($light-weights, $heavy-weights) + // ("lightest": 100, "light": 300, "medium": 500, "bold": 700) + {% endcodeExample %} + + {{ '---' | markdown }} + + {% compatibility 'dart: "1.27.0"', 'libsass: false', 'ruby: false' %} + Only Dart Sass supports calling `map.merge()` with more than two arguments. + {% endcompatibility %} + + {% markdown %} + If `$keys` is not empty, follows the `$keys` to find the nested map targeted + for merging. If any key in `$keys` is missing from a map or references a + value that is not a map, sets the value at that key to an empty map. + + Returns a copy of `$map1` where the targeted map is replaced by a new map + that contains all the keys and values from both the targeted map and + `$map2`. + {% endmarkdown %} + + {% codeExample 'map-merge-2', false %} + $fonts: ( + "Helvetica": ( + "weights": ( + "lightest": 100, + "light": 300 + ) + ) + ); + $heavy-weights: ("medium": 500, "bold": 700); + + @debug map.merge($fonts, "Helvetica", "weights", $heavy-weights); + // ( + // "Helvetica": ( + // "weights": ( + // "lightest": 100, + // "light": 300, + // "medium": 500, + // "bold": 700 + // ) + // ) + // ) + === + $fonts: ("Helvetica": ("weights": ("lightest": 100, "light": 300))) + $heavy-weights: ("medium": 500, "bold": 700) + + @debug map.merge($fonts, "Helvetica", "weights", $heavy-weights) + // ( + // "Helvetica": ( + // "weights": ( + // "lightest": 100, + // "light": 300, + // "medium": 500, + // "bold": 700 + // ) + // ) + // ) + {% endcodeExample %} +{% endfunction %} + +{% function 'map.remove($map, $keys...)', 'map-remove($map, $keys...)', 'returns:map' %} + {% markdown %} + Returns a copy of `$map` without any values associated with `$keys`. + + If a key in `$keys` doesn't have an associated value in `$map`, it's + ignored. + {% endmarkdown %} + + {% codeExample 'map-remove', false %} + $font-weights: ("regular": 400, "medium": 500, "bold": 700); + + @debug map.remove($font-weights, "regular"); // ("medium": 500, "bold": 700) + @debug map.remove($font-weights, "regular", "bold"); // ("medium": 500) + @debug map.remove($font-weights, "bolder"); + // ("regular": 400, "medium": 500, "bold": 700) + === + $font-weights: ("regular": 400, "medium": 500, "bold": 700) + + @debug map.remove($font-weights, "regular") // ("medium": 500, "bold": 700) + @debug map.remove($font-weights, "regular", "bold") // ("medium": 500) + @debug map.remove($font-weights, "bolder") + // ("regular": 400, "medium": 500, "bold": 700) + {% endcodeExample %} +{% endfunction %} + +{% function 'map.set($map, $key, $value)', 'map.set($map, $keys..., $key, $value)', 'returns:map' %} + {% headsUp %} + In practice, the actual arguments to `map.set($map, $keys..., $key, $value)` + are passed as `map.set($map, $args...)`. They are described here as `$map, + $keys..., $key, $value` for explanation purposes only. + {% endheadsUp %} + + {% markdown %} + If `$keys` are not passed, returns a copy of `$map` with the value at `$key` + set to `$value`. + {% endmarkdown %} + + {% codeExample 'map-set', false %} + $font-weights: ("regular": 400, "medium": 500, "bold": 700); + + @debug map.set($font-weights, "regular", 300); + // ("regular": 300, "medium": 500, "bold": 700) + === + $font-weights: ("regular": 400, "medium": 500, "bold": 700) + + @debug map.set($font-weights, "regular", 300) + // ("regular": 300, "medium": 500, "bold": 700) + {% endcodeExample %} + + {{ '---' | markdown }} + + {% compatibility 'dart: "1.27.0"', 'libsass: false', 'ruby: false' %} + Only Dart Sass supports calling `map.set()` with more than three arguments. + {% endcompatibility %} + + {% markdown %} + If `$keys` are passed, follows the `$keys` to find the nested map targeted + for updating. If any key in `$keys` is missing from a map or references a + value that is not a map, sets the value at that key to an empty map. + + Returns a copy of `$map` with the targeted map's value at `$key` set to + `$value`. + {% endmarkdown %} + + {% codeExample 'map-set-2', false %} + $fonts: ( + "Helvetica": ( + "weights": ( + "regular": 400, + "medium": 500, + "bold": 700 + ) + ) + ); + + @debug map.set($fonts, "Helvetica", "weights", "regular", 300); + // ( + // "Helvetica": ( + // "weights": ( + // "regular": 300, + // "medium": 500, + // "bold": 700 + // ) + // ) + // ) + === + $fonts: ("Helvetica": ("weights": ("regular": 400, "medium": 500, "bold": 700))) + + @debug map.set($fonts, "Helvetica", "weights", "regular", 300) + // ( + // "Helvetica": ( + // "weights": ( + // "regular": 300, + // "medium": 500, + // "bold": 700 + // ) + // ) + // ) + {% endcodeExample %} +{% endfunction %} + +{% function 'map.values($map)', 'map-values($map)', 'returns:list' %} + {% markdown %} + Returns a comma-separated list of all the values in `$map`. + {% endmarkdown %} + + {% codeExample 'map-values', false %} + $font-weights: ("regular": 400, "medium": 500, "bold": 700); + + @debug map.values($font-weights); // 400, 500, 700 + === + $font-weights: ("regular": 400, "medium": 500, "bold": 700) + + @debug map.values($font-weights) // 400, 500, 700 + {% endcodeExample %} +{% endfunction %} diff --git a/source/documentation/modules/math.liquid b/source/documentation/modules/math.liquid new file mode 100644 index 000000000..7556ffd7c --- /dev/null +++ b/source/documentation/modules/math.liquid @@ -0,0 +1,658 @@ +--- +title: sass:math +--- + +{% render 'doc_snippets/built-in-module-status' %} + +{{ '## Variables' | markdown }} + +{% function 'math.$e' %} + {% compatibility 'dart: "1.25.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + The closest 64-bit floating point approximation of the [mathematical + constant *e*][]. + + [mathematical constant *e*]: https://en.wikipedia.org/wiki/E_(mathematical_constant) + {% endmarkdown %} + + {% codeExample 'math-e', false %} + @debug math.$e; // 2.7182818285 + === + @debug math.$e // 2.7182818285 + {% endcodeExample %} +{% endfunction %} + +{% function 'math.$epsilon' %} + {% compatibility 'dart: "1.55.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + The difference between 1 and the smallest 64-bit floating point number + greater than 1 according to floating-point comparisons. Because of Sass + numbers' [10 digits of precision](/documentation/values/numbers), in many + cases this will appear to be 0. + {% endmarkdown %} +{% endfunction %} + +{% function 'math.$max-number' %} + {% compatibility 'dart: "1.55.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + The maximum finite number that can be represented as a 64-bit floating point + number. + {% endmarkdown %} + + {% codeExample 'math-max-number', false %} + @debug math.$max-number; // 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + === + @debug math.$max-number // 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + {% endcodeExample %} +{% endfunction %} + +{% function 'math.$max-safe-integer' %} + {% compatibility 'dart: "1.55.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + The maximum integer `n` such that both `n` and `n + 1` can be precisely + represented as a 64-bit floating-point number. + {% endmarkdown %} + + {% codeExample 'math-max-safe-integer', false %} + @debug math.$max-safe-integer; // 9007199254740991 + === + @debug math.$max-safe-integer // 9007199254740991 + {% endcodeExample %} +{% endfunction %} + +{% function 'math.$min-number' %} + {% compatibility 'dart: "1.55.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + The smallest positive number that can be represented as a 64-bit floating + point number. Because of Sass numbers' [10 digits of + precision](/documentation/values/numbers), in many cases this will appear to + be 0. + {% endmarkdown %} +{% endfunction %} + +{% function 'math.$min-safe-integer' %} + {% compatibility 'dart: "1.55.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + The minimum integer `n` such that both `n` and `n - 1` can be precisely + represented as a 64-bit floating-point number. + {% endmarkdown %} + + {% codeExample 'math-min-safe-integer', false %} + @debug math.$min-safe-integer; // -9007199254740991 + === + @debug math.$min-safe-integer // -9007199254740991 + {% endcodeExample %} +{% endfunction %} + +{% function 'math.$pi' %} + {% compatibility 'dart: "1.25.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + The closest 64-bit floating point approximation of the [mathematical + constant *π*][]. + + [mathematical constant *π*]: https://en.wikipedia.org/wiki/Pi + {% endmarkdown %} + + {% codeExample 'math-pi', false %} + @debug math.$pi; // 3.1415926536 + === + @debug math.$pi // 3.1415926536 + {% endcodeExample %} +{% endfunction %} + +{{ '## Bounding Functions' | markdown }} + +{% function 'math.ceil($number)', 'ceil($number)', 'returns:number' %} + {% markdown %} + Rounds `$number` up to the next highest whole number. + {% endmarkdown %} + + {% codeExample 'math-ceil', false %} + @debug math.ceil(4); // 4 + @debug math.ceil(4.2); // 5 + @debug math.ceil(4.9); // 5 + === + @debug math.ceil(4) // 4 + @debug math.ceil(4.2) // 5 + @debug math.ceil(4.9) // 5 + {% endcodeExample %} +{% endfunction %} + +{% function 'math.clamp($min, $number, $max)', 'returns:number' %} + {% compatibility 'dart: "1.25.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + Restricts `$number` to the range between `$min` and `$max`. If `$number` is + less than `$min` this returns `$min`, and if it's greater than `$max` this + returns `$max`. + + `$min`, `$number`, and `$max` must have compatible units, or all be + unitless. + {% endmarkdown %} + + {% codeExample 'math-clamp', false %} + @debug math.clamp(-1, 0, 1); // 0 + @debug math.clamp(1px, -1px, 10px); // 1px + @debug math.clamp(-1in, 1cm, 10mm); // 10mm + === + @debug math.clamp(-1, 0, 1) // 0 + @debug math.clamp(1px, -1px, 10px) // 1px + @debug math.clamp(-1in, 1cm, 10mm) // 10mm + {% endcodeExample %} +{% endfunction %} + +{% function 'math.floor($number)', 'floor($number)', 'returns:number' %} + {% markdown %} + Rounds `$number` down to the next lowest whole number. + {% endmarkdown %} + + {% codeExample 'math-floor', false %} + @debug math.floor(4); // 4 + @debug math.floor(4.2); // 4 + @debug math.floor(4.9); // 4 + === + @debug math.floor(4) // 4 + @debug math.floor(4.2) // 4 + @debug math.floor(4.9) // 4 + {% endcodeExample %} +{% endfunction %} + +{% function 'math.max($number...)', 'max($number...)', 'returns:number' %} + {% markdown %} + Returns the highest of one or more numbers. + {% endmarkdown %} + + {% codeExample 'math-max', false %} + @debug math.max(1px, 4px); // 4px + + $widths: 50px, 30px, 100px; + @debug math.max($widths...); // 100px + === + @debug math.max(1px, 4px) // 4px + + $widths: 50px, 30px, 100px + @debug math.max($widths...) // 100px + {% endcodeExample %} +{% endfunction %} + +{% function 'math.min($number...)', 'min($number...)', 'returns:number' %} + {% markdown %} + Returns the lowest of one or more numbers. + {% endmarkdown %} + + {% codeExample 'math-min', false %} + @debug math.min(1px, 4px); // 1px + + $widths: 50px, 30px, 100px; + @debug math.min($widths...); // 30px + === + @debug math.min(1px, 4px) // 1px + + $widths: 50px, 30px, 100px + @debug math.min($widths...) // 30px + {% endcodeExample %} +{% endfunction %} + +{% function 'math.round($number)', 'round($number)', 'returns:number' %} + {% markdown %} + Rounds `$number` to the nearest whole number. + {% endmarkdown %} + + {% codeExample 'math-round', false %} + @debug math.round(4); // 4 + @debug math.round(4.2); // 4 + @debug math.round(4.9); // 5 + === + @debug math.round(4) // 4 + @debug math.round(4.2) // 4 + @debug math.round(4.9) // 5 + {% endcodeExample %} +{% endfunction %} + +{{ '## Distance Functions' | markdown }} + +{% function 'math.abs($number)', 'abs($number)', 'returns:number' %} + {% markdown %} + Returns the [absolute value][] of `$number`. If `$number` is negative, this + returns `-$number`, and if `$number` is positive, it returns `$number` + as-is. + + [absolute value]: https://en.wikipedia.org/wiki/Absolute_value + {% endmarkdown %} + + {% codeExample 'math-abs', false %} + @debug math.abs(10px); // 10px + @debug math.abs(-10px); // 10px + === + @debug math.abs(10px) // 10px + @debug math.abs(-10px) // 10px + {% endcodeExample %} +{% endfunction %} + +{% function 'math.hypot($number...)', 'returns:number' %} + {% compatibility 'dart: "1.25.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + Returns the length of the *n*-dimensional [vector][] that has components + equal to each `$number`. For example, for three numbers *a*, *b*, and *c*, + this returns the square root of *a² + b² + c²*. + + The numbers must either all have compatible units, or all be unitless. And + since the numbers' units may differ, the output takes the unit of the first + number. + + [vector]: https://en.wikipedia.org/wiki/Euclidean_vector + {% endmarkdown %} + + {% codeExample 'math-hypot', false %} + @debug math.hypot(3, 4); // 5 + + $lengths: 1in, 10cm, 50px; + @debug math.hypot($lengths...); // 4.0952775683in + === + @debug math.hypot(3, 4) // 5 + + $lengths: 1in, 10cm, 50px + @debug math.hypot($lengths...) // 4.0952775683in + {% endcodeExample %} +{% endfunction %} + +{{ '## Exponential Functions' | markdown }} + +{% function 'math.log($number, $base: null)', 'returns:number' %} + {% compatibility 'dart: "1.25.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + Returns the [logarithm][] of `$number` with respect to `$base`. If `$base` + is `null`, the [natural log][] is calculated. + + `$number` and `$base` must be unitless. + + [logarithm]: https://en.wikipedia.org/wiki/Logarithm + [natural log]: https://en.wikipedia.org/wiki/Natural_logarithm + {% endmarkdown %} + + {% codeExample 'math-log', false %} + @debug math.log(10); // 2.302585093 + @debug math.log(10, 10); // 1 + === + @debug math.log(10) // 2.302585093 + @debug math.log(10, 10) // 1 + {% endcodeExample %} +{% endfunction %} + +{% function 'math.pow($base, $exponent)', 'returns:number' %} + {% compatibility 'dart: "1.25.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + Raises `$base` [to the power of][] `$exponent`. + + `$base` and `$exponent` must be unitless. + + [to the power of]: https://en.wikipedia.org/wiki/Exponentiation + {% endmarkdown %} + + {% codeExample 'math-pow', false %} + @debug math.pow(10, 2); // 100 + @debug math.pow(100, math.div(1, 3)); // 4.6415888336 + @debug math.pow(5, -2); // 0.04 + === + @debug math.pow(10, 2) // 100 + @debug math.pow(100, math.div(1, 3)) // 4.6415888336 + @debug math.pow(5, -2) // 0.04 + {% endcodeExample %} +{% endfunction %} + +{% function 'math.sqrt($number)', 'returns:number' %} + {% compatibility 'dart: "1.25.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + Returns the [square root][] of `$number`. + + `$number` must be unitless. + + [square root]: https://en.wikipedia.org/wiki/Square_root + {% endmarkdown %} + + {% codeExample 'math-sqrt', false %} + @debug math.sqrt(100); // 10 + @debug math.sqrt(math.div(1, 3)); // 0.5773502692 + @debug math.sqrt(-1); // NaN + === + @debug math.sqrt(100) // 10 + @debug math.sqrt(math.div(1, 3)) // 0.5773502692 + @debug math.sqrt(-1) // NaN + {% endcodeExample %} +{% endfunction %} + +{{ '## Trigonometric Functions' | markdown }} + +{% function 'math.cos($number)', 'returns:number' %} + {% compatibility 'dart: "1.25.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + Returns the [cosine][] of `$number`. + + `$number` must be an angle (its units must be compatible with `deg`) or + unitless. If `$number` has no units, it is assumed to be in `rad`. + + [cosine]: https://en.wikipedia.org/wiki/Trigonometric_functions#Right-angled_triangle_definitions + {% endmarkdown %} + + {% codeExample 'math-cos', false %} + @debug math.cos(100deg); // -0.1736481777 + @debug math.cos(1rad); // 0.5403023059 + @debug math.cos(1); // 0.5403023059 + === + @debug math.cos(100deg) // -0.1736481777 + @debug math.cos(1rad) // 0.5403023059 + @debug math.cos(1) // 0.5403023059 + {% endcodeExample %} +{% endfunction %} + +{% function 'math.sin($number)', 'returns:number' %} + {% compatibility 'dart: "1.25.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + Returns the [sine][] of `$number`. + + `$number` must be an angle (its units must be compatible with `deg`) or + unitless. If `$number` has no units, it is assumed to be in `rad`. + + [sine]: https://en.wikipedia.org/wiki/Trigonometric_functions#Right-angled_triangle_definitions + {% endmarkdown %} + + {% codeExample 'math-sin', false %} + @debug math.sin(100deg); // 0.984807753 + @debug math.sin(1rad); // 0.8414709848 + @debug math.sin(1); // 0.8414709848 + === + @debug math.sin(100deg) // 0.984807753 + @debug math.sin(1rad) // 0.8414709848 + @debug math.sin(1) // 0.8414709848 + {% endcodeExample %} +{% endfunction %} + +{% function 'math.tan($number)', 'returns:number' %} + {% compatibility 'dart: "1.25.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + Returns the [tangent][] of `$number`. + + `$number` must be an angle (its units must be compatible with `deg`) or + unitless. If `$number` has no units, it is assumed to be in `rad`. + + [tangent]: https://en.wikipedia.org/wiki/Trigonometric_functions#Right-angled_triangle_definitions + {% endmarkdown %} + + {% codeExample 'math-tan', false %} + @debug math.tan(100deg); // -5.6712818196 + @debug math.tan(1rad); // 1.5574077247 + @debug math.tan(1); // 1.5574077247 + === + @debug math.tan(100deg) // -5.6712818196 + @debug math.tan(1rad) // 1.5574077247 + @debug math.tan(1) // 1.5574077247 + {% endcodeExample %} +{% endfunction %} + +{% function 'math.acos($number)', 'returns:number' %} + {% compatibility 'dart: "1.25.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + Returns the [arccosine][] of `$number` in `deg`. + + `$number` must be unitless. + + [arccosine]: https://en.wikipedia.org/wiki/Inverse_trigonometric_functions#Basic_properties + {% endmarkdown %} + + {% codeExample 'math-acos', false %} + @debug math.acos(0.5); // 60deg + @debug math.acos(2); // NaNdeg + === + @debug math.acos(0.5) // 60deg + @debug math.acos(2) // NaNdeg + {% endcodeExample %} +{% endfunction %} + +{% function 'math.asin($number)', 'returns:number' %} + {% compatibility 'dart: "1.25.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + Returns the [arcsine][] of `$number` in `deg`. + + `$number` must be unitless. + + [arcsine]: https://en.wikipedia.org/wiki/Inverse_trigonometric_functions#Basic_properties + {% endmarkdown %} + + {% codeExample 'math-asin', false %} + @debug math.asin(0.5); // 30deg + @debug math.asin(2); // NaNdeg + === + @debug math.asin(0.5) // 30deg + @debug math.asin(2) // NaNdeg + {% endcodeExample %} +{% endfunction %} + +{% function 'math.atan($number)', 'returns:number' %} + {% compatibility 'dart: "1.25.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + Returns the [arctangent][] of `$number` in `deg`. + + `$number` must be unitless. + + [arctangent]: https://en.wikipedia.org/wiki/Inverse_trigonometric_functions#Basic_properties + {% endmarkdown %} + + {% codeExample 'math-atan', false %} + @debug math.atan(10); // 84.2894068625deg + === + @debug math.atan(10) // 84.2894068625deg + {% endcodeExample %} +{% endfunction %} + +{% function 'math.atan2($y, $x)', 'returns:number' %} + {% compatibility 'dart: "1.25.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + Returns the [2-argument arctangent][] of `$y` and `$x` in `deg`. + + `$y` and `$x` must have compatible units or be unitless. + + [2-argument arctangent]: https://en.wikipedia.org/wiki/Atan2 + {% endmarkdown %} + + {% funFact %} + `math.atan2($y, $x)` is distinct from `atan(math.div($y, $x))` because it + preserves the quadrant of the point in question. For example, `math.atan2(1, + -1)` corresponds to the point `(-1, 1)` and returns `135deg`. In contrast, + `math.atan(math.div(1, -1))` and `math.atan(math.div(-1, 1))` resolve first + to `atan(-1)`, so both return `-45deg`. + {% endfunFact %} + + {% codeExample 'math-atan2', false %} + @debug math.atan2(-1, 1); // 135deg + === + @debug math.atan2(-1, 1) // 135deg + {% endcodeExample %} +{% endfunction %} + +{{ '## Unit Functions' | markdown }} + +{% function 'math.compatible($number1, $number2)', 'comparable($number1, $number2)', 'returns:boolean' %} + {% markdown %} + Returns whether `$number1` and `$number2` have compatible units. + + If this returns `true`, `$number1` and `$number2` can safely be [added][], + [subtracted][], and [compared][]. Otherwise, doing so will produce errors. + + [added]: /documentation/operators/numeric + [subtracted]: /documentation/operators/numeric + [compared]: /documentation/operators/relational + {% endmarkdown %} + + {% headsUp %} + The global name of this function is + comparable, but when it was added to the + `sass:math` module the name was changed to + compatible to more clearly convey what the + function does. + {% endheadsUp %} + + {% codeExample 'math-compatible', false %} + @debug math.compatible(2px, 1px); // true + @debug math.compatible(100px, 3em); // false + @debug math.compatible(10cm, 3mm); // true + === + @debug math.compatible(2px, 1px) // true + @debug math.compatible(100px, 3em) // false + @debug math.compatible(10cm, 3mm) // true + {% endcodeExample %} +{% endfunction %} + +{% function 'math.is-unitless($number)', 'unitless($number)', 'returns:boolean' %} + {% markdown %} + Returns whether `$number` has no units. + {% endmarkdown %} + + {% codeExample 'math-is-unitless', false %} + @debug math.is-unitless(100); // true + @debug math.is-unitless(100px); // false + === + @debug math.is-unitless(100) // true + @debug math.is-unitless(100px) // false + {% endcodeExample %} +{% endfunction %} + +{% function 'math.unit($number)', 'unit($number)', 'returns:quoted string' %} + {% markdown %} + Returns a string representation of `$number`'s units. + {% endmarkdown %} + + {% headsUp %} + This function is intended for debugging; its output format is not guaranteed + to be consistent across Sass versions or implementations. + {% endheadsUp %} + + {% codeExample 'math-unitless', false %} + @debug math.unit(100); // "" + @debug math.unit(100px); // "px" + @debug math.unit(5px * 10px); // "px*px" + @debug math.unit(math.div(5px, 1s)); // "px/s" + === + @debug math.unit(100) // "" + @debug math.unit(100px) // "px" + @debug math.unit(5px * 10px) // "px*px" + @debug math.unit(math.div(5px, 1s)) // "px/s" + {% endcodeExample %} +{% endfunction %} + +{{ '## Other Functions' | markdown }} + +{% function 'math.div($number1, $number2)', 'returns:number' %} + {% compatibility 'dart: "1.33.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + Returns the result of dividing `$number1` by `$number2`. + + Any units shared by both numbers will be canceled out. Units in `$number1` + that aren't in `$number2` will end up in the return value's numerator, and + units in `$number2` that aren't in `$number1` will end up in its + denominator. + {% endmarkdown %} + + {% headsUp %} + For backwards-compatibility purposes, this returns the *exact same result* + as [the deprecated `/` operator], including concatenating two strings with a + `/` character between them. However, this behavior will be removed + eventually and shouldn't be used in new stylesheets. + + [the deprecated `/` operator]: /documentation/breaking-changes/slash-div + {% endheadsUp %} + + {% codeExample 'math-div', false %} + @debug math.div(1, 2); // 0.5 + @debug math.div(100px, 5px); // 20 + @debug math.div(100px, 5); // 20px + @debug math.div(100px, 5s); // 20px/s + === + @debug math.div(1, 2) // 0.5 + @debug math.div(100px, 5px) // 20 + @debug math.div(100px, 5) // 20px + @debug math.div(100px, 5s) // 20px/s + {% endcodeExample %} +{% endfunction %} + +{% function 'math.percentage($number)', 'percentage($number)', 'returns:number' %} + {% markdown %} + Converts a unitless `$number` (usually a decimal between 0 and 1) to a + percentage. + {% endmarkdown %} + + {% funFact %} + This function is identical to `$number * 100%`. + {% endfunFact %} + + {% codeExample 'math-percentage', false %} + @debug math.percentage(0.2); // 20% + @debug math.percentage(math.div(100px, 50px)); // 200% + === + @debug math.percentage(0.2) // 20% + @debug math.percentage(math.div(100px, 50px)) // 200% + {% endcodeExample %} +{% endfunction %} + +{% function 'math.random($limit: null)', 'random($limit: null)', 'returns:number' %} + {% markdown %} + If `$limit` is `null`, returns a random decimal number between 0 and 1. + {% endmarkdown %} + + {% codeExample 'math-random', false %} + @debug math.random(); // 0.2821251858 + @debug math.random(); // 0.6221325814 + === + @debug math.random() // 0.2821251858 + @debug math.random() // 0.6221325814 + {% endcodeExample %} + + {% markdown %} + * * * + + If `$limit` is a number greater than or equal to 1, returns a random whole + number between 1 and `$limit`. + {% endmarkdown %} + + {% headsUp false %} + {% markdown %} + `random()` ignores units in `$limit`. [This behavior is deprecated] and + `random($limit)` will return a random integer with the same units as the + `$limit` argument. + + [This behavior is deprecated]: /documentation/breaking-changes/random-with-units + {% endmarkdown %} + + {% codeExample 'math-random-warning', false %} + @debug math.random(100px); // 42 + === + @debug math.random(100px) // 42 + {% endcodeExample %} + {% endheadsUp %} + + {% codeExample 'math-random-limit', false %} + @debug math.random(10); // 4 + @debug math.random(10000); // 5373 + === + @debug math.random(10) // 4 + @debug math.random(10000) // 5373 + {% endcodeExample %} +{% endfunction %} diff --git a/source/documentation/modules/meta.liquid b/source/documentation/modules/meta.liquid new file mode 100644 index 000000000..86b7436de --- /dev/null +++ b/source/documentation/modules/meta.liquid @@ -0,0 +1,572 @@ +--- +title: sass:meta +--- + +{% render 'doc_snippets/built-in-module-status' %} + +{{ '## Mixins' | markdown }} + +{% function 'meta.load-css($url, $with: null)' %} + {% compatibility 'dart: "1.23.0"', 'libsass: false', 'ruby: false' %} + Only Dart Sass currently supports this mixin. + {% endcompatibility %} + + {% markdown %} + Loads the [module][] at `$url` and includes its CSS as though it were + written as the contents of this mixin. The `$with` parameter provides + [configuration][] for the modules; if it's passed, it must be a map from + variable names (without `$`) to the values of those variables to use in the + loaded module. + + [module]: /documentation/at-rules/use + [configuration]: /documentation/at-rules/use#configuration + + If `$url` is relative, it's interpreted as relative to the file in which + `meta.load-css()` is included. + + **Like the [`@use` rule][]**: + + [`@use` rule]: /documentation/at-rules/use + + * This will only evaluate the given module once, even if it's loaded + multiple times in different ways. + + * This cannot provide configuration to a module that's already been loaded, + whether or not it was already loaded with configuration. + + **Unlike the [`@use` rule][]**: + + * This doesn't make any members from the loaded module available in the + current module. + + * This can be used anywhere in a stylesheet. It can even be nested within + style rules to create nested styles! + + * The module URL being loaded can come from a variable and include + [interpolation][]. + + [interpolation]: /documentation/interpolation + {% endmarkdown %} + + {% headsUp %} + The `$url` parameter should be a string containing a URL like you'd pass to + the `@use` rule. It shouldn't be a CSS `url()`! + {% endheadsUp %} + + {% codeExample 'load-css', false %} + // dark-theme/_code.scss + $border-contrast: false !default; + + code { + background-color: #6b717f; + color: #d2e1dd; + @if $border-contrast { + border-color: #dadbdf; + } + } + --- + // style.scss + @use "sass:meta"; + + body.dark { + @include meta.load-css("dark-theme/code", + $with: ("border-contrast": true)); + } + === + // dark-theme/_code.sass + $border-contrast: false !default + + code + background-color: #6b717f + color: #d2e1dd + @if $border-contrast + border-color: #dadbdf + --- + // style.sass + @use "sass:meta" + + body.dark + $configuration: ("border-contrast": true) + @include meta.load-css("dark-theme/code", $with: $configuration) + === + body.dark code { + background-color: #6b717f; + color: #d2e1dd; + border-color: #dadbdf; + } + {% endcodeExample %} +{% endfunction %} + +{{ '## Functions' | markdown }} + +{% function 'meta.calc-args($calc)', 'returns:list' %} + {% compatibility 'dart: "1.40.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + Returns the arguments for the given [calculation]. + + [calculation]: /documentation/values/calculations + + If an argument is a number or a nested calculation, it's returned as that + type. Otherwise, it's returned as an unquoted string. + {% endmarkdown %} + + {% codeExample 'calc-args', false %} + @debug meta.calc-args(calc(100px + 10%)); // unquote("100px + 10%") + @debug meta.calc-args(clamp(50px, var(--width), 1000px)); // 50px, unquote("var(--width)"), 1000px + === + @debug meta.calc-args(calc(100px + 10%)) // unquote("100px + 10%") + @debug meta.calc-args(clamp(50px, var(--width), 1000px)) // 50px, unquote("var(--width)"), 1000px + {% endcodeExample %} +{% endfunction %} + +{% function 'meta.calc-name($calc)', 'returns:quoted string' %} + {% compatibility 'dart: "1.40.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + Returns the name of the given [calculation]. + + [calculation]: /documentation/values/calculations + {% endmarkdown %} + + {% codeExample 'calc-name', false %} + @debug meta.calc-name(calc(100px + 10%)); // "calc" + @debug meta.calc-name(clamp(50px, var(--width), 1000px)); // "clamp" + === + @debug meta.calc-name(calc(100px + 10%)) // "calc" + @debug meta.calc-name(clamp(50px, var(--width), 1000px)) // "clamp" + {% endcodeExample %} +{% endfunction %} + +{% function 'meta.call($function, $args...)', 'call($function, $args...)' %} + {% render 'doc_snippets/call-impl-status' %} + + {% markdown %} + Invokes `$function` with `$args` and returns the result. + + The `$function` should be a [function][] returned by + [`meta.get-function()`][]. + + [function]: /documentation/values/functions + [`meta.get-function()`]: #get-function + {% endmarkdown %} + + {% render 'code_snippets/example-first-class-function' %} +{% endfunction %} + +{% function 'meta.content-exists()', 'content-exists()', 'returns:boolean' %} + {% markdown %} + Returns whether the current mixin was passed a [`@content` block][]. + + [`@content` block]: /documentation/at-rules/mixin#content-blocks + + Throws an error if called outside of a mixin. + {% endmarkdown %} + + {% codeExample 'content-exists', false %} + @mixin debug-content-exists { + @debug meta.content-exists(); + @content; + } + + @include debug-content-exists; // false + @include debug-content-exists { // true + // Content! + } + === + @mixin debug-content-exists + @debug meta.content-exists() + @content + + + @include debug-content-exists // false + @include debug-content-exists // true + // Content! + {% endcodeExample %} +{% endfunction %} + +{% function 'meta.feature-exists($feature)', 'feature-exists($feature)', 'returns:boolean' %} + {% markdown %} + Returns whether the current Sass implementation supports `$feature`. + + The `$feature` must be a string. The currently recognized features are: + + * `global-variable-shadowing`, which means that a local variable will + [shadow][] a global variable unless it has the `!global` flag. + * `extend-selector-pseudoclass`, which means that the [`@extend` rule][] + will affect selectors nested in pseudo-classes like `:not()`. + * `units-level3`, which means that [unit arithmetic][] supports units + defined in [CSS Values and Units Level 3][]. + * `at-error`, which means that the [`@error` rule][] is supported. + * `custom-property`, which means that [custom property declaration][] values + don't support any [expressions][] other than [interpolation][]. + + [shadow]: /documentation/variables#shadowing + [`@extend` rule]: /documentation/at-rules/extend + [unit arithmetic]: /documentation/values/numbers#units + [CSS Values and Units Level 3]: http://www.w3.org/TR/css3-values + [`@error` rule]: /documentation/at-rules/error + [custom property declaration]: /documentation/style-rules/declarations#custom-properties + [expressions]: /documentation/syntax/structure#expressions + [interpolation]: /documentation/interpolation + + Returns `false` for any unrecognized `$feature`. + {% endmarkdown %} + + {% codeExample 'feature-exists', false %} + @debug meta.feature-exists("at-error"); // true + @debug meta.feature-exists("unrecognized"); // false + === + @debug meta.feature-exists("at-error") // true + @debug meta.feature-exists("unrecognized") // false + {% endcodeExample %} +{% endfunction %} + +{% function 'meta.function-exists($name, $module: null)', 'function-exists($name)', 'returns:boolean' %} + {% markdown %} + Returns whether a function named `$name` is defined, either as a built-in + function or a user-defined function. + + If `$module` is passed, this also checks the module named `$module` for the + function definition. `$module` must be a string matching the namespace of a + [`@use` rule][] in the current file. + + [`@use` rule]: /documentation/at-rules/use + {% endmarkdown %} + + {% codeExample 'function-exists', false %} + @use "sass:math"; + + @debug meta.function-exists("div", "math"); // true + @debug meta.function-exists("scale-color"); // true + @debug meta.function-exists("add"); // false + + @function add($num1, $num2) { + @return $num1 + $num2; + } + @debug meta.function-exists("add"); // true + === + @use "sass:math" + + @debug meta.function-exists("div", "math") // true + @debug meta.function-exists("scale-color") // true + @debug meta.function-exists("add") // false + + @function add($num1, $num2) + @return $num1 + $num2 + + @debug meta.function-exists("add") // true + {% endcodeExample %} +{% endfunction %} + +{% function 'meta.get-function($name, $css: false, $module: null)', 'get-function($name, $css: false, $module: null)', 'returns:function' %} + {% markdown %} + Returns the [function][] named `$name`. + + [function]: /documentation/values/functions + + If `$module` is `null`, this returns the function named `$name` without a + namespace (including [global built-in functions][]). Otherwise, `$module` + must be a string matching the namespace of a [`@use` rule][] in the current + file, in which case this returns the function in that module named `$name`. + + [global built-in functions]: /documentation/modules#global-functions + [`@use` rule]: /documentation/at-rules/use + + By default, this throws an error if `$name` doesn't refer to Sass function. + However, if `$css` is `true`, it instead returns a [plain CSS function][]. + + [user-defined]: /documentation/at-rules/function + [plain CSS function]: /documentation/at-rules/function#plain-css-functions + + The returned function can be called using [`meta.call()`](#call). + {% endmarkdown %} + + {% render 'code_snippets/example-first-class-function' %} +{% endfunction %} + +{% function 'meta.global-variable-exists($name, $module: null)', 'global-variable-exists($name, $module: null)', 'returns:boolean' %} + {% markdown %} + Returns whether a [global variable][] named `$name` (without the `$`) + exists. + + [global variable]: /documentation/variables#scope + + If `$module` is `null`, this returns whether a variable named `$name` + without a namespace exists. Otherwise, `$module` must be a string matching + the namespace of a [`@use` rule][] in the current file, in which case this + returns whether that module has a variable named `$name`. + + [`@use` rule]: /documentation/at-rules/use + + See also [`meta.variable-exists()`](#variable-exists). + {% endmarkdown %} + + {% codeExample 'global-variable-exists', false %} + @debug meta.global-variable-exists("var1"); // false + + $var1: value; + @debug meta.global-variable-exists("var1"); // true + + h1 { + // $var2 is local. + $var2: value; + @debug meta.global-variable-exists("var2"); // false + } + === + @debug meta.global-variable-exists("var1") // false + + $var1: value + @debug meta.global-variable-exists("var1") // true + + h1 + // $var2 is local. + $var2: value + @debug meta.global-variable-exists("var2") // false + {% endcodeExample %} +{% endfunction %} + +{% function 'meta.inspect($value)', 'inspect($value)', 'returns:unquoted string' %} + {% markdown %} + Returns a string representation of `$value`. + + Returns a representation of *any* Sass value, not just those that can be + represented in CSS. As such, its return value is not guaranteed to be valid + CSS. + {% endmarkdown %} + + {% headsUp %} + This function is intended for debugging; its output format is not guaranteed + to be consistent across Sass versions or implementations. + {% endheadsUp %} + + {% codeExample 'inspect', false %} + @debug meta.inspect(10px 20px 30px); // unquote("10px 20px 30px") + @debug meta.inspect(("width": 200px)); // unquote('("width": 200px)') + @debug meta.inspect(null); // unquote("null") + @debug meta.inspect("Helvetica"); // unquote('"Helvetica"') + === + @debug meta.inspect(10px 20px 30px) // unquote("10px 20px 30px") + @debug meta.inspect(("width": 200px)) // unquote('("width": 200px)') + @debug meta.inspect(null) // unquote("null") + @debug meta.inspect("Helvetica") // unquote('"Helvetica"') + {% endcodeExample %} +{% endfunction %} + +{% function 'meta.keywords($args)', 'keywords($args)', 'returns:map' %} + {% markdown %} + Returns the keywords passed to a mixin or function that takes [arbitrary + arguments][]. The `$args` argument must be an [argument list][]. + + [arbitrary arguments]: /documentation/at-rules/mixin#taking-arbitrary-arguments + [argument list]: /documentation/values/lists#argument-lists + + The keywords are returned as a map from argument names as unquoted strings + (not including `$`) to the values of those arguments. + {% endmarkdown %} + + {% render 'code_snippets/example-mixin-arbitrary-keyword-arguments' %} +{% endfunction %} + +{% function 'meta.mixin-exists($name, $module: null)', 'mixin-exists($name, $module: null)', 'returns:boolean' %} + {% markdown %} + Returns whether a [mixin][] named `$name` exists. + + [mixin]: /documentation/at-rules/mixin + + If `$module` is `null`, this returns whether a mixin named `$name` without a + namespace exists. Otherwise, `$module` must be a string matching the + namespace of a [`@use` rule][] in the current file, in which case this + returns whether that module has a mixin named `$name`. + + [`@use` rule]: /documentation/at-rules/use + {% endmarkdown %} + + {% codeExample 'mixin-exists', false %} + @debug meta.mixin-exists("shadow-none"); // false + + @mixin shadow-none { + box-shadow: none; + } + + @debug meta.mixin-exists("shadow-none"); // true + === + @debug meta.mixin-exists("shadow-none") // false + + @mixin shadow-none + box-shadow: none + + + @debug meta.mixin-exists("shadow-none") // true + {% endcodeExample %} +{% endfunction %} + +{% function 'meta.module-functions($module)', 'returns:map' %} + {% render 'doc_snippets/module-system-function-status' %} + + {% markdown %} + Returns all the functions defined in a module, as a map from function names + to [function values][]. + + [function values]: /documentation/values/functions + + The `$module` parameter must be a string matching the namespace of a [`@use` + rule][] in the current file. + + [`@use` rule]: /documentation/at-rules/use + {% endmarkdown %} + + {% codeExample 'module-functions', false %} + // _functions.scss + @function pow($base, $exponent) { + $result: 1; + @for $_ from 1 through $exponent { + $result: $result * $base; + } + @return $result; + } + --- + @use "sass:map"; + @use "sass:meta"; + + @use "functions"; + + @debug meta.module-functions("functions"); // ("pow": get-function("pow")) + + @debug meta.call(map.get(meta.module-functions("functions"), "pow"), 3, 4); // 81 + === + // _functions.sass + @function pow($base, $exponent) + $result: 1 + @for $_ from 1 through $exponent + $result: $result * $base + + @return $result + --- + @use "sass:map" + @use "sass:meta" + + @use "functions" + + @debug meta.module-functions("functions") // ("pow": get-function("pow")) + + @debug meta.call(map.get(meta.module-functions("functions"), "pow"), 3, 4) // 81 + {% endcodeExample %} +{% endfunction %} + +{% function 'meta.module-variables($module)', 'returns:map' %} + {% render 'doc_snippets/module-system-function-status' %} + + {% markdown %} + Returns all the variables defined in a module, as a map from variable names + (without `$`) to the values of those variables. + + The `$module` parameter must be a string matching the namespace of a [`@use` + rule][] in the current file. + + [`@use` rule]: /documentation/at-rules/use + {% endmarkdown %} + + {% codeExample 'module-variables', false %} + // _variables.scss + $hopbush: #c69; + $midnight-blue: #036; + $wafer: #e1d7d2; + --- + @use "sass:meta"; + + @use "variables"; + + @debug meta.module-variables("variables"); + // ( + // "hopbush": #c69, + // "midnight-blue": #036, + // "wafer": #e1d7d2 + // ) + === + // _variables.sass + $hopbush: #c69 + $midnight-blue: #036 + $wafer: #e1d7d2 + --- + @use "sass:meta" + + @use "variables" + + @debug meta.module-variables("variables") + // ( + // "hopbush": #c69, + // "midnight-blue": #036, + // "wafer": #e1d7d2 + // ) + {% endcodeExample %} +{% endfunction %} + +{% function 'meta.type-of($value)', 'type-of($value)', 'returns:unquoted string' %} + {% markdown %} + Returns the type of `$value`. + + This can return the following values: + + * [`number`](/documentation/values/numbers) + * [`string`](/documentation/values/strings) + * [`color`](/documentation/values/colors) + * [`list`](/documentation/values/lists) + * [`map`](/documentation/values/maps) + * [`calculation`](/documentation/values/calculations) + * [`bool`](/documentation/values/booleans) + * [`null`](/documentation/values/null) + * [`function`](/documentation/values/functions) + * [`arglist`](/documentation/values/lists#argument-lists) + + New possible values may be added in the future. It may return either `list` + or `map` for `()`, depending on whether or not it was returned by a [map + function][]. + + [map function]: /documentation/modules/map + {% endmarkdown %} + + {% codeExample 'type-of', false %} + @debug meta.type-of(10px); // number + @debug meta.type-of(10px 20px 30px); // list + @debug meta.type-of(()); // list + === + @debug meta.type-of(10px) // number + @debug meta.type-of(10px 20px 30px) // list + @debug meta.type-of(()) // list + {% endcodeExample %} +{% endfunction %} + +{% function 'meta.variable-exists($name)', 'variable-exists($name)', 'returns:boolean' %} + {% markdown %} + Returns whether a variable named `$name` (without the `$`) exists in the + current [scope][]. + + [scope]: /documentation/variables#scope + + See also [`meta.global-variable-exists()`](#global-variable-exists). + {% endmarkdown %} + + {% codeExample 'variable-exists', false %} + @debug meta.variable-exists("var1"); // false + + $var1: value; + @debug meta.variable-exists("var1"); // true + + h1 { + // $var2 is local. + $var2: value; + @debug meta.variable-exists("var2"); // true + } + === + @debug meta.variable-exists("var1") // false + + $var1: value + @debug meta.variable-exists("var1") // true + + h1 + // $var2 is local. + $var2: value + @debug meta.variable-exists("var2") // true + {% endcodeExample %} +{% endfunction %} diff --git a/source/documentation/modules/selector.liquid b/source/documentation/modules/selector.liquid new file mode 100644 index 000000000..abd5df783 --- /dev/null +++ b/source/documentation/modules/selector.liquid @@ -0,0 +1,255 @@ +--- +title: sass:selector +--- + +{% render 'doc_snippets/built-in-module-status' %} + +{% markdown %} + ## Selector Values + + The functions in this module inspect and manipulate selectors. Whenever they + return a selector, it's always a comma-separated [list][] (the selector list) + that contains space-separated lists (the complex selectors) that contain + [unquoted strings][] (the compound selectors). For example, the selector + `.main aside:hover, .sidebar p` would be returned as: + + [list]: /documentation/values/lists + [unquoted strings]: /documentation/values/strings#unquoted + + ```scss + @debug ((unquote(".main") unquote("aside:hover")), + (unquote(".sidebar") unquote("p"))); + // .main aside:hover, .sidebar p + ``` + + Selector arguments to these functions may be in the same format, but they can + also just be normal strings (quoted or unquoted), or a combination. For + example, `".main aside:hover, .sidebar p"` is a valid selector argument. +{% endmarkdown %} + +{% function 'selector.is-superselector($super, $sub)', 'is-superselector($super, $sub)', 'returns:boolean' %} + {% markdown %} + Returns whether the selector `$super` matches all the elements that the + selector `$sub` matches. + + Still returns true even if `$super` matches *more* elements than `$sub`. + + The `$super` and `$sub` selectors may contain [placeholder selectors][], but + not [parent selectors][]. + + [placeholder selectors]: /documentation/style-rules/placeholder-selectors + [parent selectors]: /documentation/style-rules/parent-selector + {% endmarkdown %} + + {% codeExample 'is-superselector', false %} + @debug selector.is-superselector("a", "a.disabled"); // true + @debug selector.is-superselector("a.disabled", "a"); // false + @debug selector.is-superselector("a", "sidebar a"); // true + @debug selector.is-superselector("sidebar a", "a"); // false + @debug selector.is-superselector("a", "a"); // true + === + @debug selector.is-superselector("a", "a.disabled") // true + @debug selector.is-superselector("a.disabled", "a") // false + @debug selector.is-superselector("a", "sidebar a") // true + @debug selector.is-superselector("sidebar a", "a") // false + @debug selector.is-superselector("a", "a") // true + {% endcodeExample %} +{% endfunction %} + +{% function 'selector.append($selectors...)', 'selector-append($selectors...)', 'returns:selector' %} + {% markdown %} + Combines `$selectors` without [descendant combinators][]—that is, without + whitespace between them. + + [descendant combinators]: https://developer.mozilla.org/en-US/docs/Web/CSS/Descendant_selectors + + If any selector in `$selectors` is a selector list, each complex selector is + combined separately. + + The `$selectors` may contain [placeholder selectors][], but not [parent + selectors][]. + + [placeholder selectors]: /documentation/style-rules/placeholder-selectors + [parent selectors]: /documentation/style-rules/parent-selector + + See also [`selector.nest()`](#nest). + {% endmarkdown %} + + {% codeExample 'append', false %} + @debug selector.append("a", ".disabled"); // a.disabled + @debug selector.append(".accordion", "__copy"); // .accordion__copy + @debug selector.append(".accordion", "__copy, __image"); + // .accordion__copy, .accordion__image + === + @debug selector.append("a", ".disabled") // a.disabled + @debug selector.append(".accordion", "__copy") // .accordion__copy + @debug selector.append(".accordion", "__copy, __image") + // .accordion__copy, .accordion__image + {% endcodeExample %} +{% endfunction %} + +{% function 'selector.extend($selector, $extendee, $extender)', 'selector-extend($selector, $extendee, $extender)', 'returns:selector' %} + {% markdown %} + Extends `$selector` as with the [`@extend` rule][]. + + [`@extend` rule]: /documentation/at-rules/extend + + Returns a copy of `$selector` modified with the following `@extend` rule: + + ```scss + #{$extender} { + @extend #{$extendee}; + } + ``` + + In other words, replaces all instances of `$extendee` in `$selector` with + `$extendee, $extender`. If `$selector` doesn't contain `$extendee`, returns + it as-is. + + The `$selector`, `$extendee`, and `$extender` selectors may contain + [placeholder selectors][], but not [parent selectors][]. + + [placeholder selectors]: /documentation/style-rules/placeholder-selectors + [parent selectors]: /documentation/style-rules/parent-selector + + See also [`selector.replace()`](#replace). + {% endmarkdown %} + + {% codeExample 'extend', false %} + @debug selector.extend("a.disabled", "a", ".link"); // a.disabled, .link.disabled + @debug selector.extend("a.disabled", "h1", "h2"); // a.disabled + @debug selector.extend(".guide .info", ".info", ".content nav.sidebar"); + // .guide .info, .guide .content nav.sidebar, .content .guide nav.sidebar + === + @debug selector.extend("a.disabled", "a", ".link") // a.disabled, .link.disabled + @debug selector.extend("a.disabled", "h1", "h2") // a.disabled + @debug selector.extend(".guide .info", ".info", ".content nav.sidebar") + // .guide .info, .guide .content nav.sidebar, .content .guide nav.sidebar + {% endcodeExample %} +{% endfunction %} + +{% function 'selector.nest($selectors...)', 'selector-nest($selectors...)', 'returns:selector' %} + {% markdown %} + Combines `$selectors` as though they were nested within one another in the + stylesheet. + + The `$selectors` may contain [placeholder selectors][]. Unlike other + selector functions, all of them except the first may also contain [parent + selectors][]. + + [placeholder selectors]: /documentation/style-rules/placeholder-selectors + [parent selectors]: /documentation/style-rules/parent-selector + + See also [`selector.append()`](#append). + {% endmarkdown %} + + {% codeExample 'nest', false %} + @debug selector.nest("ul", "li"); // ul li + @debug selector.nest(".alert, .warning", "p"); // .alert p, .warning p + @debug selector.nest(".alert", "&:hover"); // .alert:hover + @debug selector.nest(".accordion", "&__copy"); // .accordion__copy + === + @debug selector.nest("ul", "li") // ul li + @debug selector.nest(".alert, .warning", "p") // .alert p, .warning p + @debug selector.nest(".alert", "&:hover") // .alert:hover + @debug selector.nest(".accordion", "&__copy") // .accordion__copy + {% endcodeExample %} +{% endfunction %} + +{% function 'selector.parse($selector)', 'selector-parse($selector)', 'returns:selector' %} + {% markdown %} + Returns `$selector` in the [selector value](#selector-values) format. + {% endmarkdown %} + + {% codeExample 'parse', false %} + @debug selector.parse(".main aside:hover, .sidebar p"); + // ((unquote(".main") unquote("aside:hover")), + // (unquote(".sidebar") unquote("p"))) + === + @debug selector.parse(".main aside:hover, .sidebar p") + // ((unquote(".main") unquote("aside:hover")), + // (unquote(".sidebar") unquote("p"))) + {% endcodeExample %} +{% endfunction %} + +{% function 'selector.replace($selector, $original, $replacement)', 'selector-replace($selector, $original, $replacement)', 'returns:selector' %} + {% markdown %} + Returns a copy of `$selector` with all instances of `$original` replaced by + `$replacement`. + + This uses the [`@extend` rule][]'s [intelligent unification][] to make sure + `$replacement` is seamlessly integrated into `$selector`. If `$selector` + doesn't contain `$original`, returns it as-is. + + [`@extend` rule]: /documentation/at-rules/extend + [intelligent unification]: /documentation/at-rules/extend#how-it-works + + The `$selector`, `$original`, and `$replacement` selectors may contain + [placeholder selectors][], but not [parent selectors][]. + + [placeholder selectors]: /documentation/style-rules/placeholder-selectors + [parent selectors]: /documentation/style-rules/parent-selector + + See also [`selector.extend()`](#extend). + {% endmarkdown %} + + {% codeExample 'replace', false %} + @debug selector.replace("a.disabled", "a", ".link"); // .link.disabled + @debug selector.replace("a.disabled", "h1", "h2"); // a.disabled + @debug selector.replace(".guide .info", ".info", ".content nav.sidebar"); + // .guide .content nav.sidebar, .content .guide nav.sidebar + === + @debug selector.replace("a.disabled", "a", ".link") // .link.disabled + @debug selector.replace("a.disabled", "h1", "h2") // a.disabled + @debug selector.replace(".guide .info", ".info", ".content nav.sidebar") + // .guide .content nav.sidebar, .content .guide nav.sidebar + {% endcodeExample %} +{% endfunction %} + +{% function 'selector.unify($selector1, $selector2)', 'selector-unify($selector1, $selector2)', 'returns:selector | null' %} + {% markdown %} + Returns a selector that matches only elements matched by *both* `$selector1` + and `$selector2`. + + Returns `null` if `$selector1` and `$selector2` don't match any of the same + elements, or if there's no selector that can express their overlap. + + Like selectors generated by the [`@extend` rule][], the returned selector + isn't guaranteed to match *all* the elements matched by both `$selector1` + and `$selector2` if they're both complex selectors. + + [`@extend` rule]: /documentation/at-rules/extend#html-heuristics + {% endmarkdown %} + + {% codeExample 'unify', false %} + @debug selector.unify("a", ".disabled"); // a.disabled + @debug selector.unify("a.disabled", "a.outgoing"); // a.disabled.outgoing + @debug selector.unify("a", "h1"); // null + @debug selector.unify(".warning a", "main a"); // .warning main a, main .warning a + === + @debug selector.unify("a", ".disabled") // a.disabled + @debug selector.unify("a.disabled", "a.outgoing") // a.disabled.outgoing + @debug selector.unify("a", "h1") // null + @debug selector.unify(".warning a", "main a") // .warning main a, main .warning a + {% endcodeExample %} +{% endfunction %} + +{% function 'selector.simple-selectors($selector)', 'simple-selectors($selector)', 'returns:list' %} + {% markdown %} + Returns a list of simple selectors in `$selector`. + + The `$selector` must be a single string that contains a compound selector. + This means it may not contain combinators (including spaces) or commas. + + The returned list is comma-separated, and the simple selectors are unquoted + strings. + {% endmarkdown %} + + {% codeExample 'simple-selectors', false %} + @debug selector.simple-selectors("a.disabled"); // a, .disabled + @debug selector.simple-selectors("main.blog:after"); // main, .blog, :after + === + @debug selector.simple-selectors("a.disabled") // a, .disabled + @debug selector.simple-selectors("main.blog:after") // main, .blog, :after + {% endcodeExample %} +{% endfunction %} diff --git a/source/documentation/modules/string.liquid b/source/documentation/modules/string.liquid new file mode 100644 index 000000000..505871cf4 --- /dev/null +++ b/source/documentation/modules/string.liquid @@ -0,0 +1,188 @@ +--- +title: sass:string +--- + +{% render 'doc_snippets/built-in-module-status' %} + +{% function 'string.quote($string)', 'quote($string)', 'returns:string' %} + {% markdown %} + Returns `$string` as a quoted string. + {% endmarkdown %} + + {% codeExample 'quote', false %} + @debug string.quote(Helvetica); // "Helvetica" + @debug string.quote("Helvetica"); // "Helvetica" + === + @debug string.quote(Helvetica) // "Helvetica" + @debug string.quote("Helvetica") // "Helvetica" + {% endcodeExample %} +{% endfunction %} + +{% function 'string.index($string, $substring)', 'str-index($string, $substring)', 'returns:number' %} + {% markdown %} + Returns the first [index][] of `$substring` in `$string`, or `null` if + `$string` doesn't contain `$substring`. + + [index]: /documentation/values/strings#string-indexes + {% endmarkdown %} + + {% codeExample 'index', false %} + @debug string.index("Helvetica Neue", "Helvetica"); // 1 + @debug string.index("Helvetica Neue", "Neue"); // 11 + === + @debug string.index("Helvetica Neue", "Helvetica") // 1 + @debug string.index("Helvetica Neue", "Neue") // 11 + {% endcodeExample %} +{% endfunction %} + +{% function 'string.insert($string, $insert, $index)', 'str-insert($string, $insert, $index)', 'returns:string' %} + {% markdown %} + Returns a copy of `$string` with `$insert` inserted at [`$index`][]. + + [`$index`]: /documentation/values/strings#string-indexes + {% endmarkdown %} + + {% codeExample 'insert', false %} + @debug string.insert("Roboto Bold", " Mono", 7); // "Roboto Mono Bold" + @debug string.insert("Roboto Bold", " Mono", -6); // "Roboto Mono Bold" + === + @debug string.insert("Roboto Bold", " Mono", 7) // "Roboto Mono Bold" + @debug string.insert("Roboto Bold", " Mono", -6) // "Roboto Mono Bold" + {% endcodeExample %} + + {% markdown %} + If `$index` is higher than the length of `$string`, `$insert` is added to + the end. If `$index` is smaller than the negative length of the string, + `$insert` is added to the beginning. + {% endmarkdown %} + + {% codeExample 'insert-2', false %} + @debug string.insert("Roboto", " Bold", 100); // "Roboto Bold" + @debug string.insert("Bold", "Roboto ", -100); // "Roboto Bold" + === + @debug string.insert("Roboto", " Bold", 100) // "Roboto Bold" + @debug string.insert("Bold", "Roboto ", -100) // "Roboto Bold" + {% endcodeExample %} +{% endfunction %} + +{% function 'string.length($string)', 'str-length($string)', 'returns:number' %} + {% markdown %} + Returns the number of characters in `$string`. + {% endmarkdown %} + + {% codeExample 'length', false %} + @debug string.length("Helvetica Neue"); // 14 + @debug string.length(bold); // 4 + @debug string.length(""); // 0 + === + @debug string.length("Helvetica Neue") // 14 + @debug string.length(bold) // 4 + @debug string.length("") // 0 + {% endcodeExample %} +{% endfunction %} + +{% function 'string.slice($string, $start-at, $end-at: -1)', 'str-slice($string, $start-at, $end-at: -1)', 'returns:string' %} + {% markdown %} + Returns the slice of `$string` starting at [index][] `$start-at` and ending + at index `$end-at` (both inclusive). + + [index]: /documentation/values/strings#string-indexes + {% endmarkdown %} + + {% codeExample 'slice', false %} + @debug string.slice("Helvetica Neue", 11); // "Neue" + @debug string.slice("Helvetica Neue", 1, 3); // "Hel" + @debug string.slice("Helvetica Neue", 1, -6); // "Helvetica" + === + @debug string.slice("Helvetica Neue", 11) // "Neue" + @debug string.slice("Helvetica Neue", 1, 3) // "Hel" + @debug string.slice("Helvetica Neue", 1, -6) // "Helvetica" + {% endcodeExample %} +{% endfunction %} + +{% function 'string.split($string, $separator, $limit: null)', 'returns:list' %} + {% compatibility 'dart: "1.57.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %} + + {% markdown %} + Returns a bracketed, comma-separated list of substrings of `$string` that + are separated by `$separator`. The `$separator`s aren't included in these + substrings. + + If `$limit` is a number `1` or higher, this splits on at most that many + `$separator`s (and so returns at most `$limit + 1` strings). The last + substring contains the rest of the string, including any remaining + `$separator`s. + {% endmarkdown %} + + {% codeExample 'split', false %} + @debug string.split("Segoe UI Emoji", " "); // ["Segoe", "UI", "Emoji"] + @debug string.split("Segoe UI Emoji", " ", $limit: 1); // ["Segoe", "UI Emoji"] + === + @debug string.split("Segoe UI Emoji", " ") // ["Segoe", "UI", "Emoji"] + @debug string.split("Segoe UI Emoji", " ", $limit: 1) // ["Segoe", "UI Emoji"] + {% endcodeExample %} +{% endfunction %} + +{% function 'string.to-upper-case($string)', 'to-upper-case($string)', 'returns:string' %} + {% markdown %} + Returns a copy of `$string` with the [ASCII][] letters converted to upper + case. + + [ASCII]: https://en.wikipedia.org/wiki/ASCII + {% endmarkdown %} + + {% codeExample 'to-upper-case', false %} + @debug string.to-upper-case("Bold"); // "BOLD" + @debug string.to-upper-case(sans-serif); // SANS-SERIF + === + @debug string.to-upper-case("Bold") // "BOLD" + @debug string.to-upper-case(sans-serif) // SANS-SERIF + {% endcodeExample %} +{% endfunction %} + +{% function 'string.to-lower-case($string)', 'to-lower-case($string)', 'returns:string' %} + {% markdown %} + Returns a copy of `$string` with the [ASCII][] letters converted to lower + case. + + [ASCII]: https://en.wikipedia.org/wiki/ASCII + {% endmarkdown %} + + {% codeExample 'to-lower-case', false %} + @debug string.to-lower-case("Bold"); // "bold" + @debug string.to-lower-case(SANS-SERIF); // sans-serif + === + @debug string.to-lower-case("Bold") // "bold" + @debug string.to-lower-case(SANS-SERIF) // sans-serif + {% endcodeExample %} +{% endfunction %} + +{% function 'string.unique-id()', 'unique-id()', 'returns:string' %} + {% markdown %} + Returns a randomly-generated unquoted string that's guaranteed to be a valid + CSS identifier and to be unique within the current Sass compilation. + {% endmarkdown %} + + {% codeExample 'unique-id', false %} + @debug string.unique-id(); // uabtrnzug + @debug string.unique-id(); // u6w1b1def + === + @debug string.unique-id(); // uabtrnzug + @debug string.unique-id(); // u6w1b1def + {% endcodeExample %} +{% endfunction %} + +{% function 'string.unquote($string)', 'unquote($string)', 'returns:string' %} + {% markdown %} + Returns `$string` as an unquoted string. This can produce strings that + aren't valid CSS, so use with caution. + {% endmarkdown %} + + {% codeExample 'unquote', false %} + @debug string.unquote("Helvetica"); // Helvetica + @debug string.unquote(".widget:hover"); // .widget:hover + === + @debug string.unquote("Helvetica") // Helvetica + @debug string.unquote(".widget:hover") // .widget:hover + {% endcodeExample %} +{% endfunction %} diff --git a/source/documentation/style-rules/declarations.liquid b/source/documentation/style-rules/declarations.liquid index 5df2c066f..80ebd7c25 100644 --- a/source/documentation/style-rules/declarations.liquid +++ b/source/documentation/style-rules/declarations.liquid @@ -168,7 +168,9 @@ introduction: > [interpolation]: /documentation/interpolation {% endmarkdown %} - +{% comment -%} + TODO(nweiz): auto-generate this CSS once we're compiling with Dart Sass +{%- endcomment -%} {% codeExample 'custom-properties' %} $primary: #81899b; $accent: #302e24; diff --git a/source/documentation/style-rules/index.liquid b/source/documentation/style-rules/index.liquid index 5a15b5d48..54b8522ce 100644 --- a/source/documentation/style-rules/index.liquid +++ b/source/documentation/style-rules/index.liquid @@ -32,7 +32,7 @@ introduction: > rule's. {% endmarkdown %} -{% render 'code-snippets/example-nesting' %} +{% render 'code_snippets/example-nesting' %} {% headsUp %} Nested rules are super helpful, but they can also make it hard to visualize diff --git a/source/documentation/style-rules/parent-selector.liquid b/source/documentation/style-rules/parent-selector.liquid index 8b2f4a265..24f38bbe8 100644 --- a/source/documentation/style-rules/parent-selector.liquid +++ b/source/documentation/style-rules/parent-selector.liquid @@ -154,7 +154,7 @@ introduction: > [falsey]: /documentation/at-rules/control/if#truthiness-and-falsiness {% endmarkdown %} -{% render 'code-snippets/example-if-parent-selector' %} +{% render 'code_snippets/example-if-parent-selector' %} {% markdown %} ### Advanced Nesting @@ -168,7 +168,7 @@ introduction: > [`@at-root` rule]: /documentation/at-rules/at-root {% endmarkdown %} -{% render 'code-snippets/example-advanced-nesting' %} +{% render 'code_snippets/example-advanced-nesting' %} {% headsUp %} When Sass is nesting selectors, it doesn't know what interpolation was used to diff --git a/source/documentation/style-rules/placeholder-selectors.liquid b/source/documentation/style-rules/placeholder-selectors.liquid index b6b14691e..2e6471f0e 100644 --- a/source/documentation/style-rules/placeholder-selectors.liquid +++ b/source/documentation/style-rules/placeholder-selectors.liquid @@ -8,7 +8,7 @@ introduction: > CSS, nor is any style rule whose selectors all contain placeholders. --- -{% render 'code-snippets/example-placeholder' %} +{% render 'code_snippets/example-placeholder' %} {% markdown %} What's the use of a selector that isn't emitted? It can still be [extended][]! diff --git a/source/documentation/syntax/structure.md b/source/documentation/syntax/structure.md index d9dc6eb66..845949619 100644 --- a/source/documentation/syntax/structure.md +++ b/source/documentation/syntax/structure.md @@ -95,7 +95,7 @@ The simplest expressions just represent static values: Sass defines syntax for a number of operations: -{% render 'documentation/snippets/operator-list', parens: true %} +{% render 'doc_snippets/operator-list', parens: true %} ### Other Expressions diff --git a/source/documentation/variables.liquid b/source/documentation/variables.liquid index 0d1d9b390..e59aec7db 100644 --- a/source/documentation/variables.liquid +++ b/source/documentation/variables.liquid @@ -99,7 +99,7 @@ introduction: > ### Configuring Modules - {% render 'documentation/snippets/module-system-status' %} + {% render 'doc_snippets/module-system-status' %} Variables defined with `!default` can be configured when loading a module with the [`@use` rule][]. Sass libraries often use `!default` variables to allow @@ -113,7 +113,7 @@ introduction: > stylesheet with a `!default` flag can be configured. {% endmarkdown %} -{% render 'code-snippets/example-use-with' %} +{% render 'code_snippets/example-use-with' %} {% markdown %} ## Built-in Variables diff --git a/source/guide.liquid b/source/guide.liquid index 2cf37ac8a..638e2efaf 100644 --- a/source/guide.liquid +++ b/source/guide.liquid @@ -207,7 +207,7 @@ navigation: | {% markdown %} ## Modules - {% render 'documentation/snippets/module-system-status' %} + {% render 'doc_snippets/module-system-status' %} You don't have to write all your Sass in a single file. You can split it up however you want with the `@use` rule. This rule loads another Sass file as diff --git a/source/helpers/function.ts b/source/helpers/function.ts new file mode 100644 index 000000000..168781075 --- /dev/null +++ b/source/helpers/function.ts @@ -0,0 +1,93 @@ +import * as cheerio from 'cheerio'; +import stripIndent from 'strip-indent'; + +import { codeBlock } from './components'; +import { liquidEngine } from './engines'; + +const links: Record = { + number: '/documentation/values/numbers', + string: '/documentation/values/strings', + 'quoted string': '/documentation/values/strings#quoted', + 'unquoted string': '/documentation/values/strings#unquoted', + color: '/documentation/values/colors', + list: '/documentation/values/lists', + map: '/documentation/values/maps', + boolean: '/documentation/values/booleans', + null: '/documentation/values/null', + function: '/documentation/values/functions', + selector: '/documentation/modules/selector#selector-values', +}; + +const returnTypeLink = (returnType: string) => + returnType + .split('|') + .map((type) => { + type = type.trim(); + const link = links[type]; + if (!link) { + throw new Error(`Unknown type ${type}`); + } + return `${type}`; + }) + .join(' | '); + +/** Renders API docs for a Sass function (or mixin). + * + * The function's name is parsed from the signature. The API description is + * passed as an HTML block. If `returns:type` is passed as the last argument, + * it's included as the function's return type. + * + * Multiple signatures may be passed, in which case they're all included in + * sequence. + */ +export function _function(content: string, ...signatures: string[]) { + // Parse the last argument as the return type, if it's present + const returns = signatures.at(-1)?.match(/returns?:\s*(.*)/)?.[1]; + if (returns) { + signatures.pop(); + } + + // Highlight each signature + const names: string[] = []; + const highlightedSignatures = signatures.map((signature) => { + signature = stripIndent(signature).trim(); + const [name] = signature.split('(', 2); + const nameWithoutNamespace = name.split('.').at(-1) || name; + const html = codeBlock(`@function ${signature}`, 'scss'); + const $ = cheerio.load(html); + const signatureElements = $('pre code') + .contents() + .filter((index, element) => $(element).text() !== '@function'); + // Add a class to make it easier to index function documentation. + if (!names.includes(nameWithoutNamespace)) { + names.push(nameWithoutNamespace); + const nameEl = signatureElements + .filter((index, element) => { + return $(element).text() == nameWithoutNamespace; + }) + .eq(0); + nameEl.addClass('docSearch-function'); + nameEl.attr('name', name); + } + return signatureElements + .toArray() + .map((el) => $.html(el)) + .join('') + .trim(); + }); + + // Render the final HTML + return liquidEngine.renderFile('function', { + names, + signatures: highlightedSignatures.join('\n'), + content, + returns: returns ? returnTypeLink(returns) : null, + }); +} + +/* eslint-disable @typescript-eslint/no-unsafe-member-access, + @typescript-eslint/no-unsafe-call, + @typescript-eslint/no-explicit-any */ +export default function typePlugin(eleventyConfig: any) { + eleventyConfig.addPairedLiquidShortcode('function', _function); +} diff --git a/source/helpers/type.ts b/source/helpers/type.ts index cb3b030d9..ffe86919c 100644 --- a/source/helpers/type.ts +++ b/source/helpers/type.ts @@ -66,6 +66,13 @@ export const replaceInternalLinks = (content: string, url: string) => */ export const startsWith = (str: string, check: string) => str.startsWith(check); +/** + * Strips leading whitespace from each line in a string. + * + * @see https://github.com/sindresorhus/strip-indent + */ +export const stripInd = (str: string) => stripIndent(str); + /* eslint-disable @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any */ @@ -77,6 +84,7 @@ export default function typePlugin(eleventyConfig: any) { eleventyConfig.addLiquidFilter('typogr', typogr); eleventyConfig.addLiquidFilter('replaceInternalLinks', replaceInternalLinks); eleventyConfig.addLiquidFilter('startsWith', startsWith); + eleventyConfig.addLiquidFilter('stripIndent', stripInd); // shortcodes... eleventyConfig.addLiquidShortcode('lorem', getLorem);