gerillass 1.6.0 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -31,6 +31,7 @@ Hope you’ll enjoy using it!
31
31
  - [Using with Grunt](#using-with-grunt)
32
32
  - [Cloning the Repository from Github](#cloning-the-repository-from-github)
33
33
  - [Versions these examples were tested with](#versions-these-examples-were-tested-with)
34
+ - [Using Gerillass with an AI coding agent](#using-gerillass-with-an-ai-coding-agent)
34
35
  - [Namespace Usage](#namespace-usage)
35
36
  - [Vendor Prefix Support](#vendor-prefix-support)
36
37
  - [Experimenting](#experimenting)
@@ -186,6 +187,24 @@ Including to the project:
186
187
  | Gulp / gulp-sass | 5.0.1 / 6.0.1 |
187
188
  | Grunt / grunt-sass | 1.6.3 / 4.1.0 |
188
189
 
190
+ ## Using Gerillass with an AI coding agent
191
+
192
+ Gerillass ships two files that let a coding agent use the library correctly instead of guessing at it. Both are inside the installed package, so an agent working in your project can read them straight out of `node_modules/gerillass/`.
193
+
194
+ **`gerillass.json`** describes every mixin and function: its signature, what each argument accepts, examples that compile, and inputs that are refused.
195
+
196
+ const api = require("gerillass/gerillass.json");
197
+
198
+ **`SKILL.md`** is a written guide generated from that manifest — how to load the library, the full catalogue, and the argument forms that are easy to get wrong. If your agent supports [Agent Skills](https://code.claude.com/docs/en/skills), copy it into your skills folder:
199
+
200
+ mkdir -p .claude/skills/gerillass
201
+ cp node_modules/gerillass/SKILL.md .claude/skills/gerillass/
202
+
203
+ Otherwise, point your agent at the file and it will read it as plain Markdown.
204
+
205
+ Neither file is generated by hand: signatures are parsed from the Sass sources, and every example and refusal in the manifest is executed by the test suite. What the manifest says the library does is what the library does.
206
+
207
+
189
208
  ## Namespace Usage
190
209
 
191
210
  You can use Gerillass with or without `gls-` namespace. It is optional, but I strongly recommend you to use it to prevent having conflicts with other Sass libraries or frameworks like Bootstrap.
package/SKILL.md CHANGED
@@ -5,7 +5,7 @@ description: Use the Gerillass Sass mixin library — loading it, the mixin cata
5
5
 
6
6
  # Gerillass
7
7
 
8
- A Sass mixin library: 51 mixins and 21 functions that emit CSS from
8
+ A Sass mixin library: 51 mixins and 22 functions that emit CSS from
9
9
  semantic declarations. It is Sass source only — there is no runtime and no
10
10
  utility classes, so styles live in your stylesheet and your markup stays clean.
11
11
 
@@ -65,20 +65,27 @@ a dropped declaration rather than an error.
65
65
 
66
66
  | Mixin | Rejects, for example |
67
67
  |---|---|
68
+ | `after` | `.a { @include after(42) { color: red; } }` |
68
69
  | `all-buttons` | `@include all-buttons(nonsense) { color: red; }` |
69
70
  | `all-text-inputs` | `@include all-text-inputs(nonsense) { color: red; }` |
70
71
  | `antialias` | `@include antialias(only);` |
71
72
  | `background-dots` | `.a { @include background-dots(red, 1em, 5em, maybe); }` |
72
73
  | `background-image` | `.a { @include background-image("/img/a.png", (red, blue), sideways); }` |
74
+ | `background-stripes` | `.a { @include background-stripes(red, 2em, nonsense); }` |
75
+ | `before` | `.a { @include before(42) { color: red; } }` |
73
76
  | `border-box` | `@include border-box(only);` |
74
77
  | `border-radius` | `.a { @include border-radius(1px, 2px, 3px); }` |
78
+ | `breakpointer` | `.a { @include breakpointer(42); }` |
75
79
  | `center` | `.modal { @include center(diagonal); }` |
76
80
  | `columnizer` | `.grid { @include columnizer(3, 20px, true, 9); }` |
81
+ | `escape-to-parent` | `.a { @include escape-to-parent(42) { color: red; } }` |
77
82
  | `except` | `.a { @include except(#ff0000) { margin: 0; } }` |
78
83
  | `font-face` | `.a { @include font-face("Inter", "/fonts/inter"); }` |
79
84
  | `hide` | `.a { @include hide(nonsense); }` |
85
+ | `linear-gradient` | `.a { @include linear-gradient(sideways, (red, blue)); }` |
80
86
  | `loadify` | `@include loadify(nonsense);` |
81
87
  | `only` | `.a { @include only(#ff0000) { margin: 0; } }` |
88
+ | `radial-gradient` | `.a { @include radial-gradient(42, "center", (red, blue)); }` |
82
89
  | `ratio-box` | `.hero { @include ratio-box(16 9); }` |
83
90
  | `remove` | `.a { @include remove(a, b, c); }` |
84
91
  | `reset-css` | `.a { @include reset-css; }` |
@@ -88,7 +95,9 @@ a dropped declaration rather than an error.
88
95
  | `sprite` | `.icon { @include sprite("/img/sprite.txt"); }` |
89
96
  | `stretched-link` | `.card a { @include stretched-link(middle); }` |
90
97
  | `tablet` | `.a { @include tablet(Surface) { display: none; } }` |
98
+ | `text-gradient` | `.a { @include text-gradient(sideways, (red, blue)); }` |
91
99
  | `text-selection` | `.a { @include text-selection(bogus) { background: yellow; } }` |
100
+ | `text-shadow` | `.a { @include text-shadow(42); }` |
92
101
  | `triangle` | `.caret { @include triangle(sideways); }` |
93
102
 
94
103
  ## Mixins
@@ -162,6 +171,7 @@ functions rather than mixins; they are public API.
162
171
  | `__fontSizer($size, $time)` | Multiplies a size by a factor. Handy for a modular scale. |
163
172
  | `__fontSource($font-family, $file-path, $file-formats)` | Builds one src entry for an @font-face rule. |
164
173
  | `__isColor($value)` | Returns the value if every item in it is a colour, and errors otherwise. |
174
+ | `__isGutter($value)` | True for anything that can sit where a CSS length is expected: a number, a calculation, or a CSS function such as var(). |
165
175
  | `__isNumber($value)` | Returns the value if it is a number. |
166
176
  | `__isTime($value)` | Returns the value if it is a time in s or ms, and errors otherwise. |
167
177
  | `__lighten($color, $percentage)` | Mixes a colour towards white by a percentage. |
package/gerillass.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gerillass",
3
- "version": "1.6.0",
3
+ "version": "1.6.2",
4
4
  "description": "Gerillass is an open-source toolkit that contains a set of Sass mixins to help designers and developers to create better, faster and consistent user interfaces.",
5
5
  "homepage": "https://gerillass.com",
6
6
  "documentation": "https://docs.gerillass.com",
@@ -54,7 +54,10 @@
54
54
  ".a { @include after(\"data-label\") { color: red; } }",
55
55
  ".a { @include after { content: \"x\"; } }"
56
56
  ],
57
- "rejects": []
57
+ "rejects": [
58
+ ".a { @include after(42) { color: red; } }",
59
+ ".a { @include after(16 9) { color: red; } }"
60
+ ]
58
61
  },
59
62
  {
60
63
  "name": "all-buttons",
@@ -188,7 +191,8 @@
188
191
  ".a { @include background-dots(red, 1em, 5em, false); }"
189
192
  ],
190
193
  "rejects": [
191
- ".a { @include background-dots(red, 1em, 5em, maybe); }"
194
+ ".a { @include background-dots(red, 1em, 5em, maybe); }",
195
+ ".a { @include background-dots(red, 1em, 5em, true, 42); }"
192
196
  ]
193
197
  },
194
198
  {
@@ -272,7 +276,10 @@
272
276
  ".a { @include background-stripes; }",
273
277
  ".a { @include background-stripes(red, 2em, -30deg); }"
274
278
  ],
275
- "rejects": []
279
+ "rejects": [
280
+ ".a { @include background-stripes(red, 2em, nonsense); }",
281
+ ".a { @include background-stripes(red, 2em, -45deg, 42); }"
282
+ ]
276
283
  },
277
284
  {
278
285
  "name": "before",
@@ -295,7 +302,9 @@
295
302
  ".a { @include before(\"→\") { color: red; } }",
296
303
  ".a { @include before(\"data-label\") { color: red; } }"
297
304
  ],
298
- "rejects": []
305
+ "rejects": [
306
+ ".a { @include before(42) { color: red; } }"
307
+ ]
299
308
  },
300
309
  {
301
310
  "name": "border-box",
@@ -428,7 +437,9 @@
428
437
  "@include breakpointer;",
429
438
  ".a { @include breakpointer; }"
430
439
  ],
431
- "rejects": []
440
+ "rejects": [
441
+ ".a { @include breakpointer(42); }"
442
+ ]
432
443
  },
433
444
  {
434
445
  "name": "center",
@@ -495,8 +506,8 @@
495
506
  "name": "$params",
496
507
  "variadic": true,
497
508
  "accepts": [
498
- "a column count",
499
- "a column count plus a gutter length",
509
+ "a column count, which may be a CSS function such as var(--cols)",
510
+ "a column count plus a gutter length, which may also be a CSS function",
500
511
  "a column count plus a boolean fill flag",
501
512
  "a column count, a gutter and a fill flag"
502
513
  ]
@@ -508,10 +519,13 @@
508
519
  "examples": [
509
520
  ".grid { @include columnizer(3); }",
510
521
  ".grid { @include columnizer(3, 20px); }",
511
- ".grid { @include columnizer(3, 20px, true); }"
522
+ ".grid { @include columnizer(3, 20px, true); }",
523
+ ".grid { @include columnizer(3, var(--gap)); }",
524
+ ".grid { @include columnizer(var(--cols)); }"
512
525
  ],
513
526
  "rejects": [
514
- ".grid { @include columnizer(3, 20px, true, 9); }"
527
+ ".grid { @include columnizer(3, 20px, true, 9); }",
528
+ ".grid { @include columnizer(3, nonsense); }"
515
529
  ]
516
530
  },
517
531
  {
@@ -584,7 +598,9 @@
584
598
  "examples": [
585
599
  ".a { @include escape-to-parent(\".theme-dark\") { color: white; } }"
586
600
  ],
587
- "rejects": []
601
+ "rejects": [
602
+ ".a { @include escape-to-parent(42) { color: red; } }"
603
+ ]
588
604
  },
589
605
  {
590
606
  "name": "except",
@@ -661,7 +677,9 @@
661
677
  "@include font-face(\"Inter\", \"/fonts/inter\", italic, 700, woff2 woff);"
662
678
  ],
663
679
  "rejects": [
664
- ".a { @include font-face(\"Inter\", \"/fonts/inter\"); }"
680
+ ".a { @include font-face(\"Inter\", \"/fonts/inter\"); }",
681
+ "@include font-face(42, \"/fonts/x\");",
682
+ "@include font-face(\"Inter\", 42);"
665
683
  ]
666
684
  },
667
685
  {
@@ -715,7 +733,10 @@
715
733
  ".a { @include linear-gradient(\"top\", (red, blue)); }",
716
734
  ".a { @include linear-gradient(45deg, (red, blue)); }"
717
735
  ],
718
- "rejects": []
736
+ "rejects": [
737
+ ".a { @include linear-gradient(sideways, (red, blue)); }",
738
+ ".a { @include linear-gradient(true, (red, blue)); }"
739
+ ]
719
740
  },
