@apliteni/apliteni-ui 0.36.0 → 0.37.1

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.
@@ -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,29 +76,29 @@
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); /* 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; }
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
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
90
  .ui-fbcomposer textarea::placeholder { /* muted-ink: placeholder — empty feedback field */ color: var(--muted); }
91
- .ui-fbcomposer textarea:focus { outline: none; box-shadow: 0 0 0 2px var(--accent); }
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
95
  .ui-fbbtn.ghost { background: transparent; color: var(--text); }
96
- .ui-fbbtn.ghost:hover { color: var(--strong); background: var(--surface-2); }
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);
101
+ background: var(--disabled-surface); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */
102
102
  /* muted-ink: state — unavailable control */ color: var(--disabled-ink);
103
103
  cursor: default;
104
104
  filter: none;
@@ -108,7 +108,7 @@
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; }
@@ -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; }
@@ -14,7 +14,7 @@
14
14
  --footer-title: var(--strong);
15
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); }
@@ -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,10 +36,10 @@
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
  }
@@ -57,7 +57,7 @@
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
62
  /* muted-ink: state — unavailable control */ color: var(--disabled-ink);
63
63
  cursor: not-allowed;
@@ -65,7 +65,7 @@
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; }
@@ -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; }
@@ -13,7 +13,7 @@
13
13
  display: grid;
14
14
  grid-template-columns: max-content 1fr;
15
15
  min-height: calc(100vh - var(--ui-app-top, 0px));
16
- background: var(--bg);
16
+ background: var(--bg); /* ring-gap: inherit — page background inherits root overrides. */
17
17
  color: var(--text);
18
18
  font-family: var(--font-sans);
19
19
  /* The rail's width, and the one property the fold moves: the nav's own column
@@ -39,7 +39,7 @@
39
39
  gap: var(--space-5);
40
40
  padding: var(--space-5) var(--space-4);
41
41
  border-right: var(--ui-rail-rule) solid var(--border);
42
- background: var(--surface-2);
42
+ background: var(--surface-2); --ring-gap: var(--surface-2);
43
43
  position: sticky;
44
44
  top: var(--ui-app-top, 0px);
45
45
  height: calc(100vh - var(--ui-app-top, 0px));
@@ -63,7 +63,7 @@
63
63
  hovering a row do nothing. The danger row and the current row are left out by
64
64
  name, because this rule outranks the states nav.css writes for them.
65
65
  why: CONTRIBUTING.md#a-rule-that-outranks-another-cancels-every-state-that-other-one-writes */
66
- .ui-app__rail .ui-nav__item:not(.is-danger):not(.is-active):hover { background: var(--surface-3); }
66
+ .ui-app__rail .ui-nav__item:not(.is-danger):not(.is-active):hover { background: var(--surface-3); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ }
67
67
  .ui-app__brand {
68
68
  display: flex;
69
69
  align-items: center;
@@ -109,7 +109,7 @@
109
109
  smallest of three weak signals doing the most work, so it grows with the row. */
110
110
  /* Re-tuned at #295, which took the light rail down a step with the page. Below 720px
111
111
  the glyph is the whole row, so it is held at text grade rather than at 1.4.11's bar —
112
- stories/apps/shell-states.test.js:1400 `a resting glyph is dimmer but still legible`. */
112
+ stories/apps/shell-states.test.js:1403 `a resting glyph is dimmer but still legible`. */
113
113
  .ui-app__rail .ui-nav__ic svg { opacity: 0.66; }
114
114
  .ui-app__rail .ui-nav__item.is-active .ui-nav__ic svg,
115
115
  .ui-app__rail .ui-nav__item.is-current .ui-nav__ic svg { opacity: 1; stroke: currentColor; } /* motion: still — the glyph brightens with its row, whose own highlight already transitions */
@@ -153,7 +153,7 @@
153
153
  width: var(--ui-app-av); height: var(--ui-app-av); flex: none;
154
154
  margin-inline-start: calc((var(--ui-nav-strip) - var(--ui-app-av)) / 2);
155
155
  border-radius: var(--radius-pill);
156
- background: var(--surface-3);
156
+ background: var(--surface-3); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */
157
157
  color: var(--strong);
158
158
  display: grid; place-items: center;
159
159
  font-size: calc(var(--text-xs) + 1px);
@@ -187,7 +187,7 @@
187
187
  font-weight: inherit;
188
188
  letter-spacing: normal;
189
189
  }
