@codenhub/styles 0.5.0 → 0.7.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 (119) hide show
  1. package/NOTICE +18 -16
  2. package/README.es.md +61 -0
  3. package/README.md +27 -22
  4. package/README.pt-BR.md +61 -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/components.css +2 -2
  13. package/dist/index.css +2 -2
  14. package/dist/native.css +2 -2
  15. package/dist/palette.css +1 -1
  16. package/dist/theme.css +2 -2
  17. package/dist/tw/aesthetics/chunky-tile.css +120 -62
  18. package/dist/tw/aesthetics/cyber.css +102 -40
  19. package/dist/tw/aesthetics/glass.css +371 -19
  20. package/dist/tw/aesthetics/index.css +2 -1
  21. package/dist/tw/aesthetics/neobrutalism.css +121 -33
  22. package/dist/tw/aesthetics/pixel.css +111 -16
  23. package/dist/tw/aesthetics/sketch.css +100 -28
  24. package/dist/tw/box.css +69 -28
  25. package/dist/tw/components/button.css +35 -14
  26. package/dist/tw/components/feedback.css +61 -58
  27. package/dist/tw/components/form.css +255 -142
  28. package/dist/tw/components/index.css +4 -9
  29. package/dist/tw/components/surface.css +80 -68
  30. package/dist/tw/components/tooltip.css +75 -41
  31. package/dist/tw/elevation.css +14 -22
  32. package/dist/tw/index.css +15 -19
  33. package/dist/tw/intent.css +35 -27
  34. package/dist/tw/native.css +22 -12
  35. package/dist/tw/palette.css +14 -11
  36. package/dist/tw/presentation.css +16 -30
  37. package/dist/tw/reset.css +36 -43
  38. package/dist/tw/surface.css +11 -12
  39. package/dist/tw/theme.css +87 -103
  40. package/dist/tw/utilities/content.css +311 -200
  41. package/dist/tw/utilities/layout.css +41 -37
  42. package/docs/accessibility.md +104 -58
  43. package/docs/changelog/0.1.1.md +1 -0
  44. package/docs/changelog/0.2.0.md +1 -0
  45. package/docs/changelog/0.3.0.md +1 -0
  46. package/docs/changelog/0.4.0.md +1 -0
  47. package/docs/changelog/0.5.0.md +8 -3
  48. package/docs/changelog/0.6.0.md +53 -0
  49. package/docs/changelog/0.7.0.md +50 -0
  50. package/docs/changelog/index.md +2 -0
  51. package/docs/concepts.md +188 -46
  52. package/docs/es/accessibility.md +150 -0
  53. package/docs/es/concepts.md +212 -0
  54. package/docs/es/index.md +104 -0
  55. package/docs/es/integrating/astro.md +35 -0
  56. package/docs/es/integrating/index.md +47 -0
  57. package/docs/es/integrating/nextjs.md +67 -0
  58. package/docs/es/integrating/svelte.md +45 -0
  59. package/docs/es/integrating/tailwind.md +78 -0
  60. package/docs/es/integrating/vue.md +31 -0
  61. package/docs/es/integrating/without-a-bundler.md +115 -0
  62. package/docs/es/setup.md +180 -0
  63. package/docs/es/usage/aesthetics.md +276 -0
  64. package/docs/es/usage/buttons.md +131 -0
  65. package/docs/es/usage/composing.md +164 -0
  66. package/docs/es/usage/content-and-layout.md +215 -0
  67. package/docs/es/usage/customizing.md +298 -0
  68. package/docs/es/usage/feedback.md +153 -0
  69. package/docs/es/usage/forms.md +231 -0
  70. package/docs/es/usage/index.md +29 -0
  71. package/docs/es/usage/solo.md +84 -0
  72. package/docs/es/usage/theming.md +296 -0
  73. package/docs/es/usage/tooltips.md +127 -0
  74. package/docs/index.md +89 -21
  75. package/docs/integrating/astro.md +3 -4
  76. package/docs/integrating/index.md +18 -13
  77. package/docs/integrating/nextjs.md +4 -5
  78. package/docs/integrating/svelte.md +5 -4
  79. package/docs/integrating/tailwind.md +29 -8
  80. package/docs/integrating/vue.md +3 -4
  81. package/docs/integrating/without-a-bundler.md +114 -0
  82. package/docs/pt-BR/accessibility.md +150 -0
  83. package/docs/pt-BR/concepts.md +212 -0
  84. package/docs/pt-BR/index.md +104 -0
  85. package/docs/pt-BR/integrating/astro.md +35 -0
  86. package/docs/pt-BR/integrating/index.md +47 -0
  87. package/docs/pt-BR/integrating/nextjs.md +67 -0
  88. package/docs/pt-BR/integrating/svelte.md +45 -0
  89. package/docs/pt-BR/integrating/tailwind.md +78 -0
  90. package/docs/pt-BR/integrating/vue.md +31 -0
  91. package/docs/pt-BR/integrating/without-a-bundler.md +115 -0
  92. package/docs/pt-BR/setup.md +180 -0
  93. package/docs/pt-BR/usage/aesthetics.md +276 -0
  94. package/docs/pt-BR/usage/buttons.md +131 -0
  95. package/docs/pt-BR/usage/composing.md +164 -0
  96. package/docs/pt-BR/usage/content-and-layout.md +215 -0
  97. package/docs/pt-BR/usage/customizing.md +298 -0
  98. package/docs/pt-BR/usage/feedback.md +153 -0
  99. package/docs/pt-BR/usage/forms.md +231 -0
  100. package/docs/pt-BR/usage/index.md +29 -0
  101. package/docs/pt-BR/usage/solo.md +84 -0
  102. package/docs/pt-BR/usage/theming.md +296 -0
  103. package/docs/pt-BR/usage/tooltips.md +127 -0
  104. package/docs/setup.md +134 -91
  105. package/docs/usage/aesthetics.md +102 -65
  106. package/docs/usage/buttons.md +108 -40
  107. package/docs/usage/composing.md +105 -92
  108. package/docs/usage/content-and-layout.md +159 -127
  109. package/docs/usage/customizing.md +112 -43
  110. package/docs/usage/feedback.md +135 -70
  111. package/docs/usage/forms.md +184 -99
  112. package/docs/usage/index.md +18 -14
  113. package/docs/usage/solo.md +83 -0
  114. package/docs/usage/theming.md +260 -119
  115. package/docs/usage/tooltips.md +103 -42
  116. package/llms-full.txt +2465 -1272
  117. package/llms.txt +25 -17
  118. package/package.json +28 -35
  119. package/dist/tw/components/loader.css +0 -117