720
741
  {
721
742
  "name": "loadify",
@@ -853,7 +874,10 @@
853
874
  "examples": [
854
875
  ".a { @include radial-gradient(circle, \"center\", (red, blue)); }"
855
876
  ],
856
- "rejects": []
877
+ "rejects": [
878
+ ".a { @include radial-gradient(42, \"center\", (red, blue)); }",
879
+ ".a { @include radial-gradient(circle, 42, (red, blue)); }"
880
+ ]
857
881
  },
858
882
  {
859
883
  "name": "ratio-box",
@@ -1021,7 +1045,8 @@
1021
1045
  ".a { @include scissors(4px 8px 12px 16px); }"
1022
1046
  ],
1023
1047
  "rejects": [
1024
- ".a { @include scissors(5px 10px); }"
1048
+ ".a { @include scissors(5px 10px); }",
1049
+ ".a { @include scissors(nonsense); }"
1025
1050
  ]
1026
1051
  },
1027
1052
  {
@@ -1214,7 +1239,9 @@
1214
1239
  "examples": [
1215
1240
  ".a { @include text-gradient(\"top\", (red, blue)); }"
1216
1241
  ],
1217
- "rejects": []
1242
+ "rejects": [
1243
+ ".a { @include text-gradient(sideways, (red, blue)); }"
1244
+ ]
1218
1245
  },
