@codenhub/styles 0.8.0 → 0.9.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.
Files changed (76) hide show
  1. package/README.es.md +9 -9
  2. package/README.md +8 -8
  3. package/README.pt-BR.md +9 -9
  4. package/dist/aesthetics/chunky-tile.css +1 -1
  5. package/dist/aesthetics/cyber.css +1 -1
  6. package/dist/aesthetics/glass.css +1 -1
  7. package/dist/aesthetics/index.css +1 -1
  8. package/dist/aesthetics/neobrutalism.css +1 -1
  9. package/dist/aesthetics/pixel.css +1 -1
  10. package/dist/aesthetics/sketch.css +1 -1
  11. package/dist/cli.js +231 -62
  12. package/dist/components.css +49 -1
  13. package/dist/index.css +49 -1
  14. package/dist/native.css +49 -1
  15. package/dist/purge/classes.json +1 -0
  16. package/dist/purge/index.css +50 -0
  17. package/dist/theme.css +1 -1
  18. package/docs/changelog/0.8.0.md +1 -1
  19. package/docs/changelog/0.9.0.md +40 -0
  20. package/docs/changelog/index.md +1 -0
  21. package/docs/es/index.md +2 -2
  22. package/docs/es/integrating/tailwind.md +2 -1
  23. package/docs/es/integrating/without-a-bundler.md +60 -31
  24. package/docs/es/setup.md +4 -3
  25. package/docs/es/usage/aesthetics.md +2 -2
  26. package/docs/es/usage/content-and-layout.md +3 -1
  27. package/docs/es/usage/customizing.md +30 -16
  28. package/docs/es/usage/forms.md +2 -2
  29. package/docs/es/usage/index.md +2 -2
  30. package/docs/es/usage/solo.md +2 -2
  31. package/docs/es/usage/theming.md +2 -2
  32. package/docs/es/usage/tooltips.md +2 -2
  33. package/docs/index.md +1 -1
  34. package/docs/integrating/tailwind.md +1 -0
  35. package/docs/integrating/without-a-bundler.md +59 -30
  36. package/docs/pt-BR/index.md +2 -2
  37. package/docs/pt-BR/integrating/tailwind.md +2 -1
  38. package/docs/pt-BR/integrating/without-a-bundler.md +60 -31
  39. package/docs/pt-BR/setup.md +4 -3
  40. package/docs/pt-BR/usage/aesthetics.md +2 -2
  41. package/docs/pt-BR/usage/content-and-layout.md +3 -1
  42. package/docs/pt-BR/usage/customizing.md +30 -16
  43. package/docs/pt-BR/usage/forms.md +2 -2
  44. package/docs/pt-BR/usage/index.md +2 -2
  45. package/docs/pt-BR/usage/solo.md +2 -2
  46. package/docs/pt-BR/usage/theming.md +2 -2
  47. package/docs/pt-BR/usage/tooltips.md +2 -2
  48. package/docs/setup.md +3 -2
  49. package/docs/usage/aesthetics.md +1 -1
  50. package/docs/usage/content-and-layout.md +2 -0
  51. package/docs/usage/customizing.md +29 -15
  52. package/docs/usage/forms.md +1 -1
  53. package/docs/usage/index.md +1 -1
  54. package/docs/usage/solo.md +1 -1
  55. package/docs/usage/theming.md +1 -1
  56. package/docs/usage/tooltips.md +1 -1
  57. package/llms-full.txt +145 -61
  58. package/llms.txt +3 -2
  59. package/package.json +12 -10
  60. package/tw/aesthetics/chunky-tile.css +4 -1
  61. package/tw/aesthetics/cyber.css +4 -1
  62. package/tw/aesthetics/glass.css +5 -0
  63. package/tw/aesthetics/neobrutalism.css +4 -1
  64. package/tw/aesthetics/pixel.css +3 -0
  65. package/tw/aesthetics/sketch.css +18 -0
  66. package/tw/box.css +21 -15
  67. package/tw/components/button.rules.css +1 -1
  68. package/tw/components/feedback.rules.css +22 -3
  69. package/tw/components/form.rules.css +65 -35
  70. package/tw/components/surface.rules.css +8 -5
  71. package/tw/components/tooltip.rules.css +39 -34
  72. package/tw/elevation.css +8 -8
  73. package/tw/native.css +4 -1
  74. package/tw/theme.css +1 -1
  75. package/tw/utilities/content.css +74 -54
  76. package/tw/utilities/layout.css +7 -5
@@ -194,6 +194,9 @@
194
194
  --_tile-bar: color-mix(in oklab, var(--_solo-ink) 72%, rgb(0 0 0));
195
195
  --_solo-depth: calc(var(--_tile-lift) * var(--ui-elevation, 1));
196
196
 
197
+ /* Printed as drawn; see `.glass-solo`. */
198
+ print-color-adjust: exact;
199
+
197
200
  border: 2px solid var(--_solo-edge);
198
201
  border-radius: var(--tile-radius, 0.75rem);
199
202
  box-shadow:
@@ -282,7 +285,7 @@
282
285
  input[type="button"],
