@codenhub/styles 0.7.0 → 0.9.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 (130) hide show
  1. package/README.es.md +9 -8
  2. package/README.md +8 -7
  3. package/README.pt-BR.md +9 -8
  4. package/dist/aesthetics/chunky-tile.css +1 -1
  5. package/dist/aesthetics/cyber.css +1 -1
  6. package/dist/aesthetics/glass.css +1 -1
  7. package/dist/aesthetics/index.css +2 -2
  8. package/dist/aesthetics/neobrutalism.css +1 -1
  9. package/dist/aesthetics/pixel.css +1 -1
  10. package/dist/aesthetics/sketch.css +1 -1
  11. package/dist/cli.js +358 -0
  12. package/dist/components.css +50 -2
  13. package/dist/index.css +50 -2
  14. package/dist/index.d.ts +1 -0
  15. package/dist/native.css +50 -2
  16. package/dist/palette.css +1 -1
  17. package/dist/purge/classes.json +1 -0
  18. package/dist/purge/index.css +50 -0
  19. package/dist/theme.css +2 -2
  20. package/docs/accessibility.md +46 -27
  21. package/docs/changelog/0.8.0.md +70 -0
  22. package/docs/changelog/0.9.0.md +40 -0
  23. package/docs/changelog/index.md +2 -0
  24. package/docs/es/accessibility.md +47 -28
  25. package/docs/es/index.md +4 -3
  26. package/docs/es/integrating/index.md +38 -10
  27. package/docs/es/integrating/tailwind.md +79 -8
  28. package/docs/es/integrating/without-a-bundler.md +157 -5
  29. package/docs/es/setup.md +43 -34
  30. package/docs/es/usage/aesthetics.md +20 -14
  31. package/docs/es/usage/buttons.md +6 -2
  32. package/docs/es/usage/composing.md +6 -2
  33. package/docs/es/usage/content-and-layout.md +10 -4
  34. package/docs/es/usage/customizing.md +46 -26
  35. package/docs/es/usage/feedback.md +5 -1
  36. package/docs/es/usage/forms.md +30 -5
  37. package/docs/es/usage/index.md +7 -3
  38. package/docs/es/usage/presets.md +92 -0
  39. package/docs/es/usage/solo.md +4 -2
  40. package/docs/es/usage/theming.md +29 -16
  41. package/docs/es/usage/tooltips.md +2 -2
  42. package/docs/index.md +3 -2
  43. package/docs/integrating/index.md +38 -10
  44. package/docs/integrating/tailwind.md +78 -7
  45. package/docs/integrating/without-a-bundler.md +156 -4
  46. package/docs/pt-BR/accessibility.md +47 -28
  47. package/docs/pt-BR/index.md +4 -3
  48. package/docs/pt-BR/integrating/index.md +38 -10
  49. package/docs/pt-BR/integrating/tailwind.md +79 -8
  50. package/docs/pt-BR/integrating/without-a-bundler.md +157 -5
  51. package/docs/pt-BR/setup.md +43 -34
  52. package/docs/pt-BR/usage/aesthetics.md +20 -14
  53. package/docs/pt-BR/usage/buttons.md +6 -2
  54. package/docs/pt-BR/usage/composing.md +6 -2
  55. package/docs/pt-BR/usage/content-and-layout.md +10 -4
  56. package/docs/pt-BR/usage/customizing.md +46 -26
  57. package/docs/pt-BR/usage/feedback.md +5 -1
  58. package/docs/pt-BR/usage/forms.md +30 -5
  59. package/docs/pt-BR/usage/index.md +7 -3
  60. package/docs/pt-BR/usage/presets.md +92 -0
  61. package/docs/pt-BR/usage/solo.md +4 -2
  62. package/docs/pt-BR/usage/theming.md +22 -9
  63. package/docs/pt-BR/usage/tooltips.md +2 -2
  64. package/docs/setup.md +31 -22
  65. package/docs/usage/aesthetics.md +19 -13
  66. package/docs/usage/buttons.md +5 -1
  67. package/docs/usage/composing.md +5 -1
  68. package/docs/usage/content-and-layout.md +9 -3
  69. package/docs/usage/customizing.md +45 -25
  70. package/docs/usage/feedback.md +4 -0
  71. package/docs/usage/forms.md +29 -4
  72. package/docs/usage/index.md +6 -2
  73. package/docs/usage/presets.md +91 -0
  74. package/docs/usage/solo.md +3 -1
  75. package/docs/usage/theming.md +21 -8
  76. package/docs/usage/tooltips.md +1 -1
  77. package/llms-full.txt +695 -134
  78. package/llms.txt +3 -2
  79. package/package.json +117 -65
  80. package/{dist/tw → tw}/aesthetics/chunky-tile.css +43 -20
  81. package/{dist/tw → tw}/aesthetics/cyber.css +19 -25
  82. package/{dist/tw → tw}/aesthetics/glass.css +110 -36
  83. package/{dist/tw → tw}/aesthetics/neobrutalism.css +41 -21
  84. package/{dist/tw → tw}/aesthetics/pixel.css +3 -0
  85. package/tw/aesthetics/sketch.css +435 -0
  86. package/{dist/tw → tw}/box.css +109 -26
  87. package/tw/components/button.css +11 -0
  88. package/tw/components/button.rules.css +187 -0
  89. package/tw/components/feedback.css +11 -0
  90. package/{dist/tw/components/feedback.css → tw/components/feedback.rules.css} +156 -33
  91. package/tw/components/form.css +11 -0
  92. package/{dist/tw/components/form.css → tw/components/form.rules.css} +584 -141
  93. package/tw/components/index.css +16 -0
  94. package/tw/components/rules.css +41 -0
  95. package/tw/components/surface.css +11 -0
  96. package/tw/components/surface.rules.css +225 -0
  97. package/tw/components/tooltip.css +11 -0
  98. package/{dist/tw/components/tooltip.css → tw/components/tooltip.rules.css} +82 -61
  99. package/{dist/tw → tw}/corner.css +18 -12
  100. package/{dist/tw → tw}/elevation.css +27 -21
  101. package/{dist/tw → tw}/index.css +11 -9
  102. package/tw/index.d.ts +1 -0
  103. package/tw/intent.css +312 -0
  104. package/tw/modifiers.css +127 -0
  105. package/tw/native.css +323 -0
  106. package/tw/presentation.css +139 -0
  107. package/tw/reset.css +10 -0
  108. package/{dist/tw/reset.css → tw/reset.rules.css} +7 -8
  109. package/tw/size.css +34 -0
  110. package/tw/surface.css +9 -0
  111. package/tw/surface.rules.css +212 -0
  112. package/{dist/tw → tw}/theme.css +93 -29
  113. package/tw/typography.css +8 -0
  114. package/{dist/tw/typography.css → tw/typography.rules.css} +0 -3
  115. package/{dist/tw → tw}/utilities/content.css +248 -108
  116. package/tw/utilities/index.css +11 -0
  117. package/tw/utilities/layout.css +210 -0
  118. package/tw/utilities/rules.css +8 -0
  119. package/dist/tw/aesthetics/sketch.css +0 -398
  120. package/dist/tw/components/button.css +0 -139
  121. package/dist/tw/components/index.css +0 -35
  122. package/dist/tw/components/surface.css +0 -184
  123. package/dist/tw/intent.css +0 -229
  124. package/dist/tw/native.css +0 -203
  125. package/dist/tw/palette.css +0 -460
  126. package/dist/tw/presentation.css +0 -68
  127. package/dist/tw/surface.css +0 -61
  128. package/dist/tw/utilities/index.css +0 -6
  129. package/dist/tw/utilities/layout.css +0 -144
  130. /package/{dist/tw → tw}/aesthetics/index.css +0 -0
