@apliteni/apliteni-ui 0.35.0 → 0.37.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.
@@ -68,7 +68,7 @@
68
68
  display: flex;
69
69
  flex-direction: column;
70
70
  font-family: var(--font-sans);
71
- background: var(--cmdk-surface);
71
+ background: var(--cmdk-surface); --ring-gap: var(--cmdk-surface);
72
72
  /* The two-step edge and the drop: --border-strong outside, --border one pixel
73
73
  inside it, then the broad faint drop. why: docs/specification.md#elevation */
74
74
  border: 1px solid var(--border-strong);
@@ -86,7 +86,7 @@
86
86
  box-shadow list replaces the whole list, so `box-shadow: var(--ring)` alone
87
87
  took the panel's edge and its drop off for as long as it held focus.
88
88
  why: docs/specification.md#elevation */
89
- .ui-cmdk__panel:focus-visible { outline: none; box-shadow: var(--ring), inset 0 0 0 1px var(--elev-edge, var(--border)), var(--elev-drop); }
89
+ .ui-cmdk__panel:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring), inset 0 0 0 1px var(--elev-edge, var(--border)), var(--elev-drop); }
90
90
 
91
91
  /* -- The text box --------------------------------------------------------- */
92
92
 
@@ -99,7 +99,7 @@
99
99
  }
100
100
  .ui-cmdk__search-ic {
101
101
  display: inline-flex;
102
- color: var(--muted);
102
+ /* muted-ink: glyph — search icon */ color: var(--muted);
103
103
  flex: none;
104
104
  }
105
105
  /* 2.2 of a 24 box drawn at 17px = 1.56 CSS px, which is what keeps the mark a
@@ -130,7 +130,7 @@
130
130
  already carries one, and the caret says where the typing goes. */
131
131
  outline: none;
132
132
  }
133
- .ui-cmdk__input::placeholder { color: var(--muted); }
133
+ .ui-cmdk__input::placeholder { /* muted-ink: placeholder — empty command search */ color: var(--muted); }
134
134
 
135
135
  /* -- The list ------------------------------------------------------------- */
136
136
 
@@ -149,7 +149,7 @@
149
149
  padding: var(--space-2) var(--space-3) var(--space-1);
150
150
  font-size: var(--text-sm);
151
151
  font-weight: var(--weight-medium);
152
- color: var(--muted);
152
+ color: var(--text);
153
153
  }
154
154
 
155
155
  /* Row — [icon] [label + description] [badge] [keys]. A <div role="option">, so
@@ -178,12 +178,12 @@
178
178
  background: none;
179
179
  }
180
180
  .ui-cmdk__item[hidden] { display: none; } /* motion: still — a row the query no longer answers is gone on the next keystroke */
181
- .ui-cmdk__item.is-active { background: var(--surface-3); }
181
+ .ui-cmdk__item.is-active { background: var(--surface-3); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ }
182
182
  .ui-cmdk__item.is-active .ui-cmdk__label { color: var(--strong); }
183
183
 
184
184
  .ui-cmdk__ic {
185
185
  display: inline-flex;
186
- color: var(--dim);
186
+ /* muted-ink: glyph — result icon */ color: var(--dim);
187
187
  flex: none;
188
188
  }
189
189
  /* 2.3 of a 24 box drawn at 16px = 1.53 CSS px. Same arithmetic as the dropdown's
@@ -208,10 +208,10 @@
208
208
  }
209
209
  .ui-cmdk__desc {
210
210
  font-size: var(--text-sm);
211
- /* --dim and not --muted: a row's note is how two rows called INV-4812 and
211
+ /* Body ink: a row's note is how two rows called INV-4812 and
212
212
  INV-4809 are told apart, which is not something a reader may skip.
213
- why: docs/specification.md#colour-and-contrast */
214
- color: var(--dim);
213
+ why: docs/specification.md#text-ink */
214
+ color: var(--text);
215
215
  white-space: nowrap;
216
216
  overflow: hidden;
217
217
  text-overflow: ellipsis;
@@ -224,7 +224,7 @@
224
224
  border-radius: var(--radius-pill);
225
225
  border: 1px solid var(--border);
226
226
  font-size: var(--text-xs);
227
- color: var(--dim);
227
+ color: var(--text);
228
228
  }
229
229
 
230
230
  /* Destructive rows take the kit's danger ink, never the accent, and stay quiet
@@ -232,8 +232,8 @@
232
232
  writes with :hover. A palette row is reached with an arrow key far more often
233
233
  than with a pointer, so `.is-active` carries the signal beside :hover: it is
234
234
  the same moment, and it is the row Enter would run.
235
- why: docs/specification.md#colour-and-contrast */
236
- .ui-cmdk__item.is-danger .ui-cmdk__label { color: var(--muted); }
235
+ why: docs/specification.md#text-ink */
236
+ .ui-cmdk__item.is-danger .ui-cmdk__label { color: var(--text); }
237
237
  .ui-cmdk__item.is-danger:hover .ui-cmdk__label,
