Port modules/index

This commit is contained in:
Ed Rivas 2023-05-26 23:11:28 +00:00
parent 20f485336e
commit 9b497e2c75
4 changed files with 376 additions and 0 deletions

View File

@ -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);

View File

@ -0,0 +1,262 @@
---
title: Built-In Modules
eleventyComputed:
before_introduction: >
{% render 'documentation/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 '1.15.0', false, null, false, '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 true, false, null, '3.7.0', 'Percent Alpha' %}
LibSass and older versions of Ruby Sass don't support alpha values specified as
percentages.
{% endcompatibility %}
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).
[unitless]: values/numbers#units
{% funFact %}
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
{% 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)' %}
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
{% 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 '1.15.0', false, null, false, '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 true, false, null, '3.7.0', 'Percent Alpha' %}
LibSass and older versions of Ruby Sass don't support alpha values specified
as percentages.
{% endcompatibility %}
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
{% funFact %}
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
{% 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 %}
---
If `$color` and `$alpha` are passed, this returns `$color` with the given
`$alpha` channel instead of its original alpha channel.
{% 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 %}

View File

@ -0,0 +1,4 @@
{% compatibility '1.23.0', false, null, 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 %}

108
source/helpers/function.ts Normal file
View File

@ -0,0 +1,108 @@
import * as cheerio from 'cheerio';
import { codeBlock } from './components';
import { markdown } from './type';
const links: Record<string, string> = {
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 `<a href="${link}">${type}</a>`;
})
.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 a Markdown 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) => {
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');
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()
.replace(/\n/g, '&#x0000A');
});
// Add the return type after the last signature
let mergedSignatures = highlightedSignatures.join('&#x0000A');
if (returns) {
mergedSignatures += ` <span class="token comment">//=> ${returnTypeLink(
returns,
)}</span>`;
}
// Assemble the final HTML
const $ = cheerio.load('');
const div = $('<div></div>')
.addClass('sl-c-callout sl-c-callout--function')
.attr('id', names[0]);
const pre = $('<pre></pre>').addClass('language-scss');
const anchor = $('<a></a>').addClass('anchor').attr('href', `#${names[0]}`);
const code = $('<code></code>')
.addClass('language-scss')
.html(mergedSignatures);
pre.append(anchor).append(code);
div.append(pre).append(markdown(content));
$('body').append(div);
names.slice(1).forEach((name) => {
const div = $('<div></div>').attr('id', name);
$('body').append(div);
});
return $('body').html() || '';
}
/* 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);
}