gerillass 2.3.0 → 2.3.1

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.
Files changed (48) hide show
  1. package/SKILL.md +17 -1
  2. package/gerillass.json +287 -68
  3. package/package.json +1 -1
  4. package/scss/internal/_color-problem.scss +99 -0
  5. package/scss/internal/_color-stops-problem.scss +94 -0
  6. package/scss/internal/_condition-width.scss +22 -0
  7. package/scss/internal/_custom-property-in.scss +25 -0
  8. package/scss/internal/_image-value.scss +68 -0
  9. package/scss/internal/_is-condition-value.scss +94 -0
  10. package/scss/internal/_is-css-function.scss +21 -0
  11. package/scss/internal/_is-size.scss +22 -0
  12. package/scss/internal/_keyword-value.scss +76 -0
  13. package/scss/internal/_length-problem.scss +127 -0
  14. package/scss/internal/_sibling-index.scss +51 -0
  15. package/scss/internal/_words.scss +35 -0
  16. package/scss/library/_adaptive.scss +17 -0
  17. package/scss/library/_after.scss +2 -12
  18. package/scss/library/_background-dots.scss +29 -37
  19. package/scss/library/_background-image.scss +37 -25
  20. package/scss/library/_background-stripes.scss +34 -37
  21. package/scss/library/_before.scss +2 -12
  22. package/scss/library/_border-radius.scss +83 -0
  23. package/scss/library/_brand-logo.scss +10 -24
  24. package/scss/library/_breakpoint.scss +16 -23
  25. package/scss/library/_circle.scss +7 -0
  26. package/scss/library/_columnizer.scss +62 -2
  27. package/scss/library/_container-query.scss +11 -27
  28. package/scss/library/_container.scss +2 -19
  29. package/scss/library/_counter.scss +3 -14
  30. package/scss/library/_ellipsis.scss +78 -1
  31. package/scss/library/_except.scss +15 -0
  32. package/scss/library/_focus-ring.scss +29 -0
  33. package/scss/library/_font-face.scss +117 -1
  34. package/scss/library/_line-clamp.scss +2 -13
  35. package/scss/library/_linear-gradient.scss +7 -0
  36. package/scss/library/_only.scss +16 -0
  37. package/scss/library/_position.scss +34 -3
  38. package/scss/library/_radial-gradient.scss +154 -0
  39. package/scss/library/_resizable.scss +29 -2
  40. package/scss/library/_screen-agent.scss +8 -22
  41. package/scss/library/_sizer.scss +9 -0
  42. package/scss/library/_sprite.scss +65 -0
  43. package/scss/library/_text-gradient.scss +8 -0
  44. package/scss/library/_text-image.scss +2 -23
  45. package/scss/library/_text-stroke.scss +30 -0
  46. package/scss/library/_triangle.scss +25 -56
  47. package/scss/utilities/_validate-breakpoint.scss +32 -6
  48. package/scss/utilities/_validate-scissors.scss +7 -0
