@codenhub/styles 0.7.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 (130) hide show
  1. package/README.es.md +9 -8
  2. package/README.md +8 -7
  3. package/README.pt-BR.md +9 -8
  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 +2 -2
  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 +358 -0
  12. package/dist/components.css +50 -2
  13. package/dist/index.css +50 -2
  14. package/dist/index.d.ts +1 -0
  15. package/dist/native.css +50 -2
  16. package/dist/palette.css +1 -1
  17. package/dist/purge/classes.json +1 -0
  18. package/dist/purge/index.css +50 -0
  19. package/dist/theme.css +2 -2
  20. package/docs/accessibility.md +46 -27
  21. package/docs/changelog/0.8.0.md +70 -0
  22. package/docs/changelog/0.9.0.md +40 -0
  23. package/docs/changelog/index.md +2 -0
  24. package/docs/es/accessibility.md +47 -28
  25. package/docs/es/index.md +4 -3
  26. package/docs/es/integrating/index.md +38 -10
  27. package/docs/es/integrating/tailwind.md +79 -8
  28. package/docs/es/integrating/without-a-bundler.md +157 -5
  29. package/docs/es/setup.md +43 -34
  30. package/docs/es/usage/aesthetics.md +20 -14
  31. package/docs/es/usage/buttons.md +6 -2
  32. package/docs/es/usage/composing.md +6 -2
  33. package/docs/es/usage/content-and-layout.md +10 -4
  34. package/docs/es/usage/customizing.md +46 -26
  35. package/docs/es/usage/feedback.md +5 -1
  36. package/docs/es/usage/forms.md +30 -5
  37. package/docs/es/usage/index.md +7 -3
  38. package/docs/es/usage/presets.md +92 -0
  39. package/docs/es/usage/solo.md +4 -2
  40. package/docs/es/usage/theming.md +29 -16
  41. package/docs/es/usage/tooltips.md +2 -2
  42. package/docs/index.md +3 -2
  43. package/docs/integrating/index.md +38 -10
  44. package/docs/integrating/tailwind.md +78 -7
  45. package/docs/integrating/without-a-bundler.md +156 -4
  46. package/docs/pt-BR/accessibility.md +47 -28
  47. package/docs/pt-BR/index.md +4 -3
  48. package/docs/pt-BR/integrating/index.md +38 -10
  49. package/docs/pt-BR/integrating/tailwind.md +79 -8
  50. package/docs/pt-BR/integrating/without-a-bundler.md +157 -5
  51. package/docs/pt-BR/setup.md +43 -34
  52. package/docs/pt-BR/usage/aesthetics.md +20 -14
  53. package/docs/pt-BR/usage/buttons.md +6 -2
  54. package/docs/pt-BR/usage/composing.md +6 -2
  55. package/docs/pt-BR/usage/content-and-layout.md +10 -4
  56. package/docs/pt-BR/usage/customizing.md +46 -26
  57. package/docs/pt-BR/usage/feedback.md +5 -1
  58. package/docs/pt-BR/usage/forms.md +30 -5
  59. package/docs/pt-BR/usage/index.md +7 -3
  60. package/docs/pt-BR/usage/presets.md +92 -0
  61. package/docs/pt-BR/usage/solo.md +4 -2
  62. package/docs/pt-BR/usage/theming.md +22 -9
  63. package/docs/pt-BR/usage/tooltips.md +2 -2
  64. package/docs/setup.md +31 -22
  65. package/docs/usage/aesthetics.md +19 -13
  66. package/docs/usage/buttons.md +5 -1
  67. package/docs/usage/composing.md +5 -1
  68. package/docs/usage/content-and-layout.md +9 -3
  69. package/docs/usage/customizing.md +45 -25
  70. package/docs/usage/feedback.md +4 -0
  71. package/docs/usage/forms.md +29 -4
  72. package/docs/usage/index.md +6 -2
  73. package/docs/usage/presets.md +91 -0
  74. package/docs/usage/solo.md +3 -1
  75. package/docs/usage/theming.md +21 -8
  76. package/docs/usage/tooltips.md +1 -1
  77. package/llms-full.txt +695 -134
  78. package/llms.txt +3 -2
  79. package/package.json +117 -65
  80. package/{dist/tw → tw}/aesthetics/chunky-tile.css +43 -20
  81. package/{dist/tw → tw}/aesthetics/cyber.css +19 -25
  82. package/{dist/tw → tw}/aesthetics/glass.css +110 -36
  83. package/{dist/tw → tw}/aesthetics/neobrutalism.css +41 -21
  84. package/{dist/tw → tw}/aesthetics/pixel.css +3 -0
  85. package/tw/aesthetics/sketch.css +435 -0
  86. package/{dist/tw → tw}/box.css +109 -26
  87. package/tw/components/button.css +11 -0
  88. package/tw/components/button.rules.css +187 -0
  89. package/tw/components/feedback.css +11 -0
  90. package/{dist/tw/components/feedback.css → tw/components/feedback.rules.css} +156 -33
  91. package/tw/components/form.css +11 -0
  92. package/{dist/tw/components/form.css → tw/components/form.rules.css} +584 -141
  93. package/tw/components/index.css +16 -0
  94. package/tw/components/rules.css +41 -0
  95. package/tw/components/surface.css +11 -0
  96. package/tw/components/surface.rules.css +225 -0
  97. package/tw/components/tooltip.css +11 -0
  98. package/{dist/tw/components/tooltip.css → tw/components/tooltip.rules.css} +82 -61
  99. package/{dist/tw → tw}/corner.css +18 -12
  100. package/{dist/tw → tw}/elevation.css +27 -21
  101. package/{dist/tw → tw}/index.css +11 -9
  102. package/tw/index.d.ts +1 -0
  103. package/tw/intent.css +312 -0
  104. package/tw/modifiers.css +127 -0
  105. package/tw/native.css +323 -0
  106. package/tw/presentation.css +139 -0
  107. package/tw/reset.css +10 -0
  108. package/{dist/tw/reset.css → tw/reset.rules.css} +7 -8
  109. package/tw/size.css +34 -0
  110. package/tw/surface.css +9 -0
  111. package/tw/surface.rules.css +212 -0
  112. package/{dist/tw → tw}/theme.css +93 -29
  113. package/tw/typography.css +8 -0
  114. package/{dist/tw/typography.css → tw/typography.rules.css} +0 -3
  115. package/{dist/tw → tw}/utilities/content.css +248 -108
  116. package/tw/utilities/index.css +11 -0
  117. package/tw/utilities/layout.css +210 -0
  118. package/tw/utilities/rules.css +8 -0
  119. package/dist/tw/aesthetics/sketch.css +0 -398
  120. package/dist/tw/components/button.css +0 -139
  121. package/dist/tw/components/index.css +0 -35
  122. package/dist/tw/components/surface.css +0 -184
  123. package/dist/tw/intent.css +0 -229
  124. package/dist/tw/native.css +0 -203
  125. package/dist/tw/palette.css +0 -460
  126. package/dist/tw/presentation.css +0 -68
  127. package/dist/tw/surface.css +0 -61
  128. package/dist/tw/utilities/index.css +0 -6
  129. package/dist/tw/utilities/layout.css +0 -144
  130. /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
