@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,187 @@
1
+ /* A filled, edgeless, raised box you can press. `box` paints it; everything
2
+ here is what makes it a button rather than some other box.
3
+
4
+ The resting values are the published default: a button with no presentation
5
+ class is solid and edgeless, and that is a decision written on three lines
6
+ rather than a `var()` fallback buried in an expression. */
7
+ @utility btn {
8
+ /* The neutral reset, carried so `@apply btn` gets it: the reset in
9
+ `intent.css` names `.btn`, which a consumer's `.my-btn` is not, so an
10
+ applied one inside a `.card.success` took the card's intent. In
11
+ `families`, beside the intent classes, so one applied with it wins; never
12
+ on `.btn` itself, which keeps the reset in `components`. See
13
+ docs/internal/model.md#what-apply-carries. */
14
+ @layer families {
15
+ :where(&:not(.btn, .primary, .secondary, .success, .warning, .destructive, .info)) {
16
+ --intent-color: var(--color-text);
17
+ --intent-contrast: var(--color-text-contrast);
18
+ --intent-hover: var(--color-text-hover);
19
+ --intent-strong: var(--color-text-strong);
20
+ --intent-subtle: var(--color-surface);
21
+ --intent-border: var(--ui-ink, var(--color-border));
22
+ --intent-fill-max: 20%;
23
+ }
24
+ }
25
+
26
+ /* The modifier flags, restated so a container's do not reach this button:
27
+ a `.card.p-xs` would otherwise shrink every button inside it. At zero
28
+ specificity in `families`, beside the flag utilities, so one on the
29
+ element wins, applied or as a class. See `modifiers.css`. */
30
+ @layer families {
31
+ :where(&:not(.icon)) {
32
+ --_m-icon: initial;
33
+ }
34
+
35
+ :where(&:not(.pill)) {
36
+ --_m-pill: initial;
37
+ }
38
+
39
+ :where(&:not(.p-xs)) {
40
+ --_m-p-xs: initial;
41
+ }
42
+
43
+ :where(&:not(.p-sm)) {
44
+ --_m-p-sm: initial;
45
+ }
46
+
47
+ :where(&:not(.p-lg)) {
48
+ --_m-p-lg: initial;
49
+ }
50
+ }
51
+
52
+ @apply box box-hover box-active relative inline-flex w-fit cursor-pointer items-center justify-center gap-2 px-4 py-2 whitespace-nowrap no-underline select-none;
53
+
54
+ /* After `@apply box`: `box` states its own resting defaults, and the later
55
+ declaration wins. */
56
+ --_d-fill: 100%;
57
+ --_d-fg-on-fill: 100%;
58
+ --_d-border: 0%;
59
+ --_d-elevation: 1;
60
+ --_d-holds-ink: ;
61
+
62
+ /* With no fill, the neutral line is the region's unfilled ink when the
63
+ aesthetic names one -- glass does, for a `.ghost.edged` button. See `--_bare-ink` in
64
+ `box.css`. Unset, the reference is invalid and `box` keeps its line. */
65
+ --_bare-ink: var(--_unfilled-ink);
66
+
67
+ /* The default step's vertical padding gives back the line `box` draws, so a
68
+ default button is exactly `--control-height` tall like `.ipt` beside it,
69
+ under any aesthetic's line width; at the default 16px/1.5 text the label
70
+ and the padding fill it. The surrounding font, not the user agent's
71
+ smaller button one, on every entry: `/components` carries no reset to say
72
+ so, and a `.btn` link and a `.btn` button have to print the same label.
73
+ Both are the fallbacks of the size steps below. */
74
+ font-family: inherit;
75
+ /* The label is material, shared with `.badge`: weight, tracking, case, and a
76
+ text shadow, set by an aesthetic as tokens (`.chunky-tile`'s heavier,
77
+ tracked label), and a consumer's own `font-*`, `tracking-*`, `uppercase`, or
78
+ text-shadow utility still beats the aesthetic. Tracking, case, and shadow
79
+ fall back to `normal` and `none`, the label's own values, so none of them
80
+ inherits: body copy's wider tracking is set on `body`, and a button that
81
+ took it, or a container's, printed a tracked label nothing asked for. The
82
+ user agent already stops case and shadow inheriting into a button, and
83
+ `/native` maps `<button>` and the button inputs here. The shadow is written
84
+ without a colour, so it takes the label's own and cannot lower its
85
+ contrast. See docs/internal/model.md#cascade-layers. */
86
+ font-weight: var(--ui-label-weight, var(--font-weight-semibold));
87
+ letter-spacing: var(--ui-label-tracking, normal);
88
+ text-transform: var(--ui-label-case, none);
89
+ text-shadow: var(--ui-label-shadow, none);
90
+
91
+ /* The size steps. `sm` and `lg` raise a flag (`size.css`) and each line
92
+ below computes only while its flag is up, so the declarations that read
93
+ them fall back to the default step. Written in the component's own body,
94
+ not as `&.sm`, so `@apply btn sm` carries the step and a consumer's
95
+ `min-h-*` or `px-*` beats it as it beats the default. `--_scale-size`
96
+ is the step `box` scales the corner and the depth by. */
97
+ --_sm-h: var(--_size-sm) 2rem;
98
+ --_lg-h: var(--_size-lg) 3rem;
99
+ --_sm-px: var(--_size-sm) 0.75rem;
100
+ --_lg-px: var(--_size-lg) 1.25rem;
101
+ --_sm-py: var(--_size-sm) 0.25rem;
102
+ --_lg-py: var(--_size-lg) 0.625rem;
103
+ --_sm-text: var(--_size-sm) var(--text-sm);
104
+ --_lg-text: var(--_size-lg) 1.0625rem;
105
+ --_sm-leading: var(--_size-sm) var(--text-sm--line-height);
106
+ --_sm-scale: var(--_size-sm) 0.75;
107
+ --_lg-scale: var(--_size-lg) 1.25;
108
+ --_scale-size: var(--_sm-scale, var(--_lg-scale));
109
+
110
+ /* The modifiers: `icon`, `pill`, and the padding steps. Each raises a flag
111
+ (`modifiers.css`) and the lines below compute only while theirs is up, as
112
+ the size steps above do, so `@apply btn icon` carries the shape. A line
113
+ that names two flags needs both.
114
+
115
+ Bare `sm`/`lg` are the size modifier's names, so the padding steps take
116
+ a `p-` prefix. `p-xs` is the tightest, below `p-sm`: a chip-sized
117
+ button in a toolbar or a table cell. */
118
+ --_icon-pad: var(--_m-icon) var(--_sm-icon-pad, 0.5rem);
119
+ --_sm-icon-pad: var(--_size-sm) calc(0.375rem - var(--_width));
120
+ --_xs-px: var(--_m-p-xs) --spacing(2);
121
+ --_xs-py: var(--_m-p-xs) --spacing(0.5);
122
+ --_psm-px: var(--_m-p-sm) --spacing(2.5);
123
+ --_psm-py: var(--_m-p-sm) --spacing(1);
124
+ --_plg-px: var(--_m-p-lg) --spacing(6);
125
+ --_plg-py: var(--_m-p-lg) --spacing(3);
126
+ /* An icon button's padding step is the same on every side. */
127
+ --_xs-icon-px: var(--_m-icon) var(--_xs-py);
128
+ --_psm-icon-px: var(--_m-icon) var(--_psm-py);
129
+ --_plg-icon-px: var(--_m-icon) var(--_plg-py);
130
+ --_xs-scale: var(--_m-p-xs) 0.5;
131
+ --_psm-scale: var(--_m-p-sm) 0.75;
132
+ --_plg-scale: var(--_m-p-lg) 1.25;
133
+ --_scale-pad: var(--_plg-scale, var(--_psm-scale, var(--_xs-scale)));
134
+
135
+ /* The size floor. `p-xs` is the one padding step that drops it for a
136
+ labeled button: at that tier the button is a chip in a toolbar or a table
137
+ cell, where `--control-height` is taller than the row it sits in; every
138
+ other size and padding class keeps the floor, so a plain button stays a
139
+ comfortable target.
140
+
141
+ An icon button drops both floors at every padding tier. It is already
142
+ square -- `aspect-ratio` ties its width to whatever height it lands on
143
+ -- so a surviving floor would keep the button bigger than its own glyph
144
+ however tight the padding asked to be. */
145
+ --_floor: var(--_sm-h, var(--_lg-h, var(--control-height)));
146
+ --_xs-floor: var(--_m-p-xs) 0px;
147
+ --_psm-icon-floor: var(--_m-icon) var(--_m-p-sm) 0px;
148
+ --_plg-icon-floor: var(--_m-icon) var(--_m-p-lg) 0px;
149
+ --_icon-floor: var(--_m-icon) var(--_xs-floor, var(--_psm-icon-floor, var(--_plg-icon-floor, var(--_floor))));
150
+
151
+ min-height: var(--_xs-floor, var(--_psm-icon-floor, var(--_plg-icon-floor, var(--_floor))));
152
+ /* Square at each size step. The width is named as well as the height,
153
+ because WebKit does not let `aspect-ratio` widen a box past its
154
+ `min-width`. */
155
+ min-width: var(--_icon-floor, auto);
156
+ --_icon-ratio: var(--_m-icon) 1 / 1;
157
+ aspect-ratio: var(--_icon-ratio, auto);
158
+ padding-inline: var(
159
+ --_plg-icon-px,
160
+ var(
161
+ --_psm-icon-px,
162
+ var(
163
+ --_xs-icon-px,
164
+ var(--_plg-px, var(--_psm-px, var(--_xs-px, var(--_icon-pad, var(--_sm-px, var(--_lg-px, 1rem))))))
165
+ )
166
+ )
167
+ );
168
+ padding-block: var(
169
+ --_plg-py,
170
+ var(--_psm-py, var(--_xs-py, var(--_icon-pad, var(--_sm-py, var(--_lg-py, calc(0.5rem - var(--_width)))))))
171
+ );
172
+ font-size: var(--_sm-text, var(--_lg-text, inherit));
173
+ line-height: var(--_sm-leading, inherit);
174
+
175
+ /* A pill takes the round material ahead of the box radius, which is
176
+ `box`'s own formula restated. Sketch gives the token the button's own box
177
+ outline; a bevel still cuts the full radius to points under cyber. */
178
+ --_pill-radius: var(--_m-pill) var(--ui-radius-pill, calc(infinity * 1px));
179
+ border-radius: var(
180
+ --_pill-radius,
181
+ var(
182
+ --ui-radius,
183
+ calc(var(--_corner) * var(--ui-corner-tl, 1)) calc(var(--_corner) * var(--ui-corner-tr, 1))
184
+ calc(var(--_corner) * var(--ui-corner-br, 1)) calc(var(--_corner) * var(--ui-corner-bl, 1))
185
+ )
186
+ );
187
+ }
@@ -0,0 +1,11 @@
1
+ @reference "tailwindcss";
2
+ @import "../theme.css";
3
+ @reference "../surface.css";
4
+
5
+ /* The focused entry: what the rules in `feedback.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 "./feedback.rules.css";
@@ -1,8 +1,3 @@
1
- @reference "tailwindcss";
2
- @import "../theme.css";
3
- @reference "../box.css";
4
- @reference "../surface.css";
5
-
6
1
  @keyframes anim-skeleton {
7
2
  0% {
8
3
  background-position: 200% 0;
@@ -39,12 +34,11 @@
39
34
 
40
35
  /* Same carve-out as `.panel`'s, and for the same reason: a neutral alert
41
36
  mixes 12% of near-black ink onto the page (`217, 217, 217` against `250,
42
- 250, 250`), a slab rather than a status signal. Scoped to no named intent:
43
- a named intent keeps its ordinary wash, because untinting
44
- `.alert.soft.destructive` would silence the status it exists to carry
45
- rather than quiet it. `:not(...)` names every intent class, plus
46
- `.solid`/`.ghost` so an explicit presentation on the element is never
47
- clamped to a cap it did not ask for.
37
+ 250, 250`), a slab rather than a status signal. Only a quiet neutral tint
38
+ is untinted, through `surface`'s `--_quiet`, stepped from the resolved
39
+ fill so a container's `.ghost` or `.solid` renders the alert as the
40
+ alert's own `.ghost` or `.solid` would. A named intent keeps its ordinary wash, because untinting
41
+ `.alert.soft.destructive` would silence the status it exists to carry.
48
42
 
49
43
  Grounds on `--color-foreground` through `--_d-surface-ground`, exactly like
50
44
  `.panel` and a resting `.card.soft`, so glass's ground and a consumer's
@@ -52,10 +46,8 @@
52
46
  reveal a tint into either, so this is permanent. A neutral alert keeps its
53
47
  identity: `--_d-border: 100%` above holds regardless of fill, so it still
54
48
  reads as a framed box, just a quiet one instead of a grey slab. */
