@redseed/redseed-ui-tailwindcss 7.67.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.
@@ -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 the library's always-on underline (the LinkSlot
34
- convention) plus a hover state on the pill itself. Half-measures are the
35
- failure here: a pointer alone is not a visible affordance. */
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 underline-offset-2 transition-colors;
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;
@@ -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 two DARK tiles, measured:
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
- ink on tone-green #0e5942 on tone-teal #0f373c
279
- --color-text-success #067647 1.46:1 2.26:1
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
- Both invisible — the same failure this file already documents for text-brand
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 and 6.36:1 pairing, read the other way round.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redseed/redseed-ui-tailwindcss",
3
- "version": "7.67.0",
3
+ "version": "7.68.0",
4
4
  "description": "RedSeed UI Tailwindcss",
5
5
  "main": "index.js",
6
6
  "style": "index.css",
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-text-info: #0077cc;
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,