@@ -67,17 +77,16 @@
67
77
  two presentations separate. A larger multiplier lands both on a fully
68
78
  opaque plate, leaving only the small intent mix to tell them apart.
69
79
 
70
- It reads `--ui-fill` rather than `--table-fill` because the question is
80
+ It reads `--ui-fill` rather than `--_table-fill` because the question is
71
81
  what the presentation wanted, not what the intent cap and an aesthetic's
72
82
  alpha left of it: a glass `.soft` table still wants its head plate, and
73
83
  neutral's 20% cap has no bearing on whether there is one. Reading the raw
74
84
  class value also keeps the expression a `calc()` over one `var()` rather
75
- than over `--table-fill`'s own `calc()`/`min()` chain, which matters for
76
- the reason `--table-rule-plated` below records. */
77
- --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
- --table-head-bg: color-mix(in oklab, var(--intent-color) var(--table-fill), var(--table-head-plate));
80
- --table-head-fg: color-mix(in oklab, var(--intent-contrast) var(--_on-fill), var(--intent-strong));
85
+ than over `--_table-fill`'s own `calc()`/`min()` chain, which matters for
86
+ the reason `--_table-rule-plated` below records. */
87
+ --_table-ground: min(100%, calc(var(--ui-fill, var(--_d-fill)) * 5));
88
+ --_table-head-bg: color-mix(in oklab, var(--intent-color) var(--_table-fill), var(--_table-head-plate));
89
+ --_table-head-fg: color-mix(in oklab, var(--intent-contrast) var(--_on-fill), var(--intent-strong));
81
90
  /* The table's own contrast ink, resolved here so a row with its own intent
82
91
  prints on the table's plate in the ink that plate takes: the row's
83
92
  `--intent-contrast` is chosen for the row's fill, which a row does not
@@ -101,25 +110,45 @@
101
110
  element, so nothing here fights `.soft` for `--ui-fill`. `.ghost` is left
102
111
  at box's default (`--_d-ground: transparent`), so it stays the fully bare
103
112
  boundaries-only table; `.soft` differs from it by keeping an opaque,
104
- untinted plate and a head label strip.
105
-
106
- `:not(.solid, .ghost)` rather than `.soft`: the published resting fill is
107
- `soft`, so a bare `<table class="data-table">` with no fill class at all is
108
- 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. */
109
151
  &:not(.solid, .ghost) {
110
- --table-fill: 0%;
111
- --table-head-plate: var(--table-ground-plate);
112
- --_fill-cap: 0%;
113
- --_d-ground: var(--table-ground-plate);
114
-
115
- /* A table is a pane of content, so under an aesthetic whose surfaces are
116
- glass it is glass too: the plate is the aesthetic's surface ground when
117
- one is named, and the blur behind it is the one surfaces take. Head and
118
- body share the plate, as they do on the page. Only this arm reads them:
119
- a `.solid` table is opaque, like every `.solid`, and a `.ghost` one is
120
- boundaries and type with nothing behind them to blur. */
121
- --table-ground-plate: var(--ui-surface-ground, var(--color-foreground));
122
-
123
152
  -webkit-backdrop-filter: var(--ui-backdrop, none);
124
153
  backdrop-filter: var(--ui-backdrop, none);
125
154
  }