190
- .ui-app .ui-app__user-trigger:hover { background: var(--surface-3); }
190
+ .ui-app .ui-app__user-trigger:hover { background: var(--surface-3); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ }
191
191
  .ui-app__user-trigger .ui-app__who { flex: 1 1 auto; text-align: left; }
192
192
  /* The panel is portalled onto <body>, so nothing here may hang off .ui-app: by the
193
193
  time it is open it is no longer inside the rail that drew it.
@@ -269,7 +269,7 @@
269
269
  why: docs/specification.md#elevation */
270
270
  :where(.ui-app.is-collapsed) .ui-app__rail .ui-nav__item:is(:hover, :focus-visible) > .ui-nav__label,
271
271
  :where(.ui-app.is-collapsed) .ui-app__rail .ui-app__user-trigger:is(:hover, :focus-visible) > .ui-app__who,
272
- .ui-app__rail .ui-app__fold:is(:hover, :focus-visible) > .ui-nav__label { position: fixed; left: calc(var(--ui-rail-w) + var(--space-2)); z-index: var(--z-dropdown); width: max-content; max-width: 15rem; margin-block: -7px; padding: 6px 10px; border: 1px solid var(--border-strong); box-shadow: inset 0 0 0 1px var(--elev-edge, var(--border)), var(--elev-drop); border-radius: var(--radius-xs); background: var(--surface-3); color: var(--strong); opacity: 1; pointer-events: none; }
272
+ .ui-app__rail .ui-app__fold:is(:hover, :focus-visible) > .ui-nav__label { position: fixed; left: calc(var(--ui-rail-w) + var(--space-2)); z-index: var(--z-dropdown); width: max-content; max-width: 15rem; margin-block: -7px; padding: 6px 10px; border: 1px solid var(--border-strong); box-shadow: inset 0 0 0 1px var(--elev-edge, var(--border)), var(--elev-drop); border-radius: var(--radius-xs); background: var(--surface-3); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */ color: var(--strong); opacity: 1; pointer-events: none; }
273
273
  @supports (anchor-name: --a) and (anchor-scope: --a) {
274
274
  .ui-app.is-collapsed,
275
275
  .ui-app:has(.ui-app__fold) { anchor-scope: --ui-rail; }
@@ -318,7 +318,7 @@
318
318
  height: var(--ui-app-band);
319
319
  flex: none;
320
320
  padding-inline: clamp(24px, 4vw, 56px);
321
- background: var(--bg);
321
+ background: var(--bg); /* ring-gap: inherit — page background inherits root overrides. */
322
322
  border-bottom: 1px solid var(--border);
323
323
  /* Sticky for the reason the rail beside it is: chrome that scrolls away is chrome
324
324
  you scroll back up for. */
@@ -362,7 +362,7 @@
362
362
  Artur chose this over three alternatives on #318; the survey of what seven
363
363
  other systems do is docs/reviews/318-search-field.md.
364
364
  why: docs/specification.md#elevation */
365
- background: var(--surface);
365
+ background: var(--surface); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */
366
366
  border: 1px solid var(--border);
367
367
  border-radius: var(--radius-md);
368
368
  cursor: pointer;
@@ -373,7 +373,7 @@
373
373
  without `.ui-btn`, so base.css's shared rule never reached it and focus fell
374
374
  through to the browser's own square outline, drawn around a 12px radius.
375
375
  why: docs/specification.md#the-focus-ring */
376
- .ui-app__search:focus-visible { outline: none; box-shadow: var(--ring); border-color: var(--accent); }
376
+ .ui-app__search:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); border-color: var(--accent); }
377
377
  /* The same glyph the palette draws in its own search box, at the same box and the
378
378
  same stroke: the trigger and the thing it opens are not two search marks.
379
379
  src/styles/command-palette.css:107 `.ui-cmdk__search-ic svg` */
@@ -467,7 +467,7 @@
467
467
  .ui-app__rail .ui-nav__item,
468
468
  .ui-app__rail .ui-app__user-trigger { min-height: 44px; }