@@ -1,12 +1,11 @@
1
1
  /* Glass: translucent surfaces over a blurred backdrop with a hairline highlight
2
- edge. It needs something behind it to blur -- on a flat page background it is a
3
- translucent panel and nothing more.
2
+ edge. It needs something behind it to blur -- on a flat page background it is
3
+ a translucent panel and nothing more.
4
4
 
5
- Tier 1 only. The scope that used to take a hand-written selector list is now a
6
- token: only `surface` resolves `--ui-backdrop`, so blur reaches cards,
7
- panels, alerts and tooltips while controls stay solid. An active blur costs a
8
- compositing layer apiece, and one under every control of a dense cluster reads
9
- as noise. */
5
+ Tier 1 only: the scope is a token. Only `surface` resolves `--ui-backdrop`,
6
+ so blur reaches cards, panels, alerts and tooltips while controls stay solid.
7
+ An active blur costs a compositing layer apiece, and one under every control
8
+ of a dense cluster reads as noise. */
10
9
 
11
10
  /* The layer map, stated before anything uses a layer -- see `theme.css`. The
12
11
  aesthetic's token class sits in `components`, below every utility, so a
@@ -40,8 +39,19 @@
40
39
  on a field's 60% resting line, in light. See
41
40
  docs/internal/model.md#presentation. */
42
41
  --ui-control-ink: light-dark(rgb(0 0 0 / 0.55), rgb(255 255 255 / 0.55));
42
+ /* Nor is a line drawn on a plate: the hairline vanishes on a table's light
43
+ plate, and its default, `.ruled`, and `.ruleless` forms would render as
44
+ one. A table's frame and rules take the theme's ink at a fifth,
45
+ translucent for the same reason the control line is. */
46
+ --ui-rule-ink: light-dark(rgb(0 0 0 / 0.2), rgb(255 255 255 / 0.2));
43
47
 