@@ -0,0 +1,16 @@
1
+ /* The layer map, stated before any import uses a layer -- see `theme.css`.
2
+ Tailwind's utilities are imported into `utilities` the way
3
+ `tailwindcss/index.css` does it, so every component lands there on this
4
+ entrypoint and on `/native` too, exactly where `.` already puts them. Without
5
+ `layer(utilities)` the CLI emitted them unlayered here, and they beat every
6
+ consumer rule on these two entries alone. */
7
+ @layer theme, base, components, utilities;
8
+
9
+ /* The theme first, as `native.css` has it: Tailwind writes the theme's
10
+ variables at the first `@theme` it meets. See docs/internal/model.md
11
+ (Which files import the theme). */
12
+ @import "../theme.css";
13
+ @import "tailwindcss/utilities.css" layer(utilities);
14
+ /* Everything that is not `src/` -- see the note in `index.css`. Keeps a class
15
+ name in a docs code fence from shipping as a real rule in `dist/components.css`. */
16
+ @import "./rules.css";
@@ -0,0 +1,41 @@
1
+ /* Every component, with nothing they need to compile: the entry that imports
2
+ this file brings the theme and Tailwind once. `index.css` beside it is that
3
+ entry for `/components`; the complete stylesheet and `/native` are the
4
+ others. See docs/internal/model.md (One route to each utility). */
5
+ @import "../typography.rules.css";
6
+
7
+ /* `hidden` hides. The user agent says so at user-agent origin, which loses to
8
+ any author `display`, and every component sets one. The complete stylesheet
9
+ gets this rule from Tailwind's Preflight; this entry and `/native`, which
10
+ imports it, ship no Preflight, so a hidden `.btn` or `.card` stayed on the
11
+ page. Preflight's own rule, in the same layer, so the three entries agree. */
12
+ @layer base {
13
+ [hidden]:where(:not([hidden="until-found"])) {
14
+ display: none !important;
15
+ }
16
+ }
17
+
18
+ /* `box` and `surface` once each, then every component's rules: the focused
19
+ entries beside this file each bring their own reference to `box.css`, and
20
+ importing five of them stacked five copies of every `@apply box`. */
21
+ @import "../box.css";
22
+ @import "../surface.rules.css";
23
+ @import "./button.rules.css";
24
+ @import "./feedback.rules.css";
25
+ @import "./form.rules.css";
26
+ @import "./surface.rules.css";
27
+ @import "./tooltip.rules.css";
28
+
29
+ /* The composition utilities -- `box`, `box-hover`, `box-active`,
30
+ `text-control`, `surface`, `input-group` -- are `@utility` rules whose bodies
31
+ the content scan will not emit on its own. `box` and friends appear only as
32
+ `@utility` blocks and `@apply` targets; `text-control`, `surface` and
33
+ `input-group` appear only inside `:is()`/`:where()` argument lists in
34
+ `intent.css` and `form.css`, never as a selector of their own. Safelisting
35
+ all six makes the emission deliberate; the composition-body check in
36
+ `exports.test.ts` fails the build if one is dropped. `text-control` and
37
+ `surface` are also documented as consumer-usable on their own.
38
+
39
+ The list is for the compiled build only. `scripts/build-tw.mjs` strips it
40
+ from the `tw/` copy, so a `/tw` consumer's Tailwind emits just the classes
41
+ it finds in their files rather than every one named here. */
@@ -0,0 +1,11 @@
1
+ @reference "tailwindcss";
2
+ @import "../theme.css";
3
+ @import "../surface.css";
4
+
5
+ /* The focused entry: what the rules in `surface.rules.css` need to compile on their
6
+ own, then the rules. An aggregate entry imports the rules file directly and
7
+ supplies those needs once -- Tailwind stacks a `@utility` each time a file
8
+ that defines it is imported or referenced, so a second route to `box.css`
9
+ doubles every `@apply box`. See docs/internal/model.md (One route to each
10
+ utility). */
11
+ @import "./surface.rules.css";
@@ -0,0 +1,225 @@
1
+ /* A ghost, edged container that takes one unit when depth is supplied. */
2
+ @utility card {
3
+ @layer families {
4
+ :where(&:not(.p-xs)) {
5
+ --_m-p-xs: initial;
6
+ }
7
+
8
+ :where(&:not(.p-sm)) {
9
+ --_m-p-sm: initial;
10
+ }
11
+
12
+ :where(&:not(.p-lg)) {
13
+ --_m-p-lg: initial;
14
+ }
15
+
16
+ :where(&:not(.flush)) {
17
+ --_m-flush: initial;
18
+ }
19
+ }
20
+
21
+ /* The hover flag, raised by `interactive` and `hoverable` below. */
22
+ @layer families {
23
+ :where(&:not(.interactive, .hoverable)) {
24
+ --_m-hover: initial;
25
+ }
26
+ }
27
+
28
+ @apply surface;
29
+
30
+ /* After the `@apply`, not before: `surface` inlines its own resting geometry
31
+ at that point, so defaults written above it would be overridden by the very
32
+ utility they are meant to specialise. */
33
+ --_d-fill: 0%;
34
+ --_d-fg-on-fill: 0%;
35
+ --_d-border: 100%;
36
+ --_d-elevation: 1;
37
+
38
+ /* The padding steps and `flush`. Each raises a flag (`modifiers.css`) and
39
+ the lines below compute only while theirs is up, so `@apply card p-xs`
40
+ carries the step. `--_scale-pad` is the step `box` scales the corner
41
+ and the depth by; `flush` leaves it alone. */
42
+ --_xs-pad: var(--_m-p-xs) 0.5rem;
43
+ --_sm-pad: var(--_m-p-sm) 0.75rem;
44
+ --_lg-pad: var(--_m-p-lg) 2rem;
45
+ --_flush-pad: var(--_m-flush) 0px;
46
+ --_xs-scale: var(--_m-p-xs) 0.5;
47
+ --_sm-scale: var(--_m-p-sm) 0.75;
48
+ --_lg-scale: var(--_m-p-lg) 1.25;
49
+ --_scale-pad: var(--_lg-scale, var(--_sm-scale, var(--_xs-scale)));
50
+
51
+ padding: var(--_flush-pad, var(--_lg-pad, var(--_sm-pad, var(--_xs-pad, 1.25rem))));
52
+
53
+ /* A quiet neutral tint -- `.soft`, on the card or on a container -- rests
54
+ untinted instead of mixing its 12%: 12% of `--color-text` onto any of
55
+ `--color-background`, `--color-foreground`, or `--color-surface` lands
56
+ within a few units of the others (a near-black ink dominates the result),
57
+ so swapping the ground token alone does not read as quieter. A resting
58
+ neutral `.card.soft` is `--color-foreground` with no ink mixed in -- a
59
+ plain step up from the page that marks a different container without
60
+ drawing attention to it.
61
+
62
+ A named intent keeps its ordinary 12% wash: 12% of an actual hue does not
63
+ read as a slab the way 12% of near-black ink does (`.card.soft.destructive`
64
+ is a gentle `249, 225, 225`), and untinting it would silence it rather than
65
+ quiet it. `.ghost` and `.solid` are not a tint and keep their own plates.
66
+
67
+ Which case applies is `surface`'s `--_quiet`, stepped from the resolved
68
+ fill and the intent's cap rather than keyed on classes on the card, so a
69
+ container's `.soft` renders the plate the card's own `.soft` does. Keyed
70
+ on the card's classes, `.soft > .card` drew the grey slab. `--_fill-cap`
71
+ (box.css's override slot, not `--ui-fill` directly -- see the comment
72
+ there) drops to 0% only when quiet, and the ground steps to the
73
+ foreground only then, through `--_d-surface-ground`, the default
74
+ `surface` reads beneath the public `--ui-surface-ground`, so a consumer's
75
+ override, or glass's ground, wins.
76
+
77
+ Composed with `box-hover`'s formula (`min(100%, resting + 14%)`), the
78
+ quiet rest reveals the intent on hover with no second fill declaration:
79
+ resting at 0% means hover lands at exactly the hover step, 14%, in
80
+ `--intent-hover`, and the ground steps to `--color-surface` at the same
81
+ moment. Scoped to `.interactive` and `.hoverable`, the two classes that
82
+ opt a card into `box-hover`. A named intent rests tinted, so the generic
83
+ formula deepens it on hover (12% to 26%). */
84
+ --_fill-cap: calc(100% - var(--_quiet));
85
+ --_d-surface-ground: color-mix(in oklab, var(--color-foreground) var(--_quiet), var(--color-background));
86
+
87
+ --_hover-ground: var(--_m-hover) color-mix(in oklab, var(--color-surface) var(--_quiet), var(--color-background));
88
+
89
+ /* Behind the query `box-hover` is, so a tapped card keeps its ground. */
90
+ @media (hover: hover) {
91
+ &:hover {
92
+ --_d-surface-ground: var(
93
+ --_hover-ground,
94
+ color-mix(in oklab, var(--color-foreground) var(--_quiet), var(--color-background))
95
+ );
96
+ }
97
+ }
98
+
99
+ /* Opt-in, because a card is only sometimes a control. Applications still owe
100
+ it a real interactive element and an accessible name. Hover and press are
101
+ the derived ones every other pressable box gets; there is nothing
102
+ card-specific about either.
103
+
104
+ `.interactive` is the whole opt-in and stays that way; `.hoverable` and
105
+ `.pressable` are its halves, for a card that answers one gesture and not
106
+ the other. A card with its own nested controls takes `.hoverable` for the
107
+ hover warmth without the press-scale that reads as "you activated the
108
+ card" when the click really landed on a nested button. `.hoverable` alone
109
+ does not set the pointer cursor: that travels with being a click target,
110
+ i.e. with `.pressable` / `.interactive`. */
111
+ }
112
+
113
+ /* The opt-in itself: utilities, so `@apply card interactive` carries the
114
+ gestures. Each is `box-hover` or `box-active` under the name a card's
115
+ markup uses, and the hover half raises the flag the card's quiet ground
116
+ reads. The pointer is 0-1-0 here, below `box`'s disabled cursor, so a
117
+ disabled interactive card does not invite the click. */
118
+ @utility hoverable {
119
+ @layer families {
120
+ --_m-hover: ;
121
+ }
122
+
123
+ @apply box-hover;
124
+ }
125
+
126
+ @utility pressable {
127
+ @apply box-active cursor-pointer;
128
+ }
129
+
130
+ @utility interactive {
131
+ @layer families {
132
+ --_m-hover: ;
133
+ }
134
+
135
+ @apply box-hover box-active cursor-pointer;
136
+ }
137
+
138
+ /* A panel is the flush, unelevated sibling: sidebars, toolbars, and wells that
139
+ sit flat against the layout rather than floating above it. Soft and edgeless
140
+ is what makes it read as a well without drawing a line to say so. */
141
+ @utility panel {
142
+ @layer families {
143
+ :where(&:not(.p-xs)) {
144
+ --_m-p-xs: initial;
145
+ }
146
+
147
+ :where(&:not(.p-sm)) {
148
+ --_m-p-sm: initial;
149
+ }
150
+
151
+ :where(&:not(.p-lg)) {
152
+ --_m-p-lg: initial;
153
+ }
154
+
155
+ :where(&:not(.flush)) {
156
+ --_m-flush: initial;
157
+ }
158
+ }
159
+
160
+ @apply surface;
161
+
162
+ --_d-fill: 12%;
163
+ --_d-fg-on-fill: 0%;
164
+ --_d-border: 0%;
165
+ --_d-elevation: 0;
166
+
167
+ /* The padding steps and `flush`. Each raises a flag (`modifiers.css`) and
168
+ the lines below compute only while theirs is up, so `@apply card p-xs`
169
+ carries the step. `--_scale-pad` is the step `box` scales the corner
170
+ and the depth by; `flush` leaves it alone. */
171
+ --_xs-pad: var(--_m-p-xs) 0.25rem;
172
+ --_sm-pad: var(--_m-p-sm) 0.5rem;
173
+ --_lg-pad: var(--_m-p-lg) 1.5rem;
174
+ --_flush-pad: var(--_m-flush) 0px;
175
+ --_xs-scale: var(--_m-p-xs) 0.5;
176
+ --_sm-scale: var(--_m-p-sm) 0.75;
177
+ --_lg-scale: var(--_m-p-lg) 1.25;
178
+ --_scale-pad: var(--_lg-scale, var(--_sm-scale, var(--_xs-scale)));
179
+
180
+ padding: var(--_flush-pad, var(--_lg-pad, var(--_sm-pad, var(--_xs-pad, 1rem))));
181
+
182
+ /* Neutral panels are the common case -- layout wells, sidebars, toolbars with
183
+ no named intent -- and 12% of `--color-text` (near-black) mixed onto the
184
+ page is far more assertive than 12% of a hue (`217, 217, 217` against a
185
+ `250, 250, 250` page): the same "neutral ink reads louder" effect
186
+ `.card.soft` answers, and the reason `--intent-fill-max` stops neutral's
187
+ fill short everywhere else. So a quiet neutral tint rests untinted on
188
+ `--color-foreground`, through `--_d-surface-ground`, the default
189
+ `.card.soft` rests on, so a bare `.panel` rests at exactly the plate a
190
+ `.card.soft` does, and a consumer's `--ui-surface-ground` or glass's
191
+ translucent ground wins over it: a neutral panel under `.glass` is glass.
192
+
193
+ A named intent keeps its wash: a panel is edgeless by default, so the fill
194
+ is the only channel a `.primary` bulk-actions bar or a `.destructive`
195
+ danger-zone panel has to say what it is (a resting `.panel.destructive` is
196
+ a gentle `249, 225, 225` wash). `.ghost` and `.solid` keep their own
197
+ plates. Which case applies is `surface`'s `--_quiet`, stepped from the
198
+ resolved fill, so a container's `.ghost` or `.solid` renders the panel its
199
+ own would; keyed on the panel's classes, both landed on the quiet plate.
200
+ Permanent rather than hover-revealed: a panel has no interaction state to
201
+ reveal a tint into. */
202
+ --_fill-cap: calc(100% - var(--_quiet));
203
+ --_d-surface-ground: color-mix(in oklab, var(--color-foreground) var(--_quiet), var(--color-background));
204
+ }
205
+
206
+ /* A closed `<dialog>` is hidden, and an open one centred, by `surface` itself,
207
+ so every element that applies it -- these three, `.alert`, a consumer's
208
+ `@apply surface` -- gets both (see `surface.css`). That hide is in the
209
+ layer the surface is applied in. For the three containers a consumer
210
+ reaches for as a dialog, it is restated here unlayered: hiding a closed
211
+ dialog is behaviour, so it beats any layered rule whatever its specificity.
212
+ The centring stays a default in `utilities` at zero specificity, so a
213
+ consumer's own margin -- a utility, or unlayered CSS -- still places one.
214
+
215
+ Here rather than in the reset, so every entrypoint that ships `.card` ships
216
+ the fix, `/components` included. */
217
+ dialog:is(.card, .panel, .surface):not([open]) {
218
+ display: none;
219
+ }
220
+
221
+ /* The popover counterpart, unlayered for the same reason. `.alert` joins the
222
+ list: a toast is the surface most often given `popover`. */
223
+ :is(.card, .panel, .surface, .alert)[popover]:not(:popover-open) {
224
+ display: none;
225
+ }
@@ -0,0 +1,11 @@
1
+ @reference "tailwindcss";
2
+ @import "../theme.css";
3
+ @reference "../surface.css";
4
+
5
+ /* The focused entry: what the rules in `tooltip.rules.css` need to compile on their
6
+ own, then the rules. An aggregate entry imports the rules file directly and
7
+ supplies those needs once -- Tailwind stacks a `@utility` each time a file
8
+ that defines it is imported or referenced, so a second route to `box.css`
9
+ doubles every `@apply box`. See docs/internal/model.md (One route to each
10
+ utility). */
11
+ @import "./tooltip.rules.css";
@@ -1,8 +1,3 @@
1
- @reference "tailwindcss";
2
- @import "../theme.css";
3
- @reference "../box.css";
4
- @reference "../surface.css";
5
-
6
1
  /* The host is a positioning wrapper and nothing else -- it paints no fill, no
7
2
  edge, no material of its own. The bubble is a real element beside the trigger
8
3
  rather than a pseudo-element, so intent, presentation, aesthetic, and
@@ -17,40 +12,45 @@
17
12
  trigger's own `aria-describedby`; wiring that association is the consumer's,
18
13
  this package only styles the bubble. See docs/usage/tooltips.md. */