469
469
  .ui-app__rail .ui-nav__item:is(:hover, :focus-visible) > .ui-nav__label,
470
- .ui-app__rail .ui-app__user-trigger:is(:hover, :focus-visible) > .ui-app__who { position: fixed; left: calc(var(--ui-rail-w) + var(--space-2)); z-index: var(--z-dropdown); width: max-content; max-width: 15rem; margin-block: -7px; padding: 6px 10px; border: 1px solid var(--border-strong); box-shadow: inset 0 0 0 1px var(--elev-edge, var(--border)), var(--elev-drop); border-radius: var(--radius-xs); background: var(--surface-3); color: var(--strong); opacity: 1; pointer-events: none; }
470
+ .ui-app__rail .ui-app__user-trigger:is(:hover, :focus-visible) > .ui-app__who { position: fixed; left: calc(var(--ui-rail-w) + var(--space-2)); z-index: var(--z-dropdown); width: max-content; max-width: 15rem; margin-block: -7px; padding: 6px 10px; border: 1px solid var(--border-strong); box-shadow: inset 0 0 0 1px var(--elev-edge, var(--border)), var(--elev-drop); border-radius: var(--radius-xs); background: var(--surface-3); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */ color: var(--strong); opacity: 1; pointer-events: none; }
471
471
  @supports (anchor-name: --a) and (anchor-scope: --a) {
472
472
  .ui-app { anchor-scope: --ui-rail; }
473
473
  .ui-app__rail { anchor-name: --ui-rail; }
@@ -501,7 +501,7 @@
501
501
  max-width: var(--panel-md);
502
502
  /* Floats over a spotlit page, so it takes the floating step and the hairline.
503
503
  why: docs/specification.md#elevation */
504
- background: var(--bg-elevated);
504
+ background: var(--bg-elevated); --ring-gap: var(--bg-elevated);
505
505
  border: 1px solid var(--border);
506
506
  border-radius: 22px;
507
507
  padding: 38px 36px 32px;
@@ -523,7 +523,7 @@
523
523
  width: 38px; height: 38px; flex: none;
524
524
  border-radius: 11px;
525
525
  display: grid; place-items: center;
526
- background: var(--glow-purple);
526
+ background: var(--glow-purple); /* ring-gap: inherit — translucent wash retains the opaque containing gap. */
527
527
  color: var(--accent);
528
528
  }
529
529
  .ui-scope__icon svg { width: 21px; height: 21px; stroke: currentColor; fill: none; stroke-width: 1.8; }
@@ -544,7 +544,7 @@
544
544
  font-size: calc(var(--text-xs) + 1px);
545
545
  letter-spacing: 0.06em;
546
546
  color: var(--purple-mid);
547
- background: var(--glow-purple);
547
+ background: var(--glow-purple); /* ring-gap: inherit — translucent wash retains the opaque containing gap. */
548
548
  border-radius: 999px;
549
549
  padding: 6px 14px;
550
550
  margin-bottom: 26px;
@@ -589,7 +589,7 @@
589
589
  width: 46px; height: 46px;
590
590
  border-radius: 13px;
591
591
  display: grid; place-items: center;
592
- background: var(--glow-purple);
592
+ background: var(--glow-purple); /* ring-gap: inherit — translucent wash retains the opaque containing gap. */
593
593
  color: var(--accent);
594
594
  margin-bottom: 16px;
595
595
  }
@@ -99,7 +99,7 @@
99
99
  display: grid;
100
100
  place-items: center;
101
101
  /* muted-ink: glyph — permission seal */ color: var(--dim);
102
- background: color-mix(in srgb, var(--text) 7%, transparent);
102
+ background: color-mix(in srgb, var(--text) 7%, transparent); /* ring-gap: inherit — decorative paint has no nested focus target. */
103
103
  }