238
238
  .ui-cmdk__item.is-danger.is-active .ui-cmdk__label { color: var(--pink); }
239
239
  .ui-cmdk__item.is-danger.is-active .ui-cmdk__ic { color: var(--pink); }
@@ -242,11 +242,11 @@
242
242
  for a control with no box of its own: --disabled-surface IS --surface-2,
243
243
  which is the panel behind it, so a disabled row paints no box a reader can
244
244
  see and its label is read on whatever the panel is.
245
- why: docs/specification.md#colour-and-contrast */
246
- .ui-cmdk__item.is-disabled { cursor: default; color: var(--disabled-ink-bare); }
245
+ why: docs/specification.md#text-ink */
246
+ .ui-cmdk__item.is-disabled { cursor: default; /* muted-ink: state — unavailable control */ color: var(--disabled-ink-bare); }
247
247
  .ui-cmdk__item.is-disabled .ui-cmdk__label,
248
248
  .ui-cmdk__item.is-disabled .ui-cmdk__desc,
249
- .ui-cmdk__item.is-disabled .ui-cmdk__ic { color: var(--disabled-ink-bare); }
249
+ .ui-cmdk__item.is-disabled .ui-cmdk__ic { /* muted-ink: state — unavailable control */ color: var(--disabled-ink-bare); }
250
250
 
251
251
  /* -- Keys, empty state, legend -------------------------------------------- */
252
252
 
@@ -258,11 +258,11 @@
258
258
  border-radius: var(--radius-xs);
259
259
  /* A key cap reads as raised, so it takes the step above the panel rather than
260
260
  the card step below it. why: docs/specification.md#elevation */
261
- background: var(--surface-3);
261
+ background: var(--surface-3); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */
262
262
  font-family: var(--font-sans);
263
263
  font-size: var(--text-xs);
264
264
  line-height: 1.5;
265
- color: var(--dim);
265
+ color: var(--text);
266
266
  text-align: center;
267
267
  }
268
268
 
@@ -271,7 +271,7 @@
271
271
  padding: var(--space-6) var(--space-4) var(--space-8);
272
272
  text-align: center;
273
273
  font-size: var(--text-base);
274
- color: var(--dim);
274
+ color: var(--text);
275
275
  }
276
276
 
277
277
  .ui-cmdk__foot {
@@ -281,7 +281,7 @@
281
281
  padding: var(--space-2) var(--space-4);
282
282
  border-top: 1px solid var(--border);
283
283
  font-size: var(--text-xs);
284
- color: var(--muted);
284
+ color: var(--text);
285
285
  }
286
286
  .ui-cmdk__foot span { display: inline-flex; align-items: center; gap: 5px; }
287
287
 
@@ -301,10 +301,10 @@
301
301
  align-items: center;
302
302
  justify-content: center;
303
303
  border-radius: var(--radius-sm);
304
- background: var(--surface);
304
+ background: var(--surface); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */
305
305
  border: 1px solid var(--border);
306
306
  }
307
- .ui-cmdk--roomy .ui-cmdk__item.is-active .ui-cmdk__ic { background: var(--surface-2); }
307
+ .ui-cmdk--roomy .ui-cmdk__item.is-active .ui-cmdk__ic { background: var(--surface-2); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */ }
308
308
 
309
309
  /* -- Narrow --------------------------------------------------------------- */
310
310
  /* At the kit's narrowest step the palette is the page: full width, off the top
@@ -61,7 +61,7 @@
61
61
  flex-direction: column;
62
62
  gap: var(--space-2);
63
63
  padding: var(--space-5);
64
- background: var(--confirm-surface);
64
+ background: var(--confirm-surface); --ring-gap: var(--confirm-surface);
65
65
  /* The two-step edge and the drop: --border-strong outside, --border one pixel
66
66
  inside it, then the broad faint drop. why: docs/specification.md#elevation */
67
67
  border: 1px solid var(--border-strong);
@@ -78,7 +78,7 @@
78
78
  box-shadow list replaces the whole list, so `box-shadow: var(--ring)` alone
79
79
  took the panel's edge and its drop off for as long as it held focus.
80
80
  why: docs/specification.md#elevation */
81
- .ui-confirm__panel:focus-visible { outline: none; box-shadow: var(--ring), inset 0 0 0 1px var(--elev-edge, var(--border)), var(--elev-drop); }
81
+ .ui-confirm__panel:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring), inset 0 0 0 1px var(--elev-edge, var(--border)), var(--elev-drop); }
82
82
 
83
83
  /* -- Slots ----------------------------------------------------------------- */