@@ -0,0 +1,127 @@
1
+ @charset "UTF-8";
2
+
3
+ @use "sass:list";
4
+ @use "sass:math";
5
+ @use "sass:meta";
6
+ @use "sass:string";
7
+ @use "is-condition-value" as *;
8
+
9
+ // Shared by the mixins and not public API; see _is-css-function.scss.
10
+
11
+ // Maths functions a browser resolves to a length, and the substitutions whose
12
+ // value only the browser knows. Sass reduces most maths on plain numbers before
13
+ // they get here (`round(10.5px, 1px)` arrives as 11px), so these are the forms
14
+ // it leaves as text.
15
+ $-functions: "var" "env" "attr" "calc" "clamp" "min" "max" "round" "abs" "sign" "mod" "rem" "hypot" "sqrt" "pow" "exp" "log";
16
+ $-css-wide: "inherit" "initial" "unset" "revert" "revert-layer";
17
+
18
+ // Returns what is wrong with $value as one length for a property, or null when
19
+ // a browser keeps it. Each caller passes what its property takes, measured in
20
+ // Chrome 152 (see todos/silent-values-plan.md, S7):
21
+ //
22
+ // - $percent: whether a percentage works (width yes, outline-width no);
23
+ // - $negative: whether a negative length works (outline-offset yes, width no);
24
+ // - $keywords: the words the property takes, such as auto or thin;
25
+ // - $functions: sizing functions beyond maths, such as anchor-size;
26
+ // - $css-wide: whether a CSS-wide keyword works, which is only as a whole
27
+ // declaration;
28
+ // - $quoted: whether a quoted length works, which is only where the mixin
29
+ // interpolates it; written raw, `width: "10px"` is dropped.
30
+ //
31
+ // A unitless 0 is accepted; a caller whose output breaks on it handles it.
32
+ // A calculation is taken on trust, since Sass keeps it only when it cannot
33
+ // reduce it.
34
+ @function lengthProblem($value, $percent: true, $negative: true, $keywords: (), $functions: (), $css-wide: false, $quoted: false) {
35
+ @if meta.type-of($value) == "calculation" {
36
+ @return null;
37
+ }
38
+ @if meta.type-of($value) == "list" and list.length($value) > 1 {
39
+ @return "it is several values where one length is meant";
40
+ }
41
+ @if meta.type-of($value) == "number" {
42
+ @if math.is-unitless($value) {
43
+ @if $value == 0 {
44
+ @return null;
45
+ }
46
+ @return "a unitless number is not a length; add a unit, such as `#{$value}px`";
47
+ }
48
+ @if math.unit($value) == "%" and not $percent {
49
+ @return "a percentage does not work here";
50
+ }
51
+ @if not isConditionValue($value, "length-percentage") {
52
+ @return "`#{math.unit($value)}` is not a length unit";
53
+ }
54
+ @if not $negative and $value < 0 {
55
+ @return "a length below zero does not work here";
56
+ }
57
+ @return null;
58
+ }
59
+ @if meta.type-of($value) != "string" {
60
+ @return "it is not a length";
61
+ }
62
+ $kind: "length";
63
+ @if $percent {
64
+ $kind: "length-percentage";
65
+ }
66
+ @if string.slice(meta.inspect($value), 1, 1) == "\"" {
67
+ @if $quoted and isConditionValue($value, $kind) {
68
+ @return null;
69
+ }
70
+ @return "a length in quotes is text here; write it without quotes";
71
+ }
72
+ $lower: string.to-lower-case($value);
73
+ $open: string.index($lower, "(");
74
+ @if $open {
75
+ $name: string.slice($lower, 1, $open - 1);
76
+ @if list.index($-functions, $name) or list.index($functions, $name) {
77
+ @return null;
78
+ }
79
+ @return "`#{$name}()` does not give a length here";
80
+ }
81
+ @if string.index($lower, " ") {
82
+ @return "it is several values where one length is meant";
83
+ }
84
+ // Interpolation, such as `#{$n}px`, gives an unquoted string rather than a
85
+ // number, and it is written into the CSS as it reads, so its content decides.
86
+ @if isConditionValue($value, $kind) {
87
+ @if not $negative and string.index($lower, "-") == 1 {
88
+ @return "a length below zero does not work here";
89
+ }
90
+ @return null;
91
+ }
92
+ @if string.index($lower, "%") and isConditionValue($value, "length-percentage") {
93
+ @return "a percentage does not work here";
94
+ }
95
+ @if list.index($keywords, $lower) {
96
+ @return null;
97
+ }
98
+ @if list.index($-css-wide, $lower) {
99
+ @if $css-wide {
100
+ @return null;
101
+ }
102
+ @return "a CSS-wide keyword only works as a whole declaration, not here";
103
+ }
104
+ @if list.length($keywords) > 0 {
105
+ @return "it is not a length or one of: #{list.join($keywords, (), $separator: comma)}";
106
+ }
107
+ @return "it is not a length";
108
+ }
109
+
110
+ // lengthProblem for a width or height, and for max-width with $max. Measured in
111
+ // Chrome 152: width and height keep the intrinsic keywords, stretch,
112
+ // -webkit-fill-available, anchor-size(), calc-size() and a CSS-wide keyword;
113
+ // max-width keeps none where width keeps auto, and drops calc-size(). Both drop
114
+ // a negative length. -moz-available is Firefox's spelling of stretch; Chrome
115
+ // drops it, and it is let through like the other vendor keywords.
116
+ //
117
+ // null is accepted, since Sass leaves the declaration out, as it always has.
118
+ @function sizeProblem($value, $max: false) {
119
+ @if $value == null {
120
+ @return null;
121
+ }
122
+ $keywords: "min-content" "max-content" "fit-content" "stretch" "-webkit-fill-available" "-moz-available";
123
+ @if $max {
124
+ @return lengthProblem($value, $negative: false, $keywords: list.append($keywords, "none"), $functions: ("anchor-size",), $css-wide: true);
125
+ }
126
+ @return lengthProblem($value, $negative: false, $keywords: list.append($keywords, "auto"), $functions: "anchor-size" "calc-size", $css-wide: true);
127
+ }
@@ -0,0 +1,51 @@
1
+ @charset "UTF-8";
2
+
3
+ @use "sass:math";
4
+ @use "sass:meta";
5
+
6
+ // Shared by the mixins and not public API; see _is-css-function.scss.
7
+
8
+ // Returns what is wrong with $value as a sibling position for `only` or
9
+ // `except`, or null when it is a whole number other than 0. Measured in Chrome
10
+ // 152 on six siblings:
11
+ //
12
+ // - a number with any unit or a fraction, `:nth-of-type(10deg)`,
13
+ // `:nth-of-type(10px)`, `:nth-of-type(1.5)`, is an invalid selector, and the
14
+ // browser drops the whole rule. In a selector list that takes every other
15
+ // selector with it: `.a:nth-of-type(1), .a:nth-of-type(10deg)` was dropped.
16
+ // - `:nth-of-type(0)` is valid and matched none of the six, and
17
+ // `:not(:nth-of-type(0))` matched all six, since positions count from 1.
18
+ // - a negative number, `+3` and `99999` were kept; `only` and `except` turn a
19
+ // negative position into :nth-last-of-type.
20
+ //
21
+ // $several is true when the value is one of several arguments, where only
22
+ // positions are taken; alone, a word is a keyword or a selector.
23
+ @function siblingIndexProblem($value, $mixin, $several: false) {
24
+ @if meta.type-of($value) != "number" {
25
+ @if $several {
26
+ @return "several arguments are all positions, whole numbers such as `1` or `-2`. Pass a keyword such as `first`, or a selector such as `.foo`, on its own";
27
+ }
28
+ @return null;
29
+ }
30
+ // A multiple of n, such as `3n`, reaches here as a number whose unit is n.
31
+ // Measured in Chrome 152, :nth-of-type(3n) is valid and matched two of six
32
+ // siblings, and main compiled only(3n) to it, so it is accepted. Its
33
+ // coefficient is checked like a position: `1.5n` was dropped and `0n`
34
+ // matched nothing.
35
+ $coefficient: $value;
36
+ @if math.unit($value) == "n" {
37
+ $coefficient: math.div($value, 1n);
38
+ } @else if not math.is-unitless($value) {
39
+ @return "a position is a whole number such as `2`, or a multiple of n such as `3n`, and `:nth-of-type(#{math.abs($value)})` is an invalid selector, so the browser would drop the rule. Pass a number such as `2`, or `-2` to count from the end";
40
+ }
41
+ @if math.round($coefficient) != $coefficient {
42
+ @return "a position is a whole number, and `:nth-of-type(#{math.abs($value)})` is an invalid selector, so the browser would drop the rule. Pass a number such as `2`, or `-2` to count from the end";
43
+ }
44
+ @if $coefficient == 0 {
45
+ @if $mixin == "except" {
46
+ @return "positions count from 1, so `:not(:nth-of-type(0))` excludes nothing. Pass `1` for the first, or `-1` for the last";
47
+ }
48
+ @return "positions count from 1, so `:nth-of-type(0)` matches nothing. Pass `1` for the first, or `-1` for the last";
49
+ }
50
+ @return null;
51
+ }
@@ -0,0 +1,35 @@
1
+ @charset "UTF-8";
2
+
3
+ @use "sass:list";
4
+ @use "sass:meta";
5
+ @use "sass:string";
6
+
7
+ // Shared by the mixins and not public API; see _is-css-function.scss.
8
+
9
+ // The words of a value, as a space-separated list. A quoted string such as
10
+ // "block flow-root" is split on its spaces, the way the browser reads it once
11
+ // the quotes are gone; a space-separated list gives its items; anything else is
12
+ // one word. Empty words from repeated spaces are skipped.
13
+ @function wordsOf($value) {
14
+ @if meta.type-of($value) == "list" and list.separator($value) != "comma" {
15
+ @return $value;
16
+ }
17
+ @if meta.type-of($value) != "string" {
18
+ @return ($value,);
19
+ }
20
+ $words: ();
21
+ $rest: $value;
22
+ $at: string.index($rest, " ");
23
+ @while $at {
24
+ $word: string.slice($rest, 1, $at - 1);
25
+ @if string.length($word) > 0 {
26
+ $words: list.append($words, $word);
27
+ }
28
+ $rest: string.slice($rest, $at + 1);
29
+ $at: string.index($rest, " ");
30
+ }
31
+ @if string.length($rest) > 0 {
32
+ $words: list.append($words, $rest);
33
+ }
34
+ @return $words;
35
+ }
@@ -1,9 +1,26 @@
1
1
  @charset "UTF-8";
