Skip to content

Commit

Permalink
Document first-class mixins (sass#852)
Browse files Browse the repository at this point in the history
Closes sass#786
  • Loading branch information
Israel-4Ever authored Oct 6, 2023
1 parent bd69b69 commit 8cb868d
Show file tree
Hide file tree
Showing 9 changed files with 222 additions and 12 deletions.
40 changes: 40 additions & 0 deletions .github/util/dart-sass/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: sass-spec
description: Check out Dart Sass, build it, and link it into package.json.
runs:
using: composite
steps:
- name: Check out Dart Sass
id: clone
uses: sass/clone-linked-repo@v1
with:
repo: sass/dart-sass
default-ref: null

- uses: dart-lang/setup-dart@v1
if: steps.clone.outputs.cloned == 'true'

- uses: bufbuild/[email protected]
with: {github_token: '${{ inputs.github-token }}'}
if: steps.clone.outputs.cloned == 'true'

- run: dart pub get
working-directory: dart-sass
shell: bash
if: steps.clone.outputs.cloned == 'true'

- name: Check out the language repo
uses: sass/clone-linked-repo@v1
with: {repo: sass/sass, path: dart-sass/build/language}
if: steps.clone.outputs.cloned == 'true'

- name: Build Dart Sass
run: dart run grinder protobuf pkg-npm-dev
working-directory: dart-sass
shell: bash
if: steps.clone.outputs.cloned == 'true'

- name: Install Dart Sass
run: npm install dart-sass/build/npm
if: steps.clone.outputs.cloned == 'true'
env: {UPDATE_SASS_PROTOCOL: false}
shell: bash
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ jobs:
with:
node-version-file: .nvmrc
cache: npm
- uses: ./.github/util/dart-sass
- name: Install & build
run: |
npm ci
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/shadow-repo-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ name: Shadow Repo

on:
push:
branches: [main, feature.*]
pull_request:
types: [opened]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
Expand All @@ -13,13 +13,14 @@ jobs:
build:
name: Build
runs-on: ubuntu-latest
if: github.event_name == 'push'
if: github.event_name == 'push' || github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
node-version-file: .nvmrc
cache: npm
- uses: ./.github/util/dart-sass
- run: npm ci
- run: npm run build-prod
- run: tar cf site.tar _site
Expand Down
7 changes: 4 additions & 3 deletions .github/workflows/shadow-repo-update.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,27 +46,28 @@ jobs:
if: github.event_name == 'workflow_run'
steps:
- name: Download built site
id: download_site
uses: dawidd6/[email protected]
with:
name: site
run_id: ${{ github.event.workflow_run.id }}
if_no_artifact_found: ignore
- name: Clone shadow repo
uses: actions/checkout@v4
if: steps.download_comment.outputs.found_artifact == 'true'
if: steps.download_site.outputs.found_artifact == 'true'
with:
repository: ${{ vars.SHADOW_OWNER }}/${{ vars.SHADOW_REPO }}
token: ${{ secrets.SASS_SITE_TOKEN }}
ref: ${{ github.ref_name }}
path: shadow-repo
- name: Update shadow repo files
if: steps.download_comment.outputs.found_artifact == 'true'
if: steps.download_site.outputs.found_artifact == 'true'
run: |
rm -rf shadow_repo/*
tar xf site.tar
cp -rT _site shadow_repo
- uses: EndBug/add-and-commit@v9
if: steps.download_comment.outputs.found_artifact == 'true'
if: steps.download_site.outputs.found_artifact == 'true'
with:
cwd: shadow-repo
author_name: Sass Bot
Expand Down
1 change: 1 addition & 0 deletions source/_data/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ toc:
- <code>null</code>: /documentation/values/null/
- Calculations: /documentation/values/calculations/
- Functions: /documentation/values/functions/
- Mixins: /documentation/values/mixins/
- Operators: /documentation/operators/
:children:
- Equality: /documentation/operators/equality/
Expand Down
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 %}
120 changes: 114 additions & 6 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` block]: /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 @@ -279,7 +308,30 @@ title: sass:meta
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 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
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].

[plain CSS function]: /documentation/at-rules/function/#plain-css-functions

The returned function can be called using [`meta.call()`](#call).
Expand Down Expand Up @@ -457,6 +509,62 @@ 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 stretch() {
align-items: stretch;
display: flex;
flex-direction: row;
}
---
@use "sass:map";
@use "sass:meta";

@use "mixins";

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

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

@use "mixins"

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

.header
@include meta.apply(map.get(meta.module-mixins("mixins"), "stretch"))
===
.header {
align-items: stretch;
display: flex;
flex-direction: row;
}
{% endcodeExample %}
{% endfunction %}

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

Expand Down
2 changes: 1 addition & 1 deletion source/documentation/values/functions.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: Functions
title: Function Values
---

{% render 'doc_snippets/call-impl-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: Mixin Values
---

{% 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-mixin()` function]: /documentation/modules/meta#get-mixin
[`meta.apply()` mixin]: /documentation/modules/meta#apply

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

0 comments on commit 8cb868d

Please sign in to comment.