283
286
  input[type="submit"],
284
287
  input[type="reset"]
285
- ):active:not(:disabled, [aria-disabled="true"]) {
288
+ ):active:not(:disabled, .disabled, [aria-disabled="true"], [data-disabled]) {
286
289
  box-shadow:
287
290
  0 0 0 0 var(--_tile-bar),
288
291
  inset 0 0 0 100vmax var(--_solo-fill);
@@ -179,6 +179,9 @@
179
179
  );
180
180
  --_solo-edge: color-mix(in oklab, var(--_solo-ink) var(--ui-border, 100%), var(--_solo-fill));
181
181
 
182
+ /* Printed as drawn; see `.glass-solo`. */
183
+ print-color-adjust: exact;
184
+
182
185
  border: 1px solid var(--_solo-edge);
183
186
  /* The surface shape: the element this exists for is usually a pane. An action
184
187
  takes the control shape below. */
@@ -275,6 +278,6 @@
275
278
  input[type="button"],
276
279
  input[type="submit"],
277
280
  input[type="reset"]
278
- ):active:not(:disabled, [aria-disabled="true"]) {
281
+ ):active:not(:disabled, .disabled, [aria-disabled="true"], [data-disabled]) {
279
282
  scale: 0.97;
280
283
  }
@@ -445,6 +445,11 @@
445
445
  does, rather than to the fill. */
446
446
  --_solo-edge: color-mix(in oklab, var(--_solo-ink) var(--ui-border, 100%), transparent);
447
447
 
448
+ /* Printed as drawn, as `box` is: a browser leaves backgrounds off paper
449
+ and greys a contrast label, so a solid element printed its label on
450
+ no plate. Inherited, so what it holds keeps its paint too. */
451
+ print-color-adjust: exact;
452
+
448
453
  background-color: color-mix(in oklab, var(--_solo-color) var(--_solo-capped), var(--_solo-ground));
449
454
  border: 1px solid var(--_solo-edge);
450
455
  /* The surface corner: the element this exists for is a pane. */
@@ -187,6 +187,9 @@
187
187
  and `.floating` doubles it, and the press travels the same distance. */
188
188
  --_solo-depth: calc(var(--_neo-offset) * var(--ui-elevation, 1));
189
189
 
190
+ /* Printed as drawn; see `.glass-solo`. */
191
+ print-color-adjust: exact;
192
+
190
193
  border: 2px solid var(--_solo-edge);
191
194
  border-radius: 0;
192
195
  box-shadow:
@@ -260,7 +263,7 @@
260
263
  input[type="button"],
261
264
  input[type="submit"],
262
265
  input[type="reset"]