84
84
  /* The question and the answers keep their size; the consequence between them is
@@ -105,7 +105,7 @@
105
105
  font-size: var(--text-sm);
106
106
  font-weight: var(--weight-normal);
107
107
  line-height: 1.55;
108
- color: var(--muted);
108
+ color: var(--text);
109
109
  }
110
110
  .ui-confirm__acts {
111
111
  display: flex;
@@ -57,7 +57,7 @@
57
57
  position: absolute;
58
58
  display: flex;
59
59
  flex-direction: column;
60
- background: var(--drawer-surface);
60
+ background: var(--drawer-surface); --ring-gap: var(--drawer-surface);
61
61
  /* A drawer is flush to a screen edge, so it has one edge rather than four and the
62
62
  two-step edge runs along that one: --border-strong on the edge rule below, the
63
63
  inner line one pixel inside it. This is the one floating surface that writes no
@@ -115,7 +115,7 @@
115
115
  box-shadow list replaces the whole list, so a bare `box-shadow: var(--ring)` would
116
116
  take the drawer's edge and its drop off for as long as it held focus.
117
117
  why: docs/specification.md#elevation */
118
- .ui-drawer__panel:focus-visible { outline: none; box-shadow: var(--ring), var(--drawer-line), var(--elev-drop); }
118
+ .ui-drawer__panel:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring), var(--drawer-line), var(--elev-drop); }
119
119
 
120
120
  /* -- Sizes ----------------------------------------------------------------- */
121
121
  /* left|right → width; top|bottom → height. Same names, axis chosen by side. */
@@ -159,7 +159,7 @@
159
159
  width: 30px; height: 30px;
160
160
  flex: none;
161
161
  cursor: pointer;
162
- color: var(--muted);
162
+ /* muted-ink: glyph — dismiss icon */ color: var(--muted);
163
163
  background: transparent;
164
164
  border: 1px solid transparent;
165
165
  border-radius: var(--radius-sm);
@@ -169,8 +169,8 @@
169
169
  background var(--dur-fast) var(--ease),
170
170
  border-color var(--dur-fast) var(--ease);
171
171
  }
172
- .ui-drawer__close:hover { color: var(--strong); background: var(--surface-3); }
173
- .ui-drawer__close:focus-visible { outline: none; box-shadow: var(--ring); border-color: var(--accent); }
172
+ .ui-drawer__close:hover { color: var(--strong); background: var(--surface-3); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ }
173
+ .ui-drawer__close:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); border-color: var(--accent); }
174
174
  .ui-drawer__close svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 2.1; }
175
175
 
176
176
  .ui-drawer__body {
@@ -191,7 +191,7 @@
191
191
  gap: 10px;
192
192
  padding: 16px 20px;
193
193
  border-top: 1px solid var(--border);
194
- background: var(--surface-2);
194
+ background: var(--surface-2); --ring-gap: var(--surface-2);
195
195
  flex: none;
196
196
  }
197
197
 
@@ -232,7 +232,7 @@
232
232
  padding-block: var(--space-1);
233
233
  overflow-wrap: anywhere;
234
234
  }
235
- .ui-drawer__row dt { color: var(--muted); }
235
+ .ui-drawer__row dt { color: var(--text); }
236
236
  .ui-drawer__row dd { color: var(--text); font-variant-numeric: tabular-nums; }
237
237
  .ui-drawer__rows + * { margin-top: var(--space-3); }
238
238
 
@@ -17,15 +17,15 @@
17
17
  font-size: calc(var(--text-sm) - 0.5px);
18
18
  font-weight: 500;
19
19
  color: var(--text);
20
- background: var(--surface);
20
+ background: var(--surface); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */
21
21
  border: 1px solid var(--border);
22
22
  border-radius: var(--radius-pill);
23
23
  padding: 7px 11px 7px 13px;
24
24
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
25
25
  }
26
26
  .ui-dropdown__trigger:hover { border-color: var(--accent); }
27
- .ui-dropdown__trigger:focus-visible { outline: none; box-shadow: var(--ring); border-color: var(--accent); }
28
- .ui-dropdown__pre { color: var(--muted); }
27
+ .ui-dropdown__trigger:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); border-color: var(--accent); }
28
+ .ui-dropdown__pre { color: var(--text); }
29
29
  .ui-dropdown__value { color: var(--strong); font-weight: 600; letter-spacing: 0.01em; }
30
30
 
31
31
  /* Chevron — border caret that flips when the container is .open. Its ink is an L
@@ -70,7 +70,7 @@
70
70
  min-width: 240px;
71
71
  /* The floating step, not the sunken one — a panel held open over a card cannot be
72
72
  painted the colour of an inset. why: docs/specification.md#elevation */
73
- background: var(--bg-elevated);
73
+ background: var(--bg-elevated); --ring-gap: var(--bg-elevated);
74
74
  /* The two-step edge and the drop: --border-strong outside, --border one pixel
75
75
  inside it, then the broad faint drop. why: docs/specification.md#elevation */
76
76
  border: 1px solid var(--border-strong);
@@ -152,12 +152,12 @@
152
152
  transition: background var(--dur-fast) var(--ease);
153
153
  }