104
104
  .ui-denied__seal svg {
105
105
  width: 20px;
@@ -81,7 +81,7 @@
81
81
  .m-skeleton {
82
82
  position: relative;
83
83
  overflow: hidden;
84
- background: color-mix(in srgb, var(--text) 8%, transparent);
84
+ background: color-mix(in srgb, var(--text) 8%, transparent); /* ring-gap: inherit — loading placeholder has no nested focus target. */
85
85
  border-radius: var(--radius-sm, 8px);
86
86
  }
87
87
  .m-skeleton::after {
@@ -69,9 +69,9 @@
69
69
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
70
70
  }
71
71
  .ui-nav .ui-nav__item { color: var(--text); } /* (0,2,0): a host sheet's a:link is (0,1,1) — why: docs/specification.md#the-page-shell */
72
- .ui-nav__item:hover { background: var(--surface-2); color: var(--strong); }
72
+ .ui-nav__item:hover { background: var(--surface-2); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ color: var(--strong); }
73
73
  .ui-nav__item.is-active {
74
- background: var(--surface-3);
74
+ background: var(--surface-3); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */
75
75
  color: var(--strong);
76
76
  font-weight: 500;
77
77
  }
@@ -88,8 +88,8 @@
88
88
  background: var(--accent);
89
89
  transform: translateY(-50%); /* motion: still — a mark on the current row, whose own highlight already transitions; this centres it, it does not travel */
90
90
  }
91
- .ui-nav__item:focus-visible { outline: none; box-shadow: var(--ring); }
92
- .ui-nav__item.is-active:focus-visible { box-shadow: var(--ring); }
91
+ .ui-nav__item:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); }
92
+ .ui-nav__item.is-active:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); }
93
93
 
94
94
  .ui-nav__ic { display: inline-flex; flex: none; }
95
95
  .ui-nav__ic svg { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 2.2; opacity: 0.72; }
@@ -108,10 +108,10 @@
108
108
  /* Destructive row (sign out, revoke). Body ink at rest, --pink once a pointer or
109
109
  the keyboard reaches it — as .ui-btn--danger and .ui-dropdown__item do. */
110
110
  .ui-nav__item.is-danger { color: var(--text); }
111
- .ui-nav__item.is-danger:hover { background: var(--glow-pink); color: var(--pink); }
111
+ .ui-nav__item.is-danger:hover { background: var(--glow-pink); /* ring-gap: inherit — translucent wash retains the opaque containing gap. */ color: var(--pink); }
112
112
  /* Tabbing onto it is the same "about to click this" the pointer means. The ring
113
113
  says where you are; only this says what the row does. */
114
- .ui-nav__item.is-danger:focus-visible { background: var(--glow-pink); color: var(--pink); }
114
+ .ui-nav__item.is-danger:focus-visible { background: var(--glow-pink); /* ring-gap: inherit — translucent wash retains the opaque containing gap. */ color: var(--pink); }
115
115
  /* An unavailable row uses disabled ink directly; opacity would also fade its
116
116
  background and pull its label toward the rail behind it (#220). */
117
117
  .ui-nav__item.is-disabled { /* muted-ink: state — unavailable control */ color: var(--disabled-ink); cursor: default; pointer-events: none; }
@@ -124,7 +124,7 @@
124
124
  rules so it cannot out-rank them — sign out is never the current row, but a
125
125
  rule that could take its wash would be one to argue with later. */
126
126
  .ui-nav__item.is-active:not(.is-danger):hover {
127
- background: color-mix(in srgb, var(--text) 10%, var(--surface-3));
127
+ background: color-mix(in srgb, var(--text) 10%, var(--surface-3)); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */
128
128
  }
129
129
 
130
130
  /* Per-item badge / counter */
@@ -140,17 +140,17 @@
140
140
  font-size: var(--text-xs);
141
141
  font-weight: 600;
142
142
  line-height: 1;
143
- background: var(--surface-3);
143
+ background: var(--surface-3); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */
144
144
  color: var(--text);
145
145
  /* The count fades with the words. It is never lost: sidebarNav() spells it
146
146
  into the row's aria-label, so "Pending 3" is what a reader hears at either
147
147
  width. */
148
148
  transition: opacity var(--dur-fast) var(--ease);
149
149
  }
