Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document first-class mixins #852

Merged
merged 18 commits into from
Oct 6, 2023
Merged
41 changes: 41 additions & 0 deletions source/_includes/code_snippets/example-first-class-mixin.liquid
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{% codeExample 'first-class-mixin' %}
@use "sass:meta";
@use "sass:string";

/// Passes each element of $list to a separate invocation of $mixin.
@mixin apply-to-all($mixin, $list) {
@each $element in $list {
@include meta.apply($mixin, $element);
}
}

@mixin font-class($size) {
.font-#{$size} {
font-size: $size;
}
}

$sizes: 8px, 12px 2rem;

@include apply-to-all(meta.get-mixin("font-class"), $sizes);
===
@use "sass:meta"
@use "sass:string"

/// Passes each element of $list to a separate invocation of $mixin.
@mixin apply-to-all($mixin, $list)
@each $element in $list
@include meta.apply($mixin, $element)



@mixin font-class($size)
.font-#{$size}
font-size: $size



$sizes: 8px, 12px 2rem

@include apply-to-all(meta.get-mixin("font-class"), $sizes)
{% endcodeExample %}
113 changes: 108 additions & 5 deletions source/documentation/modules/meta.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@ title: sass:meta

## Mixins

{% function 'meta.apply($mixin, $args... %}
{% compatibility 'dart: "1.69.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %}

Includes `$mixin` with `$args`. If this is passed a [`@content` block], it's
forwarded to `$mixin`.

[`@content`]: /documentation/at-rules/mixin#content-blocks

The `$mixin` must be a [mixin value], such as one returned by
[`meta.get-mixin()`].

[mixin value]: /documentation/values/mixins
[`meta.get-mixin()`]: #get-mixin

{% render 'code_snippets/example-first-class-mixin %}
{% endfunction %}

{% function 'meta.load-css($url, $with: null)' %}
{% compatibility 'dart: "1.23.0"', 'libsass: false', 'ruby: false' %}
Only Dart Sass currently supports this mixin.
Expand Down Expand Up @@ -97,6 +114,18 @@ title: sass:meta

## Functions

{% function 'meta.accepts-content($mixin)', 'returns:boolean' %}
{% compatibility 'dart: "1.69.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %}

Returns whether the given [mixin value] can accept a [`@content` block].

[mixin value]: /documentation/values/mixins
[`@content` block]: /documentation/at-rules/mixin#content-blocks

This returns true if it's _possible_ for the mixin to accept a `@content`
block, even if it doesn't always do so.
{% endfunction %}

{% function 'meta.calc-args($calc)', 'returns:list' %}
{% compatibility 'dart: "1.40.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %}

Expand Down Expand Up @@ -145,10 +174,10 @@ title: sass:meta

Invokes `$function` with `$args` and returns the result.

The `$function` should be a [function][] returned by
[`meta.get-function()`][].
The `$function` must be a [function value], such as one returned by
[`meta.get-function()`].

[function]: /documentation/values/functions
[function value]: /documentation/values/functions
[`meta.get-function()`]: #get-function

{% render 'code_snippets/example-first-class-function' %}
Expand Down Expand Up @@ -264,9 +293,9 @@ title: sass:meta
{% endfunction %}

{% function 'meta.get-function($name, $css: false, $module: null)', 'get-function($name, $css: false, $module: null)', 'returns:function' %}
Returns the [function][] named `$name`.
Returns the [function value] named `$name`.

[function]: /documentation/values/functions
[function value]: /documentation/values/functions

If `$module` is `null`, this returns the function named `$name` without a
namespace (including [global built-in functions][]). Otherwise, `$module` must
Expand All @@ -282,6 +311,31 @@ title: sass:meta
[user-defined]: /documentation/at-rules/function
[plain CSS function]: /documentation/at-rules/function/#plain-css-functions

The returned mixin can be included using [`meta.apply()`](#apply).

{% render 'code_snippets/example-first-class-mixin' %}
{% endfunction %}

{% function 'meta.get-mixin($name, $module: null)', 'returns:function' %}
{% compatibility 'dart: "1.69.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %}

Returns the [mixin value] named `$name`.

[mixin value]: /documentation/values/mixins

If `$module` is `null`, this returns the mixin named `$name` defined in the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I take it that meta.get-mixin(my-module.my-mixin) won't return anything because it should have been written as meta.get-mixin(my-module, my-mixin), is that right?

I think it might be helpful to call that out, but I realize get-function doesn't, so maybe that's OK.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's wait and see if users actually make that mistake before we add extra text for it. I don't think I've seen anyone do that with meta.get-function().

current module. Otherwise, `$module` must be a string matching the namespace
of a [`@use` rule] in the current file, in which case this returns the
mixin in that module named `$name`.

[`@use` rule]: /documentation/at-rules/use

By default, this throws an error if `$name` doesn't refer to a mixin. 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).

{% render 'code_snippets/example-first-class-function' %}
Expand Down Expand Up @@ -457,6 +511,55 @@ title: sass:meta
{% endcodeExample %}
{% endfunction %}

{% function 'meta.module-mixins($module)', 'returns:map' %}
{% compatibility 'dart: "1.69.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %}

Returns all the mixins defined in a module, as a map from mixin names to
[mixin values].

[mixin values]: /documentation/values/mixins

The `$module` parameter must be a string matching the namespace of a [`@use`
rule] in the current file.

[`@use` rule]: /documentation/at-rules/use

{% codeExample 'module-mixins' %}
// _mixins.scss
@mixin header-stretch() {
align-items: stretch;
display: flex;
flex-direction: row;
}
---
@use "sass:map";
@use "sass:meta";

@use "mixins";

@debug meta.module-mixins("mixins");
// => ("header-stretch": get-mixin("header-stretch"))

@include meta.apply(map.get(meta.module-mixins("mixins"), "header-stretch"));
===
// _mixins.scss
@mixin header-stretch()
align-items: stretch
display: flex
flex-direction: row
---
@use "sass:map"
@use "sass:meta"

@use "mixins"

@debug meta.module-mixins("mixins")
// => ("header-stretch": get-mixin("header-stretch"))

@include meta.apply(map.get(meta.module-mixins("mixins"), "header-stretch"))
{% endcodeExample %}
{% endfunction %}

{% function 'meta.module-variables($module)', 'returns:map' %}
{% render 'doc_snippets/module-system-function-status' %}

Expand Down
17 changes: 17 additions & 0 deletions source/documentation/values/mixins.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
title: Mixins
---

{% compatibility 'dart: "1.69.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %}

[Mixins] can also be values! You can't directly write a mixin as a value, but
you can pass a mixin's name to the [`meta.get-mixin()` function] to get it as a
value. Once you have a mixin value, you can pass it to the [`meta.apply()`
mixin] to call it. This is for libraries to be extensible in complex and
powerful ways.

[Mixins]: /documentation/at-rules/mixin
[`meta.get-function()` function]: /documentation/modules/meta#get-function
[`meta.call()` function]: /documentation/modules/meta#call

{% render 'code_snippets/example-first-class-mixin' %}