154
154
  /* A row in a raised panel lifts: the step above it, never the card step below. why: docs/specification.md#elevation */
155
- .ui-dropdown__item:hover { background: var(--surface-3); }
156
- .ui-dropdown__item:focus-visible { outline: none; background: var(--surface-3); box-shadow: var(--ring); }
155
+ .ui-dropdown__item:hover { background: var(--surface-3); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ }
156
+ .ui-dropdown__item:focus-visible { outline: 2px solid transparent; background: var(--surface-3); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ box-shadow: var(--ring); }
157
157
  .ui-dropdown__main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
158
158
  .ui-dropdown__label { font-family: var(--font-sans); font-size: calc(var(--text-sm) - 0.5px); color: var(--strong); font-weight: 600; letter-spacing: 0.01em; }
159
- .ui-dropdown__desc { font-size: var(--text-xs); color: var(--muted); line-height: 1.35; }
160
- .ui-dropdown__ic { display: inline-flex; flex: none; color: var(--muted); }
159
+ .ui-dropdown__desc { font-size: var(--text-xs); color: var(--text); line-height: 1.35; }
160
+ .ui-dropdown__ic { display: inline-flex; flex: none; /* muted-ink: glyph — option icon */ color: var(--muted); }
161
161
  .ui-dropdown__ic svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 2.1; }
162
162
 
163
163
  /* Trailing selected tick (listbox variant) */
@@ -178,25 +178,25 @@
178
178
  border-radius: var(--radius-pill);
179
179
  margin-left: auto;
180
180
  flex: none;
181
- color: var(--muted);
181
+ color: var(--text);
182
182
  /* A chip is the top step. why: docs/specification.md#elevation */
183
- background: var(--surface-3);
183
+ background: var(--surface-3); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */
184
184
  }
185
- .ui-dropdown__badge.is-live { color: var(--chip-success-ink); background: var(--chip-success-fill); }
185
+ .ui-dropdown__badge.is-live { color: var(--chip-success-ink); background: var(--chip-success-fill); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */ }
186
186
  /* The accent counter keeps the badge's flat surface and changes only the ink — the shape
187
187
  src/styles/nav.css:163 `.ui-nav__item.is-active .ui-nav__badge.is-accent` already holds.
188
188
  The accent wash goes on a base surface, never a raised one, and #295 made this panel a
189
189
  raised one. why: docs/specification.md#elevation */
190
- .ui-dropdown__badge.is-accent { color: var(--accent); background: var(--surface-3); }
190
+ .ui-dropdown__badge.is-accent { color: var(--accent); background: var(--surface-3); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */ }
191
191
 
192
192
  /* Disabled + danger rows */
193
- .ui-dropdown__item.is-disabled { color: var(--disabled-ink); cursor: not-allowed; pointer-events: none; }
193
+ .ui-dropdown__item.is-disabled { /* muted-ink: state — unavailable control */ color: var(--disabled-ink); cursor: not-allowed; pointer-events: none; }
194
194
  .ui-dropdown__item.is-disabled .ui-dropdown__label,
195
195
  .ui-dropdown__item.is-disabled .ui-dropdown__desc { color: inherit; }
196
- /* Quiet at rest, --pink on the way to being pressed. Both states, not hover alone:
196
+ /* Body ink at rest, --pink on the way to being pressed. Both states, not hover alone:
197
197
  the ring says where the reader is, not that this row ends something.
198
198
  why: docs/specification.md#a-dropdown-row-is-a-div-a-link-or-a-button */
199
- .ui-dropdown__item.is-danger .ui-dropdown__label { color: var(--muted); }
199
+ .ui-dropdown__item.is-danger .ui-dropdown__label { color: var(--text); }
200
200
  .ui-dropdown__item.is-danger:hover .ui-dropdown__label,
201
201
  .ui-dropdown__item.is-danger:focus-visible .ui-dropdown__label { color: var(--pink); }
202
202
 
@@ -207,7 +207,7 @@
207
207
  /* rank: label */
208
208
  font-size: var(--text-sm);
209
209
  font-weight: var(--weight-medium);
210
- color: var(--muted);
210
+ color: var(--text);
211
211
  padding: 7px 12px 4px;
212
212
  }
213
213
 
@@ -244,7 +244,7 @@
244
244
  position: absolute;
245
245
  left: 11px;
246
246
  display: inline-flex;
247
- color: var(--muted);
247
+ /* muted-ink: glyph — search icon */ color: var(--muted);
248
248
  pointer-events: none;
249
249
  }
250
250
  /* 16px at 2.4 paints 1.6px, over the 1.5px floor stories/glyph-stroke.test.js holds. */
@@ -258,7 +258,7 @@
258
258
  font-size: calc(var(--text-sm) - 0.5px);
259
259
  color: var(--strong);
260
260
  /* A field is the sunken step, the same one .ui-input takes. why: docs/specification.md#elevation */
