gerillass 2.3.0 → 3.0.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.
Files changed (55) hide show
  1. package/README.md +46 -7
  2. package/SKILL.md +37 -12
  3. package/gerillass.json +527 -145
  4. package/package.json +1 -1
  5. package/scss/internal/_color-problem.scss +99 -0
  6. package/scss/internal/_color-stops-problem.scss +94 -0
  7. package/scss/internal/_condition-width.scss +22 -0
  8. package/scss/internal/_custom-property-in.scss +25 -0
  9. package/scss/internal/_gradient.scss +425 -0
  10. package/scss/internal/_image-value.scss +68 -0
  11. package/scss/internal/_is-condition-value.scss +94 -0
  12. package/scss/internal/_is-css-function.scss +21 -0
  13. package/scss/internal/_is-size.scss +22 -0
  14. package/scss/internal/_keyword-value.scss +76 -0
  15. package/scss/internal/_length-problem.scss +127 -0
  16. package/scss/internal/_sibling-index.scss +51 -0
  17. package/scss/internal/_words.scss +35 -0
  18. package/scss/library/_adaptive.scss +17 -0
  19. package/scss/library/_after.scss +2 -12
  20. package/scss/library/_background-dots.scss +36 -43
  21. package/scss/library/_background-image.scss +40 -52
  22. package/scss/library/_background-stripes.scss +40 -54
  23. package/scss/library/_before.scss +2 -12
  24. package/scss/library/_border-radius.scss +83 -0
  25. package/scss/library/_brand-logo.scss +10 -24
  26. package/scss/library/_breakpoint.scss +19 -26
  27. package/scss/library/_circle.scss +7 -0
  28. package/scss/library/_columnizer.scss +62 -2
  29. package/scss/library/_container-query.scss +13 -29
  30. package/scss/library/_container.scss +2 -19
  31. package/scss/library/_counter.scss +3 -14
  32. package/scss/library/_ellipsis.scss +78 -1
  33. package/scss/library/_except.scss +15 -0
  34. package/scss/library/_focus-ring.scss +29 -0
  35. package/scss/library/_font-face.scss +117 -1
  36. package/scss/library/_gradient.scss +31 -0
  37. package/scss/library/_index.scss +1 -2
  38. package/scss/library/_line-clamp.scss +2 -13
  39. package/scss/library/_only.scss +16 -0
  40. package/scss/library/_position.scss +34 -3
  41. package/scss/library/_remove.scss +1 -1
  42. package/scss/library/_resizable.scss +29 -2
  43. package/scss/library/_screen-agent.scss +8 -22
  44. package/scss/library/_sizer.scss +9 -0
  45. package/scss/library/_sprite.scss +65 -0
  46. package/scss/library/_text-gradient.scss +36 -18
  47. package/scss/library/_text-image.scss +2 -23
  48. package/scss/library/_text-stroke.scss +30 -0
  49. package/scss/library/_triangle.scss +25 -56
  50. package/scss/utilities/_gradient-value.scss +24 -0
  51. package/scss/utilities/_index.scss +1 -0
  52. package/scss/utilities/_validate-breakpoint.scss +32 -6
  53. package/scss/utilities/_validate-scissors.scss +7 -0
  54. package/scss/library/_linear-gradient.scss +0 -26
  55. package/scss/library/_radial-gradient.scss +0 -30
@@ -3,6 +3,7 @@
3
3
  @use "sass:list";
4
4
  @use "sass:meta";
5
5
  @use "sass:string";
6
+ @use "../internal/sibling-index" as *;
6
7
 
