@redseed/redseed-ui-tailwindcss 7.55.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.
- package/colors.css +14 -1
- package/components/badge.css +20 -3
- package/components/button_card.css +4 -1
- package/components/button_slot.css +6 -1
- package/components/button_tertiary.css +9 -2
- package/components/card.css +23 -2
- package/components/card_header.css +11 -0
- package/components/checkbox_card.css +4 -1
- package/components/comment.css +3 -1
- package/components/form_field_checkbox.css +28 -2
- package/components/form_field_date_range.css +32 -0
- package/components/form_fields_login.css +12 -1
- package/components/form_fields_register.css +12 -1
- package/components/icon.css +104 -1
- package/components/list_item.css +64 -1
- package/components/message_box.css +118 -4
- package/components/meta_info.css +11 -3
- package/components/metric_card.css +70 -1
- package/components/page_header.css +76 -0
- package/components/radio_card.css +32 -7
- package/components/section_footer.css +68 -0
- package/components/section_header.css +16 -2
- package/components/section_slider.css +302 -0
- package/components/stage_dial.css +22 -0
- package/components/switcher.css +106 -0
- package/components/switcher_item.css +21 -2
- package/components/tab_slider_item.css +22 -1
- package/components.css +0 -1
- package/package.json +1 -1
- package/tokens.css +5 -1
- package/components/hero_section.css +0 -32
package/colors.css
CHANGED
|
@@ -565,7 +565,20 @@
|
|
|
565
565
|
--Colors-Warm-Mauve-800: #855878;
|
|
566
566
|
--Colors-Warm-Mauve-900: #6a4660;
|
|
567
567
|
--Colors-Warm-Mauve-950: #4c3145;
|
|
568
|
-
/* Secondary brand accents (single tokens, Figma Brand palette · Secondary)
|
|
568
|
+
/* Secondary brand accents (single tokens, Figma Brand palette · Secondary)
|
|
569
|
+
|
|
570
|
+
DO NOT USE these in product surfaces. Components and pages draw only on
|
|
571
|
+
the six primary identity ramps above — Journey Green, Deep Teal, Modern
|
|
572
|
+
Bone, Warm Yellow, Soft Lilac, Warm Mauve — plus the Grey ramp and the
|
|
573
|
+
semantic tokens (success / warning / error / info).
|
|
574
|
+
|
|
575
|
+
They are defined here for completeness against the Figma palette, which
|
|
576
|
+
is why they have no ramp: single values only, so they cannot carry text
|
|
577
|
+
either. Cornflower measures 3.2:1 on white and Apricot Lift 2.25:1, both
|
|
578
|
+
short of the 4.5:1 that WCAG 1.4.3 asks of body text.
|
|
579
|
+
|
|
580
|
+
One accepted exception: --color-button-primary-hover consumes Plum Ink
|
|
581
|
+
(see tokens.css). That is deliberate and predates this note. */
|
|
569
582
|
--Colors-Apricot-Lift: #e89b6c;
|
|
570
583
|
--Colors-Cornflower: #6e8fd6;
|
|
571
584
|
--Colors-Marigold: #f2b33c;
|
package/components/badge.css
CHANGED
|
@@ -1,19 +1,30 @@
|
|
|
1
1
|
.rsui-badge {
|
|
2
|
-
|
|
2
|
+
/* whitespace-nowrap: a badge is a pill, so its label must stay on one line. Without it
|
|
3
|
+
a badge in a narrow column wrapped to two lines and grew to 42px tall — seen in
|
|
4
|
+
production on the Twinsies home page, where "1 answer to redo" and "2 answers to redo"
|
|
5
|
+
both broke across two lines inside a rounded-full chip. Every other chip-shaped
|
|
6
|
+
control in the system already declares this: Pill, ButtonSlot, TabSliderItem and
|
|
7
|
+
SwitcherItem. Badge was the only one that did not. */
|
|
8
|
+
@apply inline-flex items-center justify-center gap-x-1 whitespace-nowrap;
|
|
3
9
|
@apply w-fit h-fit rounded-full;
|
|
4
10
|
@apply text-sm font-medium leading-4;
|
|
5
11
|
}
|
|
6
12
|
|
|
13
|
+
/* Horizontal padding grows with the size: it used to sit at px-2 for xs, sm and md alike,
|
|
14
|
+
so a md badge was exactly as wide as an xs one and its 8/8 padding collapsed to square —
|
|
15
|
+
on a rounded-full shape that puts the label closer to the curved ends than to the flat
|
|
16
|
+
top and bottom. The 8 / 10 / 12 progression matches Pill, the other chip in the system.
|
|
17
|
+
Vertical padding is untouched, so no badge changes height. */
|
|
7
18
|
.rsui-badge--xs {
|
|
8
19
|
@apply text-xs leading-3 px-2 py-0.5;
|
|
9
20
|
}
|
|
10
21
|
|
|
11
22
|
.rsui-badge--sm {
|
|
12
|
-
@apply px-2 py-1;
|
|
23
|
+
@apply px-2.5 py-1;
|
|
13
24
|
}
|
|
14
25
|
|
|
15
26
|
.rsui-badge--md {
|
|
16
|
-
@apply px-
|
|
27
|
+
@apply px-3 py-2;
|
|
17
28
|
}
|
|
18
29
|
|
|
19
30
|
.rsui-badge--secondary {
|
|
@@ -40,6 +51,12 @@
|
|
|
40
51
|
@apply text-brand-500 bg-brand-50 border border-brand-200;
|
|
41
52
|
}
|
|
42
53
|
|
|
54
|
+
/* Classic and Coach still ride the pre-rebrand pink and redseed-blue ramps rather than the
|
|
55
|
+
current brand ones (Skillscheck above already moved to Journey Green). Left as-is on
|
|
56
|
+
purpose: both categories are due to be phased out, so re-homing them onto brand colours
|
|
57
|
+
would be churn. There is also only one unclaimed brand ramp (Deep Teal) against two
|
|
58
|
+
categories — Journey Green, Warm Mauve and Modern Bone are taken by brand, AI and page
|
|
59
|
+
grounds. Revisit only if the phase-out stalls. */
|
|
43
60
|
.rsui-badge--classic {
|
|
44
61
|
@apply text-pink-500 bg-pink-50 border border-pink-200;
|
|
45
62
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
/* ButtonCard now takes the family's tonal hover from .rsui-card--hoverable, which it opts
|
|
2
|
+
into via :hoverable="!disabled". It previously cancelled the card shadow and darkened the
|
|
3
|
+
border instead — the only bespoke hover treatment in the family, and the reason hovering
|
|
4
|
+
a ButtonCard looked nothing like hovering a Card. */
|
|
1
5
|
.rsui-button-card {
|
|
2
|
-
@apply hover:shadow-none hover:border-text-primary;
|
|
3
6
|
}
|
|
4
7
|
|
|
5
8
|
.rsui-button-card--disabled {
|
|
@@ -53,8 +53,13 @@
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
/* modifier xs size */
|
|
56
|
+
/* px-2.5, not px-1.5: horizontal padding steps 10/12/14/16/18/20 across xs..2xl, and
|
|
57
|
+
xs was the only size where px equalled py — every other size runs 1.4-1.6x wider than
|
|
58
|
+
tall, so an xs label sat 6px from the border and read visibly cramped. Height is
|
|
59
|
+
unchanged (py untouched); only the button's width grows. --xs-icon keeps its own p-1.5
|
|
60
|
+
because a square icon button is correct at that size. */
|
|
56
61
|
.rsui-button-slot--xs {
|
|
57
|
-
@apply text-sm px-
|
|
62
|
+
@apply text-sm px-2.5 py-1.5 gap-1;
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
/* modifier xs size with icon */
|
|
@@ -3,13 +3,20 @@
|
|
|
3
3
|
@apply text-button-tertiary bg-transparent border-transparent;
|
|
4
4
|
|
|
5
5
|
/* default hover state */
|
|
6
|
-
|
|
6
|
+
/* A ghost button fills faintly on hover; it must not sprout an outline. This used to
|
|
7
|
+
set border-button-tertiary-hover, taking the border from transparent to solid
|
|
8
|
+
#124943 — so hovering a borderless button made a hard dark rule appear, while the
|
|
9
|
+
fill stayed transparent. That is the inverse of the intended ghost behaviour, and
|
|
10
|
+
the opposite of primary (fill darkens) and secondary (outline darkens). */
|
|
11
|
+
@apply hover:text-button-tertiary-hover hover:bg-background-secondary hover:border-transparent;
|
|
7
12
|
|
|
8
13
|
/* default focus state */
|
|
9
14
|
@apply focus-visible:ring-button-tertiary focus-visible:border-transparent;
|
|
10
15
|
|
|
11
16
|
/* default active state */
|
|
12
|
-
|
|
17
|
+
/* Pressed goes one step darker than hover (transparent -> Grey-50 -> Grey-100) so the
|
|
18
|
+
press reads as deeper rather than clearing the hover fill back to nothing. */
|
|
19
|
+
@apply active:text-button-tertiary-active active:bg-background-tertiary-invert active:border-transparent;
|
|
13
20
|
|
|
14
21
|
/* default disabled state */
|
|
15
22
|
@apply disabled:bg-transparent disabled:border-transparent disabled:text-text-disabled;
|
package/components/card.css
CHANGED
|
@@ -68,6 +68,9 @@
|
|
|
68
68
|
@apply relative border-0;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
/* Positioning only. The dashed outline itself is drawn by the <rect> inside this element —
|
|
72
|
+
it is an <svg>, and the rect strokes 1px with stroke-dasharray 8 16. Do not add CSS border
|
|
73
|
+
utilities here: they paint a second dashed edge on top of the SVG stroke. */
|
|
71
74
|
.rsui-card__draft-border {
|
|
72
75
|
@apply absolute inset-0 size-full pointer-events-none rounded-xl border-border-secondary;
|
|
73
76
|
}
|
|
@@ -79,8 +82,20 @@
|
|
|
79
82
|
@apply overflow-x-hidden;
|
|
80
83
|
}
|
|
81
84
|
|
|
82
|
-
.
|
|
83
|
-
|
|
85
|
+
/* Hover state layer. The card family is an Outlined system — flat, 1px border, no resting
|
|
86
|
+
elevation — so hover tints the surface rather than lifting it. Two reasons this is a
|
|
87
|
+
::before scrim and not a background-color swap: every colour variant (secondary, brand,
|
|
88
|
+
success, info, warning, error, draft, ai) has to survive underneath it, and a scrim
|
|
89
|
+
composites over the ai variant's gradient where a flat colour could not. ::before rather
|
|
90
|
+
than ::after so it paints beneath the card's content in DOM order. */
|
|
91
|
+
.rsui-card--hoverable::before {
|
|
92
|
+
content: '';
|
|
93
|
+
@apply absolute inset-0 rounded-xl pointer-events-none;
|
|
94
|
+
@apply bg-background-primary-contrast opacity-0 transition-opacity duration-200;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.rsui-card--hoverable:hover::before {
|
|
98
|
+
@apply opacity-5;
|
|
84
99
|
}
|
|
85
100
|
|
|
86
101
|
.rsui-card--clickable {
|
|
@@ -105,6 +120,12 @@
|
|
|
105
120
|
colour alone (WCAG 1.4.1). */
|
|
106
121
|
.rsui-card__action-layer--active {
|
|
107
122
|
@apply ring-2 ring-offset-2 ring-brand-500;
|
|
123
|
+
|
|
124
|
+
/* Hold the brand ring through focus. Without this the focus-visible rule above repaints
|
|
125
|
+
the ring border-primary grey, so focusing a selected card silently dropped its
|
|
126
|
+
selection cue — the ring is the same geometry for both states, so only colour carries
|
|
127
|
+
the difference. */
|
|
128
|
+
@apply focus-visible:ring-brand-500;
|
|
108
129
|
}
|
|
109
130
|
|
|
110
131
|
.rsui-card__action-layer-label {
|
|
@@ -28,6 +28,17 @@
|
|
|
28
28
|
@apply @md/card-header:items-center;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
/* Centring only holds while the text block is a lone title. `--single-line` means the *title*
|
|
32
|
+
is clamped to one line, not that the header is one row — add a subtitle and a badge and the
|
|
33
|
+
block is 78px tall, where centring a 48px avatar left it sitting 15px low against the text
|
|
34
|
+
it belongs to. Seen in production on the Twinsies meeting cards. Top-align as soon as the
|
|
35
|
+
block carries more than the title; a lone title is shorter than the avatar, so centring
|
|
36
|
+
stays correct there. */
|
|
37
|
+
.rsui-card-header__header--single-line:has(.rsui-card-header__subtitle),
|
|
38
|
+
.rsui-card-header__header--single-line:has(.rsui-card-header__badge) {
|
|
39
|
+
@apply @md/card-header:items-start;
|
|
40
|
+
}
|
|
41
|
+
|
|
31
42
|
.rsui-card-header__avatar {
|
|
32
43
|
@apply w-12 overflow-hidden flex items-center justify-center;
|
|
33
44
|
}
|
|
@@ -83,7 +83,10 @@
|
|
|
83
83
|
/* Success state styling */
|
|
84
84
|
/* Increase specificity so it overrides base .rsui-card styles */
|
|
85
85
|
.rsui-card.rsui-checkbox-card--success {
|
|
86
|
-
|
|
86
|
+
/* bg-background-success, not the raw bg-success-100 palette step: this was the only
|
|
87
|
+
success surface in the library painting #DCFAE6 while Card, Badge, MessageBox and
|
|
88
|
+
Section all use the #ECFDF3 token — two different success greens, same border. */
|
|
89
|
+
@apply bg-background-success border-border-success;
|
|
87
90
|
}
|
|
88
91
|
|
|
89
92
|
.rsui-checkbox-card--success .rsui-checkbox-card__title-text {
|
package/components/comment.css
CHANGED
|
@@ -78,7 +78,9 @@
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
.rsui-comment__body {
|
|
81
|
-
|
|
81
|
+
/* 14px vertical, matching the row rhythm ListItem uses — a comment is a row of prose
|
|
82
|
+
and read tight against its neighbour at 12px. */
|
|
83
|
+
@apply py-3.5 px-4 rounded-xl border;
|
|
82
84
|
@apply rounded-tl-none ml-8;
|
|
83
85
|
@apply text-text-primary bg-background-primary;
|
|
84
86
|
@apply border-border-primary;
|
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
.rsui-form-field-checkbox__check {
|
|
10
|
-
|
|
10
|
+
/* Centres the tick. The input is absolutely positioned so it stays out of flow and keeps
|
|
11
|
+
filling the box; only the SVG is laid out here. */
|
|
12
|
+
@apply size-6 shrink-0 relative flex items-center justify-center bg-background-primary rounded-lg cursor-pointer;
|
|
11
13
|
@apply has-[:disabled]:text-text-disabled has-[:disabled]:cursor-default;
|
|
12
14
|
}
|
|
13
15
|
|
|
@@ -16,11 +18,21 @@
|
|
|
16
18
|
@apply w-full h-full text-text-primary border border-border-primary text-transparent bg-none;
|
|
17
19
|
@apply focus-visible:ring-3 focus-visible:ring-border-primary focus-visible:border-border-primary;
|
|
18
20
|
@apply invalid:border-border-error invalid:ring-0;
|
|
21
|
+
|
|
22
|
+
/* Checked fills the box and the tick inverts, which is how every mainstream checkbox
|
|
23
|
+
reads. It used to stay transparent, so a checked box was a white square with a dark
|
|
24
|
+
tick — and the success variant already filled, so the default was the odd one out. */
|
|
25
|
+
@apply checked:bg-text-primary checked:border-text-primary;
|
|
26
|
+
|
|
19
27
|
@apply disabled:text-text-disabled disabled:bg-background-disabled disabled:cursor-default;
|
|
28
|
+
@apply disabled:checked:bg-background-disabled disabled:checked:border-border-disabled;
|
|
20
29
|
}
|
|
21
30
|
|
|
31
|
+
/* The tick is inset rather than size-full. At size-full a 24px glyph sat in a 24px box with
|
|
32
|
+
no breathing room, so its strokes ran into the border and the control read cramped. Each
|
|
33
|
+
size keeps roughly a 4px optical inset — see the sm/xs overrides further down. */
|
|
22
34
|
.rsui-form-field-checkbox__check svg {
|
|
23
|
-
@apply size-
|
|
35
|
+
@apply size-4 relative z-1 pointer-events-none stroke-[2.5] origin-center;
|
|
24
36
|
/* Short, cute draw-on: the tick strokes itself in with a tiny pop.
|
|
25
37
|
dasharray is set a touch above the check path length so the resting
|
|
26
38
|
state has no gap. Disabled under reduced-motion below. */
|
|
@@ -28,6 +40,12 @@
|
|
|
28
40
|
animation: rsui-checkbox-tick 200ms ease-out;
|
|
29
41
|
}
|
|
30
42
|
|
|
43
|
+
/* Invert the tick against the filled box. Scoped to :not(:disabled) because a disabled
|
|
44
|
+
checked box keeps the pale disabled fill, where an inverted tick would disappear. */
|
|
45
|
+
.rsui-form-field-checkbox__check:has(input:checked:not(:disabled)) svg {
|
|
46
|
+
@apply text-text-on-color;
|
|
47
|
+
}
|
|
48
|
+
|
|
31
49
|
@keyframes rsui-checkbox-tick {
|
|
32
50
|
0% { stroke-dashoffset: 30; transform: scale(0.8); }
|
|
33
51
|
60% { transform: scale(1.06); }
|
|
@@ -53,6 +71,10 @@
|
|
|
53
71
|
@apply rounded-md;
|
|
54
72
|
}
|
|
55
73
|
|
|
74
|
+
.rsui-form-field-checkbox__check--sm svg {
|
|
75
|
+
@apply size-3;
|
|
76
|
+
}
|
|
77
|
+
|
|
56
78
|
.rsui-form-field-checkbox__label--sm {
|
|
57
79
|
@apply leading-5 text-sm;
|
|
58
80
|
}
|
|
@@ -65,6 +87,10 @@
|
|
|
65
87
|
@apply rounded;
|
|
66
88
|
}
|
|
67
89
|
|
|
90
|
+
.rsui-form-field-checkbox__check--xs svg {
|
|
91
|
+
@apply size-2;
|
|
92
|
+
}
|
|
93
|
+
|
|
68
94
|
.rsui-form-field-checkbox__label--xs {
|
|
69
95
|
@apply leading-4 text-xs;
|
|
70
96
|
}
|
|
@@ -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
|
*
|
|
@@ -1,4 +1,15 @@
|
|
|
1
|
+
/* The field group owns its own spacing. It used to render a bare list of siblings and
|
|
2
|
+
inherit spacing from `.rsui-form-slot__content > *`, so anywhere that ancestor was absent —
|
|
3
|
+
including every one of its own stories — the fields and the submit button collapsed flush
|
|
4
|
+
against each other with a 0px gap. gap-space-md (16px) matches the mt-4 that rule applied,
|
|
5
|
+
so the group is unchanged inside FormSlot and correct everywhere else. */
|
|
6
|
+
.rsui-form-fields-login {
|
|
7
|
+
@apply flex flex-col gap-space-md;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/* pt-4 dropped: the group's gap-space-md now supplies the 16px above this link, and
|
|
11
|
+
keeping both stacked them into 32px. */
|
|
1
12
|
.rsui-form-login__signup {
|
|
2
|
-
@apply text-sm
|
|
13
|
+
@apply text-sm;
|
|
3
14
|
}
|
|
4
15
|
|
|
@@ -1,4 +1,15 @@
|
|
|
1
|
+
/* The field group owns its own spacing. It used to render a bare list of siblings and
|
|
2
|
+
inherit spacing from `.rsui-form-slot__content > *`, so anywhere that ancestor was absent —
|
|
3
|
+
including every one of its own stories — the fields and the submit button collapsed flush
|
|
4
|
+
against each other with a 0px gap. gap-space-md (16px) matches the mt-4 that rule applied,
|
|
5
|
+
so the group is unchanged inside FormSlot and correct everywhere else. */
|
|
6
|
+
.rsui-form-fields-register {
|
|
7
|
+
@apply flex flex-col gap-space-md;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/* pt-4 dropped: the group's gap-space-md now supplies the 16px above this link, and
|
|
11
|
+
keeping both stacked them into 32px. */
|
|
1
12
|
.rsui-form-register__signin {
|
|
2
|
-
@apply text-sm
|
|
13
|
+
@apply text-sm;
|
|
3
14
|
}
|
|
4
15
|
|
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
|
+
}
|
package/components/list_item.css
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
.rsui-list-item {
|
|
2
|
-
|
|
2
|
+
/* 14px vertical rather than the 12px of a flat p-3: at 12px a two-line row reads as
|
|
3
|
+
cramped against its neighbours, which is the note that came back from laying these
|
|
4
|
+
out on a real page. Horizontal padding is unchanged. Half-steps are already used
|
|
5
|
+
across the button sizes, so 3.5 is in keeping. */
|
|
6
|
+
@apply relative flex flex-col rounded-xl px-3 py-3.5 overflow-hidden;
|
|
3
7
|
}
|
|
4
8
|
|
|
5
9
|
.rsui-list-item--clickable {
|
|
@@ -69,6 +73,65 @@
|
|
|
69
73
|
@apply mt-2 flex flex-wrap gap-x-4 gap-y-2;
|
|
70
74
|
}
|
|
71
75
|
|
|
76
|
+
/* Leading icon. Inline, unlike .rsui-list-item__avatar above, which hangs outside the row
|
|
77
|
+
box on a negative offset — an avatar identifies a person, an icon classifies the row, so
|
|
78
|
+
they sit differently and can be used together. */
|
|
79
|
+
.rsui-list-item__icon {
|
|
80
|
+
@apply shrink-0 size-8 rounded-lg overflow-hidden;
|
|
81
|
+
@apply flex items-center justify-center;
|
|
82
|
+
@apply bg-background-secondary;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.rsui-list-item__icon svg {
|
|
86
|
+
@apply size-4;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* Accent rail — a pseudo-element, so an accented row has the same DOM as a plain one.
|
|
90
|
+
Needs z-1 to sit above .rsui-list-item__bg, which is absolute and would otherwise paint
|
|
91
|
+
over it; the root's rounded-xl clips the rail into the corner. */
|
|
92
|
+
.rsui-list-item--accented::before {
|
|
93
|
+
content: '';
|
|
94
|
+
@apply absolute left-0 top-0 bottom-0 w-1 z-1 pointer-events-none;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.rsui-list-item--accent-warning::before {
|
|
98
|
+
@apply bg-background-warning-contrast;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.rsui-list-item--accent-info::before {
|
|
102
|
+
@apply bg-background-info-contrast;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.rsui-list-item--accent-success::before {
|
|
106
|
+
@apply bg-background-success-contrast;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.rsui-list-item--accent-error::before {
|
|
110
|
+
@apply bg-background-error-contrast;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.rsui-list-item--accent-neutral::before {
|
|
114
|
+
@apply bg-background-primary-contrast;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/* The icon tile takes the accent's own tint when both are used, so the rail and the tile
|
|
118
|
+
read as one mark rather than two. */
|
|
119
|
+
.rsui-list-item--accent-warning .rsui-list-item__icon {
|
|
120
|
+
@apply bg-background-warning;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.rsui-list-item--accent-info .rsui-list-item__icon {
|
|
124
|
+
@apply bg-background-info;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.rsui-list-item--accent-success .rsui-list-item__icon {
|
|
128
|
+
@apply bg-background-success;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.rsui-list-item--accent-error .rsui-list-item__icon {
|
|
132
|
+
@apply bg-background-error;
|
|
133
|
+
}
|
|
134
|
+
|
|
72
135
|
.rsui-list-item__description {
|
|
73
136
|
@apply line-clamp-2 mt-2 text-base;
|
|
74
137
|
}
|