261
- background: var(--surface-2);
261
+ background: var(--surface-2); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */
262
262
  border: 1px solid var(--border);
263
263
  border-radius: var(--radius-sm);
264
264
  padding: 8px 12px 8px 34px;
@@ -271,11 +271,11 @@
271
271
  kit paints, so --surface-2 falls two rungs under it rather than one. The
272
272
  numbers are in the spec and the gate is stories/dropdown-field-ground.test.js.
273
273
  why: docs/specification.md#elevation */
274
- :root[data-theme="light"] .ui-dropdown__search-input { background: var(--bg); }
274
+ :root[data-theme="light"] .ui-dropdown__search-input { background: var(--bg); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ }
275
275
 
276
- .ui-dropdown__search-input::placeholder { color: var(--muted); }
276
+ .ui-dropdown__search-input::placeholder { /* muted-ink: placeholder — empty search field */ color: var(--muted); }
277
277
  .ui-dropdown__search-input:hover { border-color: var(--border-strong); }
278
- .ui-dropdown__search-input:focus-visible { outline: none; border-color: var(--accent); box-shadow: var(--ring); }
278
+ .ui-dropdown__search-input:focus-visible { outline: 2px solid transparent; border-color: var(--accent); box-shadow: var(--ring); }
279
279
 
280
280
  .ui-dropdown__list { max-height: 300px; overflow-y: auto; }
281
281
  /* A row is `display: flex`, which outranks the UA's [hidden], so a row the
@@ -284,11 +284,13 @@
284
284
 
285
285
  /* The row Enter would pick. Focus stays in the field, which carries the ring,
286
286
  so this row takes the hover fill and a bar rather than a second ring. */
287
- .ui-dropdown__item.is-active { background: var(--surface-3); box-shadow: inset 2px 0 0 var(--accent); }
287
+ .ui-dropdown__item.is-active { background: var(--surface-3); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ box-shadow: inset 2px 0 0 var(--accent); }
288
288
 
289
289
  /* No match — a nudge and no action, per Guidelines / Microcopy. Empty while
290
290
  anything matches, so it takes no room. */
291
291
  .ui-dropdown__none { display: flex; flex-direction: column; gap: 3px; padding: 14px 12px 10px; text-align: center; }
292
292
  .ui-dropdown__none:empty { display: none; }
293
293
  .ui-dropdown__none-title { font-size: calc(var(--text-sm) - 0.5px); font-weight: 600; color: var(--strong); }
294
- .ui-dropdown__none-hint { font-size: var(--text-xs); color: var(--muted); line-height: 1.35; }
294
+ .ui-dropdown__none-hint { font-size: var(--text-xs); color: var(--text); line-height: 1.35; }
295
+
296
+ .ui-dropdown__badge.is-state { /* muted-ink: state — off, unset, disabled or archived badge */ color: var(--muted); }
@@ -22,7 +22,7 @@
22
22
  text-align: center;
23
23
  gap: 8px;
24
24
  padding: 48px 24px;
25
- color: var(--muted);
25
+ color: var(--text);
26
26
  }
27
27
 
28
28
  /* Duotone illustration slot. */
@@ -77,7 +77,7 @@
77
77
  width: 32px;
78
78
  height: 32px;
79
79
  margin: 0 auto 14px;
80
- color: var(--muted);
80
+ /* muted-ink: glyph — empty-state illustration */ color: var(--muted);
81
81
  opacity: 0.6;
82
82
  }