@@ -137,8 +166,8 @@
137
166
  both. So `.ruled` writes 100% and gets rules everywhere, `.ruleless` writes
138
167
  0% and gets a table with no lines in it at all, and a table nobody classed
139
168
  sits between them. */
140
- --table-rule-amount: var(--ui-rule, 0%);
141
- --table-boundary-amount: var(--ui-rule, 100%);
169
+ --_table-rule-amount: var(--ui-rule, 0%);
170
+ --_table-boundary-amount: var(--ui-rule, 100%);
142
171
  /* What the rule is drawn in, which is not the same tone at every fill.
143
172
  `.ghost` and `.soft` keep `--intent-border` -- the slot an aesthetic
144
173
  restates through `--ui-ink` -- because at those fills the plate has not
@@ -148,24 +177,28 @@
148
177
  is busiest.
149
178
 
150
179
  The filled case draws in `currentColor` at a fifth instead: the ink the
151
- cell is already printing (`--table-head-fg` on a head, `box`'s own `--_fg`
180
+ cell is already printing (`--_table-head-fg` on a head, `box`'s own `--_fg`
152
181
  in the body), so it reads on a light plate and a dark one without a
153
182
  per-intent branch, and it lands *over* the plate because a translucent
154
183
  border composites onto the background the element already painted.
155
184
 
156
185
  Naming the ink rather than composing it also keeps this expression cheap,
157
186
  which is a requirement: every `th` and `td` on a page resolves it, and
158
- written as `color-mix(--table-head-fg 20%, --_bg)` it expands, through four
187
+ written as `color-mix(--_table-head-fg 20%, --_bg)` it expands, through four
159
188
  levels of substitution, into a `color-mix()` over two more `color-mix()`es
160
189
  over a `min()`/`max()`/`calc()` chain, which crashes WebKit's renderer on
161
190
  the routes that draw tables. `currentColor` and `transparent` are leaves
162
191
  the engine has already resolved, which takes the expression back down to
163
- something every engine can carry. `--table-row-hover` below is written the
192
+ something every engine can carry. `--_table-row-hover` below is written the
164
193
  same way, for the same reason. */
