@ropav/styles 0.3.0 → 0.4.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 (62) hide show
  1. package/README.md +155 -19
  2. package/dist/components/accordion.css +7 -0
  3. package/dist/components/alert.css +8 -0
  4. package/dist/components/autocomplete.css +8 -0
  5. package/dist/components/avatar.css +9 -0
  6. package/dist/components/badge.css +7 -0
  7. package/dist/components/button.css +27 -0
  8. package/dist/components/calendar-year-picker.css +1 -0
  9. package/dist/components/calendar.css +8 -0
  10. package/dist/components/card.css +21 -0
  11. package/dist/components/chip.css +21 -0
  12. package/dist/components/close-button.css +8 -0
  13. package/dist/components/color-input-group.css +9 -0
  14. package/dist/components/color-swatch-picker.css +11 -0
  15. package/dist/components/date-input-group.css +9 -0
  16. package/dist/components/dropdown/dropdown.styles.d.ts.map +1 -1
  17. package/dist/components/dropdown/dropdown.styles.js +1 -1
  18. package/dist/components/dropdown.css +16 -11
  19. package/dist/components/input-group.css +9 -0
  20. package/dist/components/input.css +10 -0
  21. package/dist/components/menu.css +6 -0
  22. package/dist/components/meter.css +12 -0
  23. package/dist/components/number-field.css +8 -0
  24. package/dist/components/pagination.css +4 -0
  25. package/dist/components/progress-bar.css +12 -0
  26. package/dist/components/progress-circle.css +12 -0
  27. package/dist/components/radio.css +7 -0
  28. package/dist/components/range-calendar.css +28 -0
  29. package/dist/components/search-field.css +8 -0
  30. package/dist/components/select.css +9 -0
  31. package/dist/components/separator.css +18 -1
  32. package/dist/components/skeleton.css +9 -0
  33. package/dist/components/slider.css +61 -0
  34. package/dist/components/surface.css +24 -0
  35. package/dist/components/switch.css +30 -0
  36. package/dist/components/table.css +8 -0
  37. package/dist/components/tabs.css +28 -0
  38. package/dist/components/tag.css +10 -0
  39. package/dist/components/textarea.css +10 -0
  40. package/dist/components/toggle-button.css +10 -0
  41. package/dist/components/toolbar.css +7 -0
  42. package/dist/components/typography.css +21 -0
  43. package/dist/index.css +7 -2
  44. package/dist/ropav.min.css +1 -1
  45. package/dist/themes/airbnb.css +226 -0
  46. package/dist/themes/all.css +16 -0
  47. package/dist/themes/coinbase.css +226 -0
  48. package/dist/themes/{default/variables.css → default.css} +12 -5
  49. package/dist/themes/discord.css +226 -0
  50. package/dist/themes/lavender.css +226 -0
  51. package/dist/themes/mint.css +226 -0
  52. package/dist/themes/netflix.css +226 -0
  53. package/dist/themes/rabbit.css +226 -0
  54. package/dist/themes/sky.css +226 -0
  55. package/dist/themes/spotify.css +226 -0
  56. package/dist/themes/uber.css +226 -0
  57. package/dist/utilities/index.css +70 -0
  58. package/dist/variants/index.css +5 -15
  59. package/package.json +2 -3
  60. package/src/components/dropdown/dropdown.styles.ts +3 -1
  61. package/dist/themes/default/components/index.css +0 -6
  62. package/dist/themes/default/index.css +0 -8
@@ -18,6 +18,14 @@
18
18
  @apply grid h-9 items-center overflow-hidden rounded-field border bg-field text-sm text-field-foreground shadow-field outline-none;
19
19
  border-width: var(--border-width-field);
20
20
  border-color: var(--field-border);
21
+
22
+ /* Forced Colors Mode flattens the fill onto the page and strips the shadow, leaving a control
23
+ * with nothing to say it is one. `ButtonBorder` is the keyword the platform uses for a
24
+ * control's edge. */
25
+ @media (forced-colors: active) {
26
+ outline: 1px solid ButtonBorder;
27
+ outline-offset: -1px;
28
+ }
21
29
  grid-template-columns: 1fr;
22
30
 