83
83
  .ui-empty__icon svg {
@@ -66,7 +66,7 @@
66
66
  /* The composer panel (bottom-centre) */
67
67
  .ui-fbcomposer {
68
68
  position: fixed; z-index: 71; left: 50%; bottom: 26px; transform: translateX(-50%) translateY(14px);
69
- width: min(520px, calc(100vw - 32px)); background: var(--bg-elevated); border-radius: 18px;
69
+ width: min(520px, calc(100vw - 32px)); background: var(--bg-elevated); --ring-gap: var(--bg-elevated); border-radius: 18px;
70
70
  /* The shadow was its only edge until #295; now it takes the hairline. */
71
71
  border: 1px solid var(--border); opacity: 0; pointer-events: none;
72
72
  transition: opacity var(--dur-med) var(--ease), transform var(--dur-med) var(--ease); overflow: hidden;
@@ -76,30 +76,30 @@
76
76
  .ui-fbc__head { display: flex; align-items: center; gap: 12px; padding: 17px 20px 0; }
77
77
  .ui-fbc__chip { display: inline-flex; align-items: center; gap: 8px; color: var(--accent); font-size: var(--text-sm); font-weight: var(--weight-semibold); letter-spacing: .01em; white-space: nowrap; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
78
78
  .ui-fbc__chip svg { width: 14px; height: 14px; flex: none; opacity: .85; stroke-width: 2.7; }
79
- .ui-fbc__x { margin-left: auto; background: var(--surface-2); color: var(--dim); border: 0; width: 28px; height: 28px; border-radius: 8px; cursor: pointer; display: grid; place-items: center; flex: none; font: inherit; }
79
+ .ui-fbc__x { margin-left: auto; background: var(--surface-2); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ /* muted-ink: glyph — dismiss icon */ color: var(--dim); border: 0; width: 28px; height: 28px; border-radius: 8px; cursor: pointer; display: grid; place-items: center; flex: none; font: inherit; }
80
80
  .ui-fbc__x:hover { color: var(--strong); }
81
81
  .ui-fbc__x svg { width: 14px; height: 14px; stroke-width: 2.7; }
82
- .ui-fbc__quote { margin: 14px 20px 0; background: var(--surface-2); border-radius: 12px; padding: 12px 15px; display: flex; gap: 11px; }
82
+ .ui-fbc__quote { margin: 14px 20px 0; background: var(--surface-2); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */ border-radius: 12px; padding: 12px 15px; display: flex; gap: 11px; }
83
83
  .ui-fbc__qm { color: var(--accent); font-size: var(--text-xl); font-weight: var(--weight-bold); line-height: .9; flex: none; opacity: .55; }
84
- .ui-fbc__quote q { quotes: none; color: var(--dim); font-style: italic; font-size: calc(var(--text-sm) + 0.5px); line-height: 1.55; }
84
+ .ui-fbc__quote q { quotes: none; color: var(--text); font-style: italic; font-size: calc(var(--text-sm) + 0.5px); line-height: 1.55; }
85
85
  .ui-fbc__body { padding: 14px 20px 0; }
86
86
  .ui-fbcomposer textarea {
87
87
  width: 100%; min-height: 96px; resize: vertical; font-family: var(--font-sans); font-size: var(--text-base); line-height: 1.55;
88
- color: var(--strong); background: var(--surface-2); border: 0; border-radius: 12px; padding: 13px 15px; transition: box-shadow var(--dur-fast) var(--ease);
88
+ color: var(--strong); background: var(--surface-2); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ border: 0; border-radius: 12px; padding: 13px 15px; transition: box-shadow var(--dur-fast) var(--ease);
89
89
  }
90
- .ui-fbcomposer textarea::placeholder { color: var(--muted); }
91
- .ui-fbcomposer textarea:focus { outline: none; box-shadow: 0 0 0 2px var(--accent); }
90
+ .ui-fbcomposer textarea::placeholder { /* muted-ink: placeholder — empty feedback field */ color: var(--muted); }
91
+ .ui-fbcomposer textarea:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); }
92
92
  .ui-fbc__foot { display: flex; align-items: center; justify-content: flex-end; gap: 12px; padding: 16px 20px 18px; }
93
93
 
94
94
  .ui-fbbtn { font-family: var(--font-sans); font-size: calc(var(--text-sm) + 0.5px); font-weight: var(--weight-semibold); border-radius: 10px; padding: 9px 17px; cursor: pointer; border: 0; transition: filter var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease); }
95
- .ui-fbbtn.ghost { background: transparent; color: var(--dim); }
96
- .ui-fbbtn.ghost:hover { color: var(--strong); background: var(--surface-2); }
95
+ .ui-fbbtn.ghost { background: transparent; color: var(--text); }
96
+ .ui-fbbtn.ghost:hover { color: var(--strong); background: var(--surface-2); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ }
97
97
  .ui-fbbtn.primary { background: var(--accent-strong); color: var(--accent-contrast); display: inline-flex; align-items: center; gap: 9px; }
98
98
  .ui-fbbtn.primary:hover { filter: brightness(1.08); }
99
99
  /* Same flat pair as .ui-btn:disabled, for the same reason (#220). */
100
100
  .ui-fbbtn.primary:disabled {
101
- background: var(--disabled-surface);
102
- color: var(--disabled-ink);
101
+ background: var(--disabled-surface); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */
102
+ /* muted-ink: state — unavailable control */ color: var(--disabled-ink);
103
103
  cursor: default;
104
104
  filter: none;
105
105
  }
@@ -108,14 +108,14 @@
108
108
  @keyframes uiFbSpin { to { transform: rotate(360deg); } }
109
109
 
110
110
  /* Success state */
111
- .ui-fbc__done { padding: 30px 26px 24px; text-align: center; background: radial-gradient(112% 60% at 50% 0%, var(--glow-green), transparent 62%); }
111
+ .ui-fbc__done { padding: 30px 26px 24px; text-align: center; background: radial-gradient(112% 60% at 50% 0%, var(--glow-green), transparent 62%); /* ring-gap: inherit — translucent wash retains the opaque containing gap. */ }
112
112
  .ui-fbc__ck { width: 88px; height: 88px; margin: 0 auto 13px; display: grid; place-items: center; }
113
113
  .ui-fbck { width: 88px; height: 88px; overflow: visible; display: block; transform-origin: center; animation: uiFbCkSpring 1s cubic-bezier(.22,1,.3,1) both; } /* motion: choreographed — the frame the mark and the comet are timed against */