44
- --ui-bg-alpha: 0.8;
48
+ /* The glass is the ground, not the fill. Thinning fills with
49
+ `--ui-bg-alpha` would make how much colour a presentation asked for
50
+ depend on the aesthetic, which is presentation's question (R1): a
51
+ `.solid` button, a checked toggle, and a `.solid` card stay opaque.
52
+ Translucency lives in `--ui-surface-ground` below, the plate a partial
53
+ fill is painted over. */
54
+ --ui-bg-alpha: 1;
45
55
  --ui-backdrop: blur(14px) saturate(1.6);
46
56
  --ui-surface-image: initial;
47
57
 
@@ -54,6 +64,15 @@
54
64
  --ui-shadow-y: 0px;
55
65
  --ui-shadow-blur: 0px;
56
66
  --ui-shadow-spread: 0px;
67
+ --ui-shadow-inset: initial;
68
+ --ui-shadow-ink: initial;
69
+ --ui-active-shadow-x: initial;
70
+ --ui-active-shadow-y: initial;
71
+ /* The theme's depth colour, restated: the surface shadow below resolves it
72
+ here, on the region, so inside chunky tile it inherited opaque black and
73
+ cast a black slab under every card, and inside cyber it inherited
74
+ transparent and cast none. */
75
+ --elevation-color: light-dark(rgb(15 23 42 / 0.08), rgb(0 0 0 / 0.55));
57
76
  /* No halo, cleared so a region nested inside another
58
77
  aesthetic does not inherit one. */
59
78
  --ui-halo-blur: initial;
@@ -88,8 +107,14 @@
88
107
  /* Nothing here draws its edge as a ring, and an aesthetic nested inside pixel
89
108
  has to say so rather than inherit it. */
90
109
  --ui-shadow-edge: initial;
110
+ /* Nothing clips, so the global focus outline shows; pixel's inset focus
111
+ layer is cleared rather than inherited. */
112
+ --ui-focus-inset: initial;
91
113
  --ui-hover-transform: none;
92
- --ui-active-transform: none;
114
+ /* The base press, stated because every token here is: the depth in this
115
+ aesthetic is the blur, so a control has no slab to press into, and a pane
116
+ that gives slightly under the pointer is the one answer a press gets. */
117
+ --ui-active-transform: scale(0.97);
93
118
  --ui-active-translate-x: initial;
94
119
  --ui-active-translate-y: initial;
95
120
 
@@ -105,11 +130,212 @@
105
130
  surface rather than a softer blur. */
106
131
  @media (prefers-reduced-transparency: reduce) {
107
132
  .glass {
108
- --ui-bg-alpha: 1;
109
133
  --ui-backdrop: none;
110
134
  --ui-surface-ground: var(--color-background);
111
135
  }
112
136
  }
