@apliteni/apliteni-ui 0.8.1 → 0.9.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apliteni/apliteni-ui",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "workspaces": [
5
5
  "react"
6
6
  ],
@@ -235,6 +235,14 @@ export function toast({
235
235
  + `<div class="ui-toast__body">${title ? `<div class="ui-toast__title">${esc(title)}</div>` : ''}${bodyHtml}</div>`
236
236
  + `${actBtn}${closeBtn}${timerBar}</div>`;
237
237
  }
238
+ // The inline confirmation. A check, a title and one line of sub, dropped into
239
+ // the page the user is already on — under a form that just submitted, or inside
240
+ // a card. Two strings, no options; styles are .ui-success in styles/callout.css.
241
+ //
242
+ // The kit also publishes success() from components/success.js, which is the same
243
+ // idea at page size: layouts, a backdrop, follow-up buttons and an auto-redirect
244
+ // countdown. Pick by how much of the screen the confirmation owns. The two share
245
+ // no CSS, so changing one never moves the other.
238
246
  export function successPanel({ title = 'Done', sub = '' } = {}) {
239
247
  return `<div class="ui-success"><div class="ui-success__check">${icon('check')}</div><div class="ui-success__title">${esc(title)}</div>${sub ? `<div class="ui-success__sub">${esc(sub)}</div>` : ''}</div>`;
240
248
  }
@@ -61,6 +61,16 @@ function countdownEl({ seconds = 5, label = 'Redirecting' } = {}) {
61
61
  </div>`;
62
62
  }
63
63
 
64
+ // The page-sized confirmation — what a flow lands on once it is over. It fills
65
+ // the space it is given, and carries the things a screen needs that a block does
66
+ // not: somewhere to go next, and optionally a countdown that takes the user
67
+ // there.
68
+ //
69
+ // For a confirmation that stays inside the page the user is already on, the kit
70
+ // publishes successPanel() from components/index.js — a check, a title and one
71
+ // line of sub, and nothing to configure. Pick by how much of the screen the
72
+ // confirmation owns. The two share no CSS (.ui-sx here, .ui-success there), so
73
+ // changing one never moves the other.
64
74
  export function success({
65
75
  layout = 'hero', // 'hero' | 'split' | 'compact'
66
76
  backdrop = 'aurora', // 'aurora' | 'glow' | 'flat'
@@ -7,10 +7,28 @@ import { wireDropdown } from './dropdown.js';
7
7
 
8
8
  const THEME_KEY = 'apliteni-strategy-theme';
9
9
 
10
- export function themeToggle() {
11
- // Single icon-only switch (sun in dark, moon in light). Icon pre-filled so it
12
- // renders before applyTheme() runs; aria-label carries the accessible name.
13
- return `<button class="toggle" data-theme-toggle aria-label="Toggle theme" title="Toggle light / dark"><span class="ic" data-theme-icon>${sun}</span></button>`;
10
+ // A stateful control in this kit reports the state it is IN, never the state a
11
+ // click would produce the same reading as segmented()'s aria-pressed, the
12
+ // Deck/Text switch's aria-current, and the accent chips. So: moon while dark,
13
+ // sun while light. Storybook's own toolbar toggle (.storybook/theme-toggle.jsx)
14
+ // reads the same way, and topbar stories render directly beneath it.
15
+ export const themeIcon = (t) => (t === 'light' ? sun : moon);
16
+
17
+ // The name carries both the state and what the click does, which is why there
18
+ // is no aria-pressed and no role="switch": dark and light are peers, not on and
19
+ // off, and an ARIA state on top of this name would announce the theme twice.
20
+ // The button is icon-only, so `title` is the only sighted reading of the same
21
+ // fact and carries the identical string — nothing for WCAG 2.5.3 to disagree
22
+ // with. Both are rewritten by applyTheme on every flip; a name that is right
23
+ // once and never again tells a screen-reader user nothing.
24
+ export const themeName = (t) =>
25
+ (t === 'light' ? 'Theme: Light. Switch to dark.' : 'Theme: Dark. Switch to light.');
26
+
27
+ export function themeToggle(theme = 'dark') {
28
+ // Single icon-only switch. Icon and name are pre-filled for `theme` so the
29
+ // first paint is already truthful; applyTheme() keeps them so afterwards.
30
+ const name = esc(themeName(theme));
31
+ return `<button class="toggle" data-theme-toggle aria-label="${name}" title="${name}"><span class="ic" data-theme-icon>${themeIcon(theme)}</span></button>`;
14
32
  }
15
33
 
16
34
  export function deckTextSwitch(active = 'deck') {
@@ -69,11 +87,14 @@ export function topbar({
69
87
  // ---- Behaviours (call once after markup mounts) --------------------------
70
88
  export function applyTheme(t, root = document.documentElement) {
71
89
  root.setAttribute('data-theme', t);
90
+ const name = themeName(t);
72
91
  root.querySelectorAll('[data-theme-toggle]').forEach((btn) => {
73
92
  const ic = btn.querySelector('[data-theme-icon]');
74
- const lbl = btn.querySelector('[data-theme-label]');
75
- if (ic) ic.innerHTML = t === 'dark' ? sun : moon;
76
- if (lbl) lbl.textContent = t === 'dark' ? 'Light' : 'Dark';
93
+ if (ic) ic.innerHTML = themeIcon(t);
94
+ // The announced name is the point of the control, so it moves with the
95
+ // theme like the glyph does.
96
+ btn.setAttribute('aria-label', name);
97
+ btn.setAttribute('title', name);
77
98
  });
78
99
  try { localStorage.setItem(THEME_KEY, t); } catch (e) { /* no-op */ }
79
100
  }
package/src/index.js CHANGED
@@ -7,8 +7,10 @@ export * from './components/nav.js';
7
7
  export * from './components/drawer.js';
8
8
  export * from './components/topbar.js';
9
9
  export * from './components/shell.js';
10
+ export * from './components/footer.js';
10
11
  export * from './components/feedback.js';
11
12
  export * from './components/toasts.js';
13
+ export * from './components/success.js';
12
14
  export * from './assets/icons.js';
13
15
  export * from './assets/brand.js';
14
16
  export * from './motion.js';
package/src/inline.js CHANGED
@@ -39,6 +39,7 @@ export const styles = {
39
39
  nav: read('styles/nav.css'),
40
40
  drawer: read('styles/drawer.css'),
41
41
  table: read('styles/table.css'),
42
+ empty: read('styles/empty.css'),
42
43
  callout: read('styles/callout.css'),
43
44
  code: read('styles/code.css'),
44
45
  topbar: topbarCss,
@@ -63,6 +64,7 @@ export const cssText = [
63
64
  styles.nav,
64
65
  styles.drawer,
65
66
  styles.table,
67
+ styles.empty,
66
68
  styles.callout,
67
69
  styles.code,
68
70
  styles.topbar,
@@ -24,7 +24,7 @@
24
24
  wash out and the ink has to deepen to clear AA, and that is a token decision. */
25
25
  .ui-badge--live { color: var(--chip-success-ink); background: var(--chip-success-fill); }
26
26
  .ui-badge--soon { color: var(--purple-mid); background: var(--glow-purple); }
27
- .ui-badge--info { color: var(--cyan); background: var(--glow-cyan); }
27
+ .ui-badge--info { color: var(--chip-info-ink); background: var(--chip-info-fill); }
28
28
  .ui-badge--warn { color: var(--chip-warn-ink); background: var(--chip-warn-fill); }
29
29
  .ui-badge--danger { color: var(--chip-danger-ink); background: var(--chip-danger-fill); }
30
30
  .ui-badge--archive { color: var(--muted); background: var(--surface); }
@@ -54,7 +54,10 @@
54
54
  padding: 4px 11px;
55
55
  font-weight: var(--weight-semibold);
56
56
  }
57
- .ui-pill--live { color: var(--green); background: var(--glow-green); }
57
+ /* The live pill is the same status paint as the live badge, so it takes the
58
+ same pair. On its own it was --green on --glow-green, which is 3.97:1 over
59
+ white — under AA, while the badge beside it cleared at 4.64:1. See issue #131. */
60
+ .ui-pill--live { color: var(--chip-success-ink); background: var(--chip-success-fill); }
58
61
  .ui-pill--soon { color: var(--purple-mid); background: var(--glow-purple); }
59
62
 
60
63
  /* Status dot */
@@ -101,9 +101,17 @@ a {
101
101
  }
102
102
 
103
103
  /* Sensible default size for inline icons that a parent rule doesn't size.
104
- Component rules (.ui-btn svg, .ui-card__icon svg…) come later and win the
105
- tie, so they always override this it only catches bare icon() calls. */
106
- svg:not([width]):not([height]) {
104
+ A floor, not a ceiling: :where() holds the whole filter at zero specificity,
105
+ so this weighs (0,0,1)one element, nothing more and any component rule
106
+ that sizes an icon outranks it. Written bare, :not([width]):not([height])
107
+ would count both attribute selectors and weigh (0,2,1), which beats every
108
+ `.ui-btn svg`-shaped rule in the kit no matter what order the files load in.
109
+ It did, for every one of them bar three, and for `.ui-fbck` besides — that
110
+ one is a class on the svg itself, so it is easy to miss when you go looking.
111
+ The count is deliberately not written here: it moved once already, and the
112
+ rules outside this package (site/, the Iconography story) are not in it.
113
+ src/styles/icon-size.test.js keeps the ones in src/styles honest. */
114
+ svg:where(:not([width]):not([height])) {
107
115
  width: 1.1em;
108
116
  height: 1.1em;
109
117
  flex: none;
@@ -29,7 +29,7 @@
29
29
  /* ----------------------------------------------------------------------------
30
30
  * Toast — a floating, dismissible notification.
31
31
  * A status (colour) × style (surface) matrix, entirely token-driven:
32
- * status success · danger · warn · info · neutral → sets --toast-accent/-glow/-on
32
+ * status success · danger · warn · info · neutral → sets --toast-accent/-glow/-on/-solid
33
33
  * style soft (tinted) · solid (filled) · outline (bordered)
34
34
  * -------------------------------------------------------------------------- */
35
35
  .ui-toast {
@@ -47,13 +47,16 @@
47
47
  animation: ui-toast-in 0.28s cubic-bezier(0.22, 1, 0.36, 1) both;
48
48
  }
49
49
 
50
- /* status → the paint tokens every style below consumes (--toast-on = the ink
51
- that reads on the accent when it becomes a fill) */
52
- .ui-toast--success { --toast-accent: var(--green); --toast-glow: var(--glow-green); --toast-on: var(--signal-contrast); }
53
- .ui-toast--danger { --toast-accent: var(--pink); --toast-glow: var(--glow-pink); --toast-on: var(--danger-contrast); }
54
- .ui-toast--warn { --toast-accent: var(--amber); --toast-glow: color-mix(in srgb, var(--amber) 14%, transparent); --toast-on: var(--signal-contrast); }
55
- .ui-toast--info { --toast-accent: var(--cyan); --toast-glow: var(--glow-cyan); --toast-on: var(--signal-contrast); }
56
- .ui-toast--neutral { --toast-accent: var(--muted); --toast-glow: var(--surface-2); --toast-on: var(--strong); }
50
+ /* status → the paint tokens every style below consumes.
51
+ --toast-accent is the status as a line or a small mark, --toast-glow its wash,
52
+ --toast-on the glyph ink on the accent circle. --toast-solid is separate on
53
+ purpose: a full fill is a different job from a 22px circle, and it is chosen
54
+ as a PAIR with --signal-solid-ink rather than against a global. See #149. */
55
+ .ui-toast--success { --toast-accent: var(--green); --toast-glow: var(--glow-green); --toast-on: var(--signal-contrast); --toast-solid: var(--signal-solid-success); }
56
+ .ui-toast--danger { --toast-accent: var(--pink); --toast-glow: var(--glow-pink); --toast-on: var(--danger-contrast); --toast-solid: var(--signal-solid-danger); }
57
+ .ui-toast--warn { --toast-accent: var(--amber); --toast-glow: color-mix(in srgb, var(--amber) 14%, transparent); --toast-on: var(--signal-contrast); --toast-solid: var(--signal-solid-warn); }
58
+ .ui-toast--info { --toast-accent: var(--cyan); --toast-glow: var(--glow-cyan); --toast-on: var(--signal-contrast); --toast-solid: var(--signal-solid-info); }
59
+ .ui-toast--neutral { --toast-accent: var(--muted); --toast-glow: var(--surface-2); --toast-on: var(--strong); --toast-solid: var(--signal-solid-neutral); }
57
60
 
58
61
  /* left status marker (soft + outline; solid is already a full fill) */
59
62
  .ui-toast--soft::before,
@@ -83,17 +86,32 @@
83
86
  /* style: soft — tinted surface */
84
87
  .ui-toast--soft { background: var(--toast-glow); box-shadow: var(--shadow-sm); }
85
88
 
86
- /* style: solid — filled accent, ink flips to --toast-on */
87
- .ui-toast--solid { background: var(--toast-accent); color: var(--toast-on); box-shadow: var(--shadow-lg); }
88
- .ui-toast--solid .ui-toast__title,
89
- .ui-toast--solid .ui-toast__text { color: var(--toast-on); }
90
- .ui-toast--solid .ui-toast__text { opacity: 0.82; }
91
- .ui-toast--solid .ui-toast__icon { background: color-mix(in srgb, var(--toast-on) 22%, transparent); color: var(--toast-on); }
92
- .ui-toast--solid .ui-toast__action { color: var(--toast-on); }
93
- .ui-toast--solid .ui-toast__action:hover { background: color-mix(in srgb, var(--toast-on) 18%, transparent); }
94
- .ui-toast--solid .ui-toast__close { color: color-mix(in srgb, var(--toast-on) 68%, transparent); }
95
- .ui-toast--solid .ui-toast__close:hover { color: var(--toast-on); }
96
- .ui-toast--solid .ui-toast__timer { background: color-mix(in srgb, var(--toast-on) 55%, transparent); }
89
+ /* style: solid — the status at fill strength, inked with the pole opposite it.
90
+ The fill and the ink are one pair, taken together from --signal-solid-*: dark
91
+ fills are the bright signals and take near-black, light fills are the deepened
92
+ ones and take white. What this replaced picked the fill from the status and
93
+ the ink from one of two globals, on independent axes, so nothing made them
94
+ clear each other and four of the ten status x theme combinations did not.
95
+ Gated for all ten in stories/signal-contrast.test.js. See #149. */
96
+ .ui-toast--solid {
97
+ --toast-ink: var(--signal-solid-ink);
98
+ background: var(--toast-solid);
99
+ color: var(--toast-ink);
100
+ box-shadow: var(--shadow-lg);
101
+ }
102
+ .ui-toast--solid .ui-toast__title { color: var(--toast-ink); }
103
+ /* The body copy is the quieter of the two, but it stays quiet by dilution the
104
+ pair can carry: 90% of the ink measures 4.71 on the worst fill in the matrix
105
+ (--chip-success-ink in light). The opacity: 0.82 this replaced measured 4.19
106
+ there, and 2.25 on the dark danger fill — the pair was correct and the
107
+ dilution then spent it. */
108
+ .ui-toast--solid .ui-toast__text { color: color-mix(in srgb, var(--toast-ink) 90%, transparent); }
109
+ .ui-toast--solid .ui-toast__icon { background: color-mix(in srgb, var(--toast-ink) 22%, transparent); color: var(--toast-ink); }
110
+ .ui-toast--solid .ui-toast__action { color: var(--toast-ink); }
111
+ .ui-toast--solid .ui-toast__action:hover { background: color-mix(in srgb, var(--toast-ink) 18%, transparent); }
112
+ .ui-toast--solid .ui-toast__close { color: color-mix(in srgb, var(--toast-ink) 68%, transparent); }
113
+ .ui-toast--solid .ui-toast__close:hover { color: var(--toast-ink); }
114
+ .ui-toast--solid .ui-toast__timer { background: color-mix(in srgb, var(--toast-ink) 55%, transparent); }
97
115
 
98
116
  /* style: outline — elevated card with a status-tinted border */
99
117
  .ui-toast--outline { background: var(--bg-elevated); box-shadow: var(--shadow-md); border: 1px solid color-mix(in srgb, var(--toast-accent) 40%, transparent); }
@@ -102,7 +102,7 @@
102
102
  color: var(--muted);
103
103
  background: var(--surface);
104
104
  }
105
- .ui-dropdown__badge.is-live { color: var(--green); background: var(--glow-green); }
105
+ .ui-dropdown__badge.is-live { color: var(--chip-success-ink); background: var(--chip-success-fill); }
106
106
  .ui-dropdown__badge.is-accent { color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, transparent); }
107
107
 
108
108
  /* Disabled + danger rows */
@@ -67,10 +67,15 @@
67
67
  margin-top: 12px;
68
68
  }
69
69
 
70
- /* Legacy line-icon slot — kept so existing .ui-empty__icon markup still works. */
70
+ /* Legacy line-icon slot — kept so existing .ui-empty__icon markup still works.
71
+ 44px until #148. The icon inside is `width: 100%`, and that rule lost to the
72
+ base.css reset for as long as this slot has existed, so the slot's number was
73
+ never the size anyone saw: it rendered at 15.94px. Once the reset stopped
74
+ winning, 44px was a size nobody had chosen with their eyes open. 32px is the
75
+ size that was chosen, against the alternatives, on a rendered comparison. */
71
76
  .ui-empty__icon {
72
- width: 44px;
73
- height: 44px;
77
+ width: 32px;
78
+ height: 32px;
74
79
  margin: 0 auto 14px;
75
80
  color: var(--muted);
76
81
  opacity: 0.6;
@@ -6,7 +6,7 @@
6
6
  * ========================================================================== */
7
7
 
8
8
  .ui-nav { font-family: var(--font-sans); }
9
- .ui-nav ul, .ui-nav ol { list-style: none; margin: 0; padding: 0; }
9
+ .ui-nav :where(ul, ol) { list-style: none; margin: 0; padding: 0; } /* floor, not ceiling */
10
10
 
11
11
  /* -- Sidebar rail -------------------------------------------------------- */
12
12
  .ui-nav--side {
@@ -49,7 +49,7 @@
49
49
  }
50
50
  .ui-nav__item:hover { background: var(--surface-2); color: var(--strong); }
51
51
  .ui-nav__item.is-active {
52
- background: var(--surface);
52
+ background: var(--surface-3);
53
53
  color: var(--strong);
54
54
  font-weight: 500;
55
55
  }
@@ -98,7 +98,7 @@
98
98
  color: var(--dim);
99
99
  }
100
100
  .ui-nav__badge.is-accent { background: var(--glow-purple); color: var(--accent); }
101
- .ui-nav__badge.is-live { background: var(--glow-green); color: var(--green); }
101
+ .ui-nav__badge.is-live { background: var(--chip-success-fill); color: var(--chip-success-ink); }
102
102
  .ui-nav__badge.is-danger { background: var(--glow-pink); color: var(--pink); }
103
103
  .ui-nav__item.is-active .ui-nav__badge.is-neutral { background: var(--surface-3); color: var(--strong); }
104
104
 
@@ -117,14 +117,18 @@
117
117
  display: flex;
118
118
  flex-direction: column;
119
119
  gap: 2px;
120
- margin: 2px 0 2px 15px;
121
- padding-left: 11px;
120
+ margin: var(--space-1) 0 var(--space-1) var(--space-4);
121
+ padding-left: var(--space-3);
122
122
  border-left: 1px solid var(--border);
123
123
  }
124
124
  .ui-nav__sub[hidden] { display: none; }
125
125
  .ui-nav__item--sub { font-size: 14px; padding: 7px 12px; }
126
- .ui-nav--side .ui-nav__item--sub.is-active { box-shadow: none; }
127
- .ui-nav--side .ui-nav__item--sub.is-active::before { left: -12px; height: 15px; }
126
+ /* No hairline on a nested current row — but only at rest. box-shadow carries
127
+ both the hairline and the focus ring, so a rule about the resting look must
128
+ say so, or it silently cancels the ring (WCAG 2.4.7). */
129
+ .ui-nav--side .ui-nav__item--sub.is-active:not(:focus-visible) { box-shadow: none; }
130
+ /* Hangs back onto the guide hairline the indent above draws. */
131
+ .ui-nav--side .ui-nav__item--sub.is-active::before { left: calc(-1 * var(--space-3)); height: 15px; }
128
132
 
129
133
  /* Footer slot (divider + trailing links, e.g. sign-out) */
130
134
  .ui-nav__foot { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border); }
@@ -149,7 +149,7 @@
149
149
  flex: none;
150
150
  margin-top: 1px;
151
151
  }
152
- .vbadge.live { color: var(--green); background: var(--glow-green); }
152
+ .vbadge.live { color: var(--chip-success-ink); background: var(--chip-success-fill); }
153
153
  .vbadge.arch { color: var(--muted); background: var(--surface); }
154
154
  @media (max-width: 600px) { .vsw__btn .lbl { display: none; } }
155
155
 
@@ -109,7 +109,12 @@
109
109
  --purple-mid: #b479ff;
110
110
  --cyan: #20dcf5;
111
111
  --green: #98ff8f;
112
- --pink: #e35b8f;
112
+ /* Lighter than it looks like it should be, and deliberately so: --pink is read
113
+ mostly ON its own 16% glow, and that wash lifts the ground faster than the
114
+ hue lifts the ink. #e35b8f measured 3.82:1 on --glow-pink over a card —
115
+ under AA — so danger moved AWAY from the canvas, not toward it. Light moves
116
+ the opposite way for the same reason. See issue #131. */
117
+ --pink: #e97ca5;
113
118
  --amber: #ffcf6a;
114
119
 
115
120
  --bg: #16151f;
@@ -136,16 +141,25 @@
136
141
  --ink: #e9e7f0;
137
142
 
138
143
  /* Ink that reads on a SIGNAL colour once that colour becomes a fill — the
139
- glyph in a success circle, the text in a solid toast. The signal hues sit
140
- high enough in dark and mid-tone in light that near-black wins on all of
141
- them; the pink danger fill deliberately takes light ink instead. */
144
+ glyph in a success circle, the glyph on a toast's status circle.
145
+ In dark every signal is a bright hue, so one near-black ink clears all
146
+ five: 15.87 on --green, 7.35 on --pink, 13.40 on --amber, 11.75 on --cyan
147
+ and 6.29 on --muted. Danger keeps a token of its own only because LIGHT
148
+ still needs it: there the signals are deepened to read as ink on white,
149
+ which parks them mid-luminance, and --pink is the one that comes out ahead
150
+ against white (5.78) rather than against near-black (3.39).
151
+ Dark danger was white until now, which was correct while --pink was
152
+ #e35b8f. Issue #131 lightened it to #e97ca5 to clear AA as INK on its own
153
+ glow, and that carried the fill up past the point where white could read on
154
+ it — 2.66 on the toast's status circle, under the 3:1 of WCAG 1.4.11. The
155
+ ten status x theme icon pairs are gated in stories/signal-contrast.test.js. */
142
156
  --signal-contrast: #0c0c0c;
143
- --danger-contrast: #ffffff;
157
+ --danger-contrast: var(--signal-contrast);
144
158
 
145
159
  --glow-green: rgba(152, 255, 143, 0.16);
146
160
  --glow-purple: rgba(155, 93, 255, 0.18);
147
161
  --glow-cyan: rgba(32, 220, 245, 0.14);
148
- --glow-pink: rgba(227, 91, 143, 0.16);
162
+ --glow-pink: rgba(233, 124, 165, 0.16);
149
163
 
150
164
  /* Status-chip paint — the ink + fill of a badge / pill. Dark tints with the
151
165
  signal's own glow; light needs a solid, deepened pair (the glows wash out
@@ -156,6 +170,23 @@
156
170
  --chip-warn-fill: color-mix(in srgb, var(--amber) 15%, transparent);
157
171
  --chip-danger-ink: var(--pink);
158
172
  --chip-danger-fill: var(--glow-pink);
173
+ --chip-info-ink: var(--cyan);
174
+ --chip-info-fill: var(--glow-cyan);
175
+
176
+ /* Solid signal surfaces — a status that has stopped being ink and become the
177
+ FILL (a solid toast). The fill and its ink are ONE pair, never two
178
+ independent picks: the fill is the status at this theme's extreme, and the
179
+ ink is the pole opposite it, so a single ink clears all five. Dark fills are
180
+ the bright signals, so the ink is near-black — it measures 15.87 on --green,
181
+ 7.35 on --pink, 13.40 on --amber, 11.75 on --cyan and 6.29 on --muted.
182
+ Neutral needs no chip pair to take part: --muted is already its
183
+ fill-strength form. See issues #131 and #149. */
184
+ --signal-solid-ink: var(--signal-contrast);
185
+ --signal-solid-success: var(--green);
186
+ --signal-solid-danger: var(--pink);
187
+ --signal-solid-warn: var(--amber);
188
+ --signal-solid-info: var(--cyan);
189
+ --signal-solid-neutral: var(--muted);
159
190
 
160
191
  --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.28);
161
192
  --shadow-md: 0 8px 24px rgba(0, 0, 0, 0.4);
@@ -191,7 +222,11 @@
191
222
  --purple-mid: #7a3be0;
192
223
  --cyan: #0c8fa8;
193
224
  --green: #1c8a2c;
194
- --pink: #d63c72;
225
+ /* Deeper than the dark pink for the same reason dark's is lighter: the wash
226
+ under it is white, so the ink has to come down to meet it. #d63c72 measured
227
+ 3.87:1 on --glow-pink over white and 4.42:1 on plain white — the required
228
+ marker and the form error missed AA on nothing at all. See issue #131. */
229
+ --pink: #b63361;
195
230
  --amber: #b5730a;
196
231
 
197
232
  /* A WHITE app: the page and cards are both white; cards are delineated by a
@@ -219,20 +254,51 @@
219
254
  --signal-contrast: #0c0c0c;
220
255
  --danger-contrast: #ffffff;
221
256
 
222
- --glow-green: rgba(30, 150, 50, 0.1);
257
+ /* Re-tinted from --green. It was rgba(30, 150, 50, .1) — a colour that exists
258
+ nowhere else in the kit, carried unedited since the initial commit while the
259
+ other three light glows are exact tints of their own token. The wash over
260
+ white moves #e9f5eb -> #e8f3ea, at most two levels on a channel, which is
261
+ 0.86 of a deltaE — under the 1.0 that is normally called imperceptible. No
262
+ verdict moves with it: body copy on the success wash goes 9.29 -> 9.15
263
+ against the 4.5 AA bar, and the --green icon on it 3.97 -> 3.91 against the
264
+ 3.0 bar WCAG 1.4.11 sets for a graphic. The success chips are untouched —
265
+ light's --chip-success-fill is the solid #dff3e4, not this. Gated for all
266
+ four glows in stories/signal-contrast.test.js. See issue #131. */
267
+ --glow-green: rgba(28, 138, 44, 0.1);
223
268
  --glow-purple: rgba(106, 45, 204, 0.09);
224
269
  --glow-cyan: rgba(12, 143, 168, 0.1);
225
- --glow-pink: rgba(214, 60, 114, 0.1);
270
+ --glow-pink: rgba(182, 51, 97, 0.1);
226
271
 
227
272
  /* Solid chip pairs for the white app. Each ink is deepened past its signal
228
273
  hue so 10px uppercase text clears WCAG AA on its own pale fill —
229
- e.g. --pink (#d63c72) on #fbe0ea is 3.6:1, #b7295f is 4.9:1. */
274
+ e.g. --pink (#b63361) on #fbe0ea is 4.66:1, #b7295f is 4.85:1. */
230
275
  --chip-success-ink: #1f7a38;
231
276
  --chip-success-fill: #dff3e4;
232
277
  --chip-warn-ink: #8a5e00;
233
278
  --chip-warn-fill: #fbedd2;
234
279
  --chip-danger-ink: #b7295f;
235
280
  --chip-danger-fill: #fbe0ea;
281
+ /* Info was the one signal family without a pair, so the info badge was still
282
+ painting --cyan (#0c8fa8) on --glow-cyan over white, which is 3.39:1 —
283
+ under AA. #0a7286 on #ddeff3 is 4.71:1, inside the 4.64–4.93 band the other
284
+ three pairs already sit in. See issue #131. */
285
+ --chip-info-ink: #0a7286;
286
+ --chip-info-fill: #ddeff3;
287
+
288
+ /* Light runs the same rule the other way. The signals are mid-tone here — a
289
+ solid --green fill measures 4.40 against near-black and 4.45 against white,
290
+ so NO ink rescues it and the fill is what has to move. It moves to the
291
+ deepened form the chip pair already carries, and the pole above it, white,
292
+ clears every one: #ffffff measures 5.39 on --chip-success-ink, 6.01 on
293
+ --chip-danger-ink, 5.70 on --chip-warn-ink, 5.58 on --chip-info-ink and 6.11
294
+ on --muted. A future status supplies one --signal-solid-* value dark enough
295
+ for white and inherits the ink. See issues #131 and #149. */
296
+ --signal-solid-ink: #ffffff;
297
+ --signal-solid-success: var(--chip-success-ink);
298
+ --signal-solid-danger: var(--chip-danger-ink);
299
+ --signal-solid-warn: var(--chip-warn-ink);
300
+ --signal-solid-info: var(--chip-info-ink);
301
+ --signal-solid-neutral: var(--muted);
236
302
 
237
303
  --shadow-sm: 0 1px 3px rgba(30, 30, 50, 0.08);
238
304
  --shadow-md: 0 8px 24px rgba(30, 30, 50, 0.1);