@codenhub/styles 0.3.0 → 0.5.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 +1 -1
- package/dist/aesthetics/chunky-tile.css +1 -1
- package/dist/aesthetics/cyber.css +1 -0
- package/dist/aesthetics/glass.css +1 -1
- package/dist/aesthetics/index.css +1 -1
- package/dist/aesthetics/neobrutalism.css +1 -1
- package/dist/aesthetics/pixel.css +1 -1
- package/dist/aesthetics/sketch.css +1 -0
- package/dist/components.css +1 -1
- package/dist/index.css +1 -1
- package/dist/native.css +1 -1
- package/dist/palette.css +1 -1
- package/dist/theme.css +1 -1
- package/dist/tw/aesthetics/chunky-tile.css +198 -111
- package/dist/tw/aesthetics/cyber.css +227 -0
- package/dist/tw/aesthetics/glass.css +134 -47
- package/dist/tw/aesthetics/index.css +2 -0
- package/dist/tw/aesthetics/neobrutalism.css +149 -49
- package/dist/tw/aesthetics/pixel.css +151 -63
- package/dist/tw/aesthetics/sketch.css +326 -0
- package/dist/tw/box.css +107 -35
- package/dist/tw/components/button.css +40 -19
- package/dist/tw/components/feedback.css +38 -15
- package/dist/tw/components/form.css +161 -154
- package/dist/tw/components/index.css +15 -3
- package/dist/tw/components/loader.css +34 -25
- package/dist/tw/components/surface.css +28 -71
- package/dist/tw/components/tooltip.css +20 -10
- package/dist/tw/corner.css +26 -0
- package/dist/tw/elevation.css +8 -9
- package/dist/tw/intent.css +17 -9
- package/dist/tw/native.css +45 -39
- package/dist/tw/palette.css +452 -446
- package/dist/tw/presentation.css +9 -4
- package/dist/tw/surface.css +62 -0
- package/dist/tw/theme.css +106 -75
- package/dist/tw/utilities/content.css +25 -16
- package/dist/tw/utilities/layout.css +2 -2
- package/docs/accessibility.md +16 -16
- package/docs/changelog/0.4.0.md +12 -0
- package/docs/changelog/0.5.0.md +38 -0
- package/docs/changelog/index.md +2 -0
- package/docs/concepts.md +5 -5
- package/docs/index.md +3 -3
- package/docs/setup.md +23 -3
- package/docs/usage/aesthetics.md +119 -2
- package/docs/usage/buttons.md +13 -13
- package/docs/usage/composing.md +11 -11
- package/docs/usage/content-and-layout.md +13 -13
- package/docs/usage/customizing.md +97 -44
- package/docs/usage/feedback.md +1 -1
- package/docs/usage/forms.md +11 -11
- package/docs/usage/theming.md +1 -1
- package/llms-full.txt +359 -122
- package/package.json +22 -2
|
@@ -35,44 +35,12 @@
|
|
|
35
35
|
derived from it and the two must not drift apart. Typed text is
|
|
36
36
|
`--intent-color` over a wash of its own tone, past 10:1 in both themes at
|
|
37
37
|
any value this cap is likely to take, so the bound still does what it was
|
|
38
|
-
written for.
|
|
38
|
+
written for.
|
|
39
|
+
|
|
40
|
+
The fill and the ink are `box`'s own, unrestated: its `--_capped` reads
|
|
41
|
+
this cap, so the plate and the ink ramp stay in step at every bound,
|
|
42
|
+
including the checked state below that lifts it. */
|
|
39
43
|
--_fill-cap: 6%;
|
|
40
|
-
/* The floor is zero everywhere except a checked toggle, which needs a ground
|
|
41
|
-
under its mark. A container's `.ghost` would otherwise leave a checked
|
|
42
|
-
checkbox as a tick floating on the page -- our own cascade producing the
|
|
43
|
-
broken result, which is the test a bound has to pass. */
|
|
44
|
-
--_fill-floor: 0%;
|
|
45
|
-
--_fill: calc(
|
|
46
|
-
clamp(
|
|
47
|
-
var(--_fill-floor),
|
|
48
|
-
min(var(--ui-fill, var(--_d-fill)), var(--_fill-cap), var(--intent-fill-max, 100%)),
|
|
49
|
-
100%
|
|
50
|
-
) *
|
|
51
|
-
var(--ui-bg-alpha, 1)
|
|
52
|
-
);
|
|
53
|
-
/* The ink follows *this* control's fill, not `box`'s. `box` ramps against
|
|
54
|
-
`--_capped`, which knows `--intent-fill-max` but not `--_fill-cap`, so on a
|
|
55
|
-
control it would read a fill far higher than the one actually painted: a
|
|
56
|
-
`.primary.solid` checkbox has `--ui-fill: 100%` and an unbounded intent, so
|
|
57
|
-
`box` would call for the contrast tone while the plate sits at 20%. White
|
|
58
|
-
tick on light grey.
|
|
59
|
-
|
|
60
|
-
Ramping against `--_fill` keeps the two in step at every bound, including
|
|
61
|
-
the checked state that lifts them.
|
|
62
|
-
|
|
63
|
-
`--_fg-on-fill-floor` is the seam `:checked` pins instead of touching
|
|
64
|
-
`--ui-fg-on-fill` directly. `.ghost`/`.soft` are unlayered CSS (P3, and see
|
|
65
|
-
model.md's `--_fill-cap` history) and `text-control` compiles into
|
|
66
|
-
Tailwind's `utilities` layer, so a layered `:checked` writing
|
|
67
|
-
`--ui-fg-on-fill` the way it writes `--_fill-cap`/`--_fill-floor` would
|
|
68
|
-
lose to `.ghost`'s unlayered `--ui-fg-on-fill: 0%` regardless of
|
|
69
|
-
specificity -- the same trap `--_fill-cap` exists to avoid on the fill
|
|
70
|
-
itself. A private floor `.ghost`/`.soft` never touch has nothing
|
|
71
|
-
unlayered contesting it. */
|
|
72
|
-
--_on-fill: max(
|
|
73
|
-
var(--_fg-on-fill-floor, 0%),
|
|
74
|
-
min(var(--ui-fg-on-fill, var(--_d-fg-on-fill)), max(0%, calc(var(--_fill) * 2 - 100%)))
|
|
75
|
-
);
|
|
76
44
|
/* The resting line is a fraction of the intent rather than all of it, and the
|
|
77
45
|
fraction is what makes the pointer visible. Drawn at full strength a control
|
|
78
46
|
already sits at its intent's darkest, and hover moves it to a neighbouring
|
|
@@ -87,9 +55,11 @@
|
|
|
87
55
|
there is nothing else marking an unchecked box. */
|
|
88
56
|
--_line-rest: 60%;
|
|
89
57
|
/* Two seams rather than one composed result. `--_line-tone` is what colour the
|
|
90
|
-
line is, and the states below move it; `--_line` folds in
|
|
91
|
-
the
|
|
92
|
-
restate
|
|
58
|
+
line is, and the states below move it; `--_line` folds in the fill, walking
|
|
59
|
+
the line toward the intent colour by the fill amount, so a state never has
|
|
60
|
+
to restate that blend to keep it. It is a field's own form of P3 rather than
|
|
61
|
+
`box`'s: a field keeps its line whatever it is filled with, so the line walks
|
|
62
|
+
toward the fill's colour instead of fading out into it.
|
|
93
63
|
|
|
94
64
|
Written as a single `--_line` that states overwrote, a hovered or focused
|
|
95
65
|
control lost the blend along with the tone, because replacing a composed
|
|
@@ -133,14 +103,18 @@
|
|
|
133
103
|
--_line-tone: var(--intent-color);
|
|
134
104
|
}
|
|
135
105
|
|
|
136
|
-
/* R8, P6: `:checked`
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
106
|
+
/* R8, P6: `:checked` is a state, so it re-declares the presentation inputs
|
|
107
|
+
-- a full fill and full contrast ink -- and lifts the cap that would bound
|
|
108
|
+
them, and `box` composes the rest. It does not write a result (the failure
|
|
109
|
+
mode P6 exists for: an early version pinned `--_fill` and `--_fg` directly,
|
|
110
|
+
which made a checked toggle ignore its own aesthetic and elevation along
|
|
111
|
+
with presentation). It sits in `utilities` at 0-2-0, above the fill
|
|
112
|
+
classes in `components`, so a checked toggle renders one plate whatever
|
|
113
|
+
presentation it or a container asks for, and a consumer's utility loses to
|
|
114
|
+
it because state wins over every choice (model.md#precedence). A toggle is
|
|
115
|
+
a void `<input>`, so the inputs it writes reach nothing but its own mark.
|
|
116
|
+
|
|
117
|
+
The fill used to be pinned at `12%` rather than `100%`, which kept presentation
|
|
144
118
|
live on the checked mark too -- a checked `.soft` toggle rendered a pale
|
|
145
119
|
plate with just the mark at full strength, the classic ring-and-dot radio.
|
|
146
120
|
The stress-test pass found that cost more than it bought on a real screen:
|
|
@@ -154,20 +128,19 @@
|
|
|
154
128
|
something. Lives here, not on the toggles, because a text input is never
|
|
155
129
|
`:checked`.
|
|
156
130
|
|
|
157
|
-
The ink is
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
full contrast too, or the ramp's own ceiling has nothing to reach. */
|
|
131
|
+
The ink is declared alongside the fill: the other half of what a fill
|
|
132
|
+
class asks for is how much contrast ink it wants (`--ui-fg-on-fill: 0%` is
|
|
133
|
+
`.ghost`/`.soft` declaring "none, my plate stays quiet"), and setting the
|
|
134
|
+
fill without this left a checked `.ghost`/`.soft` toggle with an opaque
|
|
135
|
+
plate and a mark that still read as though the plate were quiet -- both
|
|
136
|
+
derived from the same intent tone, close enough to disappear into each
|
|
137
|
+
other. `box` ramps the ink against the bounded fill before `--ui-bg-alpha`
|
|
138
|
+
thins it, so a checked toggle under `.glass` keeps full contrast ink on
|
|
139
|
+
its translucent plate. */
|
|
167
140
|
&:checked {
|
|
141
|
+
--ui-fill: 100%;
|
|
142
|
+
--ui-fg-on-fill: 100%;
|
|
168
143
|
--_fill-cap: 100%;
|
|
169
|
-
--_fill-floor: 100%;
|
|
170
|
-
--_fg-on-fill-floor: 100%;
|
|
171
144
|
}
|
|
172
145
|
|
|
173
146
|
/* Native decorations a field replaces with its own artwork. */
|
|
@@ -187,94 +160,104 @@
|
|
|
187
160
|
}
|
|
188
161
|
}
|
|
189
162
|
|
|
190
|
-
/*
|
|
191
|
-
utility
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
163
|
+
/* The component seams: rules that exist to beat their own component's
|
|
164
|
+
`@utility`, placed in `utilities` and winning there on specificity, each at
|
|
165
|
+
least 0-2-0 against the component's 0-1-0. They write only package privates
|
|
166
|
+
(`--_fill-cap`, `--_edge-floor`) or a state's intent slots, so nothing a
|
|
167
|
+
consumer writes competes with them. They used to be unlayered, which stopped
|
|
168
|
+
working once `/components` and `/native` put the components in `utilities`
|
|
169
|
+
too. See docs/internal/model.md#cascade-layers. */
|
|
170
|
+
@layer utilities {
|
|
171
|
+
/* A fill class on the element names its own cap; the 6% cascade cap in the
|
|
172
|
+
utility is for a container reaching a field nobody classed. Both raise
|
|
173
|
+
`--_fill-cap` rather than writing `--_fill` (P6), which is also what keeps
|
|
174
|
+
`.soft` < `.solid` -- written as a rewrite the louder name rendered the
|
|
175
|
+
quieter box, measured and inverted on `.ipt`, `.checkbox` and `.radio`.
|
|
176
|
+
20% clears `.soft`'s 12% and matches the plate a `.solid` badge draws.
|
|
177
|
+
|
|
178
|
+
`.switch` is deliberately absent: its track caps at 40% in its own utility,
|
|
179
|
+
and a 0-2-0 rule here would overrule that. See model.md#the-bounds-that-survive-and-the-test-for-keeping-one. */
|
|
180
|
+
:is(.text-control, .ipt, .textarea, .select, .input-group).soft {
|
|
181
|
+
--_fill-cap: 12%;
|
|
182
|
+
}
|
|
202
183
|
|
|
203
|
-
:is(.text-control, .ipt, .textarea, .select, .input-group).solid {
|
|
204
|
-
|
|
205
|
-
}
|
|
184
|
+
:is(.text-control, .ipt, .textarea, .select, .input-group).solid {
|
|
185
|
+
--_fill-cap: 20%;
|
|
186
|
+
}
|
|
206
187
|
|
|
207
|
-
/* A checked text-control fills with its own intent, so neutral has to drop its
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
188
|
+
/* A checked text-control fills with its own intent, so neutral has to drop its
|
|
189
|
+
20% cap here. No toggle needs this arm any more (see below), but a text
|
|
190
|
+
input is never `:checked` in practice either -- kept for whatever custom
|
|
191
|
+
`.text-control` composition does check itself. A plain rule at 0-2-0 in
|
|
192
|
+
`utilities`, above every intent class in `components`, so `.neutral` on the
|
|
193
|
+
element cannot put the cap back. */
|
|
194
|
+
:is(.text-control, input):checked {
|
|
195
|
+
--intent-fill-max: 100%;
|
|
196
|
+
}
|
|
215
197
|
|
|
216
|
-
/* The three toggles read their own component cap (`--_fill-cap`, below) as the
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
:is(.checkbox, .radio, .switch) {
|
|
226
|
-
|
|
227
|
-
}
|
|
198
|
+
/* The three toggles read their own component cap (`--_fill-cap`, below) as the
|
|
199
|
+
ceiling regardless of intent, checked or not -- not just at `:checked`, the
|
|
200
|
+
way the arm above still works for a hypothetical checked text-control. A
|
|
201
|
+
neutral fill stops at 20% everywhere else because it is the page's own ink,
|
|
202
|
+
and a full one reads as a slab on a badge or a card. That reasoning does not
|
|
203
|
+
hold for a toggle at rest: a neutral checkbox is the common case, most
|
|
204
|
+
controls carry no intent class at all, and it has to look like a present,
|
|
205
|
+
interactive box rather than a washed-out one just because nothing colored
|
|
206
|
+
it. Declared where the intents are, for the reason given above. */
|
|
207
|
+
:is(.checkbox, .radio, .switch) {
|
|
208
|
+
--intent-fill-max: 100%;
|
|
209
|
+
}
|
|
228
210
|
|
|
229
|
-
/* The element's own `.edgeless` lowers the floor `text-control` raises -- a
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
:is(.text-control, .ipt, .textarea, .select, .switch, .input-group).edgeless {
|
|
236
|
-
|
|
237
|
-
}
|
|
211
|
+
/* The element's own `.edgeless` lowers the floor `text-control` raises -- a
|
|
212
|
+
class on the element is a consumer's request, a class on a container is a
|
|
213
|
+
cascade. `.checkbox` and `.radio` are absent: an unchecked one is a
|
|
214
|
+
transparent square/circle with nothing but its line, so the floor is absolute
|
|
215
|
+
and `.edgeless` on them is published unsupported. `.input-group` is on it
|
|
216
|
+
because the group is the field boundary now. */
|
|
217
|
+
:is(.text-control, .ipt, .textarea, .select, .switch, .input-group).edgeless {
|
|
218
|
+
--_edge-floor: 0%;
|
|
219
|
+
}
|
|
238
220
|
|
|
239
|
-
/* `.switch`'s one exception to the opt-out above: `.ghost` already asks for
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
.switch.ghost.edgeless {
|
|
251
|
-
|
|
252
|
-
}
|
|
221
|
+
/* `.switch`'s one exception to the opt-out above: `.ghost` already asks for
|
|
222
|
+
zero fill, so a `.switch.ghost.edgeless` track has neither a line nor a
|
|
223
|
+
plate and disappears entirely -- nothing marks its bounds but the floating
|
|
224
|
+
knob. Every other fill on this component still has something to paint
|
|
225
|
+
(`.soft`/`.solid`/default all rest above 0%), so only this one pairing
|
|
226
|
+
needs the floor held up. `.switch.ghost.edgeless` renders identically to
|
|
227
|
+
`.switch.ghost.edged` now, published unsupported as a distinct look --
|
|
228
|
+
the same "produces a row indistinguishable from its neighbour" reason
|
|
229
|
+
`.solid.edgeless`/`.solid.edged` already collapse to one row for. Higher
|
|
230
|
+
specificity than the rule above (three classes, not an `:is()` of one)
|
|
231
|
+
wins regardless of source order. */
|
|
232
|
+
.switch.ghost.edgeless {
|
|
233
|
+
--_edge-floor: 100%;
|
|
234
|
+
}
|
|
253
235
|
|
|
254
|
-
/* An invalid control is destructive whatever else it was asked to be.
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
:is(
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
)[aria-invalid="true"],
|
|
274
|
-
.input-group:has(> :is(.ipt, .textarea, .select, .text-control, input, textarea, select)[aria-invalid="true"]) {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
236
|
+
/* An invalid control is destructive whatever else it was asked to be. It sits
|
|
237
|
+
in `utilities`, above every intent class and both intent resets in
|
|
238
|
+
`components`, and `:is()` gives it 0-2-0 so it also outranks the control's
|
|
239
|
+
own utility; a consumer's utility setting an intent slot on an invalid field
|
|
240
|
+
loses to it too, because state wins over every choice (model.md#precedence). `--intent-hover` is redirected with the others so the line does not go
|
|
241
|
+
grey under the pointer. The `:has()` arm bridges an inner control's state to
|
|
242
|
+
an `.input-group`. */
|
|
243
|
+
:is(
|
|
244
|
+
.text-control,
|
|
245
|
+
.ipt,
|
|
246
|
+
.textarea,
|
|
247
|
+
.select,
|
|
248
|
+
.checkbox,
|
|
249
|
+
.radio,
|
|
250
|
+
.switch,
|
|
251
|
+
.input-group,
|
|
252
|
+
input,
|
|
253
|
+
textarea,
|
|
254
|
+
select
|
|
255
|
+
)[aria-invalid="true"],
|
|
256
|
+
.input-group:has(> :is(.ipt, .textarea, .select, .text-control, input, textarea, select)[aria-invalid="true"]) {
|
|
257
|
+
--intent-color: var(--color-destructive);
|
|
258
|
+
--intent-hover: var(--color-destructive-hover);
|
|
259
|
+
--intent-border: var(--color-destructive);
|
|
260
|
+
}
|
|
278
261
|
}
|
|
279
262
|
|
|
280
263
|
/* A ghost, edged, unelevated text control. */
|
|
@@ -316,7 +299,7 @@
|
|
|
316
299
|
letter-spacing: 0.05em;
|
|
317
300
|
line-height: 100%;
|
|
318
301
|
|
|
319
|
-
&.
|
|
302
|
+
&.destructive {
|
|
320
303
|
color: var(--intent-color);
|
|
321
304
|
}
|
|
322
305
|
}
|
|
@@ -363,6 +346,17 @@
|
|
|
363
346
|
calc(100% - 0.6875rem) center;
|
|
364
347
|
background-repeat: no-repeat;
|
|
365
348
|
background-size: 0.3125rem 0.3125rem;
|
|
349
|
+
|
|
350
|
+
/* The open list is painted by the browser, over the select's own background
|
|
351
|
+
when it has one. A ghost select has none, so Chromium falls back to its
|
|
352
|
+
default popup -- white on Windows -- while the options inherit the select's
|
|
353
|
+
text, near-white in the dark theme: white on white. The options take the
|
|
354
|
+
page's ground and text instead, which follow the theme. */
|
|
355
|
+
& option,
|
|
356
|
+
& optgroup {
|
|
357
|
+
background-color: var(--color-background);
|
|
358
|
+
color: var(--color-text);
|
|
359
|
+
}
|
|
366
360
|
}
|
|
367
361
|
|
|
368
362
|
/* A wrapper that owns the field box so a control can carry adornments beside it
|
|
@@ -485,10 +479,12 @@
|
|
|
485
479
|
--_d-border: 100%;
|
|
486
480
|
--_d-elevation: 0;
|
|
487
481
|
|
|
488
|
-
/*
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
482
|
+
/* A chip keeps a small default even under a large control corner, but an
|
|
483
|
+
explicit tight radius is a complete shape, including elliptical corners. */
|
|
484
|
+
border-radius: var(
|
|
485
|
+
--ui-radius-tight,
|
|
486
|
+
min(var(--ui-radius, var(--ui-corner, var(--radius-small))), var(--radius-small))
|
|
487
|
+
);
|
|
492
488
|
|
|
493
489
|
/* Chip geometry: the tight silhouette, and an inset edge no thicker than the
|
|
494
490
|
line the aesthetic draws elsewhere. A 16px box cannot spend four pixels a
|
|
@@ -504,7 +500,7 @@
|
|
|
504
500
|
rather than as an `outline`, so a clipping aesthetic clips it with
|
|
505
501
|
everything else and its own inset focus layer still paints over the top. */
|
|
506
502
|
&:focus-visible {
|
|
507
|
-
--_shadow: var(--_shadow-parts), 0 0 0 var(--focus-ring-width) var(--focus-ring);
|
|
503
|
+
--_shadow: var(--_shadow-parts) var(--_halo-layer,), 0 0 0 var(--focus-ring-width) var(--focus-ring);
|
|
508
504
|
}
|
|
509
505
|
|
|
510
506
|
&::after {
|
|
@@ -529,7 +525,9 @@
|
|
|
529
525
|
/* The same control with a round silhouette and a dot for its mark, and the
|
|
530
526
|
same fill cap raise given on `checkbox`. */
|
|
531
527
|
@utility radio {
|
|
532
|
-
@apply text-control inline-grid size-4 cursor-pointer place-content-center
|
|
528
|
+
@apply text-control inline-grid size-4 cursor-pointer place-content-center;
|
|
529
|
+
|
|
530
|
+
border-radius: var(--ui-radius-pill, calc(infinity * 1px));
|
|
533
531
|
|
|
534
532
|
--_d-fill: 12%;
|
|
535
533
|
--_d-fg-on-fill: 0%;
|
|
@@ -548,12 +546,16 @@
|
|
|
548
546
|
|
|
549
547
|
/* The ring, for the reason given on `checkbox`. */
|
|
550
548
|
&:focus-visible {
|
|
551
|
-
--_shadow: var(--_shadow-parts), 0 0 0 var(--focus-ring-width) var(--focus-ring);
|
|
549
|
+
--_shadow: var(--_shadow-parts) var(--_halo-layer,), 0 0 0 var(--focus-ring-width) var(--focus-ring);
|
|
552
550
|
}
|
|
553
551
|
|
|
554
552
|
&::after {
|
|
555
553
|
@apply block size-2 scale-0 rounded-full opacity-0 transition-[transform,opacity] duration-(--motion-duration-normal) ease-(--motion-ease);
|
|
556
554
|
|
|
555
|
+
/* The dot is the ring's shape at a smaller size: a diamond inside a diamond
|
|
556
|
+
under a bevelling aesthetic, a circle inside a circle otherwise. */
|
|
557
|
+
corner-shape: var(--ui-corner-shape, round);
|
|
558
|
+
|
|
557
559
|
background-color: currentcolor;
|
|
558
560
|
content: "";
|
|
559
561
|
}
|
|
@@ -614,8 +616,10 @@
|
|
|
614
616
|
|
|
615
617
|
height: var(--switch-height);
|
|
616
618
|
width: var(--switch-width);
|
|
617
|
-
/* Pill by default; a squaring aesthetic reaches the track and the knob
|
|
618
|
-
|
|
619
|
+
/* Pill by default; a squaring aesthetic reaches the track and the knob
|
|
620
|
+
together, and an aesthetic with a pill corner of its own reaches both
|
|
621
|
+
through that first. */
|
|
622
|
+
border-radius: var(--ui-radius-pill, var(--ui-radius, var(--ui-corner, calc(infinity * 1px))));
|
|
619
623
|
|
|
620
624
|
/* Chip geometry, for the reason given on `checkbox`: a 20px track has no more
|
|
621
625
|
room for a thick inset edge than a 16px box does. */
|
|
@@ -625,7 +629,7 @@
|
|
|
625
629
|
|
|
626
630
|
/* The ring, for the reason given on `checkbox`. */
|
|
627
631
|
&:focus-visible {
|
|
628
|
-
--_shadow: var(--_shadow-parts), 0 0 0 var(--focus-ring-width) var(--focus-ring);
|
|
632
|
+
--_shadow: var(--_shadow-parts) var(--_halo-layer,), 0 0 0 var(--focus-ring-width) var(--focus-ring);
|
|
629
633
|
}
|
|
630
634
|
|
|
631
635
|
&::after {
|
|
@@ -636,7 +640,10 @@
|
|
|
636
640
|
tone as the label beside it. Checked, it takes the mark colour like the
|
|
637
641
|
other two toggles. */
|
|
638
642
|
background-color: var(--color-text-secondary);
|
|
639
|
-
border-radius: var(--ui-radius, calc(infinity * 1px));
|
|
643
|
+
border-radius: var(--ui-radius-pill, var(--ui-radius, var(--ui-corner, calc(infinity * 1px))));
|
|
644
|
+
/* A pseudo-element is not a box: the knob takes the track's corner shape
|
|
645
|
+
itself, so the two stay the same shape. */
|
|
646
|
+
corner-shape: var(--ui-corner-shape, round);
|
|
640
647
|
content: "";
|
|
641
648
|
height: var(--switch-knob);
|
|
642
649
|
width: var(--switch-knob);
|
|
@@ -1,8 +1,20 @@
|
|
|
1
|
+
/* The layer map, stated before any import uses a layer -- see `theme.css`.
|
|
2
|
+
Tailwind's utilities are imported into `utilities` the way
|
|
3
|
+
`tailwindcss/index.css` does it, so every component lands there on this
|
|
4
|
+
entrypoint and on `/native` too, exactly where `.` already puts them. Without
|
|
5
|
+
`layer(utilities)` the CLI emitted them unlayered here, and they beat every
|
|
6
|
+
consumer rule on these two entries alone. */
|
|
7
|
+
@layer theme, base, components, utilities;
|
|
8
|
+
|
|
9
|
+
/* The theme before `@reference "tailwindcss"`, as `native.css` has it:
|
|
10
|
+
Tailwind writes the theme's variables at the first `@theme` it meets, and
|
|
11
|
+
after the reference that is Tailwind's own reference-only one. See
|
|
12
|
+
docs/internal/model.md (Which files import the theme). */
|
|
13
|
+
@import "../theme.css";
|
|
1
14
|
@reference "tailwindcss";
|
|
2
|
-
@import "tailwindcss/utilities.css";
|
|
15
|
+
@import "tailwindcss/utilities.css" layer(utilities);
|
|
3
16
|
/* Everything that is not `src/` -- see the note in `index.css`. Keeps a class
|
|
4
17
|
name in a docs code fence from shipping as a real rule in `dist/components.css`. */
|
|
5
|
-
@import "../theme.css";
|
|
6
18
|
@import "../typography.css";
|
|
7
19
|
@import "../box.css";
|
|
8
20
|
@import "./button.css";
|
|
@@ -25,4 +37,4 @@
|
|
|
25
37
|
makes the emission deliberate; the composition-body check in
|
|
26
38
|
`exports.test.ts` fails the build if one is dropped. `text-control` and
|
|
27
39
|
`surface` are also documented as consumer-usable on their own. */
|
|
28
|
-
@source inline("box box-hover box-active loader-mask text-control surface input-group text-display text-title-lg text-title text-title-sm text-label-lg text-label text-body btn field label hint ipt email password url tel search date datetime month week time icon left right textarea select checkbox radio switch alert alert-icon badge skeleton progress tooltip tooltip-bubble tooltip-icon card panel interactive flush
|
|
40
|
+
@source inline("box box-hover box-active loader-mask text-control surface input-group text-display text-title-lg text-title text-title-sm text-label-lg text-label text-body btn field label hint ipt email password url tel search date datetime month week time icon left right textarea select checkbox radio switch alert alert-icon badge skeleton progress tooltip tooltip-bubble tooltip-icon card panel interactive flush neutral solid soft ghost edged edgeless flat raised floating loader");
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
@reference "tailwindcss";
|
|
2
1
|
@source inline("loader dots-wave dots-fade dots-queue dots-rotate dots-grow dots-grow-alternate dot-bounce bars-wave pulse-ring");
|
|
3
2
|
|
|
4
3
|
/* The mask machinery, shared by the loader and by any component that shows a
|
|
5
4
|
spinner of its own. Artwork is a mask rather than an image so it takes its
|
|
6
5
|
colour from whatever paints behind it, which is the whole indicator contract:
|
|
7
|
-
an intent class recolours the loader and nothing else has to know.
|
|
6
|
+
an intent class recolours the loader and nothing else has to know.
|
|
7
|
+
|
|
8
|
+
Each artwork's closing `style` tag is percent-encoded. A stylesheet pasted
|
|
9
|
+
into an HTML `<style>` element ends at the first literal close tag, so one
|
|
10
|
+
left raw would silently drop every rule after it; the data URI decodes the
|
|
11
|
+
escape before the SVG is parsed, so the artwork is unchanged. The exports
|
|
12
|
+
test fails a build that ships the raw sequence. */
|
|
8
13
|
@utility loader-mask {
|
|
9
14
|
mask-image: var(--loader-art);
|
|
10
15
|
-webkit-mask-image: var(--loader-art);
|
|
@@ -16,56 +21,60 @@
|
|
|
16
21
|
-webkit-mask-position: center;
|
|
17
22
|
}
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
24
|
+
/* In `utilities`, beside the loader and art utilities it defaults for -- see
|
|
25
|
+
the seams note in `form.css`. */
|
|
26
|
+
@layer utilities {
|
|
27
|
+
.loader:not(
|
|
28
|
+
:where(
|
|
29
|
+
.dots-wave,
|
|
30
|
+
.dots-fade,
|
|
31
|
+
.dots-queue,
|
|
32
|
+
.dots-rotate,
|
|
33
|
+
.dots-grow,
|
|
34
|
+
.dots-grow-alternate,
|
|
35
|
+
.dot-bounce,
|
|
36
|
+
.bars-wave,
|
|
37
|
+
.pulse-ring
|
|
38
|
+
)
|
|
39
|
+
) {
|
|
40
|
+
--loader-art: url("data:image/svg+xml;charset=utf-8,<svg stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><style>.spinner_V8m1{transform-origin:center;animation:spinner_zKoa 2s linear infinite}.spinner_V8m1 circle{stroke-linecap:round;animation:spinner_YpZS 1.5s ease-in-out infinite}@keyframes spinner_zKoa{100%{transform:rotate(360deg)}}@keyframes spinner_YpZS{0%{stroke-dasharray:0 150;stroke-dashoffset:0}47.5%{stroke-dasharray:42 150;stroke-dashoffset:-16}95%,100%{stroke-dasharray:42 150;stroke-dashoffset:-59}}%3C/style%3E<g class='spinner_V8m1'><circle cx='12' cy='12' r='9.5' fill='none' stroke-width='3'></circle></g></svg>");
|
|
41
|
+
}
|
|
33
42
|
}
|
|
34
43
|
|
|
35
44
|
@utility dots-wave {
|
|
36
|
-
--loader-art: url("data:image/svg+xml;charset=utf-8,<svg fill='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><style>@keyframes spinner_8HQG{0%,57.14%{animation-timing-function:cubic-bezier(.33,.66,.66,1);transform:translate(0)}28.57%{animation-timing-function:cubic-bezier(.33,0,.66,.33);transform:translateY(-6px)}to{transform:translate(0)}}.spinner_qM83{animation:spinner_8HQG 1.05s infinite}
|
|
45
|
+
--loader-art: url("data:image/svg+xml;charset=utf-8,<svg fill='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><style>@keyframes spinner_8HQG{0%,57.14%{animation-timing-function:cubic-bezier(.33,.66,.66,1);transform:translate(0)}28.57%{animation-timing-function:cubic-bezier(.33,0,.66,.33);transform:translateY(-6px)}to{transform:translate(0)}}.spinner_qM83{animation:spinner_8HQG 1.05s infinite}%3C/style%3E<circle class='spinner_qM83' cx='4' cy='12' r='3'/><circle class='spinner_qM83' cx='12' cy='12' r='3' style='animation-delay:.1s'/><circle class='spinner_qM83' cx='20' cy='12' r='3' style='animation-delay:.2s'/></svg>");
|
|
37
46
|
}
|
|
38
47
|
|
|
39
48
|
@utility dots-fade {
|
|
40
|
-
--loader-art: url("data:image/svg+xml;charset=utf-8,<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><style>.dot{animation:dot-anim 1s ease-in-out infinite;fill:currentColor}.d1{animation-delay:-0.32s}.d2{animation-delay:-0.16s}@keyframes dot-anim{0%,80%,100%{transform:scale(0.6);opacity:0.2}40%{transform:scale(1);opacity:1}}
|
|
49
|
+
--loader-art: url("data:image/svg+xml;charset=utf-8,<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><style>.dot{animation:dot-anim 1s ease-in-out infinite;fill:currentColor}.d1{animation-delay:-0.32s}.d2{animation-delay:-0.16s}@keyframes dot-anim{0%,80%,100%{transform:scale(0.6);opacity:0.2}40%{transform:scale(1);opacity:1}}%3C/style%3E<circle class='dot d1' cx='4' cy='12' r='3' transform-origin='4px 12px'></circle><circle class='dot d2' cx='12' cy='12' r='3' transform-origin='12px 12px'></circle><circle class='dot d3' cx='20' cy='12' r='3' transform-origin='20px 12px'></circle></svg>");
|
|
41
50
|
}
|
|
42
51
|
|
|
43
52
|
@utility dots-queue {
|
|
44
|
-
--loader-art: url("data:image/svg+xml;charset=utf-8,<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><style>@keyframes spinner_qtyZ{0%{r:0}25%{r:3px;cx:4px}50%{r:3px;cx:12px}75%{r:3px;cx:20px}to{r:0;cx:20px}}.spinner_nOfF{animation:spinner_qtyZ 2s cubic-bezier(.36,.6,.31,1) infinite}
|
|
53
|
+
--loader-art: url("data:image/svg+xml;charset=utf-8,<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><style>@keyframes spinner_qtyZ{0%{r:0}25%{r:3px;cx:4px}50%{r:3px;cx:12px}75%{r:3px;cx:20px}to{r:0;cx:20px}}.spinner_nOfF{animation:spinner_qtyZ 2s cubic-bezier(.36,.6,.31,1) infinite}%3C/style%3E<circle class='spinner_nOfF' cx='4' cy='12' r='3'/><circle class='spinner_nOfF' cx='4' cy='12' r='3' style='animation-delay:-.5s'/><circle class='spinner_nOfF' cx='4' cy='12' r='3' style='animation-delay:-1s'/><circle class='spinner_nOfF' cx='4' cy='12' r='3' style='animation-delay:-1.5s'/></svg>");
|
|
45
54
|
}
|
|
46
55
|
|
|
47
56
|
@utility dots-rotate {
|
|
48
|
-
--loader-art: url("data:image/svg+xml;charset=utf-8,<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><style>@keyframes spinner_XVY9{50%{transform:rotate(180deg)}to{transform:rotate(360deg)}}
|
|
57
|
+
--loader-art: url("data:image/svg+xml;charset=utf-8,<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><style>@keyframes spinner_XVY9{50%{transform:rotate(180deg)}to{transform:rotate(360deg)}}%3C/style%3E<circle cx='12' cy='12' r='3'/><g style='transform-origin:center;animation:spinner_XVY9 1s cubic-bezier(.36,.6,.31,1) infinite'><circle cx='4' cy='12' r='3'/><circle cx='20' cy='12' r='3'/></g></svg>");
|
|
49
58
|
}
|
|
50
59
|
|
|
51
60
|
@utility dots-grow {
|
|
52
|
-
--loader-art: url("data:image/svg+xml;charset=utf-8,<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><style>@keyframes spinner_xe7Q{93.75%,to{r:3px}46.875%{r:.2px}}.spinner_b2T7{animation:spinner_xe7Q .8s linear infinite}
|
|
61
|
+
--loader-art: url("data:image/svg+xml;charset=utf-8,<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><style>@keyframes spinner_xe7Q{93.75%,to{r:3px}46.875%{r:.2px}}.spinner_b2T7{animation:spinner_xe7Q .8s linear infinite}%3C/style%3E<circle class='spinner_b2T7' cx='4' cy='12' r='3'/><circle class='spinner_b2T7' cx='12' cy='12' r='3' style='animation-delay:-.65s'/><circle class='spinner_b2T7' cx='20' cy='12' r='3' style='animation-delay:-.5s'/></svg>");
|
|
53
62
|
}
|
|
54
63
|
|
|
55
64
|
@utility dots-grow-alternate {
|
|
56
|
-
--loader-art: url("data:image/svg+xml;charset=utf-8,<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><style>@keyframes spinner_qhi1{0%,to{r:1.5px}50%{r:3px}}.spinner_I8Q1{animation:spinner_qhi1 .75s linear infinite}
|
|
65
|
+
--loader-art: url("data:image/svg+xml;charset=utf-8,<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><style>@keyframes spinner_qhi1{0%,to{r:1.5px}50%{r:3px}}.spinner_I8Q1{animation:spinner_qhi1 .75s linear infinite}%3C/style%3E<circle class='spinner_I8Q1' cx='4' cy='12' r='1.5'/><circle class='spinner_I8Q1' cx='12' cy='12' r='3' style='animation-delay:-.375s'/><circle class='spinner_I8Q1' cx='20' cy='12' r='1.5'/></svg>");
|
|
57
66
|
}
|
|
58
67
|
|
|
59
68
|
@utility dot-bounce {
|
|
60
|
-
--loader-art: url("data:image/svg+xml;charset=utf-8,<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><style>@keyframes spinner_YeBj{0%{animation-timing-function:cubic-bezier(.33,0,.66,.33);cy:5px}46.875%{cy:20px;rx:4px;ry:4px}50%{animation-timing-function:cubic-bezier(.33,.66,.66,1);cy:20.5px;rx:4.8px;ry:3px}53.125%{rx:4px;ry:4px}to{cy:5px}}
|
|
69
|
+
--loader-art: url("data:image/svg+xml;charset=utf-8,<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><style>@keyframes spinner_YeBj{0%{animation-timing-function:cubic-bezier(.33,0,.66,.33);cy:5px}46.875%{cy:20px;rx:4px;ry:4px}50%{animation-timing-function:cubic-bezier(.33,.66,.66,1);cy:20.5px;rx:4.8px;ry:3px}53.125%{rx:4px;ry:4px}to{cy:5px}}%3C/style%3E<circle cx='12' cy='5' style='animation:spinner_YeBj .8s infinite' r='4'/></svg>");
|
|
61
70
|
}
|
|
62
71
|
|
|
63
72
|
@utility bars-wave {
|
|
64
|
-
--loader-art: url("data:image/svg+xml;charset=utf-8,<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><style>.bar{animation:bar-anim 1s ease-in-out infinite;fill:currentColor}.b1{animation-delay:-0.4s}.b2{animation-delay:-0.2s}@keyframes bar-anim{0%,100%{transform:scaleY(0.3)}50%{transform:scaleY(1)}}
|
|
73
|
+
--loader-art: url("data:image/svg+xml;charset=utf-8,<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><style>.bar{animation:bar-anim 1s ease-in-out infinite;fill:currentColor}.b1{animation-delay:-0.4s}.b2{animation-delay:-0.2s}@keyframes bar-anim{0%,100%{transform:scaleY(0.3)}50%{transform:scaleY(1)}}%3C/style%3E<rect class='bar b1' x='3' y='2' width='4' height='20' rx='2' transform-origin='5px 12px'></rect><rect class='bar b2' x='10' y='2' width='4' height='20' rx='2' transform-origin='12px 12px'></rect><rect class='bar b3' x='17' y='2' width='4' height='20' rx='2' transform-origin='19px 12px'></rect></svg>");
|
|
65
74
|
}
|
|
66
75
|
|
|
67
76
|
@utility pulse-ring {
|
|
68
|
-
--loader-art: url("data:image/svg+xml;charset=utf-8,<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><style>.pulse{animation:pulse-anim 1.2s cubic-bezier(0, 0.2, 0.8, 1) infinite;stroke:currentColor;transform-origin:center}.p2{animation-delay:-0.6s}@keyframes pulse-anim{0%{transform:scale(0);opacity:1}100%{transform:scale(1);opacity:0}}
|
|
77
|
+
--loader-art: url("data:image/svg+xml;charset=utf-8,<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><style>.pulse{animation:pulse-anim 1.2s cubic-bezier(0, 0.2, 0.8, 1) infinite;stroke:currentColor;transform-origin:center}.p2{animation-delay:-0.6s}@keyframes pulse-anim{0%{transform:scale(0);opacity:1}100%{transform:scale(1);opacity:0}}%3C/style%3E<circle class='pulse p1' cx='12' cy='12' r='10' fill='none' stroke-width='3'></circle><circle class='pulse p2' cx='12' cy='12' r='10' fill='none' stroke-width='3'></circle></svg>");
|
|
69
78
|
}
|
|
70
79
|
|
|
71
80
|
/* A loader takes a colour and its own geometry, and nothing else. No fill, no
|