137
+
138
+ @media (prefers-reduced-motion: reduce) {
139
+ .glass {
140
+ --ui-active-transform: none;
141
+ }
142
+ }
143
+
144
+ /* Liquid glass: a modifier of glass, the way `.sketch-rounded` is of sketch.
145
+ Continuous corners, a bright rim where light catches the pane, a lighter
146
+ blur, and -- in engines that render an SVG filter in `backdrop-filter`,
147
+ Chromium today -- a lens that bends the backdrop at the edges. On the region
148
+ beside `.glass`, or on one surface inside a glass region.
149
+
150
+ Only the corners apply on their own. The ink, ground, and blur are glass
151
+ material, so they are set below only where `.glass` is in scope: a lone
152
+ `.glass-liquid` would otherwise make every surface translucent with nothing
153
+ to blur and every field's line a near-invisible hairline.
154
+
155
+ The corner knobs are glass's own, with larger defaults: a continuous corner
156
+ reads smaller than a round one of the same radius. Where `corner-shape` is
157
+ not drawn the corners stay round.
158
+
159
+ Not on a solo element: `.glass-solo.glass-liquid` paints its own corner
160
+ below, and a token declared on it would inherit into whatever it holds --
161
+ a `.btn` inside a solo toast took liquid's corners on a page with no glass
162
+ in scope. Every scope below skips a solo element for the same reason. */
163
+ .glass-liquid:not(.glass-solo) {
164
+ --ui-corner: var(--glass-radius, 1rem);
165
+ --ui-corner-surface: var(--glass-radius-surface, 1.5rem);
166
+ --ui-corner-shape: squircle;
167
+ }
168
+
169
+ /* The layers' values, on a solo element too: it draws the same two. */
170
+ .glass-liquid {
171
+ --_glass-liquid-blur: blur(2px) saturate(1.8);
172
+
173
+ /* The lens is its own layer rather than a second function in the blur's
174
+ list: Firefox and WebKit accept `url()` there and draw nothing for it, and
175
+ a filter list with an unusable reference is dropped whole -- the blur with
176
+ it. Kept apart, an engine that ignores the lens still blurs.
177
+
178
+ The displacement map is built inside the filter from the box itself:
179
+ erode a flood of the filter region by 8px, blur it into a ramp, and take
180
+ the ramp's slope across x and y as the two displacement channels. Every
181
+ length is absolute, so the band is the same width on a tooltip and on a
182
+ page-wide card; no length is a percentage of the box. The blur is wider
183
+ than the erosion so the ramp's corners round off -- otherwise the square
184
+ core shows through as loops at every corner. A filter cannot read a custom
185
+ property, so the band and the strength are fixed, not knobs. */
186
+ --_glass-liquid-lens: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg'><filter id='lens' x='0' y='0' width='100%' height='100%' color-interpolation-filters='sRGB'><feFlood flood-color='white'/><feMorphology operator='erode' radius='8'/><feGaussianBlur stdDeviation='10' result='ramp'/><feOffset in='ramp' dx='-3' result='l'/><feOffset in='ramp' dx='3' result='r'/><feComposite in='l' in2='r' operator='arithmetic' k2='2' k3='-2' k4='0.5' result='dx'/><feOffset in='ramp' dy='-3' result='u'/><feOffset in='ramp' dy='3' result='d'/><feComposite in='u' in2='d' operator='arithmetic' k2='2' k3='-2' k4='0.5' result='dy'/><feColorMatrix in='dx' values='0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1' result='x'/><feColorMatrix in='dy' values='0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1'/><feComposite in='x' operator='arithmetic' k2='1' k3='1'/><feDisplacementMap in='SourceGraphic' scale='110' xChannelSelector='R' yChannelSelector='G'/></filter></svg>#lens");
187
+
188
+ /* The same lens at a control's size. The pane's band is 8px of erosion
189
+ under a 10px blur, which is most of a 40px button's height: the two
190
+ sides' ramps meet in the middle and the label sits in a loop. A third of
191
+ each length keeps a clear centre for the label. */
192
+ --_glass-liquid-lens-control: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg'><filter id='lens' x='0' y='0' width='100%' height='100%' color-interpolation-filters='sRGB'><feFlood flood-color='white'/><feMorphology operator='erode' radius='3'/><feGaussianBlur stdDeviation='3.5' result='ramp'/><feOffset in='ramp' dx='-1' result='l'/><feOffset in='ramp' dx='1' result='r'/><feComposite in='l' in2='r' operator='arithmetic' k2='2' k3='-2' k4='0.5' result='dx'/><feOffset in='ramp' dy='-1' result='u'/><feOffset in='ramp' dy='1' result='d'/><feComposite in='u' in2='d' operator='arithmetic' k2='2' k3='-2' k4='0.5' result='dy'/><feColorMatrix in='dx' values='0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1' result='x'/><feColorMatrix in='dy' values='0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1'/><feComposite in='x' operator='arithmetic' k2='1' k3='1'/><feDisplacementMap in='SourceGraphic' scale='40' xChannelSelector='R' yChannelSelector='G'/></filter></svg>#lens");
193
+
194
+ /* The rim: light caught on the top-left edge and, fainter, the bottom-right,
195
+ over a soft inner glow. Drawn on the lens layer, the topmost, because a
196
+ rim drawn on the surface itself would sit under the layers and blur. */
197
+ --_glass-liquid-rim:
198
+ inset 1px 1px 0 light-dark(rgb(255 255 255 / 0.75), rgb(255 255 255 / 0.35)),
199
+ inset -1px -1px 0 light-dark(rgb(255 255 255 / 0.35), rgb(255 255 255 / 0.12)),
200
+ inset 0 0 18px light-dark(rgb(255 255 255 / 0.22), rgb(255 255 255 / 0.05));
201
+ --_glass-liquid-sheen: linear-gradient(
202
+ 160deg,
203
+ light-dark(rgb(255 255 255 / 0.22), rgb(255 255 255 / 0.06)),
204
+ transparent 45%
205
+ );
206
+ }
207
+
208
+ /* The glass material the modifier changes, where glass is in scope: on the
209
+ same element, or on a surface inside a glass region.
210
+
211
+ The backdrop is liquid's lighter blur, for whatever frosts under glass and
212
+ takes no layers below: a bare `<table>` under `/native`, a consumer's own
213
+ `@apply surface`. The layers' selector list cannot name those, and with
214
+ the backdrop cleared here they kept the clearer ground with nothing
215
+ blurred behind it. A surface that does take the layers clears its own. */
216
+ .glass.glass-liquid:not(.glass-solo),
217
+ .glass .glass-liquid:not(.glass-solo) {
218
+ --ui-ink: light-dark(rgb(255 255 255 / 0.45), rgb(255 255 255 / 0.16));
219
+ --ui-surface-ground: color-mix(in srgb, var(--color-background) 30%, transparent);
220
+ --ui-surface-image: initial;
221
+ --ui-backdrop: var(--_glass-liquid-blur);
222
+ }
223
+
224
+ @media (prefers-reduced-transparency: reduce) {
225
+ .glass.glass-liquid:not(.glass-solo),
226
+ .glass .glass-liquid:not(.glass-solo) {
227
+ --ui-surface-ground: var(--color-background);
228
+ }
229
+
230
+ .glass-liquid {
231
+ --_glass-liquid-blur: none;
232
+ --_glass-liquid-lens: none;
233
+ --_glass-liquid-lens-control: none;
234
+ }
235
+ }
236
+
237
+ /* The layers: a treatment no token reaches, so a selector list glass owns and
238
+ the registry records. Two pseudo-elements behind the content, the blur
239
+ beneath and the lens with the rim and sheen above it. `isolation` keeps
240
+ them above the element's own plate, which they then blur with the page;
241
+ `position` anchors them, and a component that positions itself -- the
242
+ tooltip bubble -- keeps its own. A default or `.soft` table frosts under
243
+ glass, so it takes the layers too. Both slots are spent, so a host carries
244
+ no `::before` or `::after` of the consumer's; the fields cannot host one.
245
+
246
+ The blur is on the layer and not on the host: an element with a backdrop
247
+ filter of its own is a backdrop root, and a layer inside one sees only that
248
+ element, never the page behind it. So a host clears the region's backdrop.
249
+
250
+ Surfaces take the layers from a liquid scope. A region of another
251
+ aesthetic nested inside, or of plain glass, keeps its own material and
252
+ takes none; this list names the other aesthetics, and `registry.test.ts`
253
+ fails when one is missing.
254
+
255
+ A button takes them only when it carries `.glass-liquid` itself, in a
256
+ glass scope. Controls stay solid under a liquid region, for the reason
257
+ glass gives -- two composited layers and a filter under every control of a
258
+ dense cluster -- and a class on the one button that is meant to be a pane
259
+ is a request rather than a cascade, the idiom `text-control` uses for
260
+ `.edgeless`. Its fill is still presentation's: `.solid` stays an opaque
261
+ plate under the rim, and `.soft` and `.ghost` are the clear panes. A
262
+ disabled one is dimmed by `opacity`, which makes it a backdrop root, so it
263
+ keeps the ground and the rim and bends nothing. */
264
+ :is(.glass-liquid:not(.glass-solo), .glass-liquid:not(.glass-solo) *):is(.glass, .glass *):is(
265
+ .card,
266
+ .panel,
267
+ .alert,
268
+ .tooltip-bubble,
269
+ .surface,
270
+ .data-table:not(.solid, .ghost)
271
+ ):not(
272
+ .glass-liquid :is(.neobrutalism, .pixel, .chunky-tile, .cyber, .sketch),
273
+ .glass-liquid :is(.neobrutalism, .pixel, .chunky-tile, .cyber, .sketch) *,
274
+ .glass-liquid .glass:not(.glass-liquid),
275
+ .glass-liquid .glass:not(.glass-liquid) *
276
+ ),
277
+ .btn.glass-liquid:is(.glass, .glass *) {
278
+ --ui-backdrop: none;
279
+
280
+ position: relative;
281
+ isolation: isolate;
282
+
283
+ &::before,
284
+ &::after {
285
+ position: absolute;
286
+ inset: 0;
287
+ z-index: -1;
288
+ border-radius: inherit;
289
+ corner-shape: inherit;
290
+ content: "";
291
+ pointer-events: none;
292
+ }
293
+
294
+ /* Prefixed first, for the reason `surface` gives. */
295
+ &::before {
296
+ -webkit-backdrop-filter: var(--_glass-liquid-blur);
297
+ backdrop-filter: var(--_glass-liquid-blur);
298
+ }
299
+
300
+ &::after {
301
+ backdrop-filter: var(--_glass-liquid-lens);
302
+ background-image: var(--_glass-liquid-sheen);
303
+ box-shadow: var(--_glass-liquid-rim);
304
+ }
305
+ }
306
+
307
+ /* A button's lens is the control one. Swapped as the value the layer reads
308
+ rather than on the layer: the list above outranks any selector that names
309
+ the button's `::after` alone. */
310
+ .btn.glass-liquid:is(.glass, .glass *) {
311
+ --_glass-liquid-lens: var(--_glass-liquid-lens-control);
312
+ }
313
+ }
314
+
315
+ /* A liquid button rests on liquid's ground rather than on nothing, so a
316
+ `.ghost` or `.soft` one is a pane and not a hole in the page. It reads the
317
+ surface ground the modifier set on it, so reduced transparency and a
318
+ consumer's own `--ui-surface-ground` reach it as they reach a card.
319
+
320
+ In `utilities`, not with the layers: it has to beat `box`'s own
321
+ `--_d-ground: transparent`, which it does there on specificity, and it
322
+ writes only a private nothing a consumer sets. See
323
+ docs/internal/model.md#cascade-layers. */
324
+ @layer utilities {
325
+ .btn.glass-liquid:is(.glass, .glass *) {
326
+ --_d-ground: var(--ui-surface-ground, transparent);
327
+ }
328
+ }
329
+
330
+ /* Forced colours paint the plain surface: no layer blurs, bends, or lights it. */
331
+ @media (forced-colors: active) {
332
+ .glass-liquid {
333
+ --_glass-liquid-blur: none;
334
+ --_glass-liquid-lens: none;
335
+ --_glass-liquid-lens-control: none;
336
+ --_glass-liquid-rim: none;
337
+ --_glass-liquid-sheen: none;
338
+ }
113
339
  }