23
31
  &:has([slot="decrement"]) {
@@ -79,6 +79,10 @@
79
79
  --pagination-link-bg: var(--default);
80
80
  --pagination-link-bg-hover: var(--default-hover);
81
81
  --pagination-link-bg-pressed: var(--default-hover);
82
+
83
+ /* The active page is a background and nothing else, and an inactive link is transparent -
84
+ * forced colors flattens the first onto the page and leaves the pair identical. */
85
+ @apply forced-selected;
82
86
  }
83
87
  }
84
88
 
@@ -32,6 +32,14 @@
32
32
 
33
33
  /* Default height (matches --md) */
34
34
  @apply h-2;
35
+
36
+ /* Track and fill are both plain backgrounds, so forced colors flattens the pair onto the
37
+ * page and the whole bar stops rendering. */
38
+ @media (forced-colors: active) {
39
+ background-color: Canvas;
40
+ outline: 1px solid CanvasText;
41
+ outline-offset: -1px;
42
+ }
35
43
  }
36
44
 
37
45
  .progress-bar__fill {
@@ -39,6 +47,10 @@
39
47
 
40
48
  background-color: var(--progress-bar-fill);
41
49
 
50
+ @media (forced-colors: active) {
51
+ background-color: Highlight;
52
+ }
53
+
42
54
  /**
43
55
  * Transitions
44
56
  * CRITICAL: motion-reduce must be AFTER transition for correct override specificity
@@ -17,11 +17,23 @@
17
17
 
18
18
  .progress-circle__track-circle {
19
19
  stroke: var(--progress-circle-track-stroke);
20
+
21
+ /* Forced colors leaves SVG strokes alone, so the ring renders but in the library's own
22
+ * palette rather than the user's. The track steps out of the way entirely: every keyword
23
+ * dark enough to read against `Canvas` is also close enough to `Highlight` to swallow the
24
+ * arc in one palette or the other, and the arc is the part carrying the value. */
25
+ @media (forced-colors: active) {
26
+ stroke: Canvas;
27
+ }
20
28
  }
21
29
 
22
30
  .progress-circle__fill-circle {
23
31
  stroke: var(--progress-circle-stroke);
24
32
 
33
+ @media (forced-colors: active) {
34
+ stroke: Highlight;
35
+ }
36
+
25
37
  /**
26
38
  * Transitions
27
39
  * CRITICAL: motion-reduce must be AFTER transition for correct override specificity
@@ -162,6 +162,13 @@
162
162
  .radio:has(input:checked) .radio__indicator:empty::before {
163
163
  @apply bg-accent-foreground;
164
164
  scale: 0.4286; /* 6px */
165
+
166
+ /* Forced Colors Mode paints the control and this dot the same `Canvas`, which leaves a
167
+ * checked radio looking exactly like an unchecked one - the scale change shows nothing on
168
+ * its own. */
169
+ @media (forced-colors: active) {
170
+ background-color: CanvasText;
171
+ }
165
172
  }
166
173
 
167
174
  /* Selected + Pressed state */
