gerillass 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -6
- package/SKILL.md +11 -4
- package/gerillass.json +146 -2
- package/package.json +2 -2
- package/scss/library/_container-query.scss +63 -0
- package/scss/library/_container.scss +26 -0
- package/scss/library/_index.scss +3 -0
- package/scss/library/_line-clamp.scss +36 -0
- package/scss/library/_loadify.scss +11 -0
- package/scss/utilities/_fluid.scss +76 -0
- package/scss/utilities/_index.scss +1 -0
package/README.md
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
[Gerillass](https://gerillass.com) is a library built on top of [Sass (Syntactically Awesome Style Sheets)](https://sass-lang.com/) to give you flexibility for your projects and accelerate your performance and creativity.
|
|
10
10
|
|
|
11
|
+
It is also built to be read by coding agents. Every mixin and function ships with a machine-readable manifest, and the test suite compiles every documented example and asserts every documented refusal. So what the manifest says the library does is what the library does. The docs cannot drift away from the code, because a stale manifest fails the build.
|
|
12
|
+
|
|
11
13
|
Many of the utilities that come with Gerillass are the solutions I have come up with for the challenges I have faced as a frontend developer over the years. These solutions have been shaped by the inspiration of other popular libraries and frameworks like [Bourbon](https://www.bourbon.io/), [Susy](https://www.oddbird.net/), [Scut](https://github.com/davidtheclark/scut), [Bootstrap](https://getbootstrap.com/), etc. over time and helped me create Gerillass.
|
|
12
14
|
|
|
13
15
|
Hope you’ll enjoy using it!
|
|
@@ -53,7 +55,7 @@ Or with Yarn:
|
|
|
53
55
|
|
|
54
56
|
yarn add gerillass --dev
|
|
55
57
|
|
|
56
|
-
Then load it. If your setup resolves packages from **node_modules
|
|
58
|
+
Then load it. If your setup resolves packages from **node_modules**, which Vite, webpack, Next.js and most modern bundlers do, this is all you need:
|
|
57
59
|
|
|
58
60
|
@use 'gerillass' as *;
|
|
59
61
|
|
|
@@ -139,7 +141,7 @@ Then:
|
|
|
139
141
|
|
|
140
142
|
### Using with Grunt
|
|
141
143
|
|
|
142
|
-
Use `grunt-sass` with Dart Sass as the implementation. The option here is **`loadPaths`** as well
|
|
144
|
+
Use `grunt-sass` with Dart Sass as the implementation. The option here is **`loadPaths`** as well, not `loadPath`, and not `includePaths`.
|
|
143
145
|
|
|
144
146
|
module.exports = function (grunt) {
|
|
145
147
|
grunt.loadNpmTasks("grunt-sass");
|
|
@@ -188,25 +190,31 @@ Including to the project:
|
|
|
188
190
|
|
|
189
191
|
## Using Gerillass with an AI coding agent
|
|
190
192
|
|
|
191
|
-
|
|
193
|
+
A library this size has no training data behind it, so an agent asked to use Gerillass will guess at the argument forms and get them wrong. Two files ship with the package to stop that. Both live inside the installed package, so an agent working in your project can read them straight out of `node_modules/gerillass/`.
|
|
192
194
|
|
|
193
195
|
**`gerillass.json`** describes every mixin and function: its signature, what each argument accepts, examples that compile, and inputs that are refused.
|
|
194
196
|
|
|
195
197
|
const api = require("gerillass/gerillass.json");
|
|
196
198
|
|
|
197
|
-
**`SKILL.md`** is a written guide generated from that manifest
|
|
199
|
+
**`SKILL.md`** is a written guide generated from that manifest. It covers 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:
|
|
198
200
|
|
|
199
201
|
mkdir -p .claude/skills/gerillass
|
|
200
202
|
cp node_modules/gerillass/SKILL.md .claude/skills/gerillass/
|
|
201
203
|
|
|
202
204
|
Otherwise, point your agent at the file and it will read it as plain Markdown.
|
|
203
205
|
|
|
204
|
-
|
|
206
|
+
### Why you can trust what they say
|
|
207
|
+
|
|
208
|
+
Neither file is written by hand. Signatures are parsed from the Sass sources, and the semantics come from a separate set of notes, so nobody can describe a mixin that does not exist.
|
|
209
|
+
|
|
210
|
+
The part that matters is what happens next. The test suite takes every example in the manifest and compiles it. It takes every input the manifest claims is refused and checks that the library really does refuse it, with its own error message rather than an internal Sass one. It runs every example a second time under the `gls-` prefixed name and requires byte-identical CSS. And it fails the build if either generated file is out of date.
|
|
211
|
+
|
|
212
|
+
So the manifest cannot claim behaviour the library does not have. That is the whole point of it. Documentation drifts away from code in most projects, quietly, and an agent reading stale docs writes code that does not work. Here it cannot happen without turning the test suite red first.
|
|
205
213
|
|
|
206
214
|
|
|
207
215
|
## Three ways to call the same mixin
|
|
208
216
|
|
|
209
|
-
None of them is required
|
|
217
|
+
None of them is required. Pick whichever reads best in your project, and stay with it in a given file.
|
|
210
218
|
|
|
211
219
|
**Bare.** The shortest, and fine unless another library defines the same name.
|
|
212
220
|
|
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:
|
|
8
|
+
A Sass mixin library: 53 mixins and 23 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
|
|
|
@@ -79,10 +79,13 @@ a dropped declaration rather than an error.
|
|
|
79
79
|
| `breakpointer` | `.a { @include breakpointer(42); }` |
|
|
80
80
|
| `center` | `.modal { @include center(diagonal); }` |
|
|
81
81
|
| `columnizer` | `.grid { @include columnizer(3, 20px, true, 9); }` |
|
|
82
|
+
| `container-query` | `.title { @include container-query("min", 400px, 800px) { color: red; } }` |
|
|
83
|
+
| `container` | `.card { @include container("card", sideways); }` |
|
|
82
84
|
| `escape-to-parent` | `.a { @include escape-to-parent(42) { color: red; } }` |
|
|
83
85
|
| `except` | `.a { @include except(#ff0000) { margin: 0; } }` |
|
|
84
86
|
| `font-face` | `.a { @include font-face("Inter", "/fonts/inter"); }` |
|
|
85
87
|
| `hide` | `.a { @include hide(nonsense); }` |
|
|
88
|
+
| `line-clamp` | `.a { @include line-clamp(0); }` |
|
|
86
89
|
| `linear-gradient` | `.a { @include linear-gradient(sideways, (red, blue)); }` |
|
|
87
90
|
| `loadify` | `@include loadify(nonsense);` |
|
|
88
91
|
| `only` | `.a { @include only(#ff0000) { margin: 0; } }` |
|
|
@@ -122,14 +125,17 @@ a dropped declaration rather than an error.
|
|
|
122
125
|
| `circle($size)` | Square element with a fully rounded border, i.e. a circle. |
|
|
123
126
|
| `clearfix` | Clears floated children using an ::after pseudo-element. |
|
|
124
127
|
| `columnizer($params...)` | Flexbox grid of equal columns, with an optional gutter and fill behaviour. |
|
|
128
|
+
| `container-query($params...)` | A @container rule, taking the same argument shapes as breakpoint so the two read alike. Sizes may be a key from $map-for-breakpoints or a raw length, and a length is the common case because a container is usually narrower than the viewport. Nothing matches at all unless an ancestor was declared with the container mixin. |
|
|
129
|
+
| `container($name: null, $type: inline-size)` | Marks an element as a query container, so container-query can ask about its width instead of the viewport's. The rule that asks has to sit on a descendant: an element is never matched by a @container rule reading its own container, and nothing warns you when it is not. |
|
|
125
130
|
| `counter($params...)` | CSS counter for a list, with optional text before and after the number. |
|
|
126
131
|
| `ellipsis($width: 100%, $display: inline-block)` | Truncates a single line of text with an ellipsis. |
|
|
127
132
|
| `escape-to-parent($selector: null)` | Re-roots the current selector under another one using @at-root. |
|
|
128
133
|
| `except($params...)` | Selects every sibling except the ones named. |
|
|
129
134
|
| `font-face($font-family, $file-path, $font-style: normal, $font-weight: 400, $file-formats: eot woff2 woff ttf svg)` | Emits an @font-face rule for one family across several file formats. Must be called at the root. |
|
|
130
135
|
| `hide($toggle: "hide")` | Visually hides an element while keeping it available to screen readers, or reverses that. |
|
|
136
|
+
| `line-clamp($lines: 3)` | Truncates text after a number of lines, where ellipsis truncates one. It emits five declarations rather than one because -webkit-line-clamp does nothing on its own: without display: -webkit-box or without -webkit-box-orient: vertical the text is not clamped at all and nothing warns you, and without overflow: hidden the clamped text spills out below the box. The unprefixed line-clamp is emitted too, for when it becomes Baseline. |
|
|
131
137
|
| `linear-gradient($direction, $colors)` | Linear gradient background from a direction name or an angle. |
|
|
132
|
-
| `loadify($params...)` | Fades elements in on page load. Call once at the root to set up, then on each element. |
|
|
138
|
+
| `loadify($params...)` | Fades elements in on page load. Call once at the root to set up, then on each element. Under prefers-reduced-motion: reduce the end state is applied directly and no animation runs. Switching the animation off alone would not do, because the element starts invisible and the animation is what reveals it, so the content would stay hidden for good. |
|
|
133
139
|
| `only($params...)` | Selects only the siblings named. |
|
|
134
140
|
| `placeholder-shown` | Styles an input while its placeholder is visible. |
|
|
135
141
|
| `placeholder` | Styles the placeholder text of an input across vendor prefixes. |
|
|
@@ -156,8 +162,8 @@ a dropped declaration rather than an error.
|
|
|
156
162
|
|
|
157
163
|
## Functions
|
|
158
164
|
|
|
159
|
-
Called like normal Sass functions
|
|
160
|
-
|
|
165
|
+
Called like normal Sass functions, with no `@include`. They are public API,
|
|
166
|
+
and camelCase is what tells them apart from the kebab-case mixins above.
|
|
161
167
|
|
|
162
168
|
| Signature | What it does |
|
|
163
169
|
|---|---|
|
|
@@ -166,6 +172,7 @@ functions rather than mixins; they are public API.
|
|
|
166
172
|
| `convertToEm($value)` | Converts a pixel length to em, against a 16px base. |
|
|
167
173
|
| `convertToNumber($value)` | Parses a string of digits into a number. |
|
|
168
174
|
| `fillNulls($value, $seperation: comma, $skip: false)` | Replaces null entries in a list with 0, or drops them. |
|
|
175
|
+
| `fluid($min, $max, $min-viewport: 320px, $max-viewport: 1280px)` | A clamp() value that grows with the viewport between two widths, then stops. The preferred value keeps a rem term rather than being pure vw, because a vw-only value ignores browser text zoom and fails WCAG 1.4.4. It is a function rather than a mixin because the value is the hard part and belongs to any property, not only font-size. |
|
|
169
176
|
| `fontSizer($size, $time)` | Multiplies a size by a factor. Handy for a modular scale. |
|
|
170
177
|
| `fontSource($font-family, $file-path, $file-formats)` | Builds one src entry for an @font-face rule. |
|
|
171
178
|
| `isColor($value)` | Returns the value if every item in it is a colour, and errors otherwise. |
|
package/gerillass.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gerillass",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
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",
|
|
@@ -571,6 +571,72 @@
|
|
|
571
571
|
".grid { @include columnizer(3, nonsense); }"
|
|
572
572
|
]
|
|
573
573
|
},
|
|
574
|
+
{
|
|
575
|
+
"name": "container-query",
|
|
576
|
+
"kind": "mixin",
|
|
577
|
+
"arguments": [
|
|
578
|
+
{
|
|
579
|
+
"name": "$params",
|
|
580
|
+
"variadic": true,
|
|
581
|
+
"accepts": [
|
|
582
|
+
"a size",
|
|
583
|
+
"two sizes for a range",
|
|
584
|
+
"one of min, max, only or between followed by a size",
|
|
585
|
+
"$name as a keyword, to query one named container"
|
|
586
|
+
]
|
|
587
|
+
}
|
|
588
|
+
],
|
|
589
|
+
"file": "scss/library/_container-query.scss",
|
|
590
|
+
"signature": "container-query($params...)",
|
|
591
|
+
"summary": "A @container rule, taking the same argument shapes as breakpoint so the two read alike. Sizes may be a key from $map-for-breakpoints or a raw length, and a length is the common case because a container is usually narrower than the viewport. Nothing matches at all unless an ancestor was declared with the container mixin.",
|
|
592
|
+
"examples": [
|
|
593
|
+
".title { @include container-query(\"min\", 400px) { font-size: 2rem; } }",
|
|
594
|
+
".title { @include container-query(\"max\", 399px) { font-size: 1rem; } }",
|
|
595
|
+
".title { @include container-query(\"between\", 300px 500px) { color: red; } }",
|
|
596
|
+
".title { @include container-query(300px, 500px) { color: red; } }",
|
|
597
|
+
".title { @include container-query(\"min\", \"medium\") { color: red; } }",
|
|
598
|
+
".title { @include container-query(\"min\", 400px, $name: \"card\") { color: red; } }"
|
|
599
|
+
],
|
|
600
|
+
"rejects": [
|
|
601
|
+
".title { @include container-query(\"min\", 400px, 800px) { color: red; } }",
|
|
602
|
+
".title { @include container-query(\"min\", 400px, $name: 42) { color: red; } }"
|
|
603
|
+
]
|
|
604
|
+
},
|
|
605
|
+
{
|
|
606
|
+
"name": "container",
|
|
607
|
+
"kind": "mixin",
|
|
608
|
+
"arguments": [
|
|
609
|
+
{
|
|
610
|
+
"name": "$name",
|
|
611
|
+
"default": "null",
|
|
612
|
+
"accepts": [
|
|
613
|
+
"a container name as a string, such as \"card\"",
|
|
614
|
+
"null for an unnamed container"
|
|
615
|
+
]
|
|
616
|
+
},
|
|
617
|
+
{
|
|
618
|
+
"name": "$type",
|
|
619
|
+
"default": "inline-size",
|
|
620
|
+
"accepts": [
|
|
621
|
+
"inline-size",
|
|
622
|
+
"size",
|
|
623
|
+
"normal"
|
|
624
|
+
]
|
|
625
|
+
}
|
|
626
|
+
],
|
|
627
|
+
"file": "scss/library/_container.scss",
|
|
628
|
+
"signature": "container($name: null, $type: inline-size)",
|
|
629
|
+
"summary": "Marks an element as a query container, so container-query can ask about its width instead of the viewport's. The rule that asks has to sit on a descendant: an element is never matched by a @container rule reading its own container, and nothing warns you when it is not.",
|
|
630
|
+
"examples": [
|
|
631
|
+
".card { @include container(\"card\"); }",
|
|
632
|
+
".panel { @include container; }",
|
|
633
|
+
".panel { @include container(null, size); }"
|
|
634
|
+
],
|
|
635
|
+
"rejects": [
|
|
636
|
+
".card { @include container(\"card\", sideways); }",
|
|
637
|
+
".card { @include container(42); }"
|
|
638
|
+
]
|
|
639
|
+
},
|
|
574
640
|
{
|
|
575
641
|
"name": "counter",
|
|
576
642
|
"kind": "mixin",
|
|
@@ -749,6 +815,35 @@
|
|
|
749
815
|
".a { @include hide(nonsense); }"
|
|
750
816
|
]
|
|
751
817
|
},
|
|
818
|
+
{
|
|
819
|
+
"name": "line-clamp",
|
|
820
|
+
"kind": "mixin",
|
|
821
|
+
"arguments": [
|
|
822
|
+
{
|
|
823
|
+
"name": "$lines",
|
|
824
|
+
"default": "3",
|
|
825
|
+
"accepts": [
|
|
826
|
+
"a whole number of lines, at least 1",
|
|
827
|
+
"none, to undo a clamp"
|
|
828
|
+
]
|
|
829
|
+
}
|
|
830
|
+
],
|
|
831
|
+
"file": "scss/library/_line-clamp.scss",
|
|
832
|
+
"signature": "line-clamp($lines: 3)",
|
|
833
|
+
"summary": "Truncates text after a number of lines, where ellipsis truncates one. It emits five declarations rather than one because -webkit-line-clamp does nothing on its own: without display: -webkit-box or without -webkit-box-orient: vertical the text is not clamped at all and nothing warns you, and without overflow: hidden the clamped text spills out below the box. The unprefixed line-clamp is emitted too, for when it becomes Baseline.",
|
|
834
|
+
"examples": [
|
|
835
|
+
".excerpt { @include line-clamp(3); }",
|
|
836
|
+
".title { @include line-clamp(2); }",
|
|
837
|
+
".full { @include line-clamp(none); }",
|
|
838
|
+
".default { @include line-clamp; }"
|
|
839
|
+
],
|
|
840
|
+
"rejects": [
|
|
841
|
+
".a { @include line-clamp(0); }",
|
|
842
|
+
".a { @include line-clamp(2.5); }",
|
|
843
|
+
".a { @include line-clamp(3px); }",
|
|
844
|
+
".a { @include line-clamp(\"three\"); }"
|
|
845
|
+
]
|
|
846
|
+
},
|
|
752
847
|
{
|
|
753
848
|
"name": "linear-gradient",
|
|
754
849
|
"kind": "mixin",
|
|
@@ -796,7 +891,7 @@
|
|
|
796
891
|
],
|
|
797
892
|
"file": "scss/library/_loadify.scss",
|
|
798
893
|
"signature": "loadify($params...)",
|
|
799
|
-
"summary": "Fades elements in on page load. Call once at the root to set up, then on each element.",
|
|
894
|
+
"summary": "Fades elements in on page load. Call once at the root to set up, then on each element. Under prefers-reduced-motion: reduce the end state is applied directly and no animation runs. Switching the animation off alone would not do, because the element starts invisible and the animation is what reveals it, so the content would stay hidden for good.",
|
|
800
895
|
"examples": [
|
|
801
896
|
"@include loadify(init);\n.a { @include loadify; }",
|
|
802
897
|
"@include loadify;\n.a { @include loadify(0.4s, 1s); }"
|
|
@@ -1509,6 +1604,55 @@
|
|
|
1509
1604
|
".a { --x: #{fillNulls(1px null, nonsense)}; }"
|
|
1510
1605
|
]
|
|
1511
1606
|
},
|
|
1607
|
+
{
|
|
1608
|
+
"name": "fluid",
|
|
1609
|
+
"kind": "function",
|
|
1610
|
+
"arguments": [
|
|
1611
|
+
{
|
|
1612
|
+
"name": "$min",
|
|
1613
|
+
"required": true,
|
|
1614
|
+
"accepts": [
|
|
1615
|
+
"a length in px or rem, the value at $min-viewport"
|
|
1616
|
+
]
|
|
1617
|
+
},
|
|
1618
|
+
{
|
|
1619
|
+
"name": "$max",
|
|
1620
|
+
"required": true,
|
|
1621
|
+
"accepts": [
|
|
1622
|
+
"a length in px or rem, the value at $max-viewport, no smaller than $min"
|
|
1623
|
+
]
|
|
1624
|
+
},
|
|
1625
|
+
{
|
|
1626
|
+
"name": "$min-viewport",
|
|
1627
|
+
"default": "320px",
|
|
1628
|
+
"accepts": [
|
|
1629
|
+
"a length in px or rem, the width below which the value stops shrinking"
|
|
1630
|
+
]
|
|
1631
|
+
},
|
|
1632
|
+
{
|
|
1633
|
+
"name": "$max-viewport",
|
|
1634
|
+
"default": "1280px",
|
|
1635
|
+
"accepts": [
|
|
1636
|
+
"a length in px or rem, larger than $min-viewport"
|
|
1637
|
+
]
|
|
1638
|
+
}
|
|
1639
|
+
],
|
|
1640
|
+
"file": "scss/utilities/_fluid.scss",
|
|
1641
|
+
"signature": "fluid($min, $max, $min-viewport: 320px, $max-viewport: 1280px)",
|
|
1642
|
+
"summary": "A clamp() value that grows with the viewport between two widths, then stops. The preferred value keeps a rem term rather than being pure vw, because a vw-only value ignores browser text zoom and fails WCAG 1.4.4. It is a function rather than a mixin because the value is the hard part and belongs to any property, not only font-size.",
|
|
1643
|
+
"examples": [
|
|
1644
|
+
".title { font-size: fluid(24px, 48px); }",
|
|
1645
|
+
".title { font-size: fluid(1rem, 3rem, 320px, 1200px); }",
|
|
1646
|
+
".section { padding: fluid(16px, 64px) fluid(8px, 40px); }",
|
|
1647
|
+
".stack { gap: fluid(0.5rem, 2rem); }"
|
|
1648
|
+
],
|
|
1649
|
+
"rejects": [
|
|
1650
|
+
".title { font-size: fluid(16px, 24px, 1280px, 320px); }",
|
|
1651
|
+
".title { font-size: fluid(40px, 20px); }",
|
|
1652
|
+
".title { font-size: fluid(16, 24); }",
|
|
1653
|
+
".title { font-size: fluid(1em, 2em); }"
|
|
1654
|
+
]
|
|
1655
|
+
},
|
|
1512
1656
|
{
|
|
1513
1657
|
"name": "fontSizer",
|
|
1514
1658
|
"kind": "function",
|
package/package.json
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
18
|
"license": "Apache-2.0",
|
|
19
|
-
"version": "2.
|
|
19
|
+
"version": "2.1.0",
|
|
20
20
|
"repository": {
|
|
21
21
|
"type": "git",
|
|
22
22
|
"url": "git+https://github.com/selfishprimate/gerillass.git"
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"sass-tool-set"
|
|
41
41
|
],
|
|
42
42
|
"scripts": {
|
|
43
|
-
"manifest": "node tools/build-manifest.js && node tools/build-skill.js",
|
|
43
|
+
"manifest": "node tools/build-manifest.js && node tools/build-skill.js && node tools/build-llms-txt.js",
|
|
44
44
|
"test": "jest"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
@charset "UTF-8";
|
|
2
|
+
|
|
3
|
+
@use "sass:list";
|
|
4
|
+
@use "sass:map";
|
|
5
|
+
@use "sass:meta";
|
|
6
|
+
@use "sass:string";
|
|
7
|
+
@use "../maps/map-for-breakpoints" as *;
|
|
8
|
+
@use "../utilities/validate-breakpoint" as *;
|
|
9
|
+
|
|
10
|
+
// A @container rule, taking the same argument shapes as `breakpoint` so the two
|
|
11
|
+
// read alike. Sizes may be a key from $map-for-breakpoints or a raw length; a
|
|
12
|
+
// container is usually narrower than the viewport, so a length is the common
|
|
13
|
+
// case.
|
|
14
|
+
//
|
|
15
|
+
// Pass $name to query one named container rather than the nearest ancestor.
|
|
16
|
+
// The container itself is declared with the `container` mixin, on an ancestor
|
|
17
|
+
// of whatever this styles.
|
|
18
|
+
@mixin container-query($params...) {
|
|
19
|
+
$keywords: meta.keywords($params);
|
|
20
|
+
$name: map.get($keywords, name);
|
|
21
|
+
$count: list.length($params);
|
|
22
|
+
|
|
23
|
+
@if $name != null and meta.type-of($name) != "string" {
|
|
24
|
+
@error "`#{$name}` is not a valid $name for `container-query`. Pass a container name as a string, such as `\"card\"`.";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
$condition: null;
|
|
28
|
+
|
|
29
|
+
@if $count == 1 {
|
|
30
|
+
$condition: "(width: #{validateBreakpoint(list.nth($params, 1))})";
|
|
31
|
+
} @else if $count == 2 {
|
|
32
|
+
$mode: list.nth($params, 1);
|
|
33
|
+
@if not list.index("only" "min" "max" "between", $mode) {
|
|
34
|
+
// Two bare sizes mean a range, as they do in `breakpoint`.
|
|
35
|
+
$condition: "(min-width: #{validateBreakpoint($mode)}) and (max-width: #{validateBreakpoint(list.nth($params, 2))})";
|
|
36
|
+
} @else {
|
|
37
|
+
$value: list.nth($params, 2);
|
|
38
|
+
@if $mode == "only" {
|
|
39
|
+
$condition: "(width: #{validateBreakpoint($value)})";
|
|
40
|
+
} @else if $mode == "min" {
|
|
41
|
+
$condition: "(min-width: #{validateBreakpoint($value)})";
|
|
42
|
+
} @else if $mode == "max" {
|
|
43
|
+
$condition: "(max-width: #{validateBreakpoint($value)})";
|
|
44
|
+
} @else {
|
|
45
|
+
$start: list.nth($value, 1);
|
|
46
|
+
$end: list.nth($value, 2);
|
|
47
|
+
$condition: "(min-width: #{validateBreakpoint($start)}) and (max-width: #{validateBreakpoint($end)})";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
} @else {
|
|
51
|
+
@error "`container-query` takes one or two arguments, and was given #{$count}. Pass a size, two sizes for a range, or one of `min`, `max`, `only` or `between` followed by a size.";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@if $name {
|
|
55
|
+
@container #{string.unquote($name)} #{$condition} {
|
|
56
|
+
@content;
|
|
57
|
+
}
|
|
58
|
+
} @else {
|
|
59
|
+
@container #{$condition} {
|
|
60
|
+
@content;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
@charset "UTF-8";
|
|
2
|
+
|
|
3
|
+
@use "sass:list";
|
|
4
|
+
@use "sass:meta";
|
|
5
|
+
@use "sass:string";
|
|
6
|
+
|
|
7
|
+
// Marks an element as a query container, so `container-query` can ask about its
|
|
8
|
+
// width instead of the viewport's.
|
|
9
|
+
//
|
|
10
|
+
// The rule that asks has to sit on a descendant. An element is not matched by a
|
|
11
|
+
// @container rule that reads its own container, and nothing warns you: the rule
|
|
12
|
+
// is simply never applied. Measured in a browser, a 600px element with
|
|
13
|
+
// `container-type: inline-size` on itself did not match `@container (min-width:
|
|
14
|
+
// 400px)`, while the same query did match a child of it.
|
|
15
|
+
@mixin container($name: null, $type: inline-size) {
|
|
16
|
+
@if not list.index("inline-size" "size" "normal", $type) {
|
|
17
|
+
@error "`#{$type}` is not a valid $type for `container`. Pass one of: inline-size, size, normal.";
|
|
18
|
+
}
|
|
19
|
+
@if $name != null and meta.type-of($name) != "string" {
|
|
20
|
+
@error "`#{$name}` is not a valid $name for `container`. Pass a name as a string, such as `\"card\"`, or no name at all.";
|
|
21
|
+
}
|
|
22
|
+
container-type: $type;
|
|
23
|
+
@if $name {
|
|
24
|
+
container-name: string.unquote($name);
|
|
25
|
+
}
|
|
26
|
+
}
|
package/scss/library/_index.scss
CHANGED
|
@@ -19,12 +19,15 @@
|
|
|
19
19
|
@forward "circle";
|
|
20
20
|
@forward "clearfix";
|
|
21
21
|
@forward "columnizer";
|
|
22
|
+
@forward "container";
|
|
23
|
+
@forward "container-query";
|
|
22
24
|
@forward "counter";
|
|
23
25
|
@forward "ellipsis";
|
|
24
26
|
@forward "escape-to-parent";
|
|
25
27
|
@forward "except";
|
|
26
28
|
@forward "font-face";
|
|
27
29
|
@forward "hide";
|
|
30
|
+
@forward "line-clamp";
|
|
28
31
|
@forward "linear-gradient";
|
|
29
32
|
@forward "loadify";
|
|
30
33
|
@forward "only";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
@charset "UTF-8";
|
|
2
|
+
|
|
3
|
+
@use "sass:math";
|
|
4
|
+
@use "sass:meta";
|
|
5
|
+
|
|
6
|
+
// Truncates text after a number of lines. `ellipsis` does one line; this does
|
|
7
|
+
// several, and needs five declarations rather than one because -webkit-line-clamp
|
|
8
|
+
// only works alongside two other properties.
|
|
9
|
+
//
|
|
10
|
+
// All five were measured in a browser, on a paragraph that runs to five lines
|
|
11
|
+
// with a clamp of three:
|
|
12
|
+
//
|
|
13
|
+
// the three together 3 lines, with an ellipsis
|
|
14
|
+
// without -webkit-box-orient 5 lines, no clamping and no warning
|
|
15
|
+
// without display: -webkit-box 5 lines, likewise
|
|
16
|
+
// the standard line-clamp alone 5 lines; it is not Baseline yet
|
|
17
|
+
// without overflow: hidden the box is 3 lines, but the rest of the text
|
|
18
|
+
// spills out below it
|
|
19
|
+
//
|
|
20
|
+
// So the standard property is emitted for the future and the prefixed trio is
|
|
21
|
+
// what actually does the work today.
|
|
22
|
+
@mixin line-clamp($lines: 3) {
|
|
23
|
+
@if $lines != none {
|
|
24
|
+
@if meta.type-of($lines) != "number" or not math.is-unitless($lines) {
|
|
25
|
+
@error "`#{$lines}` is not a valid $lines for `line-clamp`. Pass a whole number of lines, or `none` to undo a clamp.";
|
|
26
|
+
}
|
|
27
|
+
@if $lines < 1 or $lines != math.round($lines) {
|
|
28
|
+
@error "`#{$lines}` is not a valid $lines for `line-clamp`. Pass a whole number of lines that is at least 1, or `none` to undo a clamp.";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
display: -webkit-box;
|
|
32
|
+
-webkit-box-orient: vertical;
|
|
33
|
+
-webkit-line-clamp: $lines;
|
|
34
|
+
line-clamp: $lines;
|
|
35
|
+
overflow: hidden;
|
|
36
|
+
}
|
|
@@ -20,6 +20,17 @@
|
|
|
20
20
|
backface-visibility: hidden;
|
|
21
21
|
animation-name: loadify;
|
|
22
22
|
animation-fill-mode: forwards;
|
|
23
|
+
|
|
24
|
+
// The element starts invisible and the animation is what reveals it,
|
|
25
|
+
// so switching the animation off is not enough on its own: it would
|
|
26
|
+
// leave the content hidden for good. Under reduced motion the end
|
|
27
|
+
// state is applied directly instead, and nothing moves.
|
|
28
|
+
@media (prefers-reduced-motion: reduce) {
|
|
29
|
+
opacity: 1;
|
|
30
|
+
visibility: visible;
|
|
31
|
+
backface-visibility: visible;
|
|
32
|
+
animation: none;
|
|
33
|
+
}
|
|
23
34
|
}
|
|
24
35
|
} @else if (list.length($params) == 1 and list.nth($params, 1) != "init") or (list.length($params) == 1 and meta.type-of(list.nth($params, 1)) != "string") {
|
|
25
36
|
@error "#{list.nth($params, 1)} is not a valid argument. Please pass `init` as an argument to initialize the effect or do not pass any argument at all.";
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
@charset "UTF-8";
|
|
2
|
+
|
|
3
|
+
@use "sass:list";
|
|
4
|
+
@use "sass:math";
|
|
5
|
+
@use "sass:meta";
|
|
6
|
+
@use "sass:string";
|
|
7
|
+
@use "../utilities/clear-unit" as *;
|
|
8
|
+
@use "../utilities/remify" as *;
|
|
9
|
+
|
|
10
|
+
// A value that grows with the viewport between two widths, then stops.
|
|
11
|
+
//
|
|
12
|
+
// clamp() takes a floor, a preferred value and a ceiling. The preferred value
|
|
13
|
+
// is the straight line through (min-viewport, min) and (max-viewport, max):
|
|
14
|
+
//
|
|
15
|
+
// slope = (max - min) / (max-viewport - min-viewport)
|
|
16
|
+
// intercept = min - slope * min-viewport
|
|
17
|
+
// preferred = intercept + slope * 100vw
|
|
18
|
+
//
|
|
19
|
+
// The intercept is emitted in rem rather than px deliberately. A preferred
|
|
20
|
+
// value made only of vw ignores browser zoom, which fails WCAG 1.4.4, and
|
|
21
|
+
// keeping a rem term in the sum is what restores it. This is the part that is
|
|
22
|
+
// easy to get wrong by hand, and the reason this is a function rather than a
|
|
23
|
+
// mixin: the value is the hard part, and it belongs to any property, not just
|
|
24
|
+
// font-size.
|
|
25
|
+
//
|
|
26
|
+
// Sass cannot add rem to vw, so the sum is assembled as a string. CSS evaluates
|
|
27
|
+
// it; Sass only writes it.
|
|
28
|
+
|
|
29
|
+
$_fluid-root-size: 16px;
|
|
30
|
+
|
|
31
|
+
@function fluid($min, $max, $min-viewport: 320px, $max-viewport: 1280px) {
|
|
32
|
+
// Pairs rather than a map: two arguments can legitimately hold the same
|
|
33
|
+
// length, and a map would reject that as a duplicate key.
|
|
34
|
+
$given: (($min, "$min"), ($max, "$max"), ($min-viewport, "$min-viewport"), ($max-viewport, "$max-viewport"));
|
|
35
|
+
@each $pair in $given {
|
|
36
|
+
$value: list.nth($pair, 1);
|
|
37
|
+
$label: list.nth($pair, 2);
|
|
38
|
+
@if meta.type-of($value) != "number" or math.is-unitless($value) {
|
|
39
|
+
@error "`#{$value}` is not a valid #{$label} for `fluid`. Pass a length in px or rem.";
|
|
40
|
+
}
|
|
41
|
+
@if math.unit($value) != "px" and math.unit($value) != "rem" {
|
|
42
|
+
@error "`#{$value}` is not a valid #{$label} for `fluid`. Pass a length in px or rem, not #{math.unit($value)}.";
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Everything is compared in px, because Sass cannot convert rem to px on its
|
|
47
|
+
// own: it has no way to know the root font size, so the assumption is stated
|
|
48
|
+
// here rather than hidden.
|
|
49
|
+
$min-px: $min;
|
|
50
|
+
$max-px: $max;
|
|
51
|
+
$from-px: $min-viewport;
|
|
52
|
+
$to-px: $max-viewport;
|
|
53
|
+
@if math.unit($min) == "rem" { $min-px: clearUnit($min) * $_fluid-root-size; }
|
|
54
|
+
@if math.unit($max) == "rem" { $max-px: clearUnit($max) * $_fluid-root-size; }
|
|
55
|
+
@if math.unit($min-viewport) == "rem" { $from-px: clearUnit($min-viewport) * $_fluid-root-size; }
|
|
56
|
+
@if math.unit($max-viewport) == "rem" { $to-px: clearUnit($max-viewport) * $_fluid-root-size; }
|
|
57
|
+
|
|
58
|
+
@if $from-px >= $to-px {
|
|
59
|
+
@error "`fluid` needs $min-viewport to be smaller than $max-viewport, and was given #{$min-viewport} and #{$max-viewport}.";
|
|
60
|
+
}
|
|
61
|
+
@if $min-px > $max-px {
|
|
62
|
+
@error "`fluid` needs $min to be no larger than $max, and was given #{$min} and #{$max}. clamp() would return the floor at every width, so the value would never grow.";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
$slope: math.div($max-px - $min-px, $to-px - $from-px);
|
|
66
|
+
$intercept: $min-px - $slope * $from-px;
|
|
67
|
+
|
|
68
|
+
// Four decimals is past the point any browser renders differently, and keeps
|
|
69
|
+
// the output readable.
|
|
70
|
+
$vw: math.div(math.round($slope * 100 * 10000), 10000);
|
|
71
|
+
$floor: math.div(math.round(clearUnit(remify($min-px)) * 10000), 10000);
|
|
72
|
+
$base: math.div(math.round(clearUnit(remify($intercept)) * 10000), 10000);
|
|
73
|
+
$ceiling: math.div(math.round(clearUnit(remify($max-px)) * 10000), 10000);
|
|
74
|
+
|
|
75
|
+
@return string.unquote("clamp(#{$floor}rem, #{$base}rem + #{$vw}vw, #{$ceiling}rem)");
|
|
76
|
+
}
|