@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.
Files changed (51) hide show
  1. package/README.md +8 -69
  2. package/dist/aesthetics/chunky-tile.css +1 -1
  3. package/dist/aesthetics/index.css +1 -1
  4. package/dist/components.css +1 -1
  5. package/dist/index.css +1 -1
  6. package/dist/native.css +1 -1
  7. package/dist/theme.css +1 -1
  8. package/dist/tw/aesthetics/chunky-tile.css +20 -2
  9. package/dist/tw/box.css +45 -2
  10. package/dist/tw/components/button.css +38 -29
  11. package/dist/tw/components/feedback.css +58 -41
  12. package/dist/tw/components/form.css +139 -47
  13. package/dist/tw/components/index.css +16 -6
  14. package/dist/tw/components/loader.css +2 -16
  15. package/dist/tw/components/surface.css +122 -5
  16. package/dist/tw/components/tooltip.css +102 -91
  17. package/dist/tw/elevation.css +57 -14
  18. package/dist/tw/index.css +23 -5
  19. package/dist/tw/intent.css +36 -12
  20. package/dist/tw/native.css +4 -20
  21. package/dist/tw/reset.css +40 -32
  22. package/dist/tw/theme.css +46 -8
  23. package/dist/tw/utilities/content.css +104 -18
  24. package/dist/tw/utilities/index.css +1 -1
  25. package/dist/tw/utilities/layout.css +33 -0
  26. package/docs/accessibility.md +68 -48
  27. package/docs/changelog/0.1.0.md +37 -0
  28. package/docs/changelog/0.1.1.md +10 -0
  29. package/docs/changelog/0.2.0.md +41 -0
  30. package/docs/changelog/index.md +11 -0
  31. package/docs/concepts.md +21 -46
  32. package/docs/index.md +18 -45
  33. package/docs/integrating/astro.md +13 -7
  34. package/docs/integrating/index.md +25 -16
  35. package/docs/integrating/nextjs.md +31 -8
  36. package/docs/integrating/svelte.md +14 -8
  37. package/docs/integrating/tailwind.md +31 -15
  38. package/docs/integrating/vue.md +13 -7
  39. package/docs/setup.md +31 -32
  40. package/docs/usage/aesthetics.md +31 -107
  41. package/docs/usage/buttons.md +24 -36
  42. package/docs/usage/composing.md +31 -102
  43. package/docs/usage/content-and-layout.md +65 -83
  44. package/docs/usage/customizing.md +25 -90
  45. package/docs/usage/feedback.md +25 -56
  46. package/docs/usage/forms.md +48 -80
  47. package/docs/usage/index.md +9 -17
  48. package/docs/usage/theming.md +25 -81
  49. package/docs/usage/tooltips.md +55 -35
  50. package/llms-full.txt +1193 -1510
  51. package/package.json +121 -44
@@ -1,43 +1,36 @@
1
1
  ---
2
2
  title: Forms
3
3
  description: Field, text control, and toggle class reference.
4
+ order: 6
4
5
  ---
5
6
 
6
7
  # Forms
7
8
 
8
- | Class or Selector | Purpose |
9
- | --------------------------------------------------------------------- | ------------------------------------------------------------- |
10
- | `.field` | Vertical field wrapper. |
11
- | `.label` | Form label text. |
12
- | `.hint` | Secondary helper text. |
13
- | `.hint.error` | Helper text with destructive intent. |
14
- | `.surface` | Shared public container composition utility. |
15
- | `.text-control` | Shared public text-control composition utility. |
16
- | `.ipt` | Input control styling. |
17
- | `.input-group` | Wrapper that owns the field box so a control can carry icons. |
18
- | `.textarea` | Textarea control styling. |
19
- | `.select` | Select control styling. |
20
- | `input[type="checkbox"].checkbox` | Custom checkbox control styling. |
21
- | `input[type="radio"].radio` | Custom radio control styling. |
22
- | `input[type="checkbox"].switch` | Custom switch control styling. |
23
- | `[aria-invalid="true"]` on controls | Destructive border and focus color. |
24
- | `[disabled]`, `[aria-disabled="true"]`, `[data-disabled]` on controls | Disabled styling. |
9
+ | Class or Selector | Purpose |
10
+ | --------------------------------------------------------------------- | ------------------------------------------------------------------------ |
11
+ | `.field` | Vertical field wrapper. |
12
+ | `.label` | Form label text. |
13
+ | `.hint` | Secondary helper text. |
14
+ | `.hint.error` | Helper text with destructive intent. |
15
+ | `.surface` | Shared public container composition utility. |
16
+ | `.text-control` | Shared public text-control composition utility. |
17
+ | `.ipt` | Input control styling. |
18
+ | `.input-group` | Wrapper that owns the field box so a control can carry an icon or affix. |
19
+ | `.textarea` | Textarea control styling. |
20
+ | `.select` | Select control styling. |
21
+ | `input[type="checkbox"].checkbox` | Custom checkbox control styling. |
22
+ | `input[type="radio"].radio` | Custom radio control styling. |
23
+ | `input[type="checkbox"].switch` | Custom switch control styling. |
24
+ | `[aria-invalid="true"]` on controls | Destructive border and focus color. |
25
+ | `[disabled]`, `[aria-disabled="true"]`, `[data-disabled]` on controls | Disabled styling. |
25
26
 