@@ -218,6 +218,12 @@
218
218
  &[data-today="true"] {
219
219
  .range-calendar__cell-button {
220
220
  @apply bg-accent-soft text-accent-soft-foreground;
221
+
222
+ /* An edge rather than a fill, so today still reads as itself once it is also in range */
223
+ @media (forced-colors: active) {
224
+ outline: 1px solid CanvasText;
225
+ outline-offset: -1px;
226
+ }
221
227
  }
222
228
 
223
229
  @media (hover: hover) {
@@ -233,6 +239,20 @@
233
239
  /* Range selected track (middle segment) */
234
240
  &[data-selected="true"]:not([data-outside-month="true"]) {
235
241
  @apply rounded-none bg-accent-soft;
242
+
243
+ /* The track is a background and nothing else, so Forced Colors Mode flattens it into the
244
+ * page and the range stops being visible. The day button declares its own `color`, so
245
+ * `HighlightText` has to be restated there rather than left to inherit. */
246
+ @media (forced-colors: active) {
247
+ background-color: Highlight;
248
+
249
+ .range-calendar__cell-button {
250
+ /* Suppresses the `Canvas` backplate behind the day number, which would otherwise cover
251
+ * the track and leave the digits invisible against it. */
252
+ forced-color-adjust: none;
253
+ color: HighlightText;
254
+ }
255
+ }
236
256
  }
237
257
 
238
258
  /* Round row boundaries so the range track looks continuous across weeks */
@@ -257,6 +277,14 @@
257
277
 
258
278
  .range-calendar__cell-button {
259
279
  @apply bg-accent text-accent-foreground;
280
+
281
+ /* Caps read the same as the track here. Forced colors offers one selection colour, and
282
+ * spending it on the shape that matters - the range - beats splitting it. */
283
+ @media (forced-colors: active) {
284
+ forced-color-adjust: none;
285
+ background-color: Highlight;
286
+ color: HighlightText;
287
+ }
260
288
  }
261
289
  }
262
290
 
@@ -25,6 +25,14 @@
25
25
  border-width: var(--border-width-field);
26
26
  border-color: var(--field-border);
27
27
 
28
+ /* Forced Colors Mode flattens the fill onto the page and strips the shadow, leaving a control
29
+ * with nothing to say it is one. `ButtonBorder` is the keyword the platform uses for a
30
+ * control's edge. */
31
+ @media (forced-colors: active) {
32
+ outline: 1px solid ButtonBorder;
33
+ outline-offset: -1px;
34
+ }
35
+
28
36
  /**
29
37
  * Transitions
30
38
  * CRITICAL: motion-reduce must be AFTER transition for correct override specificity
@@ -17,6 +17,15 @@
17
17
  .select__trigger {
18
18
  @apply relative isolate inline-flex min-h-9 rounded-field border bg-field px-3 py-2 text-sm text-field-foreground shadow-field outline-none select-none no-highlight;
19
19
 
20
+ /* Forced Colors Mode flattens the fill onto the page and strips the shadow, leaving a field that
21
+ * reads as bare text - `--border-width-field` is 0, so the declared border brings nothing back on
22
+ * its own. An outline rather than a width so nothing shifts, and the focus ring replaces it while
23
+ * focused, which is the right order of importance. */
24
+ @media (forced-colors: active) {
25
+ outline: 1px solid ButtonBorder;
26
+ outline-offset: -1px;
27
+ }
28
+
20
29
  /**
21
30
  * Transitions
22
31
  * CRITICAL: motion-reduce must be AFTER transition for correct override specificity
@@ -17,17 +17,34 @@
17
17
  @apply h-auto min-h-2 w-px self-stretch;
18
18
  }
19
19
 
20
- /* Color variants */
20
+ /* Color variants
21
+ *
22
+ * The whole separator *is* its background, so Forced Colors Mode flattening it onto the page
23
+ * removes the rule entirely rather than weakening it. Each variant restates the fallback because
24
+ * each one sets `background-color` after the base rule and would otherwise win. All three land on
25
+ * the same ink - the mode has no second tier to give them. */
21
26
  .separator--default {
22
27
  @apply bg-separator;
28
+
29
+ @media (forced-colors: active) {
30
+ background-color: CanvasText;
31
+ }
23
32
  }
24
33
 
25
34
  .separator--secondary {
26
35
  @apply bg-separator-secondary;
36
+
37
+ @media (forced-colors: active) {
38
+ background-color: CanvasText;
39
+ }
27
40
  }
28
41
 
29
42
  .separator--tertiary {
30
43
  @apply bg-separator-tertiary;
44
+
45
+ @media (forced-colors: active) {
46
+ background-color: CanvasText;
47
+ }
31
48
  }
32
49
 
33
50
  .separator__container {
@@ -1,5 +1,14 @@
1
1
  .skeleton {
2
2
  @apply pointer-events-none relative overflow-hidden rounded-sm bg-surface-tertiary/70;
3
+
4
+ /* Forced Colors Mode flattens the fill to `Canvas` - the same colour as the page behind it -
5
+ * and drops the shimmer gradient outright, so the placeholder disappears entirely. An inset
6
+ * outline is what is left to say something is loading here, and unlike a border it costs no
7
+ * layout. */
8
+ @media (forced-colors: active) {
9
+ outline: 1px solid GrayText;
10
+ outline-offset: -1px;
11
+ }
3
12
  }
4
13
 
5
14
  /* TODO: Skeleton with parent are not visible */
@@ -24,16 +24,37 @@
24
24
  @apply relative rounded-xl bg-default;
25
25
 
26
26
  grid-area: track;
27
+
28
+ /* Forced Colors Mode flattens the bar to `Canvas`, the colour of the page behind it, so
29
+ * the track stops existing - hence the edge. The spacer borders are dealt with per
30
+ * orientation below, which is where they are set. */
31
+ @media (forced-colors: active) {
32
+ background-color: Canvas;
33
+ outline: 1px solid CanvasText;
34
+ outline-offset: -1px;
35
+ }
27
36
  }
28
37
 
29
38
  .slider__fill {
30
39
  @apply pointer-events-none absolute bg-accent;
40
+
41
+ /* The only thing showing how far along the value is */
42
+ @media (forced-colors: active) {
43
+ background-color: Highlight;
44
+ }
31
45
  }
32
46
 
33
47
  .slider__thumb {
34
48
  /* Flex container for centering pseudo-elements */
35
49
  @apply absolute flex cursor-grab items-center justify-center rounded-xl bg-accent no-highlight;
36
50
 
51
+ /* The wrapper is the fill colour in ordinary use, which hides it against the fill. Flattened
52
+ * to `Canvas` it would punch a hole through the fill instead, so it steps back and lets
53
+ * `::after` - the knob you actually see - carry the control. */
54
+ @media (forced-colors: active) {
55
+ background-color: transparent;
56
+ }
57
+
37
58
  /**
38
59
  * Transitions
39
60
  * CRITICAL: motion-reduce must be AFTER transition for correct override specificity
@@ -49,6 +70,15 @@
49
70
  @apply relative z-10 rounded-lg bg-accent-foreground text-black shadow-field;
50
71
  content: "";
51
72
 
73
+ /* Both the knob's fill and the shadow that separates it from the track are gone under
74
+ * forced colors. `Canvas` inside a `CanvasText` ring reads against the bare track on one
75
+ * side and the `Highlight` fill on the other, which no single flat colour manages. */
76
+ @media (forced-colors: active) {
77
+ background-color: Canvas;
78
+ outline: 2px solid CanvasText;
79
+ outline-offset: -2px;
80
+ }
81
+
52
82
  /**
53
83
  * Transitions
54
84
  */
@@ -96,12 +126,29 @@
96
126
  /* Thumb width is 1.5rem (24px), so half is 0.75rem (12px) */
97
127
  @apply border-x-[0.75rem] border-x-transparent;
98
128
 
129
+ /* A transparent border-color is one of the few things forced colors turns *opaque*, so
130
+ * without this the two spacers render as solid blocks at either end of the track. */
131
+ @media (forced-colors: active) {
132
+ border-inline-start-color: Canvas;
133
+ border-inline-end-color: Canvas;
134
+ }
135
+
99
136
  &[data-fill-start="true"] {
100
137
  @apply border-s-accent;
138
+
139
+ /* Carries the fill past the spacer to the end of the bar, so it has to be the fill
140
+ * colour rather than the `Canvas` the base rule neutralises the spacers with. */
141
+ @media (forced-colors: active) {
142
+ border-inline-start-color: Highlight;
143
+ }
101
144
  }
102
145
 
103
146
  &[data-fill-end="true"] {
104
147
  @apply border-e-accent;
148
+
149
+ @media (forced-colors: active) {
150
+ border-inline-end-color: Highlight;
151
+ }
105
152
  }
106
153
  }
107
154
 
@@ -150,12 +197,26 @@
150
197
  @apply border-y-[0.75rem] border-y-transparent;
151
198
  justify-self: center;
152
199
 
200
+ /* Same as the horizontal spacers - opaque without this */
201
+ @media (forced-colors: active) {
202
+ border-top-color: Canvas;
203
+ border-bottom-color: Canvas;
204
+ }
205
+
153
206
  &[data-fill-start="true"] {
154
207
  @apply border-b-accent;
208
+
209
+ @media (forced-colors: active) {
210
+ border-bottom-color: Highlight;
211
+ }
155
212
  }
156
213
 
157
214
  &[data-fill-end="true"] {
158
215
  @apply border-t-accent;
216
+
217
+ @media (forced-colors: active) {
218
+ border-top-color: Highlight;
219
+ }
159
220
  }
160
221
  }