1219
1246
  {
1220
1247
  "name": "text-image",
@@ -1278,7 +1305,10 @@
1278
1305
  "examples": [
1279
1306
  ".a { @include text-shadow(bottom-right red 5px); }"
1280
1307
  ],
1281
- "rejects": []
1308
+ "rejects": [
1309
+ ".a { @include text-shadow(42); }",
1310
+ ".a { @include text-shadow(top red); }"
1311
+ ]
1282
1312
  },
1283
1313
  {
1284
1314
  "name": "text-stroke",
@@ -1405,7 +1435,9 @@
1405
1435
  "examples": [
1406
1436
  ".a { content: __clearWhitespace(\"a b c\"); }"
1407
1437
  ],
1408
- "rejects": []
1438
+ "rejects": [
1439
+ ".a { --x: #{__clearWhitespace(42)}; }"
1440
+ ]
1409
1441
  },
1410
1442
  {
1411
1443
  "name": "__convertToEm",
@@ -1445,7 +1477,9 @@
1445
1477
  "examples": [
1446
1478
  ".a { z-index: __convertToNumber(\"42\"); }"
1447
1479
  ],
1448
- "rejects": []
1480
+ "rejects": [
1481
+ ".a { --x: #{__convertToNumber(42)}; }"
1482
+ ]
1449
1483
  },