165
- --table-rule-plated: color-mix(in oklab, currentColor 20%, transparent);
166
- --table-rule-tone: color-mix(in oklab, var(--table-rule-plated) var(--ui-fill, var(--_d-fill)), var(--intent-border));
167
- --table-rule: color-mix(in oklab, var(--table-rule-tone) var(--table-rule-amount), transparent);
168
- --table-boundary: color-mix(in oklab, var(--table-rule-tone) var(--table-boundary-amount), transparent);
194
+ --_table-rule-plated: color-mix(in oklab, currentColor 20%, transparent);
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
+ );
200
+ --_table-rule: color-mix(in oklab, var(--_table-rule-tone) var(--_table-rule-amount), transparent);
201
+ --_table-boundary: color-mix(in oklab, var(--_table-rule-tone) var(--_table-boundary-amount), transparent);
169
202
  /* A hovered row is a step from the table it is in, not from a tone the page
170
203
  happens to own. A translucent step is relative at every fill, the way
171
204
  `box-hover` is: the row composites onto whatever plate the table drew, so a
@@ -183,9 +216,12 @@
183
216
  already carries and therefore always contrasts with it.
184
217
 
185
218
  Both ends are leaves, and the step lands on `transparent` rather than on
186
- `--_bg`, for the reason `--table-rule-plated` above gives. */
187
- --table-hover-tone: color-mix(in oklab, currentColor var(--ui-fill, var(--_d-fill)), var(--intent-hover));
188
- --table-row-hover: color-mix(in oklab, var(--table-hover-tone) 12%, transparent);
219
+ `--_bg`, for the reason `--_table-rule-plated` above gives. */
220
+ --_table-hover-tone: color-mix(in oklab, currentColor var(--ui-fill, var(--_d-fill)), var(--intent-hover));
221
+ /* The default hover. `--table-hover` is public and read where the row
222
+ paints, not here, so a value on any ancestor of the cells reaches them,
223
+ a `tbody` or row included; `transparent` turns the hover off. */
224
+ --_table-row-hover: color-mix(in oklab, var(--_table-hover-tone) 12%, transparent);
189
225
 
190
226
  border-spacing: 0;
191
227
 
@@ -251,11 +287,11 @@
251
287
  corner cells round it: a section's background ignores the cells' corners. */
252
288
  & thead,
253
289
  & tfoot {
254
- color: var(--table-head-fg);
290
+ color: var(--_table-head-fg);
255
291
  }
256
292
 
257
293
  & :is(thead, tfoot) > tr > * {
258
- background-color: var(--table-head-bg);
294
+ background-color: var(--_table-head-bg);
259
295
  }
260
296
 
261
297
  /* Every cell carries the hairline; what varies is whether it is painted. The
@@ -275,13 +311,20 @@
275
311
  `th` centers unless its parent names an alignment, and `start` is the
276
312
  initial value, which counts as naming none. */
277
313
  --_table-rule-width: var(--ui-border-width, var(--border-width));