161
222
 
@@ -12,12 +12,36 @@
12
12
 
13
13
  .surface--default {
14
14
  @apply bg-surface text-surface-foreground;
15
+
16
+ /* Forced Colors Mode flattens the fill onto the page and strips the shadow, so the container
17
+ * stops having any shape - its contents just sit loose on the page. An outline rather than a
18
+ * border so nothing shifts, and it follows the radius the component sets. */
19
+ @media (forced-colors: active) {
20
+ outline: 1px solid CanvasText;
21
+ outline-offset: -1px;
22
+ }
15
23
  }
16
24
 
17
25
  .surface--secondary {
18
26
  @apply bg-surface-secondary text-surface-secondary-foreground;
27
+
28
+ /* Forced Colors Mode flattens the fill onto the page and strips the shadow, so the container
29
+ * stops having any shape - its contents just sit loose on the page. An outline rather than a
30
+ * border so nothing shifts, and it follows the radius the component sets. */
31
+ @media (forced-colors: active) {
32
+ outline: 1px solid CanvasText;
33
+ outline-offset: -1px;
34
+ }
19
35
  }
20
36
 
21
37
  .surface--tertiary {
22
38
  @apply bg-surface-tertiary text-surface-tertiary-foreground;
39
+
40
+ /* Forced Colors Mode flattens the fill onto the page and strips the shadow, so the container
41
+ * stops having any shape - its contents just sit loose on the page. An outline rather than a
42
+ * border so nothing shifts, and it follows the radius the component sets. */
43
+ @media (forced-colors: active) {
44
+ outline: 1px solid CanvasText;
45
+ outline-offset: -1px;
46
+ }
23
47
  }