1450
1484
  {
1451
1485
  "name": "__darken",
@@ -1559,6 +1593,27 @@
1559
1593
  ".a { color: __isColor(nonsense); }"
1560
1594
  ]
1561
1595
  },
1596
+ {
1597
+ "name": "__isGutter",
1598
+ "kind": "function",
1599
+ "arguments": [
1600
+ {
1601
+ "name": "$value",
1602
+ "required": true,
1603
+ "accepts": [
1604
+ "any value"
1605
+ ]
1606
+ }
1607
+ ],
1608
+ "file": "scss/utilities/_is-gutter.scss",
1609
+ "signature": "__isGutter($value)",
1610
+ "summary": "True for anything that can sit where a CSS length is expected: a number, a calculation, or a CSS function such as var().",
1611
+ "examples": [
1612
+ ".a { --x: #{__isGutter(20px)}; }",
1613
+ ".a { --x: #{__isGutter(var(--gap))}; }"
1614
+ ],
1615
+ "rejects": []
1616
+ },
1562
1617
  {
1563
1618
  "name": "__isNumber",
1564
1619
  "kind": "function",
@@ -1690,7 +1745,9 @@
1690
1745
  "examples": [
1691
1746
  ".a { margin: __null(1px null 3px null, space); }"
1692
1747
  ],
1693
- "rejects": []
1748
+ "rejects": [
1749
+ ".a { --x: #{__null(1px null, nonsense)}; }"
1750
+ ]
1694
1751
  },
1695
1752
  {
1696
1753
  "name": "__pixelify",
@@ -1710,7 +1767,9 @@
1710
1767
  "examples": [
1711
1768
  ".a { width: __pixelify(24); }"
1712
1769
  ],
1713
- "rejects": []
1770
+ "rejects": [
1771
+ ".a { --x: #{__pixelify(nonsense)}; }"
1772
+ ]
1714
1773
  },
1715
1774
  {
1716
1775
  "name": "__pseudoSelector",
package/package.json CHANGED
@@ -16,13 +16,13 @@
16
16
  }
17
17
  },
18
18
  "license": "Apache-2.0",
19
- "version": "1.6.0",
19
+ "version": "1.6.2",
20
20
  "repository": {
21
21
  "type": "git",
22
22
  "url": "git+https://github.com/selfishprimate/gerillass.git"
23
23
  },
24
24
  "bugs": {
25
- "url": "https://github.com/selfihsprimate/gerillass/issues"
25
+ "url": "https://github.com/selfishprimate/gerillass/issues"
26
26
  },