19
14
  @utility tooltip {
20
- --tooltip-gap: 0.5rem;
21
- /* How long the bubble stays after the pointer leaves the trigger: long
22
- enough to cross `--tooltip-gap` onto the bubble, which keeps it open
23
- (WCAG 1.4.13). */
24
- --tooltip-linger: 300ms;
25
- --tooltip-top: calc(var(--tooltip-gap) * -1);
26
- --tooltip-right: auto;
27
- --tooltip-bottom: auto;
28
- --tooltip-left: 50%;
29
- --tooltip-origin: bottom;
30
- --tooltip-transform: translate(-50%, -100%);
15
+ /* `--tooltip-gap` and `--tooltip-linger` are public, read with their
16
+ defaults rather than declared, so a value set on any ancestor reaches the
17
+ tooltip. The linger is how long the bubble stays after the pointer leaves
18
+ the trigger: long enough to cross the gap onto the bubble, which keeps it
19
+ open (WCAG 1.4.13). The default is resolved here, so a bubble outside a
20
+ tooltip keeps no delay unless a linger is set above it. The bubble reads
21
+ the public one first, so a value set on the bubble itself, as 0.8
22
+ allowed, still wins. */
23
+ --_tooltip-gap: var(--tooltip-gap, 0.5rem);
24
+ --_tooltip-linger: var(--tooltip-linger, 300ms);
25
+ --_tooltip-top: calc(var(--_tooltip-gap) * -1);
26
+ --_tooltip-right: auto;
27
+ --_tooltip-bottom: auto;
28
+ --_tooltip-left: 50%;
29
+ --_tooltip-origin: bottom;
30
+ --_tooltip-transform: translate(-50%, -100%);
31
31
 
32
32
  @apply relative inline-block;
33
33
 
34
34
  &[data-tooltip-position="bottom"] {
35
- --tooltip-top: auto;
36
- --tooltip-bottom: calc(var(--tooltip-gap) * -1);
37
- --tooltip-origin: top;
38
- --tooltip-transform: translate(-50%, 100%);
35
+ --_tooltip-top: auto;
36
+ --_tooltip-bottom: calc(var(--_tooltip-gap) * -1);
37
+ --_tooltip-origin: top;
38
+ --_tooltip-transform: translate(-50%, 100%);
39
39
  }
40
40
 
41
41
  &[data-tooltip-position="left"] {
42
- --tooltip-top: 50%;
43
- --tooltip-left: calc(var(--tooltip-gap) * -1);
44
- --tooltip-origin: right;
45
- --tooltip-transform: translate(-100%, -50%);
42
+ --_tooltip-top: 50%;
43
+ --_tooltip-left: calc(var(--_tooltip-gap) * -1);
44
+ --_tooltip-origin: right;
45
+ --_tooltip-transform: translate(-100%, -50%);
46
46
  }
47
47
 
48
48
  &[data-tooltip-position="right"] {
49
- --tooltip-top: 50%;
50
- --tooltip-left: auto;
51
- --tooltip-right: calc(var(--tooltip-gap) * -1);
52
- --tooltip-origin: left;
53
- --tooltip-transform: translate(100%, -50%);
49
+ --_tooltip-top: 50%;
50
+ --_tooltip-left: auto;
51
+ --_tooltip-right: calc(var(--_tooltip-gap) * -1);
52
+ --_tooltip-origin: left;
53
+ --_tooltip-transform: translate(100%, -50%);
54
54
  }
55
55
  }
