@codenhub/styles 0.0.3 → 0.1.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 (75) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +52 -0
  3. package/README.md +60 -90
  4. package/dist/aesthetics/chunky-tile.css +1 -0
  5. package/dist/aesthetics/glass.css +1 -0
  6. package/dist/aesthetics/index.css +2 -0
  7. package/dist/aesthetics/neobrutalism.css +1 -0
  8. package/dist/aesthetics/pixel.css +1 -0
  9. package/dist/components.css +2 -2
  10. package/dist/index.css +2 -2
  11. package/dist/native.css +2 -0
  12. package/dist/theme.css +2 -2
  13. package/dist/tw/aesthetics/chunky-tile.css +107 -0
  14. package/dist/tw/aesthetics/glass.css +66 -0
  15. package/dist/tw/aesthetics/index.css +9 -0
  16. package/dist/tw/aesthetics/neobrutalism.css +63 -0
  17. package/dist/tw/aesthetics/pixel.css +88 -0
  18. package/dist/tw/box.css +185 -0
  19. package/dist/tw/components/button.css +88 -0
  20. package/dist/tw/components/feedback.css +163 -0
  21. package/dist/tw/components/form.css +601 -0
  22. package/dist/tw/components/index.css +18 -0
  23. package/dist/tw/components/loader.css +122 -0
  24. package/dist/tw/components/surface.css +98 -0
  25. package/dist/tw/components/tooltip.css +132 -0
  26. package/dist/tw/elevation.css +39 -0
  27. package/dist/tw/index.css +12 -0
  28. package/dist/tw/intent.css +189 -0
  29. package/dist/tw/native.css +203 -0
  30. package/dist/tw/presentation.css +77 -0
  31. package/dist/tw/reset.css +142 -0
  32. package/dist/tw/theme.css +218 -0
  33. package/dist/tw/typography.css +34 -0
  34. package/dist/tw/utilities/content.css +349 -0
  35. package/dist/tw/utilities/index.css +6 -0
  36. package/dist/tw/utilities/layout.css +107 -0
  37. package/docs/accessibility.md +43 -14
  38. package/docs/concepts.md +94 -0
  39. package/docs/index.md +62 -73
  40. package/docs/integrating/astro.md +29 -0
  41. package/docs/integrating/index.md +32 -0
  42. package/docs/integrating/nextjs.md +44 -0
  43. package/docs/integrating/svelte.md +37 -0
  44. package/docs/integrating/tailwind.md +40 -0
  45. package/docs/integrating/vue.md +25 -0
  46. package/docs/setup.md +116 -0
  47. package/docs/usage/aesthetics.md +197 -0
  48. package/docs/usage/buttons.md +74 -0
  49. package/docs/usage/composing.md +218 -0
  50. package/docs/usage/content-and-layout.md +200 -0
  51. package/docs/usage/customizing.md +213 -0
  52. package/docs/usage/feedback.md +117 -0
  53. package/docs/usage/forms.md +177 -0
  54. package/docs/usage/index.md +32 -0
  55. package/docs/usage/theming.md +210 -0
  56. package/docs/usage/tooltips.md +45 -0
  57. package/llms-full.txt +2122 -0
  58. package/llms.txt +26 -0
  59. package/package.json +96 -32
  60. package/docs/classes.md +0 -198
  61. package/docs/roadmap.md +0 -34
  62. package/docs/tests.md +0 -128
  63. package/docs/tokens.md +0 -101
  64. package/src/components/button.css +0 -155
  65. package/src/components/feedback.css +0 -214
  66. package/src/components/form.css +0 -296
  67. package/src/components/index.css +0 -9
  68. package/src/components/loader.css +0 -56
  69. package/src/components/surface.css +0 -6
  70. package/src/components/tooltip.css +0 -60
  71. package/src/index.css +0 -7
  72. package/src/overrides.css +0 -76
  73. package/src/theme.css +0 -136
  74. package/src/typography.css +0 -30
  75. package/src/utilities.css +0 -40