27
27
  "keywords": [
28
28
  "gerillass",
@@ -17,7 +17,9 @@
17
17
  &::after {
18
18
  @if $content == null {
19
19
  @content;
20
- } @else if $content != null {
20
+ } @else if type-of($content) != "string" {
21
+ @error "`#{$content}` is not a valid $content for `after`. Pass a string to use as the content value, a data- attribute name such as `\"data-label\"` to render it with attr(), or no argument at all and write the content yourself.";
22
+ } @else {
21
23
  $data: str-slice($content, 1, 5);
22
24
  @if $data == "data-" {
23
25
  content: attr(#{$content});
@@ -107,6 +109,9 @@
107
109
  $diagonal: true,
108
110
  $image: null
109
111
  ) {
112
+ @if $image != null and type-of($image) != "string" {
113
+ @error "`#{$image}` is not a valid $image for `background-dots`. Pass an image path as a string.";
114
+ }
110
115
  @if $diagonal != true and $diagonal != false {
111
116
  @error "`#{$diagonal}` is not a valid $diagonal for `background-dots`. Pass `true` or `false`.";
112
117
  }
@@ -233,6 +238,12 @@
233
238
  $rotation: -45deg,
234
239
  $image: null
235
240
  ) {
241
+ @if type-of($rotation) != "number" {
242
+ @error "`#{$rotation}` is not a valid $rotation for `background-stripes`. Pass an angle such as `-45deg`, or a unitless number which is treated as degrees.";
243
+ }
244
+ @if $image != null and type-of($image) != "string" {
245
+ @error "`#{$image}` is not a valid $image for `background-stripes`. Pass an image path as a string.";
246
+ }
236
247
  $validate-unit: if(not index("deg", unit($rotation)), $rotation + deg, $rotation);
237
248
  $get-image: if($image, unquote(", ") + url(unquote($image)), unquote(""));
238
249
  @if not $color {
@@ -284,7 +295,9 @@
284
295
  &::before {
285
296
  @if $content == null {
286
297
  @content;
287
- } @else if $content != null {
298
+ } @else if type-of($content) != "string" {
299
+ @error "`#{$content}` is not a valid $content for `before`. Pass a string to use as the content value, a data- attribute name such as `\"data-label\"` to render it with attr(), or no argument at all and write the content yourself.";
300
+ } @else {
288
301
  $data: str-slice($content, 1, 5);
289
302
  @if $data == "data-" {
290
303
  content: attr(#{$content});
@@ -473,6 +486,9 @@
473
486
 
474
487
 
475
488
  @mixin gls-breakpointer($selector: null) {
489
+ @if $selector != null and type-of($selector) != "string" {
490
+ @error "`#{$selector}` is not a valid $selector for `breakpointer`. Pass a selector as a string, such as `\".debug\"`, or no argument at all to attach it to body.";
491
+ }
476
492
  @if & {
477
493
  &::before {
478
494
  @content;
@@ -571,7 +587,7 @@
571
587
  &:not(:last-child) {
572
588
  margin-right: 0;
573
589
  }
574
- } @else if type-of(nth($params, 2)) == number {
590
+ } @else if __isGutter(nth($params, 2)) {
575
591
  $gutter: nth($params, length($params));
576
592
  flex-grow: 0;
577
593
  flex-shrink: 0;
@@ -584,7 +600,7 @@
584
600
  margin-right: 0;
585
601
  }
586
602
  } @else {
587
- @error "`#{nth($params, 2)}` is not a valid second argument for `columnizer`. Pass a gutter length such as `20px`, or a boolean telling the last row whether to fill.";
603
+ @error "`#{nth($params, 2)}` is not a valid second argument for `columnizer`. Pass a gutter length such as `20px` or `var(--gap)`, or a boolean telling the last row whether to fill.";
588
604
  }
589
605
  } @else if length($params) == 3 {
590
606
  $gutter: nth($params, 2);
@@ -663,6 +679,9 @@
663
679
 
664
680
 
665
681
  @mixin gls-escape-to-parent($selector: null) {
682
+ @if $selector != null and type-of($selector) != "string" {
683
+ @error "`#{$selector}` is not a valid $selector for `escape-to-parent`. Pass a selector as a string, such as `\".theme-dark\"`.";
684
+ }
666
685
  @at-root #{$selector}#{&} {
667
686
  @content;
668
687
  }
@@ -732,6 +751,10 @@
732
751
  ) {
733
752
  @if & {
734
753
  @error "You must call the mixin at the root level of your style sheet, not in the `#{&+'{'+'}'}` selector.";
754
+ } @else if type-of($font-family) != "string" {
755
+ @error "`#{$font-family}` is not a valid $font-family for `font-face`. Pass the family name as a string, such as `\"Inter\"`.";
756
+ } @else if type-of($file-path) != "string" {
757
+ @error "`#{$file-path}` is not a valid $file-path for `font-face`. Pass the path to the font files without an extension, as a string.";
735
758
  } @else {
736
759
 
737
760
  $list: ();
@@ -811,8 +834,8 @@
811
834
  $list
812
835
  );
813
836
  } @else if not map-has-key($map-for-directions, $direction) {
814
- @if not index("deg", unit($direction)) {
815
- @error "#{$direction} is a wrong value for the $direction parameter. The value must be eighter a number followed by the 'deg' unit or one of the followings: #{map-keys($map-for-directions)}.";
837
+ @if type-of($direction) != "number" or not index("deg", unit($direction)) {
838
+ @error "`#{$direction}` is not a valid $direction for `linear-gradient`. Pass an angle such as `45deg`, or one of: #{map-keys($map-for-directions)}.";
816
839
  } @else {
817
840
  background: linear-gradient($direction, $list);
818
841
  }
@@ -963,6 +986,12 @@
963
986
 
964
987
 
965
988
  @mixin gls-radial-gradient($shape, $position, $colors) {
989
+ @if type-of($shape) != "string" {
990
+ @error "`#{$shape}` is not a valid $shape for `radial-gradient`. Pass `circle` or `ellipse`.";
991
+ }
992
+ @if type-of($position) != "string" {
993
+ @error "`#{$position}` is not a valid $position for `radial-gradient`. Pass a position name such as `center` or `top-left`, or a raw CSS position as a string.";
994
+ }
966
995
  $list: ();
967
996
  $shape: unquote($shape);
968
997
  $position: unquote($position);
@@ -1311,8 +1340,8 @@
1311
1340
  $list
1312
1341
  );
1313
1342
  } @else if not map-has-key($map-for-directions, $direction) {
1314
- @if not index("deg", unit($direction)) {
1315
- @error "#{$direction} is a wrong value for the $direction parameter. The value must be eighter a number followed by the 'deg' unit or one of the followings: #{map-keys($map-for-directions)}.";
1343
+ @if type-of($direction) != "number" or not index("deg", unit($direction)) {
1344
+ @error "`#{$direction}` is not a valid $direction for `text-gradient`. Pass an angle such as `45deg`, or one of: #{map-keys($map-for-directions)}.";
1316
1345
  } @else {
1317
1346
  background: linear-gradient($direction, $list);
1318
1347
  }
@@ -1369,6 +1398,13 @@
1369
1398
  // Takes each items come from $params.
1370
1399
  $item: nth($params, $i);
1371
1400
 
1401
+ // Each group must carry at least a direction, a colour and a size. Without
1402
+ // this the nth() calls below fail with "Invalid index", which says nothing
1403
+ // about what the mixin wanted.
1404
+ @if length($item) < 3 {
1405
+ @error "`#{$item}` is not a valid shadow for `text-shadow`. Each group needs a direction, a colour and a size, such as `bottom-right red 5px`. An optional blur and a fill flag may follow.";
1406
+ }
1407
+
1372
1408
  // The first value comes from the $item is direction.
1373
1409
  // This will be used in the @if, @else statements with $size to control the offset of the shadows.
1374
1410
  $direction: nth($item, 1);
@@ -27,6 +27,7 @@
27
27
  @import "utilities/font-sizer";
28
28
  @import "utilities/font-source";
29
29
  @import "utilities/is-color";
30
+ @import "utilities/is-gutter";
30
31
  @import "utilities/is-number";
31
32
  @import "utilities/is-time";
32
33
  @import "utilities/lighten";
@@ -4,7 +4,9 @@
4
4
  &::after {
5
5
  @if $content == null {
6
6
  @content;
7
- } @else if $content != null {
7
+ } @else if type-of($content) != "string" {
8
+ @error "`#{$content}` is not a valid $content for `after`. Pass a string to use as the content value, a data- attribute name such as `\"data-label\"` to render it with attr(), or no argument at all and write the content yourself.";
9
+ } @else {
8
10
  $data: str-slice($content, 1, 5);
9
11
  @if $data == "data-" {
10
12
  content: attr(#{$content});
@@ -7,6 +7,9 @@
7
7
  $diagonal: true,
8
8
  $image: null
9
9
  ) {
10
+ @if $image != null and type-of($image) != "string" {
11
+ @error "`#{$image}` is not a valid $image for `background-dots`. Pass an image path as a string.";
12
+ }
10
13
  @if $diagonal != true and $diagonal != false {
11
14
  @error "`#{$diagonal}` is not a valid $diagonal for `background-dots`. Pass `true` or `false`.";
12
15
  }
@@ -6,6 +6,12 @@
6
6
  $rotation: -45deg,
7
7
  $image: null
8
8
  ) {
9
+ @if type-of($rotation) != "number" {
10
+ @error "`#{$rotation}` is not a valid $rotation for `background-stripes`. Pass an angle such as `-45deg`, or a unitless number which is treated as degrees.";
11
+ }
12
+ @if $image != null and type-of($image) != "string" {
13
+ @error "`#{$image}` is not a valid $image for `background-stripes`. Pass an image path as a string.";
14
+ }
9
15
  $validate-unit: if(not index("deg", unit($rotation)), $rotation + deg, $rotation);
10
16
  $get-image: if($image, unquote(", ") + url(unquote($image)), unquote(""));
11
17
  @if not $color {
@@ -4,7 +4,9 @@
4
4
  &::before {
5
5
  @if $content == null {
6
6
  @content;
7
- } @else if $content != null {
7
+ } @else if type-of($content) != "string" {
8
+ @error "`#{$content}` is not a valid $content for `before`. Pass a string to use as the content value, a data- attribute name such as `\"data-label\"` to render it with attr(), or no argument at all and write the content yourself.";
9
+ } @else {
8
10
  $data: str-slice($content, 1, 5);
9
11
  @if $data == "data-" {
10
12
  content: attr(#{$content});
@@ -1,6 +1,9 @@
1
1
  @charset "UTF-8";
2
2
 
3
3
  @mixin breakpointer($selector: null) {
4
+ @if $selector != null and type-of($selector) != "string" {
5
+ @error "`#{$selector}` is not a valid $selector for `breakpointer`. Pass a selector as a string, such as `\".debug\"`, or no argument at all to attach it to body.";
6
+ }
4
7
  @if & {
5
8
  &::before {
6
9
  @content;
@@ -27,7 +27,7 @@
27
27
  &:not(:last-child) {
28
28
  margin-right: 0;
29
29
  }
30
- } @else if type-of(nth($params, 2)) == number {
30
+ } @else if __isGutter(nth($params, 2)) {
31
31
  $gutter: nth($params, length($params));
32
32
  flex-grow: 0;
33
33
  flex-shrink: 0;
@@ -40,7 +40,7 @@
40
40
  margin-right: 0;
41
41
  }
42
42
  } @else {
43
- @error "`#{nth($params, 2)}` is not a valid second argument for `columnizer`. Pass a gutter length such as `20px`, or a boolean telling the last row whether to fill.";
43
+ @error "`#{nth($params, 2)}` is not a valid second argument for `columnizer`. Pass a gutter length such as `20px` or `var(--gap)`, or a boolean telling the last row whether to fill.";
44
44
  }
45
45
  } @else if length($params) == 3 {
46
46
  $gutter: nth($params, 2);
@@ -1,6 +1,9 @@
1
1
  @charset "UTF-8";
2
2
 
3
3
  @mixin escape-to-parent($selector: null) {
4
+ @if $selector != null and type-of($selector) != "string" {
5
+ @error "`#{$selector}` is not a valid $selector for `escape-to-parent`. Pass a selector as a string, such as `\".theme-dark\"`.";
6
+ }
4
7
  @at-root #{$selector}#{&} {
5
8
  @content;
6
9
  }
@@ -9,6 +9,10 @@
9
9
  ) {
10
10
  @if & {
11
11
  @error "You must call the mixin at the root level of your style sheet, not in the `#{&+'{'+'}'}` selector.";
12
+ } @else if type-of($font-family) != "string" {
13
+ @error "`#{$font-family}` is not a valid $font-family for `font-face`. Pass the family name as a string, such as `\"Inter\"`.";
14
+ } @else if type-of($file-path) != "string" {
15
+ @error "`#{$file-path}` is not a valid $file-path for `font-face`. Pass the path to the font files without an extension, as a string.";
12
16
  } @else {
13
17
 
14
18
  $list: ();
@@ -11,8 +11,8 @@
11
11
  $list
12
12
  );
13
13
  } @else if not map-has-key($map-for-directions, $direction) {
14
- @if not index("deg", unit($direction)) {
15
- @error "#{$direction} is a wrong value for the $direction parameter. The value must be eighter a number followed by the 'deg' unit or one of the followings: #{map-keys($map-for-directions)}.";
14
+ @if type-of($direction) != "number" or not index("deg", unit($direction)) {
15
+ @error "`#{$direction}` is not a valid $direction for `linear-gradient`. Pass an angle such as `45deg`, or one of: #{map-keys($map-for-directions)}.";
16
16
  } @else {
17
17
  background: linear-gradient($direction, $list);
18
18
  }
@@ -1,6 +1,12 @@
1
1
  @charset "UTF-8";
2
2
 
3
3
  @mixin radial-gradient($shape, $position, $colors) {
4
+ @if type-of($shape) != "string" {
5
+ @error "`#{$shape}` is not a valid $shape for `radial-gradient`. Pass `circle` or `ellipse`.";
6
+ }
7
+ @if type-of($position) != "string" {
8
+ @error "`#{$position}` is not a valid $position for `radial-gradient`. Pass a position name such as `center` or `top-left`, or a raw CSS position as a string.";
9
+ }
4
10
  $list: ();
5
11
  $shape: unquote($shape);
6
12
  $position: unquote($position);
@@ -11,8 +11,8 @@
11
11
  $list
12
12
  );
13
13
  } @else if not map-has-key($map-for-directions, $direction) {
14
- @if not index("deg", unit($direction)) {
15
- @error "#{$direction} is a wrong value for the $direction parameter. The value must be eighter a number followed by the 'deg' unit or one of the followings: #{map-keys($map-for-directions)}.";
14
+ @if type-of($direction) != "number" or not index("deg", unit($direction)) {
15
+ @error "`#{$direction}` is not a valid $direction for `text-gradient`. Pass an angle such as `45deg`, or one of: #{map-keys($map-for-directions)}.";
16
16
  } @else {
17
17
  background: linear-gradient($direction, $list);
18
18
  }
@@ -7,6 +7,13 @@
7
7
  // Takes each items come from $params.
8
8
  $item: nth($params, $i);
9
9
 
10
+ // Each group must carry at least a direction, a colour and a size. Without
11
+ // this the nth() calls below fail with "Invalid index", which says nothing
12
+ // about what the mixin wanted.
13
+ @if length($item) < 3 {
14
+ @error "`#{$item}` is not a valid shadow for `text-shadow`. Each group needs a direction, a colour and a size, such as `bottom-right red 5px`. An optional blur and a fill flag may follow.";
15
+ }
16
+
10
17
  // The first value comes from the $item is direction.
11
18
  // This will be used in the @if, @else statements with $size to control the offset of the shadows.
12
19
  $direction: nth($item, 1);
@@ -1,6 +1,9 @@
1
1
  @charset "UTF-8";
2
2
 
3
3
  @function __clearWhitespace($string) {
4
+ @if type-of($string) != "string" {
5
+ @error "`#{$string}` is not a valid $string for `__clearWhitespace`. Pass a string.";
6
+ }
4
7
  @while (str-index($string, " ") != null) {
5
8
  $index: str-index($string, " ");
6
9
  $string: "#{str-slice($string, 0, $index - 1)}#{str-slice($string, $index + 1)}";
@@ -2,6 +2,10 @@
2
2
 
3
3
  @function __convertToNumber($value) {
4
4
 
5
+ @if type-of($value) != "string" {
6
+ @error "`#{$value}` is not a valid $value for `__convertToNumber`. Pass a string made only of digits, such as `\"42\"`.";
7
+ }
8
+
5
9
  $string-numerals: "0" "1" "2" "3" "4" "5" "6" "7" "8" "9";
6
10
  $calc: 0;
7
11
 
@@ -0,0 +1,21 @@
1
+ @charset "UTF-8";
2
+
3
+ // True for anything that can legally sit where a CSS length is expected: a
4
+ // number, a Sass calculation such as calc() or clamp(), or an unquoted string
5
+ // holding a parenthesis, which is how var() and env() arrive.
6
+ //
7
+ // Written because `type-of($value) == number` rejects `var(--gap)`, and a
8
+ // gutter expressed as a custom property is perfectly reasonable CSS.
9
+
10
+ @function __isGutter($value) {
11
+ @if type-of($value) == "number" {
12
+ @return true;
13
+ }
14
+ @if type-of($value) == "calculation" {
15
+ @return true;
16
+ }
17
+ @if type-of($value) == "string" and str-index(quote($value), "(") {
18
+ @return true;
19
+ }
20
+ @return false;
21
+ }
@@ -1,6 +1,9 @@
1
1
  @charset "UTF-8";
2
2
 
3
3
  @function __null($value, $seperation: comma, $skip: false) {
4
+ @if not index("comma" "space" "slash", $seperation) {
5
+ @error "`#{$seperation}` is not a valid $seperation for `__null`. Pass `comma`, `space` or `slash`.";
6
+ }
4
7
  $list: ();
5
8
  @for $i from 1 through length($value) {
6
9
  @if nth($value, $i) == null {
@@ -3,6 +3,9 @@
3
3
  // This function checks if the argument has a px unit. If not, it adds a px unit to the end of the argument.
4
4
 
5
5
  @function __pixelify($value) {
6
+ @if type-of($value) != "number" {
7
+ @error "`#{$value}` is not a valid $value for `__pixelify`. Pass a number, with or without a unit.";
8
+ }
6
9
  @if unit($value) == "px" {
7
10
  @return $value;
8
11
  } @else if unit($value) != "px" {
@@ -1,13 +1,25 @@
1
1
  @charset "UTF-8";
2
2
 
3
3
  @function __validateLength($value) {
4
- $list: ("auto", "inherit", "initial", 0);
5
- @if index($list, $value) {
4
+ $keywords: ("auto", "inherit", "initial", "unset", "revert", 0);
5
+
6
+ @if index($keywords, $value) {
7
+ @return $value;
8
+ }
9
+ @if type-of($value) == "number" and not unitless($value) {
6
10
  @return $value;
7
- } @else if type-of($value) == "number" and not unitless($value) {
11
+ }
12
+ // calc(), clamp(), min() and max() arrive as Sass calculations; var() and
13
+ // env() arrive as unquoted strings holding a parenthesis. Both are valid
14
+ // lengths, and warning about them means warning about correct modern CSS,
15
+ // which this function used to do.
16
+ @if type-of($value) == "calculation" {
8
17
  @return $value;
9
- } @else {
10
- @warn "Please pass some values with unit like `#{$value}px`, `#{$value}em`, or `#{$value}%` etc. or pass one of the following arguments: `auto`, ìnherit`, ìnitial` or `0`.";
18
+ }
19
+ @if type-of($value) == "string" and str-index(quote($value), "(") {
11
20
  @return $value;
12
21
  }
22
+
23
+ @warn "`#{$value}` does not look like a length. Pass a number with a unit such as `20px`, a CSS function such as `var(--gap)` or `calc(1rem + 2px)`, or one of: auto, inherit, initial, unset, revert, 0.";
24
+ @return $value;
13
25
  }
@@ -3,6 +3,12 @@
3
3
  @function __validateScissors($value) {
4
4
  $list: ();
5
5
  @for $i from 1 through length($value) {
6
+ $item: nth($value, $i);
7
+ // unitless() below throws on anything that is not a number, and its message
8
+ // names its own parameter rather than the corner that was wrong.
9
+ @if $item != null and type-of($item) != "number" {
10
+ @error "`#{$item}` is not a valid corner size. Pass a length such as `12px`, or a unitless number which is treated as pixels.";
11
+ }
6
12
  @if nth($value, $i) == null {
7
13
  $list: append($list, 0px, comma);
8
14
  } @else if unitless(nth($value, $i)) {