@codenhub/styles 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +1 -1
  2. package/dist/aesthetics/chunky-tile.css +1 -1
  3. package/dist/aesthetics/cyber.css +1 -1
  4. package/dist/aesthetics/glass.css +1 -1
  5. package/dist/aesthetics/index.css +1 -1
  6. package/dist/aesthetics/neobrutalism.css +1 -1
  7. package/dist/aesthetics/pixel.css +1 -1
  8. package/dist/aesthetics/sketch.css +1 -0
  9. package/dist/components.css +1 -1
  10. package/dist/index.css +1 -1
  11. package/dist/native.css +1 -1
  12. package/dist/palette.css +1 -1
  13. package/dist/theme.css +1 -1
  14. package/dist/tw/aesthetics/chunky-tile.css +142 -126
  15. package/dist/tw/aesthetics/cyber.css +132 -95
  16. package/dist/tw/aesthetics/glass.css +97 -56
  17. package/dist/tw/aesthetics/index.css +1 -0
  18. package/dist/tw/aesthetics/neobrutalism.css +99 -52
  19. package/dist/tw/aesthetics/pixel.css +127 -87
  20. package/dist/tw/aesthetics/sketch.css +326 -0
  21. package/dist/tw/box.css +100 -35
  22. package/dist/tw/components/button.css +40 -22
  23. package/dist/tw/components/feedback.css +29 -14
  24. package/dist/tw/components/form.css +151 -155
  25. package/dist/tw/components/index.css +15 -3
  26. package/dist/tw/components/loader.css +34 -25
  27. package/dist/tw/components/surface.css +28 -71
  28. package/dist/tw/components/tooltip.css +20 -10
  29. package/dist/tw/corner.css +26 -0
  30. package/dist/tw/elevation.css +8 -9
  31. package/dist/tw/intent.css +17 -9
  32. package/dist/tw/native.css +45 -39
  33. package/dist/tw/palette.css +452 -446
  34. package/dist/tw/presentation.css +9 -4
  35. package/dist/tw/surface.css +62 -0
  36. package/dist/tw/theme.css +106 -75
  37. package/dist/tw/utilities/content.css +25 -16
  38. package/dist/tw/utilities/layout.css +2 -2
  39. package/docs/accessibility.md +16 -16
  40. package/docs/changelog/0.5.0.md +38 -0
  41. package/docs/changelog/index.md +1 -0
  42. package/docs/index.md +3 -3
  43. package/docs/setup.md +23 -3
  44. package/docs/usage/aesthetics.md +56 -13
  45. package/docs/usage/buttons.md +13 -13
  46. package/docs/usage/composing.md +11 -11
  47. package/docs/usage/content-and-layout.md +13 -13
  48. package/docs/usage/customizing.md +90 -52
  49. package/docs/usage/feedback.md +1 -1
  50. package/docs/usage/forms.md +11 -11
  51. package/docs/usage/theming.md +1 -1
  52. package/llms-full.txt +274 -136
  53. package/package.json +12 -2
@@ -6,97 +6,137 @@
6
6
  which is exactly what `--ui-shadow-inset`, `--ui-shadow-spread` and
7
7
  `--ui-shadow-edge` are for. The ring is still coloured by each component's own
8
8
  intent, because the parts carry no colour. */
