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.
- package/SKILL.md +17 -1
- package/gerillass.json +287 -68
- package/package.json +1 -1
- package/scss/internal/_color-problem.scss +99 -0
- package/scss/internal/_color-stops-problem.scss +94 -0
- package/scss/internal/_condition-width.scss +22 -0
- package/scss/internal/_custom-property-in.scss +25 -0
- package/scss/internal/_image-value.scss +68 -0
- package/scss/internal/_is-condition-value.scss +94 -0
- package/scss/internal/_is-css-function.scss +21 -0
- package/scss/internal/_is-size.scss +22 -0
- package/scss/internal/_keyword-value.scss +76 -0
- package/scss/internal/_length-problem.scss +127 -0
- package/scss/internal/_sibling-index.scss +51 -0
- package/scss/internal/_words.scss +35 -0
- package/scss/library/_adaptive.scss +17 -0
- package/scss/library/_after.scss +2 -12
- package/scss/library/_background-dots.scss +29 -37
- package/scss/library/_background-image.scss +37 -25
- package/scss/library/_background-stripes.scss +34 -37
- package/scss/library/_before.scss +2 -12
- package/scss/library/_border-radius.scss +83 -0
- package/scss/library/_brand-logo.scss +10 -24
- package/scss/library/_breakpoint.scss +16 -23
- package/scss/library/_circle.scss +7 -0
- package/scss/library/_columnizer.scss +62 -2
- package/scss/library/_container-query.scss +11 -27
- package/scss/library/_container.scss +2 -19
- package/scss/library/_counter.scss +3 -14
- package/scss/library/_ellipsis.scss +78 -1
- package/scss/library/_except.scss +15 -0
- package/scss/library/_focus-ring.scss +29 -0
- package/scss/library/_font-face.scss +117 -1
- package/scss/library/_line-clamp.scss +2 -13
- package/scss/library/_linear-gradient.scss +7 -0
- package/scss/library/_only.scss +16 -0
- package/scss/library/_position.scss +34 -3
- package/scss/library/_radial-gradient.scss +154 -0
- package/scss/library/_resizable.scss +29 -2
- package/scss/library/_screen-agent.scss +8 -22
- package/scss/library/_sizer.scss +9 -0
- package/scss/library/_sprite.scss +65 -0
- package/scss/library/_text-gradient.scss +8 -0
- package/scss/library/_text-image.scss +2 -23
- package/scss/library/_text-stroke.scss +30 -0
- package/scss/library/_triangle.scss +25 -56
- package/scss/utilities/_validate-breakpoint.scss +32 -6
- package/scss/utilities/_validate-scissors.scss +7 -0
|
@@ -2,10 +2,113 @@
|
|
|
2
2
|
|
|
3
3
|
@use "sass:list";
|
|
4
4
|
@use "sass:map";
|
|
5
|
+
@use "sass:math";
|
|
5
6
|
@use "sass:meta";
|
|
6
7
|
@use "sass:string";
|
|
7
8
|
@use "../maps/map-for-font-formats" as *;
|
|
8
9
|
@use "../utilities/font-source" as *;
|
|
10
|
+
@use "../internal/is-condition-value" as *;
|
|
11
|
+
@use "../internal/words" as *;
|
|
12
|
+
|
|
13
|
+
// What a @font-face rule keeps in `font-style` and `font-weight`, measured in
|
|
14
|
+
// Chrome 152 by inserting the rule and reading the descriptor back, in any
|
|
15
|
+
// case. font-style kept normal, italic, auto, oblique, and oblique with one or
|
|
16
|
+
// two angles from -90deg to 90deg (`oblique 10deg 20deg`, `oblique 0.2rad`),
|
|
17
|
+
// and dropped a CSS-wide keyword, `oblique 100deg`, an angle without a unit
|
|
18
|
+
// and a weight such as `400`. font-weight kept normal, bold, auto, a number
|
|
19
|
+
// from 1 to 1000 (`450`, `400.5`, `calc(400 + 100)`) and two numbers as a range
|
|
20
|
+
// in either order, and dropped bolder and lighter, a CSS-wide keyword, 0, 1001,
|
|
21
|
+
// three numbers, and a keyword beside a number or another keyword.
|
|
22
|
+
//
|
|
23
|
+
// A quoted value is split on its spaces and checked word by word, but Sass
|
|
24
|
+
// cannot turn the text of a number into one, so a quoted number is checked for
|
|
25
|
+
// its shape and not its range: "1001" and "oblique 100deg" are let through.
|
|
26
|
+
|
|
27
|
+
// A quoted or unquoted number, without a unit, for a weight.
|
|
28
|
+
@function -is-weight-number($word) {
|
|
29
|
+
@if meta.type-of($word) == "number" {
|
|
30
|
+
@return math.is-unitless($word) and $word >= 1 and $word <= 1000;
|
|
31
|
+
}
|
|
32
|
+
@if meta.type-of($word) != "string" {
|
|
33
|
+
@return false;
|
|
34
|
+
}
|
|
35
|
+
$text: string.unquote($word);
|
|
36
|
+
$nonzero: false;
|
|
37
|
+
@for $i from 1 through string.length($text) {
|
|
38
|
+
$character: string.slice($text, $i, $i);
|
|
39
|
+
@if not string.index("0123456789.", $character) {
|
|
40
|
+
@return false;
|
|
41
|
+
}
|
|
42
|
+
@if string.index("123456789", $character) {
|
|
43
|
+
$nonzero: true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
@return $nonzero;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@function -weight-problem($weight) {
|
|
50
|
+
@if $weight == null or meta.type-of($weight) == "calculation" {
|
|
51
|
+
@return null;
|
|
52
|
+
}
|
|
53
|
+
$words: wordsOf($weight);
|
|
54
|
+
@if list.length($words) > 2 {
|
|
55
|
+
@return "a weight is one value, or two numbers for a range such as `100 900`";
|
|
56
|
+
}
|
|
57
|
+
@if list.length($words) == 1 {
|
|
58
|
+
$word: list.nth($words, 1);
|
|
59
|
+
@if meta.type-of($word) == "string" and list.index("normal" "bold" "auto", string.to-lower-case(string.unquote($word))) {
|
|
60
|
+
@return null;
|
|
61
|
+
}
|
|
62
|
+
@if -is-weight-number($word) {
|
|
63
|
+
@return null;
|
|
64
|
+
}
|
|
65
|
+
@return "a weight is normal, bold, auto or a number from 1 to 1000; bolder, lighter and the CSS-wide keywords are dropped";
|
|
66
|
+
}
|
|
67
|
+
@each $word in $words {
|
|
68
|
+
@if not -is-weight-number($word) {
|
|
69
|
+
@return "a range is two numbers from 1 to 1000, such as `100 900`";
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
@return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@function -style-problem($style) {
|
|
76
|
+
@if $style == null {
|
|
77
|
+
@return null;
|
|
78
|
+
}
|
|
79
|
+
$words: wordsOf($style);
|
|
80
|
+
$first: list.nth($words, 1);
|
|
81
|
+
$keyword: null;
|
|
82
|
+
@if meta.type-of($first) == "string" {
|
|
83
|
+
$keyword: string.to-lower-case(string.unquote($first));
|
|
84
|
+
}
|
|
85
|
+
@if list.index("normal" "italic" "auto", $keyword) {
|
|
86
|
+
@if list.length($words) > 1 {
|
|
87
|
+
@return "`#{$keyword}` takes nothing after it; only oblique takes angles";
|
|
88
|
+
}
|
|
89
|
+
@return null;
|
|
90
|
+
}
|
|
91
|
+
@if $keyword != "oblique" {
|
|
92
|
+
@return "a style is normal, italic, oblique or auto; the CSS-wide keywords are dropped";
|
|
93
|
+
}
|
|
94
|
+
@if list.length($words) > 3 {
|
|
95
|
+
@return "oblique takes at most two angles";
|
|
96
|
+
}
|
|
97
|
+
@for $i from 2 through list.length($words) {
|
|
98
|
+
$angle: list.nth($words, $i);
|
|
99
|
+
@if meta.type-of($angle) == "number" {
|
|
100
|
+
@if not isConditionValue($angle, "angle") {
|
|
101
|
+
@return "`#{$angle}` is not an angle; pass one such as `10deg`";
|
|
102
|
+
}
|
|
103
|
+
@if $angle < -90deg or $angle > 90deg {
|
|
104
|
+
@return "an oblique angle is between -90deg and 90deg";
|
|
105
|
+
}
|
|
106
|
+
} @else if not isConditionValue($angle, "angle") {
|
|
107
|
+
@return "`#{$angle}` is not an angle; pass one such as `10deg`";
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
@return null;
|
|
111
|
+
}
|
|
9
112
|
|
|
10
113
|
@mixin font-face(
|
|
11
114
|
$font-family,
|
|
@@ -51,7 +154,10 @@
|
|
|
51
154
|
@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`.";
|
|
52
155
|
}
|
|
53
156
|
|
|
54
|
-
|
|
157
|
+
// A number in $font-style is a weight. Only the hundreds used to be read
|
|
158
|
+
// that way, so `font-face("I", "/f/i", 450)` wrote `font-style: 450`, which
|
|
159
|
+
// a @font-face rule drops; any weight a rule keeps, 1 to 1000, is read so.
|
|
160
|
+
@if meta.type-of($font-style) == "number" and math.is-unitless($font-style) and $font-style >= 1 and $font-style <= 1000 {
|
|
55
161
|
$font-weight: $font-style;
|
|
56
162
|
$font-style: normal;
|
|
57
163
|
} @else if not
|
|
@@ -71,6 +177,16 @@
|
|
|
71
177
|
}
|
|
72
178
|
}
|
|
73
179
|
|
|
180
|
+
// Checked after the reinterpretation above, on the values that are written.
|
|
181
|
+
$problem: -style-problem($font-style);
|
|
182
|
+
@if $problem {
|
|
183
|
+
@error "`#{meta.inspect($font-style)}` is not a valid $font-style for `font-face`: #{$problem}. A @font-face rule drops the descriptor.";
|
|
184
|
+
}
|
|
185
|
+
$problem: -weight-problem($font-weight);
|
|
186
|
+
@if $problem {
|
|
187
|
+
@error "`#{meta.inspect($font-weight)}` is not a valid $font-weight for `font-face`: #{$problem}. A @font-face rule drops the descriptor.";
|
|
188
|
+
}
|
|
189
|
+
|
|
74
190
|
// $font-display comes last so the reinterpretation of $font-style above is
|
|
75
191
|
// left alone, and it is written only when given: the browser's default is
|
|
76
192
|
// what a call without it has always had. Measured in Chrome 152, a
|
|
@@ -1,19 +1,8 @@
|
|
|
1
1
|
@charset "UTF-8";
|
|
2
2
|
|
|
3
|
-
@use "sass:list";
|
|
4
3
|
@use "sass:math";
|
|
5
4
|
@use "sass:meta";
|
|
6
|
-
@use "
|
|
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
|
+
@use "../internal/is-css-function" as *;
|
|
17
6
|
|
|
18
7
|
// Truncates text after a number of lines. `ellipsis` does one line; this does
|
|
19
8
|
// several, and needs five declarations rather than one because -webkit-line-clamp
|
|
@@ -33,7 +22,7 @@
|
|
|
33
22
|
// what actually does the work today.
|
|
34
23
|
@mixin line-clamp($lines: 3) {
|
|
35
24
|
// var() is resolved by the browser, so only a literal count is checked.
|
|
36
|
-
@if $lines != none and not
|
|
25
|
+
@if $lines != none and not isCssFunction($lines, "var" "env") {
|
|
37
26
|
@if meta.type-of($lines) != "number" or not math.is-unitless($lines) {
|
|
38
27
|
@error "`#{$lines}` is not a valid $lines for `line-clamp`. Pass a whole number of lines, or `none` to undo a clamp.";
|
|
39
28
|
}
|
|
@@ -5,8 +5,15 @@
|
|
|
5
5
|
@use "sass:math";
|
|
6
6
|
@use "sass:meta";
|
|
7
7
|
@use "../maps/map-for-directions" as *;
|
|
8
|
+
@use "../internal/color-stops-problem" as *;
|
|
8
9
|
|
|
9
10
|
@mixin linear-gradient($direction, $colors) {
|
|
11
|
+
// A stop the browser does not keep drops the whole gradient; see
|
|
12
|
+
// scss/internal/_color-stops-problem.scss for what was measured.
|
|
13
|
+
$problem: colorStopsProblem($colors);
|
|
14
|
+
@if $problem {
|
|
15
|
+
@error "`#{meta.inspect($colors)}` is not a valid $colors for `linear-gradient`: #{$problem}. A browser drops the whole gradient.";
|
|
16
|
+
}
|
|
10
17
|
$list: ();
|
|
11
18
|
@for $i from 1 through list.length($colors) {
|
|
12
19
|
$list: list.append($list, list.nth($colors, $i), comma);
|
package/scss/library/_only.scss
CHANGED
|
@@ -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 "../
|
|
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:
|
|
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
|
|
68
|
+
@if $value != null {
|
|
38
69
|
#{list.nth($edges, $i)}: $value;
|
|
39
70
|
}
|
|
40
71
|
}
|
|
@@ -5,6 +5,148 @@
|
|
|
5
5
|
@use "sass:meta";
|
|
6
6
|
@use "sass:string";
|
|
7
7
|
@use "../maps/map-for-positions" as *;
|
|
8
|
+
@use "../internal/is-condition-value" as *;
|
|
9
|
+
@use "../internal/words" as *;
|
|
10
|
+
@use "../internal/color-stops-problem" as *;
|
|
11
|
+
|
|
12
|
+
// The mixin writes `radial-gradient($shape $position, ...)`, so $shape carries
|
|
13
|
+
// the shape and may carry a size, and a $position that is not a key from
|
|
14
|
+
// $map-for-positions carries a size, `at` and a position, or both. Each is
|
|
15
|
+
// checked against what Chrome 152 kept in `background-image`, in any case:
|
|
16
|
+
//
|
|
17
|
+
// - kept: circle, ellipse, an extent keyword such as closest-side, a length
|
|
18
|
+
// (`circle 10px`, `10px`), two lengths or percentages (`ellipse 10% 20%`),
|
|
19
|
+
// `at` followed by one or two keywords from left, center, right, top and
|
|
20
|
+
// bottom or up to four keywords and lengths (`at left 10px top 20px`), and
|
|
21
|
+
// var() or calc() anywhere;
|
|
22
|
+
// - dropped: a word outside those (`huge`, `auto`, `inherit`), a percentage or a
|
|
23
|
+
// negative length as a circle's size (`circle 10%`, `circle -10px`), the
|
|
24
|
+
// shape twice (`circle circle`), four keywords after `at`, and the logical
|
|
25
|
+
// keywords `at x-start` and `at block-start`.
|
|
26
|
+
//
|
|
27
|
+
// It checks each part on its own, so a shape in one argument and a size in the
|
|
28
|
+
// other that do not fit together, such as `ellipse` with a single length, are
|
|
29
|
+
// not caught.
|
|
30
|
+
$-extents: "closest-side" "closest-corner" "farthest-side" "farthest-corner";
|
|
31
|
+
$-places: "left" "center" "right" "top" "bottom";
|
|
32
|
+
|
|
33
|
+
// "extent", "length" or "percentage" for a word that can size the gradient,
|
|
34
|
+
// or null.
|
|
35
|
+
@function -size-kind($word) {
|
|
36
|
+
$lower: string.to-lower-case("#{$word}");
|
|
37
|
+
@if list.index($-extents, $lower) {
|
|
38
|
+
@return "extent";
|
|
39
|
+
}
|
|
40
|
+
@if string.index($lower, "-") == 1 {
|
|
41
|
+
@return null;
|
|
42
|
+
}
|
|
43
|
+
@if isConditionValue($word, "length") {
|
|
44
|
+
@return "length";
|
|
45
|
+
}
|
|
46
|
+
@if isConditionValue($word, "length-percentage") {
|
|
47
|
+
@return "percentage";
|
|
48
|
+
}
|
|
49
|
+
@return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Checks the size words before `at`. $shape is the shape named, or null.
|
|
53
|
+
@function -size-problem($words, $shape) {
|
|
54
|
+
$extents: 0;
|
|
55
|
+
$lengths: 0;
|
|
56
|
+
$percentages: 0;
|
|
57
|
+
@each $word in $words {
|
|
58
|
+
$kind: -size-kind($word);
|
|
59
|
+
@if $kind == null {
|
|
60
|
+
@return "`#{$word}` is not a shape, an extent keyword such as `closest-side`, or a length";
|
|
61
|
+
} @else if $kind == "extent" {
|
|
62
|
+
$extents: $extents + 1;
|
|
63
|
+
} @else if $kind == "length" {
|
|
64
|
+
$lengths: $lengths + 1;
|
|
65
|
+
} @else {
|
|
66
|
+
$percentages: $percentages + 1;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
$sizes: $lengths + $percentages;
|
|
70
|
+
@if $extents > 1 or ($extents == 1 and $sizes > 0) {
|
|
71
|
+
@return "it gives the size twice";
|
|
72
|
+
}
|
|
73
|
+
@if $sizes > 2 {
|
|
74
|
+
@return "a size is one length or two";
|
|
75
|
+
}
|
|
76
|
+
@if $sizes == 1 and ($percentages == 1 or $shape == "ellipse") {
|
|
77
|
+
@return "one size is a circle's radius, which is a length and not a percentage";
|
|
78
|
+
}
|
|
79
|
+
@if $sizes == 2 and $shape == "circle" {
|
|
80
|
+
@return "a circle takes one length, not two";
|
|
81
|
+
}
|
|
82
|
+
@return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@function -shape-problem($shape) {
|
|
86
|
+
@if string.index($shape, "(") {
|
|
87
|
+
@return null;
|
|
88
|
+
}
|
|
89
|
+
$named: null;
|
|
90
|
+
$sizes: ();
|
|
91
|
+
@each $word in wordsOf($shape) {
|
|
92
|
+
$lower: string.to-lower-case($word);
|
|
93
|
+
@if list.index("circle" "ellipse", $lower) {
|
|
94
|
+
@if $named {
|
|
95
|
+
@return "it names the shape twice";
|
|
96
|
+
}
|
|
97
|
+
$named: $lower;
|
|
98
|
+
} @else {
|
|
99
|
+
$sizes: list.append($sizes, $word);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
@return -size-problem($sizes, $named);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
@function -position-problem($position) {
|
|
106
|
+
@if map.has-key($map-for-positions, $position) or string.index($position, "(") {
|
|
107
|
+
@return null;
|
|
108
|
+
}
|
|
109
|
+
$sizes: ();
|
|
110
|
+
$places: ();
|
|
111
|
+
$after-at: false;
|
|
112
|
+
@each $word in wordsOf($position) {
|
|
113
|
+
$lower: string.to-lower-case($word);
|
|
114
|
+
@if $lower == "at" {
|
|
115
|
+
@if $after-at {
|
|
116
|
+
@return "it has `at` twice";
|
|
117
|
+
}
|
|
118
|
+
$after-at: true;
|
|
119
|
+
} @else if $after-at {
|
|
120
|
+
$places: list.append($places, $word);
|
|
121
|
+
} @else if list.index("circle" "ellipse", $lower) {
|
|
122
|
+
@return "the shape goes in $shape, not $position";
|
|
123
|
+
} @else {
|
|
124
|
+
$sizes: list.append($sizes, $word);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
$problem: -size-problem($sizes, null);
|
|
128
|
+
@if $problem {
|
|
129
|
+
@return "#{$problem}. Pass a position name such as `center` or `top-left`, an extent keyword such as `closest-side`, or `at` followed by a position, such as `at 30% 40%`";
|
|
130
|
+
}
|
|
131
|
+
@if $after-at and list.length($places) == 0 {
|
|
132
|
+
@return "`at` needs a position after it, such as `at center`";
|
|
133
|
+
}
|
|
134
|
+
@if list.length($places) > 4 {
|
|
135
|
+
@return "a position takes at most four values";
|
|
136
|
+
}
|
|
137
|
+
$keywords: 0;
|
|
138
|
+
@each $word in $places {
|
|
139
|
+
@if list.index($-places, string.to-lower-case($word)) {
|
|
140
|
+
$keywords: $keywords + 1;
|
|
141
|
+
} @else if not isConditionValue($word, "length-percentage") {
|
|
142
|
+
@return "`#{$word}` is not a position; after `at` pass left, center, right, top, bottom or a length";
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
@if $keywords > 2 and $keywords == list.length($places) {
|
|
146
|
+
@return "a position of keywords alone takes at most two, such as `at top left`";
|
|
147
|
+
}
|
|
148
|
+
@return null;
|
|
149
|
+
}
|
|
8
150
|
|
|
9
151
|
@mixin radial-gradient($shape, $position, $colors) {
|
|
10
152
|
@if meta.type-of($shape) != "string" {
|
|
@@ -16,6 +158,18 @@
|
|
|
16
158
|
$list: ();
|
|
17
159
|
$shape: string.unquote($shape);
|
|
18
160
|
$position: string.unquote($position);
|
|
161
|
+
$problem: -shape-problem($shape);
|
|
162
|
+
@if $problem {
|
|
163
|
+
@error "`#{$shape}` is not a valid $shape for `radial-gradient`: #{$problem}. A browser drops the whole gradient.";
|
|
164
|
+
}
|
|
165
|
+
$problem: -position-problem($position);
|
|
166
|
+
@if $problem {
|
|
167
|
+
@error "`#{$position}` is not a valid $position for `radial-gradient`: #{$problem}. A browser drops the whole gradient.";
|
|
168
|
+
}
|
|
169
|
+
$problem: colorStopsProblem($colors);
|
|
170
|
+
@if $problem {
|
|
171
|
+
@error "`#{meta.inspect($colors)}` is not a valid $colors for `radial-gradient`: #{$problem}. A browser drops the whole gradient.";
|
|
172
|
+
}
|
|
19
173
|
@for $i from 1 through list.length($colors) {
|
|
20
174
|
$list: list.append($list, list.nth($colors, $i), comma);
|
|
21
175
|
}
|
|
@@ -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
|
-
|
|
5
|
-
|
|
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 "
|
|
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:
|
|
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
|
}
|
package/scss/library/_sizer.scss
CHANGED
|
@@ -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;
|
|
@@ -5,8 +5,16 @@
|
|
|
5
5
|
@use "sass:math";
|
|
6
6
|
@use "sass:meta";
|
|
7
7
|
@use "../maps/map-for-directions" as *;
|
|
8
|
+
@use "../internal/color-stops-problem" as *;
|
|
8
9
|
|
|
9
10
|
@mixin text-gradient($direction, $colors) {
|
|
11
|
+
// A stop the browser does not keep drops the whole gradient, and with it the
|
|
12
|
+
// text, which this mixin makes transparent; see
|
|
13
|
+
// scss/internal/_color-stops-problem.scss.
|
|
14
|
+
$problem: colorStopsProblem($colors);
|
|
15
|
+
@if $problem {
|
|
16
|
+
@error "`#{meta.inspect($colors)}` is not a valid $colors for `text-gradient`: #{$problem}. A browser drops the whole gradient, and the transparent text with it.";
|
|
17
|
+
}
|
|
10
18
|
$list: ();
|
|
11
19
|
@for $i from 1 through list.length($colors) {
|
|
12
20
|
$list: list.append($list, list.nth($colors, $i), comma);
|