150
- .ui-nav__badge.is-accent { background: var(--glow-purple); color: var(--accent); }
151
- .ui-nav__badge.is-live { background: var(--chip-success-fill); color: var(--chip-success-ink); }
152
- .ui-nav__badge.is-danger { background: var(--glow-pink); color: var(--pink); }
153
- .ui-nav__item.is-active .ui-nav__badge.is-neutral { background: var(--surface-3); color: var(--strong); }
150
+ .ui-nav__badge.is-accent { background: var(--glow-purple); /* ring-gap: inherit — translucent wash retains the opaque containing gap. */ color: var(--accent); }
151
+ .ui-nav__badge.is-live { background: var(--chip-success-fill); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */ color: var(--chip-success-ink); }
152
+ .ui-nav__badge.is-danger { background: var(--glow-pink); /* ring-gap: inherit — translucent wash retains the opaque containing gap. */ color: var(--pink); }
153
+ .ui-nav__item.is-active .ui-nav__badge.is-neutral { background: var(--surface-3); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */ color: var(--strong); }
154
154
  /* Same shape, one row up: on an active row the accent counter takes the row's
155
155
  OWN surface instead of laying its wash over it. An active row is already
156
156
  painted --surface-3, so --glow-purple there is two tints stacked. Every
@@ -160,7 +160,7 @@
160
160
  it, the largest that clears being 0.06 in dark Nebula and 0.02 in light
161
161
  Emerald. So the accent wash is painted on a base surface, never a raised one.
162
162
  See #157 and docs/specification.md#colour-and-contrast. */
163
- .ui-nav__item.is-active .ui-nav__badge.is-accent { background: var(--surface-3); color: var(--accent); }
163
+ .ui-nav__item.is-active .ui-nav__badge.is-accent { background: var(--surface-3); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */ color: var(--accent); }
164
164
 
165
165
  /* Collapsible group */
166
166
  .ui-nav__group { display: flex; flex-direction: column; }
@@ -275,7 +275,7 @@
275
275
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
276
276
  }
277
277
  .ui-nav__tab:hover { color: var(--strong); }
278
- .ui-nav__tab:focus-visible { outline: none; box-shadow: var(--ring); border-radius: 8px; }
278
+ .ui-nav__tab:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); border-radius: 8px; }
279
279
  .ui-nav__tab.is-active { color: var(--strong); }
280
280
 
281
281
  /* underline variant */
@@ -294,9 +294,9 @@
294
294
 
295
295
  /* pill variant */
296
296
  .ui-nav--tabs.is-pill .ui-nav__tab { border-radius: var(--radius-pill); padding: 8px 15px; }
297
- .ui-nav--tabs.is-pill .ui-nav__tab:hover { background: var(--surface-2); }
297
+ .ui-nav--tabs.is-pill .ui-nav__tab:hover { background: var(--surface-2); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ }
298
298
  .ui-nav--tabs.is-pill .ui-nav__tab.is-active {
299
- background: var(--glow-purple);
299
+ background: var(--glow-purple); /* ring-gap: inherit — translucent wash retains the opaque containing gap. */
300
300
  color: var(--accent);
301
301
  }
302
302
 
@@ -329,7 +329,7 @@
329
329
  does above: a linked crumb is an <a>, and a host stylesheet's `a:link` is
330
330
  (0,1,1), which outranks a bare (0,1,0) class. */
331
331
  .ui-nav .ui-nav__crumb { color: var(--text); }
332
- a.ui-nav__crumb:hover { color: var(--strong); background: var(--surface-2); }
333
- a.ui-nav__crumb:focus-visible { outline: none; box-shadow: var(--ring); }
332
+ a.ui-nav__crumb:hover { color: var(--strong); background: var(--surface-2); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ }
333
+ a.ui-nav__crumb:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); }
334
334
  .ui-nav__crumb.is-current { color: var(--strong); font-weight: 500; }
335
335
  .ui-nav__crumb .ui-nav__ic svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 2.5; }
@@ -69,7 +69,7 @@
69
69
  rule about an ENABLED control out of the disabled-rule sweep in
70
70
  stories/guidelines/accessibility-floor.test.js, which reads selector text. */