56
56
 
@@ -64,13 +64,13 @@
64
64
  /* Hidden rather than merely transparent, so a closed bubble is nothing
65
65
  under the pointer, and it lingers before it goes: the host stays hovered
66
66
  once the pointer is over the bubble, so crossing the gap in
67
- `--tooltip-linger` keeps it open. Visibility switches at the end of the
67
+ `--_tooltip-linger` keeps it open. Visibility switches at the end of the
68
68
  fade, so the bubble is there to land on for the whole of it. */
69
69
  visibility: hidden;
70
70
  transition-property: opacity, transform, visibility;
71
71
  transition-duration: var(--motion-duration-normal);
72
72
  transition-timing-function: var(--motion-ease);
73
- transition-delay: var(--tooltip-linger, 0s);
73
+ transition-delay: var(--tooltip-linger, var(--_tooltip-linger, 0s));
74
74
 
75
75
  /* Filled, over a ground of its own. An intent floods the fill
76
76
  (`.tooltip-bubble.primary` is the primary colour); with no intent
@@ -86,6 +86,10 @@
86
86
  --_d-elevation: 0;
87
87
  --_d-ground: var(--ui-surface-ground, var(--intent-subtle));
88
88
 
89
+ /* `surface` refuses the silhouette clip because it holds other things. The
90
+ bubble holds a line of text, so it takes the aesthetic's shape back. */
91
+ clip-path: var(--ui-clip, none);
92
+
89
93
  /* The bubble's line is a boundary against content nobody chose, so `box`'s
90
94
  edge blend is wrong here: at a full fill the line *is* the plate. Restate
91
95
  the colour only; how much line comes from `--_edge-amount`, floored below. */