26
27
  ## Icons
27
28
 
28
- The package ships no icons. It used to paint one per input type as a
29
- `background-image` — a `data:` URI, which cannot read `currentColor` or a
30
- custom property, so every glyph shipped a light and a dark copy and a theme
31
- selector chose between them. That put icon artwork, and the package's only
32
- theme-by-selector branch, inside a CSS-only design system whose glyphs did
33
- not fit every aesthetic. Icons are the consumer's to choose now.
29
+ No icon is shipped for text inputs — that's what this section covers. It used to paint one per input type as a `background-image` — a `data:` URI, which cannot read `currentColor` or a custom property, so every glyph shipped a light and a dark copy and a theme selector chose between them. That put icon artwork, and the package's only theme-by-selector branch, inside a CSS-only design system whose glyphs did not fit every aesthetic. Icons for `.input-group` are the consumer's to choose now.
34
30
 
35
- `.input-group` is the wrapper for a control that carries one. It owns the
36
- field box — border, radius, fill, focus ring, invalid and disabled state —
37
- and the control inside it goes flush, so the boundary is drawn once. Any
38
- icon element works, placed before the control for a leading icon or after it
39
- for a trailing one: an inline `<svg>`, an `<img>`, or a class from an icon
40
- set such as `@codenhub/icons`.
31
+ This is separate from the toggle marks below, which stay small, fixed, built-in glyphs. `.alert`'s icon followed the same path since — see [Feedback → Icon](./feedback.md#example) — the package no longer paints one there either.
32
+
33
+ `.input-group` is the wrapper for a control that carries one. It owns the field box — border, radius, fill, focus ring, invalid and disabled state — and the control inside it goes flush, so the boundary is drawn once. Any icon element works, placed before the control for a leading icon or after it for a trailing one: an inline `<svg>`, an `<img>`, or a class from an icon set such as `@codenhub/icons`.
41
34
 
42
35
  ```html
43
36
  <label class="field">
@@ -59,22 +52,24 @@ set such as `@codenhub/icons`.
59
52
  </label>
60
53
  ```
61
54
 