278
- & th,
279
- & td {
280
- @apply px-4 py-3 text-start align-top;
314
+ /* In `:where()`: these are the cells' defaults, and at the 0-1-1 the plain
315
+ selector carried, `p-0` or `text-center` on a cell lost to them.
316
+ `--table-cell-padding` is public and read with the default, `py-3 px-4`,
317
+ as its fallback. A physical `padding`, because one variable has to carry
318
+ both axes: in a vertical writing mode its two values stay top-bottom and
319
+ left-right rather than following the block and inline axes. */
320
+ :where(& :is(th, td)) {
321
+ @apply text-start align-top;
322
+
323
+ padding: var(--table-cell-padding, calc(var(--spacing) * 3) calc(var(--spacing) * 4));
281
324
 
282
325
  border-bottom-style: var(--ui-rule-style, var(--ui-line-style, solid));
283
326
  border-bottom-width: var(--_table-rule-width);
284
- border-color: var(--table-rule);
327
+ border-color: var(--_table-rule);
285
328
  }
286
329
 
287
330
  /* The two lines that separate the parts of a table from each other: under the
@@ -294,27 +337,16 @@
294
337
  because with rules off that row paints nothing. Stated on the foot, the
295
338
  boundary holds at every setting. */
296
339
  & thead tr:last-child > * {
297
- border-bottom-color: var(--table-boundary);
340
+ border-bottom-color: var(--_table-boundary);
298
341
  }
299
342
 
300
343
  & tfoot tr:first-child > * {
301
344
  border-top-style: var(--ui-rule-style, var(--ui-line-style, solid));
302
345
  border-top-width: var(--_table-rule-width);
303
- border-top-color: var(--table-boundary);
304
- }
305
-
306
- /* Rules between body rows, on request -- and every line off, on the opposite
307
- request. `.ruleless` is declared second so it wins the nonsense combination
308
- rather than leaving it to whichever class the author typed first. */
309
- &.ruled {
310
- --ui-rule: 100%;
346
+ border-top-color: var(--_table-boundary);
311
347
  }
312
348
 
313
- &.ruleless {
314
- --ui-rule: 0%;
315
- }
316
-
317
- & th {
349
+ :where(& th) {
318
350
  @apply text-label font-semibold;
319
351
  }
320
352
 
@@ -333,29 +365,30 @@
333
365
  the same values, but a `color` declared on every row would override a
334
366
  colour a consumer set on the table. */
335
367
  & tbody tr:is(.neutral, .primary, .secondary, .success, .warning, .destructive, .info) {
336
- --table-rule-tone: color-mix(
368
+ --_table-rule-tone: color-mix(
337
369
  in oklab,
338
- var(--table-rule-plated) var(--ui-fill, var(--_d-fill)),
370
+ var(--_table-rule-plated) var(--ui-fill, var(--_d-fill)),
339
371
  var(--intent-border)
340
372
  );
341
- --table-rule: color-mix(in oklab, var(--table-rule-tone) var(--table-rule-amount), transparent);
342
- --table-hover-tone: color-mix(in oklab, currentColor var(--ui-fill, var(--_d-fill)), var(--intent-hover));
343
- --table-row-hover: color-mix(in oklab, var(--table-hover-tone) 12%, transparent);
344
-
345
- color: color-mix(in oklab, var(--_table-contrast) var(--_on-fill), var(--intent-strong));
346
- }
347
-
348
- /* A neutral solid table caps its fill at 20%, so its plate is a light grey
349
- and the ramp keeps `--_on-fill` low: the row's ink is then mostly the
350
- row's `--intent-strong`, which is chosen against the row's own subtle
351
- ground rather than a grey one, and success, warning and destructive fall
352
- under 4.5:1 on it. Those rows print the table's own ink, `--_fg`,
353
- inherited already resolved against the table, and keep their intent in
354
- the rule and the hover. */
355
- &.solid:not(.primary, .secondary, .success, .warning, .destructive, .info)
356
- tbody
357
- tr:is(.neutral, .primary, .secondary, .success, .warning, .destructive, .info) {
358
- color: var(--_fg);
373
+ --_table-rule: color-mix(in oklab, var(--_table-rule-tone) var(--_table-rule-amount), transparent);
374
+ --_table-hover-tone: color-mix(in oklab, currentColor var(--ui-fill, var(--_d-fill)), var(--intent-hover));
375
+ --_table-row-hover: color-mix(in oklab, var(--_table-hover-tone) 12%, transparent);
376
+
377
+ /* A neutral solid table caps its fill at 20%, so its plate is a light grey
378
+ and the ramp keeps `--_on-fill` low: the row's ink is then mostly the
379
+ row's `--intent-strong`, which is chosen against the row's own subtle
380
+ ground rather than a grey one, and success, warning and destructive fall
381
+ under 4.5:1 on it. Those rows print the table's own ink, `--_fg`,
382
+ inherited already resolved against the table, and keep their intent in
383
+ the rule and the hover.
384
+ `--_plate` is the table's too: 100% where the intent's cap stopped its
385
+ fill, read rather than a `.solid` class on the table, which a
386
+ container's `.solid` does not put there. */
387
+ color: color-mix(
388
+ in oklab,
389
+ var(--_fg) var(--_plate, 0%),
390
+ color-mix(in oklab, var(--_table-contrast) var(--_on-fill), var(--intent-strong))
391
+ );
359
392
  }
360
393
 
361
394
  & tbody tr > * {
@@ -363,9 +396,12 @@
363
396
  }
364
397
 
365
398
  /* On the cells, for the reason the head's plate is: a hovered last row keeps
366
- the table's bottom corners. */
367
- & tbody tr:hover > * {
368
- background-color: var(--table-row-hover);
399
+ the table's bottom corners. Behind the query `box-hover` is, so a tapped
400
+ row does not stay tinted. */
401
+ @media (hover: hover) {
402
+ & tbody tr:hover > * {
403
+ background-color: var(--table-hover, var(--_table-row-hover));
404
+ }
369
405
  }
370
406
 
371
407
  /* The rule comes off the row that is visually last, which is not always the
@@ -413,9 +449,23 @@
413
449
  separate from, so it alone drops the extra ink. The ground itself is not
414
450
  moved the way `.panel`'s is: `--intent-subtle` already resolves to
415
451
  `--color-surface` for neutral, a real, visible step past the page on its
416
- own, so only the wash on top of it goes. `:not(.solid)` so an explicit
417
- `.solid` chip is never clamped to a cap it did not ask for. */
452
+ own, so only the wash on top of it goes. The cap is stepped from the
453
+ resolved fill, so a full fill -- the chip's own `.solid` or a container's --
454
+ is never clamped to a cap it did not ask for. */
418
455
  @utility kbd {
456
+ /* The neutral reset, carried so `@apply kbd` gets it, as `btn` carries its own. */
457
+ @layer families {
458
+ :where(&:not(.kbd, .primary, .secondary, .success, .warning, .destructive, .info)) {
459
+ --intent-color: var(--color-text);
460
+ --intent-contrast: var(--color-text-contrast);
461
+ --intent-hover: var(--color-text-hover);
462
+ --intent-strong: var(--color-text-strong);
463
+ --intent-subtle: var(--color-surface);
464
+ --intent-border: var(--ui-ink, var(--color-border));
465
+ --intent-fill-max: 20%;
466
+ }
467
+ }
468
+
419
469
  @apply box inline-flex items-center px-1.5 py-0.5 font-mono text-xs font-medium;
420
470
 
421
471
  --_d-fill: 12%;
@@ -424,6 +474,10 @@
424
474
  --_d-elevation: 0;
425
475
  --_d-ground: var(--intent-subtle);
426
476
 
477
+ /* A ground of its own, so no ink from above passes through, as on `surface`. */
478
+ --_held-ink: var(--_fg-own);
479
+ --_held-on: var(--_own-hold);
480
+
427
481
  /* A small default remains when no aesthetic asks for a complete chip shape. */
428
482
  border-radius: var(
429
483
  --ui-radius-tight,
@@ -431,13 +485,35 @@
431
485
  );
432
486
  clip-path: var(--ui-clip-tight, var(--ui-clip, none));
433
487
 
434
- &:not(.primary, .secondary, .success, .warning, .destructive, .info, .solid) {
435
- --_fill-cap: 0%;
436
- }
488
+ /* The neutral wash goes, and only that: a neutral intent (its cap is below
489
+ 100%) asking for less than its cap. Stepped from the resolved fill, as
490
+ `surface`'s `--_quiet` is, not keyed on a class here: presentation
491
+ cascades, and keyed on the class a container's `.solid` reached the ink
492
+ and the line while the plate stayed capped. */
493
+ --_fill-cap: calc(
494
+ 100% -
495
+ min(
496
+ clamp(0%, calc((100% - var(--intent-fill-max, 100%)) * 1000), 100%),
497
+ clamp(0%, calc((var(--intent-fill-max, 100%) - var(--ui-fill, var(--_d-fill))) * 1000), 100%)
498
+ )
499
+ );
437
500
  }
438
501
 
439
502
  /* The same plate with no line around it. */
440
503
  @utility code {
504
+ /* The neutral reset, carried so `@apply code` gets it, as `btn` carries its own. */
505
+ @layer families {
506
+ :where(&:not(.code, .primary, .secondary, .success, .warning, .destructive, .info)) {
507
+ --intent-color: var(--color-text);
508
+ --intent-contrast: var(--color-text-contrast);
509
+ --intent-hover: var(--color-text-hover);
510
+ --intent-strong: var(--color-text-strong);
511
+ --intent-subtle: var(--color-surface);
512
+ --intent-border: var(--ui-ink, var(--color-border));
513
+ --intent-fill-max: 20%;
514
+ }
515
+ }
516
+
441
517
  @apply box px-1.25 py-0.75 font-mono;
442
518
 
443
519
  --_d-fill: 12%;
@@ -446,19 +522,45 @@
446
522
  --_d-elevation: 0;
447
523
  --_d-ground: var(--intent-subtle);
448
524
 
525
+ /* A ground of its own, so no ink from above passes through, as on `surface`. */
526
+ --_held-ink: var(--_fg-own);
527
+ --_held-on: var(--_own-hold);
528
+
449
529
  border-radius: var(
450
530
  --ui-radius-tight,
451
531
  min(var(--ui-radius, var(--ui-corner, var(--radius-small))), var(--radius-small))
452
532
  );
453
533
  clip-path: var(--ui-clip-tight, var(--ui-clip, none));
454
534
 
455
- &:not(.primary, .secondary, .success, .warning, .destructive, .info, .solid) {
456
- --_fill-cap: 0%;
457
- }
535
+ /* The neutral wash goes, and only that: a neutral intent (its cap is below
536
+ 100%) asking for less than its cap. Stepped from the resolved fill, as
537
+ `surface`'s `--_quiet` is, not keyed on a class here: presentation
538
+ cascades, and keyed on the class a container's `.solid` reached the ink
539
+ and the line while the plate stayed capped. */
540
+ --_fill-cap: calc(
541
+ 100% -
542
+ min(
543
+ clamp(0%, calc((100% - var(--intent-fill-max, 100%)) * 1000), 100%),
544
+ clamp(0%, calc((var(--intent-fill-max, 100%) - var(--ui-fill, var(--_d-fill))) * 1000), 100%)
545
+ )
546
+ );
458
547
  }
459
548
 
460
549
  /* And at block scale, where the radius is a control's rather than a chip's. */
461
550
  @utility pre {
551
+ /* The neutral reset, carried so `@apply pre` gets it, as `btn` carries its own. */
552
+ @layer families {
553
+ :where(&:not(.pre, .primary, .secondary, .success, .warning, .destructive, .info)) {
554
+ --intent-color: var(--color-text);
555
+ --intent-contrast: var(--color-text-contrast);
556
+ --intent-hover: var(--color-text-hover);
557
+ --intent-strong: var(--color-text-strong);
558
+ --intent-subtle: var(--color-surface);
559
+ --intent-border: var(--ui-ink, var(--color-border));
560
+ --intent-fill-max: 20%;
561
+ }
562
+ }
563
+
462
564
  @apply box block overflow-x-auto px-2.5 py-1.5 font-mono;
463
565
 
464
566
  --_d-fill: 12%;
@@ -467,9 +569,22 @@
467
569
  --_d-elevation: 0;
468
570
  --_d-ground: var(--intent-subtle);
469
571
 
470
- &:not(.primary, .secondary, .success, .warning, .destructive, .info, .solid) {
471
- --_fill-cap: 0%;
472
- }
572
+ /* A ground of its own, so no ink from above passes through, as on `surface`. */
573
+ --_held-ink: var(--_fg-own);
574
+ --_held-on: var(--_own-hold);
575
+
576
+ /* The neutral wash goes, and only that: a neutral intent (its cap is below
577
+ 100%) asking for less than its cap. Stepped from the resolved fill, as
578
+ `surface`'s `--_quiet` is, not keyed on a class here: presentation
579
+ cascades, and keyed on the class a container's `.solid` reached the ink
580
+ and the line while the plate stayed capped. */
581
+ --_fill-cap: calc(
582
+ 100% -
583
+ min(
584
+ clamp(0%, calc((100% - var(--intent-fill-max, 100%)) * 1000), 100%),
585
+ clamp(0%, calc((var(--intent-fill-max, 100%) - var(--ui-fill, var(--_d-fill))) * 1000), 100%)
586
+ )
587
+ );
473
588
  }
474
589
 
475
590
  /* Typography wearing a component's name, so it composes none of `box`: `box`
@@ -487,23 +602,42 @@
487
602
  what makes the bar read as a bar at the default hairline: doubling 1px gives
488
603
  2px, and a quotation mark needs more presence than a table rule. */
489
604
  @utility quote {
605
+ /* The neutral reset, carried so `@apply quote` gets it, as `btn` carries its own. */
606
+ @layer families {
607
+ :where(&:not(.quote, .primary, .secondary, .success, .warning, .destructive, .info)) {
608
+ --intent-color: var(--color-text);
609
+ --intent-contrast: var(--color-text-contrast);
610
+ --intent-hover: var(--color-text-hover);
611
+ --intent-strong: var(--color-text-strong);
612
+ --intent-subtle: var(--color-surface);
613
+ --intent-border: var(--ui-ink, var(--color-border));
614
+ --intent-fill-max: 20%;
615
+ }
616
+ }
617
+
490
618
  --_d-fill: 0%;
491
619
  --_d-fg-on-fill: 0%;
492
620
  --_d-border: 100%;
493
621
  --_d-elevation: 0;
622
+
623
+ /* Printed as drawn, as `box` is. A browser leaves backgrounds off paper by
624
+ default, and a filled quotation prints its text in the contrast colour:
625
+ white on nothing. */
626
+ print-color-adjust: exact;
627
+
494
628
  /* Presentation capped by the intent, the same `--_capped` `box` composes, kept
495
- separate from `--quote-fill` because the ink ramp below reads it before
629
+ separate from `--_quote-fill` because the ink ramp below reads it before
496
630
  `--ui-bg-alpha` thins it. */
497
- --quote-capped: min(var(--ui-fill, var(--_d-fill)), var(--intent-fill-max, 100%));
498
- --quote-fill: calc(var(--quote-capped) * var(--ui-bg-alpha, 1));
631
+ --_quote-capped: min(var(--ui-fill, var(--_d-fill)), var(--intent-fill-max, 100%));
632
+ --_quote-fill: calc(var(--_quote-capped) * var(--ui-bg-alpha, 1));
499
633
  /* `box`'s `--_plate`: a fill the intent's cap stopped short rests on the page
500
634
  background, so a neutral `.solid` quotation is a plate rather than a tint
501
635
  of whatever is behind it. */
502
- --quote-plate: clamp(0%, calc((var(--ui-fill, var(--_d-fill)) - var(--quote-capped)) * 1000), 100%);
503
- --quote-bg: color-mix(
636
+ --_quote-plate: clamp(0%, calc((var(--ui-fill, var(--_d-fill)) - var(--_quote-capped)) * 1000), 100%);
637
+ --_quote-bg: color-mix(
504
638
  in oklab,
505
- var(--intent-color) var(--quote-fill),
506
- color-mix(in oklab, var(--color-background) var(--quote-plate), transparent)
639
+ var(--intent-color) var(--_quote-fill),
640
+ color-mix(in oklab, var(--color-background) var(--_quote-plate), transparent)
507
641
  );
508
642
  /* The same edge blend `box` draws, in the same order and for the same reason:
509
643
  the blend is the line's colour, and how much line there is comes after it.
@@ -517,15 +651,21 @@
517
651
  A quotation draws its own edge instead of taking `box`'s, so an edge change
518
652
  is made in `box`, `.quote`, and `.progress`. Recorded in `registry.json`
519
653
  beside the decision to compose none of `box`. */
520
- --quote-line: color-mix(in oklab, transparent var(--ui-fill, var(--_d-fill)), var(--intent-border));
521
- --quote-edge: color-mix(in oklab, var(--quote-line) var(--ui-border, var(--_d-border)), transparent);
522
- --quote-width: calc(var(--ui-border-width, var(--border-width)) * 2 + 1px);
654
+ --_quote-line: color-mix(
655
+ in oklab,
656
+ transparent var(--ui-fill, var(--_d-fill)),
657
+ var(--intent-border, var(--ui-ink, var(--color-border)))
658
+ );
659
+ --_quote-edge: color-mix(in oklab, var(--_quote-line) var(--ui-border, var(--_d-border)), transparent);
660
+ /* Public, read with its default, so a value set on any ancestor reaches the
661
+ bar. */
662
+ --_quote-width: var(--quote-width, calc(var(--ui-border-width, var(--border-width)) * 2 + 1px));
523
663
 
524
664
  @apply my-0 border-s py-2 ps-4 italic;
525
665
 
526
- background-color: var(--quote-bg);
527
- border-inline-start-color: var(--quote-edge);
528
- border-inline-start-width: var(--quote-width);
666
+ background-color: var(--_quote-bg);
667
+ border-inline-start-color: var(--_quote-edge);
668
+ border-inline-start-width: var(--_quote-width);
529
669
  /* The ink is gated by the plate, not tied to the fill: `--ui-fg-on-fill` is
530
670
  the ceiling, and the contrast tone only ramps in past a half fill, reaching
531
671
  full at a full fill. Below that the ink stays `--intent-strong`, the tone
@@ -535,7 +675,7 @@
535
675
  color: color-mix(
536
676
  in oklab,
537
677
  var(--intent-contrast)
538
- min(var(--ui-fg-on-fill, var(--_d-fg-on-fill)), max(0%, calc(var(--quote-capped) * 2 - 100%))),
678
+ min(var(--ui-fg-on-fill, var(--_d-fg-on-fill)), max(0%, calc(var(--_quote-capped) * 2 - 100%))),
539
679
  var(--intent-strong)
540
680
  );
541
681
 
@@ -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";