@codenhub/styles 0.6.0 → 0.8.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 (151) hide show
  1. package/NOTICE +18 -16
  2. package/README.es.md +62 -0
  3. package/README.md +28 -22
  4. package/README.pt-BR.md +62 -0
  5. package/dist/aesthetics/chunky-tile.css +1 -1
  6. package/dist/aesthetics/cyber.css +1 -1
  7. package/dist/aesthetics/glass.css +1 -1
  8. package/dist/aesthetics/index.css +2 -2
  9. package/dist/aesthetics/neobrutalism.css +1 -1
  10. package/dist/aesthetics/pixel.css +1 -1
  11. package/dist/aesthetics/sketch.css +1 -1
  12. package/dist/cli.js +189 -0
  13. package/dist/components.css +2 -2
  14. package/dist/index.css +2 -2
  15. package/dist/index.d.ts +1 -0
  16. package/dist/native.css +2 -2
  17. package/dist/palette.css +1 -1
  18. package/dist/theme.css +2 -2
  19. package/docs/accessibility.md +108 -60
  20. package/docs/changelog/0.1.1.md +1 -0
  21. package/docs/changelog/0.2.0.md +1 -0
  22. package/docs/changelog/0.3.0.md +1 -0
  23. package/docs/changelog/0.4.0.md +1 -0
  24. package/docs/changelog/0.5.0.md +8 -3
  25. package/docs/changelog/0.6.0.md +2 -1
  26. package/docs/changelog/0.7.0.md +50 -0
  27. package/docs/changelog/0.8.0.md +70 -0
  28. package/docs/changelog/index.md +2 -0
  29. package/docs/concepts.md +188 -46
  30. package/docs/es/accessibility.md +169 -0
  31. package/docs/es/concepts.md +212 -0
  32. package/docs/es/index.md +105 -0
  33. package/docs/es/integrating/astro.md +35 -0
  34. package/docs/es/integrating/index.md +75 -0
  35. package/docs/es/integrating/nextjs.md +67 -0
  36. package/docs/es/integrating/svelte.md +45 -0
  37. package/docs/es/integrating/tailwind.md +148 -0
  38. package/docs/es/integrating/vue.md +31 -0
  39. package/docs/es/integrating/without-a-bundler.md +238 -0
  40. package/docs/es/setup.md +188 -0
  41. package/docs/es/usage/aesthetics.md +282 -0
  42. package/docs/es/usage/buttons.md +135 -0
  43. package/docs/es/usage/composing.md +168 -0
  44. package/docs/es/usage/content-and-layout.md +219 -0
  45. package/docs/es/usage/customizing.md +304 -0
  46. package/docs/es/usage/feedback.md +157 -0
  47. package/docs/es/usage/forms.md +256 -0
  48. package/docs/es/usage/index.md +33 -0
  49. package/docs/es/usage/presets.md +92 -0
  50. package/docs/es/usage/solo.md +86 -0
  51. package/docs/es/usage/theming.md +309 -0
  52. package/docs/es/usage/tooltips.md +127 -0
  53. package/docs/index.md +86 -26
  54. package/docs/integrating/astro.md +3 -4
  55. package/docs/integrating/index.md +46 -13
  56. package/docs/integrating/nextjs.md +3 -4
  57. package/docs/integrating/svelte.md +5 -4
  58. package/docs/integrating/tailwind.md +89 -6
  59. package/docs/integrating/vue.md +3 -4
  60. package/docs/integrating/without-a-bundler.md +237 -0
  61. package/docs/pt-BR/accessibility.md +169 -0
  62. package/docs/pt-BR/concepts.md +212 -0
  63. package/docs/pt-BR/index.md +105 -0
  64. package/docs/pt-BR/integrating/astro.md +35 -0
  65. package/docs/pt-BR/integrating/index.md +75 -0
  66. package/docs/pt-BR/integrating/nextjs.md +67 -0
  67. package/docs/pt-BR/integrating/svelte.md +45 -0
  68. package/docs/pt-BR/integrating/tailwind.md +148 -0
  69. package/docs/pt-BR/integrating/vue.md +31 -0
  70. package/docs/pt-BR/integrating/without-a-bundler.md +238 -0
  71. package/docs/pt-BR/setup.md +188 -0
  72. package/docs/pt-BR/usage/aesthetics.md +282 -0
  73. package/docs/pt-BR/usage/buttons.md +135 -0
  74. package/docs/pt-BR/usage/composing.md +168 -0
  75. package/docs/pt-BR/usage/content-and-layout.md +219 -0
  76. package/docs/pt-BR/usage/customizing.md +304 -0
  77. package/docs/pt-BR/usage/feedback.md +157 -0
  78. package/docs/pt-BR/usage/forms.md +256 -0
  79. package/docs/pt-BR/usage/index.md +33 -0
  80. package/docs/pt-BR/usage/presets.md +92 -0
  81. package/docs/pt-BR/usage/solo.md +86 -0
  82. package/docs/pt-BR/usage/theming.md +309 -0
  83. package/docs/pt-BR/usage/tooltips.md +127 -0
  84. package/docs/setup.md +142 -91
  85. package/docs/usage/aesthetics.md +89 -73
  86. package/docs/usage/buttons.md +112 -40
  87. package/docs/usage/composing.md +109 -92
  88. package/docs/usage/content-and-layout.md +160 -125
  89. package/docs/usage/customizing.md +117 -41
  90. package/docs/usage/feedback.md +138 -71
  91. package/docs/usage/forms.md +201 -107
  92. package/docs/usage/index.md +22 -14
  93. package/docs/usage/presets.md +91 -0
  94. package/docs/usage/solo.md +85 -0
  95. package/docs/usage/theming.md +273 -118
  96. package/docs/usage/tooltips.md +90 -49
  97. package/llms-full.txt +2820 -1298
  98. package/llms.txt +24 -22
  99. package/package.json +134 -91
  100. package/{dist/tw → tw}/aesthetics/chunky-tile.css +107 -29
  101. package/{dist/tw → tw}/aesthetics/cyber.css +88 -40
  102. package/{dist/tw → tw}/aesthetics/glass.css +257 -61
  103. package/{dist/tw → tw}/aesthetics/neobrutalism.css +113 -20
  104. package/{dist/tw → tw}/aesthetics/pixel.css +93 -8
  105. package/tw/aesthetics/sketch.css +417 -0
  106. package/{dist/tw → tw}/box.css +105 -9
  107. package/tw/components/button.css +11 -0
  108. package/tw/components/button.rules.css +187 -0
  109. package/tw/components/feedback.css +11 -0
  110. package/{dist/tw/components/feedback.css → tw/components/feedback.rules.css} +136 -32
  111. package/tw/components/form.css +11 -0
  112. package/{dist/tw/components/form.css → tw/components/form.rules.css} +577 -125
  113. package/tw/components/index.css +16 -0
  114. package/tw/components/rules.css +41 -0
  115. package/tw/components/surface.css +11 -0
  116. package/tw/components/surface.rules.css +222 -0
  117. package/tw/components/tooltip.css +11 -0
  118. package/{dist/tw/components/tooltip.css → tw/components/tooltip.rules.css} +43 -27
  119. package/{dist/tw → tw}/corner.css +18 -12
  120. package/{dist/tw → tw}/elevation.css +23 -17
  121. package/{dist/tw → tw}/index.css +12 -10
  122. package/tw/index.d.ts +1 -0
  123. package/tw/intent.css +312 -0
  124. package/tw/modifiers.css +127 -0
  125. package/tw/native.css +320 -0
  126. package/tw/presentation.css +139 -0
  127. package/tw/reset.css +10 -0
  128. package/{dist/tw/reset.css → tw/reset.rules.css} +21 -22
  129. package/tw/size.css +34 -0
  130. package/tw/surface.css +9 -0
  131. package/tw/surface.rules.css +212 -0
  132. package/{dist/tw → tw}/theme.css +121 -45
  133. package/tw/typography.css +8 -0
  134. package/{dist/tw/typography.css → tw/typography.rules.css} +0 -3
  135. package/{dist/tw → tw}/utilities/content.css +212 -50
  136. package/tw/utilities/index.css +11 -0
  137. package/tw/utilities/layout.css +208 -0
  138. package/tw/utilities/rules.css +8 -0
  139. package/dist/tw/aesthetics/sketch.css +0 -333
  140. package/dist/tw/components/button.css +0 -139
  141. package/dist/tw/components/index.css +0 -36
  142. package/dist/tw/components/loader.css +0 -116
  143. package/dist/tw/components/surface.css +0 -175
  144. package/dist/tw/intent.css +0 -228
  145. package/dist/tw/native.css +0 -203
  146. package/dist/tw/palette.css +0 -460
  147. package/dist/tw/presentation.css +0 -68
  148. package/dist/tw/surface.css +0 -61
  149. package/dist/tw/utilities/index.css +0 -6
  150. package/dist/tw/utilities/layout.css +0 -130
  151. /package/{dist/tw → tw}/aesthetics/index.css +0 -0
