gerillass 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -0
- package/SKILL.md +57 -5
- package/gerillass.json +315 -66
- package/package.json +1 -1
- package/scss/library/_after.scss +15 -0
- package/scss/library/_all-text-inputs.scss +1 -1
- package/scss/library/_background-dots.scss +55 -9
- package/scss/library/_background-image.scss +31 -5
- package/scss/library/_background-stripes.scss +43 -4
- package/scss/library/_before.scss +15 -0
- package/scss/library/_border-radius.scss +25 -1
- package/scss/library/_brand-logo.scss +24 -1
- package/scss/library/_breakpoint.scss +34 -0
- package/scss/library/_container-query.scss +73 -1
- package/scss/library/_counter.scss +24 -2
- package/scss/library/_focus-ring.scss +34 -0
- package/scss/library/_font-face.scss +61 -3
- package/scss/library/_index.scss +3 -0
- package/scss/library/_line-clamp.scss +14 -1
- package/scss/library/_loadify.scss +7 -0
- package/scss/library/_motion-safe.scss +28 -0
- package/scss/library/_remove.scss +4 -1
- package/scss/library/_reset-css.scss +7 -5
- package/scss/library/_screen-agent.scss +27 -0
- package/scss/library/_text-image.scss +24 -1
- package/scss/library/_tokens.scss +111 -0
- package/scss/library/_triangle.scss +109 -9
- package/scss/utilities/_clear-unit.scss +4 -0
- package/scss/utilities/_convert-to-em.scss +6 -0
- package/scss/utilities/_convert-to-number.scss +15 -0
- package/scss/utilities/_font-sizer.scss +14 -0
- package/scss/utilities/_remify.scss +7 -0
- package/scss/utilities/_validate-length.scss +7 -0
- package/scss/utilities/_validate-ratio.scss +25 -2
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
@charset "UTF-8";
|
|
2
2
|
|
|
3
3
|
@use "sass:list";
|
|
4
|
+
@use "sass:map";
|
|
4
5
|
@use "sass:meta";
|
|
5
6
|
@use "sass:string";
|
|
7
|
+
@use "../maps/map-for-font-formats" as *;
|
|
6
8
|
@use "../utilities/font-source" as *;
|
|
7
9
|
|
|
8
10
|
@mixin font-face(
|
|
@@ -10,7 +12,8 @@
|
|
|
10
12
|
$file-path,
|
|
11
13
|
$font-style: normal,
|
|
12
14
|
$font-weight: 400,
|
|
13
|
-
$file-formats: eot woff2 woff ttf svg
|
|
15
|
+
$file-formats: eot woff2 woff ttf svg,
|
|
16
|
+
$font-display: null
|
|
14
17
|
) {
|
|
15
18
|
@if & {
|
|
16
19
|
@error "You must call the mixin at the root level of your style sheet, not in the `#{&+'{'+'}'}` selector.";
|
|
@@ -18,10 +21,29 @@
|
|
|
18
21
|
@error "`#{$font-family}` is not a valid $font-family for `font-face`. Pass the family name as a string, such as `\"Inter\"`.";
|
|
19
22
|
} @else if meta.type-of($file-path) != "string" {
|
|
20
23
|
@error "`#{$file-path}` is not a valid $file-path for `font-face`. Pass the path to the font files without an extension, as a string.";
|
|
24
|
+
} @else if string.slice(meta.inspect($file-path), 1, 1) != "\"" and string.index($file-path, "var(") {
|
|
25
|
+
// The path is written into url("..."), where var() is only text: the
|
|
26
|
+
// browser would request a file literally named var(--x).woff2.
|
|
27
|
+
@error "`#{$file-path}` is not a valid $file-path for `font-face`. A custom property cannot supply a font path, because the path is written into url() as text. Pass the path as a string.";
|
|
28
|
+
} @else if string.slice(meta.inspect($font-family), 1, 1) != "\"" and string.index($font-family, "var(") {
|
|
29
|
+
// Measured in Chrome 152, a @font-face rule dropped `font-family: var(--f)`,
|
|
30
|
+
// and a rule with no family is never used. A quoted "var(--f)" is only a
|
|
31
|
+
// name, which the browser kept, so it is left alone like a quoted path.
|
|
32
|
+
@error "`#{$font-family}` is not a valid $font-family for `font-face`. A custom property cannot supply it, because a @font-face rule drops var() in a descriptor, and without a family the font is never used. Pass the family name as a string, such as `\"Inter\"`.";
|
|
21
33
|
} @else {
|
|
22
34
|
|
|
23
35
|
$list: ();
|
|
24
36
|
|
|
37
|
+
// The same rule dropped `font-style: var(--s)`, `font-weight: var(--w)` and
|
|
38
|
+
// `font-weight: 100 var(--w)`, so var() is refused anywhere in either,
|
|
39
|
+
// before $font-style is reinterpreted below.
|
|
40
|
+
@if string.index(meta.inspect($font-style), "var(") {
|
|
41
|
+
@error "`#{meta.inspect($font-style)}` is not a valid $font-style for `font-face`. A custom property cannot supply it, because a @font-face rule drops var() in a descriptor. Pass the style itself, such as `italic`.";
|
|
42
|
+
}
|
|
43
|
+
@if string.index(meta.inspect($font-weight), "var(") {
|
|
44
|
+
@error "`#{meta.inspect($font-weight)}` is not a valid $font-weight for `font-face`. A custom property cannot supply it, because a @font-face rule drops var() in a descriptor. Pass the weight itself, such as `700`, or a range such as `160 700`.";
|
|
45
|
+
}
|
|
46
|
+
|
|
25
47
|
@if list.index(100 200 300 400 500 600 700 800 900, $font-style) {
|
|
26
48
|
$font-weight: $font-style;
|
|
27
49
|
$font-style: normal;
|
|
@@ -42,6 +64,27 @@
|
|
|
42
64
|
}
|
|
43
65
|
}
|
|
44
66
|
|
|
67
|
+
// $font-display comes last so the reinterpretation of $font-style above is
|
|
68
|
+
// left alone, and it is written only when given: the browser's default is
|
|
69
|
+
// what a call without it has always had. Measured in Chrome 152, a
|
|
70
|
+
// @font-face rule drops `font-display: "swap"` and `font-display: var(--d)`
|
|
71
|
+
// without a word, so a quoted keyword is unquoted and var() is refused.
|
|
72
|
+
@if $font-display != null {
|
|
73
|
+
@if meta.type-of($font-display) == "string" and string.index($font-display, "var(") {
|
|
74
|
+
@error "`#{meta.inspect($font-display)}` is not a valid $font-display for `font-face`. A custom property cannot supply it, because a @font-face rule drops var() in a descriptor. Pass one of: auto, block, swap, fallback, optional.";
|
|
75
|
+
} @else if not list.index("auto" "block" "swap" "fallback" "optional", $font-display) {
|
|
76
|
+
@error "`#{meta.inspect($font-display)}` is not a valid $font-display for `font-face`. Pass one of: auto, block, swap, fallback, optional.";
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// fontSource returns nothing for a format it does not know, so a typo
|
|
81
|
+
// dropped that source and a list of nothing but typos dropped `src`.
|
|
82
|
+
@each $format in $file-formats {
|
|
83
|
+
@if not map.has-key($map-for-font-formats, $format) {
|
|
84
|
+
@error "`#{meta.inspect($format)}` is not a valid file format for `font-face`. Pass one or more of: #{map.keys($map-for-font-formats)}.";
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
45
88
|
@font-face {
|
|
46
89
|
font-family: $font-family;
|
|
47
90
|
@if list.index($file-formats, eot) {
|
|
@@ -51,8 +94,23 @@
|
|
|
51
94
|
$list: list.append($list, fontSource($font-family, $file-path, list.nth($file-formats, $i)), comma);
|
|
52
95
|
}
|
|
53
96
|
src: $list;
|
|
54
|
-
|
|
55
|
-
font-
|
|
97
|
+
// A quoted keyword is written without its quotes. Measured in Chrome 152,
|
|
98
|
+
// `font-style: "italic"` and `font-weight: "bold"` were dropped, and this
|
|
99
|
+
// is what a quoted argument used to produce. The family keeps its quotes,
|
|
100
|
+
// since there they make it a name.
|
|
101
|
+
@if meta.type-of($font-style) == "string" {
|
|
102
|
+
font-style: string.unquote($font-style);
|
|
103
|
+
} @else {
|
|
104
|
+
font-style: $font-style;
|
|
105
|
+
}
|
|
106
|
+
@if meta.type-of($font-weight) == "string" {
|
|
107
|
+
font-weight: string.unquote($font-weight);
|
|
108
|
+
} @else {
|
|
109
|
+
font-weight: $font-weight;
|
|
110
|
+
}
|
|
111
|
+
@if $font-display {
|
|
112
|
+
font-display: string.unquote($font-display);
|
|
113
|
+
}
|
|
56
114
|
@content;
|
|
57
115
|
}
|
|
58
116
|
}
|
package/scss/library/_index.scss
CHANGED
|
@@ -25,11 +25,13 @@
|
|
|
25
25
|
@forward "ellipsis";
|
|
26
26
|
@forward "escape-to-parent";
|
|
27
27
|
@forward "except";
|
|
28
|
+
@forward "focus-ring";
|
|
28
29
|
@forward "font-face";
|
|
29
30
|
@forward "hide";
|
|
30
31
|
@forward "line-clamp";
|
|
31
32
|
@forward "linear-gradient";
|
|
32
33
|
@forward "loadify";
|
|
34
|
+
@forward "motion-safe";
|
|
33
35
|
@forward "only";
|
|
34
36
|
@forward "placeholder";
|
|
35
37
|
@forward "placeholder-shown";
|
|
@@ -52,4 +54,5 @@
|
|
|
52
54
|
@forward "text-selection";
|
|
53
55
|
@forward "text-shadow";
|
|
54
56
|
@forward "text-stroke";
|
|
57
|
+
@forward "tokens";
|
|
55
58
|
@forward "triangle";
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
@charset "UTF-8";
|
|
2
2
|
|
|
3
|
+
@use "sass:list";
|
|
3
4
|
@use "sass:math";
|
|
4
5
|
@use "sass:meta";
|
|
6
|
+
@use "sass:string";
|
|
7
|
+
|
|
8
|
+
// True for an unquoted call to one of $names, such as var(--x). A quoted string
|
|
9
|
+
// stays text, so `"var(--x)"` is not a call.
|
|
10
|
+
@function -is-css-function($value, $names) {
|
|
11
|
+
@if meta.type-of($value) != "string" or string.slice(meta.inspect($value), 1, 1) == "\"" {
|
|
12
|
+
@return false;
|
|
13
|
+
}
|
|
14
|
+
$open: string.index($value, "(");
|
|
15
|
+
@return $open != null and list.index($names, string.to-lower-case(string.slice($value, 1, $open - 1))) != null;
|
|
16
|
+
}
|
|
5
17
|
|
|
6
18
|
// Truncates text after a number of lines. `ellipsis` does one line; this does
|
|
7
19
|
// several, and needs five declarations rather than one because -webkit-line-clamp
|
|
@@ -20,7 +32,8 @@
|
|
|
20
32
|
// So the standard property is emitted for the future and the prefixed trio is
|
|
21
33
|
// what actually does the work today.
|
|
22
34
|
@mixin line-clamp($lines: 3) {
|
|
23
|
-
|
|
35
|
+
// var() is resolved by the browser, so only a literal count is checked.
|
|
36
|
+
@if $lines != none and not -is-css-function($lines, "var" "env") {
|
|
24
37
|
@if meta.type-of($lines) != "number" or not math.is-unitless($lines) {
|
|
25
38
|
@error "`#{$lines}` is not a valid $lines for `line-clamp`. Pass a whole number of lines, or `none` to undo a clamp.";
|
|
26
39
|
}
|
|
@@ -4,6 +4,13 @@
|
|
|
4
4
|
@use "sass:meta";
|
|
5
5
|
@use "../utilities/is-time" as *;
|
|
6
6
|
|
|
7
|
+
// `init` defines the %loadify placeholder, and every call extends it. Under the
|
|
8
|
+
// module system a selector can only extend a placeholder from a module it
|
|
9
|
+
// loads, so the file with the call must be the file that calls `init`, or must
|
|
10
|
+
// `@use` it. Anything else fails with "The target selector was not found".
|
|
11
|
+
// An entry file calling `init` does not help a partial it loads, since the
|
|
12
|
+
// partial does not load the entry file. Stylesheets using @import share one
|
|
13
|
+
// scope and are unaffected.
|
|
7
14
|
@mixin loadify($params...) {
|
|
8
15
|
@if not & {
|
|
9
16
|
@if list.length($params) == 0 or (list.length($params) == 1 and list.nth($params, 1) == "init") {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
@charset "UTF-8";
|
|
2
|
+
|
|
3
|
+
@use "sass:meta";
|
|
4
|
+
|
|
5
|
+
// Applies its content only for users who have not asked their system for less
|
|
6
|
+
// motion. Motion is opt-in here rather than switched off afterwards: the usual
|
|
7
|
+
// way round writes the animation and then overrides it under
|
|
8
|
+
// `prefers-reduced-motion: reduce`, and that override has to catch every
|
|
9
|
+
// animation and transition, including the ones added later. Written inside this
|
|
10
|
+
// block, nothing moves for a user who asked for less, without a list to keep.
|
|
11
|
+
//
|
|
12
|
+
// Two agent trials wrote this same wrapper by hand, as `motion-safe` and
|
|
13
|
+
// `motion-ok`. Both media queries were checked to parse in Chrome 152; the
|
|
14
|
+
// reduced-motion setting itself could not be emulated in the browser used.
|
|
15
|
+
//
|
|
16
|
+
// The trap is the resting state. An element hidden in its base rule and
|
|
17
|
+
// revealed by an animation inside the block stays hidden when the block does
|
|
18
|
+
// not apply, which is what `loadify` avoids by applying the end state. Keep the
|
|
19
|
+
// visible state in the base rule and the start of the motion in the keyframes.
|
|
20
|
+
@mixin motion-safe {
|
|
21
|
+
@if not meta.content-exists() {
|
|
22
|
+
@error "`motion-safe` wraps the motion you pass it, so call it with a block, such as `.card { @include motion-safe { transition: transform 0.2s; } }`.";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
26
|
+
@content;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -8,9 +8,12 @@
|
|
|
8
8
|
display: none;
|
|
9
9
|
} @else if list.length($params) == 1 {
|
|
10
10
|
$value: list.nth($params, 1);
|
|
11
|
-
|
|
11
|
+
// `only` rather than the one-argument form, which compiles to the same
|
|
12
|
+
// query, so the warning below names `remove` and is printed once.
|
|
13
|
+
@include breakpoint(only, $value) {
|
|
12
14
|
display: none;
|
|
13
15
|
}
|
|
16
|
+
@warn "`remove(#{$value})` hides the element only at a viewport exactly that wide, a single pixel. Write `remove(only, #{$value})` for that width, or `remove(min, #{$value})` from it upwards. The one-argument form will be refused in 3.0.0.";
|
|
14
17
|
} @else if list.length($params) == 2 {
|
|
15
18
|
@if list.index("min" "max", list.nth($params, 1)) {
|
|
16
19
|
$mode: list.nth($params, 1);
|
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
@if & {
|
|
5
5
|
@error "Please call the mixin at the root of your style sheet.";
|
|
6
6
|
} @else {
|
|
7
|
-
|
|
8
|
-
v2.0 | 20110126
|
|
9
|
-
License: none (public domain)
|
|
10
|
-
|
|
7
|
+
// http://meyerweb.com/eric/tools/css/reset/
|
|
8
|
+
// v2.0 | 20110126
|
|
9
|
+
// License: none (public domain)
|
|
10
|
+
//
|
|
11
|
+
// Comments in this file are `//`: a /* */ comment inside a mixin is emitted
|
|
12
|
+
// into the stylesheet of everyone who includes it.
|
|
11
13
|
html, body, div, span, applet, object, iframe,
|
|
12
14
|
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
|
13
15
|
a, abbr, acronym, address, big, cite, code,
|
|
@@ -28,7 +30,7 @@
|
|
|
28
30
|
font: inherit;
|
|
29
31
|
vertical-align: baseline;
|
|
30
32
|
}
|
|
31
|
-
|
|
33
|
+
// HTML5 display-role reset for older browsers
|
|
32
34
|
article, aside, details, figcaption, figure,
|
|
33
35
|
footer, header, hgroup, menu, main, nav, section {
|
|
34
36
|
display: block;
|
|
@@ -1,6 +1,33 @@
|
|
|
1
1
|
@charset "UTF-8";
|
|
2
2
|
|
|
3
|
+
@use "sass:list";
|
|
4
|
+
@use "sass:meta";
|
|
5
|
+
@use "sass:string";
|
|
6
|
+
|
|
7
|
+
// Returns the first value in $value that holds var(), looking inside lists and
|
|
8
|
+
// calculations, or null. Custom properties are not evaluated in a @media or
|
|
9
|
+
// @container condition, so a rule built from one never applies, and nothing
|
|
10
|
+
// warns. Measured in Chrome 152: `(min-width: var(--wide))` did not match with
|
|
11
|
+
// `--wide: 200px`, while `(min-width: 200px)` did.
|
|
12
|
+
@function -custom-property-in($value) {
|
|
13
|
+
@each $item in $value {
|
|
14
|
+
@if meta.type-of($item) == "list" or meta.type-of($item) == "arglist" {
|
|
15
|
+
$found: -custom-property-in($item);
|
|
16
|
+
@if $found {
|
|
17
|
+
@return $found;
|
|
18
|
+
}
|
|
19
|
+
} @else if string.index(meta.inspect($item), "var(") {
|
|
20
|
+
@return $item;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
@return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
3
26
|
@mixin screen-agent($resolution) {
|
|
27
|
+
$custom-property: -custom-property-in($resolution);
|
|
28
|
+
@if $custom-property {
|
|
29
|
+
@error "`#{$custom-property}` cannot be used as a resolution: custom properties are not evaluated in a @media condition, so the rule would never apply. Pass `1x`, `2x`, `3x` or a resolution such as `2dppx`.";
|
|
30
|
+
}
|
|
4
31
|
@if $resolution == "1x" {
|
|
5
32
|
@media (min-resolution: 96dpi) {
|
|
6
33
|
@content;
|
|
@@ -1,8 +1,31 @@
|
|
|
1
1
|
@charset "UTF-8";
|
|
2
2
|
|
|
3
|
+
@use "sass:list";
|
|
4
|
+
@use "sass:meta";
|
|
5
|
+
@use "sass:string";
|
|
6
|
+
|
|
7
|
+
// An image path goes into url(). A var(), url(), image-set() or gradient is
|
|
8
|
+
// already an image, and wrapping it produced url(var(--x)), which loads nothing.
|
|
9
|
+
@function -image($value) {
|
|
10
|
+
@if -is-css-function($value, "var" "url" "image-set" "linear-gradient" "radial-gradient" "conic-gradient" "repeating-linear-gradient" "repeating-radial-gradient" "repeating-conic-gradient") {
|
|
11
|
+
@return $value;
|
|
12
|
+
}
|
|
13
|
+
@return url($value);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// True for an unquoted call to one of $names, such as var(--x). A quoted string
|
|
17
|
+
// stays text, so `"var(--x)"` is not a call.
|
|
18
|
+
@function -is-css-function($value, $names) {
|
|
19
|
+
@if meta.type-of($value) != "string" or string.slice(meta.inspect($value), 1, 1) == "\"" {
|
|
20
|
+
@return false;
|
|
21
|
+
}
|
|
22
|
+
$open: string.index($value, "(");
|
|
23
|
+
@return $open != null and list.index($names, string.to-lower-case(string.slice($value, 1, $open - 1))) != null;
|
|
24
|
+
}
|
|
25
|
+
|
|
3
26
|
@mixin text-image($image: null) {
|
|
4
27
|
@if $image {
|
|
5
|
-
background-image:
|
|
28
|
+
background-image: -image($image);
|
|
6
29
|
}
|
|
7
30
|
background-size: cover;
|
|
8
31
|
background-position: center;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
@charset "UTF-8";
|
|
2
|
+
|
|
3
|
+
@use "sass:list";
|
|
4
|
+
@use "sass:meta";
|
|
5
|
+
@use "sass:string";
|
|
6
|
+
|
|
7
|
+
// Characters a custom property name cannot hold. Measured in Chrome 152: a
|
|
8
|
+
// declaration named `--space-0.5`, `--w-1/2`, `--a b` or `--#fff` was dropped,
|
|
9
|
+
// and `--font:body` became a property called `--font`. Letters, digits,
|
|
10
|
+
// hyphens, underscores and non-ASCII letters all survived.
|
|
11
|
+
$-forbidden: " " "." "," "/" "#" ":" ";" "(" ")" "[" "]" "{" "}" "!" "@" "$" "%" "^" "&" "*" "+" "=" "<" ">" "?" "\\" "|" "~" "`" "'" "\"";
|
|
12
|
+
|
|
13
|
+
@function -forbidden-in($text) {
|
|
14
|
+
@each $character in $-forbidden {
|
|
15
|
+
@if string.index($text, $character) {
|
|
16
|
+
@return $character;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
@return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Writes a value the way Sass writes it in an ordinary declaration. A custom
|
|
23
|
+
// property value is interpolated, and neither obvious spelling matches that:
|
|
24
|
+
// plain interpolation drops the quotes of a string, and meta.inspect keeps them
|
|
25
|
+
// but prints numbers at full precision, so oklch(0.637 0.237 25.331) came out
|
|
26
|
+
// with a hue of 25.331000000000017deg and math.div(1, 3) as 0.3333333333333333.
|
|
27
|
+
// So only strings go through meta.inspect, and a list is walked into.
|
|
28
|
+
@function -serialize($value) {
|
|
29
|
+
@if meta.type-of($value) == "string" {
|
|
30
|
+
@return meta.inspect($value);
|
|
31
|
+
}
|
|
32
|
+
@if meta.type-of($value) == "list" or meta.type-of($value) == "arglist" {
|
|
33
|
+
$joiner: " ";
|
|
34
|
+
@if list.separator($value) == "comma" {
|
|
35
|
+
$joiner: ", ";
|
|
36
|
+
} @else if list.separator($value) == "slash" {
|
|
37
|
+
$joiner: " / ";
|
|
38
|
+
}
|
|
39
|
+
$out: "";
|
|
40
|
+
@each $item in $value {
|
|
41
|
+
@if $out != "" {
|
|
42
|
+
$out: $out + $joiner;
|
|
43
|
+
}
|
|
44
|
+
$out: $out + -serialize($item);
|
|
45
|
+
}
|
|
46
|
+
@if list.is-bracketed($value) {
|
|
47
|
+
$out: "[" + $out + "]";
|
|
48
|
+
}
|
|
49
|
+
@return $out;
|
|
50
|
+
}
|
|
51
|
+
@return "#{$value}";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Writes a Sass map out as custom properties, which two agent trials wrote by
|
|
55
|
+
// hand for a light and a dark palette. Three things it does that interpolating
|
|
56
|
+
// the map in a loop does not:
|
|
57
|
+
//
|
|
58
|
+
// A null is left out. `--focus: null` is a value, not an absence: measured in
|
|
59
|
+
// Chrome 152, `color: var(--focus, red)` did not fall back to red.
|
|
60
|
+
//
|
|
61
|
+
// A quoted string keeps its quotes, through -serialize above. With plain
|
|
62
|
+
// interpolation `"→"` became `→`, and `content: var(--arrow)` then computed to
|
|
63
|
+
// none.
|
|
64
|
+
//
|
|
65
|
+
// A nested map is refused rather than flattened, so a later version can decide
|
|
66
|
+
// how to flatten it without changing what a call already produces.
|
|
67
|
+
@mixin tokens($map, $prefix: null) {
|
|
68
|
+
@if not & {
|
|
69
|
+
@error "`tokens` writes custom properties into the rule it is called in, so call it inside a selector, such as `:root { @include tokens((bg: #fff), color); }`.";
|
|
70
|
+
}
|
|
71
|
+
@if meta.type-of($map) != "map" {
|
|
72
|
+
@error "`#{meta.inspect($map)}` is not a valid $map for `tokens`. Pass a map of names to values, such as `(bg: #fff, text: #111)`.";
|
|
73
|
+
}
|
|
74
|
+
// Checked by its characters, not its type, like the names below: Sass reads
|
|
75
|
+
// an unquoted `blue` or `red` as a colour, and `tokens($blue, blue)` has to
|
|
76
|
+
// work. A colour written `#00f` or `rgb()` is still caught by `#` and `(`.
|
|
77
|
+
@if $prefix != null {
|
|
78
|
+
@if not list.index("string" "color" "number", meta.type-of($prefix)) or string.length("#{$prefix}") == 0 {
|
|
79
|
+
@error "`#{meta.inspect($prefix)}` is not a valid $prefix for `tokens`. Pass a name, such as `color`, or leave it out.";
|
|
80
|
+
}
|
|
81
|
+
$character: -forbidden-in("#{$prefix}");
|
|
82
|
+
@if $character {
|
|
83
|
+
@error "`#{$prefix}` is not a valid $prefix for `tokens`: a custom property name cannot contain `#{$character}`. Use letters, digits, hyphens and underscores.";
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@each $name, $value in $map {
|
|
88
|
+
$text: "#{$name}";
|
|
89
|
+
@if string.length($text) == 0 {
|
|
90
|
+
@error "`#{meta.inspect($name)}` is not a valid token name for `tokens`. Pass a name such as `bg` or `500`.";
|
|
91
|
+
}
|
|
92
|
+
$character: -forbidden-in($text);
|
|
93
|
+
@if $character {
|
|
94
|
+
@error "`#{$text}` is not a valid token name for `tokens`: a custom property name cannot contain `#{$character}`. Use letters, digits, hyphens and underscores, such as `space-0_5`.";
|
|
95
|
+
}
|
|
96
|
+
@if meta.type-of($value) == "map" {
|
|
97
|
+
@error "`#{$text}` holds a map, which `tokens` does not flatten. Pass each group on its own, such as `@include tokens($blue, blue)` for `$blue: (500: #3b82f6)`.";
|
|
98
|
+
}
|
|
99
|
+
@if meta.type-of($value) == "list" and list.length($value) == 0 {
|
|
100
|
+
@error "`#{$text}` has an empty value in `tokens`. Pass a value, or null to leave the property out.";
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@if $value != null {
|
|
104
|
+
$property: --#{$text};
|
|
105
|
+
@if $prefix {
|
|
106
|
+
$property: --#{$prefix}-#{$text};
|
|
107
|
+
}
|
|
108
|
+
#{$property}: #{-serialize($value)};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -1,13 +1,82 @@
|
|
|
1
1
|
@charset "UTF-8";
|
|
2
2
|
|
|
3
3
|
@use "sass:list";
|
|
4
|
-
@use "sass:
|
|
4
|
+
@use "sass:meta";
|
|
5
|
+
@use "sass:string";
|
|
5
6
|
@use "../lists/list-of-directions" as *;
|
|
6
|
-
|
|
7
|
+
|
|
8
|
+
// A size Sass can use inside calc(): a number, a calculation such as calc(),
|
|
9
|
+
// min() or clamp(), or var() and env(), which only the browser can resolve.
|
|
10
|
+
// Anything else would pass silently through calc(): `calc(nonsense * 5)`
|
|
11
|
+
// compiles.
|
|
12
|
+
@function -is-size($value) {
|
|
13
|
+
@if meta.type-of($value) == "number" or meta.type-of($value) == "calculation" {
|
|
14
|
+
@return true;
|
|
15
|
+
}
|
|
16
|
+
@if meta.type-of($value) == "string" and string.slice(meta.inspect($value), 1, 1) != "\"" {
|
|
17
|
+
$open: string.index($value, "(");
|
|
18
|
+
@return $open != null and list.index("var" "env", string.to-lower-case(string.slice($value, 1, $open - 1))) != null;
|
|
19
|
+
}
|
|
20
|
+
@return false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// A value CSS will take as a colour: a Sass colour, currentColor, or a function
|
|
24
|
+
// that returns a colour when the browser resolves it. A list passes when every
|
|
25
|
+
// item does, as it did through isColor.
|
|
26
|
+
//
|
|
27
|
+
// inherit, initial, unset and revert are left out on purpose. The mixin writes
|
|
28
|
+
// the colour into a border-color shorthand beside `transparent`, and a CSS-wide
|
|
29
|
+
// keyword is only valid as the whole value: `transparent transparent inherit`
|
|
30
|
+
// is rejected by the browser. Checked with CSS.supports in Chrome 152.
|
|
31
|
+
//
|
|
32
|
+
// Deliberately looser than isColor, which stays strict because tint and shade
|
|
33
|
+
// pass its result to color.mix, and that needs a real Sass colour. A border
|
|
34
|
+
// colour does not. The functions are named rather than "anything with a
|
|
35
|
+
// parenthesis", which would let url() through as a colour.
|
|
36
|
+
@function -is-css-color($value) {
|
|
37
|
+
@if list.length($value) > 1 {
|
|
38
|
+
@each $item in $value {
|
|
39
|
+
@if not -is-css-color($item) {
|
|
40
|
+
@return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
@return true;
|
|
44
|
+
}
|
|
45
|
+
@if meta.type-of($value) == "color" {
|
|
46
|
+
@return true;
|
|
47
|
+
}
|
|
48
|
+
@if meta.type-of($value) == "string" and string.slice(meta.inspect($value), 1, 1) != "\"" {
|
|
49
|
+
$text: string.to-lower-case($value);
|
|
50
|
+
@if $text == "currentcolor" {
|
|
51
|
+
@return true;
|
|
52
|
+
}
|
|
53
|
+
$open: string.index($text, "(");
|
|
54
|
+
@if $open and list.index("var" "color-mix" "light-dark" "env", string.slice($text, 1, $open - 1)) {
|
|
55
|
+
@return true;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
@return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// The logical directions point along the writing direction rather than a fixed
|
|
62
|
+
// side: inline-end is right in a left-to-right page and left in a right-to-left
|
|
63
|
+
// one. They are kept in a list of their own on purpose. $list-of-directions is
|
|
64
|
+
// shared with border-radius, whose corner branches have no final @else, so a
|
|
65
|
+
// keyword added there without a branch would be accepted and emit nothing.
|
|
66
|
+
$-logical-directions: ("inline-start", "inline-end", "block-start", "block-end");
|
|
7
67
|
|
|
8
68
|
@mixin triangle($direction: "bottom", $color: black, $size: 10px 8px) {
|
|
9
|
-
@if list.index($list-of-directions, $direction) {
|
|
10
|
-
|
|
69
|
+
@if list.index($list-of-directions, $direction) or list.index($-logical-directions, $direction) {
|
|
70
|
+
// Halving a size in calc() rather than math.div() keeps var() and clamp()
|
|
71
|
+
// working instead of emitting `var(--w)/2`, which is not valid CSS.
|
|
72
|
+
@each $item in $size {
|
|
73
|
+
@if not -is-size($item) {
|
|
74
|
+
@error "`#{meta.inspect($size)}` is not a valid $size for `triangle`. Pass one or two lengths, or var(), env() or a calculation in place of either.";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
@if not -is-css-color($color) {
|
|
78
|
+
@error "`#{meta.inspect($color)}` is not a valid $color for `triangle`. Pass a colour, `currentColor`, or a CSS function such as `var(--accent)`, `color-mix(...)` or `light-dark(...)`.";
|
|
79
|
+
} @else {
|
|
11
80
|
content: "";
|
|
12
81
|
height: 0;
|
|
13
82
|
width: 0;
|
|
@@ -15,32 +84,63 @@
|
|
|
15
84
|
border-style: solid;
|
|
16
85
|
@if $direction == "top"{
|
|
17
86
|
border-color: transparent transparent $color;
|
|
18
|
-
border-width: 0
|
|
87
|
+
border-width: 0 calc(list.nth($size, 1) / 2) if(list.length($size) == 2, list.nth($size, 2), list.nth($size, 1));
|
|
19
88
|
} @else if $direction == "top-right" {
|
|
20
89
|
border-color: transparent $color transparent transparent;
|
|
21
90
|
border-width: 0 list.nth($size, 1) list.nth($size, 1) 0;
|
|
22
91
|
} @else if $direction == "right" {
|
|
23
92
|
border-color: transparent transparent transparent $color;
|
|
24
|
-
border-width:
|
|
93
|
+
border-width: calc(if(list.length($size) == 2, list.nth($size, 2), list.nth($size, 1)) / 2) 0 calc(if(list.length($size) == 2, list.nth($size, 2), list.nth($size, 1)) / 2) list.nth($size, 1);
|
|
25
94
|
} @else if $direction == "bottom-right" {
|
|
26
95
|
border-color: transparent transparent $color;
|
|
27
96
|
border-width: 0 0 list.nth($size, 1) list.nth($size, 1);
|
|
28
97
|
} @else if $direction == "bottom" {
|
|
29
98
|
border-color: $color transparent transparent;
|
|
30
|
-
border-width: if(list.length($size) == 2, list.nth($size, 2), list.nth($size, 1))
|
|
99
|
+
border-width: if(list.length($size) == 2, list.nth($size, 2), list.nth($size, 1)) calc(list.nth($size, 1) / 2) 0;
|
|
31
100
|
} @else if $direction == "bottom-left" {
|
|
32
101
|
border-color: transparent transparent transparent $color;
|
|
33
102
|
border-width: list.nth($size, 1) 0 0 list.nth($size, 1);
|
|
34
103
|
} @else if $direction == "left" {
|
|
35
104
|
border-color: transparent $color transparent transparent;
|
|
36
|
-
border-width:
|
|
105
|
+
border-width: calc(if(list.length($size) == 2, list.nth($size, 2), list.nth($size, 1)) / 2) list.nth($size, 1) calc(if(list.length($size) == 2, list.nth($size, 2), list.nth($size, 1)) / 2) 0;
|
|
37
106
|
} @else if $direction == "top-left" {
|
|
38
107
|
border-color: $color transparent transparent;
|
|
39
108
|
border-width: list.nth($size, 1) list.nth($size, 1) 0 0;
|
|
109
|
+
} @else {
|
|
110
|
+
// The same shapes as right, left, bottom and top, drawn with logical
|
|
111
|
+
// borders so the browser picks the side. The first size is the length
|
|
112
|
+
// the triangle points along, the second the width across it.
|
|
113
|
+
$along: list.nth($size, 1);
|
|
114
|
+
$across: $along;
|
|
115
|
+
@if list.length($size) == 2 {
|
|
116
|
+
$across: list.nth($size, 2);
|
|
117
|
+
}
|
|
118
|
+
border-color: transparent;
|
|
119
|
+
@if $direction == "inline-end" {
|
|
120
|
+
border-inline-start-color: $color;
|
|
121
|
+
border-block-width: calc($across / 2);
|
|
122
|
+
border-inline-start-width: $along;
|
|
123
|
+
border-inline-end-width: 0;
|
|
124
|
+
} @else if $direction == "inline-start" {
|
|
125
|
+
border-inline-end-color: $color;
|
|
126
|
+
border-block-width: calc($across / 2);
|
|
127
|
+
border-inline-start-width: 0;
|
|
128
|
+
border-inline-end-width: $along;
|
|
129
|
+
} @else if $direction == "block-end" {
|
|
130
|
+
border-block-start-color: $color;
|
|
131
|
+
border-inline-width: calc($along / 2);
|
|
132
|
+
border-block-start-width: $across;
|
|
133
|
+
border-block-end-width: 0;
|
|
134
|
+
} @else {
|
|
135
|
+
border-block-end-color: $color;
|
|
136
|
+
border-inline-width: calc($along / 2);
|
|
137
|
+
border-block-start-width: 0;
|
|
138
|
+
border-block-end-width: $across;
|
|
139
|
+
}
|
|
40
140
|
}
|
|
41
141
|
@content;
|
|
42
142
|
}
|
|
43
143
|
} @else {
|
|
44
|
-
@error "The argument for direction must be one of the followings: #{$list-of-directions}";
|
|
144
|
+
@error "The argument for direction must be one of the followings: #{list.join($list-of-directions, $-logical-directions)}";
|
|
45
145
|
}
|
|
46
146
|
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
@charset "UTF-8";
|
|
2
2
|
|
|
3
3
|
@use "sass:math";
|
|
4
|
+
@use "sass:meta";
|
|
4
5
|
|
|
5
6
|
@function clearUnit($value) {
|
|
7
|
+
@if meta.type-of($value) != "number" {
|
|
8
|
+
@error "`#{meta.inspect($value)}` is not a valid $value for `clearUnit`. Pass a number, such as `24px`.";
|
|
9
|
+
}
|
|
6
10
|
@return math.div($value, ($value * 0 + 1));
|
|
7
11
|
}
|
|
@@ -2,8 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
@use "sass:string";
|
|
4
4
|
@use "sass:math";
|
|
5
|
+
@use "sass:meta";
|
|
5
6
|
|
|
6
7
|
@function convertToEm($value) {
|
|
8
|
+
// Only a pixel length converts cleanly. A unitless number or another unit
|
|
9
|
+
// compiled to `calc(1.5 / 1px)em`, and a word or var() to `var(--x)/16pxem`.
|
|
10
|
+
@if meta.type-of($value) != "number" or math.unit($value) != "px" {
|
|
11
|
+
@error "`#{meta.inspect($value)}` is not a valid $value for `convertToEm`. Pass a pixel length, such as `24px`.";
|
|
12
|
+
}
|
|
7
13
|
$base-value: 16px;
|
|
8
14
|
$rem-value: math.div($value, $base-value);
|
|
9
15
|
@return $rem-value + string.unquote("em");
|
|
@@ -11,6 +11,21 @@
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
$string-numerals: "0" "1" "2" "3" "4" "5" "6" "7" "8" "9";
|
|
14
|
+
|
|
15
|
+
// Every character has to be a digit before the parse runs. Otherwise an
|
|
16
|
+
// empty string or a sign failed with Sass's own "Undefined operation", and a
|
|
17
|
+
// letter or a space was folded into nonsense such as `40-1`. The empty case
|
|
18
|
+
// is checked first because `@for $i from 1 through 0` counts down, not zero
|
|
19
|
+
// times.
|
|
20
|
+
@if string.length($value) == 0 {
|
|
21
|
+
@error "`\"\"` is not a valid $value for `convertToNumber`. Pass a string made only of digits, such as `\"42\"`.";
|
|
22
|
+
}
|
|
23
|
+
@for $i from 1 through string.length($value) {
|
|
24
|
+
@if not list.index($string-numerals, string.slice($value, $i, $i)) {
|
|
25
|
+
@error "`#{meta.inspect($value)}` is not a valid $value for `convertToNumber`. Pass a string made only of digits, such as `\"42\"`.";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
14
29
|
$calc: 0;
|
|
15
30
|
|
|
16
31
|
@for $i from 1 through string.length($value) {
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
@charset "UTF-8";
|
|
2
2
|
|
|
3
|
+
@use "sass:math";
|
|
4
|
+
@use "sass:meta";
|
|
5
|
+
|
|
3
6
|
@function fontSizer($size, $time) {
|
|
7
|
+
@if meta.type-of($size) != "number" {
|
|
8
|
+
@error "`#{meta.inspect($size)}` is not a valid $size for `fontSizer`. Pass a length, such as `16px`.";
|
|
9
|
+
}
|
|
10
|
+
@if meta.type-of($time) != "number" {
|
|
11
|
+
@error "`#{meta.inspect($time)}` is not a valid $time for `fontSizer`. Pass a multiplier, such as `1.5`.";
|
|
12
|
+
}
|
|
13
|
+
// Two units multiply into a unit squared, which Sass writes as
|
|
14
|
+
// `calc(16px * 1px)`: not a length anywhere in CSS.
|
|
15
|
+
@if not math.is-unitless($size) and not math.is-unitless($time) {
|
|
16
|
+
@error "`fontSizer(#{$size}, #{$time})` multiplies two units, which is not a length. Pass the multiplier without a unit, such as `1.5`.";
|
|
17
|
+
}
|
|
4
18
|
@return $size * $time;
|
|
5
19
|
}
|