@codenhub/styles 0.3.0 → 0.4.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.
- package/dist/aesthetics/chunky-tile.css +1 -1
- package/dist/aesthetics/cyber.css +1 -0
- package/dist/aesthetics/glass.css +1 -1
- package/dist/aesthetics/index.css +1 -1
- package/dist/aesthetics/neobrutalism.css +1 -1
- package/dist/aesthetics/pixel.css +1 -1
- package/dist/components.css +1 -1
- package/dist/index.css +1 -1
- package/dist/native.css +1 -1
- package/dist/tw/aesthetics/chunky-tile.css +71 -0
- package/dist/tw/aesthetics/cyber.css +190 -0
- package/dist/tw/aesthetics/glass.css +46 -0
- package/dist/tw/aesthetics/index.css +1 -0
- package/dist/tw/aesthetics/neobrutalism.css +53 -0
- package/dist/tw/aesthetics/pixel.css +48 -0
- package/dist/tw/box.css +7 -0
- package/dist/tw/components/button.css +3 -0
- package/dist/tw/components/feedback.css +13 -5
- package/dist/tw/components/form.css +16 -5
- package/dist/tw/components/tooltip.css +1 -1
- package/dist/tw/utilities/content.css +2 -2
- package/docs/changelog/0.4.0.md +12 -0
- package/docs/changelog/index.md +1 -0
- package/docs/concepts.md +5 -5
- package/docs/index.md +2 -2
- package/docs/setup.md +3 -3
- package/docs/usage/aesthetics.md +75 -1
- package/docs/usage/customizing.md +57 -42
- package/llms-full.txt +151 -52
- package/package.json +12 -2
|
@@ -73,6 +73,12 @@
|
|
|
73
73
|
`.btn`. */
|
|
74
74
|
--ui-clip: none;
|
|
75
75
|
--ui-clip-tight: none;
|
|
76
|
+
/* Round corners, said explicitly, and the pill and chip corners cleared back
|
|
77
|
+
to `--ui-radius`: a region nested inside `.cyber` would otherwise inherit
|
|
78
|
+
its bevel, its diamond pills, and its square chips. */
|
|
79
|
+
--ui-corner-shape: round;
|
|
80
|
+
--ui-radius-pill: initial;
|
|
81
|
+
--ui-radius-tight: initial;
|
|
76
82
|
--ui-shadow-inset: ;
|
|
77
83
|
--ui-shadow-edge: initial;
|
|
78
84
|
--ui-backdrop: none;
|
|
@@ -123,3 +129,68 @@
|
|
|
123
129
|
.chunky-tile .btn.icon.p-xs {
|
|
124
130
|
--ui-shadow-y: 2px;
|
|
125
131
|
}
|
|
132
|
+
|
|
133
|
+
/* Solo: the same look painted onto the one element carrying the class, for an
|
|
134
|
+
element the package does not style. See docs/internal/solo-utilities.md and
|
|
135
|
+
the note on `.glass-solo`: own knobs and foundation tokens with literal
|
|
136
|
+
fallbacks, never a shared `--ui-*`, `--intent-*`, or `--elevation-color`
|
|
137
|
+
token, unlayered, and not for this package's own components. */
|
|
138
|
+
.chunky-tile-solo {
|
|
139
|
+
--_tile-lift: var(--tile-lift, 4px);
|
|
140
|
+
--_tile-ink: light-dark(var(--color-neutral-400, oklch(70.8% 0 0)), var(--color-neutral-600, oklch(43.9% 0 0)));
|
|
141
|
+
/* With no fill to shade, the bar shades the edge: the colour an unfilled
|
|
142
|
+
tile's bar already is under the aesthetic class. */
|
|
143
|
+
--_tile-bar: color-mix(in oklab, var(--_tile-ink) 72%, rgb(0 0 0));
|
|
144
|
+
|
|
145
|
+
border: 2px solid var(--_tile-ink);
|
|
146
|
+
border-radius: var(--tile-radius, 0.75rem);
|
|
147
|
+
box-shadow: 0 var(--_tile-lift) 0 0 var(--_tile-bar);
|
|
148
|
+
font-family: var(--font-rounded, var(--font-default, ui-sans-serif, system-ui, sans-serif));
|
|
149
|
+
transition:
|
|
150
|
+
box-shadow var(--motion-duration-normal, 200ms) var(--motion-ease, cubic-bezier(0.2, 0, 0, 1)),
|
|
151
|
+
transform var(--motion-duration-normal, 200ms) var(--motion-ease, cubic-bezier(0.2, 0, 0, 1));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* The heavier, tracked label is an action's, as under the aesthetic class. */
|
|
155
|
+
.chunky-tile-solo:is(
|
|
156
|
+
button,
|
|
157
|
+
a[href],
|
|
158
|
+
[role="button"],
|
|
159
|
+
summary,
|
|
160
|
+
input[type="button"],
|
|
161
|
+
input[type="submit"],
|
|
162
|
+
input[type="reset"]
|
|
163
|
+
) {
|
|
164
|
+
font-weight: 800;
|
|
165
|
+
letter-spacing: 0.04em;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* The press, only on something pressed: an action, not disabled. A solo
|
|
169
|
+
container -- a toast clicked to dismiss -- stays put, the way only `.btn` and
|
|
170
|
+
an opted-in card press under the aesthetic class. */
|
|
171
|
+
.chunky-tile-solo:is(
|
|
172
|
+
button,
|
|
173
|
+
a[href],
|
|
174
|
+
[role="button"],
|
|
175
|
+
summary,
|
|
176
|
+
input[type="button"],
|
|
177
|
+
input[type="submit"],
|
|
178
|
+
input[type="reset"]
|
|
179
|
+
):active:not(:disabled, [aria-disabled="true"]) {
|
|
180
|
+
box-shadow: 0 0 0 0 var(--_tile-bar);
|
|
181
|
+
transform: translateY(var(--_tile-lift));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
@media (prefers-reduced-motion: reduce) {
|
|
185
|
+
.chunky-tile-solo:is(
|
|
186
|
+
button,
|
|
187
|
+
a[href],
|
|
188
|
+
[role="button"],
|
|
189
|
+
summary,
|
|
190
|
+
input[type="button"],
|
|
191
|
+
input[type="submit"],
|
|
192
|
+
input[type="reset"]
|
|
193
|
+
):active:not(:disabled, [aria-disabled="true"]) {
|
|
194
|
+
transform: none;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/* Cyber: bevelled corners, a thin bright edge, and a glow in the component's own
|
|
2
|
+
colour. See docs/internal/cyber-aesthetic.md for the decision.
|
|
3
|
+
|
|
4
|
+
Tier 1 only. The bevel is `corner-shape`, not `clip-path`: a clip removes
|
|
5
|
+
every outer shadow, so a clipped chamfer has nothing left to glow, and an
|
|
6
|
+
inset ring cannot follow a diagonal cut the way `.pixel`'s follows its square
|
|
7
|
+
one. `corner-shape` draws the corner itself, and the border, the glow, and the
|
|
8
|
+
focus outline all follow it. Load after the base stylesheet. */
|
|
9
|
+
.cyber {
|
|
10
|
+
/* Knobs, read with a fallback so an ancestor can set them (R8; see
|
|
11
|
+
model.md#rules-for-aesthetics). One cut size for controls and surfaces
|
|
12
|
+
alike, so a button and the card around it read as the same material; the
|
|
13
|
+
two shapes then place that cut on opposite diagonals, so they read as
|
|
14
|
+
different things. A shape knob takes any `border-radius` value -- the
|
|
15
|
+
documented presets are a hexagon, a parallelogram, and a single notch --
|
|
16
|
+
and the cut knob scales the default pair without restating it.
|
|
17
|
+
|
|
18
|
+
The cut is a ceiling, not a size: the default pair caps it at a quarter of
|
|
19
|
+
the element's own box, so a 40px button or a card takes the full cut and a
|
|
20
|
+
19px icon button about 5px rather than having its corners eaten. Nothing in
|
|
21
|
+
the model scales a corner with a component's size -- `.sm` and `.dense`
|
|
22
|
+
change padding and height, never the radius -- and a radius percentage is
|
|
23
|
+
the one length that already knows the box. Horizontal percentages resolve
|
|
24
|
+
against the width and vertical ones against the height, so on a small wide
|
|
25
|
+
button the cut runs slightly shallower than 45 degrees; at those sizes that
|
|
26
|
+
is not visible. A shape set through the knob is taken as written. */
|
|
27
|
+
--_cyber-cut: var(--cyber-cut, 0.625rem);
|
|
28
|
+
--_cyber-glow: var(--cyber-glow, 8px);
|
|
29
|
+
--_cyber-shape: var(--cyber-shape, min(var(--_cyber-cut), 25%) 0 / min(var(--_cyber-cut), 25%) 0);
|
|
30
|
+
--_cyber-shape-surface: var(--cyber-shape-surface, 0 min(var(--_cyber-cut), 25%) / 0 min(var(--_cyber-cut), 25%));
|
|
31
|
+
|
|
32
|
+
/* The neutral line. A neon hue is the look's signature, but hue is intent's
|
|
33
|
+
(R1), so the default follows the theme the way neobrutalism's and pixel's
|
|
34
|
+
ink does, and `--cyber-ink` lets an application choose a neon neutral
|
|
35
|
+
without the aesthetic imposing one on every palette. A named intent keeps
|
|
36
|
+
its own edge colour. */
|
|
37
|
+
--ui-ink: var(--cyber-ink, light-dark(var(--color-neutral-950), var(--color-neutral-50)));
|
|
38
|
+
|
|
39
|
+
/* Controls cut top-left and bottom-right, surfaces top-right and bottom-left.
|
|
40
|
+
Anything fully round -- a badge, a switch, the progress track, the tooltip
|
|
41
|
+
icon, and through their own full radius a radio and a `.btn.pill` -- cuts
|
|
42
|
+
all the way to points: a diamond, or a hexagon on something wider than it
|
|
43
|
+
is tall. Chips square outright, which is what tells the checkbox from the
|
|
44
|
+
radio beside it. */
|
|
45
|
+
--ui-corner-shape: bevel;
|
|
46
|
+
--ui-radius: var(--_cyber-shape);
|
|
47
|
+
--ui-radius-surface: var(--_cyber-shape-surface);
|
|
48
|
+
--ui-radius-pill: calc(infinity * 1px);
|
|
49
|
+
--ui-radius-tight: 0;
|
|
50
|
+
--ui-border-width: 1px;
|
|
51
|
+
--ui-border-max: 100px;
|
|
52
|
+
|
|
53
|
+
/* The glow: the component's own ink, blurred, with no offset. `box` mixes
|
|
54
|
+
depth as `color-mix(in oklab, var(--intent-border) var(--ui-shadow-ink),
|
|
55
|
+
var(--elevation-color))`, so a transparent depth colour turns 40% ink into
|
|
56
|
+
the intent colour at 40% alpha rather than into a darker shade of it.
|
|
57
|
+
Elevation scales the blur like every shadow, so the glow lights what rests
|
|
58
|
+
above zero -- buttons and cards -- and anything raised, and leaves fields
|
|
59
|
+
and chips crisp. */
|
|
60
|
+
--elevation-color: transparent;
|
|
61
|
+
--ui-shadow-ink: 40%;
|
|
62
|
+
--ui-shadow-x: 0px;
|
|
63
|
+
--ui-shadow-y: 0px;
|
|
64
|
+
--ui-shadow-blur: var(--_cyber-glow);
|
|
65
|
+
--ui-shadow-spread: 0px;
|
|
66
|
+
|
|
67
|
+
/* The glow holds still under the pointer and under a press; the press is the
|
|
68
|
+
base scale, restated because a region nested inside another aesthetic must
|
|
69
|
+
not inherit that one's travel. Reduced motion drops it below. */
|
|
70
|
+
--ui-hover-transform: none;
|
|
71
|
+
--ui-active-shadow-x: 0px;
|
|
72
|
+
--ui-active-shadow-y: 0px;
|
|
73
|
+
--ui-active-transform: scale(0.97);
|
|
74
|
+
|
|
75
|
+
/* Nothing here clips, blurs, thins its fill, or draws its edge as a ring, and
|
|
76
|
+
an aesthetic nested inside one that does has to say so rather than inherit
|
|
77
|
+
it. */
|
|
78
|
+
--ui-clip: none;
|
|
79
|
+
--ui-clip-tight: none;
|
|
80
|
+
--ui-shadow-inset: ;
|
|
81
|
+
--ui-shadow-edge: initial;
|
|
82
|
+
--ui-backdrop: none;
|
|
83
|
+
--ui-bg-alpha: 1;
|
|
84
|
+
|
|
85
|
+
/* Consumer-supplied, the way `.pixel` reads `--font-pixel`. The package ships
|
|
86
|
+
no font binary; the monospace stack is the closest technical face every
|
|
87
|
+
platform already has. */
|
|
88
|
+
font-family: var(--font-cyber, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* Firefox and Safari do not draw `corner-shape` yet, and there the radius would
|
|
92
|
+
round the corners instead. A rounded cyber element reads as a different
|
|
93
|
+
aesthetic; a square one reads as the same aesthetic simplified. What is fully
|
|
94
|
+
round stays round rather than square: a square radio is a checkbox, and a
|
|
95
|
+
pill is the nearest a diamond gets without a bevel. */
|
|
96
|
+
@supports not (corner-shape: bevel) {
|
|
97
|
+
.cyber {
|
|
98
|
+
--ui-radius: 0;
|
|
99
|
+
--ui-radius-surface: 0;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@media (prefers-reduced-motion: reduce) {
|
|
104
|
+
.cyber {
|
|
105
|
+
--ui-active-transform: none;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* Solo: the same look painted onto the one element carrying the class, for an
|
|
110
|
+
element the package does not style. See docs/internal/solo-utilities.md and
|
|
111
|
+
the note on `.glass-solo`: own knobs and foundation tokens with literal
|
|
112
|
+
fallbacks, never a shared `--ui-*`, `--intent-*`, or `--elevation-color`
|
|
113
|
+
token, unlayered, and not for this package's own components. */
|
|
114
|
+
.cyber-solo {
|
|
115
|
+
--_cyber-ink: var(
|
|
116
|
+
--cyber-ink,
|
|
117
|
+
light-dark(var(--color-neutral-950, oklch(14.5% 0 0)), var(--color-neutral-50, oklch(98.5% 0 0)))
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
border: 1px solid var(--_cyber-ink);
|
|
121
|
+
/* The surface shape: the element this exists for is usually a pane. An action
|
|
122
|
+
takes the control shape below. */
|
|
123
|
+
border-radius: var(
|
|
124
|
+
--cyber-shape-surface,
|
|
125
|
+
0 min(var(--cyber-cut, 0.625rem), 25%) / 0 min(var(--cyber-cut, 0.625rem), 25%)
|
|
126
|
+
);
|
|
127
|
+
corner-shape: bevel;
|
|
128
|
+
/* Always lit: the elevation gate the aesthetic class answers to is `box`'s,
|
|
129
|
+
and a solo element is one the consumer chose to light up. */
|
|
130
|
+
box-shadow: 0 0 var(--cyber-glow, 8px) 0 color-mix(in oklab, var(--_cyber-ink) 40%, transparent);
|
|
131
|
+
font-family: var(--font-cyber, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
|
|
132
|
+
transition: transform var(--motion-duration-normal, 200ms) var(--motion-ease, cubic-bezier(0.2, 0, 0, 1));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* An action takes the control shape, on the opposite diagonal to a pane's. */
|
|
136
|
+
.cyber-solo:is(
|
|
137
|
+
button,
|
|
138
|
+
a[href],
|
|
139
|
+
[role="button"],
|
|
140
|
+
summary,
|
|
141
|
+
input[type="button"],
|
|
142
|
+
input[type="submit"],
|
|
143
|
+
input[type="reset"]
|
|
144
|
+
) {
|
|
145
|
+
border-radius: var(--cyber-shape, min(var(--cyber-cut, 0.625rem), 25%) 0 / min(var(--cyber-cut, 0.625rem), 25%) 0);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@supports not (corner-shape: bevel) {
|
|
149
|
+
.cyber-solo,
|
|
150
|
+
.cyber-solo:is(
|
|
151
|
+
button,
|
|
152
|
+
a[href],
|
|
153
|
+
[role="button"],
|
|
154
|
+
summary,
|
|
155
|
+
input[type="button"],
|
|
156
|
+
input[type="submit"],
|
|
157
|
+
input[type="reset"]
|
|
158
|
+
) {
|
|
159
|
+
border-radius: 0;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* The press, only on something pressed: an action, not disabled. A solo
|
|
164
|
+
container stays put, the way only `.btn` and an opted-in card press under
|
|
165
|
+
the aesthetic class. */
|
|
166
|
+
.cyber-solo:is(
|
|
167
|
+
button,
|
|
168
|
+
a[href],
|
|
169
|
+
[role="button"],
|
|
170
|
+
summary,
|
|
171
|
+
input[type="button"],
|
|
172
|
+
input[type="submit"],
|
|
173
|
+
input[type="reset"]
|
|
174
|
+
):active:not(:disabled, [aria-disabled="true"]) {
|
|
175
|
+
transform: scale(0.97);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
@media (prefers-reduced-motion: reduce) {
|
|
179
|
+
.cyber-solo:is(
|
|
180
|
+
button,
|
|
181
|
+
a[href],
|
|
182
|
+
[role="button"],
|
|
183
|
+
summary,
|
|
184
|
+
input[type="button"],
|
|
185
|
+
input[type="submit"],
|
|
186
|
+
input[type="reset"]
|
|
187
|
+
):active:not(:disabled, [aria-disabled="true"]) {
|
|
188
|
+
transform: none;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -48,6 +48,12 @@
|
|
|
48
48
|
|
|
49
49
|
--ui-clip: none;
|
|
50
50
|
--ui-clip-tight: none;
|
|
51
|
+
/* Round corners, said explicitly, and the pill and chip corners cleared back
|
|
52
|
+
to `--ui-radius`: a region nested inside `.cyber` would otherwise inherit
|
|
53
|
+
its bevel, its diamond pills, and its square chips. */
|
|
54
|
+
--ui-corner-shape: round;
|
|
55
|
+
--ui-radius-pill: initial;
|
|
56
|
+
--ui-radius-tight: initial;
|
|
51
57
|
/* Nothing here draws its edge as a ring, and an aesthetic nested inside pixel
|
|
52
58
|
has to say so rather than inherit it. */
|
|
53
59
|
--ui-shadow-edge: initial;
|
|
@@ -64,3 +70,43 @@
|
|
|
64
70
|
--ui-surface-ground: var(--color-background);
|
|
65
71
|
}
|
|
66
72
|
}
|
|
73
|
+
|
|
74
|
+
/* Solo: the same look painted onto the one element carrying the class, for an
|
|
75
|
+
element the package does not style -- a toast from another library, a plain
|
|
76
|
+
`<div>` on a page that never loaded the base stylesheet. See
|
|
77
|
+
docs/internal/solo-utilities.md.
|
|
78
|
+
|
|
79
|
+
It reads the aesthetic's own knobs and the theme's foundation tokens, each
|
|
80
|
+
with its shipped literal as the fallback, and never a `--ui-*`, `--intent-*`,
|
|
81
|
+
or `--elevation-color` token: those belong to whichever aesthetic an ancestor
|
|
82
|
+
carries, and a solo element has to look the same inside any of them.
|
|
83
|
+
Unlayered and one class deep, so it beats a foreign component's
|
|
84
|
+
zero-specificity rules wherever they load. Not for this package's own
|
|
85
|
+
components, which take the aesthetic class instead. */
|
|
86
|
+
.glass-solo {
|
|
87
|
+
background-color: color-mix(
|
|
88
|
+
in srgb,
|
|
89
|
+
var(--color-background, light-dark(oklch(98.5% 0 0), oklch(14.5% 0 0))) 45%,
|
|
90
|
+
transparent
|
|
91
|
+
);
|
|
92
|
+
border: 1px solid light-dark(rgb(255 255 255 / 0.65), rgb(255 255 255 / 0.22));
|
|
93
|
+
/* The surface corner: the element this exists for is a pane. */
|
|
94
|
+
border-radius: var(--glass-radius-surface, 1rem);
|
|
95
|
+
/* The theme's depth colour as a literal, for the reason above: inside a
|
|
96
|
+
`.chunky-tile` region the token is black. */
|
|
97
|
+
box-shadow:
|
|
98
|
+
0 1px 2px -1px light-dark(rgb(15 23 42 / 0.08), rgb(0 0 0 / 0.55)),
|
|
99
|
+
0 18px 40px -16px light-dark(rgb(15 23 42 / 0.08), rgb(0 0 0 / 0.55));
|
|
100
|
+
/* Prefixed first, for the reason `surface` gives: written second, the
|
|
101
|
+
minifier folds the pair into the prefixed one and Firefox loses the blur. */
|
|
102
|
+
-webkit-backdrop-filter: blur(14px) saturate(1.6);
|
|
103
|
+
backdrop-filter: blur(14px) saturate(1.6);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
107
|
+
.glass-solo {
|
|
108
|
+
background-color: var(--color-background, light-dark(oklch(98.5% 0 0), oklch(14.5% 0 0)));
|
|
109
|
+
-webkit-backdrop-filter: none;
|
|
110
|
+
backdrop-filter: none;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -43,6 +43,12 @@
|
|
|
43
43
|
say so rather than inherit it. */
|
|
44
44
|
--ui-clip: none;
|
|
45
45
|
--ui-clip-tight: none;
|
|
46
|
+
/* Round corners, said explicitly, and the pill and chip corners cleared back
|
|
47
|
+
to `--ui-radius`: a region nested inside `.cyber` would otherwise inherit
|
|
48
|
+
its bevel, its diamond pills, and its square chips. */
|
|
49
|
+
--ui-corner-shape: round;
|
|
50
|
+
--ui-radius-pill: initial;
|
|
51
|
+
--ui-radius-tight: initial;
|
|
46
52
|
--ui-shadow-inset: ;
|
|
47
53
|
/* The slab is depth cast in ink, not this element's line, so an `.edgeless`
|
|
48
54
|
button keeps it. Cleared rather than omitted, because a neobrutalist region
|
|
@@ -61,3 +67,50 @@
|
|
|
61
67
|
.neobrutalism .alert {
|
|
62
68
|
--_d-elevation: 1;
|
|
63
69
|
}
|
|
70
|
+
|
|
71
|
+
/* Solo: the same look painted onto the one element carrying the class, for an
|
|
72
|
+
element the package does not style. See docs/internal/solo-utilities.md and
|
|
73
|
+
the note on `.glass-solo`: own knobs and foundation tokens with literal
|
|
74
|
+
fallbacks, never a shared `--ui-*`, `--intent-*`, or `--elevation-color`
|
|
75
|
+
token, unlayered, and not for this package's own components. */
|
|
76
|
+
.neobrutalism-solo {
|
|
77
|
+
--_neo-offset: var(--neo-offset, 4px);
|
|
78
|
+
--_neo-ink: light-dark(var(--color-neutral-950, oklch(14.5% 0 0)), var(--color-neutral-50, oklch(98.5% 0 0)));
|
|
79
|
+
|
|
80
|
+
border: 2px solid var(--_neo-ink);
|
|
81
|
+
border-radius: 0;
|
|
82
|
+
box-shadow: var(--_neo-offset) var(--_neo-offset) 0 0 var(--_neo-ink);
|
|
83
|
+
transition:
|
|
84
|
+
box-shadow var(--motion-duration-normal, 200ms) var(--motion-ease, cubic-bezier(0.2, 0, 0, 1)),
|
|
85
|
+
transform var(--motion-duration-normal, 200ms) var(--motion-ease, cubic-bezier(0.2, 0, 0, 1));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* The press, only on something pressed: an action, not disabled. A solo
|
|
89
|
+
container -- a toast clicked to dismiss -- stays put, the way only `.btn` and
|
|
90
|
+
an opted-in card press under the aesthetic class. */
|
|
91
|
+
.neobrutalism-solo:is(
|
|
92
|
+
button,
|
|
93
|
+
a[href],
|
|
94
|
+
[role="button"],
|
|
95
|
+
summary,
|
|
96
|
+
input[type="button"],
|
|
97
|
+
input[type="submit"],
|
|
98
|
+
input[type="reset"]
|
|
99
|
+
):active:not(:disabled, [aria-disabled="true"]) {
|
|
100
|
+
box-shadow: 0 0 0 0 var(--_neo-ink);
|
|
101
|
+
transform: translate(var(--_neo-offset), var(--_neo-offset));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@media (prefers-reduced-motion: reduce) {
|
|
105
|
+
.neobrutalism-solo:is(
|
|
106
|
+
button,
|
|
107
|
+
a[href],
|
|
108
|
+
[role="button"],
|
|
109
|
+
summary,
|
|
110
|
+
input[type="button"],
|
|
111
|
+
input[type="submit"],
|
|
112
|
+
input[type="reset"]
|
|
113
|
+
):active:not(:disabled, [aria-disabled="true"]) {
|
|
114
|
+
transform: none;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -78,6 +78,14 @@
|
|
|
78
78
|
a grid, and a square is on it. */
|
|
79
79
|
--ui-clip-tight: none;
|
|
80
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
|
+
|
|
81
89
|
--ui-hover-transform: none;
|
|
82
90
|
--ui-active-transform: none;
|
|
83
91
|
--ui-backdrop: none;
|
|
@@ -86,3 +94,43 @@
|
|
|
86
94
|
font to fall back to but a monospace stack. */
|
|
87
95
|
font-family: var(--font-pixel, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
|
|
88
96
|
}
|
|
97
|
+
|
|
98
|
+
/* 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
|
|
100
|
+
the note on `.glass-solo`: own knobs and foundation tokens with literal
|
|
101
|
+
fallbacks, never a shared `--ui-*`, `--intent-*`, or `--elevation-color`
|
|
102
|
+
token, unlayered, and not for this package's own components. */
|
|
103
|
+
.pixel-solo {
|
|
104
|
+
--_pixel-unit: var(--pixel-unit, 4px);
|
|
105
|
+
--_pixel-ink: light-dark(var(--color-neutral-950, oklch(14.5% 0 0)), var(--color-neutral-50, oklch(98.5% 0 0)));
|
|
106
|
+
|
|
107
|
+
/* The clip removes a border at the straight sides, so the ring draws the
|
|
108
|
+
edge and the element's own border goes. */
|
|
109
|
+
border: 0;
|
|
110
|
+
border-radius: 0;
|
|
111
|
+
box-shadow: inset 0 0 0 var(--_pixel-unit) var(--_pixel-ink);
|
|
112
|
+
clip-path: polygon(
|
|
113
|
+
0 var(--_pixel-unit),
|
|
114
|
+
var(--_pixel-unit) var(--_pixel-unit),
|
|
115
|
+
var(--_pixel-unit) 0,
|
|
116
|
+
calc(100% - var(--_pixel-unit)) 0,
|
|
117
|
+
calc(100% - var(--_pixel-unit)) var(--_pixel-unit),
|
|
118
|
+
100% var(--_pixel-unit),
|
|
119
|
+
100% calc(100% - var(--_pixel-unit)),
|
|
120
|
+
calc(100% - var(--_pixel-unit)) calc(100% - var(--_pixel-unit)),
|
|
121
|
+
calc(100% - var(--_pixel-unit)) 100%,
|
|
122
|
+
var(--_pixel-unit) 100%,
|
|
123
|
+
var(--_pixel-unit) calc(100% - var(--_pixel-unit)),
|
|
124
|
+
0 calc(100% - var(--_pixel-unit))
|
|
125
|
+
);
|
|
126
|
+
font-family: var(--font-pixel, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/* The clip takes the focus outline too, so focus is a second inset layer under
|
|
130
|
+
the ring, wider than it so it shows -- shadows paint first-on-top. */
|
|
131
|
+
.pixel-solo:focus-visible {
|
|
132
|
+
box-shadow:
|
|
133
|
+
inset 0 0 0 var(--_pixel-unit) var(--_pixel-ink),
|
|
134
|
+
inset 0 0 0 calc(var(--_pixel-unit) + var(--focus-ring-width, 3px))
|
|
135
|
+
var(--focus-ring, color-mix(in srgb, var(--_pixel-ink) 64%, transparent));
|
|
136
|
+
}
|
package/dist/tw/box.css
CHANGED
|
@@ -153,6 +153,13 @@
|
|
|
153
153
|
color: var(--_fg);
|
|
154
154
|
border: var(--_width) solid var(--_edge);
|
|
155
155
|
border-radius: var(--ui-radius, var(--radius-control));
|
|
156
|
+
/* What the radius draws: a round arc by default, a straight cut under an
|
|
157
|
+
aesthetic that bevels (`.cyber`). `round` is the property's initial value,
|
|
158
|
+
so with no aesthetic in scope this is a no-op, and an engine without
|
|
159
|
+
`corner-shape` drops the line at parse time. A component that overrides the
|
|
160
|
+
radius after `@apply box` keeps this, so it bevels by the same amount it
|
|
161
|
+
rounds. See model.md#material-tokens. */
|
|
162
|
+
corner-shape: var(--ui-corner-shape, round);
|
|
156
163
|
box-shadow: var(--_shadow);
|
|
157
164
|
clip-path: var(--ui-clip, none);
|
|
158
165
|
transition:
|
|
@@ -91,6 +91,9 @@
|
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
/* A pill is a shape the author asked for, so it outranks the aesthetic's
|
|
95
|
+
radius. It still takes the corner shape from `box`, so under a bevelling
|
|
96
|
+
aesthetic the full radius cuts to points at both ends. */
|
|
94
97
|
&.pill {
|
|
95
98
|
@apply rounded-full;
|
|
96
99
|
}
|
|
@@ -94,8 +94,10 @@
|
|
|
94
94
|
@apply box inline-flex min-h-6 w-fit items-center gap-1 px-2.5 text-xs font-bold;
|
|
95
95
|
|
|
96
96
|
/* Pill by default, but an aesthetic that declares no radius has to mean it, so
|
|
97
|
-
the round corner is this component's fallback rather than a hardcoded value.
|
|
98
|
-
|
|
97
|
+
the round corner is this component's fallback rather than a hardcoded value.
|
|
98
|
+
An aesthetic that keeps pills distinct from its control corner says so
|
|
99
|
+
through `--ui-radius-pill` first. */
|
|
100
|
+
border-radius: var(--ui-radius-pill, var(--ui-radius, calc(infinity * 1px)));
|
|
99
101
|
clip-path: var(--ui-clip-tight, var(--ui-clip, none));
|
|
100
102
|
}
|
|
101
103
|
|
|
@@ -115,6 +117,8 @@
|
|
|
115
117
|
background: linear-gradient(90deg, var(--skeleton-base), var(--skeleton-sheen), var(--skeleton-base));
|
|
116
118
|
background-size: 200% 100%;
|
|
117
119
|
border-radius: var(--ui-radius, var(--radius-control));
|
|
120
|
+
/* Not a box, so it reads the corner shape itself, beside the radius. */
|
|
121
|
+
corner-shape: var(--ui-corner-shape, round);
|
|
118
122
|
}
|
|
119
123
|
|
|
120
124
|
/* The track is a box, the fill is not: `box.css`'s formula composes the frame,
|
|
@@ -160,14 +164,18 @@
|
|
|
160
164
|
background-color: var(--progress-surface, var(--_progress-surface));
|
|
161
165
|
border: var(--ui-border-width, var(--border-width)) solid var(--progress-edge, var(--_progress-edge));
|
|
162
166
|
/* Round by default; an aesthetic that declares no radius squares the track and
|
|
163
|
-
its fill together. */
|
|
164
|
-
border-radius: var(--ui-radius, calc(infinity * 1px));
|
|
167
|
+
its fill together, and a pill corner reaches both first. */
|
|
168
|
+
border-radius: var(--ui-radius-pill, var(--ui-radius, calc(infinity * 1px)));
|
|
169
|
+
/* Not a box either, and the fill below restates it, so a bevelled track
|
|
170
|
+
carries a bevelled fill rather than a pill inside a cut frame. */
|
|
171
|
+
corner-shape: var(--ui-corner-shape, round);
|
|
165
172
|
|
|
166
173
|
&::after {
|
|
167
174
|
content: "";
|
|
168
175
|
@apply absolute start-0 top-0 bottom-0 block h-full transition-[width] duration-(--motion-duration-slow) ease-(--motion-ease);
|
|
169
176
|
|
|
170
|
-
border-radius: var(--ui-radius, calc(infinity * 1px));
|
|
177
|
+
border-radius: var(--ui-radius-pill, var(--ui-radius, calc(infinity * 1px)));
|
|
178
|
+
corner-shape: var(--ui-corner-shape, round);
|
|
171
179
|
background-color: var(--progress-color, var(--intent-color));
|
|
172
180
|
width: var(--progress-value, 0%);
|
|
173
181
|
z-index: 2;
|
|
@@ -487,8 +487,10 @@
|
|
|
487
487
|
|
|
488
488
|
/* Capped at its own radius: a control radius is sized for a 40px control, and
|
|
489
489
|
14px of it on a 16px box renders as a circle, which is the one shape a
|
|
490
|
-
checkbox must not be. A squaring aesthetic still reaches it
|
|
491
|
-
|
|
490
|
+
checkbox must not be. A squaring aesthetic still reaches it, through the
|
|
491
|
+
chip corner first -- `.cyber` squares chips outright, which is also what
|
|
492
|
+
tells its checkbox from its diamond radio. */
|
|
493
|
+
border-radius: min(var(--ui-radius-tight, var(--ui-radius, var(--radius-small))), var(--radius-small));
|
|
492
494
|
|
|
493
495
|
/* Chip geometry: the tight silhouette, and an inset edge no thicker than the
|
|
494
496
|
line the aesthetic draws elsewhere. A 16px box cannot spend four pixels a
|
|
@@ -554,6 +556,10 @@
|
|
|
554
556
|
&::after {
|
|
555
557
|
@apply block size-2 scale-0 rounded-full opacity-0 transition-[transform,opacity] duration-(--motion-duration-normal) ease-(--motion-ease);
|
|
556
558
|
|
|
559
|
+
/* The dot is the ring's shape at a smaller size: a diamond inside a diamond
|
|
560
|
+
under a bevelling aesthetic, a circle inside a circle otherwise. */
|
|
561
|
+
corner-shape: var(--ui-corner-shape, round);
|
|
562
|
+
|
|
557
563
|
background-color: currentcolor;
|
|
558
564
|
content: "";
|
|
559
565
|
}
|
|
@@ -614,8 +620,10 @@
|
|
|
614
620
|
|
|
615
621
|
height: var(--switch-height);
|
|
616
622
|
width: var(--switch-width);
|
|
617
|
-
/* Pill by default; a squaring aesthetic reaches the track and the knob
|
|
618
|
-
|
|
623
|
+
/* Pill by default; a squaring aesthetic reaches the track and the knob
|
|
624
|
+
together, and an aesthetic with a pill corner of its own reaches both
|
|
625
|
+
through that first. */
|
|
626
|
+
border-radius: var(--ui-radius-pill, var(--ui-radius, calc(infinity * 1px)));
|
|
619
627
|
|
|
620
628
|
/* Chip geometry, for the reason given on `checkbox`: a 20px track has no more
|
|
621
629
|
room for a thick inset edge than a 16px box does. */
|
|
@@ -636,7 +644,10 @@
|
|
|
636
644
|
tone as the label beside it. Checked, it takes the mark colour like the
|
|
637
645
|
other two toggles. */
|
|
638
646
|
background-color: var(--color-text-secondary);
|
|
639
|
-
border-radius: var(--ui-radius, calc(infinity * 1px));
|
|
647
|
+
border-radius: var(--ui-radius-pill, var(--ui-radius, calc(infinity * 1px)));
|
|
648
|
+
/* A pseudo-element is not a box: the knob takes the track's corner shape
|
|
649
|
+
itself, so the two stay the same shape. */
|
|
650
|
+
corner-shape: var(--ui-corner-shape, round);
|
|
640
651
|
content: "";
|
|
641
652
|
height: var(--switch-knob);
|
|
642
653
|
width: var(--switch-knob);
|
|
@@ -320,7 +320,7 @@
|
|
|
320
320
|
|
|
321
321
|
/* Capped at its own radius: a control radius is sized for a control, and a key
|
|
322
322
|
cap this small would read as a lozenge. A squaring aesthetic still reaches it. */
|
|
323
|
-
border-radius: min(var(--ui-radius, var(--radius-small)), var(--radius-small));
|
|
323
|
+
border-radius: min(var(--ui-radius-tight, var(--ui-radius, var(--radius-small))), var(--radius-small));
|
|
324
324
|
clip-path: var(--ui-clip-tight, var(--ui-clip, none));
|
|
325
325
|
|
|
326
326
|
&:not(.primary, .secondary, .success, .warning, .destructive, .danger, .error, .info, .solid) {
|
|
@@ -338,7 +338,7 @@
|
|
|
338
338
|
--_d-elevation: 0;
|
|
339
339
|
--_d-ground: var(--intent-subtle);
|
|
340
340
|
|
|
341
|
-
border-radius: min(var(--ui-radius, var(--radius-small)), var(--radius-small));
|
|
341
|
+
border-radius: min(var(--ui-radius-tight, var(--ui-radius, var(--radius-small))), var(--radius-small));
|
|
342
342
|
clip-path: var(--ui-clip-tight, var(--ui-clip, none));
|
|
343
343
|
|
|
344
344
|
&:not(.primary, .secondary, .success, .warning, .destructive, .danger, .error, .info, .solid) {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 0.4.0
|
|
3
|
+
date: 2026-09-23
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 0.4.0
|
|
7
|
+
|
|
8
|
+
## Added
|
|
9
|
+
|
|
10
|
+
- **`.cyber`**, a fifth aesthetic from `@codenhub/styles/aesthetics/cyber` (and `/tw/aesthetics/cyber`, and both aggregates): bevelled corners, a 1px edge, and a glow in each component's own colour. Controls cut top-left and bottom-right, surfaces top-right and bottom-left; what is fully round (a radio, a switch, a pill, a badge) cuts to diamonds and pointed hexagons, and chips square. Knobs: `--cyber-cut`, `--cyber-shape`, `--cyber-shape-surface`, `--cyber-glow`, `--cyber-ink`, and the consumer-supplied `--font-cyber`. The bevel is drawn with `corner-shape`, which only Chromium supports today; Firefox and Safari get square cut corners and round round ones. See [Aesthetics → Cyber](../usage/aesthetics.md#cyber).
|
|
11
|
+
- **A solo class for every aesthetic**: `.glass-solo`, `.neobrutalism-solo`, `.pixel-solo`, `.chunky-tile-solo`, and `.cyber-solo` paint the aesthetic's look onto the one element carrying the class, for an element this package does not style -- a toast from another library, or a `<div>` on a page without the base stylesheet. Each ships in its aesthetic's existing entrypoint and needs nothing else loaded. See [Aesthetics → Solo classes](../usage/aesthetics.md#solo-classes).
|
|
12
|
+
- **Three material tokens**: `--ui-corner-shape` (what a corner draws: `round`, or a `bevel` cut), `--ui-radius-pill` (the corner of what is fully round by default), and `--ui-radius-tight` (the corner of chips, before their `--radius-small` cap). All three default to today's look, so nothing changes unless an aesthetic or your own CSS sets them. See [Customizing → Material tokens](../usage/customizing.md#material-tokens).
|
package/docs/changelog/index.md
CHANGED
package/docs/concepts.md
CHANGED
|
@@ -12,11 +12,11 @@ Read this before the usage guides. It explains the mental model every component,
|
|
|
12
12
|
|
|
13
13
|
Every component separates three independent choices, so each can change without a component-specific variant:
|
|
14
14
|
|
|
15
|
-
| Axis | Question | Classes
|
|
16
|
-
| ------------ | --------------- |
|
|
17
|
-
| Intent | Which color? | `.primary`, `.secondary`, `.success`, `.warning`, `.destructive`, `.info`
|
|
18
|
-
| Presentation | How much of it? | `.solid`, `.soft`, `.ghost`, `.edged`, `.edgeless`
|
|
19
|
-
| Aesthetic | Made of what? | `.neobrutalism`, `.glass`, `.pixel`, `.chunky-tile` from an optional aesthetic stylesheet | Cascades |
|
|
15
|
+
| Axis | Question | Classes | Inheritance |
|
|
16
|
+
| ------------ | --------------- | --------------------------------------------------------------------------------------------------- | ------------ |
|
|
17
|
+
| Intent | Which color? | `.primary`, `.secondary`, `.success`, `.warning`, `.destructive`, `.info` | Element only |
|
|
18
|
+
| Presentation | How much of it? | `.solid`, `.soft`, `.ghost`, `.edged`, `.edgeless` | Cascades |
|
|
19
|
+
| Aesthetic | Made of what? | `.neobrutalism`, `.glass`, `.pixel`, `.chunky-tile`, `.cyber` from an optional aesthetic stylesheet | Cascades |
|
|
20
20
|
|
|
21
21
|
```html
|
|
22
22
|
<section class="soft pixel">
|
package/docs/index.md
CHANGED
|
@@ -14,7 +14,7 @@ The package ships as ready-to-import compiled CSS, or as Tailwind CSS v4 source
|
|
|
14
14
|
- Publishes color, foundation, intent, presentation, and material design tokens as CSS custom properties, themed with `light-dark()`.
|
|
15
15
|
- Provides a reset, focus-visible handling, and responsive layout/content utilities.
|
|
16
16
|
- Provides composable classes for buttons, form controls and toggles, alerts, badges, loaders, progress, cards/panels, tables, quotes, code, and tooltips.
|
|
17
|
-
- Provides
|
|
17
|
+
- Provides five opt-in aesthetics that restyle the same components by setting material tokens, not by adding component variants, each with a solo class that paints the look onto an element the package does not style.
|
|
18
18
|
- Provides classless mappings so plain HTML elements pick up base styling without added classes, from a dedicated entrypoint.
|
|
19
19
|
- Publishes Tailwind CSS v4 source entrypoints alongside the compiled output.
|
|
20
20
|
|
|
@@ -23,7 +23,7 @@ The package ships as ready-to-import compiled CSS, or as Tailwind CSS v4 source
|
|
|
23
23
|
- No JavaScript. There is no runtime, no framework bindings, and no build-time code generation beyond the CSS itself.
|
|
24
24
|
- No semantic HTML, ARIA, keyboard behavior, focus management, validation, or announcement timing. See [Accessibility](./accessibility.md) for the exact line between what the CSS provides and what an application must still add.
|
|
25
25
|
- No theme-toggling logic. Theme selection is a class or attribute a consumer applies; wiring a toggle to system preference, storage, or a user setting is outside this package. The `@codenhub/theme` package covers that need for consumers who want it.
|
|
26
|
-
- No shipped font binaries. `.pixel
|
|
26
|
+
- No shipped font binaries. `.pixel`, `.chunky-tile`, and `.cyber` accept a consumer-supplied font and fall back to a system stack when none is given.
|
|
27
27
|
- No compatibility layer for removed tokens or classes. Breaking changes are documented, not silently aliased.
|
|
28
28
|
|
|
29
29
|
## Next steps
|