@codenhub/styles 0.1.0 → 0.2.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/README.md +8 -69
- package/dist/aesthetics/chunky-tile.css +1 -1
- package/dist/aesthetics/index.css +1 -1
- package/dist/components.css +1 -1
- package/dist/index.css +1 -1
- package/dist/native.css +1 -1
- package/dist/theme.css +1 -1
- package/dist/tw/aesthetics/chunky-tile.css +20 -2
- package/dist/tw/box.css +45 -2
- package/dist/tw/components/button.css +38 -29
- package/dist/tw/components/feedback.css +58 -41
- package/dist/tw/components/form.css +139 -47
- package/dist/tw/components/index.css +16 -6
- package/dist/tw/components/loader.css +2 -16
- package/dist/tw/components/surface.css +122 -5
- package/dist/tw/components/tooltip.css +102 -91
- package/dist/tw/elevation.css +57 -14
- package/dist/tw/index.css +23 -5
- package/dist/tw/intent.css +36 -12
- package/dist/tw/native.css +4 -20
- package/dist/tw/reset.css +40 -32
- package/dist/tw/theme.css +46 -8
- package/dist/tw/utilities/content.css +104 -18
- package/dist/tw/utilities/index.css +1 -1
- package/dist/tw/utilities/layout.css +33 -0
- package/docs/accessibility.md +68 -48
- package/docs/changelog/0.1.0.md +37 -0
- package/docs/changelog/0.1.1.md +10 -0
- package/docs/changelog/0.2.0.md +41 -0
- package/docs/changelog/index.md +11 -0
- package/docs/concepts.md +21 -46
- package/docs/index.md +18 -45
- package/docs/integrating/astro.md +13 -7
- package/docs/integrating/index.md +25 -16
- package/docs/integrating/nextjs.md +31 -8
- package/docs/integrating/svelte.md +14 -8
- package/docs/integrating/tailwind.md +31 -15
- package/docs/integrating/vue.md +13 -7
- package/docs/setup.md +31 -32
- package/docs/usage/aesthetics.md +31 -107
- package/docs/usage/buttons.md +24 -36
- package/docs/usage/composing.md +31 -102
- package/docs/usage/content-and-layout.md +65 -83
- package/docs/usage/customizing.md +25 -90
- package/docs/usage/feedback.md +25 -56
- package/docs/usage/forms.md +48 -80
- package/docs/usage/index.md +9 -17
- package/docs/usage/theming.md +25 -81
- package/docs/usage/tooltips.md +55 -35
- package/llms-full.txt +1193 -1510
- package/package.json +121 -44
package/docs/usage/aesthetics.md
CHANGED
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Aesthetics
|
|
3
3
|
description: The four shipped aesthetics in depth, including their documented exceptions.
|
|
4
|
+
order: 4
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# Aesthetics
|
|
7
8
|
|
|
8
|
-
An aesthetic decides what a component is _made of_: its radius, border
|
|
9
|
-
thickness, shadow, and shape. Aesthetics ship from opt-in entrypoints, so
|
|
10
|
-
importing the stylesheet is what makes the classes available:
|
|
9
|
+
An aesthetic decides what a component is _made of_: its radius, border thickness, shadow, and shape. Aesthetics ship from opt-in entrypoints, so importing the stylesheet is what makes the classes available:
|
|
11
10
|
|
|
12
11
|
```css
|
|
13
12
|
@import "@codenhub/styles";
|
|
14
13
|
@import "@codenhub/styles/aesthetics";
|
|
15
14
|
```
|
|
16
15
|
|
|
17
|
-
Import them after the base stylesheet. `.neobrutalism` and `.pixel` replace
|
|
18
|
-
the neutral border color, and they do so at zero specificity, so source order
|
|
19
|
-
is what lets them win.
|
|
16
|
+
Import them after the base stylesheet. `.neobrutalism` and `.pixel` replace the neutral border color, and they do so at zero specificity, so source order is what lets them win.
|
|
20
17
|
|
|
21
18
|
Like presentation, an aesthetic class cascades to any subtree:
|
|
22
19
|
|
|
@@ -34,33 +31,19 @@ Like presentation, an aesthetic class cascades to any subtree:
|
|
|
34
31
|
| `.pixel` | Corners cut by one grid unit, a chunky outline drawn as an inset ring, and the consumer-supplied `--font-pixel` over monospace. |
|
|
35
32
|
| `.chunky-tile` | Rounded slabs seated on a darker shade of themselves, with a press that moves the element down into its own bar. |
|
|
36
33
|
|
|
37
|
-
Aesthetics compose with every supported fill and edge pair. `.ghost.edgeless`
|
|
38
|
-
intentionally removes the visible material traits from components whose
|
|
39
|
-
aesthetic is expressed only through their fill, edge, or elevation.
|
|
34
|
+
Aesthetics compose with every supported fill and edge pair. `.ghost.edgeless` intentionally removes the visible material traits from components whose aesthetic is expressed only through their fill, edge, or elevation.
|
|
40
35
|
|
|
41
|
-
An explicit presentation on the element still wins over the aesthetic's
|
|
42
|
-
defaults. The aesthetic supplies edge thickness; presentation only decides
|
|
43
|
-
whether that edge is drawn.
|
|
36
|
+
An explicit presentation on the element still wins over the aesthetic's defaults. The aesthetic supplies edge thickness; presentation only decides whether that edge is drawn.
|
|
44
37
|
|
|
45
|
-
An aesthetic directly on a component likewise wins over an inherited
|
|
46
|
-
aesthetic. This includes tooltip pseudo-elements: `.tooltip.glass` gets the
|
|
47
|
-
complete glass bubble under a pixel ancestor, and `.tooltip.pixel` gets the
|
|
48
|
-
complete stepped bubble under a glass ancestor.
|
|
38
|
+
An aesthetic directly on a component likewise wins over an inherited aesthetic. This includes tooltip bubbles: `.tooltip-bubble.glass` gets the complete glass bubble under a pixel ancestor, and `.tooltip-bubble.pixel` gets the complete stepped bubble under a glass ancestor.
|
|
49
39
|
|
|
50
|
-
Each aesthetic that scales from one number publishes it as a knob — see
|
|
51
|
-
[Customizing → Aesthetic tokens](./customizing.md#aesthetic-tokens) for how
|
|
52
|
-
knobs resolve and where they can be set. This page covers what each
|
|
53
|
-
aesthetic looks like and where it makes documented exceptions.
|
|
40
|
+
Each aesthetic that scales from one number publishes it as a knob — see [Customizing → Aesthetic tokens](./customizing.md#aesthetic-tokens) for how knobs resolve and where they can be set. This page covers what each aesthetic looks like and where it makes documented exceptions.
|
|
54
41
|
|
|
55
42
|
## Neobrutalism
|
|
56
43
|
|
|
57
|
-
The shadow is cast in the component's own intent, so a success button throws
|
|
58
|
-
a green shadow and a destructive card a red one. With no intent, both the
|
|
59
|
-
outline and the shadow use the ink, which follows the theme rather than the
|
|
60
|
-
palette.
|
|
44
|
+
The shadow is cast in the component's own intent, so a success button throws a green shadow and a destructive card a red one. With no intent, both the outline and the shadow use the ink, which follows the theme rather than the palette.
|
|
61
45
|
|
|
62
|
-
`--neo-offset` is the knob: the shadow offset and the distance the press
|
|
63
|
-
travels are the same number, so scaling the look scales both at once.
|
|
46
|
+
`--neo-offset` is the knob: the shadow offset and the distance the press travels are the same number, so scaling the look scales both at once.
|
|
64
47
|
|
|
65
48
|
```html
|
|
66
49
|
<section class="neobrutalism" style="--neo-offset: 6px">
|
|
@@ -70,26 +53,15 @@ travels are the same number, so scaling the look scales both at once.
|
|
|
70
53
|
|
|
71
54
|
**Exceptions:**
|
|
72
55
|
|
|
73
|
-
- Content chips are left alone. A `.kbd`, `.code`, or `.pre` is content
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
- Only `.btn` and `.card.interactive` respond to hover and press. A card
|
|
77
|
-
becomes a control when it opts in with `.interactive`; a plain card, an
|
|
78
|
-
alert, or a panel is a container and stays put.
|
|
79
|
-
- An alert rests on the slab under this aesthetic and nowhere else. Here the
|
|
80
|
-
slab is a second ink line rather than depth, and an alert is the only
|
|
81
|
-
container the package rests flat, so without it a neobrutalist alert is
|
|
82
|
-
the one box on the page that reads as unfinished. It is a resting default,
|
|
83
|
-
so `.flat` still takes it off and `.floating` still doubles it.
|
|
56
|
+
- Content chips are left alone. A `.kbd`, `.code`, or `.pre` is content rather than structure, and an ink edge on one reads as a defect: a key cap keeps the quiet border color and a code chip keeps no border at all.
|
|
57
|
+
- Only `.btn` and an opted-in `.card` respond to hover and press. A card opts in with `.interactive`, or with one of its halves — `.hoverable` for the hover response alone, `.pressable` for the press alone; a plain card, an alert, or a panel is a container and stays put.
|
|
58
|
+
- An alert rests on the slab under this aesthetic and nowhere else. Here the slab is a second ink line rather than depth, and an alert is the only container the package rests flat, so without it a neobrutalist alert is the one box on the page that reads as unfinished. It is a resting default, so `.flat` still takes it off and `.floating` still doubles it.
|
|
84
59
|
|
|
85
60
|
## Glass
|
|
86
61
|
|
|
87
|
-
`.glass` needs something behind it to blur. On a flat page background it
|
|
88
|
-
renders as a plain translucent panel.
|
|
62
|
+
`.glass` needs something behind it to blur. On a flat page background it renders as a plain translucent panel.
|
|
89
63
|
|
|
90
|
-
`--glass-radius` and `--glass-radius-surface` are the corners, at `0.75rem`
|
|
91
|
-
and `1rem`. Glass is rounder than the base geometry because a translucent
|
|
92
|
-
panel with a tight corner reads as a cut-out rather than as a pane.
|
|
64
|
+
`--glass-radius` and `--glass-radius-surface` are the corners, at `0.75rem` and `1rem`. Glass is rounder than the base geometry because a translucent panel with a tight corner reads as a cut-out rather than as a pane.
|
|
93
65
|
|
|
94
66
|
```html
|
|
95
67
|
<section class="glass" style="--glass-radius-surface: 1.5rem">
|
|
@@ -99,18 +71,12 @@ panel with a tight corner reads as a cut-out rather than as a pane.
|
|
|
99
71
|
|
|
100
72
|
**Exceptions:**
|
|
101
73
|
|
|
102
|
-
- The blur applies to `.card`, `.panel`, `.alert`, and the tooltip bubble
|
|
103
|
-
|
|
104
|
-
of a dense cluster costs a composited layer apiece and reads as noise.
|
|
105
|
-
Controls still take the aesthetic's radius, border, and shadow.
|
|
106
|
-
- Under `prefers-reduced-transparency: reduce`, glass surfaces drop the blur
|
|
107
|
-
and become opaque.
|
|
74
|
+
- The blur applies to `.card`, `.panel`, `.alert`, and the tooltip bubble only. Controls stay solid and sit on the glass: a blur under every control of a dense cluster costs a composited layer apiece and reads as noise. Controls still take the aesthetic's radius, border, and shadow.
|
|
75
|
+
- Under `prefers-reduced-transparency: reduce`, glass surfaces drop the blur and become opaque.
|
|
108
76
|
|
|
109
77
|
## Pixel
|
|
110
78
|
|
|
111
|
-
`--pixel-unit` is one pixel of the imaginary low-resolution grid, `4px` by
|
|
112
|
-
default. One square unit is cut from each corner and the outline is one unit
|
|
113
|
-
thick, so the cut and what covers it are the same size.
|
|
79
|
+
`--pixel-unit` is one pixel of the imaginary low-resolution grid, `4px` by default. One square unit is cut from each corner and the outline is one unit thick, so the cut and what covers it are the same size.
|
|
114
80
|
|
|
115
81
|
```html
|
|
116
82
|
<section class="pixel" style="--pixel-unit: 6px">
|
|
@@ -120,44 +86,17 @@ thick, so the cut and what covers it are the same size.
|
|
|
120
86
|
|
|
121
87
|
**Exceptions:**
|
|
122
88
|
|
|
123
|
-
- Corners are one unit or nothing. Chips square instead of stepping —
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
table's `overflow: hidden` fights the clip and the corners square off
|
|
129
|
-
where the two meet, and progress and skeleton never read a clip at all —
|
|
130
|
-
a squaring aesthetic reaches them through `border-radius` alone. `.pre` is
|
|
131
|
-
the one exception that steps: it carries no clip override, so it inherits
|
|
132
|
-
the same polygon a button or a card gets, and with no border by default
|
|
133
|
-
the cut shows with no ring around it.
|
|
134
|
-
- The outline is an inset ring, because a clip removes a real border — and
|
|
135
|
-
the focus ring, for the same reason, is a second inset layer rather than
|
|
136
|
-
an outline. Both are the element's own edge rather than a shadow, so the
|
|
137
|
-
border answers `.edged` and `.edgeless` the way a border does: a
|
|
138
|
-
`.edgeless` badge and a `.solid` button draw none, a `.edged` card draws
|
|
139
|
-
one, and a field keeps one whatever a container asks for.
|
|
140
|
-
- The tooltip trigger and `.radio` are hardcoded past the clip rather than
|
|
141
|
-
reached by it. `.tooltip-icon` forces `clip-path: none`, since a stepped
|
|
142
|
-
badge would clip its own bubble away with it. `.radio` forces the same,
|
|
143
|
-
because the circle is the only thing telling it from a checkbox at a
|
|
144
|
-
glance, and a stepped polygon would square it.
|
|
145
|
-
- `--font-pixel` is yours to supply. The package ships no font binary, so the
|
|
146
|
-
aesthetic has no network side effect and falls back to the monospace
|
|
147
|
-
stack.
|
|
89
|
+
- Corners are one unit or nothing. Chips square instead of stepping — badges, key caps, code, checkboxes, and switches all read `--ui-clip-tight`, which this aesthetic sets to none, because one unit off each corner of a 24px badge is a bite rather than a corner. Tables, progress bars, and skeletons square too, each for its own reason: a table's `overflow: hidden` fights the clip and the corners square off where the two meet, and progress and skeleton never read a clip at all — a squaring aesthetic reaches them through `border-radius` alone. `.pre` is the one exception that steps: it carries no clip override, so it inherits the same polygon a button or a card gets, and with no border by default the cut shows with no ring around it.
|
|
90
|
+
- The outline is an inset ring, because a clip removes a real border — and the focus ring, for the same reason, is a second inset layer rather than an outline. Both are the element's own edge rather than a shadow, so the border answers `.edged` and `.edgeless` the way a border does: a `.edgeless` badge and a `.solid` button draw none, a `.edged` card draws one, and a field keeps one whatever a container asks for.
|
|
91
|
+
- The tooltip trigger and `.radio` are hardcoded past the clip rather than reached by it. `.tooltip-icon` forces `clip-path: none` because the docs call it a circular fixed identity, the same reason `.radio` forces it: the circle is the only thing telling it from a checkbox at a glance, and a stepped polygon would square either one.
|
|
92
|
+
- `--font-pixel` is yours to supply. The package ships no font binary, so the aesthetic has no network side effect and falls back to the monospace stack.
|
|
93
|
+
- Numeral legibility depends on the face you pick. At UI sizes -- pagination, counters, small badges -- some pixel/bitmap faces render lookalike digits such as `2`/`8` or `3`/`9` closely enough to misread at a glance; this is a property of the font, not something the aesthetic's CSS can correct. Prefer a face with clearly distinct digit shapes wherever small numeric text matters.
|
|
148
94
|
|
|
149
95
|
## Chunky tile
|
|
150
96
|
|
|
151
|
-
Rounded slabs sitting on a darker shade of themselves, pressed flat on
|
|
152
|
-
click. The look mobile learning and game apps use; it is named for what it
|
|
153
|
-
is made of rather than for any one product.
|
|
97
|
+
Rounded slabs sitting on a darker shade of themselves, pressed flat on click. The look mobile learning and game apps use; it is named for what it is made of rather than for any one product.
|
|
154
98
|
|
|
155
|
-
`--tile-lift` is how deep the bar is and how far a press travels, `4px` by
|
|
156
|
-
default. One number drives both, so the top edge drops by exactly the bar's
|
|
157
|
-
depth and the bottom edge does not move. `--tile-radius` is the corner,
|
|
158
|
-
`0.75rem`, and it is one value rather than a control/surface pair on
|
|
159
|
-
purpose — the shared corner is what makes a button and the card it sits in
|
|
160
|
-
read as the same object.
|
|
99
|
+
`--tile-lift` is how deep the bar is and how far a press travels, `4px` by default. One number drives both, so the top edge drops by exactly the bar's depth and the bottom edge does not move. `--tile-radius` is the corner, `0.75rem`, and it is one value rather than a control/surface pair on purpose — the shared corner is what makes a button and the card it sits in read as the same object.
|
|
161
100
|
|
|
162
101
|
```html
|
|
163
102
|
<section class="chunky-tile" style="--tile-lift: 6px; --tile-radius: 0.5rem">
|
|
@@ -165,33 +104,18 @@ read as the same object.
|
|
|
165
104
|
</section>
|
|
166
105
|
```
|
|
167
106
|
|
|
168
|
-
The bar is a darker shade of the element's own colour rather than a shadow
|
|
169
|
-
under it, so it follows every intent: a success button sits on a dark green
|
|
170
|
-
edge, a destructive one on a dark red edge, and a plain card on a grey edge
|
|
171
|
-
that matches the line it already draws. Measured on the light palette,
|
|
172
|
-
`.btn.success` is `#007a55` on `#004c34`. On a dark page a near-black
|
|
173
|
-
surface cannot go darker, so its bar lands lighter and reads as a rim.
|
|
107
|
+
The bar is a darker shade of the element's own colour rather than a shadow under it, so it follows every intent: a success button sits on a dark green edge, a destructive one on a dark red edge, and a plain card on a grey edge that matches the line it already draws. Measured on the light palette, `.btn.success` is `#007a55` on `#004c34`. On a dark page a near-black surface cannot go darker, so its bar lands lighter and reads as a rim.
|
|
174
108
|
|
|
175
109
|
**Exceptions:**
|
|
176
110
|
|
|
177
|
-
- Only what the registry rests above zero sits on a bar — buttons and cards.
|
|
178
|
-
A badge, chip, field, or tooltip is flat until it asks for depth with
|
|
179
|
-
`.raised`, and `.flat` takes the bar off anything:
|
|
111
|
+
- Only what the registry rests above zero sits on a bar — buttons and cards. A badge, chip, field, or tooltip is flat until it asks for depth with `.raised`, and `.flat` takes the bar off anything:
|
|
180
112
|
|
|
181
113
|
```html
|
|
182
114
|
<div class="card soft edgeless info flat">Promo panel, deliberately flat</div>
|
|
183
115
|
```
|
|
184
116
|
|
|
185
|
-
- `--font-rounded` is yours to supply. A heavy rounded grotesque is most of
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
-
|
|
189
|
-
|
|
190
|
-
uppercase buttons, write that rule in your own stylesheet.
|
|
191
|
-
- `.card.interactive` presses like a `.btn`, so a chunky answer tile drops
|
|
192
|
-
onto its bar on click. A plain `.card` stays put.
|
|
193
|
-
- The shipped `.primary` is a near-black monochrome, which leaves a primary
|
|
194
|
-
button's bar almost invisible against its own plate; give `.primary` a hue
|
|
195
|
-
if it is the call to action.
|
|
196
|
-
- Depth in this aesthetic is the bar, never a blurred drop shadow. Anything
|
|
197
|
-
the registry rests flat sits flush on the page.
|
|
117
|
+
- `--font-rounded` is yours to supply. A heavy rounded grotesque is most of this look and the package ships no font binary, so with nothing supplied the page's own stack is used.
|
|
118
|
+
- Action labels are drawn heavier and slightly tracked. Casing is left alone: how a label is worded is the application's decision, so if you want uppercase buttons, write that rule in your own stylesheet.
|
|
119
|
+
- `.card.interactive` presses like a `.btn`, so a chunky answer tile drops onto its bar on click; `.card.pressable` gets the same press on its own. A plain `.card` stays put.
|
|
120
|
+
- The shipped `.primary` is a near-black-or-near-white monochrome depending on theme, and the bar mixes toward a fixed black regardless of theme. In light theme this leaves a primary button's bar almost invisible against its own near-black plate (measured `1.04:1`, 10 units of sRGB distance); in dark theme the near-white plate against the same black-anchored bar reads fine (`2.48:1`, 154 units). Give `.primary` a hue of its own if it is the call to action and needs the depth cue in both themes.
|
|
121
|
+
- Depth in this aesthetic is the bar, never a blurred drop shadow. Anything the registry rests flat sits flush on the page.
|
package/docs/usage/buttons.md
CHANGED
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Buttons
|
|
3
3
|
description: Button classes, intents, sizes, and states.
|
|
4
|
+
order: 5
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# Buttons
|
|
7
8
|
|
|
8
|
-
Use `.btn` with one optional intent class, one optional fill class, one
|
|
9
|
-
optional edge class, optional size class, and optional state. See
|
|
10
|
-
[Composing](./composing.md) for how the intent, presentation, and aesthetic
|
|
11
|
-
axes combine on any component.
|
|
9
|
+
Use `.btn` with one optional intent class, one optional fill class, one optional edge class, optional size class, and optional state. See [Composing](./composing.md) for how the intent, presentation, and aesthetic axes combine on any component.
|
|
12
10
|
|
|
13
|
-
Intent classes map color tokens into button tone slots.
|
|
14
|
-
[Presentation](./composing.md#presentation) decides how much of that intent
|
|
15
|
-
reaches background, text, border, and hover.
|
|
11
|
+
Intent classes map color tokens into button tone slots. [Presentation](./composing.md#presentation) decides how much of that intent reaches background, text, border, and hover.
|
|
16
12
|
|
|
17
13
|
Intent classes:
|
|
18
14
|
|
|
@@ -26,29 +22,22 @@ Intent classes:
|
|
|
26
22
|
| `.destructive`, `.danger`, `.error` | Destructive or error action. |
|
|
27
23
|
| `.info` | Informational action. |
|
|
28
24
|
|
|
29
|
-
Every component that supports intent accepts the same list and reads the
|
|
30
|
-
same [intent tokens](./theming.md#intent-tokens), so a custom intent class
|
|
31
|
-
works everywhere without touching a component.
|
|
25
|
+
Every component that supports intent accepts the same list and reads the same [intent tokens](./theming.md#intent-tokens), so a custom intent class works everywhere without touching a component.
|
|
32
26
|
|
|
33
|
-
`.neutral` is the only intent that does not fill all the way. Its color is
|
|
34
|
-
the page's own text color, so a full fill of it would be a black or white
|
|
35
|
-
slab — the loudest element on the page, for the intent that means nothing in
|
|
36
|
-
particular. It stops at `--intent-fill-max`, which is why a `.solid` neutral
|
|
37
|
-
is a quiet plate a step past `.soft` rather than a block of ink. Set the slot
|
|
38
|
-
on your own intent classes; see [Theming → Intent tokens](./theming.md#intent-tokens).
|
|
27
|
+
`.neutral` is the only intent that does not fill all the way. Its color is the page's own text color, so a full fill of it would be a black or white slab — the loudest element on the page, for the intent that means nothing in particular. It stops at `--intent-fill-max`, which is why a `.solid` neutral is a quiet plate a step past `.soft` rather than a block of ink. Set the slot on your own intent classes; see [Theming → Intent tokens](./theming.md#intent-tokens).
|
|
39
28
|
|
|
40
29
|
Size and shape classes:
|
|
41
30
|
|
|
42
|
-
| Class | Purpose
|
|
43
|
-
| ---------------------------------------------------------------------- |
|
|
44
|
-
| `.pill` | Fully rounded button corners (`border-radius: 9999px`).
|
|
45
|
-
| `.sm` | Smaller button.
|
|
46
|
-
| `.lg` | Larger button.
|
|
47
|
-
| `.p-
|
|
48
|
-
| `.p-
|
|
49
|
-
| `.
|
|
50
|
-
| `.
|
|
51
|
-
| `.disabled`, `[disabled]`, `[aria-disabled="true"]`, `[data-disabled]` | Disabled styling.
|
|
31
|
+
| Class | Purpose |
|
|
32
|
+
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
33
|
+
| `.pill` | Fully rounded button corners (`border-radius: 9999px`). |
|
|
34
|
+
| `.sm` | Smaller button. |
|
|
35
|
+
| `.lg` | Larger button. |
|
|
36
|
+
| `.p-xs`, `.dense` | Tightest padding modifier (`px-2 py-0.5`; icon `p-0.5`). Also drops the `--control-height` minimum so a chip-sized button fits a table cell or a dense toolbar; every other size and padding class keeps that floor for a labeled button. |
|
|
37
|
+
| `.p-sm`, `.compact` | Compact padding modifier (`px-2.5 py-1`; icon `p-1`). |
|
|
38
|
+
| `.p-lg`, `.spacious` | Spacious padding modifier (`px-6 py-3`; icon `p-3`). |
|
|
39
|
+
| `.icon` | Square icon button, sized to its own padding rather than to `--control-height`. Use an accessible name in HTML. |
|
|
40
|
+
| `.disabled`, `[disabled]`, `[aria-disabled="true"]`, `[data-disabled]` | Disabled styling. |
|
|
52
41
|
|
|
53
42
|
Examples:
|
|
54
43
|
|
|
@@ -58,17 +47,16 @@ Examples:
|
|
|
58
47
|
<button class="btn warning soft">Warning soft</button>
|
|
59
48
|
<button class="btn destructive ghost edgeless">Danger ghost</button>
|
|
60
49
|
<button class="btn icon primary" aria-label="Create">+</button>
|
|
61
|
-
<button class="btn primary
|
|
50
|
+
<button class="btn primary" disabled>
|
|
51
|
+
<span class="loader sm" aria-hidden="true"></span>
|
|
52
|
+
Saving
|
|
53
|
+
</button>
|
|
62
54
|
```
|
|
63
55
|
|
|
64
|
-
|
|
65
|
-
it with disabled behavior so users cannot trigger duplicate work. Loading
|
|
66
|
-
buttons keep only opacity and transform transitions active so spinner and
|
|
67
|
-
surface colors stay synchronized when theme tokens change.
|
|
56
|
+
## Loading
|
|
68
57
|
|
|
69
|
-
|
|
70
|
-
(`--ui-active-transform`, `scale(0.97)`). [Aesthetics](./aesthetics.md) swap
|
|
71
|
-
in their own press gesture, and `prefers-reduced-motion` turns it off.
|
|
58
|
+
The package ships no loading state of its own: a loading button is a disabled `.btn` composed with a real [`.loader`](./feedback.md) child, the same activity indicator every other component uses. Disable the button so users cannot trigger duplicate work, and drop a `.loader` in beside the label — any of its variants (`.dots-wave`, `.bars-wave`, and so on) work the same way. Whether to also hide the label is your call; the package has no built-in mechanism for that.
|
|
72
59
|
|
|
73
|
-
|
|
74
|
-
|
|
60
|
+
On the base look a press scales the button down a little (`--ui-active-transform`, `scale(0.97)`). [Aesthetics](./aesthetics.md) swap in their own press gesture, and `prefers-reduced-motion` turns it off.
|
|
61
|
+
|
|
62
|
+
Use `<button>` for actions or an accessible link for navigation; see [Accessibility](../accessibility.md).
|
package/docs/usage/composing.md
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Composing
|
|
3
3
|
description: Combining intent, presentation, and aesthetic on a component, and the axis-support matrix.
|
|
4
|
+
order: 1
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# Composing the three axes
|
|
7
8
|
|
|
8
|
-
See [Concepts](../concepts.md#the-three-axes) for what each axis means. This
|
|
9
|
-
page covers how they combine in markup, what the presentation classes mean in
|
|
10
|
-
depth, which components support which axis, and elevation.
|
|
9
|
+
See [Concepts](../concepts.md#the-three-axes) for what each axis means. This page covers how they combine in markup, what the presentation classes mean in depth, which components support which axis, and elevation.
|
|
11
10
|
|
|
12
11
|
## Combining axes
|
|
13
12
|
|
|
@@ -19,25 +18,13 @@ depth, which components support which axis, and elevation.
|
|
|
19
18
|
</div>
|
|
20
19
|
```
|
|
21
20
|
|
|
22
|
-
A declaration on the element always beats one inherited from a container, so
|
|
23
|
-
a container sets the default look for its subtree and any element opts out.
|
|
21
|
+
A declaration on the element always beats one inherited from a container, so a container sets the default look for its subtree and any element opts out.
|
|
24
22
|
|
|
25
|
-
Intent classes do not cascade. `.primary` and its siblings stay on the
|
|
26
|
-
element that shows the intent, because a container silently recoloring every
|
|
27
|
-
descendant is a trap rather than a feature — see
|
|
28
|
-
[Theming → Intent tokens](./theming.md#intent-tokens). Use `.neutral` to opt
|
|
29
|
-
an element back out of an inherited intent context. Presentation and
|
|
30
|
-
[material tokens](./customizing.md#material-tokens) do cascade, so a
|
|
31
|
-
container can set the look of its whole subtree while any element overrides
|
|
32
|
-
it.
|
|
23
|
+
Intent classes do not cascade. `.primary` and its siblings stay on the element that shows the intent, because a container silently recoloring every descendant is a trap rather than a feature — see [Theming → Intent tokens](./theming.md#intent-tokens). Use `.neutral` to opt an element back out of an inherited intent context. Presentation and [material tokens](./customizing.md#material-tokens) do cascade, so a container can set the look of its whole subtree while any element overrides it.
|
|
33
24
|
|
|
34
25
|
## Presentation
|
|
35
26
|
|
|
36
|
-
Presentation has two independent axes. `.solid`, `.soft`, and `.ghost`
|
|
37
|
-
decide how much intent fills a component; `.edged` and `.edgeless` decide
|
|
38
|
-
whether it draws a boundary. They set
|
|
39
|
-
[presentation tokens](./customizing.md#presentation-tokens) and nothing
|
|
40
|
-
else, so they work on the component itself or on any ancestor.
|
|
27
|
+
Presentation has two independent axes. `.solid`, `.soft`, and `.ghost` decide how much intent fills a component; `.edged` and `.edgeless` decide whether it draws a boundary. They set [presentation tokens](./customizing.md#presentation-tokens) and nothing else, so they work on the component itself or on any ancestor.
|
|
41
28
|
|
|
42
29
|
| Class | Look |
|
|
43
30
|
| ----------- | ---------------------------------------------------------- |
|
|
@@ -57,6 +44,8 @@ Values each class ships:
|
|
|
57
44
|
| `.edged` | | | `100%` |
|
|
58
45
|
| `.edgeless` | | | `0%` |
|
|
59
46
|
|
|
47
|
+
`.card`, `.panel`, `.alert`, `.kbd`, `.code`, and `.pre` override `.soft`'s fill at rest to `0%` for the no-named-intent case only, and reveal it on `.card`'s own `:hover`/`.interactive`/`.hoverable` -- the one place a component remaps what a presentation class resolves to for part of its intent range, the same way text controls cap it at `6%` cascaded. A named intent on any of the six keeps the ordinary `12%`. `.badge` reads the identical intent/presentation classes but keeps its 12% wash at rest even with no named intent, since it has no border to fall back on and would otherwise disappear rather than read quieter. See [Content and layout → Surfaces](./content-and-layout.md#surfaces) and [Feedback](./feedback.md).
|
|
48
|
+
|
|
60
49
|
The combinations mean exactly what they spell:
|
|
61
50
|
|
|
62
51
|
```html
|
|
@@ -68,21 +57,11 @@ The combinations mean exactly what they spell:
|
|
|
68
57
|
<!-- A field sunk into the page -->
|
|
69
58
|
```
|
|
70
59
|
|
|
71
|
-
A fill class never decides an edge and an edge class never decides a fill.
|
|
72
|
-
Use `.solid.edgeless` for a filled box with no line — and prefer it on a
|
|
73
|
-
neutral one, because a capped fill leaves the box translucent and the edge
|
|
74
|
-
blend then paints a second coat of the same tint rather than disappearing
|
|
75
|
-
into it (measured 1.53:1 against its own plate in light, 1.82:1 in dark).
|
|
60
|
+
A fill class never decides an edge and an edge class never decides a fill. Use `.solid.edgeless` for a filled box with no line — and prefer it on a neutral one, because a capped fill leaves the box translucent and the edge blend then paints a second coat of the same tint rather than disappearing into it (measured 1.53:1 against its own plate in light, 1.82:1 in dark).
|
|
76
61
|
|
|
77
|
-
The edge is the silhouette and nothing else. Rules _inside_ a component that
|
|
78
|
-
has an inside are a separate switch — see
|
|
79
|
-
[Content and layout → Table rules](./content-and-layout.md#table-rules) —
|
|
80
|
-
because tying them to this axis made them arrive by implication rather than
|
|
81
|
-
by request.
|
|
62
|
+
The edge is the silhouette and nothing else. Rules _inside_ a component that has an inside are a separate switch — see [Content and layout → Table rules](./content-and-layout.md#table-rules) — because tying them to this axis made them arrive by implication rather than by request.
|
|
82
63
|
|
|
83
|
-
With no presentation class in scope, each component keeps its own default:
|
|
84
|
-
buttons are filled, alerts and badges are tinted, surfaces and controls are
|
|
85
|
-
neutral and bordered.
|
|
64
|
+
With no presentation class in scope, each component keeps its own default: buttons are filled, alerts and badges are tinted, surfaces and controls are neutral and bordered.
|
|
86
65
|
|
|
87
66
|
| Component | Reads |
|
|
88
67
|
| ----------------------------------------------- | ------------------------------------------- |
|
|
@@ -94,15 +73,10 @@ neutral and bordered.
|
|
|
94
73
|
| `.checkbox`, `.radio` | A capped fill, checked and unchecked. |
|
|
95
74
|
| `.switch` | Border, border width, and a capped fill. |
|
|
96
75
|
| `.quote` | Fill, text, border, and width. |
|
|
97
|
-
| `.code`, `.pre`, `.tooltip`
|
|
76
|
+
| `.code`, `.pre`, `.tooltip-bubble` | Fill, text, border, and material. |
|
|
98
77
|
| `.loader`, `.skeleton`, `.progress`, `.divider` | Intent only, not presentation. |
|
|
99
78
|
|
|
100
|
-
|
|
101
|
-
text legible when a `.solid` container reaches a field nobody classed. A
|
|
102
|
-
fill class written on the control itself names its own cap instead, because
|
|
103
|
-
that is a consumer describing what they want: `.soft` takes `12%` and
|
|
104
|
-
`.solid` takes `20%`. A switch caps at `40%`, where its three fills separate
|
|
105
|
-
far enough to read on a track.
|
|
79
|
+
A text control caps a **cascaded** fill at `6%`, keeping typed text legible when a `.solid` container reaches a field nobody classed. A fill class written on the control itself names its own cap instead, because that is a consumer describing what they want: `.soft` takes `12%` and `.solid` takes `20%`.
|
|
106
80
|
|
|
107
81
|
| On a text control | fill |
|
|
108
82
|
| ----------------- | ---- |
|
|
@@ -111,45 +85,21 @@ far enough to read on a track.
|
|
|
111
85
|
| `.soft` | 12% |
|
|
112
86
|
| `.solid` | 20% |
|
|
113
87
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
drop their line at all. See [Forms](./forms.md) for the full toggle and text
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
bubble.
|
|
123
|
-
|
|
124
|
-
With no intent the bubble is `--color-tooltip`, the one plate in the package
|
|
125
|
-
chosen per theme rather than derived from an intent. Composed like
|
|
126
|
-
everything else it was `20%` of the page's ink over the surface tone, which
|
|
127
|
-
steps _lighter_ than the surface on a dark page and _darker_ on a light
|
|
128
|
-
one — so the dark bubble read as lifted and the light one as a mid-grey
|
|
129
|
-
slab two steps darker than every card on the screen. The token states each
|
|
130
|
-
direction instead: near-white over a light page, a mid grey over a dark
|
|
131
|
-
one. `--color-tooltip-contrast` is its ink.
|
|
132
|
-
|
|
133
|
-
Near-white is only 1.04:1 against a light page, so the bubble draws a
|
|
134
|
-
hairline as well, and floors it: a container's `.solid` or `.edgeless`
|
|
135
|
-
cannot take the boundary away from a bubble nobody classed, while
|
|
136
|
-
`.tooltip.edgeless` still can. The ground keeps the bubble opaque at every
|
|
137
|
-
fill, so a cascaded presentation leaves it readable over whatever it floats
|
|
138
|
-
above. It reads the material tokens too, so an [aesthetic](./aesthetics.md)
|
|
139
|
-
in scope shapes the bubble like any other component. See
|
|
140
|
-
[Tooltips](./tooltips.md) for the full tooltip reference.
|
|
88
|
+
A toggle's cap does not have that split: it is `40%` whether the presentation class is on the checkbox/radio/switch itself or cascaded from a container, the same value for all three. That is where an unchecked `.ghost` (0%), `.soft`/default (12%), and `.solid` (the cap) read as three distinct plates instead of shades of the same wash. Being checked pins the fill to `100%` instead, whatever the presentation class asks — a checkbox, a radio, and a switch each have one "on" look, and presentation only shapes them unchecked. See [Forms](./forms.md) for the full toggle reference.
|
|
89
|
+
|
|
90
|
+
The same split governs the edge: a cascaded `.edgeless` is floored and the element's own is honoured — except on `.checkbox` and `.radio`, which never drop their line at all, and on `.switch.ghost`, which keeps its line specifically against its own `.edgeless`: `.ghost` already asks for zero fill, so removing the line too would leave nothing marking the track but the knob. `.switch.ghost.edgeless` renders the same as `.switch.ghost.edged` rather than as a distinct, boundary-less look. See [Forms](./forms.md) for the full toggle and text control reference.
|
|
91
|
+
|
|
92
|
+
A tooltip bubble is filled, over a tinted ground. An intent fills it with that intent's own color: `.tooltip-bubble.primary` is the primary color, black on a light page and white on a dark one, and `.tooltip-bubble.destructive` is a red bubble.
|
|
93
|
+
|
|
94
|
+
With no intent the bubble is `--color-tooltip`, the one plate in the package chosen per theme rather than derived from an intent. Composed like everything else it was `20%` of the page's ink over the surface tone, which steps _lighter_ than the surface on a dark page and _darker_ on a light one — so the dark bubble read as lifted and the light one as a mid-grey slab two steps darker than every card on the screen. The token states each direction instead: near-white over a light page, a mid grey over a dark one. `--color-tooltip-contrast` is its ink.
|
|
95
|
+
|
|
96
|
+
Near-white is only 1.04:1 against a light page, so the bubble draws a hairline as well, and floors it: a container's `.solid` or `.edgeless` cannot take the boundary away from a bubble nobody classed, while `.tooltip-bubble.edgeless` still can. The ground keeps the bubble opaque at every fill, so a cascaded presentation leaves it readable over whatever it floats above. It reads the material tokens too, so an [aesthetic](./aesthetics.md) in scope shapes the bubble like any other component. See [Tooltips](./tooltips.md) for the full tooltip reference.
|
|
141
97
|
|
|
142
98
|
## Component axis reference
|
|
143
99
|
|
|
144
|
-
This table is the authoritative axis map for demonstrated components. "Shape"
|
|
145
|
-
means the component reads applicable radius, border-width, shadow, hover, or
|
|
146
|
-
clip/edge material tokens; an aesthetic may intentionally affect only the
|
|
147
|
-
materials that component draws.
|
|
100
|
+
This table is the authoritative axis map for demonstrated components. "Shape" means the component reads applicable radius, border-width, shadow, hover, or clip/edge material tokens; an aesthetic may intentionally affect only the materials that component draws.
|
|
148
101
|
|
|
149
|
-
Presentation is two independent axes, so they get a column each. A `Yes` means
|
|
150
|
-
the class changes what the component computes, which
|
|
151
|
-
`tests/browser/axes.spec.ts` asserts against `registry.json` in both
|
|
152
|
-
directions: an axis marked `Yes` must move, and an axis marked `No` must not.
|
|
102
|
+
Presentation is two independent axes, so they get a column each. A `Yes` means the class changes what the component computes, which `tests/browser/axes.spec.ts` asserts against `registry.json` in both directions: an axis marked `Yes` must move, and an axis marked `No` must not.
|
|
153
103
|
|
|
154
104
|
| Components | Intent | Fill | Edge | Aesthetic material |
|
|
155
105
|
| --------------------------------------------------------- | ------ | ---- | ---- | ------------------ |
|
|
@@ -165,27 +115,18 @@ directions: an axis marked `Yes` must move, and an axis marked `No` must not.
|
|
|
165
115
|
| `.switch` | Yes | Yes | Yes | Radius and edge |
|
|
166
116
|
| `.progress` | Yes | No | No | No |
|
|
167
117
|
| `.code`, `.pre` | Yes | Yes | Yes | Radius and clip |
|
|
168
|
-
| `.tooltip`
|
|
118
|
+
| `.tooltip-bubble` | Yes | Yes | Yes | Shape and overlay |
|
|
169
119
|
| `.table-wrap`, `.quote-inline`, layout and text utilities | No | No | No | No |
|
|
170
120
|
|
|
171
|
-
`.checkbox` and `.radio` are the only components that read one axis and not
|
|
172
|
-
the other. Their line is the only thing marking an unchecked box, so it is
|
|
173
|
-
drawn whatever the edge class says, from a container or from the element
|
|
174
|
-
itself. `.edgeless` on either is unsupported rather than merely discouraged.
|
|
121
|
+
`.checkbox` and `.radio` are the only components that read one axis and not the other. Their line is the only thing marking an unchecked box, so it is drawn whatever the edge class says, from a container or from the element itself. `.edgeless` on either is unsupported rather than merely discouraged.
|
|
175
122
|
|
|
176
|
-
Every other text control floors a _cascaded_ `.edgeless` and honours its own:
|
|
177
|
-
a `.edgeless` toolbar will not erase the line of a field nobody classed, while
|
|
178
|
-
`.ipt.edgeless` is a consumer describing what they want and gets it.
|
|
123
|
+
Every other text control floors a _cascaded_ `.edgeless` and honours its own: a `.edgeless` toolbar will not erase the line of a field nobody classed, while `.ipt.edgeless` is a consumer describing what they want and gets it.
|
|
179
124
|
|
|
180
|
-
Intent aliases such as `.danger` and `.error` occupy the same intent axis as
|
|
181
|
-
`.destructive`; they do not add component behavior. State and modifier
|
|
182
|
-
classes such as `.interactive`, `.loading`, `.invalid`, `.compact`, and
|
|
183
|
-
`.vertical` sit above this map and are documented with their component.
|
|
125
|
+
Intent aliases such as `.danger` and `.error` occupy the same intent axis as `.destructive`; they do not add component behavior. State and modifier classes such as `.interactive`, `.hoverable`, `.pressable`, `.invalid`, `.compact`, and `.vertical` sit above this map and are documented with their component.
|
|
184
126
|
|
|
185
127
|
## Elevation
|
|
186
128
|
|
|
187
|
-
Depth is a modifier, not an axis. Nothing is raised until an
|
|
188
|
-
[aesthetic](./aesthetics.md) draws depth or one of these classes asks for it.
|
|
129
|
+
Depth is a modifier, not an axis. Nothing is raised until an [aesthetic](./aesthetics.md) draws depth or one of these classes asks for it.
|
|
189
130
|
|
|
190
131
|
| Class | Alias | Depth |
|
|
191
132
|
| ----------------- | ----------- | ------------------------------------------------------------------------ |
|
|
@@ -193,17 +134,9 @@ Depth is a modifier, not an axis. Nothing is raised until an
|
|
|
193
134
|
| `.elevation-sm` | `.raised` | One unit of the depth in scope. Cards, tiles, popovers that sit close. |
|
|
194
135
|
| `.elevation-md` | `.floating` | Twice it, for menus and popovers. |
|
|
195
136
|
|
|
196
|
-
Both names in a row are equivalent, interchangeable classes — `.elevation-sm`
|
|
197
|
-
and `.raised` are aliases, not a canonical form and a deprecated one, the way
|
|
198
|
-
`.destructive` and `.danger` are on the intent axis. The `none`/`sm`/`md`
|
|
199
|
-
naming matches the raw `--elevation-*` tokens in
|
|
200
|
-
[Theming](./theming.md#foundation-tokens) for the same weight of depth, even
|
|
201
|
-
though the two are read from different places.
|
|
137
|
+
Both names in a row are equivalent, interchangeable classes — `.elevation-sm` and `.raised` are aliases, not a canonical form and a deprecated one, the way `.destructive` and `.danger` are on the intent axis. The `none`/`sm`/`md` naming matches the raw `--elevation-*` tokens in [Theming](./theming.md#foundation-tokens) for the same weight of depth, even though the two are read from different places.
|
|
202
138
|
|
|
203
|
-
Each is one unitless multiplier over whatever shadow geometry is in scope, so
|
|
204
|
-
the same class reads as a soft blur on a plain page and as a hard offset slab
|
|
205
|
-
under `.neobrutalism` — the aesthetic decides what depth looks like, the
|
|
206
|
-
class decides how much of it this element takes.
|
|
139
|
+
Each is one unitless multiplier over whatever shadow geometry is in scope, so the same class reads as a soft blur on a plain page and as a hard offset slab under `.neobrutalism` — the aesthetic decides what depth looks like, the class decides how much of it this element takes.
|
|
207
140
|
|
|
208
141
|
```html
|
|
209
142
|
<article class="card elevation-sm">Lifted</article>
|
|
@@ -211,8 +144,4 @@ class decides how much of it this element takes.
|
|
|
211
144
|
<article class="card elevation-none">Flat, whatever the page or container says</article>
|
|
212
145
|
```
|
|
213
146
|
|
|
214
|
-
The multiplier is
|
|
215
|
-
flattens a whole region and any element inside it still opts out on itself.
|
|
216
|
-
Spread is deliberately left out of the multiplication, because an aesthetic
|
|
217
|
-
that draws its edge as an inset ring spends spread on it and scaling that
|
|
218
|
-
would erase the edge.
|
|
147
|
+
The multiplier does not inherit: an elevation class lifts or flattens the element it is written on only, and an unclassed element inside it takes no depth of its own. Give each element that needs depth its own elevation class. Spread is deliberately left out of the multiplication, because an aesthetic that draws its edge as an inset ring spends spread on it and scaling that would erase the edge.
|