@redseed/redseed-ui-tailwindcss 7.56.0 → 7.57.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.
|
@@ -153,6 +153,38 @@
|
|
|
153
153
|
@apply hover:bg-background-brand-contrast;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
/*
|
|
157
|
+
* Square off the edge each endpoint faces the range on, so the band closes.
|
|
158
|
+
*
|
|
159
|
+
* The base `.rsui-form-field-date-range__day` is `rounded-lg` on all four
|
|
160
|
+
* corners and the endpoints only override the background, so an endpoint used to
|
|
161
|
+
* sit between two square in-range fills as a rounded square — leaving a white
|
|
162
|
+
* notch at each corner where its curve pulled away from its neighbour. The band
|
|
163
|
+
* read as interrupted by the endpoint rather than containing it.
|
|
164
|
+
*
|
|
165
|
+
* MUI solves the same problem by drawing a continuous highlight band behind the
|
|
166
|
+
* cells and putting the endpoint circle on top of it. That needs a second
|
|
167
|
+
* element per cell; squaring the inner edge of the endpoint's own fill gets the
|
|
168
|
+
* same join out of the existing one.
|
|
169
|
+
*/
|
|
170
|
+
.rsui-form-field-date-range__day--range-start {
|
|
171
|
+
@apply rounded-r-none;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.rsui-form-field-date-range__day--range-end {
|
|
175
|
+
@apply rounded-l-none;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/*
|
|
179
|
+
* Both modifiers land on the same day for a single-day window — `today`, or a
|
|
180
|
+
* range picked start-and-end on one date. There is no band to join, so restore
|
|
181
|
+
* the full radius. This selector is deliberately last and doubly-classed so it
|
|
182
|
+
* outranks the two above.
|
|
183
|
+
*/
|
|
184
|
+
.rsui-form-field-date-range__day--range-start.rsui-form-field-date-range__day--range-end {
|
|
185
|
+
@apply rounded-lg;
|
|
186
|
+
}
|
|
187
|
+
|
|
156
188
|
/*
|
|
157
189
|
* The day the presets resolve against — "today" as the server sees it.
|
|
158
190
|
*
|
package/components/icon.css
CHANGED
|
@@ -88,42 +88,52 @@
|
|
|
88
88
|
|
|
89
89
|
.rsui-icon--primary {
|
|
90
90
|
@apply *:text-text-primary;
|
|
91
|
+
--rsui-icon-pulse: var(--color-text-primary);
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
.rsui-icon--secondary {
|
|
94
95
|
@apply *:text-text-secondary;
|
|
96
|
+
--rsui-icon-pulse: var(--color-text-secondary);
|
|
95
97
|
}
|
|
96
98
|
|
|
97
99
|
.rsui-icon--brand {
|
|
98
100
|
@apply *:text-text-brand;
|
|
101
|
+
--rsui-icon-pulse: var(--color-text-brand);
|
|
99
102
|
}
|
|
100
103
|
|
|
101
104
|
.rsui-icon--success {
|
|
102
105
|
@apply *:text-text-success;
|
|
106
|
+
--rsui-icon-pulse: var(--color-text-success);
|
|
103
107
|
}
|
|
104
108
|
|
|
105
109
|
.rsui-icon--info {
|
|
106
110
|
@apply *:text-text-info;
|
|
111
|
+
--rsui-icon-pulse: var(--color-text-info);
|
|
107
112
|
}
|
|
108
113
|
|
|
109
114
|
.rsui-icon--warning {
|
|
110
115
|
@apply *:text-text-warning;
|
|
116
|
+
--rsui-icon-pulse: var(--color-text-warning);
|
|
111
117
|
}
|
|
112
118
|
|
|
113
119
|
.rsui-icon--error {
|
|
114
120
|
@apply *:text-text-error;
|
|
121
|
+
--rsui-icon-pulse: var(--color-text-error);
|
|
115
122
|
}
|
|
116
123
|
|
|
117
124
|
.rsui-icon--ai {
|
|
118
125
|
@apply *:text-text-ai;
|
|
126
|
+
--rsui-icon-pulse: var(--color-text-ai);
|
|
119
127
|
}
|
|
120
128
|
|
|
121
129
|
.rsui-icon--disabled {
|
|
122
130
|
@apply *:text-text-disabled;
|
|
131
|
+
--rsui-icon-pulse: var(--color-text-disabled);
|
|
123
132
|
}
|
|
124
133
|
|
|
125
134
|
.rsui-icon--classic {
|
|
126
135
|
@apply *:text-pink-500;
|
|
136
|
+
--rsui-icon-pulse: var(--color-pink-500);
|
|
127
137
|
}
|
|
128
138
|
|
|
129
139
|
.rsui-icon--primary.rsui-icon--invert {
|
|
@@ -216,52 +226,145 @@
|
|
|
216
226
|
@apply *:text-text-on-color;
|
|
217
227
|
}
|
|
218
228
|
|
|
229
|
+
/*
|
|
230
|
+
* Both flags together is the one combination that puts the glyph back on its
|
|
231
|
+
* variant colour, against a tinted fill — so each of these restates the pulse
|
|
232
|
+
* token alongside it, undoing the on-colour default set below. Kept here rather
|
|
233
|
+
* than in a block of its own so the ring colour is edited in the same place as
|
|
234
|
+
* the glyph colour it has to match.
|
|
235
|
+
*/
|
|
219
236
|
.rsui-icon--primary.rsui-icon--background.rsui-icon--invert {
|
|
220
237
|
@apply bg-background-primary-invert;
|
|
221
238
|
@apply *:text-text-primary;
|
|
239
|
+
--rsui-icon-pulse: var(--color-text-primary);
|
|
222
240
|
}
|
|
223
241
|
|
|
224
242
|
.rsui-icon--secondary.rsui-icon--background.rsui-icon--invert {
|
|
225
243
|
@apply bg-background-secondary-invert;
|
|
226
244
|
@apply *:text-text-secondary;
|
|
245
|
+
--rsui-icon-pulse: var(--color-text-secondary);
|
|
227
246
|
}
|
|
228
247
|
|
|
229
248
|
.rsui-icon--brand.rsui-icon--background.rsui-icon--invert {
|
|
230
249
|
@apply bg-background-brand-invert;
|
|
231
250
|
@apply *:text-text-brand;
|
|
251
|
+
--rsui-icon-pulse: var(--color-text-brand);
|
|
232
252
|
}
|
|
233
253
|
|
|
234
254
|
.rsui-icon--success.rsui-icon--background.rsui-icon--invert {
|
|
235
255
|
@apply bg-background-success-invert;
|
|
236
256
|
@apply *:text-text-success;
|
|
257
|
+
--rsui-icon-pulse: var(--color-text-success);
|
|
237
258
|
}
|
|
238
259
|
|
|
239
260
|
.rsui-icon--info.rsui-icon--background.rsui-icon--invert {
|
|
240
261
|
@apply bg-background-info-invert;
|
|
241
262
|
@apply *:text-text-info;
|
|
263
|
+
--rsui-icon-pulse: var(--color-text-info);
|
|
242
264
|
}
|
|
243
265
|
|
|
244
266
|
.rsui-icon--warning.rsui-icon--background.rsui-icon--invert {
|
|
245
267
|
@apply bg-background-warning-invert;
|
|
246
268
|
@apply *:text-text-warning;
|
|
269
|
+
--rsui-icon-pulse: var(--color-text-warning);
|
|
247
270
|
}
|
|
248
271
|
|
|
249
272
|
.rsui-icon--error.rsui-icon--background.rsui-icon--invert {
|
|
250
273
|
@apply bg-background-error-invert;
|
|
251
274
|
@apply *:text-text-error;
|
|
275
|
+
--rsui-icon-pulse: var(--color-text-error);
|
|
252
276
|
}
|
|
253
277
|
|
|
254
278
|
.rsui-icon--ai.rsui-icon--background.rsui-icon--invert {
|
|
255
279
|
@apply bg-background-ai-invert;
|
|
256
280
|
@apply *:text-text-ai;
|
|
281
|
+
--rsui-icon-pulse: var(--color-text-ai);
|
|
257
282
|
}
|
|
258
283
|
|
|
259
284
|
.rsui-icon--disabled.rsui-icon--background.rsui-icon--invert {
|
|
260
285
|
@apply bg-background-disabled-invert;
|
|
261
286
|
@apply *:text-text-disabled;
|
|
287
|
+
--rsui-icon-pulse: var(--color-text-disabled);
|
|
262
288
|
}
|
|
263
289
|
|
|
264
290
|
.rsui-icon--classic.rsui-icon--background.rsui-icon--invert {
|
|
265
291
|
@apply bg-pink-50;
|
|
266
292
|
@apply *:text-pink-500;
|
|
267
|
-
|
|
293
|
+
--rsui-icon-pulse: var(--color-pink-500);
|
|
294
|
+
}
|
|
295
|
+
/*
|
|
296
|
+
* On a filled or inverted icon the glyph is repainted to an on-colour token, so
|
|
297
|
+
* the ring follows it rather than staying on a variant colour it no longer
|
|
298
|
+
* matches. Same specificity as the base variant rules and declared after them,
|
|
299
|
+
* so it wins on source order — but it loses to the two-flag rules above, which
|
|
300
|
+
* is the point: those put the glyph back on its variant colour and take the ring
|
|
301
|
+
* with them.
|
|
302
|
+
*/
|
|
303
|
+
.rsui-icon--background,
|
|
304
|
+
.rsui-icon--invert {
|
|
305
|
+
--rsui-icon-pulse: var(--color-text-on-color);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/*
|
|
309
|
+
* Optional attention pulse: a ring expands out from the icon and fades, a few
|
|
310
|
+
* times, when the icon arrives.
|
|
311
|
+
*
|
|
312
|
+
* Deliberately a fixed number of iterations rather than `infinite`. An animation
|
|
313
|
+
* that repeats for more than five seconds needs a pause control (WCAG 2.2.2), and
|
|
314
|
+
* an icon has nowhere sensible to put one — three bursts draw the eye on arrival
|
|
315
|
+
* without taking on that obligation.
|
|
316
|
+
*
|
|
317
|
+
* Duration x iterations is the number that matters there, and it must stay under
|
|
318
|
+
* five seconds: 1.4s x 3 leaves 800ms of headroom. Slowing the ring to 1.8s to
|
|
319
|
+
* soften it further would put the total at 5.4s and trip the criterion, so the
|
|
320
|
+
* softening is done with opacity, border width and travel instead.
|
|
321
|
+
*
|
|
322
|
+
* The ring takes its colour from `--rsui-icon-pulse`, which each colour variant
|
|
323
|
+
* sets alongside its glyph rule.
|
|
324
|
+
*
|
|
325
|
+
* A custom property rather than `currentColor`: the variants colour the SVG child
|
|
326
|
+
* (`*:text-…`) and not the wrapper, so an inherited ring comes out near-black.
|
|
327
|
+
* Setting `color` on the wrapper instead would fix the base variants but silently
|
|
328
|
+
* miss `--invert` and `--background`, which repaint the glyph and would leave the
|
|
329
|
+
* ring on the old colour. A property the combination rules can override keeps the
|
|
330
|
+
* answer in one place — and, unlike a wrapper colour, it is inert for everything
|
|
331
|
+
* that does not read it.
|
|
332
|
+
*
|
|
333
|
+
* `pointer-events-none` because the ring overflows the icon's box and would
|
|
334
|
+
* otherwise sit over whatever is beside it.
|
|
335
|
+
*/
|
|
336
|
+
.rsui-icon--pulse {
|
|
337
|
+
@apply relative;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.rsui-icon--pulse::after {
|
|
341
|
+
@apply content-[''] absolute inset-0 rounded-full pointer-events-none;
|
|
342
|
+
border: 1px solid var(--rsui-icon-pulse, currentColor);
|
|
343
|
+
/* Invisible at rest, in both directions. Without this the ring reverts to its
|
|
344
|
+
un-animated state once the iterations finish — full opacity at scale 1 —
|
|
345
|
+
and a solid ring stays welded around the icon for the life of the page. */
|
|
346
|
+
@apply opacity-0;
|
|
347
|
+
animation: rsui-icon-pulse 1.4s ease-out 3;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
@keyframes rsui-icon-pulse {
|
|
351
|
+
0% {
|
|
352
|
+
opacity: 0.35;
|
|
353
|
+
transform: scale(1);
|
|
354
|
+
}
|
|
355
|
+
100% {
|
|
356
|
+
opacity: 0;
|
|
357
|
+
transform: scale(1.8);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/*
|
|
362
|
+
* The pulse is decoration on top of an icon that is already visible, so removing
|
|
363
|
+
* it costs nothing — no state is conveyed by the animation alone.
|
|
364
|
+
*/
|
|
365
|
+
@media (prefers-reduced-motion: reduce) {
|
|
366
|
+
.rsui-icon--pulse::after {
|
|
367
|
+
animation: none;
|
|
368
|
+
display: none;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
@@ -1,5 +1,46 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* A row: optional icon, the text column, optional close. `items-start` so the
|
|
3
|
+
* icon and the close button align with the first line rather than centring
|
|
4
|
+
* against a tall body.
|
|
5
|
+
*/
|
|
1
6
|
.rsui-message-box {
|
|
2
|
-
@apply border rounded-lg overflow-hidden p-4 flex
|
|
7
|
+
@apply border rounded-lg overflow-hidden p-4 flex items-start gap-x-3;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
* The text column. Carries the vertical rhythm the box itself used to own, so a
|
|
12
|
+
* box with no icon and no actions lays out exactly as it did before.
|
|
13
|
+
*/
|
|
14
|
+
.rsui-message-box__main {
|
|
15
|
+
@apply flex-1 flex flex-col gap-y-3;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/*
|
|
19
|
+
* Narrow and with an icon: the icon and the close button take a row of their own
|
|
20
|
+
* and the text runs full width beneath them, rather than indenting past a column
|
|
21
|
+
* that costs more than it gives at this width.
|
|
22
|
+
*
|
|
23
|
+
* Ordering is what makes it work — the DOM runs icon, text, close, so without it
|
|
24
|
+
* the full-width text column would wrap between the two and strand the close
|
|
25
|
+
* button on a third row. Scoped to `--with-icon` because a box with no icon has
|
|
26
|
+
* no column to escape: its close button already sits beside the title, and a
|
|
27
|
+
* lone X on its own row would be a wasted line.
|
|
28
|
+
*
|
|
29
|
+
* The box itself sets only `gap-x`, which buys nothing once these become two
|
|
30
|
+
* flex lines — so the row gap is set here, matching the text column's own
|
|
31
|
+
* rhythm. Inert at `sm:flex-nowrap`, where there is only ever one line.
|
|
32
|
+
*/
|
|
33
|
+
.rsui-message-box--with-icon {
|
|
34
|
+
@apply flex-wrap gap-y-3 sm:flex-nowrap;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.rsui-message-box--with-icon .rsui-message-box__close {
|
|
38
|
+
@apply order-2 ms-auto sm:order-3 sm:ms-0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* `sm:basis-0` rather than `basis-auto`, to restore exactly what `flex-1` sets. */
|
|
42
|
+
.rsui-message-box--with-icon .rsui-message-box__main {
|
|
43
|
+
@apply order-3 basis-full sm:order-2 sm:basis-0;
|
|
3
44
|
}
|
|
4
45
|
|
|
5
46
|
.rsui-message-box--default {
|
|
@@ -37,17 +78,76 @@
|
|
|
37
78
|
}
|
|
38
79
|
|
|
39
80
|
.rsui-message-box__head {
|
|
40
|
-
@apply flex
|
|
81
|
+
@apply flex items-center gap-x-2;
|
|
41
82
|
}
|
|
42
83
|
|
|
43
84
|
.rsui-message-box__content {
|
|
44
|
-
@apply flex
|
|
85
|
+
@apply flex items-start gap-x-2;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/*
|
|
89
|
+
* `items-start` so the icon aligns with the first line of the message rather
|
|
90
|
+
* than centring against a multi-line body. `shrink-0` keeps it from being
|
|
91
|
+
* squeezed by long text.
|
|
92
|
+
*/
|
|
93
|
+
.rsui-message-box__icon {
|
|
94
|
+
@apply shrink-0 flex items-center h-6;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/*
|
|
98
|
+
* Actions sit under the message, indented to the body rather than the icon, so
|
|
99
|
+
* they read as belonging to the text.
|
|
100
|
+
*
|
|
101
|
+
* Neutral, not variant-coloured: a coloured action on a coloured tint is a
|
|
102
|
+
* contrast problem, and the action is a way out of the message rather than a
|
|
103
|
+
* restatement of its severity. The dismiss is quieter than whatever the consumer
|
|
104
|
+
* puts beside it, which is the only hierarchy this row needs.
|
|
105
|
+
*/
|
|
106
|
+
.rsui-message-box__actions {
|
|
107
|
+
@apply flex flex-wrap items-center gap-x-4 gap-y-2 pt-1;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.rsui-message-box__dismiss,
|
|
111
|
+
.rsui-message-box__action {
|
|
112
|
+
@apply bg-transparent border-0 p-0 cursor-pointer;
|
|
113
|
+
@apply text-sm font-semibold transition-colors;
|
|
114
|
+
@apply hover:underline underline-offset-4 decoration-2;
|
|
115
|
+
@apply outline-hidden focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-border-primary rounded-sm;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/*
|
|
119
|
+
* Two tiers, and the only hierarchy this row needs: dismiss is the quieter of
|
|
120
|
+
* the pair, whatever the consumer puts beside it is the one they want pressed.
|
|
121
|
+
*/
|
|
122
|
+
.rsui-message-box__dismiss {
|
|
123
|
+
@apply text-text-secondary hover:text-text-primary;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.rsui-message-box__action {
|
|
127
|
+
@apply text-text-primary;
|
|
45
128
|
}
|
|
46
129
|
|
|
130
|
+
/*
|
|
131
|
+
* `-my-2` so the 40px hit target centres on the first 24px line rather than
|
|
132
|
+
* hanging 8px below it. Under `items-start` the button's own box starts at the
|
|
133
|
+
* padding edge, which put its glyph at y=36 against a title centred at y=28 —
|
|
134
|
+
* and, with an icon shown, two icons in the same row not lining up. The negative
|
|
135
|
+
* margin keeps the target size while claiming only 24px of layout height.
|
|
136
|
+
*/
|
|
47
137
|
.rsui-message-box__close {
|
|
48
|
-
@apply size-10;
|
|
138
|
+
@apply size-10 shrink-0 -my-2;
|
|
49
139
|
}
|
|
50
140
|
|
|
141
|
+
/*
|
|
142
|
+
* The glyph colour is set on the Icon itself (`secondary`), not here — the
|
|
143
|
+
* variant rules target the SVG child directly, so a colour on this button would
|
|
144
|
+
* lose to them.
|
|
145
|
+
*
|
|
146
|
+
* It was `disabled` before, which painted a working control in the disabled text
|
|
147
|
+
* colour — Grey-400, 2.41:1 on white — so it looked permanently greyed out and
|
|
148
|
+
* fell under the 3:1 WCAG 1.4.11 asks of a control. Grey-700 reads as quiet
|
|
149
|
+
* without reading as dead.
|
|
150
|
+
*/
|
|
51
151
|
.rsui-message-box__close-icon {
|
|
52
152
|
@apply size-10 cursor-pointer rounded-lg flex items-center justify-center hover:bg-border-secondary transition duration-200;
|
|
53
153
|
/* Reset native <button> chrome so it matches the prior inline affordance ... */
|
|
@@ -17,3 +17,71 @@
|
|
|
17
17
|
@apply flex-1;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/*
|
|
21
|
+
* The view-all sits in its own region so it can centre, rather than joining the
|
|
22
|
+
* right-aligned action row.
|
|
23
|
+
*
|
|
24
|
+
* `order-first md:order-last` keeps it trailing at both breakpoints. The footer
|
|
25
|
+
* is `flex-col-reverse` below `md`, so the last DOM child paints at the top —
|
|
26
|
+
* appending the region put the view-all *above* the actions and the content on
|
|
27
|
+
* mobile, which is the opposite of what every comment about it claims. Ordering
|
|
28
|
+
* it first puts it back at the bottom there, and `md:order-last` restores it to
|
|
29
|
+
* the end of the desktop row.
|
|
30
|
+
*/
|
|
31
|
+
.rsui-section-footer__view-all-region {
|
|
32
|
+
@apply flex items-center justify-center order-first md:order-last;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/*
|
|
36
|
+
* `flex-1` only when the view-all has the footer to itself, which is the case
|
|
37
|
+
* that wants centring. Applying it unconditionally made the region a second
|
|
38
|
+
* greedy sibling alongside `content-start`: the two split the row and squeezed
|
|
39
|
+
* the action buttons towards the middle instead of the right edge — measured at
|
|
40
|
+
* x=599 of 1265. With actions present the region stays content-sized and
|
|
41
|
+
* `justify-between` puts it at the end of the row where it belongs.
|
|
42
|
+
*/
|
|
43
|
+
.rsui-section-footer__view-all-region--full {
|
|
44
|
+
@apply flex-1 w-full;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/*
|
|
48
|
+
* The trailing "view all" link. Matches the "See all" treatment already used in
|
|
49
|
+
* section headers: `font-semibold text-sm` on the tertiary-button colour, no
|
|
50
|
+
* underline, no arrow. That pairing is what makes it read as an action without
|
|
51
|
+
* competing with the section's own type.
|
|
52
|
+
*
|
|
53
|
+
* Deliberately not LinkSlot, which sits at `font-medium`, carries an always-on
|
|
54
|
+
* underline rule, and pins a font size per variant — all three wrong for this.
|
|
55
|
+
* No `text-*` here either: the size comes from whatever encloses the footer, so
|
|
56
|
+
* the link sits a step below the section's own type wherever it lands rather than
|
|
57
|
+
* locking to one scale. The semibold is what makes it read as an action.
|
|
58
|
+
*
|
|
59
|
+
* Colour comes from `--color-button-tertiary` rather than a literal brand value,
|
|
60
|
+
* so it tracks whatever tertiary actions are doing. It is overridden per section
|
|
61
|
+
* variant below rather than hardcoded, because a brand-coloured link is
|
|
62
|
+
* invisible on a brand-coloured surface.
|
|
63
|
+
*
|
|
64
|
+
* `w-fit` so the link stays the width of its own text. Its region centres it, and
|
|
65
|
+
* an underline that ran the width of the footer would read as a rule rather than
|
|
66
|
+
* as part of the link.
|
|
67
|
+
*/
|
|
68
|
+
.rsui-section-footer__view-all {
|
|
69
|
+
@apply inline-flex w-fit items-center whitespace-nowrap;
|
|
70
|
+
@apply font-semibold no-underline cursor-pointer transition-colors;
|
|
71
|
+
@apply text-button-tertiary hover:text-button-tertiary-hover;
|
|
72
|
+
/* Underline on hover only: the resting state stays as clean as the header's
|
|
73
|
+
"See all", but the link still confirms itself as one under the pointer. */
|
|
74
|
+
@apply hover:underline underline-offset-4 decoration-2;
|
|
75
|
+
@apply focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-button-tertiary outline-hidden;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/*
|
|
79
|
+
* No dark-surface override here, deliberately. Every plain Section variant is a
|
|
80
|
+
* light tint, so the tertiary colour reads on all nine. The one dark surface in
|
|
81
|
+
* the package is the slider's `--featured` (brand-600), where a brand link would
|
|
82
|
+
* be brand on brand — but `SectionSlider` renders no footer and does not forward
|
|
83
|
+
* Section's `#footer` slot, so no consumer can put this link there. A rule for it
|
|
84
|
+
* would be unreachable, untested, and would imply a capability that does not
|
|
85
|
+
* exist. Add it alongside a footer slot on the slider, if that is ever wanted.
|
|
86
|
+
*/
|
|
87
|
+
|
|
@@ -24,12 +24,314 @@
|
|
|
24
24
|
|
|
25
25
|
.rsui-section-slider__action {
|
|
26
26
|
@apply shrink-0 inline-flex items-center justify-center size-14 rounded-full no-underline transition-all disabled:opacity-50 disabled:text-grey-400 hover:cursor-pointer disabled:hover:cursor-default border border-grey-400 bg-transparent;
|
|
27
|
+
@apply focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-border-primary;
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
/*
|
|
31
|
+
* The trailing "view all" card. The element carries `rsui-card
|
|
32
|
+
* rsui-card--bordered` plus any `rsui-card--<variant>`, so radius, background,
|
|
33
|
+
* border colour and the transition all come from Card — including every
|
|
34
|
+
* variant's border colour, which is the point of composing rather than
|
|
35
|
+
* restating. This file adds only what Card has no opinion about.
|
|
36
|
+
*
|
|
37
|
+
* The dashed edge is the one deliberate departure, and it is what marks the card
|
|
38
|
+
* as an opening rather than content. It follows form_field_uploader.css, the
|
|
39
|
+
* package's only other dashed affordance. `section_slider.css` is imported after
|
|
40
|
+
* `card.css`, so this wins on source order at equal specificity.
|
|
41
|
+
*
|
|
42
|
+
* `hover:shadow-md` rather than a background change, mirroring
|
|
43
|
+
* `rsui-card--hoverable`: it reads the same on every variant, whereas shifting
|
|
44
|
+
* the background would fight whatever colour the variant just set.
|
|
45
|
+
*
|
|
46
|
+
* No `color` here on purpose. Card does not set one either — its text comes from
|
|
47
|
+
* whichever Section encloses it — and Tailwind's preflight already gives buttons
|
|
48
|
+
* `color: inherit`, so the card picks up the section the same way a real card
|
|
49
|
+
* does. Setting it explicitly produced the same pixels today but would have
|
|
50
|
+
* ignored a section that ever chose a different text colour.
|
|
51
|
+
*
|
|
52
|
+
* Height is not declared: the rail item is a flex container that stretches, so
|
|
53
|
+
* the shell takes its height from the tallest card in the track. A fixed height
|
|
54
|
+
* would go short or overhang the moment consumer cards changed.
|
|
55
|
+
*
|
|
56
|
+
* Width is a floor rather than a fixed value. The rail item has no intrinsic
|
|
57
|
+
* width — consumer cards supply their own — so `w-full` here would resolve
|
|
58
|
+
* against content and collapse to the label. `min-w-64` matches the common card
|
|
59
|
+
* width and still grows if the consumer puts something wider inside.
|
|
60
|
+
*/
|
|
61
|
+
.rsui-section-slider__view-all {
|
|
62
|
+
@apply min-w-64 items-center justify-center gap-3 border-dashed p-space-lg text-center;
|
|
63
|
+
@apply font-semibold;
|
|
64
|
+
@apply hover:cursor-pointer hover:shadow-md;
|
|
65
|
+
@apply focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-border-primary outline-hidden;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/*
|
|
69
|
+
* The `ai` variant is the exception: Card gives it an animated gradient, which is
|
|
70
|
+
* a lot of movement for a card that is meant to be an opening rather than a
|
|
71
|
+
* destination. Cleared so the section's own background — itself the ai gradient
|
|
72
|
+
* when the slider is in that variant — shows through instead.
|
|
73
|
+
*
|
|
74
|
+
* `bg-none` and not `bg-transparent`: Card paints this one with
|
|
75
|
+
* `background-image`, which a background-colour reset leaves untouched. That is
|
|
76
|
+
* also why the other variants need no rule here — theirs are background colours,
|
|
77
|
+
* and they are meant to stay.
|
|
78
|
+
*
|
|
79
|
+
* Card has no `--bordered.--ai` companion the way the other variants do, so the
|
|
80
|
+
* border would otherwise fall back to the base grey. Brand-500 matches the border
|
|
81
|
+
* the `--ai` section itself uses.
|
|
82
|
+
*/
|
|
83
|
+
.rsui-section-slider__view-all.rsui-card--ai {
|
|
84
|
+
@apply bg-none border-brand-500;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/*
|
|
88
|
+
* The icon slot, stacked above the label the way Empty stacks its image. Sizing
|
|
89
|
+
* belongs to whichever Icon component the consumer puts inside — those size
|
|
90
|
+
* their own SVG child.
|
|
91
|
+
*
|
|
92
|
+
* Colour, though, has to be taken back. Icon's variants pin the SVG to a fixed
|
|
93
|
+
* token (`--primary` is `text-text-primary`), which would leave the icon sitting
|
|
94
|
+
* still while the label darkens on hover, and dark on the brand-600 surface of
|
|
95
|
+
* `--featured` where the rest of the card has inverted. Forcing `currentColor`
|
|
96
|
+
* onto the SVG makes the icon inherit the button, so it moves and inverts with
|
|
97
|
+
* the label without the consumer having to pick a matching Icon variant.
|
|
98
|
+
*
|
|
99
|
+
* `IconCircleBackground` is excluded, though — it already carries a deliberate
|
|
100
|
+
* on-colour pairing, and forcing `currentColor` would put a dark glyph on its dark
|
|
101
|
+
* contrast-coloured circle.
|
|
102
|
+
*
|
|
103
|
+
* Both branches of the `:is()` need the guard. A bare `svg` alternative looks
|
|
104
|
+
* harmless but matches the heroicon inside a background icon just the same, and
|
|
105
|
+
* with the wrapper class in front the rule is (0,3,0) — above the (0,2,0) that
|
|
106
|
+
* paints the glyph — so it would win outright and defeat the exclusion this
|
|
107
|
+
* comment claims to make.
|
|
108
|
+
*/
|
|
109
|
+
.rsui-section-slider__view-all-icon {
|
|
110
|
+
@apply flex items-center justify-center;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.rsui-section-slider__view-all-icon :is(.rsui-icon:not(.rsui-icon--background) svg, .rsui-icon:not(.rsui-icon--background) > *) {
|
|
114
|
+
color: currentColor;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/*
|
|
118
|
+
* Card's tokens are built for a light surface, and this variant is brand-600, so
|
|
119
|
+
* they invert here — the same thing the header title and subtitle already do for
|
|
120
|
+
* `--featured`.
|
|
121
|
+
*
|
|
122
|
+
* `bg-transparent` is load-bearing, not tidying. `.rsui-card` paints
|
|
123
|
+
* `bg-background-primary`, which is white, and nothing else in this cascade
|
|
124
|
+
* resets it — inverting only the foreground leaves white text and a white dashed
|
|
125
|
+
* border on a white card, i.e. an invisible card on a brand-600 slider. The card
|
|
126
|
+
* has to sit on the brand surface for the inversion to mean anything.
|
|
127
|
+
*/
|
|
128
|
+
.rsui-section-slider--featured .rsui-section-slider__view-all {
|
|
129
|
+
@apply bg-transparent border-white/60 text-white/80;
|
|
130
|
+
@apply hover:border-white hover:bg-white/10 hover:text-white;
|
|
131
|
+
@apply focus-visible:ring-white;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/*
|
|
135
|
+
* Same reasoning on the `--ai` section, minus the inversion. That gradient is
|
|
136
|
+
* light enough for the default dark label to stay legible, so only the fill has
|
|
137
|
+
* to go — Card's white would otherwise sit on the gradient as a flat patch, which
|
|
138
|
+
* is the one place a ghost card should be showing what is behind it.
|
|
139
|
+
*
|
|
140
|
+
* `bg-transparent` and not `bg-none` here: the card's own background is a colour
|
|
141
|
+
* at this point, since `--ai` describes the section rather than the card. The
|
|
142
|
+
* `bg-none` rule above is for `viewAllVariant="ai"`, where Card paints a gradient
|
|
143
|
+
* via `background-image` instead.
|
|
144
|
+
*/
|
|
145
|
+
.rsui-section-slider--ai .rsui-section-slider__view-all {
|
|
146
|
+
@apply bg-transparent;
|
|
147
|
+
@apply hover:bg-white/40;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/*
|
|
151
|
+
* The `focus-visible` ring below is not decorative. Below `lg` this is a real
|
|
152
|
+
* scroll port, and Chrome can make scroll containers keyboard-focusable so they
|
|
153
|
+
* can be arrow-keyed — measured at 500px, this one is focusable with
|
|
154
|
+
* `outline-style: none`, i.e. a focus stop with no visible indicator (WCAG
|
|
155
|
+
* 2.4.7). At `lg` and up the track is `overflow-x-hidden`, which is not
|
|
156
|
+
* user-scrollable and so not focusable, and the rule never fires.
|
|
157
|
+
*
|
|
158
|
+
* `ring-inset` rather than an offset ring: the slider root is `overflow-x-hidden`
|
|
159
|
+
* with `px-0`, so the track sits flush against it and an outset ring would be
|
|
160
|
+
* clipped away on both edges — and clipped a second time by the mask in
|
|
161
|
+
* `--hover-actions` mode. Same reason card_group.css and form_field_date_range.css
|
|
162
|
+
* use it.
|
|
163
|
+
*/
|
|
29
164
|
.rsui-section-slider__container {
|
|
30
165
|
@apply relative flex gap-x-3 lg:gap-x-6 overflow-x-auto lg:overflow-x-hidden focus:outline-hidden scroll-smooth snap-x snap-mandatory scroll-pl-space-lg;
|
|
166
|
+
@apply focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-border-primary;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/*
|
|
170
|
+
* Position announcements for screen readers. The rail is a list of cards, not a
|
|
171
|
+
* carousel, so there is no slide to name -- what a non-sighted user cannot
|
|
172
|
+
* recover is simply how far along the rail they now are.
|
|
173
|
+
*/
|
|
174
|
+
.rsui-section-slider__announcer {
|
|
175
|
+
@apply sr-only;
|
|
31
176
|
}
|
|
32
177
|
|
|
33
178
|
.rsui-section-slider__container::-webkit-scrollbar {
|
|
34
179
|
@apply hidden;
|
|
35
180
|
}
|
|
181
|
+
|
|
182
|
+
/*
|
|
183
|
+
* Edge fades for `hover-actions`. With the arrows held back until hover, a
|
|
184
|
+
* resting rail has nothing at all to say it moves, and the peeking last card is
|
|
185
|
+
* doing that work alone. Fading the edge that has content behind it gives the
|
|
186
|
+
* rail a permanent affordance that costs no hover, no touch and no semantics.
|
|
187
|
+
*
|
|
188
|
+
* A mask rather than a gradient overlay, because the fade has to work over four
|
|
189
|
+
* different section backgrounds — default, `--featured` (brand-600) and `--ai`
|
|
190
|
+
* (itself a gradient). Colour-matching an overlay to a gradient is not possible;
|
|
191
|
+
* masking the content to transparent is background-agnostic and needs no variant
|
|
192
|
+
* of its own.
|
|
193
|
+
*
|
|
194
|
+
* Both widths rest at 0, which collapses the gradient to fully opaque, so the
|
|
195
|
+
* mask is inert until Vue adds a fade modifier. Not transitioned: the widths
|
|
196
|
+
* would need registering via @property to animate at all, and the switch happens
|
|
197
|
+
* mid-scroll where the movement already covers it.
|
|
198
|
+
*/
|
|
199
|
+
.rsui-section-slider__container {
|
|
200
|
+
--rsui-section-slider-fade-start: 0px;
|
|
201
|
+
--rsui-section-slider-fade-end: 0px;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.rsui-section-slider--hover-actions .rsui-section-slider__container {
|
|
205
|
+
mask-image: linear-gradient(
|
|
206
|
+
to right,
|
|
207
|
+
transparent 0,
|
|
208
|
+
black var(--rsui-section-slider-fade-start),
|
|
209
|
+
black calc(100% - var(--rsui-section-slider-fade-end)),
|
|
210
|
+
transparent 100%
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.rsui-section-slider__container--fade-start {
|
|
215
|
+
--rsui-section-slider-fade-start: 3rem;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.rsui-section-slider__container--fade-end {
|
|
219
|
+
--rsui-section-slider-fade-end: 3rem;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/*
|
|
223
|
+
* Positioning context for the `hover-actions` overlay. Deliberately a wrapper
|
|
224
|
+
* around the track rather than the track itself: the container is the scroll
|
|
225
|
+
* port, so anything absolutely positioned inside it scrolls away with the cards.
|
|
226
|
+
*/
|
|
227
|
+
.rsui-section-slider__viewport {
|
|
228
|
+
@apply relative;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/*
|
|
232
|
+
* Overlay navigation layer — one full-bleed layer rather than two individually
|
|
233
|
+
* positioned buttons, so `justify-between` places the pair on the two edges and
|
|
234
|
+
* `items-center` centres them on the rail without any transform maths.
|
|
235
|
+
*
|
|
236
|
+
* The layer spans the cards, so it has to stay transparent to the pointer;
|
|
237
|
+
* `pointer-events-none` here with `pointer-events-auto` back on the buttons
|
|
238
|
+
* keeps the cards underneath clickable.
|
|
239
|
+
*
|
|
240
|
+
* Hidden below `lg` on purpose. Up to that breakpoint the track is
|
|
241
|
+
* `overflow-x-auto` and swipe already moves it, so arrows over the artwork would
|
|
242
|
+
* be covering content to duplicate a gesture the user already has.
|
|
243
|
+
*/
|
|
244
|
+
.rsui-section-slider__actions {
|
|
245
|
+
@apply hidden lg:flex absolute inset-0 z-10 items-center justify-between px-3 pointer-events-none;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/*
|
|
249
|
+
* Overlaid arrows sit on card artwork, so the header's transparent pill would
|
|
250
|
+
* leave the chevron competing with whatever is behind it. Give these ones a
|
|
251
|
+
* solid surface — scoped to the overlay so header arrows keep their existing
|
|
252
|
+
* look.
|
|
253
|
+
*
|
|
254
|
+
* This single-tone border does not *guarantee* 1.4.11 against arbitrary
|
|
255
|
+
* photography: its outer edge always has an unknown image beside it, so it fails
|
|
256
|
+
* on mid-tone imagery. It narrows the gap rather than closing it, helped by the
|
|
257
|
+
* fade behind the pill — though note the fade only covers the outer part of the
|
|
258
|
+
* pill's backdrop (the fade runs 0–3rem from the edge, the pill roughly
|
|
259
|
+
* 0.75–4.25rem), and only partially at that.
|
|
260
|
+
*
|
|
261
|
+
* A two-tone indicator would actually close it: two adjacent bands with 3:1
|
|
262
|
+
* against each other means at least one of them clears 3:1 against whatever is
|
|
263
|
+
* behind, whatever that turns out to be. Deliberately not done here — it is a
|
|
264
|
+
* visible design change to the control rather than a correctness fix, so it wants
|
|
265
|
+
* a decision rather than a commit.
|
|
266
|
+
*/
|
|
267
|
+
.rsui-section-slider__actions .rsui-section-slider__action {
|
|
268
|
+
@apply pointer-events-auto bg-background-primary border-grey-600 shadow-xl;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
@media (hover: hover) {
|
|
272
|
+
/*
|
|
273
|
+
* Rest hidden, reveal on the rail. Opacity rather than `display`/`hidden` so
|
|
274
|
+
* the buttons keep their box (no reflow as they appear) and stay reachable
|
|
275
|
+
* by keyboard — a focusable control that cannot be seen fails WCAG 2.4.7,
|
|
276
|
+
* which is what `:focus-within` below is for. The base class already carries
|
|
277
|
+
* `transition-all`, so the fade needs nothing added here.
|
|
278
|
+
*
|
|
279
|
+
* Gated on `(hover: hover)`: from `lg` up the track is `overflow-x-hidden`,
|
|
280
|
+
* so these arrows are the only way to move the rail. A touch device at a
|
|
281
|
+
* desktop width — iPad in landscape, touchscreen laptop — has no swipe and
|
|
282
|
+
* no hover, and would be left with no way to navigate at all. Those devices
|
|
283
|
+
* keep the arrows on permanently.
|
|
284
|
+
*/
|
|
285
|
+
.rsui-section-slider__actions .rsui-section-slider__action {
|
|
286
|
+
@apply opacity-0;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/*
|
|
290
|
+
* `:enabled` so only arrows that can actually move the rail come forward.
|
|
291
|
+
* In rail mode that tracks scroll position, so a resting rail shows no
|
|
292
|
+
* previous arrow at all rather than one pointing into nothing, and the pair
|
|
293
|
+
* disappears entirely when every card already fits. The button stays in the
|
|
294
|
+
* DOM and in the a11y tree as disabled, which tells a screen reader user
|
|
295
|
+
* "start of the rail" — something an unmounted button cannot do.
|
|
296
|
+
*/
|
|
297
|
+
.rsui-section-slider__viewport:hover .rsui-section-slider__action:enabled,
|
|
298
|
+
.rsui-section-slider__viewport:focus-within .rsui-section-slider__action:enabled {
|
|
299
|
+
@apply opacity-100;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/*
|
|
303
|
+
* Escape hides the arrows again while the pointer is still on the rail —
|
|
304
|
+
* WCAG 1.4.13 wants hover-revealed content that obscures other content to be
|
|
305
|
+
* dismissable without moving the pointer, and these do sit over the cards.
|
|
306
|
+
*
|
|
307
|
+
* `:not(:focus-within)` because dismissal must not win over the focus reveal
|
|
308
|
+
* above. Without it, pressing Escape with the pointer on the rail and focus on
|
|
309
|
+
* an arrow hid a control that was still focused and still clickable — the
|
|
310
|
+
* exact 2.4.7 failure the reveal rule exists to prevent.
|
|
311
|
+
*
|
|
312
|
+
* `:enabled` carries no extra weight of its own here: this selector and the
|
|
313
|
+
* reveal above both compute to (0,4,0), so it only brings this level and the
|
|
314
|
+
* cascade decides on source order. That is why this block has to stay last
|
|
315
|
+
* inside the query.
|
|
316
|
+
*/
|
|
317
|
+
.rsui-section-slider__viewport--navigation-dismissed:not(:focus-within):hover .rsui-section-slider__action:enabled {
|
|
318
|
+
@apply opacity-0;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/*
|
|
323
|
+
* Reduced motion. This rule covers swipe and keyboard scrolling, which never go
|
|
324
|
+
* through JavaScript. Arrow presses are handled separately in scrollToPosition,
|
|
325
|
+
* which passes an explicit `'instant'` — a scripted `behavior` other than `auto`
|
|
326
|
+
* overrides `scroll-behavior`, so the two paths do not overlap and neither is
|
|
327
|
+
* redundant.
|
|
328
|
+
*/
|
|
329
|
+
@media (prefers-reduced-motion: reduce) {
|
|
330
|
+
.rsui-section-slider__container {
|
|
331
|
+
scroll-behavior: auto;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.rsui-section-slider__action {
|
|
335
|
+
transition: none;
|
|
336
|
+
}
|
|
337
|
+
}
|
package/components/switcher.css
CHANGED
|
@@ -15,6 +15,66 @@
|
|
|
15
15
|
@apply w-full sm:w-fit;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
/* Bordered — for a switcher on a surface that is not white. Opt in with the
|
|
19
|
+
`bordered` prop.
|
|
20
|
+
|
|
21
|
+
The track is `background-disabled` (Grey-25, #fcfcfc) and a secondary Section is
|
|
22
|
+
`background-secondary` (Grey-50, #fafafa): 2/255 apart, and the track is the
|
|
23
|
+
*lighter* of the two, so on a grey surface it does not read as a container at all.
|
|
24
|
+
|
|
25
|
+
A border rather than a darker fill, so the tonal well stays the same grey
|
|
26
|
+
everywhere it already works. Note it does add 2px to each axis, so a bordered
|
|
27
|
+
switcher no longer aligns exactly with an unbordered sibling.
|
|
28
|
+
|
|
29
|
+
`:not(--chips)` on both new modifiers: chips deliberately has no track
|
|
30
|
+
(`bg-transparent p-0 rounded-none`), so a border there is a square hairline box
|
|
31
|
+
hugging the chips with a clipped shadow — broken-looking rather than opt-out-able.
|
|
32
|
+
Excluded here rather than reset in the chips block, so no existing chips rule
|
|
33
|
+
changes. */
|
|
34
|
+
.rsui-switcher--bordered:not(.rsui-switcher--chips) {
|
|
35
|
+
@apply border border-border-secondary;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* The active item is already delineated by its own Grey-300 border
|
|
39
|
+
(`switcher_item.css`, `--active` sets `border-border-secondary` against a base of
|
|
40
|
+
`border-transparent`). Once the container gains a Grey-300 border too, the pill
|
|
41
|
+
edge and the track edge are the same colour and the nesting reads flat. The shadow
|
|
42
|
+
re-separates the two by depth rather than by adding a third colour.
|
|
43
|
+
|
|
44
|
+
`shadow-sm` rather than a raw `box-shadow`, which would be the only literal shadow
|
|
45
|
+
in the package — every other component uses a utility. */
|
|
46
|
+
.rsui-switcher--bordered:not(.rsui-switcher--chips) .rsui-switcher-item--active {
|
|
47
|
+
@apply shadow-sm;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* Inline — one row below `sm`, split between the options. Opt in with the `inline`
|
|
51
|
+
prop, and pair it with `full`: `inline` sets the direction, `full` gives it a row
|
|
52
|
+
to split. On its own the container is still `w-fit` and shrink-wraps.
|
|
53
|
+
|
|
54
|
+
Scoped to `max-sm` on purpose: at `sm` and above the default is already a row, so
|
|
55
|
+
there is nothing to change and nothing to accidentally break — `--full`'s `flex-1`
|
|
56
|
+
in particular is left entirely alone. Only the sub-`sm` stack is overridden.
|
|
57
|
+
|
|
58
|
+
Three declarations, all load-bearing:
|
|
59
|
+
- `w-auto` undoes `w-full` from `.rsui-switcher .rsui-switcher-item`, which would
|
|
60
|
+
otherwise make each option claim the full width of a row it is now sharing.
|
|
61
|
+
This works by source order at equal specificity (0,2,0), so this block must
|
|
62
|
+
stay BELOW that base item rule.
|
|
63
|
+
- `flex-1` is what actually splits the row between the options.
|
|
64
|
+
- `min-w-0` is what stops it clipping. Flex items floor at `min-width: auto`, and
|
|
65
|
+
the item is `whitespace-nowrap`, so without this they refuse to shrink below
|
|
66
|
+
their label, the row outgrows the `overflow-hidden` track, and options are cut
|
|
67
|
+
off with no scrollbar and no way to reach them — measured at 166px of a 360px
|
|
68
|
+
track lost with three long labels. `overflow-hidden` on the item keeps a long
|
|
69
|
+
label inside its own button rather than spilling over its neighbour. */
|
|
70
|
+
.rsui-switcher--inline:not(.rsui-switcher--chips) {
|
|
71
|
+
@apply max-sm:flex-row max-sm:items-center;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.rsui-switcher--inline:not(.rsui-switcher--chips) .rsui-switcher-item {
|
|
75
|
+
@apply max-sm:w-auto max-sm:flex-1 max-sm:min-w-0 max-sm:overflow-hidden;
|
|
76
|
+
}
|
|
77
|
+
|
|
18
78
|
/* Chips variant — a filter chip set rather than a segmented control. Opt in with
|
|
19
79
|
the `chips` prop; the default is untouched.
|
|
20
80
|
|