263
- ):active:not(:disabled, [aria-disabled="true"]) {
266
+ ):active:not(:disabled, .disabled, [aria-disabled="true"], [data-disabled]) {
264
267
  box-shadow:
265
268
  0 0 0 0 var(--_solo-ink),
266
269
  inset 0 0 0 100vmax var(--_solo-fill);
@@ -182,6 +182,9 @@
182
182
 
183
183
  /* The clip removes a border at the straight sides, so the ring draws the
184
184
  edge and the element's own border goes. */
185
+ /* Printed as drawn; see `.glass-solo`. */
186
+ print-color-adjust: exact;
187
+
185
188
  border: 0;
186
189
  border-radius: 0;
187
190
  box-shadow:
@@ -159,6 +159,14 @@
159
159
  with the token, so a position never reads a value inherited from an
160
160
  ancestor's. */
161
161
  &:is(.btn, .ipt, .textarea, .select, .input-group, .pre, .tooltip-bubble) {
162
+ /* The first position draws the region's own outline, declared here too
163
+ so a knob set between the region and the element reaches it. */
164
+ &:nth-child(4n + 1) {
165
+ --_sketch-p: var(--_sketch-pointed) var(--sketch-radius, 255px 15px 225px 15px / 15px 225px 15px 255px);
166
+ --_sketch-r: var(--_sketch-rounded) var(--sketch-radius-rounded, 26px 10px 24px 11px / 12px 23px 11px 20px);
167
+ --ui-radius: var(--_sketch-pass-radius, var(--_sketch-r, var(--_sketch-p)));
168
+ }
169
+
162
170
  &:nth-child(4n + 2) {
163
171
  --_sketch-p: var(--_sketch-pointed) var(--sketch-radius, 18px 240px 15px 255px / 240px 15px 255px 18px);
164
172
  --_sketch-r: var(--_sketch-rounded) var(--sketch-radius-rounded, 11px 25px 12px 24px / 23px 11px 22px 12px);
@@ -205,6 +213,13 @@
205
213
  }
206
214
 
207
215
  &:is(.card, .panel, .alert, .data-table) {
216
+ &:nth-child(4n + 1) {
217
+ --_sketch-p: var(--_sketch-pointed) var(--sketch-radius-surface, 80px 4px 75px 5px / 3px 70px 4px 80px);
218
+ --_sketch-r: var(--_sketch-rounded)
219
+ var(--sketch-radius-surface-rounded, 38px 16px 36px 17px / 16px 34px 17px 32px);
220
+ --ui-radius-surface: var(--_sketch-pass-radius-surface, var(--_sketch-r, var(--_sketch-p)));
221
+ }
222
+
208
223
  &:nth-child(4n + 2) {
209
224
  --_sketch-p: var(--_sketch-pointed) var(--sketch-radius-surface, 4px 75px 5px 80px / 75px 3px 80px 4px);
210
225
  --_sketch-r: var(--_sketch-rounded)
@@ -335,6 +350,9 @@
335
350
  --_solo-edge: color-mix(in oklab, var(--_solo-ink) var(--ui-border, 100%), var(--_solo-fill));
336
351
  --_solo-depth: calc(2px * var(--ui-elevation, 1));
337
352
 
353
+ /* Printed as drawn; see `.glass-solo`. */
354
+ print-color-adjust: exact;
355
+
338
356
  border: 1px solid var(--_solo-edge);
339
357
  /* The surface shape: the element this exists for is usually a pane. An action
340
358
  takes the control shape below. */
package/tw/box.css CHANGED
@@ -203,8 +203,8 @@
203
203
  with no aesthetic. The order lets a raised card under `.neobrutalism` keep
204
204
  the slab. Nothing declares the last slot until something asks for depth, so
205
205
  a plain unmodified page has no shadow anywhere. */
206
- --_sy: calc(var(--ui-shadow-y, var(--_d-shadow-y, var(--elevation-y, 0px))) * var(--_depth));
207
- --_sb: calc(var(--ui-shadow-blur, var(--_d-shadow-blur, var(--elevation-blur, 0px))) * var(--_depth));
206
+ --_sy: calc(var(--ui-shadow-y, var(--_d-shadow-y, var(--_elevation-y, 0px))) * var(--_depth));
207
+ --_sb: calc(var(--ui-shadow-blur, var(--_d-shadow-blur, var(--_elevation-blur, 0px))) * var(--_depth));
208
208
  /* Spread is deliberately not scaled. An aesthetic that draws its edge as an
209
209
  inset ring spends spread on it, and scaling that by elevation would erase
210
210
  the edge of every component the registry rests at zero. Elevation is depth:
@@ -363,20 +363,26 @@
363
363
  full and darkens because the colour changed.
364
364
 
365
365
  Separate from `box` because tinting on hover is right for something you press
366
- and wrong for something you type into, so a component opts in. */
366
+ and wrong for something you type into, so a component opts in.
367
+
368
+ Only where the primary pointer can hover, as Tailwind's `hover:` variant:
369
+ a touch screen leaves a tapped element in `:hover` until something else is
370
+ tapped, so without the query a button kept its hover tint after the tap. */
367
371
  @utility box-hover {
368
- &:not(:disabled, .disabled, [aria-disabled="true"], [data-disabled]):hover {
369
- --_bg: color-mix(in oklab, var(--intent-hover) var(--_hover-fill), var(--_ground));
370
- /* P3 again, against the hover tone: the line still fades as the fill fills
371
- in, but toward the hover ink. Without this the edge keeps the resting ink
372
- -- a ring of the colour the box just left. */
373
- --_line: color-mix(in oklab, transparent var(--ui-fill, var(--_d-fill)), var(--intent-hover));
374
- --_sx: calc(var(--ui-hover-shadow-x, var(--ui-shadow-x, var(--_d-shadow-x, 0px))) * var(--_depth));
375
- --_sy: calc(
376
- var(--ui-hover-shadow-y, var(--ui-shadow-y, var(--_d-shadow-y, var(--elevation-y, 0px)))) * var(--_depth)
377
- );
372
+ @media (hover: hover) {
373
+ &:not(:disabled, .disabled, [aria-disabled="true"], [data-disabled]):hover {
374
+ --_bg: color-mix(in oklab, var(--intent-hover) var(--_hover-fill), var(--_ground));
375
+ /* P3 again, against the hover tone: the line still fades as the fill fills
376
+ in, but toward the hover ink. Without this the edge keeps the resting ink
377
+ -- a ring of the colour the box just left. */
378
+ --_line: color-mix(in oklab, transparent var(--ui-fill, var(--_d-fill)), var(--intent-hover));
379
+ --_sx: calc(var(--ui-hover-shadow-x, var(--ui-shadow-x, var(--_d-shadow-x, 0px))) * var(--_depth));
380
+ --_sy: calc(
381
+ var(--ui-hover-shadow-y, var(--ui-shadow-y, var(--_d-shadow-y, var(--_elevation-y, 0px)))) * var(--_depth)
382
+ );
378
383
 
379
- transform: var(--ui-hover-transform, none);
384
+ transform: var(--ui-hover-transform, none);
385
+ }
380
386
  }
381
387
  }
382
388
 
@@ -407,7 +413,7 @@
407
413
  /* The same three slots as the resting offset, `.raised`'s plain step last,
408
414
  so with no aesthetic a pressed button keeps the depth it rests on. */
409
415
  --_sy: calc(
410
- var(--ui-active-shadow-y, var(--ui-shadow-y, var(--_d-shadow-y, var(--elevation-y, 0px)))) * var(--_depth)
416
+ var(--ui-active-shadow-y, var(--ui-shadow-y, var(--_d-shadow-y, var(--_elevation-y, 0px)))) * var(--_depth)
411
417
  );
412
418
 
413
419
  --_press: calc(var(--ui-active-translate-x, 0px) * var(--_depth))
@@ -102,7 +102,7 @@
102
102
  --_lg-py: var(--_size-lg) 0.625rem;
103
103
  --_sm-text: var(--_size-sm) var(--text-sm);
104
104
  --_lg-text: var(--_size-lg) 1.0625rem;
105
- --_sm-leading: var(--_size-sm) var(--tw-leading, var(--text-sm--line-height));
105
+ --_sm-leading: var(--_size-sm) var(--text-sm--line-height);
106
106
  --_sm-scale: var(--_size-sm) 0.75;
107
107
  --_lg-scale: var(--_size-lg) 1.25;
108
108
  --_scale-size: var(--_sm-scale, var(--_lg-scale));
@@ -137,13 +137,15 @@
137
137
  }
138
138
  }
139
139
 
140
- --skeleton-base: color-mix(in oklab, var(--intent-color) 6%, var(--intent-subtle));
141
- --skeleton-sheen: color-mix(in oklab, var(--intent-color) 10%, var(--color-foreground));
140
+ /* Public, read with their defaults, so a value set on any ancestor reaches
141
+ the placeholder. */
142
+ --_skeleton-base: var(--skeleton-base, color-mix(in oklab, var(--intent-color) 6%, var(--intent-subtle)));
143
+ --_skeleton-sheen: var(--skeleton-sheen, color-mix(in oklab, var(--intent-color) 10%, var(--color-foreground)));
142
144
 
143
145
  @apply min-h-4 w-full;
144
146
 
145
147
  animation: anim-skeleton calc(var(--motion-duration-slow) * 4) linear infinite;
146
- background: linear-gradient(90deg, var(--skeleton-base), var(--skeleton-sheen), var(--skeleton-base));
148
+ background: linear-gradient(90deg, var(--_skeleton-base), var(--_skeleton-sheen), var(--_skeleton-base));
147
149
  background-size: 200% 100%;
148
150
  /* Not a box, so it draws the control corner and its pattern itself, the way
149
151
  `box` does at scale 1, and reads the corner shape beside it. */
@@ -306,6 +308,23 @@
306
308
  animation: none;
307
309
  }
308
310
  }
311
+
312
+ /* Forced colours, carried for an applied bar, as the toggles carry theirs.
313
+ The class also keeps the unlayered copy below, which beats every layered
314
+ rule; see there for why each part is named. */
315
+ @media (forced-colors: active) {
316
+ forced-color-adjust: none;
317
+ background-color: Canvas;
318
+ border-color: CanvasText;
319
+
320
+ &::after {
321
+ background-color: CanvasText;
322
+ }
323
+
324
+ &::before {
325
+ display: none;
326
+ }
327
+ }
309
328
  }