71
71
  .ui-pager__page.is-current:not([disabled]) {
72
- background: var(--surface-3);
72
+ background: var(--surface-3); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */
73
73
  border-color: var(--border);
74
74
  color: var(--strong);
75
75
  font-weight: var(--weight-semibold);
@@ -5,7 +5,7 @@
5
5
 
6
6
  .ui-seg {
7
7
  display: inline-flex;
8
- background: var(--surface-2);
8
+ background: var(--surface-2); --ring-gap: var(--surface-2);
9
9
  border-radius: var(--radius-2xl);
10
10
  padding: 3px;
11
11
  gap: 2px;
@@ -54,4 +54,4 @@
54
54
  .ui-accent-picker button.is-active {
55
55
  box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--swatch, var(--accent));
56
56
  }
57
- .ui-accent-picker button:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--swatch, var(--accent)); }
57
+ .ui-accent-picker button:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); }
@@ -11,15 +11,17 @@
11
11
  * the foot. Markup comes from success() in components/index (re-exported).
12
12
  * ========================================================================== */
13
13
 
14
- .ui-sx {
15
- --sx-green: var(--green);
14
+ .ui-sx, .ui-success__check {
16
15
  --sx-tint: color-mix(in srgb, var(--green) 18%, transparent);
17
16
  --sx-track: color-mix(in srgb, var(--green) 32%, transparent);
17
+ }
18
18
 
19
+ .ui-sx {
20
+ --sx-green: var(--green);
19
21
  position: relative;
20
22
  overflow: hidden;
21
23
  border-radius: 20px;
22
- background: var(--bg-elevated); /* the floating step carries it. #295 */
24
+ background: var(--bg-elevated); --ring-gap: var(--bg-elevated); /* Elevated surface for the confirmation and its actions. */
23
25
  border: 1px solid var(--border);
24
26
  isolation: isolate;
25
27
  }
@@ -82,7 +84,7 @@
82
84
  }
83
85
  .ui-sx--split .ui-sx__visual {
84
86
  display: grid; place-items: center; padding: 40px 24px;
85
- background: color-mix(in srgb, var(--green) 8%, transparent);
87
+ background: color-mix(in srgb, var(--green) 8%, transparent); /* ring-gap: inherit — decorative illustration has no nested focus target. */
86
88
  border-right: 1px solid var(--border);
87
89
  }
88
90
  .ui-sx--split .ui-sx__check { width: 120px; height: 120px; }
@@ -160,7 +162,7 @@
160
162
  .ui-sx--hero .ui-sx__count { justify-content: center; }
161
163
  .ui-sx__count-ring {
162
164
  flex: none; width: 16px; height: 16px; border-radius: 50%;
163
- background: conic-gradient(var(--green) var(--sx-a, 360deg), var(--surface-2) 0);
165
+ background: conic-gradient(var(--green) var(--sx-a, 360deg), var(--surface-2) 0); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */
164
166
  mask: radial-gradient(circle, transparent 5px, #000 6px);
165
167
  -webkit-mask: radial-gradient(circle, transparent 5px, #000 6px);
166
168
  animation: ui-sx-sweep var(--sx-secs, 5s) linear forwards; /* motion: ambient — the caller sets the countdown; the ring just spends it */
@@ -176,7 +178,7 @@
176
178
  .ui-sx__tick { animation: none; stroke-dashoffset: 0; }
177
179
  .ui-sx__ring { display: none; }
178
180
  .ui-sx__confetti { display: none; }
179
- .ui-sx__count-ring { animation: none; background: var(--surface-2); }
181
+ .ui-sx__count-ring { animation: none; background: var(--surface-2); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */ }
180
182
  }
181
183
 
182
184
  /* ---- Narrow — split stacks -------------------------------------------- */
@@ -42,7 +42,7 @@
42
42
  border-spacing: 0;
43
43
  padding: 0 var(--space-2);
44
44
  }
45
- .ui-table--hover:not(.ui-table--zebra) tbody tr:hover td { background: var(--surface-2); }
45
+ .ui-table--hover:not(.ui-table--zebra) tbody tr:hover td { background: var(--surface-2); --ring-gap: var(--surface-2); }
46
46
  .ui-table--hover:not(.ui-table--zebra) tbody tr:hover td:first-child {
47
47
  border-top-left-radius: var(--radius-sm); border-bottom-left-radius: var(--radius-sm); }
48
48
  .ui-table--hover:not(.ui-table--zebra) tbody tr:hover td:last-child {
@@ -63,7 +63,7 @@
63
63
 
64
64
  .ui-table--zebra th { border-bottom-width: 2px; }
65
65
  .ui-table--zebra td { border-bottom: 0; }
66
- .ui-table--zebra tbody tr:nth-child(even) td { background: var(--surface-2); }
66
+ .ui-table--zebra tbody tr:nth-child(even) td { background: var(--surface-2); --ring-gap: var(--surface-2); }
67
67
  /* The stripe stays full-bleed (the #71 inset above is hover only), but it only reads right
68
68
  * if the end cells are inset from the tint's own edge, and `.ui-table td` sets
69
69
  * `padding-left: 0`. Only `--dense` supplied that inset, so `--zebra` without `--dense` —
@@ -74,7 +74,7 @@
74
74
  .ui-table--zebra th:last-child,
75
75
  .ui-table--zebra td:last-child { padding-right: var(--space-3); }
76
76
  .ui-table--zebra.ui-table--hover tbody tr:hover td,
77
- .ui-table--zebra tbody tr:hover td { background: color-mix(in srgb, var(--accent) 8%, transparent); }
77
+ .ui-table--zebra tbody tr:hover td { --ring-gap: inherit; background: color-mix(in srgb, var(--accent) 8%, transparent); /* ring-gap: inherit — translucent wash retains the opaque containing gap. */ }
78
78
 
79
79
  .ui-table__num { text-align: right; font-variant-numeric: tabular-nums; }
80
80
  .ui-table th.ui-table__num { text-align: right; }
@@ -22,7 +22,7 @@
22
22
  }