9
- .pixel {
10
- /* One pixel of the imaginary grid; the ring is one unit thick and the corner
11
- one unit square, so scaling it scales the whole look. Read with a fallback
12
- so an ancestor can set `--pixel-unit` -- declared here it outranks `:root`
13
- (R8; see model.md#rules-for-aesthetics). */
14
- --_pixel-unit: var(--pixel-unit, 4px);
15
9
 
16
- --ui-ink: light-dark(var(--color-neutral-950), var(--color-neutral-50));
17
- --ui-radius: 0;
18
- --ui-radius-surface: 0;
19
-
20
- /* A border would be clipped at the straight sides and paint a second line
21
- beside the ring, so the ceiling takes it out and the ring does the drawing.
22
-
23
- The width itself stays under the unit deliberately. Its one live reader is
24
- the toggle cap in `form.css`, which reads it as the thinnest line this
25
- aesthetic will accept where the full ring does not fit: a 16px checkbox
26
- spending four pixels a side leaves an eight-pixel hole and an unchecked box
27
- reads as a filled one. */
28
- --ui-border-width: 2px;
29
- --ui-border-max: 0px;
30
-
31
- /* The ring is one unit deep, which is exactly the corner cut, so the staircase
32
- is covered and the line does not read as broken at the corners. Spread is not
33
- scaled by elevation, so a chip resting at zero keeps its edge. */
34
- --ui-shadow-inset: inset;
35
- /* The ring is this element's border, not depth, drawn where `clip-path` cannot
36
- cut it. `--ui-shadow-edge` says so and `box` colours it from `--_edge`, so
37
- the edge axis still decides: an `.edgeless` badge or `.solid` button gets no
38
- ring, an `.edged` card does, a field keeps one whatever a container asks.
39
- Declared empty because it is read for presence -- the percentage form nests
40
- two `color-mix()` results that WebKit resolves as zero (see `box`). */
41
- --ui-shadow-edge: ;
42
- --ui-shadow-spread: var(--_pixel-unit);
43
- --ui-shadow-x: 0px;
44
- --ui-shadow-y: 0px;
45
- --ui-shadow-blur: 0px;
46
-
47
- /* The focus ring is a second inset layer, and shadows paint first-on-top, so it
48
- has to be wider than the edge to show at all. */
49
- --ui-focus-inset: calc(var(--_pixel-unit) + var(--focus-ring-width));
50
-
51
- /* One square unit off each corner, and nothing else. The staircase this
52
- replaces stepped twice at half the unit to approximate a curve, which is the
53
- one thing a low-resolution grid cannot do: at any size it read as a bevel
54
- that had not quite committed. A single cut of the grid's own unit is the
55
- shape the look is actually made of.
56
-
57
- The polygon resolves the unit here, on `.pixel`, and inherits down
58
- already-resolved -- so a component cannot rescale the corner by setting
59
- `--pixel-unit` on itself, and the knob belongs above rather than within. */
60
- --ui-clip: polygon(
61
- 0 var(--_pixel-unit),
62
- var(--_pixel-unit) var(--_pixel-unit),
63
- var(--_pixel-unit) 0,
64
- calc(100% - var(--_pixel-unit)) 0,
65
- calc(100% - var(--_pixel-unit)) var(--_pixel-unit),
66
- 100% var(--_pixel-unit),
67
- 100% calc(100% - var(--_pixel-unit)),
68
- calc(100% - var(--_pixel-unit)) calc(100% - var(--_pixel-unit)),
69
- calc(100% - var(--_pixel-unit)) 100%,
70
- var(--_pixel-unit) 100%,
71
- var(--_pixel-unit) calc(100% - var(--_pixel-unit)),
72
- 0 calc(100% - var(--_pixel-unit))
73
- );
74
- /* Chips take the other half of the vocabulary: no radius at all. The cut is one
75
- grid unit whatever the element is, and one unit off each corner of a 24px
76
- badge or a 16px checkbox is a bite rather than a corner. Squared is the
77
- honest small-scale answer, and it is still the pixel look -- the aesthetic is
78
- a grid, and a square is on it. */
79
- --ui-clip-tight: none;
80
-
81
- /* Square corners already, but a radius of zero draws no shape; it is
82
- restated, and the pill and chip corners cleared back to `--ui-radius`, so
83
- a region nested inside `.cyber` does not inherit its bevel onto anything
84
- that sets a radius of its own. */
85
- --ui-corner-shape: round;
86
- --ui-radius-pill: initial;
87
- --ui-radius-tight: initial;
88
-
89
- --ui-hover-transform: none;
90
- --ui-active-transform: none;
91
- --ui-backdrop: none;
92
-
93
- /* Consumer-supplied. The package ships no font binary, so the aesthetic has no
94
- font to fall back to but a monospace stack. */
95
- font-family: var(--font-pixel, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
10
+ /* The layer map, stated before anything uses a layer -- see `theme.css`. The
11
+ aesthetic's token class sits in `components`, below every utility, so a
12
+ consumer's own utility or unlayered CSS beats it; the solo class stays
13
+ unlayered, because it has to beat a foreign component's unlayered CSS. See
14
+ docs/internal/model.md#cascade-layers. */
15
+ @layer theme, base, components, utilities;
16
+
17
+ @layer components {
18
+ .pixel {
19
+ /* One pixel of the imaginary grid; the ring is one unit thick and the corner
20
+ one unit square, so scaling it scales the whole look. Read with a fallback
21
+ so an ancestor can set `--pixel-unit` -- declared here it outranks `:root`
22
+ (R8; see model.md#rules-for-aesthetics). */
23
+ --_pixel-unit: var(--pixel-unit, 4px);
24
+
25
+ --ui-ink: light-dark(var(--color-neutral-950), var(--color-neutral-50));
26
+ --ui-corner: 0;
27
+ --ui-corner-surface: 0;
28
+
29
+ /* A border would be clipped at the straight sides and paint a second line
30
+ beside the ring, so the ceiling takes it out and the ring does the drawing.
31
+
32
+ The width itself stays under the unit deliberately. Its one live reader is
33
+ the toggle cap in `form.css`, which reads it as the thinnest line this
34
+ aesthetic will accept where the full ring does not fit: a 16px checkbox
35
+ spending four pixels a side leaves an eight-pixel hole and an unchecked box
36
+ reads as a filled one. */
37
+ --ui-border-width: 2px;
38
+ --ui-border-max: 0px;
39
+ --ui-line-style: initial;
40
+ --ui-rule-style: initial;
41
+
42
+ /* The ring is one unit deep, which is exactly the corner cut, so the staircase
43
+ is covered and the line does not read as broken at the corners. Spread is not
44
+ scaled by elevation, so a chip resting at zero keeps its edge. */
45
+ --ui-shadow-inset: inset;
46
+ /* The ring is this element's border, not depth, drawn where `clip-path` cannot
47
+ cut it. `--ui-shadow-edge` says so and `box` colours it from `--_edge`, so
48
+ the edge axis still decides: an `.edgeless` badge or `.solid` button gets no
49
+ ring, an `.edged` card does, a field keeps one whatever a container asks.
50
+ Declared empty because it is read for presence -- the percentage form nests
51
+ two `color-mix()` results that WebKit resolves as zero (see `box`). */
52
+ --ui-shadow-edge: ;
53
+ --ui-shadow-spread: var(--_pixel-unit);
54
+ /* No halo, cleared so a region nested inside another
55
+ aesthetic does not inherit one. */
56
+ --ui-halo-blur: initial;
57
+ --ui-halo-spread: initial;
58
+ --ui-halo-ink: initial;
59
+ --ui-shadow-x: 0px;
60
+ --ui-shadow-y: 0px;
61
+ --ui-shadow-blur: 0px;
62
+
63
+ /* The focus ring is a second inset layer, and shadows paint first-on-top, so it
64
+ has to be wider than the edge to show at all. */
65
+ --ui-focus-inset: calc(var(--_pixel-unit) + var(--focus-ring-width));
66
+
67
+ /* One square unit off each corner, and nothing else. The staircase this
68
+ replaces stepped twice at half the unit to approximate a curve, which is the
69
+ one thing a low-resolution grid cannot do: at any size it read as a bevel
70
+ that had not quite committed. A single cut of the grid's own unit is the
71
+ shape the look is actually made of.
72
+
73
+ The polygon resolves the unit here, on `.pixel`, and inherits down
74
+ already-resolved -- so a component cannot rescale the corner by setting
75
+ `--pixel-unit` on itself, and the knob belongs above rather than within. */
76
+ --ui-clip: polygon(
77
+ 0 var(--_pixel-unit),
78
+ var(--_pixel-unit) var(--_pixel-unit),
79
+ var(--_pixel-unit) 0,
80
+ calc(100% - var(--_pixel-unit)) 0,
81
+ calc(100% - var(--_pixel-unit)) var(--_pixel-unit),
82
+ 100% var(--_pixel-unit),
83
+ 100% calc(100% - var(--_pixel-unit)),
84
+ calc(100% - var(--_pixel-unit)) calc(100% - var(--_pixel-unit)),
85
+ calc(100% - var(--_pixel-unit)) 100%,
86
+ var(--_pixel-unit) 100%,
87
+ var(--_pixel-unit) calc(100% - var(--_pixel-unit)),
88
+ 0 calc(100% - var(--_pixel-unit))
89
+ );
90
+ /* Chips take the other half of the vocabulary: no radius at all. The cut is one
91
+ grid unit whatever the element is, and one unit off each corner of a 24px
92
+ badge or a 16px checkbox is a bite rather than a corner. Squared is the
93
+ honest small-scale answer, and it is still the pixel look -- the aesthetic is
94
+ a grid, and a square is on it. */
95
+ --ui-clip-tight: none;
96
+
97
+ /* Square corners already, but a radius of zero draws no shape; it is
98
+ restated, and the pill and chip corners cleared back to `--ui-corner`, so
99
+ a region nested inside `.cyber` does not inherit its bevel onto anything
100
+ that sets a radius of its own. */
101
+ --ui-corner-shape: round;
102
+ --ui-radius-pill: initial;
103
+ --ui-radius-tight: initial;
104
+ --ui-radius: initial;
105
+ --ui-radius-surface: initial;
106
+ --ui-corner-tl: initial;
107
+ --ui-corner-tr: initial;
108
+ --ui-corner-br: initial;
109
+ --ui-corner-bl: initial;
110
+
111
+ --ui-hover-transform: none;
112
+ --ui-active-transform: none;
113
+ --ui-active-translate-x: initial;
114
+ --ui-active-translate-y: initial;
115
+ --ui-backdrop: none;
116
+ --ui-surface-ground: initial;
117
+ --ui-surface-shadow: initial;
118
+ --ui-bg-alpha: 1;
119
+ --ui-surface-image: initial;
120
+
121
+ /* Consumer-supplied. The package ships no font binary, so the aesthetic has no
122
+ font to fall back to but a monospace stack. */
123
+ font-family: var(--font-pixel, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
124
+
125
+ /* The control ink glass and chunky tile name, cleared so a region nested
126
+ inside either draws its controls in this aesthetic's own ink. */
127
+ --ui-control-ink: initial;
128
+
129
+ /* Chunky tile's heavier label, cleared so a region nested inside it keeps
130
+ the button's and the badge's own label. */
131
+ --ui-label-weight: initial;
132
+ --ui-label-tracking: initial;
133
+ --ui-label-case: initial;
134
+ --ui-label-shadow: initial;
135
+ }
96
136
  }
97
137
 
98
138
  /* Solo: the same look painted onto the one element carrying the class, for an
99
- element the package does not style. See docs/internal/solo-utilities.md and
139
+ element the package does not style. See docs/internal/model.md#solo-classes and
100
140
  the note on `.glass-solo`: own knobs and foundation tokens with literal
101
141
  fallbacks, never a shared `--ui-*`, `--intent-*`, or `--elevation-color`
102
142
  token, unlayered, and not for this package's own components. */
@@ -0,0 +1,326 @@
1
+ /* Sketch: uneven elliptical corners, a solid ink edge, and a small hard shadow.
2
+ The handwriting face is consumer-supplied, so this file has no font asset or
3
+ network side effect. Load after the base stylesheet. */
4
+
5
+ /* The layer map, stated before anything uses a layer -- see `theme.css`. The
6
+ aesthetic's token class sits in `components`, below every utility, so a
7
+ consumer's own utility or unlayered CSS beats it; the solo class stays
8
+ unlayered, because it has to beat a foreign component's unlayered CSS. See
9
+ docs/internal/model.md#cascade-layers. */
10
+ @layer theme, base, components, utilities;
11
+
12
+ @layer components {
13
+ .sketch {
14
+ /* Complete radii are taken as written, without the size step or corner
15
+ pattern. Chips use a smaller version of the same irregular outline.
16
+ Surfaces use sharp, hand-drawn square contours tailored for container
17
+ dimensions to avoid interior padding encroachment. Knobs follow R8. */
18
+ --ui-corner: initial;
19
+ --ui-corner-surface: initial;
20
+ --ui-corner-tl: initial;
21
+ --ui-corner-tr: initial;
22
+ --ui-corner-br: initial;
23
+ --ui-corner-bl: initial;
24
+ --ui-radius: var(--sketch-radius, 255px 15px 225px 15px / 15px 225px 15px 255px);
25
+ --ui-radius-surface: var(--sketch-radius-surface, 80px 4px 75px 5px / 3px 70px 4px 80px);
26
+ --ui-radius-pill: var(--sketch-radius-pill, 20px 22px 21px 19px / 20px 22px 21px 19px);
27
+ --ui-radius-tight: 11px 1px 7px 2px / 2px 7px 1px 11px;
28
+ --ui-corner-shape: round;
29
+
30
+ --ui-ink: light-dark(var(--color-neutral-950), var(--color-neutral-50));
31
+ --ui-control-ink: initial;
32
+ --ui-border-width: 1px;
33
+ --ui-border-max: 100px;
34
+ --ui-line-style: solid;
35
+ --ui-rule-style: initial;
36
+
37
+ --ui-shadow-ink: 0%;
38
+ /* The foundation depth colour is restated so a sketch region nested inside
39
+ cyber does not inherit cyber's transparent depth. */
40
+ --elevation-color: light-dark(rgb(15 23 42 / 0.08), rgb(0 0 0 / 0.55));
41
+ --ui-shadow-x: 2px;
42
+ --ui-shadow-y: 2px;
43
+ --ui-shadow-blur: 0px;
44
+ --ui-shadow-spread: 0px;
45
+ --ui-shadow-inset: ;
46
+ --ui-shadow-edge: initial;
47
+ --ui-halo-blur: initial;
48
+ --ui-halo-spread: initial;
49
+ --ui-halo-ink: initial;
50
+ --ui-focus-inset: initial;
51
+
52
+ --ui-active-shadow-x: initial;
53
+ --ui-active-shadow-y: initial;
54
+ --ui-active-transform: scale(0.97);
55
+ --ui-active-translate-x: initial;
56
+ --ui-active-translate-y: initial;
57
+ --ui-hover-transform: none;
58
+ --ui-clip: none;
59
+ --ui-clip-tight: none;
60
+ --ui-backdrop: none;
61
+ --ui-surface-image: initial;
62
+ --ui-surface-ground: initial;
63
+ --ui-surface-shadow: initial;
64
+ --ui-bg-alpha: 1;
65
+ --ui-label-weight: initial;
66
+ --ui-label-tracking: initial;
67
+ --ui-label-case: initial;
68
+ --ui-label-shadow: initial;
69
+
70
+ font-family: var(--font-sketch, cursive);
71
+ }
72
+
73
+ @media (prefers-reduced-motion: reduce) {
74
+ .sketch {
75
+ --ui-active-transform: none;
76
+ }
77
+ }
78
+
79
+ /* Rounded variant: bounded elliptical corners, so the top and bottom edges of
80
+ a wide element stay straight instead of bulging into an egg. On the region
81
+ with `.sketch`, or on one component inside it. Knobs follow R8. */
82
+ .sketch-rounded {
83
+ --ui-radius: var(--sketch-radius-rounded, 26px 10px 24px 11px / 12px 23px 11px 20px);
84
+ --ui-radius-surface: var(--sketch-radius-surface-rounded, 38px 16px 36px 17px / 16px 34px 17px 32px);
85
+ --ui-radius-pill: var(--sketch-radius-pill, 24px 16px 23px 17px / 17px 21px 16px 20px);
86
+ --ui-radius-tight: 9px 3px 8px 4px / 4px 8px 3px 7px;
87
+ }
88
+
89
+ /* One region token gives every element the same outline, so the rotation and
90
+ the dashed accents are written on components, which the registry records.
91
+ The root is a component carrying either class, or one inside either region
92
+ and not inside another aesthetic nested there, which keeps its own
93
+ material. The list below is the one place other aesthetics are named, and
94
+ `registry.test.ts` fails when one is missing from it.
95
+
96
+ Rotation is by sibling position, and a field's control takes its field's
97
+ position instead of its own, so stacked rows alternate. It is stable for a
98
+ given DOM order. A knob, when set, is one outline for every position. */
99
+ :is(
100
+ .sketch,
101
+ .sketch-rounded,
102
+ :is(.sketch, .sketch-rounded)
103
+ :not(
104
+ :is(.sketch, .sketch-rounded) :is(.neobrutalism, .glass, .pixel, .chunky-tile, .cyber),
105
+ :is(.sketch, .sketch-rounded) :is(.neobrutalism, .glass, .pixel, .chunky-tile, .cyber) *
106
+ )
107
+ ) {
108
+ /* The pointed outlines: controls, fields, surfaces, and chips. */
109
+ &:is(.sketch, .sketch *):not(.sketch-rounded, .sketch-rounded *) {
110
+ &:is(.btn, .ipt, .textarea, .select, .input-group, .pre, .tooltip-bubble, .data-table) {
111
+ &:nth-child(4n + 2) {
112
+ --ui-radius: var(--sketch-radius, 18px 240px 15px 255px / 240px 15px 255px 18px);
113
+ }
114
+
115
+ &:nth-child(4n + 3) {
116
+ --ui-radius: var(--sketch-radius, 245px 220px 15px 18px / 18px 15px 245px 235px);
117
+ }
118
+
119
+ &:nth-child(4n) {
120
+ --ui-radius: var(--sketch-radius, 18px 250px 225px 15px / 245px 15px 18px 235px);
121
+ }
122
+ }
123
+
124
+ &:is(.ipt, .textarea, .select, .input-group) {
125
+ .field:nth-child(4n + 1) & {
126
+ --ui-radius: var(--sketch-radius, 255px 15px 225px 15px / 15px 225px 15px 255px);
127
+ }
128
+
129
+ .field:nth-child(4n + 2) & {
130
+ --ui-radius: var(--sketch-radius, 18px 240px 15px 255px / 240px 15px 255px 18px);
131
+ }
132
+
133
+ .field:nth-child(4n + 3) & {
134
+ --ui-radius: var(--sketch-radius, 245px 220px 15px 18px / 18px 15px 245px 235px);
135
+ }
136
+
137
+ .field:nth-child(4n) & {
138
+ --ui-radius: var(--sketch-radius, 18px 250px 225px 15px / 245px 15px 18px 235px);
139
+ }
140
+ }
141
+
142
+ &:is(.card, .panel, .alert) {
143
+ &:nth-child(4n + 2) {
144
+ --ui-radius-surface: var(--sketch-radius-surface, 4px 75px 5px 80px / 75px 3px 80px 4px);
145
+ }
146
+
147
+ &:nth-child(4n + 3) {
148
+ --ui-radius-surface: var(--sketch-radius-surface, 75px 70px 4px 5px / 4px 3px 75px 70px);
149
+ }
150
+
151
+ &:nth-child(4n) {
152
+ --ui-radius-surface: var(--sketch-radius-surface, 5px 80px 75px 4px / 75px 4px 5px 70px);
153
+ }
154
+ }
155
+
156
+ &:is(.badge, .kbd, .code, .checkbox, .switch, .progress) {
157
+ &:nth-child(4n + 2) {
158
+ --ui-radius-tight: 2px 8px 1px 10px / 8px 2px 10px 1px;
159
+ }
160
+
161
+ &:nth-child(4n + 3) {
162
+ --ui-radius-tight: 8px 2px 10px 1px / 1px 9px 2px 8px;
163
+ }
164
+
165
+ &:nth-child(4n) {
166
+ --ui-radius-tight: 1px 10px 2px 7px / 9px 1px 8px 2px;
167
+ }
168
+ }
169
+ }
170
+
171
+ /* The rounded outlines, in the same four places. */
172
+ &:is(.sketch-rounded, .sketch-rounded *) {
173
+ &:is(.btn, .ipt, .textarea, .select, .input-group, .pre, .tooltip-bubble, .data-table) {
174
+ &:nth-child(4n + 2) {
175
+ --ui-radius: var(--sketch-radius-rounded, 11px 25px 12px 24px / 23px 11px 22px 12px);
176
+ }
177
+
178
+ &:nth-child(4n + 3) {
179
+ --ui-radius: var(--sketch-radius-rounded, 24px 12px 26px 10px / 11px 23px 12px 22px);
180
+ }
181
+
182
+ &:nth-child(4n) {
183
+ --ui-radius: var(--sketch-radius-rounded, 12px 24px 10px 25px / 22px 12px 23px 11px);
184
+ }
185
+ }
186
+
187
+ &:is(.ipt, .textarea, .select, .input-group) {
188
+ .field:nth-child(4n + 1) & {
189
+ --ui-radius: var(--sketch-radius-rounded, 26px 10px 24px 11px / 12px 23px 11px 20px);
190
+ }
191
+
192
+ .field:nth-child(4n + 2) & {
193
+ --ui-radius: var(--sketch-radius-rounded, 11px 25px 12px 24px / 23px 11px 22px 12px);
194
+ }
195
+
196
+ .field:nth-child(4n + 3) & {
197
+ --ui-radius: var(--sketch-radius-rounded, 24px 12px 26px 10px / 11px 23px 12px 22px);
198
+ }
199
+
200
+ .field:nth-child(4n) & {
201
+ --ui-radius: var(--sketch-radius-rounded, 12px 24px 10px 25px / 22px 12px 23px 11px);
202
+ }
203
+ }
204
+
205
+ &:is(.card, .panel, .alert) {
206
+ &:nth-child(4n + 2) {
207
+ --ui-radius-surface: var(--sketch-radius-surface-rounded, 17px 36px 18px 34px / 33px 16px 32px 17px);
208
+ }
209
+
210
+ &:nth-child(4n + 3) {
211
+ --ui-radius-surface: var(--sketch-radius-surface-rounded, 35px 18px 38px 15px / 17px 32px 16px 34px);
212
+ }
213
+
214
+ &:nth-child(4n) {
215
+ --ui-radius-surface: var(--sketch-radius-surface-rounded, 18px 35px 16px 37px / 31px 17px 34px 16px);
216
+ }
217
+ }
218
+
219
+ &:is(.badge, .kbd, .code, .checkbox, .switch, .progress) {
220
+ &:nth-child(4n + 2) {
221
+ --ui-radius-tight: 4px 8px 3px 9px / 8px 3px 8px 4px;
222
+ }
223
+
224
+ &:nth-child(4n + 3) {
225
+ --ui-radius-tight: 8px 4px 9px 3px / 3px 9px 4px 8px;
226
+ }
227
+
228
+ &:nth-child(4n) {
229
+ --ui-radius-tight: 3px 9px 4px 8px / 9px 4px 8px 3px;
230
+ }
231
+ }
232
+ }
233
+
234
+ /* A pill button keeps a rounded pill in either variant, bounded to the
235
+ button's half-height so a wide one keeps flat top and bottom edges. */
236
+ &.btn.pill {
237
+ --ui-radius-pill: var(--sketch-radius-pill, 24px 16px 23px 17px / 17px 21px 16px 20px);
238
+
239
+ &:nth-child(4n + 2) {
240
+ --ui-radius-pill: var(--sketch-radius-pill, 17px 23px 16px 24px / 21px 16px 20px 17px);
241
+ }
242
+
243
+ &:nth-child(4n + 3) {
244
+ --ui-radius-pill: var(--sketch-radius-pill, 23px 17px 24px 16px / 16px 20px 17px 21px);
245
+ }
246
+
247
+ &:nth-child(4n) {
248
+ --ui-radius-pill: var(--sketch-radius-pill, 16px 24px 17px 23px / 20px 17px 21px 16px);
249
+ }
250
+ }
251
+
252
+ /* Badges, progress bars, and switches take their chip outline where they
253
+ would be fully round. */
254
+ &:is(.badge, .progress, .switch) {
255
+ --ui-radius-pill: var(--ui-radius-tight);
256
+ }
257
+
258
+ /* Radios and the tooltip trigger stay circles, slightly uneven, so a radio
259
+ is never mistaken for a checkbox. */
260
+ &:is(.radio, .tooltip-icon) {
261
+ --ui-radius-pill: 48% 52% 46% 54% / 53% 46% 54% 47%;
262
+ }
263
+
264
+ /* The dashed accents; every other line stays solid. */
265
+ &:is(.sketch, .sketch *) {
266
+ &.checkbox {
267
+ --ui-line-style: dashed;
268
+ }
269
+
270
+ &:is(.divider, .data-table) {
271
+ --ui-rule-style: dashed;
272
+ }
273
+ }
274
+ }
275
+ }
276
+
277
+ /* Solo: the look on one unstyled element. It uses standalone fallbacks rather
278
+ than material tokens inherited from an aesthetic around it. */
279
+ .sketch-solo {
280
+ --_sketch-ink: light-dark(var(--color-neutral-950, oklch(14.5% 0 0)), var(--color-neutral-50, oklch(98.5% 0 0)));
281
+
282
+ border: 1px solid var(--_sketch-ink);
283
+ /* The surface shape: the element this exists for is usually a pane. An action
284
+ takes the control shape below. */
285
+ border-radius: var(--sketch-radius-surface, 80px 4px 75px 5px / 3px 70px 4px 80px);
286
+ box-shadow: 2px 2px 0 0 var(--_sketch-ink);
287
+ font-family: var(--font-sketch, cursive);
288
+ }
289
+
290
+ /* An action takes the expressive control shape. */
291
+ .sketch-solo:is(
292
+ button,
293
+ a[href],
294
+ [role="button"],
295
+ summary,
296
+ input[type="button"],
297
+ input[type="submit"],
298
+ input[type="reset"]
299
+ ) {
300
+ border-radius: var(--sketch-radius, 255px 15px 225px 15px / 15px 225px 15px 255px);
301
+ }
302
+
303
+ /* With `.sketch-rounded`, the rounded outlines, through their own knobs. */
304
+ .sketch-solo.sketch-rounded {
305
+ border-radius: var(--sketch-radius-surface-rounded, 38px 16px 36px 17px / 16px 34px 17px 32px);
306
+ }
307
+
308
+ .sketch-solo.sketch-rounded:is(
309
+ button,
310
+ a[href],
311
+ [role="button"],
312
+ summary,
313
+ input[type="button"],
314
+ input[type="submit"],
315
+ input[type="reset"]
316
+ ) {
317
+ border-radius: var(--sketch-radius-rounded, 26px 10px 24px 11px / 12px 23px 11px 20px);
318
+ }
319
+
320
+ /* This entrypoint may be the only package stylesheet loaded, so it cannot rely
321
+ on theme.css's forced-colors material reset. */
322
+ @media (forced-colors: active) {
323
+ .sketch-solo {
324
+ border-style: solid;
325
+ }
326
+ }