310
329
 
311
330
  /* Forced colours repaint every background as Canvas, the value fill on a
@@ -116,16 +116,20 @@
116
116
  /* The last exclusion is `.input-group`'s: a group whose control is disabled
117
117
  dims as a disabled field does (see `input-group`), so it does not answer
118
118
  the pointer either. The list is the group's own, so a `<select>` with a
119
- disabled `<option>` is not caught by it. */
120
- &:hover:not(
121
- :disabled,
122
- .disabled,
123
- [aria-disabled="true"],
124
- [data-disabled],
125
- :focus-visible,
126
- :has(> :is(.ipt, .textarea, .select, .text-control, input, textarea, select):disabled)
127
- ) {
128
- --_line-tone: var(--intent-hover);
119
+ disabled `<option>` is not caught by it. Under `(hover: hover)` as the
120
+ other hover responses are: a tapped checkbox, select, or group stays in
121
+ `:hover` without matching `:focus-visible`, and kept the hover line. */
122
+ @media (hover: hover) {
123
+ &:hover:not(
124
+ :disabled,
125
+ .disabled,
126
+ [aria-disabled="true"],
127
+ [data-disabled],
128
+ :focus-visible,
129
+ :has(> :is(.ipt, .textarea, .select, .text-control, input, textarea, select):disabled)
130
+ ) {
131
+ --_line-tone: var(--intent-hover);
132
+ }
129
133
  }
130
134
 
131
135
  /* The line moves to the intent on top of the `reset.css` `:focus-visible`
@@ -324,6 +328,22 @@
324
328
  --intent-fill-max: 100%;
325
329
  }
326
330
 
331
+ /* A bare toggle's "on" plate. `/native` reaches the toggles through `@apply`
332
+ in `base`, where their `:checked` and `:indeterminate` pins lose to
333
+ `.ghost` and `.soft` in `utilities`, so a checked quiet checkbox kept its
334
+ resting plate. The class route already wins on specificity in this layer;
335
+ this repeats its values for the bare element, with the neutral cap
336
+ `native.css`'s reset puts back, which the arm above lifts only when
337
+ checked. Not `radio:indeterminate`, which every radio of a group with
338
+ nothing chosen matches. */
339
+ input:is([type="checkbox"], [type="radio"]):checked,
340
+ input[type="checkbox"]:indeterminate {
341
+ --intent-fill-max: 100%;
342
+ --ui-fill: 100%;
343
+ --ui-fg-on-fill: 100%;
344
+ --_fill-cap: 100%;
345
+ }
346
+
327
347
  /* The three toggles read their own component cap (`--_fill-cap`, below) as the
328
348
  ceiling regardless of intent, checked or not -- not just at `:checked`, the
329
349
  way the arm above still works for a hypothetical checked text-control. A
@@ -389,7 +409,7 @@
389
409
  --_lg-py: var(--_size-lg) 0.625rem;
390
410
  --_sm-text: var(--_size-sm) var(--text-sm);
391
411
  --_lg-text: var(--_size-lg) 1.0625rem;
392
- --_sm-leading: var(--_size-sm) var(--tw-leading, var(--text-sm--line-height));
412
+ --_sm-leading: var(--_size-sm) var(--text-sm--line-height);
393
413
  --_sm-scale: var(--_size-sm) 0.75;
394
414
  --_lg-scale: var(--_size-lg) 1.25;
395
415
  --_scale-size: var(--_sm-scale, var(--_lg-scale));
@@ -403,9 +423,10 @@
403
423
  }
404
424
 
405
425
  @utility field {
406
- --field-gap: 0.35rem;
426
+ @apply flex w-full flex-col;
407
427
 
408
- @apply flex w-full flex-col gap-(--field-gap);
428
+ /* Public, read with its default, so a value on a form reaches every field. */
429
+ gap: var(--field-gap, 0.35rem);
409
430
  }