62
- The group reads intent and both [presentation](./composing.md#presentation)
63
- axes the same way a lone `.ipt` does — `.input-group.soft.edgeless` is the
64
- sunk variant, `.input-group.primary` colors the boundary — and follows
65
- whichever [aesthetic](./aesthetics.md) is in scope. `aria-invalid` or
66
- `disabled` on the control inside propagates to the group; so does either on
67
- the group itself. Focus is shown with `:focus-within`, so unlike a lone
68
- field the ring also appears on a mouse click.
55
+ The group reads intent and both [presentation](./composing.md#presentation) axes the same way a lone `.ipt` does — `.input-group.soft.edgeless` is the sunk variant, `.input-group.primary` colors the boundary — and follows whichever [aesthetic](./aesthetics.md) is in scope. `aria-invalid` or `disabled` on the control inside propagates to the group; so does either on the group itself. Focus is shown with `:focus-within`, so unlike a lone field the ring also appears on a mouse click.
56
+
57
+ Non-icon input types keep the browser's native `date` and `datetime-local` pickers. WebKit's native `search` decorations are still suppressed on `.text-control` to keep them from overlapping the value.
58
+
59
+ The slot works the same way for a text affix — a URL host, a currency symbol, a unit — not just an icon:
60
+
61
+ ```html
62
+ <div class="input-group">
63
+ <span>example.com/</span>
64
+ <input class="ipt" type="text" />
65
+ </div>
66
+ ```
69
67
 
70
- Non-icon input types keep the browser's native `date` and `datetime-local`
71
- pickers. WebKit's native `search` decorations are still suppressed on
72
- `.text-control` to keep them from overlapping the value.
68
+ The gap between the control and its adornment is `--input-group-gap`, `0.5rem` by default: the right amount of breathing room for a distinct icon glyph, and too much for text meant to read as glued to the value — `example.com/` followed by a gap reads as a stray space breaking the path apart. Set `--input-group-gap: 0` on the group for a flush affix; icon compositions keep the default unchanged.
73
69
 
74
70
  ## Toggles
75
71
 
76
- `.checkbox`, `.radio`, and `.switch` accept the same intent classes as
77
- buttons to set the checked color:
72
+ `.checkbox`, `.radio`, and `.switch` accept the same intent classes as buttons to set the checked color:
78
73
 
79
74
  | Class | Meaning |
80
75
  | ----------------------------------- | ----------------------- |
@@ -86,39 +81,21 @@ buttons to set the checked color:
86
81
  | `.destructive`, `.danger`, `.error` | Destructive color. |
87
82
  | `.info` | Info color. |
88
83
 
89
- All three toggles rest at `.solid`, and **`.ghost` is not supported on any of
90
- them.** An unchecked ghost toggle is the silhouette every toggle already
91
- has, and a checked one is a mark on nothing — a tick floating on the page, a
92
- dot with no ring around it. A container that cascades `.ghost` onto a
93
- toggle is floored rather than obeyed.
94
-
95
- Presentation decides the fill in both states. Being checked lifts the cap
96
- rather than pinning a fill, so the class still chooses:
84
+ All three toggles rest at `.soft`. Presentation decides the _unchecked_ plate; checked is pinned to one look regardless of which class is on the element, so a checkbox, a radio, and a switch each have a single "on" identity rather than one per presentation:
97
85
 
98
- | Toggle | unchecked | checked |
99
- | ----------------- | --------------- | ------------------------------- |
100
- | `.solid`, default | 20%, switch 40% | 100%, mark in the contrast tone |
101
- | `.soft` | 12% | 12%, mark in the strong tone |
86
+ | Toggle | unchecked | checked |
87
+ | ---------------- | --------- | ---------------------------------------- |
88
+ | `.ghost` | 0% | 100%, mark in the contrast tone (pinned) |
89
+ | `.soft`, default | 12% | 100%, mark in the contrast tone (pinned) |
90
+ | `.solid` | 40% | 100%, mark in the contrast tone (pinned) |
102
91
 
103
- A checked checkbox and a checked switch cut their mark out of that plate. A
104
- checked radio thickens its ring to twice the resting line and takes the
105
- intent whole on it, so `.radio.soft` is the classic ring-and-dot radio and
106
- `.radio.solid` is a filled circle whose dot stays readable — the mark
107
- follows the plate rather than being pinned to the intent color.
92
+ A checked checkbox and a checked switch cut their mark out of that pinned plate. A checked radio thickens its ring to twice the resting line and takes the intent whole on it, and the dot follows the same pinned plate, so `.radio.soft` and `.radio.solid` render the same filled circle once checked — presentation only changes what the radio looks like before it's picked.
108
93
 
109
94
  ## Text controls
110
95
 
111
- Text controls also take intent, which colors the resting border and the
112
- focus-visible border, and both [presentation](./composing.md#presentation)
113
- axes. `.ghost` fills nothing, `.soft` takes `12%`, and `.solid` takes `20%`
114
- — quiet enough that typed text still reads on it, and ordered so the louder
115
- name draws the stronger tint. None of them touches the line.
96
+ Text controls also take intent, which colors the resting border and the focus-visible border, and both [presentation](./composing.md#presentation) axes. `.ghost` fills nothing, `.soft` takes `12%`, and `.solid` takes `20%` — quiet enough that typed text still reads on it, and ordered so the louder name draws the stronger tint. None of them touches the line.
116
97
 
117
- The boundary is the edge axis's, and it splits the same way. A container's
118
- `.edgeless` is floored, so a toolbar cannot leave a field with no mark of
119
- where typing goes. `.edgeless` on the control itself is honoured, which is
120
- how the borderless field is spelled — it does not meet WCAG 1.4.11 at rest,
121
- so it is opt-in and never a default, and the focus ring still shows.
98
+ The boundary is the edge axis's, and it splits the same way. A container's `.edgeless` is floored, so a toolbar cannot leave a field with no mark of where typing goes. `.edgeless` on the control itself is honoured, which is how the borderless field is spelled — it does not meet WCAG 1.4.11 at rest, so it is opt-in and never a default, and the focus ring still shows.
122
99
 
123
100
  ```html
124
101
  <input class="ipt success" placeholder="Valid" />
@@ -133,11 +110,7 @@ so it is opt-in and never a default, and the focus ring still shows.
133
110
  </div>
134
111
  ```
135
112
 
136
- `.surface` is a public low-level utility. It is the box every container in
137
- the package composes: ghost, edged, surface radius, and the two slots only
138
- a surface resolves — the backdrop filter and the complete surface shadow.
139
- Use it to paint a container the package does not ship, under whichever
140
- aesthetic is in scope. Prefer `.card` or `.panel` when they fit.
113
+ `.surface` is a public low-level utility. It is the box every container in the package composes: ghost, edged, surface radius, and the two slots only a surface resolves — the backdrop filter and the complete surface shadow. Use it to paint a container the package does not ship, under whichever aesthetic is in scope. Prefer `.card` or `.panel` when they fit.
141
114
 
142
115
  ```html
143
116
  <div class="surface glass">Blurred, translucent, glass corners</div>
@@ -145,11 +118,7 @@ aesthetic is in scope. Prefer `.card` or `.panel` when they fit.
145
118
  <div class="surface primary solid">Any intent, any presentation</div>
146
119
  ```
147
120
 
148
- `.text-control` is a public low-level utility from the form entrypoint. It
149
- provides the shared control dimensions, border, placeholder, focus-visible,
150
- invalid, and disabled styles composed by `ipt`, `textarea`, and `select`.
151
- Use it for custom text-like controls; prefer those higher-level utilities
152
- when they fit.
121
+ `.text-control` is a public low-level utility from the form entrypoint. It provides the shared control dimensions, border, placeholder, focus-visible, invalid, and disabled styles composed by `ipt`, `textarea`, and `select`. Use it for custom text-like controls; prefer those higher-level utilities when they fit.
153
122
 
154
123
  ## Example
155
124
 
@@ -173,5 +142,4 @@ when they fit.
173
142
  </label>
174
143
  ```
175
144
 
176
- Use labels, `type`, validation logic, `aria-describedby`, and error message
177
- relationships; see [Accessibility](../accessibility.md).
145
+ Use labels, `type`, validation logic, `aria-describedby`, and error message relationships; see [Accessibility](../accessibility.md).
@@ -1,32 +1,24 @@
1
1
  ---
2
- title: Usage
2
+ title: Overview
3
3
  description: Guides for composing axes, theming, customizing, aesthetics, and each component family.
4
+ group: Usage
5
+ order: 3
4
6
  ---
5
7
 
6
8
  # Usage
7
9
 
8
- These guides assume the [Concepts](../concepts.md) mental model: intent,
9
- presentation, and aesthetic as three independent axes, resolved through
10
- tokens a component reads.
10
+ These guides assume the [Concepts](../concepts.md) mental model: intent, presentation, and aesthetic as three independent axes, resolved through tokens a component reads.
11
11
 
12
- - [Composing](./composing.md): Combining the three axes on a component, the
13
- axis-support matrix, and elevation modifiers.
12
+ - [Composing](./composing.md): Combining the three axes on a component, the axis-support matrix, and elevation modifiers.
14
13
  - [Theming](./theming.md): The color, intent, and foundation token reference.
15
- - [Customizing](./customizing.md): The presentation and material token
16
- reference, aesthetic knobs, and defining a custom intent.
17
- - [Aesthetics](./aesthetics.md): Each shipped aesthetic in depth, including
18
- its documented exceptions.
14
+ - [Customizing](./customizing.md): The presentation and material token reference, aesthetic knobs, and defining a custom intent.
15
+ - [Aesthetics](./aesthetics.md): Each shipped aesthetic in depth, including its documented exceptions.
19
16
  - [Buttons](./buttons.md)
20
17
  - [Forms](./forms.md)
21
18
  - [Feedback](./feedback.md)
22
19
  - [Tooltips](./tooltips.md)
23
- - [Content and layout](./content-and-layout.md): Layout helpers, tables,
24
- quotes, code, surfaces, and typography utilities.
20
+ - [Content and layout](./content-and-layout.md): Layout helpers, tables, quotes, code, surfaces, and typography utilities.
25
21
 
26
22
  ## Supported surface
27
23
 
28
- A class, component, and axis combination is supported when this documentation
29
- describes it and the package playground demonstrates it. Undocumented and
30
- undemonstrated combinations may still produce CSS, but are not maintained
31
- behavior. Playground variant matrices are the exhaustive demonstrations; small
32
- examples elsewhere illustrate usage without widening that contract.
24
+ A class, component, and axis combination is supported when this documentation describes it and the package playground demonstrates it. Undocumented and undemonstrated combinations may still produce CSS, but are not maintained behavior. Playground variant matrices are the exhaustive demonstrations; small examples elsewhere illustrate usage without widening that contract.
@@ -1,20 +1,18 @@
1
1
  ---
2
2
  title: Theming
3
3
  description: The color, intent, and foundation token reference.
4
+ order: 2
4
5
  ---
5
6
 
6
7
  # Theming
7
8
 
8
- Color tokens are how you restyle the palette; see
9
- [Concepts → Theme selection](../concepts.md#theme-selection) for how a theme
10
- is chosen. Intent tokens are how a component reads that palette once an
11
- intent class is applied. Foundation tokens are the shared layout, shape, and
12
- motion defaults underneath everything.
9
+ Color tokens are how you restyle the palette; see [Concepts → Theme selection](../concepts.md#theme-selection) for how a theme is chosen. Intent tokens are how a component reads that palette once an intent class is applied. Foundation tokens are the shared layout, shape, and motion defaults underneath everything.
13
10
 
14
11
  ## Color tokens
15
12
 
16
- Each color token holds both of its theme values at once through
17
- `light-dark()`, and the element's `color-scheme` picks one.
13
+ Each color token holds both of its theme values at once through `light-dark()`, and the element's `color-scheme` picks one.
14
+
15
+ Every row is an override point: set it on `:root` to retheme globally, or on any element to retheme that subtree. The `-hover`, `-subtle`, `-strong`, and `-contrast` rows are companion tones wired into the intent slots, so overriding a base color such as `--color-primary` on its own does not shift its companions — retheme the whole family. The `text` family has no `-subtle` row: the neutral intent draws its subtle tone from `--color-surface`.
18
16
 
19
17
  ```html
20
18
  <section data-theme="dark">
@@ -44,7 +42,6 @@ Each color token holds both of its theme values at once through
44
42
  | `--color-text-secondary` | Secondary text color. |
45
43
  | `--color-text-contrast` | Readable color on top of a full fill of `--color-text`. A checked toggle prints it. |
46
44
  | `--color-text-hover` | Text hover state. |
47
- | `--color-text-subtle` | Low-emphasis companion tone for text. Use as soft surface or subtle background. |
48
45
  | `--color-text-strong` | High-emphasis companion tone for text. Use as readable text, icon, or border on subtle surfaces. |
49
46
  | `--color-tooltip` | Tooltip bubble plate with no intent. Chosen per theme rather than derived: near-white in light, mid grey in dark. |
50
47
  | `--color-tooltip-contrast` | Ink on `--color-tooltip`. |
@@ -69,20 +66,11 @@ Each color token holds both of its theme values at once through
69
66
  | `--color-info-subtle` | Low-emphasis companion tone for info. |
70
67
  | `--color-info-strong` | High-emphasis companion tone for info. |
71
68
 
72
- > **Intent token contract**: color intent tokens own meaning and tone
73
- > variants. Intent classes map one family onto the shared intent slots below,
74
- > and presentation classes decide how much of it a component shows. Theme
75
- > changes belong in token values, not broad component-level theme checks.
76
- > Component-level theme handling should exist only when a component has an
77
- > internal structure that cannot be expressed through the token palette
78
- > alone.
69
+ > **Intent token contract**: color intent tokens own meaning and tone variants. Intent classes map one family onto the shared intent slots below, and presentation classes decide how much of it a component shows. Theme changes belong in token values, not broad component-level theme checks. Component-level theme handling should exist only when a component has an internal structure that cannot be expressed through the token palette alone.
79
70
 
80
71
  ## Intent tokens
81
72
 
82
- Intent classes set these seven slots; components read them. Because every
83
- supporting component reads the same seven names, an intent works on every
84
- component that supports intent, and a custom intent needs no component
85
- changes.
73
+ Intent classes set these seven slots; components read them. Because every supporting component reads the same seven names, an intent works on every component that supports intent, and a custom intent needs no component changes.
86
74
 
87
75
  | Token | Purpose |
88
76
  | ------------------- | ------------------------------------------------------------------------------------- |
@@ -94,49 +82,19 @@ changes.
94
82
  | `--intent-border` | Line color. Resolves to `--color-border` when no intent is set. |
95
83
  | `--intent-fill-max` | How far a fill of this intent is allowed to go. `100%` for every intent but neutral. |
96
84
 
97
- `--intent-color` and `--intent-strong` are two ends of the same family and not
98
- interchangeable. The base is a ground, chosen to be filled with; the strong
99
- tone is chosen to be read against a page. A `.soft` or `.ghost` component
100
- prints the strong one, which is why an unfilled warning reads in amber-800
101
- rather than the amber-600 its solid sibling is filled with.
102
-
103
- The strong tone has to stay recognisably its own hue, not just readable. On a
104
- dark page the `-100` shade of every family is a pale tint that carries almost
105
- no chroma at small sizes: a soft success badge and a soft destructive one
106
- printed labels that both read white, and a soft radio's dot stopped matching
107
- the ring around it. The dark tones are `-300` for that reason, and the tinted
108
- grounds under them are `-900` so there is room — measured at 5.0:1 or better
109
- everywhere a partial fill prints text, and past 9:1 on the page.
110
-
111
- `--intent-border` exists because a neutral border must stay the quiet border
112
- gray rather than the text color. Everything that draws a line reads it:
113
- control borders, table rules, dividers, key caps, and quote bars.
114
-
115
- `--intent-fill-max` exists for the neutral intent, whose color is the page's
116
- own ink. Every other intent is a hue, legible pale at 12% and saturated at
117
- 100%; a grey filled to 100% is a black or white slab, which is the loudest
118
- thing on the page and the wrong look for the intent that means _nothing in
119
- particular_. Neutral stops at `20%`, so `.solid` gives it a quiet plate that
120
- still reads as a step past `.soft`. Hover adds its step on top of the cap, so
121
- a capped fill still deepens under the pointer.
122
-
123
- The foreground is gated by the plate rather than tied to the fill.
124
- `--intent-contrast` is the ink for a _full_ fill, so printing it at full
125
- strength on a fifth of one puts white on light grey. Contrast ink therefore
126
- appears only once the fill is past halfway and reaches full at a full fill;
127
- below that a component prints `--intent-strong`, the tone chosen to be read.
128
-
129
- That means a capped neutral takes the strong tone whole rather than a
130
- fraction of the way toward the page. Tying it to the fill instead cost a
131
- neutral `.solid` button four points of contrast and a neutral tooltip nearly
132
- ten. A state that lifts the cap on purpose — a checked checkbox is filled
133
- with its own intent by definition — reaches a full fill and gets the contrast
134
- whole.
135
-
136
- Set them directly to define an intent this package does not ship. **State
137
- every slot**, including `--intent-fill-max`: components reset all seven to
138
- the neutral values, so a slot your intent leaves out keeps neutral's — and an
139
- intent that omits the cap fills to 20% of its color instead of 100%.
85
+ `--intent-color` and `--intent-strong` are two ends of the same family and not interchangeable. The base is a ground, chosen to be filled with; the strong tone is chosen to be read against a page. A `.soft` or `.ghost` component prints the strong one, which is why an unfilled warning reads in amber-800 rather than the amber-600 its solid sibling is filled with.
86
+
87
+ The strong tone has to stay recognisably its own hue, not just readable. On a dark page the `-100` shade of every family is a pale tint that carries almost no chroma at small sizes: a soft success badge and a soft destructive one printed labels that both read white, and a soft radio's dot stopped matching the ring around it. The dark tones are `-300` for that reason, and the tinted grounds under them are `-900` so there is room — measured at 5.0:1 or better everywhere a partial fill prints text, and past 9:1 on the page.
88
+
89
+ `--intent-border` exists because a neutral border must stay the quiet border gray rather than the text color. Everything that draws a line reads it: control borders, table rules, dividers, key caps, and quote bars.
90
+
91
+ `--intent-fill-max` exists for the neutral intent, whose color is the page's own ink. Every other intent is a hue, legible pale at 12% and saturated at 100%; a grey filled to 100% is a black or white slab, which is the loudest thing on the page and the wrong look for the intent that means _nothing in particular_. Neutral stops at `20%`, so `.solid` gives it a quiet plate that still reads as a step past `.soft`. Hover adds its step on top of the cap, so a capped fill still deepens under the pointer.
92
+
93
+ The foreground is gated by the plate rather than tied to the fill. `--intent-contrast` is the ink for a _full_ fill, so printing it at full strength on a fifth of one puts white on light grey. Contrast ink therefore appears only once the fill is past halfway and reaches full at a full fill; below that a component prints `--intent-strong`, the tone chosen to be read.
94
+
95
+ That means a capped neutral takes the strong tone whole rather than a fraction of the way toward the page. Tying it to the fill instead cost a neutral `.solid` button four points of contrast and a neutral tooltip nearly ten. A state that lifts the cap on purpose — a checked checkbox is filled with its own intent by definition — reaches a full fill and gets the contrast whole.
96
+
97
+ Set them directly to define an intent this package does not ship. **State every slot**, including `--intent-fill-max`: components reset all seven to the neutral values, so a slot your intent leaves out keeps neutral's — and an intent that omits the cap fills to 20% of its color instead of 100%.
140
98
 
141
99
  ```css
142
100
  .brand {
@@ -151,29 +109,16 @@ intent that omits the cap fills to 20% of its color instead of 100%.
151
109
  ```
152
110
 
153
111
  ```html
154
- <button class="btn brand">Custom intent</button>
155
- <span class="badge brand soft">Works everywhere</span>
112
+ <button class="btn brand">Custom intent</button> <span class="badge brand soft">Works everywhere</span>
156
113
  ```
157
114
 
158
- > **Intent does not cascade.** Components reset these slots to the text
159
- > palette at their own root, so a `.success` container never recolors a
160
- > nested destructive button. Put the intent class on the element that shows
161
- > the intent.
115
+ > **Intent does not cascade.** Components reset these slots to the text palette at their own root, so a `.success` container never recolors a nested destructive button. Put the intent class on the element that shows the intent.
162
116
 
163
117
  ## Foundation tokens
164
118
 
165
- Foundation tokens are not aliases for one color. They define layout, shape,
166
- motion, focus, depth, and layering behavior used by helper classes.
119
+ Foundation tokens are not aliases for one color. They define layout, shape, motion, focus, depth, and layering behavior used by helper classes.
167
120
 
168
- The four `--elevation-*` values below are raw shadows for elements you style
169
- yourself. They are a separate thing from `.elevation-none`/`.elevation-sm`/
170
- `.elevation-md` (aliased as `.flat`/`.raised`/`.floating`), which scale a
171
- _component's own_ shadow geometry and default to no shadow at all until an
172
- aesthetic or one of those classes asks for depth — see
173
- [Composing → Elevation](./composing.md#elevation) for that system. The two
174
- share `none`/`sm`/`md`/`lg` naming for the same weight of depth, but one is a
175
- fixed value and the other multiplies shadow parts, so they will not always
176
- paint identically.
121
+ The four `--elevation-*` values below are raw shadows for elements you style yourself. They are a separate thing from `.elevation-none`/`.elevation-sm`/ `.elevation-md` (aliased as `.flat`/`.raised`/`.floating`), which scale a _component's own_ shadow geometry and default to no shadow at all until an aesthetic or one of those classes asks for depth — see [Composing → Elevation](./composing.md#elevation) for that system. The two share `none`/`sm`/`md`/`lg` naming for the same weight of depth, but one is a fixed value and the other multiplies shadow parts, so they will not always paint identically.
177
122
 
178
123
  | Token | Purpose |
179
124
  | -------------------------- | ----------------------------------------------------------------------- |
@@ -206,5 +151,4 @@ paint identically.
206
151
  | `--breakpoint-xs` | Extra-small Tailwind responsive breakpoint. |
207
152
  | `--breakpoint-2xl` | Extended large Tailwind responsive breakpoint. |
208
153
 
209
- Next: [Customizing](./customizing.md) covers presentation and material
210
- tokens — how much of an intent shows, and what a component is made of.
154
+ Next: [Customizing](./customizing.md) covers presentation and material tokens — how much of an intent shows, and what a component is made of.
@@ -1,45 +1,65 @@
1
1
  ---
2
2
  title: Tooltips
3
- description: Tooltip host classes and positioning attributes.
3
+ description: Tooltip composition classes and positioning attributes.
4
+ order: 8
4
5
  ---
5
6
 
6
7
  # Tooltips
7
8
 
8
- | Class or Attribute | Purpose |
9
- | --------------------------------------- | -------------------------------------------------------------- |
10
- | `.tooltip` | Tooltip host. Uses `data-tooltip` as pseudo-element content. |
11
- | `.tooltip.tooltip-icon` | Circular icon-style tooltip host. |
12
- | `[data-tooltip-position="top"]` | Positions tooltip above host. |
13
- | `[data-tooltip-position="bottom"]` | Positions tooltip below host. |
14
- | `[data-tooltip-position="left"]` | Positions tooltip left of host. |
15
- | `[data-tooltip-position="right"]` | Positions tooltip right of host. |
16
- | No `data-tooltip-position` | Defaults to top placement. |
17
- | `[data-state="open"]` | Shows tooltip without hover. |
18
- | `.tooltip.raised` / `.tooltip.floating` | Adds one or two units of depth. The bubble is flat by default. |
19
-
20
- A tooltip bubble's fill, ground color, and floor behavior are covered in
21
- [Composing → Presentation](./composing.md#presentation), and its material
22
- follows whichever [aesthetic](./aesthetics.md) is in scope, including the
23
- pseudo-element itself.
24
-
25
- The bubble rests flat. It draws no shadow of its own on a plain page, and
26
- zeroes an aesthetic's part-based depth (such as `.neobrutalism`'s slab) the
27
- same way `.flat` does elsewhere. Add [`.raised` or
28
- `.floating`](./composing.md#elevation) to lift it — `.tooltip.floating` is the
29
- pre-0.1.0 look. An aesthetic whose shadow is a complete value, like `.glass`,
30
- still reaches the bubble as it reaches any surface.
9
+ | Class or Attribute | Purpose |
10
+ | -------------------------------------- | ------------------------------------------------------------------------ |
11
+ | `.tooltip` | Wrapper around a trigger and a bubble. Paints nothing itself. |
12
+ | `.tooltip-bubble` | The message. A real element, styled like any other surface. |
13
+ | `.tooltip-icon` | Circular icon-style trigger, for a message with nothing else to hang on. |
14
+ | `[data-tooltip-position="top"]` | Positions the bubble above the trigger, on `.tooltip`. |
15
+ | `[data-tooltip-position="bottom"]` | Positions the bubble below the trigger, on `.tooltip`. |
16
+ | `[data-tooltip-position="left"]` | Positions the bubble left of the trigger, on `.tooltip`. |
17
+ | `[data-tooltip-position="right"]` | Positions the bubble right of the trigger, on `.tooltip`. |
18
+ | No `data-tooltip-position` | Defaults to top placement. |
19
+ | `[data-state="open"]` | Shows the bubble without hover, on `.tooltip`. |
20
+ | `.tooltip-bubble.raised` / `.floating` | Adds one or two units of depth. The bubble is flat by default. |
21
+
22
+ A tooltip is composition, not a single class: `.tooltip` wraps a trigger element and a `.tooltip-bubble`, and shows the bubble on hover or focus within the wrapper. This replaced a `::after` pseudo-element that carried the message as `content: attr(data-tooltip)` — CSS generated content is not reliably exposed to assistive tech, and cannot be the target of an `aria-describedby`, since that needs a real element with a real `id`. A real bubble can be. Wire the association yourself: give the bubble an `id`, give the trigger `aria-describedby` pointing at it, and put `role="tooltip"` on the bubble.
23
+
24
+ A tooltip bubble's fill, ground color, and floor behavior are covered in [Composing → Presentation](./composing.md#presentation), and its material follows whichever [aesthetic](./aesthetics.md) is in scope. Intent, presentation, aesthetic, and elevation classes go on `.tooltip-bubble` — the element that actually paints — not on `.tooltip`, the same rule every component in this package follows: put the class on the element that shows it.
25
+
26
+ The bubble rests flat. It draws no shadow of its own on a plain page, and zeroes an aesthetic's part-based depth (such as `.neobrutalism`'s slab) the same way `.flat` does elsewhere. Add [`.raised` or `.floating`](./composing.md#elevation) to lift it, on the bubble itself — `.tooltip-bubble.floating` is the pre-0.1.0 look. An aesthetic whose shadow is a complete value, like `.glass`, still reaches the bubble as it reaches any surface.
27
+
28
+ Intent works on a tooltip bubble the same way it works on a filled component: `.tooltip-bubble.primary` fills the bubble with the primary color, black text on a light page and white on a dark one; `.tooltip-bubble.destructive` is a red bubble with the same logic. With no intent, the bubble uses `--color-tooltip`, a plate chosen per theme rather than derived from an intent; see [Composing → Presentation](./composing.md#presentation) for why.
29
+
30
+ ## Example
31
31
 
32
32
  ```html
33
- <button
34
- class="tooltip tooltip-icon"
35
- data-tooltip="More details"
36
- data-tooltip-position="right"
37
- aria-label="More details"
38
- >
39
- ?
40
- </button>
33
+ <span class="tooltip">
34
+ <button aria-describedby="status-bubble">Status</button>
35
+ <span class="tooltip-bubble" role="tooltip" id="status-bubble">Saved 2 minutes ago</span>
36
+ </span>
37
+
38
+ <span class="tooltip" data-tooltip-position="bottom">
39
+ <button aria-describedby="docs-bubble">Docs</button>
40
+ <span class="tooltip-bubble" role="tooltip" id="docs-bubble">Opens in a new tab</span>
41
+ </span>
42
+
43
+ <span class="tooltip">
44
+ <button class="btn primary" aria-describedby="deploy-bubble">Deploy</button>
45
+ <span class="tooltip-bubble raised primary" role="tooltip" id="deploy-bubble">Primary intent, lifted a step</span>
46
+ </span>
47
+
48
+ <span class="tooltip" data-state="open">
49
+ <span aria-describedby="pinned-bubble">Pinned</span>
50
+ <span class="tooltip-bubble" role="tooltip" id="pinned-bubble">Always visible, no hover needed</span>
51
+ </span>
52
+
53
+ <span class="tooltip" data-tooltip-position="right">
54
+ <span class="tooltip-icon" tabindex="0" aria-label="More details" aria-describedby="icon-bubble">?</span>
55
+ <span class="tooltip-bubble" role="tooltip" id="icon-bubble">More details</span>
56
+ </span>
41
57
  ```
42
58
 
43
- CSS pseudo-element tooltips are presentational. They are not a complete
44
- accessible tooltip implementation by themselves — provide accessible
45
- names/descriptions outside the CSS; see [Accessibility](../accessibility.md).
59
+ The first two show plain triggers with default (top) and explicit bottom placement. The third combines an intent with `.raised` on the bubble to show they compose freely — the intent decides the bubble's fill, `.raised` decides whether it casts depth. The fourth uses `data-state="open"` on the wrapper to keep a tooltip visible without a hover or focus interaction, useful for a tooltip that should be demonstrated or tested without simulating pointer input. The fifth uses `.tooltip-icon` as the trigger, for a message with no other element to hang it on.
60
+
61
+ `.tooltip` composes with any trigger: wrap an existing `.btn` or `.badge` in it and both keep their own look, gaining a bubble beside them. `.tooltip-icon` is only the circular chip for the case where there is nothing else to be the trigger.
62
+
63
+ Showing and hiding the bubble is CSS-only — `:hover` and `:focus-within` on `.tooltip` — but that is presentational. It is not a complete accessible tooltip implementation by itself: provide the `id`/`aria-describedby`/`role="tooltip"` association outside the CSS; see [Accessibility](../accessibility.md).
64
+
65
+ Next: [Content and layout](./content-and-layout.md) covers layout helpers, content chips and tables, surfaces, and typography utilities.