55
- &:not(.primary, .secondary, .success, .warning, .destructive, .info, .solid, .ghost) {
56
- --_fill-cap: 0%;
57
- --_d-surface-ground: var(--color-foreground);
58
- }
49
+ --_fill-cap: calc(100% - var(--_quiet));
50
+ --_d-surface-ground: color-mix(in oklab, var(--color-foreground) var(--_quiet), var(--color-background));
59
51
  }
60
52
 
61
53
  /* No icon is shipped. `.alert` is already `flex`, so an icon dropped in as a
@@ -82,17 +74,38 @@
82
74
  /* A soft, edgeless chip. Small enough that the structural silhouette would be
83
75
  most of its height, so it takes the tight one where an aesthetic ships a pair. */
84
76
  @utility badge {
77
+ /* The neutral reset, carried so `@apply badge` gets it, as `btn` carries its own. */
78
+ @layer families {
79
+ :where(&:not(.badge, .primary, .secondary, .success, .warning, .destructive, .info)) {
80
+ --intent-color: var(--color-text);
81
+ --intent-contrast: var(--color-text-contrast);
82
+ --intent-hover: var(--color-text-hover);
83
+ --intent-strong: var(--color-text-strong);
84
+ --intent-subtle: var(--color-surface);
85
+ --intent-border: var(--ui-ink, var(--color-border));
86
+ --intent-fill-max: 20%;
87
+ }
88
+ }
89
+
90
+ @apply box inline-flex min-h-6 w-fit items-center gap-1 px-2.5 text-xs;
91
+
92
+ /* After `@apply box`: `box` states its own resting defaults, and the later
93
+ declaration wins. */
85
94
  --_d-fill: 12%;
86
95
  --_d-fg-on-fill: 0%;
87
96
  --_d-border: 0%;
88
97
  --_d-elevation: 0;
89
98
 
90
- @apply box inline-flex min-h-6 w-fit items-center gap-1 px-2.5 text-xs;
99
+ /* With no fill, the neutral line is the region's unfilled ink when the
100
+ aesthetic names one -- glass does, for a `.ghost.edged` badge. See `--_bare-ink` in
101
+ `box.css`. Unset, the reference is invalid and `box` keeps its line. */
102
+ --_bare-ink: var(--_unfilled-ink);
91
103
 
92
- /* The label material `.btn` reads. Unset, tracking, case, and shadow inherit,
93
- as a badge's text always has. */
104
+ /* The label material `.btn` reads. Unset, tracking is `normal`, as `.btn`'s
105
+ is, so neither body copy's tracking nor a container's reaches the label;
106
+ case and shadow inherit, as a badge's text always has. */
94
107
  font-weight: var(--ui-label-weight, var(--font-weight-bold));
95
- letter-spacing: var(--ui-label-tracking);
108
+ letter-spacing: var(--ui-label-tracking, normal);
96
109
  text-transform: var(--ui-label-case);
97
110
  text-shadow: var(--ui-label-shadow);
98
111
 
@@ -108,13 +121,31 @@
108
121
  for content rather than a box with a look of its own, so there is nothing for
109
122
  a container's `.solid` or `.edgeless` to say about it. */
110
123
  @utility skeleton {
111
- --skeleton-base: color-mix(in oklab, var(--intent-color) 6%, var(--intent-subtle));
112
- --skeleton-sheen: color-mix(in oklab, var(--intent-color) 10%, var(--color-foreground));
124
+ /* The indicator reset, carried so `@apply skeleton` gets it, as `btn` carries
125
+ its own: the neutral slots, with the two an indicator takes from the
126
+ colour it sits in. Without it an applied one read an undefined
127
+ `--intent-border`. */
128
+ @layer families {
129
+ :where(&:not(.skeleton, .primary, .secondary, .success, .warning, .destructive, .info)) {
130
+ --intent-color: currentColor;
131
+ --intent-contrast: var(--color-text-contrast);
132
+ --intent-hover: var(--color-text-hover);
133
+ --intent-strong: var(--color-text-strong);
134
+ --intent-subtle: var(--color-surface);
135
+ --intent-border: currentColor;
136
+ --intent-fill-max: 20%;
137
+ }
138
+ }
139
+
140
+ /* Public, read with their defaults, so a value set on any ancestor reaches
141
+ the placeholder. */
142
+ --_skeleton-base: var(--skeleton-base, color-mix(in oklab, var(--intent-color) 6%, var(--intent-subtle)));
143
+ --_skeleton-sheen: var(--skeleton-sheen, color-mix(in oklab, var(--intent-color) 10%, var(--color-foreground)));
113
144
 
114
145
  @apply min-h-4 w-full;
115
146
 
116
147
  animation: anim-skeleton calc(var(--motion-duration-slow) * 4) linear infinite;
117
- background: linear-gradient(90deg, var(--skeleton-base), var(--skeleton-sheen), var(--skeleton-base));
148
+ background: linear-gradient(90deg, var(--_skeleton-base), var(--_skeleton-sheen), var(--_skeleton-base));
118
149
  background-size: 200% 100%;
119
150
  /* Not a box, so it draws the control corner and its pattern itself, the way
120
151
  `box` does at scale 1, and reads the corner shape beside it. */
@@ -131,6 +162,15 @@
131
162
  @media (prefers-reduced-motion: reduce) {
132
163
  animation: none;
133
164
  }
165
+
166
+ /* Forced colours remove background images, and the gradient is all a
167
+ skeleton is: it vanished. A system-colour outline keeps the placeholder's
168
+ shape on the page. A border, inside the box, so nothing moves. */
169
+ @media (forced-colors: active) {
170
+ box-sizing: border-box;
171
+ border: 1px solid CanvasText;
172
+ animation: none;
173
+ }
134
174
  }
135
175
 
136
176
  /* The track is a box, the fill is not: `box.css`'s formula composes the frame,
@@ -141,9 +181,52 @@
141
181
  docs/internal/model.md#components-that-do-not-take-the-whole-of-box for the
142
182
  decision and why the other two indicators do not get the same treatment. */
143
183
  @utility progress {
184
+ /* The indicator reset, carried so `@apply progress` gets it, as `btn` carries
185
+ its own: the neutral slots, with the two an indicator takes from the
186
+ colour it sits in. Without it an applied one read an undefined
187
+ `--intent-border`. */
188
+ @layer families {
189
+ :where(&:not(.progress, .primary, .secondary, .success, .warning, .destructive, .info)) {
190
+ --intent-color: currentColor;
191
+ --intent-contrast: var(--color-text-contrast);
192
+ --intent-hover: var(--color-text-hover);
193
+ --intent-strong: var(--color-text-strong);
194
+ --intent-subtle: var(--color-surface);
195
+ --intent-border: currentColor;
196
+ --intent-fill-max: 20%;
197
+ }
198
+ }
199
+
200
+ @layer families {
201
+ :where(&:not(.active)) {
202
+ --_m-active: initial;
203
+ }
204
+
205
+ :where(&:not(.indeterminate)) {
206
+ --_m-indeterminate: initial;
207
+ }
208
+ }
209
+
210
+ /* The two states. Each raises a flag (`modifiers.css`); the pseudo-elements
211
+ below read these lines, which inherit to them as the flags do. */
212
+ --_active-content: var(--_m-active) "";
213
+ --_active-shimmer: var(--_m-active) anim-skeleton calc(var(--motion-duration-slow) * 4) linear infinite;
214
+ --_indeterminate-width: var(--_m-indeterminate) 33%;
215
+ --_indeterminate-sweep: var(--_m-indeterminate) anim-progress-indeterminate 1.5s infinite ease-in-out;
216
+
217
+ /* Printed as drawn. A browser leaves backgrounds off paper by default, and
218
+ the bar is nothing but two backgrounds: the track and the value. */
219
+ print-color-adjust: exact;
220
+
144
221
  --_d-fill: 12%;
145
222
  --_d-fg-on-fill: 0%;
146
223
  --_d-border: 0%;
224
+ /* The track is a 12% tint, 1.2:1 against the page. With more contrast asked
225
+ for it is outlined, so where the bar ends is not left to the tint. An edge
226
+ class still decides: `--ui-border` is read before this. */
227
+ @media (prefers-contrast: more) {
228
+ --_d-border: 100%;
229
+ }
147
230
  --_d-elevation: 0;
148
231
  /* `--progress-color`/`--progress-surface`/`--progress-edge` are the Tier 1
149
232
  override path an aesthetic or consumer reaches `.progress` through (see
@@ -189,14 +272,19 @@
189
272
  border-radius: var(--ui-radius-pill, var(--ui-radius, var(--ui-corner, calc(infinity * 1px))));
190
273
  corner-shape: var(--ui-corner-shape, round);
191
274
  background-color: var(--progress-color, var(--intent-color));
192
- width: var(--progress-value, 0%);
275
+ width: var(--_indeterminate-width, var(--progress-value, 0%));
276
+ animation: var(--_indeterminate-sweep, none);
193
277
  z-index: 2;
194
278
  }
195
279
 
196
- &.active::before {
197
- content: "";
280
+ /* The shimmer, which generates no box unless `active`. Its animation is
281
+ gated with it: WebKit 26 lists an animation on a pseudo-element that
282
+ generates no box in `getAnimations()`, and an endless one there never
283
+ settles (`structure.spec.ts`, "reaches every line a component draws"). */
284
+ &::before {
285
+ content: var(--_active-content, none);
198
286
  @apply absolute inset-0 block h-full;
199
- animation: anim-skeleton calc(var(--motion-duration-slow) * 4) linear infinite;
287
+ animation: var(--_active-shimmer, none);
200
288
  background: linear-gradient(
201
289
  90deg,
202
290
  transparent,
@@ -207,11 +295,6 @@
207
295
  z-index: 1;
208
296
  }
209
297
 
210
- &.indeterminate::after {
211
- width: 33%;
212
- animation: anim-progress-indeterminate 1.5s infinite ease-in-out;
213
- }
214
-
215
298
  /* Reduced motion, for the entries without the reset; see `box.css`. The
216
299
  value moves without sliding, and the shimmer and the indeterminate sweep
217
300
  stop where the reset would stop them: the fill rests at the start. */
@@ -220,9 +303,49 @@
220
303
  transition-duration: 0.01ms;
221
304
  }
222
305
 
223
- &.active::before,
224
- &.indeterminate::after {
306
+ &::before,
307
+ &::after {
225
308
  animation: none;
226
309
  }
227
310
  }
311
+
312
+ /* Forced colours, carried for an applied bar, as the toggles carry theirs.
313
+ The class also keeps the unlayered copy below, which beats every layered
314
+ rule; see there for why each part is named. */
315
+ @media (forced-colors: active) {
316
+ forced-color-adjust: none;
317
+ background-color: Canvas;
318
+ border-color: CanvasText;
319
+
320
+ &::after {
321
+ background-color: CanvasText;
322
+ }
323
+
324
+ &::before {
325
+ display: none;
326
+ }
327
+ }
328
+ }
329
+
330
+ /* Forced colours repaint every background as Canvas, the value fill on a
331
+ Canvas track included, so a half-full bar read as an empty one. The bar opts
332
+ out, as the toggles do in `form.css`, and names system colours for every
333
+ part: the value in CanvasText, the track in Canvas, and a CanvasText
334
+ boundary at the track's own width, because a `.soft.edgeless` track has no
335
+ visible line and the extent of the bar is half of what it says. The shimmer
336
+ is decoration in the intent colour, so it goes. */
337
+ @media (forced-colors: active) {
338
+ .progress {
339
+ forced-color-adjust: none;
340
+ background-color: Canvas;
341
+ border-color: CanvasText;
342
+
343
+ &::after {
344
+ background-color: CanvasText;
345
+ }
346
+
347
+ &::before {
348
+ display: none;
349
+ }
350
+ }
228
351
  }
@@ -0,0 +1,11 @@
1
+ @reference "tailwindcss";
2
+ @import "../theme.css";
3
+ @reference "../box.css";
4
+
5
+ /* The focused entry: what the rules in `form.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 "./form.rules.css";