7
8
  @mixin only($params...) {
8
9
  @if list.length($params) == 0 {
@@ -10,6 +11,12 @@
10
11
  } @else if list.length($params) == 1 {
11
12
  $value: list.nth($params, 1);
12
13
  @if meta.type-of($value) == "number" {
14
+ // A unit or a fraction makes a selector the browser drops, and 0 matches
15
+ // nothing; see scss/internal/_sibling-index.scss.
16
+ $problem: siblingIndexProblem($value, "only");
17
+ @if $problem {
18
+ @error "`#{meta.inspect($value)}` is not a valid position for `only`: #{$problem}.";
19
+ }
13
20
  @if $value < 0 {
14
21
  &:nth-last-of-type(#{$value * -1}) {
15
22
  @content;
@@ -46,6 +53,15 @@
46
53
  @error "`#{$value}` is not a valid argument for `only`. Pass a positive or negative number, one of `first`, `last`, `odd`, `even`, or a selector such as `.foo`.";
47
54
  }
48
55
  } @else if list.length($params) > 1 {
56
+ // Every item is checked first. A word used to fail with Sass's own
57
+ // `Undefined operation`, and one bad position emptied the whole selector
58
+ // list, taking the good positions with it.
59
+ @each $item in $params {
60
+ $problem: siblingIndexProblem($item, "only", true);
61
+ @if $problem {
62
+ @error "`#{meta.inspect($item)}` is not a valid position for `only`: #{$problem}.";
63
+ }
64
+ }
49
65
  $list: ();
50
66
  @for $i from 1 through list.length($params) {
51
67
  $list: list.append($list, if(list.nth($params, $i) < 0, string.unquote("&:nth-last-of-type(#{list.nth($params, $i) * -1})"), string.unquote("&:nth-of-type(#{list.nth($params, $i)})")), comma);
@@ -2,8 +2,10 @@
2
2
 
3
3
  @use "sass:list";
4
4
  @use "sass:meta";
5
+ @use "sass:string";
5
6
  @use "../utilities/shorthand-property" as *;
6
- @use "../utilities/validate-length" as *;
7
+ @use "../internal/keyword-value" as *;
8
+ @use "../internal/length-problem" as *;
7
9
 
8
10
  // Sets position and its four offsets in shorthand order: top, right, bottom,
9
11
  // left, as `margin` and `padding` take them.
@@ -24,17 +26,46 @@
24
26
  @if $logical != true and $logical != false and $logical != null {
25
27
  @error "`#{meta.inspect($logical)}` is not a valid $logical for `position`. Pass true for offsets that follow the writing direction, or false for top, right, bottom and left.";
26
28
  }
29
+ // Measured in Chrome 152, `position` kept the five keywords in any case, the
30
+ // CSS-wide keywords and var(), and dropped anything else, a quoted keyword
31
+ // included. A vendor keyword is let through: Chrome drops `-webkit-sticky`,
32
+ // which Safari uses. null leaves position out, as it always has.
33
+ $problem: keywordProblem($position, "static" "relative" "absolute" "fixed" "sticky", $vendor: true);
34
+ @if $problem {
35
+ @error "`#{meta.inspect($position)}` is not a valid $position for `position`: #{$problem}. A browser drops the declaration.";
36
+ }
27
37
 
28
38
  $offsets: shorthandProperty($offsets);
39
+ // Each offset is a declaration of its own. Measured in Chrome 152, top, left
40
+ // and the inset-* properties kept a length, a negative length, a percentage,
41
+ // 0, auto and a CSS-wide keyword in any case, var(), maths functions, anchor()
42
+ // and anchor-size(), and dropped any other word, a unitless number, a unit
43
+ // that is not a length, a colour, an image and a quoted length. This used to
44
+ // go through validateLength, which only warns, and warned about working
45
+ // values such as AUTO and revert-layer while saying nothing about 10deg or
46
+ // url(); validateLength itself is unchanged, for callers who want a warning.
47
+ @each $value in $offsets {
48
+ @if $value != null {
49
+ $problem: lengthProblem($value, $keywords: ("auto",), $functions: "anchor" "anchor-size", $css-wide: true);
50
+ @if $problem {
51
+ @error "`#{meta.inspect($value)}` is not a valid offset for `position`: #{$problem}. A browser drops the declaration.";
52
+ }
53
+ }
54
+ }
29
55
  $edges: top right bottom left;
30
56
  @if $logical {
31
57
  $edges: inset-block-start inset-inline-end inset-block-end inset-inline-start;
32
58
  }
33
59
 
34
- position: $position;
60
+ // Written without quotes, since `position: "sticky"` was dropped.
61
+ @if meta.type-of($position) == "string" {
62
+ position: string.unquote($position);
63
+ } @else {
64
+ position: $position;
65
+ }
35
66
  @for $i from 1 through 4 {
36
67
  $value: list.nth($offsets, $i);
37
- @if validateLength($value) {
68
+ @if $value != null {
38
69
  #{list.nth($edges, $i)}: $value;
39
70
  }
40
71
  }
@@ -13,7 +13,7 @@
13
13
  @include breakpoint(only, $value) {
14
14
  display: none;
15
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.";
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.";
17
17
  } @else if list.length($params) == 2 {
18
18
  @if list.index("min" "max", list.nth($params, 1)) {
19
19
  $mode: list.nth($params, 1);
@@ -1,7 +1,34 @@
1
1
  @charset "UTF-8";
2
2
 
3
+ @use "sass:meta";
4
+ @use "sass:string";
5
+ @use "../internal/keyword-value" as *;
6
+
7
+ // Measured in Chrome 152, `resize` kept none, both, horizontal, vertical, block,
8
+ // inline and auto, and `overflow` one or two of visible, hidden, clip, scroll,
9
+ // auto and overlay, all in any case, with the CSS-wide keywords on their own and
10
+ // var(). Both dropped a quoted keyword, three overflow values, and a CSS-wide
11
+ // keyword beside another, such as `overflow: inherit auto`. null leaves the
12
+ // declaration out, as it always has.
3
13
  @mixin resizable($direction: both, $overflow: auto) {
4
- resize: $direction;
5
- overflow: $overflow;
14
+ $problem: keywordProblem($direction, "none" "both" "horizontal" "vertical" "block" "inline" "auto");
15
+ @if $problem {
16
+ @error "`#{meta.inspect($direction)}` is not a valid $direction for `resizable`: #{$problem}. A browser drops the declaration.";
17
+ }
18
+ $problem: keywordProblem($overflow, "visible" "hidden" "clip" "scroll" "auto" "overlay", 2);
19
+ @if $problem {
20
+ @error "`#{meta.inspect($overflow)}` is not a valid $overflow for `resizable`: #{$problem}. A browser drops the declaration.";
21
+ }
22
+ // Written without quotes, since `resize: "both"` was dropped.
23
+ @if meta.type-of($direction) == "string" {
24
+ resize: string.unquote($direction);
25
+ } @else {
26
+ resize: $direction;
27
+ }
28
+ @if meta.type-of($overflow) == "string" {
29
+ overflow: string.unquote($overflow);
30
+ } @else {
31
+ overflow: $overflow;
32
+ }
6
33
  max-width: 100%;
7
34
  }
@@ -1,30 +1,11 @@
1
1
  @charset "UTF-8";
2
2
 
3
- @use "sass:list";
4
3
  @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
- }
4
+ @use "../internal/custom-property-in" as *;
5
+ @use "../internal/is-condition-value" as *;
25
6
 
26
7
  @mixin screen-agent($resolution) {
27
- $custom-property: -custom-property-in($resolution);
8
+ $custom-property: customPropertyIn($resolution);
28
9
  @if $custom-property {
29
10
  @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
11
  }
@@ -41,6 +22,11 @@
41
22
  @content;
42
23
  }
43
24
  } @else {
25
+ // Measured in Chrome 152, `(min-resolution: huge)`, `(min-resolution: 2)`,
26
+ // `(min-resolution: 10px)` and `(min-resolution: -1dppx)` never match.
27
+ @if not isConditionValue($resolution, "resolution") {
28
+ @error "`#{meta.inspect($resolution)}` is not a valid $resolution for `screen-agent`: `(min-resolution: #{meta.inspect($resolution)})` would never match. Pass `1x`, `2x`, `3x`, or a resolution such as `2dppx`, `192dpi` or `38dpcm`.";
29
+ }
44
30
  @media (min-resolution: #{$resolution}) {
45
31
  @content;
46
32
  }
@@ -1,6 +1,15 @@
1
1
  @charset "UTF-8";
2
2
 
3
+ @use "sass:meta";
4
+ @use "../internal/length-problem" as *;
5
+
3
6
  @mixin sizer($width, $height: $width) {
7
+ @each $name, $value in ("$width": $width, "$height": $height) {
8
+ $problem: sizeProblem($value);
9
+ @if $problem {
10
+ @error "`#{meta.inspect($value)}` is not a valid #{$name} for `sizer`: #{$problem}. A browser drops the declaration.";
11
+ }
12
+ }
4
13
  width: $width;
5
14
  height: $height;
6
15
  }
@@ -3,6 +3,55 @@
3
3
  @use "sass:list";
4
4
  @use "sass:meta";
5
5
  @use "sass:string";
6
+ @use "../internal/length-problem" as *;
7
+
8
+ $-places: "left" "center" "right" "top" "bottom";
9
+
10
+ // What is wrong with $position as a background-position, or null. Measured in
11
+ // Chrome 152, background-position kept one or two lengths, percentages or
12
+ // place keywords, keywords with offsets such as `left 10px top 5px`, var(),
13
+ // maths functions and a CSS-wide keyword alone. It dropped a unitless number
14
+ // other than 0, a unit that is not a length, any other word or colour, an image,
15
+ // and three or four lengths with no keyword. Interpolation, as in
16
+ // `#{$x}px #{$y}px`, gives the same value as strings, so each part is read.
17
+ @function -position-problem($position) {
18
+ $parts: $position;
19
+ @if meta.type-of($position) == "string" and string.slice(meta.inspect($position), 1, 1) != "\"" and not string.index($position, "(") and string.index($position, " ") {
20
+ $parts: ();
21
+ $text: $position;
22
+ $at: string.index($text, " ");
23
+ @while $at {
24
+ @if $at > 1 {
25
+ $parts: list.append($parts, string.unquote(string.slice($text, 1, $at - 1)));
26
+ }
27
+ $text: string.slice($text, $at + 1);
28
+ $at: string.index($text, " ");
29
+ }
30
+ $parts: list.append($parts, string.unquote($text));
31
+ } @else if meta.type-of($position) != "list" {
32
+ $parts: ($position,);
33
+ }
34
+ @if list.length($parts) > 4 {
35
+ @return "a background position takes at most four values";
36
+ }
37
+ $keywords: 0;
38
+ @each $part in $parts {
39
+ @if meta.type-of($part) == "string" and list.index($-places, string.to-lower-case($part)) {
40
+ $keywords: $keywords + 1;
41
+ } @else {
42
+ $problem: lengthProblem($part, $css-wide: list.length($parts) == 1);
43
+ @if $problem == "it is not a length" {
44
+ @return "`#{meta.inspect($part)}` is not a length or one of left, center, right, top or bottom";
45
+ } @else if $problem {
46
+ @return "in `#{meta.inspect($part)}`, #{$problem}";
47
+ }
48
+ }
49
+ }
50
+ @if list.length($parts) > 2 and $keywords == 0 {
51
+ @return "three or four values need place keywords, such as `left 10px top 5px`";
52
+ }
53
+ @return null;
54
+ }
6
55
 
7
56
  @mixin sprite($params...) {
8
57
  @if list.length($params) == 1 {
@@ -19,11 +68,27 @@
19
68
  }
20
69
  } @else {
21
70
  $position: list.nth($params, 1);
71
+ $problem: -position-problem($position);
72
+ @if $problem {
73
+ @error "`#{meta.inspect($position)}` is not a valid position for `sprite`: #{$problem}. A browser drops the declaration.";
74
+ }
22
75
  background-position: $position;
23
76
  }
24
77
  } @else if list.length($params) == 2 {
25
78
  $image-url: list.nth($params, 1);
26
79
  $position: list.nth($params, 2);
80
+ // The one-argument form checks its path; this one did not, so a list was
81
+ // written into url(), which a browser drops, and a number asked for a file
82
+ // of that name. Measured in Chrome 152: `url(16 9)` was dropped.
83
+ @if meta.type-of($image-url) != "string" {
84
+ @error "`#{meta.inspect($image-url)}` is not a valid image path for `sprite`: with two arguments the first is one path as a string, and a browser drops a list in `url()`. Pass a path such as `\"/img/sprite.png\"`, then the position.";
85
+ }
86
+ @if $position != null {
87
+ $problem: -position-problem($position);
88
+ @if $problem {
89
+ @error "`#{meta.inspect($position)}` is not a valid position for `sprite`: #{$problem}. A browser drops the declaration.";
90
+ }
91
+ }
27
92
  display: inline-block;
28
93
  background-image: url($image-url);
29
94
  background-position: $position;
@@ -1,28 +1,46 @@
1
1
  @charset "UTF-8";
2
2
 
3
3
  @use "sass:list";
4
- @use "sass:map";
5
- @use "sass:math";
6
4
  @use "sass:meta";
7
- @use "../maps/map-for-directions" as *;
5
+ @use "sass:string";
6
+ @use "../internal/gradient" as *;
8
7
 
9
- @mixin text-gradient($direction, $colors) {
10
- $list: ();
11
- @for $i from 1 through list.length($colors) {
12
- $list: list.append($list, list.nth($colors, $i), comma);
13
- }
14
- @if map.has-key($map-for-directions, $direction) {
15
- background: linear-gradient(
16
- map.get($map-for-directions, $direction),
17
- $list
18
- );
19
- } @else if not map.has-key($map-for-directions, $direction) {
20
- @if meta.type-of($direction) != "number" or not list.index("deg", math.unit($direction)) {
21
- @error "`#{$direction}` is not a valid $direction for `text-gradient`. Pass an angle such as `45deg`, or one of: #{map.keys($map-for-directions)}.";
22
- } @else {
23
- background: linear-gradient($direction, $list);
8
+ // Fills the letters with a gradient: the gradient mixin's arguments, and
9
+ // background-clip: text. The gradient is built and checked in
10
+ // scss/internal/_gradient.scss.
11
+ //
12
+ // The colours come first since 3.0.0, as in gradient, so a direction, a type
13
+ // and a colour space can all be left out. A call in the old order,
14
+ // `text-gradient("top", (red, blue))`, would otherwise fail on `"top"` not
15
+ // being a colour, so it is caught and answered with the new form.
16
+ //
17
+ // It still writes the background shorthand rather than background-image, so a
18
+ // call in the new order compiles to exactly what the same call compiled to in
19
+ // the old order.
20
+ @mixin text-gradient(
21
+ $colors,
22
+ $type: linear,
23
+ $direction: null,
24
+ $shape: null,
25
+ $position: null,
26
+ $from: null,
27
+ $in: null,
28
+ $repeating: false
29
+ ) {
30
+ @if gradientDirection($colors) != null and not list.index("linear" "radial" "conic", string.to-lower-case("#{$type}")) {
31
+ $written: meta.inspect($type);
32
+ @if list.separator($type) == "comma" {
33
+ $written: "(#{$written})";
24
34
  }
35
+ @error "`text-gradient` takes the colours first since 3.0.0. Write `text-gradient(#{$written}, $direction: #{meta.inspect($colors)})`.";
36
+ }
37
+ $value: gradientOf($colors, $type, $direction, $shape, $position, $from, $in, $repeating, $member: "text-gradient");
38
+ // Written without the space first, as the gradient mixin does, for a browser
39
+ // that does not know `in`.
40
+ @if $in != null {
41
+ background: gradientOf($colors, $type, $direction, $shape, $position, $from, null, $repeating, $member: "text-gradient", $warn: false);
25
42
  }
43
+ background: $value;
26
44
  -webkit-background-clip: text;
27
45
  -webkit-text-fill-color: transparent;
28
46
  background-clip: text;
@@ -1,31 +1,10 @@
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
- }
3
+ @use "../internal/image-value" as *;
25
4
 
26
5
  @mixin text-image($image: null) {
27
6
  @if $image {
28
- background-image: -image($image);
7
+ background-image: imageValue($image, "text-image", "$image");
29
8
  }
30
9
  background-size: cover;
31
10
  background-position: center;
@@ -1,11 +1,41 @@
1
1
  @charset "UTF-8";
2
2
 
3
+ @use "sass:meta";
4
+ @use "../internal/color-problem" as *;
5
+ @use "../internal/length-problem" as *;
6
+
7
+ // Each colour is a declaration of its own, where Chrome 152 kept a CSS-wide
8
+ // keyword and dropped a word that is not a colour, a quoted colour, a length and
9
+ // two colours side by side; see scss/internal/_color-problem.scss. null leaves
10
+ // that declaration out, as it always has.
3
11
  @mixin text-stroke(
4
12
  $fallback-color: black,
5
13
  $color: transparent,
6
14
  $stroke-color: black,
7
15
  $stroke-width: 1px
8
16
  ) {
17
+ $colors: (
18
+ "$fallback-color": $fallback-color,
19
+ "$color": $color,
20
+ "$stroke-color": $stroke-color,
21
+ );
22
+ @each $name, $value in $colors {
23
+ @if $value != null {
24
+ $problem: colorProblem($value, $css-wide: true);
25
+ @if $problem {
26
+ @error "`#{meta.inspect($value)}` is not a valid #{$name} for `text-stroke`: #{$problem}. A browser drops the declaration. Pass a colour such as `black`, `currentColor`, or a function such as `var(--stroke)`.";
27
+ }
28
+ }
29
+ }
30
+ // -webkit-text-stroke-width kept a length, 0, thin, medium or thick and a
31
+ // CSS-wide keyword in Chrome 152, and dropped a percentage and a negative
32
+ // width.
33
+ @if $stroke-width != null {
34
+ $problem: lengthProblem($stroke-width, $percent: false, $negative: false, $keywords: "thin" "medium" "thick", $css-wide: true);
35
+ @if $problem {
36
+ @error "`#{meta.inspect($stroke-width)}` is not a valid $stroke-width for `text-stroke`: #{$problem}. A browser drops the declaration.";
37
+ }
38
+ }
9
39
  color: $fallback-color;
10
40
  -webkit-text-fill-color: $color;
11
41
  -webkit-text-stroke-color: $stroke-color;
@@ -2,61 +2,10 @@
2
2
 
3
3
  @use "sass:list";
4
4
  @use "sass:meta";
5
- @use "sass:string";
6
5
  @use "../lists/list-of-directions" as *;
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
- }
6
+ @use "../internal/is-size" as *;
7
+ @use "../internal/color-problem" as *;
8
+ @use "../internal/length-problem" as *;
60
9
 
61
10
  // The logical directions point along the writing direction rather than a fixed
62
11
  // side: inline-end is right in a left-to-right page and left in a right-to-left
@@ -70,11 +19,31 @@ $-logical-directions: ("inline-start", "inline-end", "block-start", "block-end")
70
19
  // Halving a size in calc() rather than math.div() keeps var() and clamp()
71
20
  // working instead of emitting `var(--w)/2`, which is not valid CSS.
72
21
  @each $item in $size {
73
- @if not -is-size($item) {
22
+ @if not isSize($item) {
74
23
  @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
24
  }
25
+ // The sizes land in border-width, which in Chrome 152 dropped a
26
+ // percentage, a negative width and a unit that is not a length. isSize
27
+ // above takes any number, since it only guards calc().
28
+ $problem: lengthProblem($item, $percent: false, $negative: false);
29
+ @if $problem {
30
+ @error "`#{meta.inspect($size)}` is not a valid $size for `triangle`: #{$problem}. A browser drops the border widths, so no triangle is drawn.";
31
+ }
32
+ }
33
+ // The colour is checked by colorProblem in scss/internal/, each item of a
34
+ // list on its own, as isColor used to accept a list. CSS-wide keywords stay
35
+ // refused: the mixin writes the colour into a border-color shorthand beside
36
+ // `transparent`, and a CSS-wide keyword is only valid as the whole value, so
37
+ // `transparent transparent inherit` was rejected in Chrome 152. The check
38
+ // used to name four functions; system colours such as Canvas and
39
+ // contrast-color(), which a border keeps, were refused until it was shared.
40
+ $color-problem: null;
41
+ @each $item in $color {
42
+ @if not $color-problem {
43
+ $color-problem: colorProblem($item);
44
+ }
76
45
  }
77
- @if not -is-css-color($color) {
46
+ @if $color-problem {
78
47
  @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
48
  } @else {
80
49
  content: "";
@@ -0,0 +1,24 @@
1
+ @charset "UTF-8";
2
+
3
+ @use "../internal/gradient" as *;
4
+
5
+ // The gradient mixin's gradient as a value, for where a mixin cannot write the
6
+ // whole declaration: layered with an image in one background-image, or as a
7
+ // mask-image or border-image. It takes the same arguments and refuses the same
8
+ // input; see scss/internal/_gradient.scss.
9
+ //
10
+ // A function returns one value, so unlike the mixin it cannot write a fallback
11
+ // before a gradient with $in. Write one yourself if an old browser matters:
12
+ // the same call with $in left out, in a declaration before it.
13
+ @function gradientValue(
14
+ $colors,
15
+ $type: linear,
16
+ $direction: null,
17
+ $shape: null,
18
+ $position: null,
19
+ $from: null,
20
+ $in: null,
21
+ $repeating: false
22
+ ) {
23
+ @return gradientOf($colors, $type, $direction, $shape, $position, $from, $in, $repeating, $member: "gradientValue");
24
+ }
@@ -8,6 +8,7 @@
8
8
  @forward "fluid";
9
9
  @forward "font-sizer";
10
10
  @forward "font-source";
11
+ @forward "gradient-value";
11
12
  @forward "is-color";
12
13
  @forward "is-gutter";
13
14
  @forward "is-number";
@@ -2,14 +2,40 @@
2
2
 
3
3
  @use "sass:list";
4
4
  @use "sass:map";
5
+ @use "sass:meta";
6
+ @use "sass:string";
5
7
  @use "../maps/map-for-breakpoints" as *;
6
8
 
9
+ // Returns the width of a key from $map-for-breakpoints, and anything else as it
10
+ // is: a declaration takes a percentage or a CSS function, and the condition
11
+ // mixins check their own sizes through scss/internal/_condition-width.scss.
12
+ //
13
+ // What it refuses is what cannot be a breakpoint anywhere. A word that is not a
14
+ // key used to come back unchanged, so `breakpoint(min, huge)` compiled to
15
+ // `@media (min-width: huge)`, which never matches, without a word; the keys are
16
+ // case-sensitive, so `"MEDIUM"` did the same. A list used to give its first
17
+ // item, because the loop here returned on its first pass.
18
+ //
19
+ // null comes back as null, as it always did, so `width: validateBreakpoint(null)`
20
+ // still leaves the declaration out. The condition mixins refuse it themselves.
7
21
  @function validateBreakpoint($value) {
8
- @for $i from 1 through list.length($value) {
9
- @if map.has-key($map-for-breakpoints, $value) {
10
- @return map.get($map-for-breakpoints, list.nth($value, $i));
11
- } @else {
12
- @return list.nth($value, $i);
13
- }
22
+ @if $value == null {
23
+ @return null;
14
24
  }
25
+ @if map.has-key($map-for-breakpoints, $value) {
26
+ @return map.get($map-for-breakpoints, $value);
27
+ }
28
+ @if list.length($value) > 1 {
29
+ @error "`#{meta.inspect($value)}` is not a breakpoint. Pass one key from $map-for-breakpoints or one length; a range is written with `between`, such as `breakpoint(between, small large)`.";
30
+ }
31
+ // A quoted length such as "600px" starts with a digit, a dot or a sign, and a
32
+ // CSS function holds a parenthesis. Of the words, the sizing keywords stay
33
+ // accepted, because `max-width: validateBreakpoint(none)` is valid CSS; a
34
+ // condition mixin still refuses them through conditionWidth. Anything else is
35
+ // a word that cannot be a breakpoint.
36
+ $sizing-keywords: "auto" "none" "min-content" "max-content" "fit-content" "stretch" "inherit" "initial" "unset" "revert" "revert-layer";
37
+ @if meta.type-of($value) == "string" and not list.index($sizing-keywords, string.to-lower-case($value)) and not string.index($value, "(") and not string.index("0123456789.+-", string.slice($value, 1, 1)) {
38
+ @error "`#{$value}` is not a breakpoint. $map-for-breakpoints has #{map.keys($map-for-breakpoints)}; pass one of those, or a length such as `600px`.";
39
+ }
40
+ @return $value;
15
41
  }
@@ -4,6 +4,7 @@
4
4
  @use "sass:math";
5
5
  @use "sass:meta";
6
6
  @use "../utilities/pixelify" as *;
7
+ @use "../internal/is-condition-value" as *;
7
8
 
8
9
  @function validateScissors($value) {
9
10
  $list: ();
@@ -14,6 +15,12 @@
14
15
  @if $item != null and meta.type-of($item) != "number" {
15
16
  @error "`#{$item}` is not a valid corner size. Pass a length such as `12px`, or a unitless number which is treated as pixels.";
16
17
  }
18
+ // Each size becomes a polygon() coordinate, which in Chrome 152 kept a
19
+ // length, a percentage and a negative length, and dropped a unit that is not
20
+ // a length, such as deg or x, so the clip-path was dropped.
21
+ @if $item != null and not math.is-unitless($item) and not isConditionValue($item, "length-percentage") {
22
+ @error "`#{$item}` is not a valid corner size: `#{math.unit($item)}` is not a length unit, and a browser drops the clip-path. Pass a length such as `12px`, or a unitless number which is treated as pixels.";
23
+ }
17
24
  @if list.nth($value, $i) == null {
18
25
  $list: list.append($list, 0px, comma);
19
26
  } @else if math.is-unitless(list.nth($value, $i)) {