@@ -23,6 +23,10 @@
23
23
 
24
24
  .switch__thumb {
25
25
  @apply bg-default-foreground/20;
26
+
27
+ @media (forced-colors: active) {
28
+ background-color: GrayText;
29
+ }
26
30
  }
27
31
  }
28
32
 
@@ -95,6 +99,15 @@
95
99
  box-shadow 150ms var(--ease-out);
96
100
  @apply motion-reduce:transition-none;
97
101
 
102
+ /* Forced Colors Mode paints the track and the thumb the same `Canvas`, so the thumb vanishes
103
+ * inside the track and the switch stops showing its state - the position it moved to is not
104
+ * visible on a shape that has no visible edge. Give the track a border, then pair each track
105
+ * colour with the thumb colour the platform means to sit on it. */
106
+ @media (forced-colors: active) {
107
+ background-color: Canvas;
108
+ border: 1px solid ButtonBorder;
109
+ }
110
+
98
111
  /* Focus states */
99
112
  .switch:focus-visible &,
100
113
  .switch:has([data-slot="switch-content"][data-focus-visible="true"]) &,
@@ -121,6 +134,11 @@
121
134
  .switch[aria-checked="true"] &,
122
135
  .switch[data-selected="true"] & {
123
136
  background-color: var(--switch-control-bg-checked);
137
+
138
+ @media (forced-colors: active) {
139
+ background-color: Highlight;
140
+ border-color: Highlight;
141
+ }
124
142
  }
125
143
 
126
144
  /* Checked + Hover states */
@@ -170,6 +188,13 @@
170
188
  height: 1rem; /* ~14px on desktop, ~16px on mobile */
171
189
  width: 1.375rem; /* ~19px on desktop, ~22px on mobile */
172
190
 
191
+ /* Reads against the `Canvas` track the control falls back to; the `color` carries any icon
192
+ * slotted inside, which would otherwise be `CanvasText` on `CanvasText`. */
193
+ @media (forced-colors: active) {
194
+ background-color: CanvasText;
195
+ color: Canvas;
196
+ }
197
+
173
198
  /**
174
199
  * Transitions
175
200
  * CRITICAL: motion-reduce must be AFTER transition for correct override specificity
@@ -187,6 +212,11 @@
187
212
  0px 0px 5px 0px rgb(0 0 0 / 0.02),
188
213
  0px 2px 10px 0px rgb(0 0 0 / 0.06),
189
214
  0px 0px 1px 0px rgb(0 0 0 / 0.3);
215
+
216
+ @media (forced-colors: active) {
217
+ background-color: HighlightText;
218
+ color: Highlight;
219
+ }
190
220
  }
191
221
 
192
222
  .switch:active &,
@@ -25,6 +25,13 @@
25
25
  .table-root--primary {
26
26
  @apply bg-surface-secondary px-1 pb-1;
27
27
  border-radius: min(32px, calc(var(--radius) * 2.5));
28
+
29
+ /* Forced Colors Mode flattens the fill onto the page and strips the shadow, so the container
30
+ * stops having any shape and its contents sit loose on the page. */
31
+ @media (forced-colors: active) {
32
+ outline: 1px solid CanvasText;
33
+ outline-offset: -1px;
34
+ }
28
35
  }
29
36
 