410
431
 
411
432
  @utility label {
@@ -465,7 +486,7 @@
465
486
  --_lg-py: var(--_size-lg) 0.625rem;
466
487
  --_sm-text: var(--_size-sm) var(--text-sm);
467
488
  --_lg-text: var(--_size-lg) 1.0625rem;
468
- --_sm-leading: var(--_size-sm) var(--tw-leading, var(--text-sm--line-height));
489
+ --_sm-leading: var(--_size-sm) var(--text-sm--line-height);
469
490
  --_sm-scale: var(--_size-sm) 0.75;
470
491
  --_lg-scale: var(--_size-lg) 1.25;
471
492
  --_scale-size: var(--_sm-scale, var(--_lg-scale));
@@ -513,7 +534,7 @@
513
534
  --_lg-py: var(--_size-lg) 0.625rem;
514
535
  --_sm-text: var(--_size-sm) var(--text-sm);
515
536
  --_lg-text: var(--_size-lg) 1.0625rem;
516
- --_sm-leading: var(--_size-sm) var(--tw-leading, var(--text-sm--line-height));
537
+ --_sm-leading: var(--_size-sm) var(--text-sm--line-height);
517
538
  --_sm-scale: var(--_size-sm) 0.75;
518
539
  --_lg-scale: var(--_size-lg) 1.25;
519
540
  --_scale-size: var(--_sm-scale, var(--_lg-scale));
@@ -553,6 +574,14 @@
553
574
  padding-inline: 0.75rem;
554
575
  background-image: none;
555
576
  }
577
+
578
+ /* Forced colours, carried for an applied drop-down: the platform's arrow, for
579
+ the reason the unlayered copy at the foot of this file gives. */
580
+ @media (forced-colors: active) {
581
+ &:not([multiple], [size]:not([size="1"])) {
582
+ appearance: auto;
583
+ }
584
+ }
556
585
  }
557
586
 
558
587
  /* A wrapper that owns the field box so a control can carry adornments beside it
@@ -573,7 +602,7 @@
573
602
  breathing room a distinct glyph needs, but a text affix meant to glue onto the
574
603
  value -- a URL host, a currency symbol, a unit -- reads the same gap as a
575
604
  stray space breaking the two apart. A consumer sets `--input-group-gap: 0` on
576
- their own `.input-group` for a flush affix; the default stays the icon-tuned
605
+ the group or an ancestor for a flush affix; the default stays the icon-tuned
577
606
  0.5rem so every existing icon composition is unchanged. */