114
340
 
115
341
  /* Solo: the same look painted onto the one element carrying the class, for an
@@ -117,24 +343,63 @@
117
343
  `<div>` on a page that never loaded the base stylesheet. See
118
344
  docs/internal/model.md#solo-classes.
119
345
 
120
- It reads the aesthetic's own knobs and the theme's foundation tokens, each
121
- with its shipped literal as the fallback, and never a `--ui-*`, `--intent-*`,
122
- or `--elevation-color` token: those belong to whichever aesthetic an ancestor
123
- carries, and a solo element has to look the same inside any of them.
346
+ It composes with what the element is asked to be, the way a component does:
347
+ the fill and edge a presentation class asks for, on the element or cascaded
348
+ from a container; the intent class on the element; the element's own
349
+ elevation class. Each is read with the unclassed look as its fallback, so
350
+ with none of them, or with no theme loaded, it paints what it always did.
351
+
352
+ Material is the exception. It reads the aesthetic's own knobs and the
353
+ theme's foundation tokens, each with its shipped literal as the fallback,
354
+ and never a material `--ui-*` or `--elevation-color` token: those belong to
355
+ whichever aesthetic an ancestor carries, and a solo element has to be this
356
+ aesthetic inside any of them.
357
+
124
358
  Unlayered and one class deep, so it beats a foreign component's
125
359
  zero-specificity rules wherever they load. Not for this package's own
126
- components, which take the aesthetic class instead. */
360
+ components, which take the aesthetic class instead.
361
+
362
+ The other five paint the fill as an inset layer over whatever the element
363
+ already is, and write no background, so an unclassed solo element keeps its
364
+ own. Glass's translucent ground is its material, so it owns the background
365
+ and mixes the fill over that ground. */
127
366
  .glass-solo {
128
- background-color: color-mix(
367
+ /* The composition, the same in every solo class; `registry.test.ts` holds
368
+ the copies together. See the note on `.glass-solo`. */
369
+ --_solo-intent-color: initial;
370
+ --_solo-intent-contrast: initial;
371
+ --_solo-intent-strong: initial;
372
+ --_solo-intent-border: initial;
373
+ --_solo-intent-fill-max: initial;
374
+ --_solo-color: var(--_solo-intent-color, var(--color-text, light-dark(oklch(14.5% 0 0), oklch(98.5% 0 0))));
375
+ --_solo-contrast: var(
376
+ --_solo-intent-contrast,
377
+ var(--color-text-contrast, light-dark(oklch(98.5% 0 0), oklch(14.5% 0 0)))
378
+ );
379
+ --_solo-strong: var(--_solo-intent-strong, var(--color-text-strong, light-dark(oklch(20.5% 0 0), oklch(97% 0 0))));
380
+ --_solo-capped: min(var(--ui-fill, 0%), var(--_solo-intent-fill-max, 20%));
381
+ --_solo-fill: color-mix(in oklab, var(--_solo-color) var(--_solo-capped), transparent);
382
+ --_solo-on-fill: min(var(--ui-fg-on-fill, 0%), max(0%, calc(var(--_solo-capped) * 2 - 100%)));
383
+ --_solo-fg: color-mix(in oklab, var(--_solo-contrast) var(--_solo-on-fill), var(--_solo-strong));
384
+ /* The line with no intent on the element: this aesthetic's own ink. */
385
+ --_solo-ink: var(--_solo-intent-border, light-dark(rgb(255 255 255 / 0.65), rgb(255 255 255 / 0.22)));
386
+ --_solo-ground: color-mix(
129
387
  in srgb,
130
388
  var(--color-background, light-dark(oklch(98.5% 0 0), oklch(14.5% 0 0))) 45%,
131
389
  transparent
132
390
  );
133
- border: 1px solid light-dark(rgb(255 255 255 / 0.65), rgb(255 255 255 / 0.22));
391
+ /* The plate runs under the border, so the line fades to nothing, as `box`'s
392
+ does, rather than to the fill. */
393
+ --_solo-edge: color-mix(in oklab, var(--_solo-ink) var(--ui-border, 100%), transparent);
394
+
395
+ background-color: color-mix(in oklab, var(--_solo-color) var(--_solo-capped), var(--_solo-ground));
396
+ border: 1px solid var(--_solo-edge);
134
397
  /* The surface corner: the element this exists for is a pane. */
135
398
  border-radius: var(--glass-radius-surface, 1rem);
136
399
  /* The theme's depth colour as a literal, for the reason above: inside a
137
- `.chunky-tile` region the token is black. */
400
+ `.chunky-tile` region the token is black. A complete shadow, so the
401
+ elevation classes do not reach it, as they do not under the aesthetic
402
+ class. */
138
403
  box-shadow:
139
404
  0 1px 2px -1px light-dark(rgb(15 23 42 / 0.08), rgb(0 0 0 / 0.55)),
140
405
  0 18px 40px -16px light-dark(rgb(15 23 42 / 0.08), rgb(0 0 0 / 0.55));
@@ -144,10 +409,97 @@
144
409
  backdrop-filter: blur(14px) saturate(1.6);
145
410
  }
146
411
 
412
+ /* An intent class on the element colours it, as on a component. Named rather
413
+ than read from `--intent-*` alone: those slots inherit, so a `.success`
414
+ container would colour a solo element where it leaves a component neutral. */
415
+ .glass-solo:is(.primary, .secondary, .success, .warning, .destructive, .info) {
416
+ --_solo-intent-color: var(--intent-color);
417
+ --_solo-intent-contrast: var(--intent-contrast);
418
+ --_solo-intent-strong: var(--intent-strong);
419
+ --_solo-intent-border: var(--intent-border);
420
+ --_solo-intent-fill-max: var(--intent-fill-max);
421
+ }
422
+
423
+ /* The text colour is written only where a fill or an intent is asked for, on
424
+ the element or by a container's fill class. Anywhere else the element keeps
425
+ its own: a dark toast keeps its light text. */
426
+ .glass-solo:is(
427
+ .solid,
428
+ .soft,
429
+ .ghost,
430
+ .primary,
431
+ .secondary,
432
+ .success,
433
+ .warning,
434
+ .destructive,
435
+ .info,
436
+ :is(.solid, .soft, .ghost) *
437
+ ) {
438
+ color: var(--_solo-fg);
439
+ }
440
+
147
441
  @media (prefers-reduced-transparency: reduce) {
148
442
  .glass-solo {
149
- background-color: var(--color-background, light-dark(oklch(98.5% 0 0), oklch(14.5% 0 0)));
443
+ --_solo-ground: var(--color-background, light-dark(oklch(98.5% 0 0), oklch(14.5% 0 0)));
444
+
150
445
  -webkit-backdrop-filter: none;
151
446
  backdrop-filter: none;
152
447
  }
153
448
  }
449
+
450
+ /* With `.glass-liquid`, the liquid pane: its corner and ground, and the same two
451
+ layers behind the element's content. The element's own blur goes, for the
452
+ backdrop-root reason the modifier gives. Both pseudo-elements are spent, so a
453
+ foreign element that draws its own `::before` or `::after` loses them.
454
+
455
+ The layers need a positioned element to anchor to. That one declaration is
456
+ layered, where the element's own `fixed` or `absolute` beats it -- a toast
457
+ keeps its placement, and an unpositioned element is anchored. */
458
+ @layer components {
459
+ .glass-solo.glass-liquid {
460
+ position: relative;
461
+ }
462
+ }
463
+
464
+ .glass-solo.glass-liquid {
465
+ --_solo-ground: color-mix(
466
+ in srgb,
467
+ var(--color-background, light-dark(oklch(98.5% 0 0), oklch(14.5% 0 0))) 30%,
468
+ transparent
469
+ );
470
+ --_solo-ink: var(--_solo-intent-border, light-dark(rgb(255 255 255 / 0.45), rgb(255 255 255 / 0.16)));
471
+
472
+ isolation: isolate;
473
+ border-radius: var(--glass-radius-surface, 1.5rem);
474
+ corner-shape: squircle;
475
+ -webkit-backdrop-filter: none;
476
+ backdrop-filter: none;
477
+
478
+ &::before,
479
+ &::after {
480
+ position: absolute;
481
+ inset: 0;
482
+ z-index: -1;
483
+ border-radius: inherit;
484
+ corner-shape: inherit;
485
+ content: "";
486
+ pointer-events: none;
487
+ }
488
+
489
+ &::before {
490
+ -webkit-backdrop-filter: var(--_glass-liquid-blur);
491
+ backdrop-filter: var(--_glass-liquid-blur);
492
+ }
493
+
494
+ &::after {
495
+ backdrop-filter: var(--_glass-liquid-lens);
496
+ background-image: var(--_glass-liquid-sheen);
497
+ box-shadow: var(--_glass-liquid-rim);
498
+ }
499
+ }
500
+
501
+ @media (prefers-reduced-transparency: reduce) {
502
+ .glass-solo.glass-liquid {
503
+ --_solo-ground: var(--color-background, light-dark(oklch(98.5% 0 0), oklch(14.5% 0 0)));
504
+ }
505
+ }
@@ -2,7 +2,8 @@
2
2
  for only the one in use: a cascading class has no static call site, so nothing
3
3
  here can be tree-shaken.
4
4
 
5
- Import after the base stylesheet in either form. */
5
+ Import before or after the base stylesheet, in either form: every entrypoint
6
+ states the layer order first, so load order decides nothing. */
6
7
  @import "./neobrutalism.css";
7
8
  @import "./glass.css";
8
9
  @import "./pixel.css";