23
23
  .ui-tabs__tab:hover { color: var(--strong); }
24
24
  .ui-tabs__tab.is-active { color: var(--strong); border-bottom-color: var(--accent); }
25
- .ui-tabs__tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 7px 7px 0 0; }
25
+ .ui-tabs__tab:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); border-radius: 7px 7px 0 0; }
26
26
 
27
27
  .ui-tabs__panels { padding-top: 20px; }
28
28
  .ui-tabs__panel[hidden] { display: none; }
@@ -31,4 +31,4 @@
31
31
  .ui-tabs__panel.is-entering { animation: m-fade var(--dur-fast) var(--ease-out) both; }
32
32
  /* The panel is focusable (tabindex="0", see tabs.js) so a keyboard user can
33
33
  reach text-only content. Show the ring when they land on it. */
34
- .ui-tabs__panel:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; border-radius: 8px; }
34
+ .ui-tabs__panel:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); border-radius: 8px; }
@@ -25,7 +25,7 @@
25
25
  width: max-content;
26
26
  max-width: var(--prose-caption);
27
27
  padding: var(--space-2) var(--space-3);
28
- background: var(--ui-tip-bg);
28
+ background: var(--ui-tip-bg); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */
29
29
  /* The two-step edge and the drop: --border-strong outside, --border one pixel
30
30
  inside it, then the broad faint drop. why: docs/specification.md#elevation */
31
31
  border: 1px solid var(--border-strong);
@@ -9,7 +9,7 @@
9
9
  height: 52px;
10
10
  display: flex;
11
11
  align-items: center;
12
- background: var(--bg);
12
+ background: var(--bg); /* ring-gap: inherit — page background inherits root overrides. */
13
13
  border-bottom: 1px solid var(--border);
14
14
  }
15
15
  .topbar__in {
@@ -48,7 +48,7 @@
48
48
  display: none;
49
49
  align-items: center;
50
50
  gap: 2px;
51
- background: var(--surface-2);
51
+ background: var(--surface-2); --ring-gap: var(--surface-2);
52
52
  border: 1px solid var(--border);
53
53
  border-radius: 24px;
54
54
  padding: 3px;
@@ -66,7 +66,7 @@
66
66
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
67
67
  }
68
68
  .dtsw a:hover { color: var(--strong); }
69
- .dtsw a.cur { color: var(--strong); background: var(--surface); }
69
+ .dtsw a.cur { color: var(--strong); background: var(--surface); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ }
70
70
 
71
71
  /* Theme toggle — a single icon-only switch (no label). */