2
2
 
3
3
  @use "sass:map";
4
+ @use "sass:math";
5
+ @use "sass:meta";
6
+ @use "sass:string";
4
7
  @use "../maps/map-for-breakpoints" as *;
8
+ @use "../internal/length-problem" as *;
5
9
 
6
10
  @mixin adaptive($gutter: 30px) {
11
+ // The gutter is subtracted twice inside calc(), which in Chrome 152 kept a
12
+ // length, a percentage, a negative length and var(), and dropped the
13
+ // max-width for a word, a CSS-wide keyword, a unitless number and a unit that
14
+ // is not a length. A quoted length works, since it is interpolated.
15
+ $problem: lengthProblem($gutter, $functions: ("anchor-size",), $quoted: true);
16
+ @if $problem {
17
+ @error "`#{meta.inspect($gutter)}` is not a valid $gutter for `adaptive`: #{$problem}. A browser drops the max-width at every breakpoint.";
18
+ }
19
+ // A unitless 0 made `calc(576px - 0 * 2)`, a length minus a number, which was
20
+ // dropped; 0px is kept.
21
+ @if (meta.type-of($gutter) == "number" and math.is-unitless($gutter)) or (meta.type-of($gutter) == "string" and string.unquote($gutter) == string.unquote("0")) {
22
+ $gutter: 0px;
23
+ }
7
24
  $actual-breakpoints: map.remove($map-for-breakpoints, "xsmall");
8
25
  margin: 0 auto;
9
26
  @each $item, $value in $actual-breakpoints {
@@ -1,18 +1,8 @@
1
1
  @charset "UTF-8";
2
2
 
3
- @use "sass:list";
4
3
  @use "sass:meta";
5
4
  @use "sass:string";
6
-
7
- // True for an unquoted call to one of $names, such as var(--x). A quoted string
8
- // stays text, so `"var(--x)"` is not a call.
9
- @function -is-css-function($value, $names) {
10
- @if meta.type-of($value) != "string" or string.slice(meta.inspect($value), 1, 1) == "\"" {
11
- @return false;
12
- }
13
- $open: string.index($value, "(");
14
- @return $open != null and list.index($names, string.to-lower-case(string.slice($value, 1, $open - 1))) != null;
15
- }
5
+ @use "../internal/is-css-function" as *;
16
6
 
17
7
  @mixin after($content: null) {
18
8
  &::after {
@@ -20,7 +10,7 @@
20
10
  @content;
21
11
  } @else if meta.type-of($content) != "string" {
22
12
  @error "`#{$content}` is not a valid $content for `after`. Pass a string to use as the content value, a data- attribute name such as `\"data-label\"` to render it with attr(), or no argument at all and write the content yourself.";
23
- } @else if -is-css-function($content, "var" "attr" "counter" "counters") {
13
+ } @else if isCssFunction($content, "var" "attr" "counter" "counters") {
24
14
  // Quoting would turn the call into text: content: "var(--label)".
25
15
  content: $content;
26
16
  @content;
@@ -3,40 +3,10 @@
3
3
  @use "sass:list";
4
4
  @use "sass:meta";
5
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(string.unquote($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
-
26
- // A size Sass can use inside calc(): a number, a calculation such as calc(),
27
- // min() or clamp(), or var() and env(), which only the browser can resolve.
28
- // Anything else would pass silently through calc(): `calc(nonsense * 5)`
29
- // compiles.
30
- @function -is-size($value) {
31
- @if meta.type-of($value) == "number" or meta.type-of($value) == "calculation" {
32
- @return true;
33
- }
34
- @if meta.type-of($value) == "string" and string.slice(meta.inspect($value), 1, 1) != "\"" {
35
- $open: string.index($value, "(");
36
- @return $open != null and list.index("var" "env", string.to-lower-case(string.slice($value, 1, $open - 1))) != null;
37
- }
38
- @return false;
39
- }
6
+ @use "../internal/is-size" as *;
7
+ @use "../internal/image-value" as *;
8
+ @use "../internal/color-problem" as *;
9
+ @use "../internal/length-problem" as *;
40
10
 
41
11
  @mixin background-dots(
42
12
  $color: null,
@@ -49,21 +19,43 @@
49
19
  // the signature lets $size be checked first, and doing the arithmetic in
50
20
  // calc() lets var() and clamp() through: Sass simplifies calc() on plain
51
21
  // numbers back to a number, so a call with lengths emits the same CSS.
52
- @if not -is-size($size) {
22
+ @if not isSize($size) {
53
23
  @error "`#{meta.inspect($size)}` is not a valid $size for `background-dots`. Pass a length, or var(), env() or a calculation such as `clamp()`.";
54
24
  }
55
25
  @if $gutter == null {
56
26
  $gutter: calc($size * 5);
57
27
  }
58
- @if not -is-size($gutter) {
28
+ @if not isSize($gutter) {
59
29
  @error "`#{meta.inspect($gutter)}` is not a valid $gutter for `background-dots`. Pass a length, or var(), env() or a calculation such as `calc()`.";
60
30
  }
31
+ // isSize takes any number, since it only guards the arithmetic. The size is a
32
+ // colour stop's position and the gutter a background-size, which in Chrome
33
+ // 152 kept a percentage and dropped a negative length, a unitless number
34
+ // other than 0 and a unit that is not a length, so no dots were drawn.
35
+ @each $name, $value in ("$size": $size, "$gutter": $gutter) {
36
+ $problem: lengthProblem($value, $negative: false);
37
+ @if $problem {
38
+ @error "`#{meta.inspect($value)}` is not a valid #{$name} for `background-dots`: #{$problem}. A browser drops the pattern.";
39
+ }
40
+ }
61
41
  @if $image != null and meta.type-of($image) != "string" {
62
42
  @error "`#{$image}` is not a valid $image for `background-dots`. Pass an image path as a string.";
63
43
  }
64
44
  @if $diagonal != true and $diagonal != false {
65
45
  @error "`#{$diagonal}` is not a valid $diagonal for `background-dots`. Pass `true` or `false`.";
66
46
  }
47
+ // Each colour goes into a radial gradient as `radial-gradient($color $size,
48
+ // ...)`. Measured in Chrome 152, a word or a quoted colour there drops the
49
+ // gradient, and a length is read as the gradient's size instead, so neither
50
+ // draws the dots meant; see scss/internal/_color-problem.scss.
51
+ @if $color != null {
52
+ @each $item in $color {
53
+ $problem: colorProblem($item);
54
+ @if $problem {
55
+ @error "`#{meta.inspect($item)}` is not a valid colour in $color for `background-dots`: #{$problem}. Pass one colour, or two for the diagonal variant, such as `red` or `var(--dot)`.";
56
+ }
57
+ }
58
+ }
67
59
  @if not $color {
68
60
  $color: rgba(0, 0, 0, 0.1);
69
61
  @if $diagonal == true {
@@ -101,7 +93,7 @@
101
93
  height: 100%;
102
94
  top: 0;
103
95
  left: 0;
104
- background-image: -image($image);
96
+ background-image: imageValue($image, "background-dots", "$image", $unquote: true);
105
97
  background-repeat: no-repeat;
106
98
  background-position: center;
107
99
  background-size: cover;
@@ -4,28 +4,12 @@
4
4
  @use "sass:map";
5
5
  @use "sass:math";
6
6
  @use "sass:meta";
7
- @use "sass:string";
8
7
  @use "../library/linear-gradient" as *;
9
8
  @use "../maps/map-for-directions" as *;
10
-
11
- // An image path goes into url(). A var(), url(), image-set() or gradient is
12
- // already an image, and wrapping it produced url(var(--x)), which loads nothing.
13
- @function -image($value) {
14
- @if -is-css-function($value, "var" "url" "image-set" "linear-gradient" "radial-gradient" "conic-gradient" "repeating-linear-gradient" "repeating-radial-gradient" "repeating-conic-gradient") {
15
- @return $value;
16
- }
17
- @return url($value);
18
- }
19
-
20
- // True for an unquoted call to one of $names, such as var(--x). A quoted string
21
- // stays text, so `"var(--x)"` is not a call.
22
- @function -is-css-function($value, $names) {
23
- @if meta.type-of($value) != "string" or string.slice(meta.inspect($value), 1, 1) == "\"" {
24
- @return false;
25
- }
26
- $open: string.index($value, "(");
27
- @return $open != null and list.index($names, string.to-lower-case(string.slice($value, 1, $open - 1))) != null;
28
- }
9
+ @use "../internal/image-value" as *;
10
+ @use "../internal/color-problem" as *;
11
+ @use "../internal/color-stops-problem" as *;
12
+ @use "../internal/is-css-function" as *;
29
13
 
30
14
  @mixin background-image(
31
15
  $image-url: null,
@@ -38,11 +22,39 @@
38
22
  @if $filter-direction and not map.has-key($map-for-directions, $filter-direction) and not (meta.type-of($filter-direction) == "number" and math.unit($filter-direction) == "deg") {
39
23
  @error "`#{$filter-direction}` is not a valid $filter-direction. Pass an angle such as `45deg`, or one of: #{map.keys($map-for-directions)}.";
40
24
  }
25
+ // One filter colour is written into `linear-gradient(to top, $c, $c)` over an
26
+ // image, or into `background` when there is none; several are the stops of a
27
+ // gradient. Measured in Chrome 152, a word or a quoted colour drops either, and
28
+ // a length in `background` is read as a position, so none of them is a filter.
29
+ // CSS-wide keywords are refused, since they drop the gradient.
30
+ // With no $image-url the image sits in the markup and the filter is written
31
+ // as an ::after layer's `background`, where an image is as valid as a colour.
32
+ // Measured in Chrome 152 over an image in a style attribute, a
33
+ // linear-gradient drew a scrim, a radial-gradient a vignette and url() a
34
+ // pattern, so those are accepted there. `inherit` drew the same image over
35
+ // itself and `10px` an empty layer, so they stay refused. With an image the
36
+ // filter goes inside linear-gradient(), where a gradient dropped the whole
37
+ // declaration, image included.
38
+ // paint(), image() and -webkit-cross-fade() were kept as images in Chrome 152;
39
+ // cross-fade(), element() and -moz-element() are in the specification or other
40
+ // engines and are let through on the same grounds.
41
+ $overlay-image: not $image-url and isCssFunction($filter-color, "url" "image-set" "var" "linear-gradient" "radial-gradient" "conic-gradient" "repeating-linear-gradient" "repeating-radial-gradient" "repeating-conic-gradient" "paint" "image" "cross-fade" "-webkit-cross-fade" "element" "-moz-element");
42
+ @if $filter-color != null and not $overlay-image {
43
+ $problem: null;
44
+ @if list.length($filter-color) == 1 {
45
+ $problem: colorProblem($filter-color);
46
+ } @else {
47
+ $problem: colorStopsProblem($filter-color);
48
+ }
49
+ @if $problem {
50
+ @error "`#{meta.inspect($filter-color)}` is not a valid $filter-color for `background-image`: #{$problem}. Pass a colour, such as `rgba(0, 0, 0, 0.5)`, or several for a gradient.";
51
+ }
52
+ }
41
53
  @if $image-url {
42
54
  @if $filter-color {
43
55
  @if list.length($filter-color) == 1 {
44
56
  background-image: linear-gradient(to top, $filter-color, $filter-color),
45
- -image($image-url);
57
+ imageValue($image-url, "background-image", "$image-url");
46
58
  } @else if list.length($filter-color) > 1 {
47
59
  $color-list: ();
48
60
  @for $i from 1 through list.length($filter-color) {
@@ -54,22 +66,22 @@
54
66
  map.get($map-for-directions, $filter-direction),
55
67
  $color-list
56
68
  ),
57
- -image($image-url);
69
+ imageValue($image-url, "background-image", "$image-url");
58
70
  } @else {
59
71
  @if meta.type-of($filter-direction) == "number" and list.index("deg", math.unit($filter-direction)) {
60
72
  background-image: linear-gradient($filter-direction, $color-list),
61
- -image($image-url);
73
+ imageValue($image-url, "background-image", "$image-url");
62
74
  } @else {
63
75
  @error "`#{$filter-direction}` is not a valid $filter-direction. Pass an angle such as `45deg`, or one of: #{map.keys($map-for-directions)}.";
64
76
  }
65
77
  }
66
78
  } @else {
67
79
  background-image: linear-gradient(to top, $color-list),
68
- -image($image-url);
80
+ imageValue($image-url, "background-image", "$image-url");
69
81
  }
70
82
  }
71
83
  } @else {
72
- background-image: -image($image-url);
84
+ background-image: imageValue($image-url, "background-image", "$image-url");
73
85
  }
74
86
  } @else {
75
87
  position: relative;
@@ -4,40 +4,10 @@
4
4
  @use "sass:math";
5
5
  @use "sass:meta";
6
6
  @use "sass:string";
7
-
8
- // An image path goes into url(). A var(), url(), image-set() or gradient is
9
- // already an image, and wrapping it produced url(var(--x)), which loads nothing.
10
- @function -image($value) {
11
- @if -is-css-function($value, "var" "url" "image-set" "linear-gradient" "radial-gradient" "conic-gradient" "repeating-linear-gradient" "repeating-radial-gradient" "repeating-conic-gradient") {
12
- @return $value;
13
- }
14
- @return url(string.unquote($value));
15
- }
16
-
17
- // True for an unquoted call to one of $names, such as var(--x). A quoted string
18
- // stays text, so `"var(--x)"` is not a call.
19
- @function -is-css-function($value, $names) {
20
- @if meta.type-of($value) != "string" or string.slice(meta.inspect($value), 1, 1) == "\"" {
21
- @return false;
22
- }
23
- $open: string.index($value, "(");
24
- @return $open != null and list.index($names, string.to-lower-case(string.slice($value, 1, $open - 1))) != null;
25
- }
26
-
27
- // A size Sass can use inside calc(): a number, a calculation such as calc(),
28
- // min() or clamp(), or var() and env(), which only the browser can resolve.
29
- // Anything else would pass silently through calc(): `calc(nonsense * 5)`
30
- // compiles.
31
- @function -is-size($value) {
32
- @if meta.type-of($value) == "number" or meta.type-of($value) == "calculation" {
33
- @return true;
34
- }
35
- @if meta.type-of($value) == "string" and string.slice(meta.inspect($value), 1, 1) != "\"" {
36
- $open: string.index($value, "(");
37
- @return $open != null and list.index("var" "env", string.to-lower-case(string.slice($value, 1, $open - 1))) != null;
38
- }
39
- @return false;
40
- }
7
+ @use "../internal/is-size" as *;
8
+ @use "../internal/image-value" as *;
9
+ @use "../internal/color-problem" as *;
10
+ @use "../internal/length-problem" as *;
41
11
 
42
12
  @mixin background-stripes(
43
13
  $color: null,
@@ -47,17 +17,44 @@
47
17
  ) {
48
18
  // The arithmetic below runs in calc() so var() and clamp() work; on plain
49
19
  // lengths Sass simplifies it back to the same number.
50
- @if not -is-size($thickness) {
20
+ @if not isSize($thickness) {
51
21
  @error "`#{meta.inspect($thickness)}` is not a valid $thickness for `background-stripes`. Pass a length, or var(), env() or a calculation such as `clamp()`.";
52
22
  }
23
+ // isSize takes any number. The thickness is a colour stop's position, which
24
+ // in Chrome 152 kept a percentage and a negative length and dropped a unitless
25
+ // number other than 0 and a unit that is not a length.
26
+ $thickness-problem: lengthProblem($thickness);
27
+ @if $thickness-problem {
28
+ @error "`#{meta.inspect($thickness)}` is not a valid $thickness for `background-stripes`: #{$thickness-problem}. A browser drops the gradient.";
29
+ }
53
30
  @if meta.type-of($rotation) != "number" {
54
31
  @error "`#{$rotation}` is not a valid $rotation for `background-stripes`. Pass an angle such as `-45deg`, or a unitless number which is treated as degrees.";
55
32
  }
56
33
  @if $image != null and meta.type-of($image) != "string" {
57
34
  @error "`#{$image}` is not a valid $image for `background-stripes`. Pass an image path as a string.";
58
35
  }
59
- $validate-unit: if(not list.index("deg", math.unit($rotation)), $rotation + deg, $rotation);
60
- $get-image: if($image, string.unquote(", ") + -image($image), string.unquote(""));
36
+ // A unitless rotation is read as degrees. `deg` used to be added to any unit
37
+ // that was not deg, so a valid `0.25turn` became `0.25turndeg` and `-10px`
38
+ // became `-10pxdeg`, both dropped. Measured in Chrome 152, a gradient kept
39
+ // deg, grad, rad and turn in any case (`10DEG`, `0.25TURN`) and a unitless 0,
40
+ // and dropped a unitless 45, `-10px` and `10%`.
41
+ // Each colour becomes a stripe in a repeating gradient, where a word or a
42
+ // quoted colour drops the whole pattern; see scss/internal/_color-problem.scss.
43
+ @if $color != null {
44
+ @each $item in $color {
45
+ $problem: colorProblem($item);
46
+ @if $problem {
47
+ @error "`#{meta.inspect($item)}` is not a valid colour in $color for `background-stripes`: #{$problem}. Pass one colour or several, such as `red` or `red var(--stripe)`.";
48
+ }
49
+ }
50
+ }
51
+ $validate-unit: $rotation;
52
+ @if math.is-unitless($rotation) {
53
+ $validate-unit: $rotation + deg;
54
+ } @else if not list.index("deg" "grad" "rad" "turn", string.to-lower-case(math.unit($rotation))) {
55
+ @error "`#{meta.inspect($rotation)}` is not a valid $rotation for `background-stripes`: `#{math.unit($rotation)}` is not an angle unit, and a browser drops the gradient. Pass an angle in deg, grad, rad or turn, such as `-45deg`, or a unitless number, which is read as degrees.";
56
+ }
57
+ $get-image: if($image, string.unquote(", ") + imageValue($image, "background-stripes", "$image", $unquote: true), string.unquote(""));
61
58
  @if not $color {
62
59
  $color: rgba(0, 0, 0, 0.1);
63
60
  background-image: repeating-linear-gradient(
@@ -1,18 +1,8 @@
1
1
  @charset "UTF-8";
2
2
 
3
- @use "sass:list";
4
3
  @use "sass:meta";
5
4
  @use "sass:string";
6
-
7
- // True for an unquoted call to one of $names, such as var(--x). A quoted string
8
- // stays text, so `"var(--x)"` is not a call.
9
- @function -is-css-function($value, $names) {
10
- @if meta.type-of($value) != "string" or string.slice(meta.inspect($value), 1, 1) == "\"" {
11
- @return false;
12
- }
13
- $open: string.index($value, "(");
14
- @return $open != null and list.index($names, string.to-lower-case(string.slice($value, 1, $open - 1))) != null;
15
- }
5
+ @use "../internal/is-css-function" as *;
16
6
 
17
7
  @mixin before($content: null) {
18
8
  &::before {
@@ -20,7 +10,7 @@
20
10
  @content;
21
11
  } @else if meta.type-of($content) != "string" {
22
12
  @error "`#{$content}` is not a valid $content for `before`. Pass a string to use as the content value, a data- attribute name such as `\"data-label\"` to render it with attr(), or no argument at all and write the content yourself.";
23
- } @else if -is-css-function($content, "var" "attr" "counter" "counters") {
13
+ } @else if isCssFunction($content, "var" "attr" "counter" "counters") {
24
14
  // Quoting would turn the call into text: content: "var(--label)".
25
15
  content: $content;
26
16
  @content;