@@ -93,9 +97,9 @@
93
97
 
94
98
  /* Floored, for the same reason the ground is opaque: a cascade must not take
95
99
  the boundary from a bubble nobody classed (worst in light, where the plate
96
- is near-white). The element's own `.edgeless` lowers it -- same
97
- element-vs-cascade idiom `text-control` uses. */
98
- --_edge-floor: 100%;
100
+ is near-white). The element's own `.edgeless` lowers it -- the same
101
+ `--_own-edge` `text-control` reads. */
102
+ --_edge-floor: var(--_own-edge);
99
103
 
100
104
  /* A bubble is a small transient popover, not a modal, so it takes the
101
105
  control corner `box` computes rather than the surface one `surface` hands
@@ -105,39 +109,52 @@
105
109
  calc(var(--_corner) * var(--ui-corner-tl, 1)) calc(var(--_corner) * var(--ui-corner-tr, 1))
106
110
  calc(var(--_corner) * var(--ui-corner-br, 1)) calc(var(--_corner) * var(--ui-corner-bl, 1))
107
111
  );
108
- left: var(--tooltip-left);
109
- right: var(--tooltip-right);
110
- top: var(--tooltip-top);
111
- bottom: var(--tooltip-bottom);
112
- transform: var(--tooltip-transform) scale(0);
113
- transform-origin: var(--tooltip-origin);
114
-
115
- &.edgeless {
116
- --_edge-floor: 0%;
117
- }
112
+ left: var(--_tooltip-left);
113
+ right: var(--_tooltip-right);
114
+ top: var(--_tooltip-top);
115
+ bottom: var(--_tooltip-bottom);
116
+ transform: var(--_tooltip-transform) scale(0);
117
+ transform-origin: var(--_tooltip-origin);
118
118
 
119
119
  /* The bubble appears without growing under reduced motion, for the entries
120
120
  without the reset; see `box.css`. */