578
607
  @utility input-group {
579
608
  @apply text-control flex w-full items-center;
@@ -597,17 +626,16 @@
597
626
  --_lg-px: var(--_size-lg) 1rem;
598
627
  --_sm-text: var(--_size-sm) var(--text-sm);
599
628
  --_lg-text: var(--_size-lg) 1.0625rem;
600
- --_sm-leading: var(--_size-sm) var(--tw-leading, var(--text-sm--line-height));
629
+ --_sm-leading: var(--_size-sm) var(--text-sm--line-height);
601
630
  --_sm-scale: var(--_size-sm) 0.75;
602
631
  --_lg-scale: var(--_size-lg) 1.25;
603
632
  --_scale-size: var(--_sm-scale, var(--_lg-scale));
604
633
 
605
- --input-group-gap: 0.5rem;
606
- --input-group-pad: var(--_sm-px, var(--_lg-px, 0.75rem));
607
-
608
634
  min-height: var(--_sm-h, var(--_lg-h, var(--control-height)));
609
- gap: var(--input-group-gap);
610
- padding-inline: var(--input-group-pad);
635
+ /* Public, read with their defaults, so a value set on any ancestor reaches
636
+ the group. */
637
+ gap: var(--input-group-gap, 0.5rem);
638
+ padding-inline: var(--input-group-padding, var(--_sm-px, var(--_lg-px, 0.75rem)));
611
639
  font-size: var(--_sm-text, var(--_lg-text, inherit));
612
640
  line-height: var(--_sm-leading, inherit);
613
641
 
@@ -1053,22 +1081,24 @@
1053
1081
  a badge or a card stays under. */
1054
1082
  --_fill-cap: 40%;
1055
1083
 
1056
- --switch-height: 1.25rem;
1057
- --switch-width: 2.5rem;
1058
- --switch-gap: 1px;
1084
+ /* The track's size is public, read with its default, so a value set on any
1085
+ ancestor reaches the switch; the knob is derived from it. */
1086
+ --_switch-height: var(--switch-height, 1.25rem);
1087
+ --_switch-width: var(--switch-width, 2.5rem);
1088
+ --_switch-gap: 1px;
1059
1089
  /* The edge an aesthetic draws as an inset ring rather than as a border
1060
1090
  (`.pixel`), present only while `--ui-shadow-edge` is declared -- the idiom
1061
1091
  `box` uses. That ring paints under the knob, so a knob sized from the
1062
1092
  border alone covered it: under pixel the border is zero, the knob met the
1063
1093
  edge, and an unchecked and a checked switch both read as two halves. */
1064
1094
  --_switch-ring: var(--ui-shadow-edge) var(--_ss);
1065
- --_switch-offset: calc(var(--_switch-ring, 0px) + var(--switch-gap));
1066
- --switch-inset: calc(var(--_width) + var(--_switch-offset));
1067
- --switch-knob: calc(var(--switch-height) - var(--switch-inset) * 2);
1068
- --switch-travel: calc(var(--switch-width) - var(--switch-inset) * 2 - var(--switch-knob));
1095
+ --_switch-offset: calc(var(--_switch-ring, 0px) + var(--_switch-gap));
1096
+ --_switch-inset: calc(var(--_width) + var(--_switch-offset));
1097
+ --_switch-knob: calc(var(--_switch-height) - var(--_switch-inset) * 2);
1098
+ --_switch-travel: calc(var(--_switch-width) - var(--_switch-inset) * 2 - var(--_switch-knob));
1069
1099
 
1070
- height: var(--switch-height);
1071
- width: var(--switch-width);
1100
+ height: var(--_switch-height);
1101
+ width: var(--_switch-width);
1072
1102
  /* Pill by default; a squaring aesthetic reaches the track and the knob
1073
1103
  together, and an aesthetic with a pill corner of its own reaches both
1074
1104
  through that first. */
@@ -1100,8 +1130,8 @@
1100
1130
  itself, so the two stay the same shape. */
1101
1131
  corner-shape: var(--ui-corner-shape, round);
1102
1132
  content: "";
1103
- height: var(--switch-knob);
1104
- width: var(--switch-knob);
1133
+ height: var(--_switch-knob);
1134
+ width: var(--_switch-knob);
1105
1135
  top: var(--_switch-offset);
1106
1136
  inset-inline-start: var(--_switch-offset);
1107
1137
  }
@@ -1111,14 +1141,14 @@
1111
1141
  /* translate has no logical form, so the checked travel is named as a
1112
1142
  physical x offset and mirrored under :dir(rtl) to travel toward the
1113
1143
  opposite edge, matching the inset-inline-start resting position. */
1114
- translate: var(--switch-travel);
1144
+ translate: var(--_switch-travel);
1115
1145
  }
1116
1146
 
1117
1147
  /* Direction sits on the control, ahead of the pseudo-element: nested as
1118
1148
  `:dir(rtl) &` inside `&:checked::after`, `&` carries `::after`, which
1119
1149
  cannot appear inside `:is()`, and the emitted rule matched nothing. */
1120
1150
  &:checked:dir(rtl)::after {
1121
- translate: calc(-1 * var(--switch-travel));
1151
+ translate: calc(-1 * var(--_switch-travel));
1122
1152
  }
1123
1153
 
1124
1154
  /* The mark appears without moving under reduced motion, for the entries
@@ -86,11 +86,14 @@
86
86
 
87
87
  --_hover-ground: var(--_m-hover) color-mix(in oklab, var(--color-surface) var(--_quiet), var(--color-background));
88
88
 
89
- &:hover {
90
- --_d-surface-ground: var(
91
- --_hover-ground,
92
- color-mix(in oklab, var(--color-foreground) var(--_quiet), var(--color-background))
93
- );
89
+ /* Behind the query `box-hover` is, so a tapped card keeps its ground. */
90
+ @media (hover: hover) {
91
+ &:hover {
92
+ --_d-surface-ground: var(
93
+ --_hover-ground,
94
+ color-mix(in oklab, var(--color-foreground) var(--_quiet), var(--color-background))
95
+ );
96
+ }
94
97
  }