30
37
  .table-root--secondary {
@@ -232,6 +239,7 @@
232
239
  /* Selected state */
233
240
  &[data-selected="true"] .table__cell {
234
241
  @apply bg-surface/10;
242
+ @apply forced-selected;
235
243
  }
236
244
 
237
245
  /* Disabled state */
@@ -141,6 +141,14 @@
141
141
  &[data-selected="true"] {
142
142
  @apply text-segment-foreground;
143
143
 
144
+ /* Sits on top of the indicator, which forced colors paints `Highlight`. `forced-color-adjust`
145
+ * suppresses the `Canvas` backplate Chromium would otherwise paint behind this label - it
146
+ * covers the indicator, and black-on-black is exactly what it would leave. */
147
+ @media (forced-colors: active) {
148
+ forced-color-adjust: none;
149
+ color: HighlightText;
150
+ }
151
+
144
152
  /* Hide separator on the selected tab and the one following it */
145
153
  .tabs__separator,
146
154
  & + .tabs__tab .tabs__separator {
@@ -217,6 +225,13 @@
217
225
 
218
226
  z-index: -1;
219
227
 
228
+ /* The indicator is a background and nothing else, so Forced Colors Mode flattens it away
229
+ * and nothing marks the selected tab. `Highlight` behind `HighlightText` is the platform's
230
+ * own pairing for a selected segment. */
231
+ @media (forced-colors: active) {
232
+ background-color: Highlight;
233
+ }
234
+
220
235
  /**
221
236
  * Transitions
222
237
  * CRITICAL: motion-reduce must be AFTER transition for correct override specificity
@@ -254,12 +269,25 @@
254
269
  /* Selected state - use text-foreground */
255
270
  &[data-selected="true"] {
256
271
  @apply text-foreground;
272
+
273
+ /* Here the indicator is a line under the tab rather than a pill behind it, so the
274
+ * label keeps the ordinary text colour instead of the base variant's
275
+ * `HighlightText`, which would be invisible against the page. */
276
+ @media (forced-colors: active) {
277
+ color: CanvasText;
278
+ }
257
279
  }
258
280
  }
259
281
 
260
282
  /* Tab indicator - line style */
261
283
  .tabs__indicator {
262
284
  @apply rounded-none bg-accent shadow-none;
285
+
286
+ /* `bg-accent` outranks the base rule's forced-colors fallback and is itself flattened
287
+ * to `Canvas`, so the underline has to be restated here or it disappears. */
288
+ @media (forced-colors: active) {
289
+ background-color: Highlight;
290
+ }
263
291
  }
264
292
  }
265
293
 
@@ -19,6 +19,15 @@
19
19
  /* Cursor */
20
20
  cursor: var(--cursor-interactive);
21
21
 
22
+ /* Forced Colors Mode flattens the fill onto the page and strips the shadow, leaving a control
23
+ * that reads as bare text. `ButtonBorder` is the keyword the platform uses for a control's
24
+ * edge; an outline rather than a border so nothing shifts, and the focus ring replaces it
25
+ * while focused, which is the right order of importance. */
26
+ @media (forced-colors: active) {
27
+ outline: 1px solid ButtonBorder;
28
+ outline-offset: -1px;
29
+ }
30
+
22
31
  /* SVG icon styling */
23
32
  & svg {
24
33
  @apply pointer-events-none size-3 shrink-0 self-center text-current;
@@ -42,6 +51,7 @@
42
51
  /* Selected state */
43
52
  .tag:is([data-selected="true"], [aria-selected="true"]) {
44
53
  @apply bg-accent-soft text-accent-soft-foreground;
54
+ @apply forced-selected;
45
55
 
46
56
  /* Selected + Hover state */
47
57
  @media (hover: hover) {
@@ -7,6 +7,16 @@
7
7
  border-width: var(--border-width-field);
8
8
  border-color: var(--field-border);
9
9
 
10
+ /* Forced Colors Mode flattens the fill onto the page and strips the shadow, leaving a field that
11
+ * reads as bare text - `--border-width-field` is 0, so the declared border brings nothing
12
+ * back on its own. `ButtonBorder` is the keyword the platform uses for a control's
13
+ * edge; an outline rather than a border so nothing shifts, and the focus ring replaces it
14
+ * while focused, which is the right order of importance. */
15
+ @media (forced-colors: active) {
16
+ outline: 1px solid ButtonBorder;
17
+ outline-offset: -1px;
18
+ }
19
+
10
20
  /**
11
21
  * Transitions
12
22
  * CRITICAL: motion-reduce must be AFTER transition for correct override specificity
@@ -16,6 +16,15 @@
16
16
  box-shadow 100ms var(--ease-out);
17
17
  @apply transform-gpu motion-reduce:transition-none;
18
18
 
19
+ /* Forced Colors Mode flattens the fill onto the page and strips the shadow, leaving a control
20
+ * that reads as bare text. `ButtonBorder` is the keyword the platform uses for a control's
21
+ * edge; an outline rather than a border so nothing shifts, and the focus ring replaces it
22
+ * while focused, which is the right order of importance. */
23
+ @media (forced-colors: active) {
24
+ outline: 1px solid ButtonBorder;
25
+ outline-offset: -1px;
26
+ }
27
+
19
28
  /* Cursor */
20
29
  cursor: var(--cursor-interactive);
21
30
 
@@ -65,6 +74,7 @@
65
74
  &[data-selected="true"] {
66
75
  background-color: var(--toggle-button-bg-selected);
67
76
  color: var(--toggle-button-fg-selected);
77
+ @apply forced-selected;
68
78
 
69
79
  @media (hover: hover) {
70
80
  &:hover,
@@ -29,4 +29,11 @@
29
29
  /* Attached variant */
30
30
  .toolbar--attached {
31
31
  @apply rounded-3xl bg-surface p-1 shadow-overlay;
32
+
33
+ /* Forced Colors Mode flattens the fill onto the page and strips the shadow, so the container
34
+ * stops having any shape and its contents sit loose on the page. */
35
+ @media (forced-colors: active) {
36
+ outline: 1px solid CanvasText;
37
+ outline-offset: -1px;
38
+ }
32
39
  }
@@ -36,6 +36,13 @@
36
36
 
37
37
  .typography-prose code {
38
38
  @apply rounded-md bg-default px-1.5 py-0.5 font-mono text-sm text-foreground;
39
+
40
+ /* Code is marked out by a tinted background and nothing else, so forced colors leaves it
41
+ * indistinguishable from the prose around it - the monospace face is all that is left. */
42
+ @media (forced-colors: active) {
43
+ outline: 1px solid CanvasText;
44
+ outline-offset: -1px;
45
+ }
39
46
  }
40
47
 
41
48
  .typography-prose a {
@@ -64,6 +71,13 @@
64
71
 
65
72
  .typography-prose pre {
66
73
  @apply my-4 overflow-x-auto rounded-xl bg-default p-4 font-mono text-sm leading-relaxed;
74
+
75
+ /* Code is marked out by a tinted background and nothing else, so forced colors leaves it
76
+ * indistinguishable from the prose around it - the monospace face is all that is left. */
77
+ @media (forced-colors: active) {
78
+ outline: 1px solid CanvasText;
79
+ outline-offset: -1px;
80
+ }
67
81
  }
68
82
 
69
83
  .typography-prose strong {
@@ -116,6 +130,13 @@
116
130
 
117
131
  .typography--code {
118
132
  @apply rounded-md bg-default px-1.5 py-0.5 font-mono text-sm text-foreground;
133
+
134
+ /* Code is marked out by a tinted background and nothing else, so forced colors leaves it
135
+ * indistinguishable from the prose around it - the monospace face is all that is left. */
136
+ @media (forced-colors: active) {
137
+ outline: 1px solid CanvasText;
138
+ outline-offset: -1px;
139
+ }
119
140
  }
120
141
 
121
142
  .typography--align-start {
package/dist/index.css CHANGED
@@ -13,8 +13,13 @@
13
13
  /* Base component structures (unstyled) */
14
14
  @import "./components/index.css" layer(components);
15
15
 
16
- /* Default theme (variables + component customizations) */
17
- @import "./themes/default/index.css" layer(theme);
16
+ /* Maps the theme tokens onto Tailwind's namespaces - @see https://tailwindcss.com/docs/theme.
17
+ * Must stay unlayered: Tailwind only picks up `@theme` outside a cascade layer. */
18
+ @import "./themes/shared/theme.css";
19
+
20
+ /* Default theme. Declares its own `@layer theme`; the bundled alternatives in
21
+ * `themes/*.css` are opt-in and layer themselves the same way. */
22
+ @import "./themes/default.css";
18
23
 
19
24
  /* Utilities */
20
25
  @import "./utilities/index.css";