114
114
  .ui-fbck-t { fill: none; stroke: var(--surface-2); stroke-width: 9; stroke-linecap: round; stroke-linejoin: round; }
115
115
  .ui-fbck-m { fill: none; stroke: var(--green); stroke-width: 9; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 118; stroke-dashoffset: 118; animation: uiFbCkDraw .6s cubic-bezier(.5,0,.3,1) .1s forwards; /* motion: choreographed — the mark draws while the spring is still settling */ }
116
116
  .ui-fbck-s { fill: none; stroke: var(--sheen); stroke-width: 9; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 15 400; stroke-dashoffset: 133; filter: drop-shadow(0 0 8px var(--green)) drop-shadow(0 0 15px var(--green)); opacity: 0; animation: uiFbCkComet .6s cubic-bezier(.5,0,.3,1) .1s forwards; /* motion: choreographed — the comet rides the mark, so it shares its duration and delay exactly */ }
117
117
  .ui-fbc__done h4 { color: var(--strong); font-size: calc(var(--text-lg) + 1px); font-weight: var(--weight-semibold); letter-spacing: -.01em; margin-bottom: 7px; }
118
- .ui-fbc__done p { color: var(--dim); font-size: calc(var(--text-sm) + 0.5px); line-height: 1.55; max-width: var(--prose-caption); margin: 0 auto; }
118
+ .ui-fbc__done p { color: var(--text); font-size: calc(var(--text-sm) + 0.5px); line-height: 1.55; max-width: var(--prose-caption); margin: 0 auto; }
119
119
  .ui-fbc__done p b { color: var(--text); font-weight: var(--weight-semibold); }
120
120
  .ui-fbc__done .row { margin-top: 18px; display: flex; gap: 10px; justify-content: center; }
121
121
  @keyframes uiFbCkDraw { to { stroke-dashoffset: 0; } }
@@ -123,7 +123,7 @@
123
123
  @keyframes uiFbCkSpring { 0% { transform: scale(1.32); } 62% { transform: scale(.965); } 82% { transform: scale(1.02); } 100% { transform: scale(1); } }
124
124
 
125
125
  /* Error banner */
126
- .ui-fbc__err { margin: 14px 20px 0; background: color-mix(in srgb, var(--pink) 14%, transparent); color: var(--pink); font-size: var(--text-sm); padding: 11px 15px; border-radius: 11px; display: none; }
126
+ .ui-fbc__err { margin: 14px 20px 0; background: color-mix(in srgb, var(--pink) 14%, transparent); /* ring-gap: inherit — text-only validation message has no nested focus target. */ color: var(--pink); font-size: var(--text-sm); padding: 11px 15px; border-radius: 11px; display: none; }
127
127
  .ui-fbc__err.show { display: block; }
128
128
  /* A failed send's line fades in; wireFeedback() adds the class as it shows it. */
129
129
  .ui-fbc__err.is-entering { animation: m-fade var(--dur-fast) var(--ease-out) both; }