95
98
 
96
99
  /* Opt-in, because a card is only sometimes a control. Applications still owe
@@ -12,40 +12,45 @@
12
12
  trigger's own `aria-describedby`; wiring that association is the consumer's,
13
13
  this package only styles the bubble. See docs/usage/tooltips.md. */
14
14
  @utility tooltip {
15
- --tooltip-gap: 0.5rem;
16
- /* How long the bubble stays after the pointer leaves the trigger: long
17
- enough to cross `--tooltip-gap` onto the bubble, which keeps it open
18
- (WCAG 1.4.13). */
19
- --tooltip-linger: 300ms;
20
- --tooltip-top: calc(var(--tooltip-gap) * -1);
21
- --tooltip-right: auto;
22
- --tooltip-bottom: auto;
23
- --tooltip-left: 50%;
24
- --tooltip-origin: bottom;
25
- --tooltip-transform: translate(-50%, -100%);
15
+ /* `--tooltip-gap` and `--tooltip-linger` are public, read with their
16
+ defaults rather than declared, so a value set on any ancestor reaches the
17
+ tooltip. The linger is how long the bubble stays after the pointer leaves
18
+ the trigger: long enough to cross the gap onto the bubble, which keeps it
19
+ open (WCAG 1.4.13). The default is resolved here, so a bubble outside a
20
+ tooltip keeps no delay unless a linger is set above it. The bubble reads
21
+ the public one first, so a value set on the bubble itself, as 0.8
22
+ allowed, still wins. */
23
+ --_tooltip-gap: var(--tooltip-gap, 0.5rem);
24
+ --_tooltip-linger: var(--tooltip-linger, 300ms);
25
+ --_tooltip-top: calc(var(--_tooltip-gap) * -1);
26
+ --_tooltip-right: auto;
27
+ --_tooltip-bottom: auto;
28
+ --_tooltip-left: 50%;
29
+ --_tooltip-origin: bottom;
30
+ --_tooltip-transform: translate(-50%, -100%);
26
31
 
27
32
  @apply relative inline-block;
28
33
 
29
34
  &[data-tooltip-position="bottom"] {
30
- --tooltip-top: auto;
31
- --tooltip-bottom: calc(var(--tooltip-gap) * -1);
32
- --tooltip-origin: top;
33
- --tooltip-transform: translate(-50%, 100%);
35
+ --_tooltip-top: auto;
36
+ --_tooltip-bottom: calc(var(--_tooltip-gap) * -1);
37
+ --_tooltip-origin: top;
38
+ --_tooltip-transform: translate(-50%, 100%);
34
39
  }
35
40
 
36
41
  &[data-tooltip-position="left"] {
37
- --tooltip-top: 50%;
38
- --tooltip-left: calc(var(--tooltip-gap) * -1);
39
- --tooltip-origin: right;
40
- --tooltip-transform: translate(-100%, -50%);
42
+ --_tooltip-top: 50%;
43
+ --_tooltip-left: calc(var(--_tooltip-gap) * -1);
44
+ --_tooltip-origin: right;
45
+ --_tooltip-transform: translate(-100%, -50%);
41
46
  }
42
47
 
43
48
  &[data-tooltip-position="right"] {
44
- --tooltip-top: 50%;
45
- --tooltip-left: auto;
46
- --tooltip-right: calc(var(--tooltip-gap) * -1);
47
- --tooltip-origin: left;
48
- --tooltip-transform: translate(100%, -50%);
49
+ --_tooltip-top: 50%;
50
+ --_tooltip-left: auto;
51
+ --_tooltip-right: calc(var(--_tooltip-gap) * -1);
52
+ --_tooltip-origin: left;
53
+ --_tooltip-transform: translate(100%, -50%);
49
54
  }
50
55
  }
51
56
 
@@ -59,13 +64,13 @@
59
64
  /* Hidden rather than merely transparent, so a closed bubble is nothing
60
65
  under the pointer, and it lingers before it goes: the host stays hovered
61
66
  once the pointer is over the bubble, so crossing the gap in
62
- `--tooltip-linger` keeps it open. Visibility switches at the end of the
67
+ `--_tooltip-linger` keeps it open. Visibility switches at the end of the
63
68
  fade, so the bubble is there to land on for the whole of it. */
64
69
  visibility: hidden;
65
70
  transition-property: opacity, transform, visibility;
66
71
  transition-duration: var(--motion-duration-normal);
67
72
  transition-timing-function: var(--motion-ease);
68
- transition-delay: var(--tooltip-linger, 0s);
73
+ transition-delay: var(--tooltip-linger, var(--_tooltip-linger, 0s));
69
74
 
