@redseed/redseed-ui-tailwindcss 7.66.0 → 7.68.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/components/category_list.css +19 -5
- package/components/form_field_slot.css +22 -2
- package/components/form_fieldset.css +12 -1
- package/components/metric_card.css +58 -10
- package/components/page_header.css +255 -0
- package/package.json +1 -1
- package/tokens.css +8 -1
|
@@ -30,16 +30,23 @@
|
|
|
30
30
|
|
|
31
31
|
/* A linked category has to look like a link. A badge with no pointer and no
|
|
32
32
|
hover reads as a label, which is what most categories are, so the ones that
|
|
33
|
-
genuinely filter a list get
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
genuinely filter a list get a pointer, a hover state on the pill, and the
|
|
34
|
+
underline on hover.
|
|
35
|
+
|
|
36
|
+
Underline on hover rather than at rest, which is where this started and was
|
|
37
|
+
wrong: it borrowed LinkSlot's always-on rule, but a pill is not a run of text
|
|
38
|
+
in a paragraph. Its own shape already separates it from what is around it, so
|
|
39
|
+
a resting underline reads as a mistake rather than as a link — the same
|
|
40
|
+
treatment a table cell link gets, where colour and weight carry it and the
|
|
41
|
+
underline is the hover affordance. */
|
|
36
42
|
.rsui-category-list__link {
|
|
37
43
|
@apply block rounded-full no-underline;
|
|
38
44
|
@apply focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-border-primary outline-hidden;
|
|
39
45
|
}
|
|
40
46
|
|
|
41
47
|
.rsui-category-list__link .rsui-badge {
|
|
42
|
-
@apply cursor-pointer underline
|
|
48
|
+
@apply cursor-pointer no-underline transition-colors;
|
|
49
|
+
@apply hover:underline underline-offset-2;
|
|
43
50
|
}
|
|
44
51
|
|
|
45
52
|
/* One grey step darker, not the usual `hover:bg-background-secondary`. That is the
|
|
@@ -56,8 +63,15 @@
|
|
|
56
63
|
|
|
57
64
|
Sibling to the list rather than inside it, so it wraps onto its own line at
|
|
58
65
|
phone widths — which is where the cap does most of its work. */
|
|
66
|
+
/* min-width, because the label changes length as it toggles: "+4 more" is 57px
|
|
67
|
+
and "Show fewer" is 80px, so without a floor the whole row shifted 23px
|
|
68
|
+
sideways under the pointer mid-press. Measured. The value is the wider of the
|
|
69
|
+
two states, so the button never grows and never shrinks; text-align keeps the
|
|
70
|
+
shorter label from drifting left inside it.
|
|
71
|
+
|
|
72
|
+
Same fix the page header's meta toggle already carries, for the same reason. */
|
|
59
73
|
.rsui-category-list__more {
|
|
60
|
-
@apply inline-flex w-fit items-center whitespace-nowrap;
|
|
74
|
+
@apply inline-flex w-fit min-w-20 justify-center items-center whitespace-nowrap;
|
|
61
75
|
@apply text-sm font-semibold no-underline cursor-pointer transition-colors;
|
|
62
76
|
@apply text-button-tertiary hover:text-button-tertiary-hover;
|
|
63
77
|
@apply hover:underline underline-offset-4 decoration-2;
|
|
@@ -1,5 +1,23 @@
|
|
|
1
|
+
/* ── A form field breathes at 8px, not 4px ──────────────────────────────────────────
|
|
2
|
+
Two nested gaps make a field: this one, label -> control, and __field's below it,
|
|
3
|
+
control -> help. Both were space-xxs (4px), which is the TIGHT PAIR value.
|
|
4
|
+
|
|
5
|
+
A tight pair is two lines of TEXT where line-height already contributes half-leading
|
|
6
|
+
at both ends, so 4px is all the visible separation the pair can take (see the note
|
|
7
|
+
above .rsui-card-header__subtitle in card_header.css). Neither of these gaps is that
|
|
8
|
+
shape. A 14px label sits above a 50px control, not above another line of text, and
|
|
9
|
+
there is no leading between them to absorb — so 4px renders as 4px and the label
|
|
10
|
+
reads as welded to the box.
|
|
11
|
+
|
|
12
|
+
8px is the brand's own label -> value step and the smallest value on its 8px rhythm.
|
|
13
|
+
It is also what the PRODUCT independently arrived at: redseed's Styles/formfield_slot.css
|
|
14
|
+
reimplements this component with `gap-y-2`, exactly 8px, which is a second opinion
|
|
15
|
+
reached without reference to this one.
|
|
16
|
+
|
|
17
|
+
The radio-group override below keeps its own 12px and is unaffected. --compact is a
|
|
18
|
+
flex ROW and does not wrap, so a row-gap never applies to it. */
|
|
1
19
|
.rsui-form-field-slot {
|
|
2
|
-
@apply flex flex-col gap-y-space-
|
|
20
|
+
@apply flex flex-col gap-y-space-xs border-0 bg-transparent p-0;
|
|
3
21
|
}
|
|
4
22
|
|
|
5
23
|
.rsui-form-field-slot.rsui-form-field-radio-group {
|
|
@@ -26,8 +44,10 @@
|
|
|
26
44
|
@apply text-base font-semibold;
|
|
27
45
|
}
|
|
28
46
|
|
|
47
|
+
/* The second half of the pair above: control -> help. This is the gap that made help
|
|
48
|
+
text look attached to the input. Same 4px -> 8px, same reasoning. */
|
|
29
49
|
.rsui-form-field-slot__field {
|
|
30
|
-
@apply shrink-0 flex flex-col gap-y-space-
|
|
50
|
+
@apply shrink-0 flex flex-col gap-y-space-xs;
|
|
31
51
|
}
|
|
32
52
|
|
|
33
53
|
.rsui-form-field-slot__help {
|
|
@@ -2,8 +2,19 @@
|
|
|
2
2
|
@apply text-base text-text-primary font-semibold;
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
+
/* ── Between whole fields: 16px ─────────────────────────────────────────────────────
|
|
6
|
+
This was space-sm (12px), which is not a value on the brand's 8px rhythm at all —
|
|
7
|
+
the rhythm runs 8, 16, 24, 32, 48, 64, 96.
|
|
8
|
+
|
|
9
|
+
16px is also what the other form context already uses: FormSlot spaces a form's
|
|
10
|
+
children with mt-space-md. Those two were the only places in the library that space
|
|
11
|
+
stacked fields, and they disagreed. Now they do not.
|
|
12
|
+
|
|
13
|
+
The gap between two fields has to stay clearly larger than the 8px inside one, or a
|
|
14
|
+
field stops reading as a unit — 16 against 8 is the same 1:2 grouping the card meta
|
|
15
|
+
uses. */
|
|
5
16
|
.rsui-form-fieldset__fields {
|
|
6
|
-
@apply flex flex-col gap-space-
|
|
17
|
+
@apply flex flex-col gap-space-md;
|
|
7
18
|
}
|
|
8
19
|
|
|
9
20
|
.rsui-form-fieldset__fields--padded {
|
|
@@ -167,7 +167,8 @@
|
|
|
167
167
|
on it — and the alternative is inventing a second readable ink per tone to
|
|
168
168
|
preserve a nicety. tone-light keeps its semantic colour, so it keeps the split. */
|
|
169
169
|
.rsui-metric-card--tone-green .rsui-metric-card__comparison-context,
|
|
170
|
-
.rsui-metric-card--tone-teal .rsui-metric-card__comparison-context
|
|
170
|
+
.rsui-metric-card--tone-teal .rsui-metric-card__comparison-context,
|
|
171
|
+
.rsui-metric-card--tone-bloom .rsui-metric-card__comparison-context {
|
|
171
172
|
color: var(--rsui-metric-card-ink-muted);
|
|
172
173
|
}
|
|
173
174
|
|
|
@@ -247,6 +248,43 @@
|
|
|
247
248
|
--rsui-metric-card-rule: rgba(255, 255, 255, 0.18);
|
|
248
249
|
}
|
|
249
250
|
|
|
251
|
+
/* For a MetricCard sitting on PageHeader's bloom band, and the one tone whose surface is
|
|
252
|
+
NOT opaque.
|
|
253
|
+
|
|
254
|
+
This is not the header's own stats strip — that is PageHeaderStat, a 150px tile with a
|
|
255
|
+
serif figure, and the two are different objects. This tone is for the case where a full
|
|
256
|
+
MetricCard, with its comparison and trend, is the content sitting on a bloom surface.
|
|
257
|
+
|
|
258
|
+
The band it sits on runs Journey Green at one end to Warm Mauve at the other, so an
|
|
259
|
+
opaque tile is the wrong colour at one end of its own row whichever step it takes.
|
|
260
|
+
Warm Mauve 900 was tried first and reads as a mauve panel dropped on the green end.
|
|
261
|
+
A white scrim instead lets the band through, so each tile picks up the part of the
|
|
262
|
+
sweep it is standing on and the row reads as one thing.
|
|
263
|
+
|
|
264
|
+
THE COST, MEASURED, AND WHAT IT DECIDES. A scrim can only lighten, so the tile is
|
|
265
|
+
always lighter than the band beneath it, and the band's lightest point is already the
|
|
266
|
+
tightest place on it. Compositing 9% white over that point — rgb(133, 107, 129):
|
|
267
|
+
|
|
268
|
+
white 4.77:1 PASS
|
|
269
|
+
Grey 200 3.97:1 fails
|
|
270
|
+
Grey 300 3.34:1 fails
|
|
271
|
+
|
|
272
|
+
So this is the one tone with NO muted ink: --rsui-metric-card-ink-muted is white too,
|
|
273
|
+
and the step between a tile's number and its label is carried by size and weight, which
|
|
274
|
+
is what the design does anyway. Nothing darker than white clears AA through a scrim on
|
|
275
|
+
this band, so a muted colour here would be a quiet failure rather than a choice.
|
|
276
|
+
|
|
277
|
+
9% is the ceiling, not a taste value: 12% takes white itself to 4.46:1. A hoverable
|
|
278
|
+
tile is safe because Card's hover scrim is DARK — it moves the tile away from the
|
|
279
|
+
limit, not towards it. Raise this figure and white goes under; re-measure rather than
|
|
280
|
+
nudge it. */
|
|
281
|
+
.rsui-metric-card--tone-bloom {
|
|
282
|
+
--rsui-metric-card-surface: rgba(255, 255, 255, 0.09);
|
|
283
|
+
--rsui-metric-card-ink: var(--Colors-Base-white);
|
|
284
|
+
--rsui-metric-card-ink-muted: var(--Colors-Base-white);
|
|
285
|
+
--rsui-metric-card-rule: rgba(255, 255, 255, 0.18);
|
|
286
|
+
}
|
|
287
|
+
|
|
250
288
|
/* The white-label case: same layout, inverted ink. */
|
|
251
289
|
.rsui-metric-card--tone-light {
|
|
252
290
|
--rsui-metric-card-surface: var(--Colors-Modern-Bone-200);
|
|
@@ -273,17 +311,23 @@
|
|
|
273
311
|
color: var(--rsui-metric-card-ink-muted);
|
|
274
312
|
}
|
|
275
313
|
|
|
276
|
-
/* The semantic status inks are unreadable on the
|
|
314
|
+
/* The semantic status inks are unreadable on the DARK tiles, measured:
|
|
315
|
+
|
|
316
|
+
ink tone-green tone-teal tone-bloom
|
|
317
|
+
#0e5942 #0f373c (scrim)
|
|
318
|
+
--color-text-success #067647 1.46:1 2.26:1 1.20:1
|
|
319
|
+
--color-text-error #b42318 1.26:1 1.96:1 1.38:1
|
|
277
320
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
--color-text-error #b42318 1.26:1 1.96:1
|
|
321
|
+
tone-bloom is measured on the lightest tile the scrim can produce,
|
|
322
|
+
rgb(133, 107, 129) — the band's lightest point seen through 9% white.
|
|
281
323
|
|
|
282
|
-
|
|
324
|
+
All invisible — the same failure this file already documents for text-brand
|
|
283
325
|
on a toned tile. So the comparison gives up its colour there and takes the
|
|
284
326
|
tile's muted ink, exactly as the active state gives up its brand number, and
|
|
285
327
|
the arrow direction plus the consumer's word carry the meaning on their own.
|
|
286
|
-
Journey Green 200 measures 5.92:1 and Deep Teal 300 6.36:1, both clearing AA
|
|
328
|
+
Journey Green 200 measures 5.92:1 and Deep Teal 300 6.36:1, both clearing AA;
|
|
329
|
+
on tone-bloom the muted ink IS white, for the reason that tone documents, so
|
|
330
|
+
the comparison lands on white at 4.75:1 there.
|
|
287
331
|
|
|
288
332
|
tone-light is deliberately NOT in this list. Its surface is Modern Bone 200
|
|
289
333
|
(#f9f8f6), where success measures 5.36:1 and error 6.19:1 — both pass AA, so
|
|
@@ -291,21 +335,25 @@
|
|
|
291
335
|
|
|
292
336
|
(0,2,0), so it outranks the (0,1,0) sentiment modifiers wherever they sit. */
|
|
293
337
|
.rsui-metric-card--tone-green .rsui-metric-card__comparison,
|
|
294
|
-
.rsui-metric-card--tone-teal .rsui-metric-card__comparison
|
|
338
|
+
.rsui-metric-card--tone-teal .rsui-metric-card__comparison,
|
|
339
|
+
.rsui-metric-card--tone-bloom .rsui-metric-card__comparison {
|
|
295
340
|
color: var(--rsui-metric-card-ink-muted);
|
|
296
341
|
}
|
|
297
342
|
|
|
298
343
|
/* The solid badge goes with it. Filling it with the error ink on a dark tile would
|
|
299
344
|
reproduce exactly the 1.26:1 the rule above exists to avoid, so it takes the
|
|
300
345
|
tile's muted ink and puts the arrow in the tile's own surface colour — the
|
|
301
|
-
same 5.92:1
|
|
346
|
+
same 5.92:1 / 6.36:1 pairing, read the other way round. On tone-bloom the
|
|
347
|
+
surface is a scrim rather than a colour, so the knocked-out arrow shows the
|
|
348
|
+
band through it, which is the same effect one step further out.
|
|
302
349
|
|
|
303
350
|
The ringed badge needs no rule of its own: its border is `currentColor`, and the
|
|
304
351
|
rule above has already moved the comparison's ink to the tile's muted value, so
|
|
305
352
|
the ring follows it. This is the tone where solid-vs-ringed does its real work,
|
|
306
353
|
because once the colour is stripped it is the only sentiment signal left. */
|
|
307
354
|
.rsui-metric-card--tone-green .rsui-metric-card__comparison--negative .rsui-metric-card__comparison-icon,
|
|
308
|
-
.rsui-metric-card--tone-teal .rsui-metric-card__comparison--negative .rsui-metric-card__comparison-icon
|
|
355
|
+
.rsui-metric-card--tone-teal .rsui-metric-card__comparison--negative .rsui-metric-card__comparison-icon,
|
|
356
|
+
.rsui-metric-card--tone-bloom .rsui-metric-card__comparison--negative .rsui-metric-card__comparison-icon {
|
|
309
357
|
background-color: var(--rsui-metric-card-ink-muted);
|
|
310
358
|
color: var(--rsui-metric-card-surface);
|
|
311
359
|
}
|
|
@@ -62,6 +62,27 @@
|
|
|
62
62
|
@apply lg:text-[2.5rem] lg:leading-[1.15] lg:tracking-[-0.01em] lg:font-black;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
/* An eyebrow above the h1, matching card_header.css's: xs, semibold, uppercase, wide
|
|
66
|
+
tracking, in the secondary ink so it labels the title rather than competing with it.
|
|
67
|
+
The two are deliberately the same recipe — an eyebrow means the same thing on a page
|
|
68
|
+
as it does on a card, and a second type style for it would be a second answer to one
|
|
69
|
+
question.
|
|
70
|
+
|
|
71
|
+
8px down to the title, the brand's smallest step, carried as a margin on this element
|
|
72
|
+
rather than as a gap on the parent so an absent eyebrow contributes nothing. Same
|
|
73
|
+
reason card_header.css gives. */
|
|
74
|
+
.rsui-page-header__eyebrow {
|
|
75
|
+
@apply flex items-center gap-x-space-xs mb-space-xs;
|
|
76
|
+
@apply text-xs font-semibold uppercase tracking-wider text-text-secondary;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* A badge brings its own case, size and tracking, so it must not inherit the uppercase
|
|
80
|
+
treatment meant for plain text. Badge sets no text-transform of its own, so without
|
|
81
|
+
this the uppercase leaks in. */
|
|
82
|
+
.rsui-page-header__eyebrow .rsui-badge {
|
|
83
|
+
@apply normal-case tracking-normal;
|
|
84
|
+
}
|
|
85
|
+
|
|
65
86
|
.rsui-page-header__title-text-container {
|
|
66
87
|
@apply flex flex-row gap-x-space-lg items-center;
|
|
67
88
|
}
|
|
@@ -75,6 +96,110 @@
|
|
|
75
96
|
@apply text-base leading-normal text-text-secondary;
|
|
76
97
|
}
|
|
77
98
|
|
|
99
|
+
/* ── The stats row ────────────────────────────────────────────────────────────
|
|
100
|
+
Ported from the /home/v2 twin rather than re-derived, because that band is the
|
|
101
|
+
thing this is meant to reproduce and it had already been worked out at every
|
|
102
|
+
width. Every number below is its number.
|
|
103
|
+
|
|
104
|
+
A wrapping row rather than a scrolling one: these are a handful of counts, and
|
|
105
|
+
a count you cannot see is not a summary.
|
|
106
|
+
|
|
107
|
+
Full width below lg, where __top is a column and the tiles sit under the title;
|
|
108
|
+
auto from lg up, where __top is a row and they sit beside it.
|
|
109
|
+
--------------------------------------------------------------------------- */
|
|
110
|
+
.rsui-page-header__stats {
|
|
111
|
+
/* stretch, not start: a two-line label must not leave its tile shorter than
|
|
112
|
+
the one beside it. */
|
|
113
|
+
@apply w-full flex items-stretch gap-[10px];
|
|
114
|
+
@apply lg:w-auto lg:shrink-0;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/* The band carries four things in one row at desktop width and needs two rows
|
|
118
|
+
before that. The twin wrapped at 1280 rather than at 1024 because the three
|
|
119
|
+
counts plus a trailing action need about 600px, which squeezes the title hard
|
|
120
|
+
long before the layout actually breaks. */
|
|
121
|
+
.rsui-page-header__top:has(.rsui-page-header__stats) {
|
|
122
|
+
@apply flex-wrap gap-[18px] lg:gap-[20px] xl:gap-[40px];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/* The title takes the rest of the row and the tiles keep their size, rather than
|
|
126
|
+
the two sharing it. min-width: 0 so a long title wraps instead of forcing the
|
|
127
|
+
tiles out of the row. */
|
|
128
|
+
.rsui-page-header__top:has(.rsui-page-header__stats) .rsui-page-header__title {
|
|
129
|
+
@apply lg:grow lg:min-w-0 lg:basis-full xl:basis-auto;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* One tile. 150px is the twin's width, at which its three labels sit on one line.
|
|
133
|
+
|
|
134
|
+
The leading edge is reserved whether or not it is painted, so a tile carrying a
|
|
135
|
+
rail and one without still line up — the mistake worth avoiding here is adding
|
|
136
|
+
the border only on the accented tile and knocking that tile 3px out of the row. */
|
|
137
|
+
.rsui-page-header__stat {
|
|
138
|
+
@apply flex flex-col items-start gap-px;
|
|
139
|
+
@apply w-full px-[16px] py-[12px] text-left;
|
|
140
|
+
@apply border-0 border-s-[3px] border-solid border-s-transparent rounded-[10px];
|
|
141
|
+
@apply lg:w-[150px];
|
|
142
|
+
|
|
143
|
+
background: var(--rsui-page-header-tile, var(--color-background-secondary));
|
|
144
|
+
border-inline-start-color: var(--rsui-page-header-stat-accent, transparent);
|
|
145
|
+
font: inherit;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* Below lg the row is full width, so the tiles share it rather than sitting at a
|
|
149
|
+
fixed size with the rest of the row empty beside them. */
|
|
150
|
+
.rsui-page-header__stats > .rsui-page-header__stat {
|
|
151
|
+
@apply flex-1 lg:flex-none;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* Only a tile that DOES something gets the pointer and the hover. A stat that
|
|
155
|
+
only reports a number is not a control, and PageHeaderStat renders it as a div
|
|
156
|
+
precisely so it does not pretend to be one. */
|
|
157
|
+
.rsui-page-header__stat--interactive {
|
|
158
|
+
@apply cursor-pointer no-underline transition-colors duration-150;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/* Hover DARKENS on the dark tones rather than lightening, which is the opposite of the
|
|
162
|
+
twin and deliberate. Lifting a scrim from 9% to 16% takes white itself to 4.10:1 on the
|
|
163
|
+
band's lightest point — the hover state would be the one that fails. Darkening moves
|
|
164
|
+
every ink on the tile away from its limit instead, and still reads as a state change.
|
|
165
|
+
tone-light darkens too, through its own solid step. */
|
|
166
|
+
.rsui-page-header__stat--interactive:hover {
|
|
167
|
+
background: var(--rsui-page-header-tile-hover, var(--color-background-secondary-contrast));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/* The figure is the one place the serif is right at this size: it is a number
|
|
171
|
+
being read as a headline, which is the range the brand keeps Newsreader for.
|
|
172
|
+
tabular-nums so a row of counts does not shift as the numbers change. */
|
|
173
|
+
.rsui-page-header__stat-value {
|
|
174
|
+
@apply font-redseed-heading text-[2.875rem] font-semibold leading-none tabular-nums;
|
|
175
|
+
|
|
176
|
+
color: var(--rsui-page-header-ink, var(--color-text-primary));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/* The label takes the tone's INK rather than its muted ink, and the step down to it is
|
|
180
|
+
carried by size and weight — 46px semibold against 12px medium is already a long way.
|
|
181
|
+
|
|
182
|
+
That is a contrast result, not a preference. A tile is a scrim, so it is always lighter
|
|
183
|
+
than the band under it, and a muted ink measured against the BAND does not survive the
|
|
184
|
+
tile. Composited over each tone's own worst ground:
|
|
185
|
+
|
|
186
|
+
tone tile ground its muted ink white
|
|
187
|
+
green rgb(32, 92, 74) JourneyGreen300 4.18 7.79
|
|
188
|
+
teal rgb(34, 67, 71) DeepTeal300 5.32 10.75
|
|
189
|
+
bloom rgb(133, 107, 129) Grey 200 3.97 4.77
|
|
190
|
+
|
|
191
|
+
Two of the three fail, and bloom's is the band's lightest point — reachable, because a
|
|
192
|
+
header with no actions lets the title grow and pushes the tiles to the right edge, which
|
|
193
|
+
is exactly where that point is. In situ at 1440 with an action present the same tile
|
|
194
|
+
measures 6.83:1, but a library component does not get to assume the layout it is given.
|
|
195
|
+
|
|
196
|
+
tone-light resolves this to Grey 900 through the same rule, at 16.08:1 on its tile. */
|
|
197
|
+
.rsui-page-header__stat-label {
|
|
198
|
+
@apply text-xs font-medium leading-[1.3];
|
|
199
|
+
|
|
200
|
+
color: var(--rsui-page-header-ink, var(--color-text-primary));
|
|
201
|
+
}
|
|
202
|
+
|
|
78
203
|
.rsui-page-header__status-actions {
|
|
79
204
|
@apply flex-1 flex gap-x-space-xs items-center justify-between lg:pr-space-xs;
|
|
80
205
|
}
|
|
@@ -151,11 +276,36 @@
|
|
|
151
276
|
@apply p-space-lg;
|
|
152
277
|
}
|
|
153
278
|
|
|
279
|
+
/* The band reaches the viewport edges; its text does not. calc(50% - 50vw) pulls each
|
|
280
|
+
side out to the edge and the matching padding puts the content back on the container's
|
|
281
|
+
inner edge, so the greeting lines up with the logo and nav above it.
|
|
282
|
+
|
|
283
|
+
Measured in JS first, which is the wrong answer: a measured value goes stale whenever
|
|
284
|
+
the element resizes without the trigger you chose firing, and the band silently insets
|
|
285
|
+
itself again. CSS cannot go stale.
|
|
286
|
+
|
|
287
|
+
The vertical padding is the toned header's own and is not touched here, so this is
|
|
288
|
+
purely a horizontal escape. See the prop for the `overflow-x: clip` the document has to
|
|
289
|
+
carry. */
|
|
290
|
+
.rsui-page-header--bleed {
|
|
291
|
+
/* width: auto is load-bearing. The base rule sets w-full, and a box with an explicit
|
|
292
|
+
width does not grow when its margins go negative — it just shifts. With w-full held
|
|
293
|
+
the band moved left to the viewport edge and stopped short on the right by exactly
|
|
294
|
+
the amount it had moved, which looks like a one-sided bug and is not one. */
|
|
295
|
+
@apply w-auto;
|
|
296
|
+
|
|
297
|
+
margin-inline: calc(50% - 50vw);
|
|
298
|
+
padding-inline: calc(50vw - 50%);
|
|
299
|
+
}
|
|
300
|
+
|
|
154
301
|
.rsui-page-header--tone-green {
|
|
155
302
|
--rsui-page-header-bg: var(--Colors-Journey-Green-800);
|
|
156
303
|
--rsui-page-header-ink: var(--Colors-Base-white);
|
|
157
304
|
--rsui-page-header-ink-muted: var(--Colors-Journey-Green-300);
|
|
158
305
|
--rsui-page-header-rule: rgba(255, 255, 255, 0.18);
|
|
306
|
+
--rsui-page-header-accent: var(--Colors-Warm-Yellow-400);
|
|
307
|
+
--rsui-page-header-tile: rgba(255, 255, 255, 0.09);
|
|
308
|
+
--rsui-page-header-tile-hover: rgba(0, 0, 0, 0.12);
|
|
159
309
|
}
|
|
160
310
|
|
|
161
311
|
.rsui-page-header--tone-teal {
|
|
@@ -163,6 +313,60 @@
|
|
|
163
313
|
--rsui-page-header-ink: var(--Colors-Base-white);
|
|
164
314
|
--rsui-page-header-ink-muted: var(--Colors-Deep-Teal-300);
|
|
165
315
|
--rsui-page-header-rule: rgba(255, 255, 255, 0.18);
|
|
316
|
+
--rsui-page-header-accent: var(--Colors-Warm-Yellow-400);
|
|
317
|
+
--rsui-page-header-tile: rgba(255, 255, 255, 0.09);
|
|
318
|
+
--rsui-page-header-tile-hover: rgba(0, 0, 0, 0.12);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/* The brand's hero treatment: three soft radial washes over a green-to-mauve
|
|
322
|
+
sweep, so the band warms across its width instead of running dark green end to
|
|
323
|
+
end. Every colour is a primary identity ramp — Journey Green and Deep Teal
|
|
324
|
+
carry the base, Warm Yellow / Soft Lilac / Warm Mauve are the washes. No
|
|
325
|
+
secondary accents.
|
|
326
|
+
|
|
327
|
+
A TONE rather than a boolean layered over green or teal. The alphas below are
|
|
328
|
+
a contrast budget measured against THIS base sweep; put the same washes over a
|
|
329
|
+
different background and the measurement is void, so there is nothing to
|
|
330
|
+
compose with.
|
|
331
|
+
|
|
332
|
+
THE ALPHAS ARE MEASURED, NOT CHOSEN. All four layers were composited on a
|
|
333
|
+
200x200 grid across eight band sizes from 1920x150 down to 375x320, and the
|
|
334
|
+
lightest point anywhere in the band taken as the worst case — the right test
|
|
335
|
+
for a library component, where a consumer may put text anywhere along the
|
|
336
|
+
band, rather than the worst point under one particular text box.
|
|
337
|
+
|
|
338
|
+
The lightest point is the Soft Lilac centre at 96% 30%, where the washes
|
|
339
|
+
combine to alpha 0.320 and the surface resolves to rgb(121, 92, 117).
|
|
340
|
+
Against it:
|
|
341
|
+
|
|
342
|
+
Base white 5.82:1 PASS
|
|
343
|
+
Grey 200 4.84:1 PASS <- the muted ink below
|
|
344
|
+
Deep Teal 200 4.00:1 large text only
|
|
345
|
+
Journey Green 300 3.12:1 large text only
|
|
346
|
+
|
|
347
|
+
Which is why the muted ink is NOT Journey Green 300, the muted step the green
|
|
348
|
+
tone uses: it fails AA at the lightest point of this band. Move a centre or
|
|
349
|
+
raise an alpha and this has to be measured again; do not eyeball it. */
|
|
350
|
+
.rsui-page-header--tone-bloom {
|
|
351
|
+
--rsui-page-header-bg:
|
|
352
|
+
/* Warm Yellow 500 — above the band, left of centre where the base is greenest */
|
|
353
|
+
radial-gradient(50% 130% at 44% -20%, rgba(238, 220, 91, 0.34) 0%, rgba(238, 220, 91, 0) 62%),
|
|
354
|
+
/* Soft Lilac 500 — the right edge, the pink */
|
|
355
|
+
radial-gradient(55% 130% at 96% 30%, rgba(233, 184, 222, 0.32) 0%, rgba(233, 184, 222, 0) 62%),
|
|
356
|
+
/* Warm Mauve 500 — below the band, deepening the purple end */
|
|
357
|
+
radial-gradient(70% 125% at 88% 112%, rgba(185, 125, 168, 0.38) 0%, rgba(185, 125, 168, 0) 65%),
|
|
358
|
+
/* base sweep: Journey Green 800 -> Deep Teal 700 -> Warm Mauve 950 */
|
|
359
|
+
linear-gradient(115deg, var(--Colors-Journey-Green-800) 0%, var(--Colors-Deep-Teal-700) 42%, var(--Colors-Warm-Mauve-950) 100%);
|
|
360
|
+
--rsui-page-header-ink: var(--Colors-Base-white);
|
|
361
|
+
--rsui-page-header-ink-muted: var(--Colors-Grey-200);
|
|
362
|
+
--rsui-page-header-rule: rgba(255, 255, 255, 0.18);
|
|
363
|
+
/* Warm Yellow 400, the twin's accent: 5.37:1 on this band. */
|
|
364
|
+
--rsui-page-header-accent: var(--Colors-Warm-Yellow-400);
|
|
365
|
+
/* A scrim rather than a colour, so each tile picks up the part of the sweep
|
|
366
|
+
it is standing on and a row of them reads as one thing rather than as
|
|
367
|
+
panels dropped on a band. */
|
|
368
|
+
--rsui-page-header-tile: rgba(255, 255, 255, 0.09);
|
|
369
|
+
--rsui-page-header-tile-hover: rgba(0, 0, 0, 0.12);
|
|
166
370
|
}
|
|
167
371
|
|
|
168
372
|
/* The white-label case. Same layout, inverted ink — a client's own background
|
|
@@ -172,6 +376,10 @@
|
|
|
172
376
|
--rsui-page-header-ink: var(--Colors-Grey-900);
|
|
173
377
|
--rsui-page-header-ink-muted: var(--Colors-Grey-600);
|
|
174
378
|
--rsui-page-header-rule: var(--Colors-Grey-300);
|
|
379
|
+
/* A scrim is invisible on a light band, so this tone names solid steps. */
|
|
380
|
+
--rsui-page-header-accent: var(--Colors-Journey-Green-700);
|
|
381
|
+
--rsui-page-header-tile: var(--Colors-Modern-Bone-200);
|
|
382
|
+
--rsui-page-header-tile-hover: var(--Colors-Grey-200);
|
|
175
383
|
}
|
|
176
384
|
|
|
177
385
|
/* Ink application is tone-agnostic: only the variable values differ, so the
|
|
@@ -184,6 +392,53 @@
|
|
|
184
392
|
color: var(--rsui-page-header-ink-muted);
|
|
185
393
|
}
|
|
186
394
|
|
|
395
|
+
/* The eyebrow takes the muted ink for the same reason the subtitle does: it is
|
|
396
|
+
the quieter of the two lines around the title, and text-text-secondary is a
|
|
397
|
+
dark grey that disappears on every tone but light. */
|
|
398
|
+
.rsui-page-header--toned .rsui-page-header__eyebrow {
|
|
399
|
+
color: var(--rsui-page-header-ink-muted);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/* ── The band's type ──────────────────────────────────────────────────────────
|
|
403
|
+
Ported from the /home/v2 twin, which is the design this tone reproduces.
|
|
404
|
+
|
|
405
|
+
A DELIBERATE DEPARTURE from the h1 rule at the top of this file, and worth a
|
|
406
|
+
reviewer's eye rather than a quiet landing. That rule drops Newsreader for the
|
|
407
|
+
sans steps, citing the book's restriction of Newsreader to Header 1 and its
|
|
408
|
+
own sizes — and it is right for a page title. This is not a page title; it is
|
|
409
|
+
a hero band, the same surface the marketing site sets in Newsreader, and it
|
|
410
|
+
carries a greeting rather than the name of a record. The departure is scoped
|
|
411
|
+
to this one tone, so every other header keeps the sans title.
|
|
412
|
+
|
|
413
|
+
27px below lg, 36px from lg up — the twin's two sizes.
|
|
414
|
+
--------------------------------------------------------------------------- */
|
|
415
|
+
.rsui-page-header--tone-bloom .rsui-page-header__title h1 {
|
|
416
|
+
@apply font-redseed-heading text-[1.6875rem] font-semibold;
|
|
417
|
+
@apply leading-[1.1] tracking-[-0.01em];
|
|
418
|
+
@apply lg:text-[2.25rem] lg:leading-[1.1] lg:font-semibold;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/* The eyebrow sits tight to the greeting — 3px, not the 8px a card eyebrow takes
|
|
422
|
+
— because the two read as one block of address here rather than as a label and
|
|
423
|
+
the thing it labels. */
|
|
424
|
+
.rsui-page-header--tone-bloom .rsui-page-header__eyebrow {
|
|
425
|
+
@apply mb-[3px] tracking-[0.06em];
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/* And the line under the title sits away from it, at 7px and one step down in
|
|
429
|
+
size: it is a second sentence, not a subtitle wrapping the first. */
|
|
430
|
+
.rsui-page-header--tone-bloom .rsui-page-header__subtitle {
|
|
431
|
+
@apply mt-[7px] text-sm;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/* The one word in that line that is the point of it. Available on every tone,
|
|
435
|
+
because a band with a highlight in it is not specific to this one. */
|
|
436
|
+
.rsui-page-header--toned .rsui-page-header__subtitle strong {
|
|
437
|
+
@apply font-semibold;
|
|
438
|
+
|
|
439
|
+
color: var(--rsui-page-header-accent, var(--rsui-page-header-ink));
|
|
440
|
+
}
|
|
441
|
+
|
|
187
442
|
.rsui-page-header--toned .rsui-page-header__meta {
|
|
188
443
|
border-top: 1px solid var(--rsui-page-header-rule);
|
|
189
444
|
|
package/package.json
CHANGED
package/tokens.css
CHANGED
|
@@ -11,7 +11,14 @@
|
|
|
11
11
|
--color-text-success: #067647;
|
|
12
12
|
--color-text-warning: #b54708;
|
|
13
13
|
--color-text-error: #b42318;
|
|
14
|
-
--color-
|
|
14
|
+
/* #0077cc measured 4.46:1 on its own fill (--color-background-info, #f6fbfd),
|
|
15
|
+
just under the 4.5 AA threshold for body text. #0070c0 is the smallest step
|
|
16
|
+
that clears it — 4.94:1 — so the info blue moves as little as it can while
|
|
17
|
+
being legible. Every other ground it sits on improves too: 4.66 -> 5.15 on
|
|
18
|
+
white.
|
|
19
|
+
--color-background-info-contrast keeps #0077cc deliberately: it is a
|
|
20
|
+
BACKGROUND, so darkening it would change surfaces rather than fix text. */
|
|
21
|
+
--color-text-info: #0070c0;
|
|
15
22
|
/* Warm-Mauve-800, not -600: the AI treatments set this over the pale AI gradient
|
|
16
23
|
(#ebe0fa -> #fff6eb), where -600 measured 2.93:1 and failed WCAG AA for 14px text.
|
|
17
24
|
-800 clears it at 4.52:1 at the gradient's worst point. Consumed by Badge AI,
|