@@ -1,5 +1,3 @@
1
- @reference "../box.css";
2
-
3
1
  /* A scroll box clips whatever paints outside it, and `overflow-x` takes the
4
2
  vertical overflow with it, so an aesthetic's halo around the table -- cyber's
5
3
  glow -- would be cut flush on every side. The wrapper makes room for the halo
@@ -40,6 +38,19 @@
40
38
  instead (below), which every engine draws on the rows. Recorded in
41
39
  `registry.json` beside the decision. */
42
40
  @utility data-table {
41
+ /* The neutral reset, carried so `@apply data-table` gets it, as `btn` carries its own. */
42
+ @layer families {
43
+ :where(&:not(.data-table, .primary, .secondary, .success, .warning, .destructive, .info)) {
44
+ --intent-color: var(--color-text);
45
+ --intent-contrast: var(--color-text-contrast);
46
+ --intent-hover: var(--color-text-hover);
47
+ --intent-strong: var(--color-text-strong);
48
+ --intent-subtle: var(--color-surface);
49
+ --intent-border: var(--ui-rule-ink, var(--ui-ink, var(--color-border)));
50
+ --intent-fill-max: 20%;
51
+ }
52
+ }
53
+
43
54
  @apply box text-body w-full border-separate text-start;
44
55
 
45
56
  --_d-fill: 12%;
@@ -54,7 +65,6 @@
54
65
  /* Capped the way `box` caps, because the head is a fill of the intent like any
55
66
  other and a neutral table head is otherwise a slab of the page's ink with
56
67
  ink printed on it. */
57
- --table-fill: calc(min(var(--ui-fill, var(--_d-fill)), var(--intent-fill-max, 100%)) * var(--ui-bg-alpha, 1));
58
68
  /* The head's plate is the tint, so a table carrying no fill has no head
59
69
  plate. `.ghost` means no tint here the way it means no fill everywhere
60
70
  else, and a ghost table is boundaries and type and nothing else -- which is
@@ -75,9 +85,13 @@
75
85
  than over `--table-fill`'s own `calc()`/`min()` chain, which matters for
76
86
  the reason `--table-rule-plated` below records. */
77
87
  --table-ground: min(100%, calc(var(--ui-fill, var(--_d-fill)) * 5));
78
- --table-head-plate: color-mix(in oklab, var(--intent-subtle) var(--table-ground), transparent);
79
88
  --table-head-bg: color-mix(in oklab, var(--intent-color) var(--table-fill), var(--table-head-plate));
80
89
  --table-head-fg: color-mix(in oklab, var(--intent-contrast) var(--_on-fill), var(--intent-strong));
90
+ /* The table's own contrast ink, resolved here so a row with its own intent
91
+ prints on the table's plate in the ink that plate takes: the row's
92
+ `--intent-contrast` is chosen for the row's fill, which a row does not
93
+ paint, and a light one on a light plate reads at 1:1. */
94
+ --_table-contrast: var(--intent-contrast);
81
95
 
82
96
  /* Untinted rather than mixed, the same call `.card.soft` makes: 12% ink onto
83
97
  `--intent-subtle` still reads as `.solid` on neutral, since neutral's ink
@@ -96,25 +110,45 @@
96
110
  element, so nothing here fights `.soft` for `--ui-fill`. `.ghost` is left
97
111
  at box's default (`--_d-ground: transparent`), so it stays the fully bare
98
112
  boundaries-only table; `.soft` differs from it by keeping an opaque,
99
- untinted plate and a head label strip.
100
-
101
- `:not(.solid, .ghost)` rather than `.soft`: the published resting fill is
102
- `soft`, so a bare `<table class="data-table">` with no fill class at all is
103
- the common case this has to reach. */
113
+ untinted plate and a head label strip. */
114
+ /* Whether the table rests on the quiet plate, as 0% or 100%: a resolved
115
+ fill above nothing and below full. Stepped from the resolved fill, as
116
+ `surface`'s `--_quiet` is, not keyed on `:not(.solid, .ghost)` here:
117
+ presentation cascades, and keyed on the class a container's `.solid`
118
+ filled the head and dropped the frame while the body kept this plate. */
119
+ --_table-quiet: min(
120
+ clamp(0%, calc(var(--ui-fill, var(--_d-fill)) * 1000), 100%),
121
+ clamp(0%, calc((100% - var(--ui-fill, var(--_d-fill))) * 1000), 100%)
122
+ );
123
+ /* A table is a pane of content, so under an aesthetic whose surfaces are
124
+ glass it is glass too: the plate is the aesthetic's surface ground when
125
+ one is named. Head and body share the plate, as they do on the page. Only
126
+ the quiet rest reads it: a `.solid` table is opaque, like every `.solid`,
127
+ and a `.ghost` one is boundaries and type. */
128
+ --table-ground-plate: var(--ui-surface-ground, var(--color-foreground));
129
+ --table-fill: min(
130
+ calc(min(var(--ui-fill, var(--_d-fill)), var(--intent-fill-max, 100%)) * var(--ui-bg-alpha, 1)),
131
+ calc(100% - var(--_table-quiet))
132
+ );
133
+ --table-head-plate: color-mix(
134
+ in oklab,
135
+ var(--table-ground-plate) var(--_table-quiet),
136
+ color-mix(in oklab, var(--intent-subtle) var(--table-ground), transparent)
137
+ );
138
+ --_fill-cap: calc(100% - var(--_table-quiet));
139
+ --_d-ground: color-mix(in oklab, var(--table-ground-plate) var(--_table-quiet), transparent);
140
+
141
+ /* A table rests on a plate of its own at every fill but `.ghost`, so no
142
+ ink from above passes through it, as on `surface`. A ghost table on a
143
+ filled parent therefore prints the page's ink on that fill: a toggle
144
+ cannot be stepped from the fill. */
145
+ --_held-ink: var(--_fg-own);
146
+ --_held-on: var(--_own-hold);
147
+
148
+ /* The blur behind the glass plate. A filter cannot be stepped from a
149
+ percentage, so this one part stays keyed on the class: a table under a
150
+ container's `.ghost` in a glass region still blurs what is behind it. */
104
151
  &:not(.solid, .ghost) {
105
- --table-fill: 0%;
106
- --table-head-plate: var(--table-ground-plate);
107
- --_fill-cap: 0%;
108
- --_d-ground: var(--table-ground-plate);
109
-
110
- /* A table is a pane of content, so under an aesthetic whose surfaces are
111
- glass it is glass too: the plate is the aesthetic's surface ground when
112
- one is named, and the blur behind it is the one surfaces take. Head and
113
- body share the plate, as they do on the page. Only this arm reads them:
114
- a `.solid` table is opaque, like every `.solid`, and a `.ghost` one is
115
- boundaries and type with nothing behind them to blur. */
116
- --table-ground-plate: var(--ui-surface-ground, var(--color-foreground));
117
-
118
152
  -webkit-backdrop-filter: var(--ui-backdrop, none);
119
153
  backdrop-filter: var(--ui-backdrop, none);
120
154
  }
@@ -158,7 +192,11 @@
158
192
  something every engine can carry. `--table-row-hover` below is written the
159
193
  same way, for the same reason. */
160
194
  --table-rule-plated: color-mix(in oklab, currentColor 20%, transparent);
161
- --table-rule-tone: color-mix(in oklab, var(--table-rule-plated) var(--ui-fill, var(--_d-fill)), var(--intent-border));
195
+ --table-rule-tone: color-mix(
196
+ in oklab,
197
+ var(--table-rule-plated) var(--ui-fill, var(--_d-fill)),
198
+ var(--intent-border, var(--ui-rule-ink, var(--ui-ink, var(--color-border))))
199
+ );
162
200
  --table-rule: color-mix(in oklab, var(--table-rule-tone) var(--table-rule-amount), transparent);
163
201
  --table-boundary: color-mix(in oklab, var(--table-rule-tone) var(--table-boundary-amount), transparent);
164
202
  /* A hovered row is a step from the table it is in, not from a tone the page
@@ -270,8 +308,9 @@
270
308
  `th` centers unless its parent names an alignment, and `start` is the
271
309
  initial value, which counts as naming none. */
272
310
  --_table-rule-width: var(--ui-border-width, var(--border-width));
273
- & th,
274
- & td {
311
+ /* In `:where()`: these are the cells' defaults, and at the 0-1-1 the plain
312
+ selector carried, `p-0` or `text-center` on a cell lost to them. */
313
+ :where(& :is(th, td)) {
275
314
  @apply px-4 py-3 text-start align-top;
276
315
 
277
316
  border-bottom-style: var(--ui-rule-style, var(--ui-line-style, solid));
@@ -298,24 +337,51 @@
298
337
  border-top-color: var(--table-boundary);
299
338
  }
300
339
 
301
- /* Rules between body rows, on request -- and every line off, on the opposite
302
- request. `.ruleless` is declared second so it wins the nonsense combination
303
- rather than leaving it to whichever class the author typed first. */
304
- &.ruled {
305
- --ui-rule: 100%;
306
- }
307
-
308
- &.ruleless {
309
- --ui-rule: 0%;
310
- }
311
-
312
- & th {
340
+ :where(& th) {
313
341
  @apply text-label font-semibold;
314
342
  }
315
343
 
316
344
  /* Rows deliberately inherit the table's intent instead of resetting it, so a
317
345
  `.data-table.success` tints its own rows. A `<tr class="warning">` still wins,
318
- because an intent class on the element beats an inherited value. */
346
+ because an intent class on the element beats an inherited value.
347
+
348
+ The class only changes the row's intent slots, though, and everything the
349
+ row paints was resolved on the table: the text, the rule and the hover
350
+ step are each a custom property whose `var()`s resolved against the
351
+ table's intent, so they inherited down already-green to a destructive row.
352
+ A row with its own intent resolves them again against it. The plate stays
353
+ the table's, so the ink keeps the table's ramp -- `--_on-fill`, inherited
354
+ as a resolved percentage -- and a solid table's rows still print the
355
+ table's contrast ink on it. Only a row that names an intent: an ordinary row would resolve
356
+ the same values, but a `color` declared on every row would override a
357
+ colour a consumer set on the table. */
358
+ & tbody tr:is(.neutral, .primary, .secondary, .success, .warning, .destructive, .info) {
359
+ --table-rule-tone: color-mix(
360
+ in oklab,
361
+ var(--table-rule-plated) var(--ui-fill, var(--_d-fill)),
362
+ var(--intent-border)
363
+ );
364
+ --table-rule: color-mix(in oklab, var(--table-rule-tone) var(--table-rule-amount), transparent);
365
+ --table-hover-tone: color-mix(in oklab, currentColor var(--ui-fill, var(--_d-fill)), var(--intent-hover));
366
+ --table-row-hover: color-mix(in oklab, var(--table-hover-tone) 12%, transparent);
367
+
368
+ /* A neutral solid table caps its fill at 20%, so its plate is a light grey
369
+ and the ramp keeps `--_on-fill` low: the row's ink is then mostly the
370
+ row's `--intent-strong`, which is chosen against the row's own subtle
371
+ ground rather than a grey one, and success, warning and destructive fall
372
+ under 4.5:1 on it. Those rows print the table's own ink, `--_fg`,
373
+ inherited already resolved against the table, and keep their intent in
374
+ the rule and the hover.
375
+ `--_plate` is the table's too: 100% where the intent's cap stopped its
376
+ fill, read rather than a `.solid` class on the table, which a
377
+ container's `.solid` does not put there. */
378
+ color: color-mix(
379
+ in oklab,
380
+ var(--_fg) var(--_plate, 0%),
381
+ color-mix(in oklab, var(--_table-contrast) var(--_on-fill), var(--intent-strong))
382
+ );
383
+ }
384
+
319
385
  & tbody tr > * {
320
386
  transition: background-color var(--motion-duration-fast) var(--motion-ease);
321
387
  }
@@ -371,9 +437,23 @@
371
437
  separate from, so it alone drops the extra ink. The ground itself is not
372
438
  moved the way `.panel`'s is: `--intent-subtle` already resolves to
373
439
  `--color-surface` for neutral, a real, visible step past the page on its
374
- own, so only the wash on top of it goes. `:not(.solid)` so an explicit
375
- `.solid` chip is never clamped to a cap it did not ask for. */
440
+ own, so only the wash on top of it goes. The cap is stepped from the
441
+ resolved fill, so a full fill -- the chip's own `.solid` or a container's --
442
+ is never clamped to a cap it did not ask for. */
376
443
  @utility kbd {
444
+ /* The neutral reset, carried so `@apply kbd` gets it, as `btn` carries its own. */
445
+ @layer families {
446
+ :where(&:not(.kbd, .primary, .secondary, .success, .warning, .destructive, .info)) {
447
+ --intent-color: var(--color-text);
448
+ --intent-contrast: var(--color-text-contrast);
449
+ --intent-hover: var(--color-text-hover);
450
+ --intent-strong: var(--color-text-strong);
451
+ --intent-subtle: var(--color-surface);
452
+ --intent-border: var(--ui-ink, var(--color-border));
453
+ --intent-fill-max: 20%;
454
+ }
455
+ }
456
+
377
457
  @apply box inline-flex items-center px-1.5 py-0.5 font-mono text-xs font-medium;
378
458
 
379
459
  --_d-fill: 12%;
@@ -382,6 +462,10 @@
382
462
  --_d-elevation: 0;
383
463
  --_d-ground: var(--intent-subtle);
384
464
 
465
+ /* A ground of its own, so no ink from above passes through, as on `surface`. */
466
+ --_held-ink: var(--_fg-own);
467
+ --_held-on: var(--_own-hold);
468
+
385
469
  /* A small default remains when no aesthetic asks for a complete chip shape. */
386
470
  border-radius: var(
387
471
  --ui-radius-tight,
@@ -389,13 +473,35 @@
389
473
  );
390
474
  clip-path: var(--ui-clip-tight, var(--ui-clip, none));
391
475
 
392
- &:not(.primary, .secondary, .success, .warning, .destructive, .info, .solid) {
393
- --_fill-cap: 0%;
394
- }
476
+ /* The neutral wash goes, and only that: a neutral intent (its cap is below
477
+ 100%) asking for less than its cap. Stepped from the resolved fill, as
478
+ `surface`'s `--_quiet` is, not keyed on a class here: presentation
479
+ cascades, and keyed on the class a container's `.solid` reached the ink
480
+ and the line while the plate stayed capped. */
481
+ --_fill-cap: calc(
482
+ 100% -
483
+ min(
484
+ clamp(0%, calc((100% - var(--intent-fill-max, 100%)) * 1000), 100%),
485
+ clamp(0%, calc((var(--intent-fill-max, 100%) - var(--ui-fill, var(--_d-fill))) * 1000), 100%)
486
+ )
487
+ );
395
488
  }
396
489
 
397
490
  /* The same plate with no line around it. */
398
491
  @utility code {
492
+ /* The neutral reset, carried so `@apply code` gets it, as `btn` carries its own. */
493
+ @layer families {
494
+ :where(&:not(.code, .primary, .secondary, .success, .warning, .destructive, .info)) {
495
+ --intent-color: var(--color-text);
496
+ --intent-contrast: var(--color-text-contrast);
497
+ --intent-hover: var(--color-text-hover);
498
+ --intent-strong: var(--color-text-strong);
499
+ --intent-subtle: var(--color-surface);
500
+ --intent-border: var(--ui-ink, var(--color-border));
501
+ --intent-fill-max: 20%;
502
+ }
503
+ }
504
+
399
505
  @apply box px-1.25 py-0.75 font-mono;
400
506
 
401
507
  --_d-fill: 12%;
@@ -404,19 +510,45 @@
404
510
  --_d-elevation: 0;
405
511
  --_d-ground: var(--intent-subtle);
406
512
 
513
+ /* A ground of its own, so no ink from above passes through, as on `surface`. */
514
+ --_held-ink: var(--_fg-own);
515
+ --_held-on: var(--_own-hold);
516
+
407
517
  border-radius: var(
408
518
  --ui-radius-tight,
409
519
  min(var(--ui-radius, var(--ui-corner, var(--radius-small))), var(--radius-small))
410
520
  );
411
521
  clip-path: var(--ui-clip-tight, var(--ui-clip, none));
412
522
 
413
- &:not(.primary, .secondary, .success, .warning, .destructive, .info, .solid) {
414
- --_fill-cap: 0%;
415
- }
523
+ /* The neutral wash goes, and only that: a neutral intent (its cap is below
524
+ 100%) asking for less than its cap. Stepped from the resolved fill, as
525
+ `surface`'s `--_quiet` is, not keyed on a class here: presentation
526
+ cascades, and keyed on the class a container's `.solid` reached the ink
527
+ and the line while the plate stayed capped. */
528
+ --_fill-cap: calc(
529
+ 100% -
530
+ min(
531
+ clamp(0%, calc((100% - var(--intent-fill-max, 100%)) * 1000), 100%),
532
+ clamp(0%, calc((var(--intent-fill-max, 100%) - var(--ui-fill, var(--_d-fill))) * 1000), 100%)
533
+ )
534
+ );
416
535
  }
417
536
 
418
537
  /* And at block scale, where the radius is a control's rather than a chip's. */
419
538
  @utility pre {
539
+ /* The neutral reset, carried so `@apply pre` gets it, as `btn` carries its own. */
540
+ @layer families {
541
+ :where(&:not(.pre, .primary, .secondary, .success, .warning, .destructive, .info)) {
542
+ --intent-color: var(--color-text);
543
+ --intent-contrast: var(--color-text-contrast);
544
+ --intent-hover: var(--color-text-hover);
545
+ --intent-strong: var(--color-text-strong);
546
+ --intent-subtle: var(--color-surface);
547
+ --intent-border: var(--ui-ink, var(--color-border));
548
+ --intent-fill-max: 20%;
549
+ }
550
+ }
551
+
420
552
  @apply box block overflow-x-auto px-2.5 py-1.5 font-mono;
421
553
 
422
554
  --_d-fill: 12%;
@@ -425,9 +557,22 @@
425
557
  --_d-elevation: 0;
426
558
  --_d-ground: var(--intent-subtle);
427
559
 
428
- &:not(.primary, .secondary, .success, .warning, .destructive, .info, .solid) {
429
- --_fill-cap: 0%;
430
- }
560
+ /* A ground of its own, so no ink from above passes through, as on `surface`. */
561
+ --_held-ink: var(--_fg-own);
562
+ --_held-on: var(--_own-hold);
563
+
564
+ /* The neutral wash goes, and only that: a neutral intent (its cap is below
565
+ 100%) asking for less than its cap. Stepped from the resolved fill, as
566
+ `surface`'s `--_quiet` is, not keyed on a class here: presentation
567
+ cascades, and keyed on the class a container's `.solid` reached the ink
568
+ and the line while the plate stayed capped. */
569
+ --_fill-cap: calc(
570
+ 100% -
571
+ min(
572
+ clamp(0%, calc((100% - var(--intent-fill-max, 100%)) * 1000), 100%),
573
+ clamp(0%, calc((var(--intent-fill-max, 100%) - var(--ui-fill, var(--_d-fill))) * 1000), 100%)
574
+ )
575
+ );
431
576
  }
432
577
 
433
578
  /* Typography wearing a component's name, so it composes none of `box`: `box`
@@ -445,6 +590,19 @@
445
590
  what makes the bar read as a bar at the default hairline: doubling 1px gives
446
591
  2px, and a quotation mark needs more presence than a table rule. */
447
592
  @utility quote {
593
+ /* The neutral reset, carried so `@apply quote` gets it, as `btn` carries its own. */
594
+ @layer families {
595
+ :where(&:not(.quote, .primary, .secondary, .success, .warning, .destructive, .info)) {
596
+ --intent-color: var(--color-text);
597
+ --intent-contrast: var(--color-text-contrast);
598
+ --intent-hover: var(--color-text-hover);
599
+ --intent-strong: var(--color-text-strong);
600
+ --intent-subtle: var(--color-surface);
601
+ --intent-border: var(--ui-ink, var(--color-border));
602
+ --intent-fill-max: 20%;
603
+ }
604
+ }
605
+
448
606
  --_d-fill: 0%;
449
607
  --_d-fg-on-fill: 0%;
450
608
  --_d-border: 100%;
@@ -475,7 +633,11 @@
475
633
  A quotation draws its own edge instead of taking `box`'s, so an edge change
476
634
  is made in `box`, `.quote`, and `.progress`. Recorded in `registry.json`
477
635
  beside the decision to compose none of `box`. */
478
- --quote-line: color-mix(in oklab, transparent var(--ui-fill, var(--_d-fill)), var(--intent-border));
636
+ --quote-line: color-mix(
637
+ in oklab,
638
+ transparent var(--ui-fill, var(--_d-fill)),
639
+ var(--intent-border, var(--ui-ink, var(--color-border)))
640
+ );
479
641
  --quote-edge: color-mix(in oklab, var(--quote-line) var(--ui-border, var(--_d-border)), transparent);
480
642
  --quote-width: calc(var(--ui-border-width, var(--border-width)) * 2 + 1px);
481
643
 
@@ -0,0 +1,11 @@
1
+ @import "../theme.css";
2
+ @import "../typography.css";
3
+ @reference "../box.css";
4
+
5
+ /* The focused entry: what the rules in `rules.css` need to compile on their
6
+ own, then the rules. An aggregate entry imports the rules file directly and
7
+ supplies those needs once -- Tailwind stacks a `@utility` each time a file
8
+ that defines it is imported or referenced, so a second route to `box.css`
9
+ doubles every `@apply box`. See docs/internal/model.md (One route to each
10
+ utility). */
11
+ @import "./rules.css";
@@ -0,0 +1,208 @@
1
+ /* The gap is a private each layout declares from `--layout-gap`, and the
2
+ density modifiers step it: `tight` and `loose` raise a flag
3
+ (`modifiers.css`) and the layout reads it, so `@apply stack tight` carries
4
+ the step. A container's `--layout-gap` still reaches every layout inside
5
+ it, because each one reads the token afresh; a `.tight` or `.loose` stays
6
+ on its own element, the way `.p-xs` stays on a card, because the layout
7
+ inside restates the flags at zero specificity in `families`, where one on
8
+ the element itself still wins. */
9
+ @utility view {
10
+ @layer families {
11
+ :where(&:not(.tight)) {
12
+ --_m-tight: initial;
13
+ }
14
+
15
+ :where(&:not(.loose)) {
16
+ --_m-loose: initial;
17
+ }
18
+
19
+ :where(&:not(.vertical)) {
20
+ --_m-vertical: initial;
21
+ }
22
+
23
+ :where(&:not(.horizontal)) {
24
+ --_m-horizontal: initial;
25
+ }
26
+ }
27
+
28
+ @apply flex gap-(--_gap);
29
+
30
+ --_tight-gap: var(--_m-tight) 0.5rem;
31
+ --_loose-gap: var(--_m-loose) 1.5rem;
32
+ --_gap: var(--_loose-gap, var(--_tight-gap, var(--layout-gap)));
33
+
34
+ /* A row unless asked: `vertical` stacks, and `horizontal` is a row that
35
+ wraps and centres its items. */
36
+ --_vertical-direction: var(--_m-vertical) column;
37
+ --_horizontal-direction: var(--_m-horizontal) row;
38
+ --_horizontal-wrap: var(--_m-horizontal) wrap;
39
+ --_horizontal-align: var(--_m-horizontal) center;
40
+
41
+ flex-direction: var(--_horizontal-direction, var(--_vertical-direction, row));
42
+ flex-wrap: var(--_horizontal-wrap, nowrap);
43
+ align-items: var(--_horizontal-align, normal);
44
+ }
45
+
46
+ @utility stack {
47
+ @layer families {
48
+ :where(&:not(.tight)) {
49
+ --_m-tight: initial;
50
+ }
51
+
52
+ :where(&:not(.loose)) {
53
+ --_m-loose: initial;
54
+ }
55
+ }
56
+
57
+ @apply flex flex-col gap-(--_gap);
58
+
59
+ --_tight-gap: var(--_m-tight) 0.5rem;
60
+ --_loose-gap: var(--_m-loose) 1.5rem;
61
+ --_gap: var(--_loose-gap, var(--_tight-gap, var(--layout-gap)));
62
+ }
63
+
64
+ @utility cluster {
65
+ @layer families {
66
+ :where(&:not(.tight)) {
67
+ --_m-tight: initial;
68
+ }
69
+
70
+ :where(&:not(.loose)) {
71
+ --_m-loose: initial;
72
+ }
73
+
74
+ :where(&:not(.between)) {
75
+ --_m-between: initial;
76
+ }
77
+ }
78
+
79
+ @apply flex flex-wrap items-center gap-(--_gap);
80
+
81
+ --_tight-gap: var(--_m-tight) 0.5rem;
82
+ --_loose-gap: var(--_m-loose) 1.5rem;
83
+ --_gap: var(--_loose-gap, var(--_tight-gap, var(--layout-gap)));
84
+
85
+ --_between-justify: var(--_m-between) space-between;
86
+
87
+ justify-content: var(--_between-justify, normal);
88
+ }
89
+
90
+ @utility auto-grid {
91
+ @layer families {
92
+ :where(&:not(.tight)) {
93
+ --_m-tight: initial;
94
+ }
95
+
96
+ :where(&:not(.loose)) {
97
+ --_m-loose: initial;
98
+ }
99
+ }
100
+
101
+ @apply grid gap-(--_gap);
102
+
103
+ --_tight-gap: var(--_m-tight) 0.5rem;
104
+ --_loose-gap: var(--_m-loose) 1.5rem;
105
+ --_gap: var(--_loose-gap, var(--_tight-gap, var(--layout-gap)));
106
+
107
+ grid-template-columns: repeat(auto-fit, minmax(min(var(--layout-grid-min), 100%), 1fr));
108
+ }
109
+
110
+ @utility section {
111
+ @apply px-(--layout-gutter) py-(--layout-section-block);
112
+ }
113
+
114
+ @utility section-content {
115
+ @layer families {
116
+ :where(&:not(.narrow)) {
117
+ --_m-narrow: initial;
118
+ }
119
+
120
+ :where(&:not(.wide)) {
121
+ --_m-wide: initial;
122
+ }
123
+ }
124
+
125
+ @apply mx-auto w-full;
126
+
127
+ --_narrow-max: var(--_m-narrow) var(--container-narrow);
128
+ --_wide-max: var(--_m-wide) var(--container-wide);
129
+
130
+ max-width: var(--_wide-max, var(--_narrow-max, var(--container-max)));
131
+ }
132
+
133
+ /* An indicator: intent only, and no presentation token at all. A rule is a
134
+ line, and neither a fill class nor an edge class has anything to say about
135
+ one -- a container's `.ghost.edgeless` must not make every `<hr>` inside it
136
+ invisible. The width is the aesthetic's line width, unscaled. */
137
+ @utility divider {
138
+ /* The indicator reset, carried so `@apply divider` gets it, as `btn` carries
139
+ its own: the neutral slots, with the two an indicator takes from the
140
+ colour it sits in. Without it an applied one read an undefined
141
+ `--intent-border`. */
142
+ @layer families {
143
+ :where(&:not(.divider, .primary, .secondary, .success, .warning, .destructive, .info)) {
144
+ --intent-color: currentColor;
145
+ --intent-contrast: var(--color-text-contrast);
146
+ --intent-hover: var(--color-text-hover);
147
+ --intent-strong: var(--color-text-strong);
148
+ --intent-subtle: var(--color-surface);
149
+ --intent-border: currentColor;
150
+ --intent-fill-max: 20%;
151
+ }
152
+ }
153
+
154
+ @layer families {
155
+ :where(&:not(.vertical)) {
156
+ --_m-vertical: initial;
157
+ }
158
+ }
159
+
160
+ --divider-color: var(--intent-color);
161
+ --divider-width: var(--ui-border-width, var(--border-width));
162
+
163
+ @apply m-0 border-0;
164
+
165
+ /* Across unless `vertical`, which turns the line to the inline-start edge
166
+ and stretches it down its row. */
167
+ --_vertical-width: var(--_m-vertical) 0px;
168
+ --_vertical-self: var(--_m-vertical) stretch;
169
+ --_vertical-line: var(--_m-vertical) var(--divider-width);
170
+
171
+ width: var(--_vertical-width, 100%);
172
+ align-self: var(--_vertical-self, auto);
173
+ border-color: var(--divider-color);
174
+ border-style: var(--ui-rule-style, var(--ui-line-style, solid));
175
+ border-top-width: var(--_vertical-width, var(--divider-width));
176
+ border-inline-start-width: var(--_vertical-line, 0px);
177
+ }
178
+
179
+ @utility selection-contrast {
180
+ &::selection,
181
+ & *::selection {
182
+ background: var(--color-primary-contrast);
183
+ color: var(--color-primary);
184
+ }
185
+ }
186
+
187
+ /* `.visually-hidden`, the standard clip-based recipe. Tailwind's `sr-only` does
188
+ nothing on the vanilla entries (`.`, `/native`), which compile real CSS from
189
+ a scan of `src/` alone with no Tailwind utility layer to reach, so the
190
+ package publishes its own.
191
+
192
+ It is not named `.sr-only`: on a `/tw` entry the consumer's own Tailwind
193
+ build supplies that utility, and two same-named utilities in one cascade
194
+ collide silently -- the collision `registry.test.ts` polices (see
195
+ `data-table`'s `renamedFrom` in `registry.json`). `@source
196
+ inline("visually-hidden")` in `utilities/index.css` force-emits it on the
197
+ entries with no scannable markup of their own. */
198
+ @utility visually-hidden {
199
+ position: absolute;
200
+ width: 1px;
201
+ height: 1px;
202
+ padding: 0;
203
+ margin: -1px;
204
+ overflow: hidden;
205
+ clip: rect(0, 0, 0, 0);
206
+ white-space: nowrap;
207
+ border-width: 0;
208
+ }
@@ -0,0 +1,8 @@
1
+ /* Every utility, with nothing they need: `content.css` applies `box`, which
2
+ the importing entry supplies once. See `index.css` beside this file. */
3
+ @import "./layout.css";
4
+ @import "./content.css";
5
+
6
+ /* Safelists the utilities for the compiled build, which has no markup to scan.
7
+ `scripts/build-tw.mjs` strips it from the `tw/` copy -- see the note in
8
+ `components/index.css`. */