72
72
  .toggle {
@@ -75,7 +75,7 @@
75
75
  width: 34px;
76
76
  height: 34px;
77
77
  cursor: pointer;
78
- background: var(--surface);
78
+ background: var(--surface); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */
79
79
  color: var(--text);
80
80
  border: 1px solid var(--border);
81
81
  border-radius: 10px;
@@ -100,7 +100,7 @@
100
100
  font-size: calc(var(--text-sm) - 0.5px);
101
101
  font-weight: 500;
102
102
  color: var(--text);
103
- background: var(--surface);
103
+ background: var(--surface); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */
104
104
  border: 1px solid var(--border);
105
105
  border-radius: 24px;
106
106
  padding: 7px 11px 7px 13px;
@@ -122,7 +122,7 @@
122
122
  top: calc(100% + 9px);
123
123
  left: 0;
124
124
  min-width: 266px;
125
- background: var(--bg-elevated); /* the floating step — see dropdown.css. #295 */
125
+ background: var(--bg-elevated); --ring-gap: var(--bg-elevated); /* the floating step — see dropdown.css. #295 */
126
126
  /* The two-step edge and the drop: --border-strong outside, --border one pixel
127
127
  inside it, then the broad faint drop. why: docs/specification.md#elevation */
128
128
  border: 1px solid var(--border-strong);
@@ -182,7 +182,7 @@
182
182
  /* The menu is raised, so a hovered row takes the step ABOVE it — see
183
183
  src/styles/dropdown.css:155 `.ui-dropdown__item:hover`.
184
184
  why: docs/specification.md#elevation */
185
- .vopt:hover { background: var(--surface-3); }
185
+ .vopt:hover { background: var(--surface-3); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ }
186
186
  .vopt .vname { font-family: var(--font-sans); font-size: calc(var(--text-sm) - 0.5px); color: var(--strong); font-weight: 600; letter-spacing: 0.01em; }
187
187
  .vopt .vmeta { font-size: var(--text-xs); color: var(--text); margin-top: 2px; }
188
188
  .vopt .vbadge {
@@ -195,8 +195,8 @@
195
195
  flex: none;
196
196
  margin-top: 1px;
197
197
  }
198
- .vbadge.live { color: var(--chip-success-ink); background: var(--chip-success-fill); }
199
- .vbadge.arch { /* muted-ink: state — archived version */ color: var(--muted); background: var(--surface-3); }
198
+ .vbadge.live { color: var(--chip-success-ink); background: var(--chip-success-fill); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */ }
199
+ .vbadge.arch { /* muted-ink: state — archived version */ color: var(--muted); background: var(--surface-3); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */ }
200
200
  /* The switcher keeps its glyph and drops its label where the shell folds — the
201
201
  same step the rail folds at: docs/specification.md#breakpoints. */
202
202
  @media (max-width: 720px) { .vsw__btn .lbl { display: none; } }
@@ -225,7 +225,7 @@
225
225
  position: absolute;
226
226
  right: 0; top: 44px;
227
227
  width: 252px;
228
- background: var(--bg-elevated); /* the floating step — see dropdown.css. #295 */
228
+ background: var(--bg-elevated); --ring-gap: var(--bg-elevated); /* the floating step — see dropdown.css. #295 */
229
229
  /* The two-step edge and the drop: --border-strong outside, --border one pixel
230
230
  inside it, then the broad faint drop. why: docs/specification.md#elevation */
231
231
  border: 1px solid var(--border-strong);
@@ -258,7 +258,7 @@
258
258
  .amenu .ahead .anm { color: var(--strong); font-size: var(--text-sm); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
259
259
  .amenu .ahead .aem { color: var(--text); font-size: calc(var(--text-xs) + 0.5px); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
260
260
  .amenu a { display: flex; align-items: center; gap: 11px; padding: 10px 15px; color: var(--text); text-decoration: none; font-family: var(--font-sans); font-size: var(--text-sm); cursor: pointer; }
261
- .amenu a:hover { background: var(--surface-3); }
261
+ .amenu a:hover { background: var(--surface-3); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ }
262
262
  .amenu a.cur { color: var(--accent); }
263
263
  .amenu a svg { width: 19px; height: 19px; flex: none; stroke: currentColor; fill: none; stroke-width: 2; opacity: 0.85; }
264
264
  .amenu .asep { height: 1px; background: var(--border); margin: 5px 0; }