70
75
  /* Filled, over a ground of its own. An intent floods the fill
71
76
  (`.tooltip-bubble.primary` is the primary colour); with no intent
@@ -104,12 +109,12 @@
104
109
  calc(var(--_corner) * var(--ui-corner-tl, 1)) calc(var(--_corner) * var(--ui-corner-tr, 1))
105
110
  calc(var(--_corner) * var(--ui-corner-br, 1)) calc(var(--_corner) * var(--ui-corner-bl, 1))
106
111
  );
107
- left: var(--tooltip-left);
108
- right: var(--tooltip-right);
109
- top: var(--tooltip-top);
110
- bottom: var(--tooltip-bottom);
111
- transform: var(--tooltip-transform) scale(0);
112
- transform-origin: var(--tooltip-origin);
112
+ left: var(--_tooltip-left);
113
+ right: var(--_tooltip-right);
114
+ top: var(--_tooltip-top);
115
+ bottom: var(--_tooltip-bottom);
116
+ transform: var(--_tooltip-transform) scale(0);
117
+ transform-origin: var(--_tooltip-origin);
113
118
 
114
119
  /* The bubble appears without growing under reduced motion, for the entries
115
120
  without the reset; see `box.css`. */
@@ -149,7 +154,7 @@
149
154
  .tooltip:focus-within &,
150
155
  .tooltip[data-state="open"] & {
151
156
  opacity: 1;
152
- transform: var(--tooltip-transform) scale(1);
157
+ transform: var(--_tooltip-transform) scale(1);
153
158
  visibility: visible;
154
159
  transition-delay: 0s;
155
160
  }
@@ -161,7 +166,7 @@
161
166
  focus alike. */
162
167
  .tooltip[data-state="closed"] & {
163
168
  opacity: 0;
164
- transform: var(--tooltip-transform) scale(0);
169
+ transform: var(--_tooltip-transform) scale(0);
165
170
  visibility: hidden;
166
171
  transition-delay: 0s;
167
172
  }
package/tw/elevation.css CHANGED
@@ -10,8 +10,8 @@
10
10
  `.flat` removes a part-based slab or blur without a conditional (complete
11
11
  surface shadows like glass remain). See model.md#elevation.
12
12
 
13
- `--ui-elevation`, and the plain-page fallback geometry (`--elevation-y`,
14
- `--elevation-blur`) below, are registered non-inheriting: a class sets them
13
+ `--ui-elevation`, and the plain-page fallback geometry (`--_elevation-y`,
14
+ `--_elevation-blur`) below, are registered non-inheriting: a class sets them
15
15
  on the element it is written on and nothing else picks them up. Inherited, a
16
16
  `.floating` dialog's ambient x2 would reach its own unclassed buttons and
17
17
  double their shadow with nothing in the markup asking for it. Depth is not
@@ -37,12 +37,12 @@
37
37
  inherits: false;
38
38
  }
39
39
 
40
- @property --elevation-y {
40
+ @property --_elevation-y {
41
41
  syntax: "*";
42
42
  inherits: false;
43
43
  }
44
44
 
45
- @property --elevation-blur {
45
+ @property --_elevation-blur {
46
46
  syntax: "*";
47
47
  inherits: false;
48
48
  }
@@ -63,8 +63,8 @@
63
63
  /* The aesthetic's depth as authored, said explicitly. */
64
64
  @utility raised {
65
65
  @layer families {
66
- --elevation-y: 1px;
67
- --elevation-blur: 3px;
66
+ --_elevation-y: 1px;
67
+ --_elevation-blur: 3px;
68
68
  --ui-elevation: 1;
69
69
  }
70
70
  }
@@ -72,8 +72,8 @@
72
72
  /* Twice it, for menus and popovers. */
73
73
  @utility floating {
74
74
  @layer families {
75
- --elevation-y: 1px;
76
- --elevation-blur: 3px;
75
+ --_elevation-y: 1px;
76
+ --_elevation-blur: 3px;
77
77
  --ui-elevation: 2;
78
78
  }
79
79
  }
package/tw/native.css CHANGED
@@ -60,7 +60,10 @@
60
60
  --intent-border: var(--ui-rule-ink, var(--ui-ink, var(--color-border)));
61
61
  }
62
62
 
63
- :where(hr) {
63
+ /* A bare rule and progress bar take the color they sit in, as `.divider`
64
+ and `.progress` do in `intent.css`; a bar in a filled container drew the
65
+ page's text color over the container's own. */
66
+ :where(hr, progress) {
64
67
  --intent-color: currentColor;
65
68
  --intent-border: currentColor;
66
69
  }
package/tw/theme.css CHANGED
@@ -271,7 +271,7 @@
271
271
  at any depth. It does not win over an explicit selector of the opposite
272
272
  theme further up: `.dark *` matches every descendant of a `.dark` element,
273
273
  including one inside a nested `.light`, so a `dark:` utility still fires
274
- there although its tokens are light. `setup.md#dark-variant` documents it. */
274
+ there although its tokens are light. `docs/usage/theming.md#the-dark-variant` documents it. */
275
275
  @custom-variant dark {
276
276
  &:where(.dark, .dark *, .theme-dark, .theme-dark *, [data-theme="dark"], [data-theme="dark"] *) {
277
277
  @slot;