@@ -9,12 +9,12 @@
9
9
  .ui-footer {
10
10
  --footer-surface: transparent;
11
11
  --footer-border: var(--border);
12
- --footer-link: var(--muted);
12
+ --footer-link: var(--text);
13
13
  --footer-link-hover: var(--strong);
14
14
  --footer-title: var(--strong);
15
- --footer-text: var(--dim);
15
+ --footer-text: var(--text);
16
16
 
17
- background: var(--footer-surface);
17
+ background: var(--footer-surface); /* ring-gap: inherit — transparent footer inherits; the app variant composes its opaque surface. */
18
18
  border-top: 1px solid var(--footer-border);
19
19
  color: var(--footer-text);
20
20
  font-family: var(--font-sans);
@@ -55,7 +55,7 @@
55
55
  border-radius: 10px;
56
56
  border: 1px solid var(--footer-border);
57
57
  color: var(--footer-link);
58
- background: var(--surface);
58
+ background: var(--surface); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */
59
59
  transition:
60
60
  color var(--dur-fast) var(--ease),
61
61
  border-color var(--dur-fast) var(--ease),
@@ -110,7 +110,7 @@
110
110
 
111
111
  /* app — compact, in-product: surface background + tighter padding. */
112
112
  .ui-footer--app {
113
- --footer-surface: var(--surface);
113
+ --footer-surface: var(--surface); --ring-gap: var(--footer-surface);
114
114
  border-top: 1px solid var(--footer-border);
115
115
  }
116
116
  .ui-footer--app .ui-footer__in { max-width: none; padding: 12px clamp(14px, 2.4vw, 22px); }
@@ -13,7 +13,7 @@
13
13
  /* Required marker. Decoration only — the control carries `required`, which is
14
14
  what assistive technology reads, so this asterisk is aria-hidden. */
15
15
  .ui-field__req { color: var(--pink); margin-left: 4px; }
16
- .ui-field__hint { color: var(--muted); font-size: var(--text-sm); margin-top: 7px; line-height: 1.5; }
16
+ .ui-field__hint { color: var(--text); font-size: var(--text-sm); margin-top: 7px; line-height: 1.5; }
17
17
  .ui-field__error { color: var(--pink); font-size: var(--text-sm); margin-top: 7px; display: flex; align-items: center; gap: 6px; }
18
18
  .ui-field__error svg { width: 16px; height: 16px; flex: none; stroke-width: 2.4; }
19
19
 
@@ -23,7 +23,7 @@
23
23
  font-family: var(--font-sans);
24
24
  font-size: var(--text-base);
25
25
  color: var(--strong);
26
- background: var(--surface-2);
26
+ background: var(--surface-2); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */
27
27
  border: 1px solid var(--border);
28
28
  border-radius: var(--radius-md);
29
29
  padding: 12px 15px;
@@ -36,16 +36,16 @@
36
36
  .ui-textarea:hover,
37
37
  .ui-select:hover { border-color: var(--border-strong); }
38
38
 
39
- .ui-input:focus,
40
- .ui-textarea:focus,
41
- .ui-select:focus {
42
- outline: none;
39
+ .ui-input:focus-visible,
40
+ .ui-textarea:focus-visible,
41
+ .ui-select:focus-visible {
42
+ outline: 2px solid transparent;
43
43
  border-color: var(--accent);
44
44
  box-shadow: var(--ring);
45
45
  }
46
46
 
47
47
  .ui-input::placeholder,
48
- .ui-textarea::placeholder { color: var(--muted); opacity: 0.9; }
48
+ .ui-textarea::placeholder { /* muted-ink: placeholder — empty form field */ color: var(--muted); opacity: 0.9; }
49
49
 
50
50
  /* The value a reader typed is still the value; it goes quiet, it does not fade
51
51
  into the field under it the way an opacity would take both down together. #220 */
@@ -57,15 +57,15 @@
57
57
  .ui-input:disabled,
58
58
  .ui-select:disabled,
59
59
  .ui-textarea:disabled {
60
- background: var(--disabled-surface);
60
+ background: var(--disabled-surface); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */
61
61
  border-color: var(--disabled-border);
62
- color: var(--disabled-ink);
62
+ /* muted-ink: state — unavailable control */ color: var(--disabled-ink);
63
63
  cursor: not-allowed;
64
64
  }
65
65
 
66
66
  .ui-input.is-invalid,
67
67
  .ui-textarea.is-invalid { border-color: var(--pink); }
68
- .ui-input.is-invalid:focus { box-shadow: 0 0 0 3px var(--glow-pink); }
68
+ /* Invalid fields keep the shared focus indicator; their border carries the error. */
69
69
 
70
70
  /* Input with a leading icon / adornment */
71
71
  .ui-input-group { position: relative; display: flex; align-items: center; }
@@ -75,7 +75,7 @@
75
75
  left: 14px;
76
76
  width: 17px;
77
77
  height: 17px;
78
- color: var(--muted);
78
+ /* muted-ink: glyph — field icon */ color: var(--muted);
79
79
  pointer-events: none;
80
80
  }
81
81
  .ui-input-group__icon svg { width: 100%; height: 100%; stroke: currentColor; fill: none; stroke-width: 2.2; }
@@ -117,7 +117,7 @@
117
117
  margin-top: 1px;
118
118
  border: 1.5px solid var(--border-strong);
119
119
  border-radius: var(--radius-xs);
120
- background: var(--surface-2);
120
+ background: var(--surface-2); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */
121
121
  cursor: pointer;
122
122
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
123
123
  position: relative;
@@ -159,7 +159,7 @@
159
159
  border-radius: 50%;
160
160
  background: var(--accent-contrast);
161
161
  }
162
- .ui-check input:focus-visible { outline: none; box-shadow: var(--ring); }
162
+ .ui-check input:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); }
163
163
 
164
164
  /* Toggle switch */
165
165
  .ui-switch {
@@ -175,7 +175,7 @@
175
175
  .ui-switch__track {
176
176
  position: absolute;
177
177
  inset: 0;
178
- background: var(--surface-3);
178
+ background: var(--surface-3); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */
179
179
  border-radius: var(--radius-pill);
180
180
  cursor: pointer;
181
181
  transition: background var(--dur-med) var(--ease);
@@ -201,5 +201,5 @@
201
201
  .ui-switch input:checked + .ui-switch__track::before {
202
202
  transform: translateX(calc(var(--sw-w) - var(--sw-h)));
203
203
  }
204
- .ui-switch input:focus-visible + .ui-switch__track { box-shadow: var(--ring); }
204
+ .ui-switch input:focus-visible + .ui-switch__track { outline: 2px solid transparent; box-shadow: var(--ring); }
205
205
  .ui-switch input:disabled + .ui-switch__track { opacity: 0.5; cursor: not-allowed; }