121
121
  @media (prefers-reduced-motion: reduce) {
122
122
  transition-duration: 0.01ms;
123
123
  }
124
- }
125
124
 
126
- /* Shown on hover or focus anywhere in the host, or pinned open with
127
- `data-state="open"`. `:focus-within` rather than the trigger's own
128
- `:focus-visible`, the same substitution `.input-group` makes: the bubble is a
129
- sibling of the trigger, so only the shared ancestor can see both at once. The
130
- cost is the one `.input-group` accepts -- the bubble also shows on a mouse
131
- click.
132
-
133
- In `utilities`, where the bubble's own `@utility` is, winning on specificity
134
- -- see the seams note in `form.css`. */
135
- @layer utilities {
136
- .tooltip:hover .tooltip-bubble,
137
- .tooltip:focus-within .tooltip-bubble,
138
- .tooltip[data-state="open"] .tooltip-bubble {
125
+ /* The no-intent plate, carried so `@apply tooltip-bubble` gets it: the rule
126
+ in `intent.css` names `.tooltip-bubble`, which a consumer's `.my-tip` is
127
+ not, so an applied bubble took `surface`'s neutral reset and drew the
128
+ page's ink at a fifth. In `families` after that reset, which `surface`
129
+ carries at the same zero specificity, so this one wins and an intent
130
+ applied with it still beats both. Never on `.tooltip-bubble` itself,
131
+ which keeps the rule in `components`. */
132
+ @layer families {
133
+ :where(&:not(.tooltip-bubble, .primary, .secondary, .success, .warning, .destructive, .info)) {
134
+ --intent-color: var(--color-tooltip);
135
+ --intent-contrast: var(--color-tooltip-contrast);
136
+ --intent-hover: var(--color-tooltip);
137
+ --intent-strong: var(--color-tooltip-contrast);
138
+ --intent-subtle: var(--color-surface);
139
+ --intent-border: var(--ui-ink, var(--color-border));
140
+ --intent-fill-max: 100%;
141
+ }
142
+ }
143
+
144
+ /* Shown on hover or focus anywhere in the host, or pinned open with
145
+ `data-state="open"`. `:focus-within` rather than the trigger's own
146
+ `:focus-visible`, the same substitution `.input-group` makes: the bubble
147
+ is a sibling of the trigger, so only the shared ancestor can see both at
148
+ once. The cost is the one `.input-group` accepts -- the bubble also shows
149
+ on a mouse click.
150
+
151
+ In the body, so an applied bubble opens in a `.tooltip` as the class
152
+ does; they win over the resting values above on specificity. */
153
+ .tooltip:hover &,
154
+ .tooltip:focus-within &,
155
+ .tooltip[data-state="open"] & {
139
156
  opacity: 1;
140
- transform: var(--tooltip-transform) scale(1);
157
+ transform: var(--_tooltip-transform) scale(1);
141
158
  visibility: visible;
142
159
  transition-delay: 0s;
143
160
  }
@@ -147,9 +164,9 @@
147
164
  writes `data-state="closed"` and clears it when the pointer and focus
148
165
  leave. Later and as specific as the rule above, so it wins over hover and
149
166
  focus alike. */
150
- .tooltip[data-state="closed"] .tooltip-bubble {
167
+ .tooltip[data-state="closed"] & {
151
168
  opacity: 0;
152
- transform: var(--tooltip-transform) scale(0);
169
+ transform: var(--_tooltip-transform) scale(0);
153
170
  visibility: hidden;
154
171
  transition-delay: 0s;
155
172
  }
@@ -176,6 +193,10 @@
176
193
  --_d-elevation: 0;
177
194
  --_d-ground: var(--intent-subtle);
178
195
 
196
+ /* A ground of its own, so no ink from above passes through, as on `surface`. */
197
+ --_held-ink: var(--_fg-own);
198
+ --_held-on: var(--_own-hold);
199
+
179
200
  /* The trigger is best a real `<button>`, which is a `.btn` under `/native`
180
201
  and carries the user agent's padding under `/components`; either would
181
202
  stretch the chip past its own size. */
@@ -1,5 +1,7 @@
1
- /* Imported into the `components` layer by `theme.css`, below every utility, so
2
- a consumer's own utility or unlayered CSS beats these classes. See
1
+ /* Each class here is a `@utility` whose body sits in `utilities.families`: a
2
+ utility, so `@apply` and Tailwind's variants reach it, and in a sublayer, so
3
+ it still loses to every rule written straight into `utilities` -- a
4
+ component's, or a consumer's own utility. See
3
5
  docs/internal/model.md#cascade-layers.
4
6
 
5
7
  The corner pattern: which corners take the corner the aesthetic draws. `box`
@@ -10,17 +12,21 @@
10
12
  is taken as written and ignores the pattern. See docs/internal/model.md#material-tokens. */
11
13
 
12
14
  /* Top-left and bottom-right. */
13
- .cut-diagonal {
14
- --ui-corner-tl: 1;
15
- --ui-corner-tr: 0;
16
- --ui-corner-br: 1;
17
- --ui-corner-bl: 0;
15
+ @utility cut-diagonal {
16
+ @layer families {
17
+ --ui-corner-tl: 1;
18
+ --ui-corner-tr: 0;
19
+ --ui-corner-br: 1;
20
+ --ui-corner-bl: 0;
21
+ }
18
22
  }
19
23
 
20
24
  /* Top-right and bottom-left. */
21
- .cut-diagonal-reverse {
22
- --ui-corner-tl: 0;
23
- --ui-corner-tr: 1;
24
- --ui-corner-br: 0;
25
- --ui-corner-bl: 1;
25
+ @utility cut-diagonal-reverse {
26
+ @layer families {
27
+ --ui-corner-tl: 0;
28
+ --ui-corner-tr: 1;
29
+ --ui-corner-br: 0;
30
+ --ui-corner-bl: 1;
31
+ }
26
32
  }
@@ -1,5 +1,7 @@
1
- /* Imported into the `components` layer by `theme.css`, below every utility, so
2
- a consumer's own utility or unlayered CSS beats these classes. See
1
+ /* Each class here is a `@utility` whose body sits in `utilities.families`: a
2
+ utility, so `@apply` and Tailwind's variants reach it, and in a sublayer, so
3
+ it still loses to every rule written straight into `utilities` -- a
4
+ component's, or a consumer's own utility. See
3
5
  docs/internal/model.md#cascade-layers. */
4
6
 
5
7
  /* Elevation is a modifier, not an axis: depth is a per-element decision (raised
@@ -8,8 +10,8 @@
8
10
  `.flat` removes a part-based slab or blur without a conditional (complete
9
11
  surface shadows like glass remain). See model.md#elevation.
10
12
 
11
- `--ui-elevation`, and the plain-page fallback geometry (`--elevation-y`,
12
- `--elevation-blur`) below, are registered non-inheriting: a class sets them
13
+ `--ui-elevation`, and the plain-page fallback geometry (`--_elevation-y`,
14
+ `--_elevation-blur`) below, are registered non-inheriting: a class sets them
13
15
  on the element it is written on and nothing else picks them up. Inherited, a
14
16
  `.floating` dialog's ambient x2 would reach its own unclassed buttons and
15
17
  double their shadow with nothing in the markup asking for it. Depth is not
@@ -35,39 +37,43 @@
35
37
  inherits: false;
36
38
  }
37
39
 
38
- @property --elevation-y {
40
+ @property --_elevation-y {
39
41
  syntax: "*";
40
42
  inherits: false;
41
43
  }
42
44
 
43
- @property --elevation-blur {
45
+ @property --_elevation-blur {
44
46
  syntax: "*";
45
47
  inherits: false;
46
48
  }
47
49
 
48
50
  /* No part-based depth. Complete surface shadow values, such as glass, are not
49
51
  reached by the elevation multiplier. */
50
- .flat {
51
- --ui-elevation: 0;
52
+ @utility flat {
53
+ @layer families {
54
+ --ui-elevation: 0;
55
+ }
52
56
  }
53
57
 
54
- /* The plain-step geometry both rungs multiply, declared once so the two cannot
55
- drift. It is the fallback for a page with no aesthetic in scope; an aesthetic
58
+ /* The plain-step geometry both rungs multiply. Each utility restates it so
59
+ `@apply raised` carries it, so the two copies must stay identical. It is the fallback for a page with no aesthetic in scope; an aesthetic
56
60
  that declares its own shadow parts overrides it, and the elevation multiplier
57
61
  scales whichever one lands. `.floating` takes its extra depth from the
58
- multiplier below, not a second copy of these parts. */
59
- .raised,
60
- .floating {
61
- --elevation-y: 1px;
62
- --elevation-blur: 3px;
63
- }
64
-
62
+ multiplier, not from different parts. */
65
63
  /* The aesthetic's depth as authored, said explicitly. */
66
- .raised {
67
- --ui-elevation: 1;
64
+ @utility raised {
65
+ @layer families {
66
+ --_elevation-y: 1px;
67
+ --_elevation-blur: 3px;
68
+ --ui-elevation: 1;
69
+ }
68
70
  }
69
71
 
70
72
  /* Twice it, for menus and popovers. */
71
- .floating {
72
- --ui-elevation: 2;
73
+ @utility floating {
74
+ @layer families {
75
+ --_elevation-y: 1px;
76
+ --_elevation-blur: 3px;
77
+ --ui-elevation: 2;
78
+ }
73
79
  }