@@ -0,0 +1,197 @@
1
+ ---
2
+ title: Aesthetics
3
+ description: The four shipped aesthetics in depth, including their documented exceptions.
4
+ ---
5
+
6
+ # Aesthetics
7
+
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:
11
+
12
+ ```css
13
+ @import "@codenhub/styles";
14
+ @import "@codenhub/styles/aesthetics";
15
+ ```
16
+
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.
20
+
21
+ Like presentation, an aesthetic class cascades to any subtree:
22
+
23
+ ```html
24
+ <section class="neobrutalism">
25
+ <button class="btn primary">Thick ink and a hard shadow</button>
26
+ <div class="card destructive">The intent still wins over the aesthetic ink</div>
27
+ </section>
28
+ ```
29
+
30
+ | Class | Look |
31
+ | --------------- | ------------------------------------------------------------------------------------------------------------------------------- |
32
+ | `.neobrutalism` | No radius, a thick ink outline, a hard unblurred offset shadow, and a press that moves the element into it. |
33
+ | `.glass` | Translucent surfaces over a blurred backdrop with a hairline highlight edge. |
34
+ | `.pixel` | Corners cut by one grid unit, a chunky outline drawn as an inset ring, and the consumer-supplied `--font-pixel` over monospace. |
35
+ | `.chunky-tile` | Rounded slabs seated on a darker shade of themselves, with a press that moves the element down into its own bar. |
36
+
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.
40
+
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.
44
+
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.
49
+
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.
54
+
55
+ ## Neobrutalism
56
+
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.
61
+
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.
64
+
65
+ ```html
66
+ <section class="neobrutalism" style="--neo-offset: 6px">
67
+ <button class="btn primary">A longer throw</button>
68
+ </section>
69
+ ```
70
+
71
+ **Exceptions:**
72
+
73
+ - Content chips are left alone. A `.kbd`, `.code`, or `.pre` is content
74
+ rather than structure, and an ink edge on one reads as a defect: a key cap
75
+ keeps the quiet border color and a code chip keeps no border at all.
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.
84
+
85
+ ## Glass
86
+
87
+ `.glass` needs something behind it to blur. On a flat page background it
88
+ renders as a plain translucent panel.
89
+
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.
93
+
94
+ ```html
95
+ <section class="glass" style="--glass-radius-surface: 1.5rem">
96
+ <div class="card">Softer</div>
97
+ </section>
98
+ ```
99
+
100
+ **Exceptions:**
101
+
102
+ - The blur applies to `.card`, `.panel`, `.alert`, and the tooltip bubble
103
+ only. Controls stay solid and sit on the glass: a blur under every control
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.
108
+
109
+ ## Pixel
110
+
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.
114
+
115
+ ```html
116
+ <section class="pixel" style="--pixel-unit: 6px">
117
+ <button class="btn primary">Chunkier</button>
118
+ </section>
119
+ ```
120
+
121
+ **Exceptions:**
122
+
123
+ - Corners are one unit or nothing. Chips square instead of stepping —
124
+ badges, key caps, code, checkboxes, and switches all read
125
+ `--ui-clip-tight`, which this aesthetic sets to none, because one unit off
126
+ each corner of a 24px badge is a bite rather than a corner. Tables,
127
+ progress bars, and skeletons square too, each for its own reason: a
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.
148
+
149
+ ## Chunky tile
150
+
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.
154
+
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.
161
+
162
+ ```html
163
+ <section class="chunky-tile" style="--tile-lift: 6px; --tile-radius: 0.5rem">
164
+ <button class="btn success">Continue</button>
165
+ </section>
166
+ ```
167
+
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.
174
+
175
+ **Exceptions:**
176
+
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:
180
+
181
+ ```html
182
+ <div class="card soft edgeless info flat">Promo panel, deliberately flat</div>
183
+ ```
184
+
185
+ - `--font-rounded` is yours to supply. A heavy rounded grotesque is most of
186
+ this look and the package ships no font binary, so with nothing supplied
187
+ the page's own stack is used.
188
+ - Action labels are drawn heavier and slightly tracked. Casing is left
189
+ alone: how a label is worded is the application's decision, so if you want
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.
@@ -0,0 +1,74 @@
1
+ ---
2
+ title: Buttons
3
+ description: Button classes, intents, sizes, and states.
4
+ ---
5
+
6
+ # Buttons
7
+
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.
12
+
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.
16
+
17
+ Intent classes:
18
+
19
+ | Class | Meaning |
20
+ | ----------------------------------- | ------------------------------ |
21
+ | `.neutral` _(default)_ | No specific intent. |
22
+ | `.primary` | Primary action. |
23
+ | `.secondary` | Secondary/accent action. |
24
+ | `.success` | Successful or positive action. |
25
+ | `.warning` | Warning/caution action. |
26
+ | `.destructive`, `.danger`, `.error` | Destructive or error action. |
27
+ | `.info` | Informational action. |
28
+
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.
32
+
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).
39
+
40
+ Size and shape classes:
41
+
42
+ | Class | Purpose |
43
+ | ---------------------------------------------------------------------- | ----------------------------------------------------------- |
44
+ | `.pill` | Fully rounded button corners (`border-radius: 9999px`). |
45
+ | `.sm` | Smaller button. |
46
+ | `.lg` | Larger button. |
47
+ | `.p-sm`, `.compact` | Compact padding modifier (`px-2.5 py-1`). |
48
+ | `.p-lg`, `.spacious` | Spacious padding modifier (`px-6 py-3`). |
49
+ | `.icon` | Square icon button. Use an accessible name in HTML. |
50
+ | `.loading` | Loading state. Hides text and shows CSS activity indicator. |
51
+ | `.disabled`, `[disabled]`, `[aria-disabled="true"]`, `[data-disabled]` | Disabled styling. |
52
+
53
+ Examples:
54
+
55
+ ```html
56
+ <button class="btn primary">Primary</button>
57
+ <button class="btn success ghost edged">Success outline</button>
58
+ <button class="btn warning soft">Warning soft</button>
59
+ <button class="btn destructive ghost edgeless">Danger ghost</button>
60
+ <button class="btn icon primary" aria-label="Create">+</button>
61
+ <button class="btn primary loading" disabled>Saving</button>
62
+ ```
63
+
64
+ `.loading` is a state, not a color or presentation class. Prefer combining
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.
68
+
69
+ On the base look a press scales the button down a little
70
+ (`--ui-active-transform`, `scale(0.97)`). [Aesthetics](./aesthetics.md) swap
71
+ in their own press gesture, and `prefers-reduced-motion` turns it off.
72
+
73
+ Use `<button>` for actions or an accessible link for navigation; see
74
+ [Accessibility](../accessibility.md).
@@ -0,0 +1,218 @@
1
+ ---
2
+ title: Composing
3
+ description: Combining intent, presentation, and aesthetic on a component, and the axis-support matrix.
4
+ ---
5
+
6
+ # Composing the three axes
7
+
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.
11
+
12
+ ## Combining axes
13
+
14
+ ```html
15
+ <div class="soft">
16
+ <button class="btn primary">Soft primary button</button>
17
+ <span class="badge success">Soft success badge</span>
18
+ <button class="btn primary ghost edged">Outlined; the element wins</button>
19
+ </div>
20
+ ```
21
+
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.
24
+
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.
33
+
34
+ ## Presentation
35
+
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.
41
+
42
+ | Class | Look |
43
+ | ----------- | ---------------------------------------------------------- |
44
+ | `.solid` | Intent-colored fill with contrast text. |
45
+ | `.soft` | Lightly tinted fill with intent-colored text. |
46
+ | `.ghost` | No fill at rest, with intent-colored text. |
47
+ | `.edged` | Intent-colored boundary at the aesthetic's material width. |
48
+ | `.edgeless` | No boundary. |
49
+
50
+ Values each class ships:
51
+
52
+ | Class | `--ui-fill` | `--ui-fg-on-fill` | `--ui-border` |
53
+ | ----------- | ----------- | ----------------- | ------------- |
54
+ | `.solid` | `100%` | `100%` | |
55
+ | `.soft` | `12%` | `0%` | |
56
+ | `.ghost` | `0%` | `0%` | |
57
+ | `.edged` | | | `100%` |
58
+ | `.edgeless` | | | `0%` |
59
+
60
+ The combinations mean exactly what they spell:
61
+
62
+ ```html
63
+ <button class="btn primary">Solid, the button default</button>
64
+ <button class="btn soft edged">Tinted with a border</button>
65
+ <button class="btn primary ghost edged">Outline</button>
66
+ <button class="btn ghost">Ghost; picks up a tint on hover</button>
67
+ <input class="ipt soft edgeless" />
68
+ <!-- A field sunk into the page -->
69
+ ```
70
+
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).
76
+
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.
82
+
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.
86
+
87
+ | Component | Reads |
88
+ | ----------------------------------------------- | ------------------------------------------- |
89
+ | `.btn`, `.alert`, `.badge`, `.card`, `.panel` | Fill, text, border, and border width. |
90
+ | `.kbd` | Fill, text, border, and border width. |
91
+ | `.data-table` | Header fill and text, border, border width. |
92
+ | `.ipt`, `.textarea`, `.select`, `.text-control` | Border, border width, and a capped fill. |
93
+ | `.input-group` | Border, border width, and a capped fill. |
94
+ | `.checkbox`, `.radio` | A capped fill, checked and unchecked. |
95
+ | `.switch` | Border, border width, and a capped fill. |
96
+ | `.quote` | Fill, text, border, and width. |
97
+ | `.code`, `.pre`, `.tooltip` | Fill, text, border, and material. |
98
+ | `.loader`, `.skeleton`, `.progress`, `.divider` | Intent only, not presentation. |
99
+
100
+ Text controls and toggles cap a **cascaded** fill at `6%`, keeping typed
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.
106
+
107
+ | On a text control | fill |
108
+ | ----------------- | ---- |
109
+ | `.ghost` | 0% |
110
+ | cascaded `.solid` | 6% |
111
+ | `.soft` | 12% |
112
+ | `.solid` | 20% |
113
+
114
+ The same split governs the edge: a cascaded `.edgeless` is floored and the
115
+ element's own is honoured — except on `.checkbox` and `.radio`, which never
116
+ drop their line at all. See [Forms](./forms.md) for the full toggle and text
117
+ control reference.
118
+
119
+ A tooltip bubble is filled, over a tinted ground. An intent fills it with
120
+ that intent's own color: `.tooltip.primary` is the primary color, black on a
121
+ light page and white on a dark one, and `.tooltip.destructive` is a red
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.
141
+
142
+ ## Component axis reference
143
+
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.
148
+
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.
153
+
154
+ | Components | Intent | Fill | Edge | Aesthetic material |
155
+ | --------------------------------------------------------- | ------ | ---- | ---- | ------------------ |
156
+ | `.btn`, `.alert`, `.badge`, `.card`, `.panel` | Yes | Yes | Yes | Shape |
157
+ | `.loader` | Yes | No | No | No |
158
+ | `.kbd`, `.data-table` | Yes | Yes | Yes | Radius and edge |
159
+ | `.quote` | Yes | Yes | Yes | Edge width |
160
+ | `.divider`, `.skeleton` | Yes | No | No | No |
161
+ | `.ipt`, `.textarea`, `.select`, `.text-control` | Yes | Yes | Yes | Shape |
162
+ | `.input-group` | Yes | Yes | Yes | Shape |
163
+ | `.surface` | Yes | Yes | Yes | Shape |
164
+ | `.checkbox`, `.radio` | Yes | Yes | No | Radius and edge |
165
+ | `.switch` | Yes | Yes | Yes | Radius and edge |
166
+ | `.progress` | Yes | No | No | No |
167
+ | `.code`, `.pre` | Yes | Yes | Yes | Radius and clip |
168
+ | `.tooltip` | Yes | Yes | Yes | Shape and overlay |
169
+ | `.table-wrap`, `.quote-inline`, layout and text utilities | No | No | No | No |
170
+
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.
175
+
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.
179
+
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.
184
+
185
+ ## Elevation
186
+
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.
189
+
190
+ | Class | Alias | Depth |
191
+ | ----------------- | ----------- | ------------------------------------------------------------------------ |
192
+ | `.elevation-none` | `.flat` | Removes part-based elevation; glass surfaces keep their complete shadow. |
193
+ | `.elevation-sm` | `.raised` | One unit of the depth in scope. Cards, tiles, popovers that sit close. |
194
+ | `.elevation-md` | `.floating` | Twice it, for menus and popovers. |
195
+
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.
202
+
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.
207
+
208
+ ```html
209
+ <article class="card elevation-sm">Lifted</article>
210
+ <div class="elevation-md panel">A menu surface</div>
211
+ <article class="card elevation-none">Flat, whatever the page or container says</article>
212
+ ```
213
+
214
+ The multiplier is a plain number, so it inherits: a container lifts or
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.