gerillass 2.2.1 → 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 +19 -2
  2. package/gerillass.json +301 -69
  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 +63 -12
  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
package/package.json CHANGED
@@ -16,7 +16,7 @@
16
16
  }
17
17
  },
18
18
  "license": "Apache-2.0",
19
- "version": "2.2.1",
19
+ "version": "2.3.1",
20
20
  "repository": {
21
21
  "type": "git",
22
22
  "url": "git+https://github.com/selfishprimate/gerillass.git"
@@ -0,0 +1,99 @@
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
+ // What a browser keeps as a colour, measured in Chrome 152 in `color`,
10
+ // `-webkit-text-stroke-color`, `outline: 2px solid ...`, `background` and a
11
+ // gradient's colour stop:
12
+ //
13
+ // - kept: a colour Sass knows (red, #333, rgba(), hsl(), oklch(), color()),
14
+ // currentColor in any case, every system colour, deprecated ones included
15
+ // (Canvas, AccentColor, ActiveBorder), a vendor colour such as -webkit-link,
16
+ // var() and env(), and the colour functions Sass leaves as text: rgb() with
17
+ // var() in it, rgb(from ...), color-mix(), light-dark() and contrast-color();
18
+ // - dropped: a word that is none of those (huge, auto, none), a quoted colour,
19
+ // a length, a number, calc(), and two colours side by side;
20
+ // - CSS-wide keywords were kept in a declaration of their own and dropped inside
21
+ // `outline` and inside a gradient, so $css-wide says which this is.
22
+ //
23
+ // device-cmyk() is in the specification and Chrome drops it; it is let through,
24
+ // as `run-in` is for display, because refusing it would refuse valid CSS once a
25
+ // browser ships it.
26
+ //
27
+ // Deliberately looser than isColor, which stays strict because tint and shade
28
+ // pass its result to color.mix, and that needs a real Sass colour.
29
+ $-system-colors: "canvas" "canvastext" "linktext" "visitedtext" "activetext" "buttonface" "buttontext" "buttonborder" "field" "fieldtext" "highlight" "highlighttext" "selecteditem" "selecteditemtext" "mark" "marktext" "graytext" "accentcolor" "accentcolortext" "activeborder" "activecaption" "appworkspace" "background" "buttonhighlight" "buttonshadow" "captiontext" "inactiveborder" "inactivecaption" "inactivecaptiontext" "infobackground" "infotext" "menu" "menutext" "scrollbar" "threeddarkshadow" "threedface" "threedhighlight" "threedlightshadow" "threedshadow" "window" "windowframe" "windowtext";
30
+ $-functions: "var" "env" "attr" "rgb" "rgba" "hsl" "hsla" "hwb" "lab" "lch" "oklab" "oklch" "color" "color-mix" "light-dark" "contrast-color" "device-cmyk";
31
+ $-css-wide: "inherit" "initial" "unset" "revert" "revert-layer";
32
+
33
+ // The CSS named colours and transparent, for a colour that reaches the check as
34
+ // an unquoted string rather than as a Sass colour.
35
+ $-named-colors: "transparent" "aliceblue" "antiquewhite" "aqua" "aquamarine" "azure" "beige" "bisque" "black" "blanchedalmond" "blue" "blueviolet" "brown" "burlywood" "cadetblue" "chartreuse" "chocolate" "coral" "cornflowerblue" "cornsilk" "crimson" "cyan" "darkblue" "darkcyan" "darkgoldenrod" "darkgray" "darkgreen" "darkgrey" "darkkhaki" "darkmagenta" "darkolivegreen" "darkorange" "darkorchid" "darkred" "darksalmon" "darkseagreen" "darkslateblue" "darkslategray" "darkslategrey" "darkturquoise" "darkviolet" "deeppink" "deepskyblue" "dimgray" "dimgrey" "dodgerblue" "firebrick" "floralwhite" "forestgreen" "fuchsia" "gainsboro" "ghostwhite" "gold" "goldenrod" "gray" "green" "greenyellow" "grey" "honeydew" "hotpink" "indianred" "indigo" "ivory" "khaki" "lavender" "lavenderblush" "lawngreen" "lemonchiffon" "lightblue" "lightcoral" "lightcyan" "lightgoldenrodyellow" "lightgray" "lightgreen" "lightgrey" "lightpink" "lightsalmon" "lightseagreen" "lightskyblue" "lightslategray" "lightslategrey" "lightsteelblue" "lightyellow" "lime" "limegreen" "linen" "magenta" "maroon" "mediumaquamarine" "mediumblue" "mediumorchid" "mediumpurple" "mediumseagreen" "mediumslateblue" "mediumspringgreen" "mediumturquoise" "mediumvioletred" "midnightblue" "mintcream" "mistyrose" "moccasin" "navajowhite" "navy" "oldlace" "olive" "olivedrab" "orange" "orangered" "orchid" "palegoldenrod" "palegreen" "paleturquoise" "palevioletred" "papayawhip" "peachpuff" "peru" "pink" "plum" "powderblue" "purple" "rebeccapurple" "red" "rosybrown" "royalblue" "saddlebrown" "salmon" "sandybrown" "seagreen" "seashell" "sienna" "silver" "skyblue" "slateblue" "slategray" "slategrey" "snow" "springgreen" "steelblue" "tan" "teal" "thistle" "tomato" "turquoise" "violet" "wheat" "white" "whitesmoke" "yellow" "yellowgreen";
36
+
37
+ // A hex colour written as text: # and then 3, 4, 6 or 8 hex digits.
38
+ @function -is-hex($text) {
39
+ @if string.index($text, "#") != 1 or not list.index(4 5 7 9, string.length($text)) {
40
+ @return false;
41
+ }
42
+ @for $i from 2 through string.length($text) {
43
+ @if not string.index("0123456789abcdef", string.slice($text, $i, $i)) {
44
+ @return false;
45
+ }
46
+ }
47
+ @return true;
48
+ }
49
+
50
+ // Returns what is wrong with $value as one colour, or null when a browser keeps
51
+ // it. null itself is the caller's to decide, since it means a default in some
52
+ // mixins and leaving a declaration out in others.
53
+ @function colorProblem($value, $css-wide: false) {
54
+ @if meta.type-of($value) == "color" {
55
+ @return null;
56
+ }
57
+ @if meta.type-of($value) == "list" and list.length($value) > 1 {
58
+ @return "it is several values where one colour is meant";
59
+ }
60
+ @if meta.type-of($value) != "string" {
61
+ @return "it is not a colour";
62
+ }
63
+ @if string.slice(meta.inspect($value), 1, 1) == "\"" {
64
+ @return "a colour in quotes is text, not a colour";
65
+ }
66
+ $lower: string.to-lower-case($value);
67
+ $open: string.index($lower, "(");
68
+ @if $open {
69
+ @if list.index($-functions, string.slice($lower, 1, $open - 1)) {
70
+ @return null;
71
+ }
72
+ @return "`#{string.slice($value, 1, $open - 1)}()` is not a colour function";
73
+ }
74
+ @if $lower == "currentcolor" or list.index($-system-colors, $lower) {
75
+ @return null;
76
+ }
77
+ // Interpolation, such as `#{$name}` or `#{$hex}`, gives an unquoted string
78
+ // rather than a colour, and it is written into the CSS as it reads, so a
79
+ // named or hex colour in one works exactly as the colour itself does.
80
+ @if list.index($-named-colors, $lower) or -is-hex($lower) {
81
+ @return null;
82
+ }
83
+ // Vendor colours are named rather than matched by prefix. Measured in Chrome
84
+ // 152, `-webkit-link` and `-webkit-activelink` were kept as colours while
85
+ // `-webkit-fill-available`, `-webkit-box` and `-webkit-text` were dropped, and
86
+ // the prefix rule this replaced had let those through. `-webkit-focus-ring-color`
87
+ // is Safari's, and `-moz-` colours are Firefox's; neither could be measured
88
+ // here, so they are let through rather than refused unseen.
89
+ @if list.index("-webkit-link" "-webkit-activelink" "-webkit-focus-ring-color", $lower) or string.index($lower, "-moz-") == 1 {
90
+ @return null;
91
+ }
92
+ @if list.index($-css-wide, $lower) {
93
+ @if $css-wide {
94
+ @return null;
95
+ }
96
+ @return "a CSS-wide keyword only works as a whole declaration, not inside this value";
97
+ }
98
+ @return "it is not a colour";
99
+ }
@@ -0,0 +1,94 @@
1
+ @charset "UTF-8";
2
+
3
+ @use "sass:list";
4
+ @use "sass:meta";
5
+ @use "sass:string";
6
+ @use "color-problem" as *;
7
+ @use "is-condition-value" as *;
8
+
9
+ // Shared by the mixins and not public API; see _is-css-function.scss.
10
+
11
+ // Returns what is wrong with a list of gradient colour stops, or null. The items
12
+ // are read the way the mixins write them, one per comma in the gradient.
13
+ // Measured in Chrome 152 in linear-gradient():
14
+ //
15
+ // - kept: a colour on its own, even a single one (`linear-gradient(to top,
16
+ // red)`), a colour with one or two positions (`red 0 10%`), a position between
17
+ // two colours as a hint (`red, 30%, blue`), and var() as a whole stop;
18
+ // - dropped: a position first or last, two positions in a row, a colour with
19
+ // three positions, a position that is not a length or a percentage
20
+ // (`red 10deg`), a colour and another colour in one stop (`red 10% blue`), and
21
+ // anything colorProblem refuses, CSS-wide keywords included.
22
+
23
+ // True for a value a stop takes as a position: a length, a percentage, 0 or a
24
+ // calculation.
25
+ @function -is-position($value) {
26
+ @return isConditionValue($value, "length-percentage");
27
+ }
28
+
29
+ // True for var(), env() or attr(), which may stand for a whole stop or a list of
30
+ // them, so nothing around it can be judged.
31
+ @function -is-substitution($value) {
32
+ @if meta.type-of($value) != "string" or string.slice(meta.inspect($value), 1, 1) == "\"" {
33
+ @return false;
34
+ }
35
+ $lower: string.to-lower-case($value);
36
+ @return string.index($lower, "var(") == 1 or string.index($lower, "env(") == 1 or string.index($lower, "attr(") == 1;
37
+ }
38
+
39
+ @function colorStopsProblem($colors) {
40
+ @if $colors == null or list.length($colors) == 0 {
41
+ @return "a gradient needs at least one colour";
42
+ }
43
+ $count: list.length($colors);
44
+ $previous: null;
45
+ $colors-seen: 0;
46
+ @for $i from 1 through $count {
47
+ $item: list.nth($colors, $i);
48
+ $kind: null;
49
+ @if -is-substitution($item) {
50
+ $kind: "substitution";
51
+ } @else if meta.type-of($item) != "list" and -is-position($item) {
52
+ $kind: "hint";
53
+ } @else if meta.type-of($item) == "list" and list.length($item) > 1 {
54
+ $problem: colorProblem(list.nth($item, 1));
55
+ @if $problem {
56
+ @return "`#{meta.inspect($item)}` does not start with a colour: #{$problem}";
57
+ }
58
+ @if list.length($item) > 3 {
59
+ @return "`#{meta.inspect($item)}` has more than two positions, and a colour stop takes at most two";
60
+ }
61
+ @for $j from 2 through list.length($item) {
62
+ @if not -is-position(list.nth($item, $j)) and not -is-substitution(list.nth($item, $j)) {
63
+ @return "in `#{meta.inspect($item)}`, `#{meta.inspect(list.nth($item, $j))}` is not a position; a stop is a colour followed by up to two lengths or percentages";
64
+ }
65
+ }
66
+ $kind: "stop";
67
+ } @else {
68
+ $problem: colorProblem($item);
69
+ @if $problem {
70
+ @return "`#{meta.inspect($item)}` is not a colour stop: #{$problem}";
71
+ }
72
+ $kind: "stop";
73
+ }
74
+
75
+ @if $kind == "hint" {
76
+ @if $i == 1 {
77
+ @return "it starts with the position `#{meta.inspect($item)}`; a position on its own goes between two colours";
78
+ }
79
+ @if $i == $count {
80
+ @return "it ends with the position `#{meta.inspect($item)}`; a position on its own goes between two colours";
81
+ }
82
+ @if $previous == "hint" {
83
+ @return "it has two positions in a row; a position on its own goes between two colours";
84
+ }
85
+ } @else {
86
+ $colors-seen: $colors-seen + 1;
87
+ }
88
+ $previous: $kind;
89
+ }
90
+ @if $colors-seen == 0 {
91
+ @return "a gradient needs at least one colour";
92
+ }
93
+ @return null;
94
+ }
@@ -0,0 +1,22 @@
1
+ @charset "UTF-8";
2
+
3
+ @use "sass:meta";
4
+ @use "../utilities/validate-breakpoint" as *;
5
+ @use "is-condition-value" as *;
6
+
7
+ // Shared by the mixins and not public API; see _is-css-function.scss.
8
+
9
+ // Resolves a size for a width condition and refuses one no condition can match.
10
+ // validateBreakpoint turns a key into its width and refuses a word that is not a
11
+ // key; it passes a percentage or a bare number through, because a declaration
12
+ // takes them. A width condition does not: `(min-width: 50%)` and
13
+ // `(min-width: 768)` never matched in Chrome 152, so they are refused here.
14
+ // Returns the same value validateBreakpoint does, so a call that worked writes
15
+ // the same query.
16
+ @function conditionWidth($value, $mixin, $at-rule: "@media") {
17
+ $width: validateBreakpoint($value);
18
+ @if not isConditionValue($width, "length") {
19
+ @error "`#{meta.inspect($value)}` cannot be used as a size in `#{$mixin}`: a width condition takes a length, and one written with `#{meta.inspect($width)}` would never match in #{$at-rule}. Pass a length such as `600px`, `40em` or `calc(30em + 1px)`, or a key from $map-for-breakpoints.";
20
+ }
21
+ @return $width;
22
+ }
@@ -0,0 +1,25 @@
1
+ @charset "UTF-8";
2
+
3
+ @use "sass:meta";
4
+ @use "sass:string";
5
+
6
+ // Shared by the mixins and not public API; see _is-css-function.scss.
7
+
8
+ // Returns the first value in $value that holds var(), looking inside lists and
9
+ // calculations, or null. Custom properties are not evaluated in a @media or
10
+ // @container condition, so a rule built from one never applies, and nothing
11
+ // warns. Measured in Chrome 152: `(min-width: var(--wide))` did not match with
12
+ // `--wide: 200px`, while `(min-width: 200px)` did.
13
+ @function customPropertyIn($value) {
14
+ @each $item in $value {
15
+ @if meta.type-of($item) == "list" or meta.type-of($item) == "arglist" {
16
+ $found: customPropertyIn($item);
17
+ @if $found {
18
+ @return $found;
19
+ }
20
+ } @else if string.index(meta.inspect($item), "var(") {
21
+ @return $item;
22
+ }
23
+ }
24
+ @return null;
25
+ }
@@ -0,0 +1,68 @@
1
+ @charset "UTF-8";
2
+
3
+ @use "sass:meta";
4
+ @use "sass:string";
5
+ @use "is-css-function" as *;
6
+
7
+ // Shared by the mixins and not public API; see _is-css-function.scss.
8
+
9
+ // An image path goes into url(). A var(), url(), image-set() or gradient is
10
+ // already an image, and wrapping it produced url(var(--x)), which loads nothing.
11
+ //
12
+ // $unquote is there because the copies this replaced differed:
13
+ // background-dots and background-stripes wrote `url(a.png)`, the other three
14
+ // `url("a.png")`. Both load the same file, and keeping each as it was means no
15
+ // stylesheet changes.
16
+ //
17
+ // What it refuses, measured in Chrome 152:
18
+ //
19
+ // - a list where one image is meant: `url(16 9)` was dropped;
20
+ // - a number, a colour or a boolean: `url(42)`, `url(#ff0000)` and `url(true)`
21
+ // are kept, but ask for a file named 42, a fragment of the page, or a file
22
+ // named true, which is never the image meant. background-dots and
23
+ // background-stripes refused these already.
24
+ //
25
+ // And an unquoted path is quoted when it has to be: `url(a b)`, `url(a(1).png)`
26
+ // and `url(a'b.png)` were dropped, while the same paths in quotes were kept and
27
+ // `url("a b")` resolved to `a%20b`. Every other path is written as before.
28
+ @function imageValue($value, $mixin, $argument, $unquote: false) {
29
+ // paint(), image() and -webkit-cross-fade() were kept as images in Chrome
30
+ // 152 and used to be wrapped into url(paint(...)), which loads nothing;
31
+ // cross-fade(), element() and -moz-element() are in the specification or
32
+ // other engines.
33
+ @if isCssFunction($value, "var" "url" "image-set" "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") {
34
+ @return $value;
35
+ }
36
+ @if meta.type-of($value) == "list" or meta.type-of($value) == "arglist" or meta.type-of($value) == "map" {
37
+ @error "`#{meta.inspect($value)}` is not a valid #{$argument} for `#{$mixin}`: it is a list where one image is meant, and a browser drops `url()` with a list in it. Pass one path as a string, such as `\"/img/hero.png\"`.";
38
+ }
39
+ @if meta.type-of($value) != "string" {
40
+ @error "`#{meta.inspect($value)}` is not a valid #{$argument} for `#{$mixin}`: an image path is a string, and `url(#{meta.inspect($value)})` asks for a file of that name, which is never the image. Pass a path as a string, such as `\"/img/hero.png\"`, or var() or a gradient.";
41
+ }
42
+ @if not $unquote {
43
+ @return url($value);
44
+ }
45
+ $text: string.unquote($value);
46
+ @if not (string.index($text, " ") or string.index($text, "(") or string.index($text, ")") or string.index($text, "'") or string.index($text, "\"")) {
47
+ @return url(string.unquote($value));
48
+ }
49
+ @if string.index($text, "\"") {
50
+ @return string.unquote("url('#{-escaped-single-quotes($text)}')");
51
+ }
52
+ @return string.unquote("url(\"#{$text}\")");
53
+ }
54
+
55
+ // A path holding a double quote goes into single quotes, so any single quote in
56
+ // it is escaped.
57
+ @function -escaped-single-quotes($text) {
58
+ $result: "";
59
+ @for $i from 1 through string.length($text) {
60
+ $character: string.slice($text, $i, $i);
61
+ @if $character == "'" {
62
+ $result: $result + "\\'";
63
+ } @else {
64
+ $result: $result + $character;
65
+ }
66
+ }
67
+ @return $result;
68
+ }
@@ -0,0 +1,94 @@
1
+ @charset "UTF-8";
2
+
3
+ @use "sass:list";
4
+ @use "sass:math";
5
+ @use "sass:meta";
6
+ @use "sass:string";
7
+
8
+ // Shared by the mixins and not public API; see _is-css-function.scss.
9
+
10
+ // Every length unit a width condition kept in Chrome 152, in @media and
11
+ // @container alike and in any case: `(min-width: 10PX)` matched. `x`, `dppx`,
12
+ // `fr`, `s`, `%` and a made-up unit never matched, and neither did a unitless
13
+ // number other than 0.
14
+ $-length-units: "px" "cm" "mm" "q" "in" "pt" "pc" "em" "rem" "ex" "rex" "ch" "rch" "cap" "rcap" "ic" "ric" "lh" "rlh" "vw" "vh" "vi" "vb" "vmin" "vmax" "svw" "svh" "svi" "svb" "svmin" "svmax" "lvw" "lvh" "lvi" "lvb" "lvmin" "lvmax" "dvw" "dvh" "dvi" "dvb" "dvmin" "dvmax" "cqw" "cqh" "cqi" "cqb" "cqmin" "cqmax";
15
+
16
+ // The units a resolution condition kept, also in any case. A unitless number, a
17
+ // length and a negative resolution never matched; `0dppx` was kept.
18
+ $-resolution-units: "dpi" "dpcm" "dppx" "x";
19
+
20
+ // Angles, for an oblique `font-style`: `oblique 0.2rad` was kept in a @font-face
21
+ // rule and `oblique 10`, unitless, was dropped.
22
+ $-angle-units: "deg" "grad" "rad" "turn";
23
+
24
+ // True for a value a @media or @container condition can match: a number in one
25
+ // of the units above, a calculation, or either written as a quoted string, which
26
+ // Sass writes into a condition without its quotes, so `breakpoint(min, "600px")`
27
+ // has always worked. A calculation is taken on trust: `calc(30em + 1px)`,
28
+ // `clamp()`, `min()` and `max()` were all kept, and Sass reduces `calc(10deg)`
29
+ // to a number before it gets here, so that one is checked like any other.
30
+ //
31
+ // $kind is "length" or "resolution" for a condition. A negative length was
32
+ // kept, since it is valid and simply matches every width or none; a negative
33
+ // resolution was not. Two more kinds serve values outside a condition, where a
34
+ // caller rules out a negative itself if the property does: "length-percentage",
35
+ // a length or a percentage, and "angle".
36
+ @function isConditionValue($value, $kind: "length") {
37
+ $units: $-length-units;
38
+ @if $kind == "resolution" {
39
+ $units: $-resolution-units;
40
+ } @else if $kind == "length-percentage" {
41
+ $units: list.append($-length-units, "%");
42
+ } @else if $kind == "angle" {
43
+ $units: $-angle-units;
44
+ }
45
+
46
+ @if meta.type-of($value) == "calculation" {
47
+ @return true;
48
+ }
49
+ @if meta.type-of($value) == "number" {
50
+ @if math.is-unitless($value) {
51
+ @return ($kind == "length" or $kind == "length-percentage") and $value == 0;
52
+ }
53
+ @if $kind == "resolution" and $value < 0 {
54
+ @return false;
55
+ }
56
+ @return list.index($units, string.to-lower-case(math.unit($value))) != null;
57
+ }
58
+ @if meta.type-of($value) != "string" {
59
+ @return false;
60
+ }
61
+
62
+ $text: string.to-lower-case(string.unquote($value));
63
+ @each $function in "calc(" "clamp(" "min(" "max(" {
64
+ @if string.index($text, $function) == 1 {
65
+ @return true;
66
+ }
67
+ }
68
+
69
+ // A quoted length, such as "600px": the number runs up to the first
70
+ // character that cannot be part of one, and the rest is the unit.
71
+ $unit-start: null;
72
+ $digits: 0;
73
+ @for $i from 1 through string.length($text) {
74
+ $character: string.slice($text, $i, $i);
75
+ @if $unit-start == null {
76
+ @if string.index("0123456789", $character) {
77
+ $digits: $digits + 1;
78
+ } @else if not string.index(".+-", $character) {
79
+ $unit-start: $i;
80
+ }
81
+ }
82
+ }
83
+ @if $digits == 0 {
84
+ @return false;
85
+ }
86
+ @if $unit-start == null {
87
+ // A quoted unitless number: only zero, and only for a length.
88
+ @return ($kind == "length" or $kind == "length-percentage") and not string.index($text, "1") and not string.index($text, "2") and not string.index($text, "3") and not string.index($text, "4") and not string.index($text, "5") and not string.index($text, "6") and not string.index($text, "7") and not string.index($text, "8") and not string.index($text, "9");
89
+ }
90
+ @if $kind == "resolution" and string.index(string.slice($text, 1, $unit-start - 1), "-") {
91
+ @return false;
92
+ }
93
+ @return list.index($units, string.slice($text, $unit-start)) != null;
94
+ }
@@ -0,0 +1,21 @@
1
+ @charset "UTF-8";
2
+
3
+ @use "sass:list";
4
+ @use "sass:meta";
5
+ @use "sass:string";
6
+
7
+ // scss/internal/ holds checks the mixins share. No _index.scss forwards it, so
8
+ // a user cannot reach these functions and they are not public API: through
9
+ // `@use "gerillass" as *` or `@import` a call to one renders as literal CSS,
10
+ // and through a namespace it is an undefined function. Their names must not
11
+ // start with `-` or `_`, which would make them private to their own file.
12
+
13
+ // True for an unquoted call to one of $names, such as var(--x). A quoted string
14
+ // stays text, so `"var(--x)"` is not a call.
15
+ @function isCssFunction($value, $names) {
16
+ @if meta.type-of($value) != "string" or string.slice(meta.inspect($value), 1, 1) == "\"" {
17
+ @return false;
18
+ }
19
+ $open: string.index($value, "(");
20
+ @return $open != null and list.index($names, string.to-lower-case(string.slice($value, 1, $open - 1))) != null;
21
+ }
@@ -0,0 +1,22 @@
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
+ // A size Sass can use inside calc(): a number, a calculation such as calc(),
10
+ // min() or clamp(), or var() and env(), which only the browser can resolve.
11
+ // Anything else would pass silently through calc(): `calc(nonsense * 5)`
12
+ // compiles.
13
+ @function isSize($value) {
14
+ @if meta.type-of($value) == "number" or meta.type-of($value) == "calculation" {
15
+ @return true;
16
+ }
17
+ @if meta.type-of($value) == "string" and string.slice(meta.inspect($value), 1, 1) != "\"" {
18
+ $open: string.index($value, "(");
19
+ @return $open != null and list.index("var" "env", string.to-lower-case(string.slice($value, 1, $open - 1))) != null;
20
+ }
21
+ @return false;
22
+ }
@@ -0,0 +1,76 @@
1
+ @charset "UTF-8";
2
+
3
+ @use "sass:list";
4
+ @use "sass:meta";
5
+ @use "sass:string";
6
+ @use "words" as *;
7
+
8
+ // Shared by the mixins and not public API; see _is-css-function.scss.
9
+
10
+ $-css-wide: "inherit" "initial" "unset" "revert" "revert-layer";
11
+
12
+ // Returns what is wrong with $value as a keyword value, or null when it is fine.
13
+ // It checks the kind of each word, not whether the words make sense together;
14
+ // a mixin whose property has a grammar, such as display, checks that itself.
15
+ //
16
+ // - null is fine: the declaration is left out, as it always was.
17
+ // - Any word holding a parenthesis makes the whole value fine when $functions is
18
+ // true. var(), env() and calc() are resolved by the browser, and a value such
19
+ // as `var(--x) auto` cannot be judged here. In a @font-face descriptor, which
20
+ // drops var(), the caller passes false.
21
+ // - Up to $max-words words, each one of $keywords in any case; `STICKY` and
22
+ // `BOTH` were kept in Chrome 152.
23
+ // - A CSS-wide keyword only on its own: `overflow: inherit auto` was dropped.
24
+ // - A vendor-prefixed word when $vendor is true. Chrome drops `-webkit-sticky`
25
+ // and `-moz-box`, but other engines use them, so refusing one could break a
26
+ // stylesheet written for Safari.
27
+ @function keywordProblem($value, $keywords, $max-words: 1, $functions: true, $vendor: false) {
28
+ @if $value == null {
29
+ @return null;
30
+ }
31
+ @if meta.type-of($value) == "list" and list.separator($value) == "comma" {
32
+ @return "it takes a space-separated value, not a comma-separated list";
33
+ }
34
+ $words: wordsOf($value);
35
+ @if list.length($words) == 0 {
36
+ @return "it is empty";
37
+ }
38
+ @each $word in $words {
39
+ @if meta.type-of($word) == "string" and string.index($word, "(") {
40
+ @if $functions {
41
+ @return null;
42
+ }
43
+ @return "a function cannot supply it here";
44
+ }
45
+ }
46
+ @if list.length($words) > $max-words {
47
+ @if $max-words == 1 {
48
+ @return "it takes one keyword, and got #{list.length($words)}";
49
+ }
50
+ @return "it takes at most #{$max-words} keywords, and got #{list.length($words)}";
51
+ }
52
+ $expected: list.join($keywords, (), $separator: comma);
53
+ @each $word in $words {
54
+ @if meta.type-of($word) != "string" {
55
+ @if list.length($words) == 1 {
56
+ @return "it takes a keyword, one of: #{$expected}";
57
+ }
58
+ @return "`#{meta.inspect($word)}` is not a keyword; pass words from: #{$expected}";
59
+ }
60
+ $lower: string.to-lower-case(string.unquote($word));
61
+ @if list.index($-css-wide, $lower) {
62
+ @if list.length($words) > 1 {
63
+ @return "`#{$lower}` only works as the whole value";
64
+ }
65
+ } @else if not list.index($keywords, $lower) {
66
+ $prefixed: string.index($lower, "-webkit-") == 1 or string.index($lower, "-moz-") == 1 or string.index($lower, "-ms-") == 1 or string.index($lower, "-o-") == 1;
67
+ @if not ($vendor and $prefixed) {
68
+ @if list.length($words) == 1 {
69
+ @return "it is not one of: #{$expected}";
70
+ }
71
+ @return "`#{$lower}` is not one of: #{$expected}";
72
+ }
73
+ }
74
+ }
75
+ @return null;
76
+ }