@apliteni/apliteni-ui 0.92.0 → 0.94.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.
Files changed (55) hide show
  1. package/README.md +5 -7
  2. package/guidelines/colour-and-theming.md +2 -0
  3. package/package.json +1 -1
  4. package/react/README.md +2 -2
  5. package/react/dist/index.d.ts +3 -3
  6. package/src/assets/icons.js +1 -1
  7. package/src/components/account-nav.js +2 -2
  8. package/src/components/back.js +2 -2
  9. package/src/components/command-palette.js +2 -2
  10. package/src/components/drawer.js +1 -1
  11. package/src/components/dropdown.js +12 -12
  12. package/src/components/filter-bar.js +2 -2
  13. package/src/components/footer.js +1 -1
  14. package/src/components/index.js +1 -1
  15. package/src/components/loading.js +1 -1
  16. package/src/components/shell.js +19 -19
  17. package/src/components/stat.js +3 -3
  18. package/src/components/success.js +3 -3
  19. package/src/components/tooltip.js +1 -1
  20. package/src/components/topbar.js +1 -1
  21. package/src/html.js +1 -1
  22. package/src/index.css +1 -1
  23. package/src/index.d.ts +1 -1
  24. package/src/logic/accents.js +1 -1
  25. package/src/logic/filter-bar.js +4 -4
  26. package/src/styles/back.css +3 -3
  27. package/src/styles/base.css +3 -3
  28. package/src/styles/button.css +3 -3
  29. package/src/styles/callout.css +2 -2
  30. package/src/styles/card.css +8 -8
  31. package/src/styles/command-palette.css +14 -14
  32. package/src/styles/confirm.css +7 -7
  33. package/src/styles/drawer.css +8 -8
  34. package/src/styles/dropdown.css +21 -21
  35. package/src/styles/feedback.css +2 -2
  36. package/src/styles/field-zoom.css +1 -1
  37. package/src/styles/file-drop.css +1 -1
  38. package/src/styles/filter-bar.css +6 -6
  39. package/src/styles/footer.css +1 -1
  40. package/src/styles/input.css +6 -6
  41. package/src/styles/layout.css +54 -57
  42. package/src/styles/loading.css +1 -1
  43. package/src/styles/motion.css +1 -1
  44. package/src/styles/nav.css +39 -50
  45. package/src/styles/pagination.css +2 -2
  46. package/src/styles/reduced-motion.css +2 -2
  47. package/src/styles/segmented.css +3 -3
  48. package/src/styles/stat.css +3 -3
  49. package/src/styles/success.css +1 -1
  50. package/src/styles/table.css +6 -6
  51. package/src/styles/tap-zone.css +1 -1
  52. package/src/styles/tooltip.css +3 -3
  53. package/src/styles/topbar.css +8 -8
  54. package/src/tokens/accents.css +1 -1
  55. package/src/tokens/tokens.css +25 -25
@@ -1,5 +1,5 @@
1
1
  /* File drop — .ui-drop. One row at rest; the field-sized box stays with .ui-file.
2
- * why: docs/specification.md#react-file-drop */
2
+ * why: docs/components.md#react-file-drop */
3
3
  .ui-drop { position: relative; }
4
4
 
5
5
  /* What the region accepts the file for, above the control. */
@@ -14,13 +14,13 @@
14
14
 
15
15
  /* Shut, a panel is the trigger's width, never the 240px floor a standalone
16
16
  dropdown gets: it is laid out while hidden, so a wider one widened the page
17
- (#467). why: docs/specification.md#a-filter-row-holds-its-panels */
17
+ (#467). why: docs/components.md#a-filter-row-holds-its-panels */
18
18
  .ui-filter-bar .ui-dropdown__panel { min-width: 100%; max-width: 100%; overflow-wrap: anywhere; }
19
19
  /* The clear action takes the kit's bordered skin minus its fill: words belong on
20
20
  the page, not on a grey block, in any state — Artur, round r28. Rest stays at
21
21
  (0,1,1) so `.ui-btn:hover`'s accent wash still outranks it; the rules under it
22
22
  answer each state that paints a NEUTRAL fill, one step above the kit rule
23
- writing it. Held by filter-bar.test.js. why: docs/specification.md#a-filter-row-holds-its-panels */
23
+ writing it. Held by filter-bar.test.js. why: docs/components.md#a-filter-row-holds-its-panels */
24
24
  [data-filter-clear] button { background: transparent; }
25
25
  .ui-filter-bar [data-filter-clear] button:disabled,
26
26
  .ui-filter-bar [data-filter-clear] button[aria-disabled="true"],
@@ -35,7 +35,7 @@
35
35
 
36
36
  /* Open, a chip's menu takes the kit's menu floor, measured and slid by
37
37
  filterPanelFit(); `is-closing` holds that geometry through the fade. #549
38
- why: docs/specification.md#a-filter-row-holds-its-panels */
38
+ why: docs/components.md#a-filter-row-holds-its-panels */
39
39
  .ui-filter-bar__chip .ui-dropdown.open .ui-dropdown__panel,
40
40
  .ui-filter-bar__chip .ui-dropdown__panel.is-closing {
41
41
  min-width: min(var(--ui-filter-panel-floor, 240px), var(--ui-filter-panel-room, 100%));
@@ -57,7 +57,7 @@
57
57
  open menu too and left 9.8px of the check in a 240px menu — a chevron, not a
58
58
  tick. Open the menu is at the floor with room for the mark, and `is-closing`
59
59
  holds the open geometry through the fade, so it is open for this as well. #550
60
- why: docs/specification.md#a-filter-row-holds-its-panels */
60
+ why: docs/components.md#a-filter-row-holds-its-panels */
61
61
  .ui-filter-bar__chip .ui-dropdown:not(.open) .ui-dropdown__panel:not(.is-closing) .ui-dropdown__tick {
62
62
  flex-shrink: 1;
63
63
  min-width: 0;
@@ -68,7 +68,7 @@
68
68
  tab you are on, and keeps the kit's 16px check. The wash is a 1.2:1 tint, so the
69
69
  check is what identifies the state under WCAG 1.4.1; Apple, Material, Carbon,
70
70
  Primer and Radix all tick a chosen menu row. #549
71
- why: docs/specification.md#a-filter-row-holds-its-panels */
71
+ why: docs/components.md#a-filter-row-holds-its-panels */
72
72
  .ui-filter-bar .ui-dropdown__item.is-selected { background: var(--glow-purple); /* ring-gap: inherit — translucent wash retains the opaque containing gap. */ }
73
73
  /* Kit-wide a chosen label steps down to --text, which left the row in force the
74
74
  faintest in the panel. Under the wash it holds --strong. */
@@ -82,5 +82,5 @@
82
82
  /* Hover's other paint is a 1px edge, 1.06:1 on the wash, which left the row in force
83
83
  the one row a pointer got no answer from. Selection is a background highlight, so
84
84
  hover deepens it: 9% more accent, the step .ui-table--zebra gives a hovered row.
85
- why: docs/specification.md#a-filter-row-holds-its-panels */
85
+ why: docs/components.md#a-filter-row-holds-its-panels */
86
86
  .ui-filter-bar .ui-dropdown__item.is-selected:hover { background: color-mix(in srgb, var(--glow-purple), var(--accent) 9%); /* ring-gap: inherit — translucent wash retains the opaque containing gap. */ }
@@ -121,7 +121,7 @@
121
121
 
122
122
  /* Responsive — columns collapse to a stacked layout on narrow screens. Two of
123
123
  the kit's three steps, and the second one is the phone step rather than a
124
- fourth value of the footer's own: docs/specification.md#breakpoints. */
124
+ fourth value of the footer's own: docs/foundations.md#breakpoints. */
125
125
  @media (max-width: 720px) {
126
126
  .ui-footer__top { flex-direction: column; gap: 30px; }
127
127
  .ui-footer__nav { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 26px 30px; }
@@ -24,7 +24,7 @@
24
24
  font-size: var(--text-base);
25
25
  color: var(--strong);
26
26
  /* Fields use the card surface and an edge in both themes.
27
- why: docs/specification.md#elevation */
27
+ why: docs/foundations.md#elevation */
28
28
  background: var(--field-bg); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */
29
29
  border: 1px solid var(--field-edge);
30
30
  border-radius: var(--radius-md);
@@ -57,8 +57,8 @@
57
57
  longhands the chevron is drawn with (#511). Held by stories/select-chevron.test.js.
58
58
  `opacity` is the other half of that grey-out, and no colour here outranks it, so
59
59
  the fade is reset below — it took the edge and the words down together. #564,
60
- argued at docs/specification.md#colour-and-contrast.
61
- why: docs/specification.md#icons-and-glyphs */
60
+ argued at docs/foundations.md#colour-and-contrast.
61
+ why: docs/foundations.md#icons-and-glyphs */
62
62
  .ui-input:disabled,
63
63
  .ui-select:disabled,
64
64
  .ui-textarea:disabled {
@@ -81,7 +81,7 @@
81
81
  `search` glyph and no clear button, so a field without one is what the
82
82
  design already says. Measured in Chromium; Firefox draws nothing here and
83
83
  Edge is untested.
84
- why: docs/specification.md#colour-and-contrast */
84
+ why: docs/foundations.md#colour-and-contrast */
85
85
  .ui-input[type="search"]::-webkit-search-cancel-button {
86
86
  appearance: none;
87
87
  display: none;
@@ -106,7 +106,7 @@
106
106
  which is the colour every other quiet glyph on a field takes. It was one frozen
107
107
  hex until #295, dark --muted's old value, which the ladder's re-pick left behind
108
108
  at 2.49:1 on the light field. Held by stories/colour-tokens.test.js.
109
- why: docs/specification.md#colour-and-contrast */
109
+ why: docs/foundations.md#colour-and-contrast */
110
110
  .ui-select {
111
111
  appearance: none;
112
112
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23a29db6' stroke-width='2.4' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
@@ -233,7 +233,7 @@
233
233
  /* --shadow-sm was the knob's only edge until #295, and a white knob on a light
234
234
  off-track has 1.20:1 to spare. --border-strong and not --border, which measures
235
235
  1.03:1 against that track and draws nothing — the checkbox above edges itself
236
- the same way. why: docs/specification.md#elevation */
236
+ the same way. why: docs/foundations.md#elevation */
237
237
  border: 1px solid var(--border-strong);
238
238
  box-sizing: border-box;
239
239
  border-radius: 50%;
@@ -18,7 +18,7 @@
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
20
20
  plus the inset either side plus the rule, so the shell has no geometry of its
21
- own to drift from the rows inside it. why: docs/specification.md#the-page-shell */
21
+ own to drift from the rows inside it. why: docs/components.md#the-page-shell */
22
22
  --ui-rail-rule: 1px;
23
23
  --ui-rail-w: calc(var(--ui-nav-col) + 2 * var(--space-4) + var(--ui-rail-rule));
24
24
  }
@@ -30,7 +30,7 @@
30
30
  /* The rail uses the card surface so its labels stay on a reading ground.
31
31
  Dark keeps its edge (--rail-edge); light casts --elev-rail, sideways, because the rail
32
32
  is flush to a screen edge. Reopens the light half of #454.
33
- why: docs/specification.md#elevation
33
+ why: docs/foundations.md#elevation
34
34
  overflow-y, because the box is pinned at the viewport's height for the whole
35
35
  scroll of .ui-app — without it a row past that height paints below the fold
36
36
  and no amount of document scrolling brings it back. The rail's own padding is
@@ -49,7 +49,7 @@
49
49
  width: var(--ui-rail-w);
50
50
  /* hidden across, auto down: the fold clips what leaves the screen rather than
51
51
  laying it out again. Two longhands, not the shorthand — the gates' CSS engine
52
- does not read the pair. why: docs/specification.md#the-page-shell */
52
+ does not read the pair. why: docs/components.md#the-page-shell */
53
53
  overflow-x: hidden;
54
54
  overflow-y: auto;
55
55
  transition: width var(--dur-med) var(--ease);
@@ -58,15 +58,17 @@
58
58
  nav declares its own; these are the three that are not the nav. Scoped to the rail,
59
59
  because the topbar layout draws the reader's block in its band instead and a column
60
60
  the rail's width is no measurement for a box on a 52px line.
61
- why: docs/specification.md#the-page-shell */
61
+ why: docs/components.md#the-page-shell */
62
62
  .ui-app__rail > .ui-app__head,
63
63
  .ui-app__rail > .ui-app__user,
64
64
  .ui-app__rail > .ui-app__foot { width: var(--ui-nav-col); flex: none; }
65
- /* Hover keeps the reading surface and uses the edge declared in nav.css.
66
- The danger row and the current row are left out by name because this
67
- rule outranks the states nav.css writes for them.
68
- */
69
- .ui-app__rail .ui-nav__item:not(.is-danger):not(.is-active):hover { background: var(--surface); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ }
65
+ /* The rail is itself --surface, so nav.css's plate has no step here: 1.000:1, with a
66
+ hairline as the whole resting mark. Both marks go a rung further — the current row
67
+ to the --surface-3 the folded rail already paints, a hovered row to half of it, so
68
+ the pointer stays quieter than the reader's own row in both themes (#475 r34). The
69
+ danger and current rows are named out: this rule outranks nav.css's states. */
70
+ .ui-app__rail .ui-nav__item:not(.is-danger):not(.is-active):hover { background: color-mix(in srgb, var(--surface-3) 50%, transparent); /* ring-gap: inherit — translucent wash retains the opaque containing gap. */ }
71
+ .ui-app__rail .ui-nav__item.is-active { background: var(--surface-3); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ }
70
72
  .ui-app__brand {
71
73
  display: flex;
72
74
  align-items: center;
@@ -75,7 +77,7 @@
75
77
  text-decoration: none;
76
78
  /* The lockup leaves on the words' clock, `visibility` with it so the link leaves
77
79
  the tab order — `linear`, as every discrete property in the kit is.
78
- why: docs/specification.md#the-page-shell */
80
+ why: docs/components.md#the-page-shell */
79
81
  transition: opacity var(--dur-fast) var(--ease), visibility var(--dur-fast) linear;
80
82
  /* display: brand — the rail's lockup is the same mark topbar.css sets, and the
81
83
  two have to be the same face or the shell reads as two products. */
@@ -91,7 +93,7 @@
91
93
 
92
94
  /* The head band: the product's mark, and the rail's own control at the far end of
93
95
  the same line. `min-width: 0` so a long word shrinks the lockup rather than pushing
94
- the control off the band. why: docs/specification.md#the-page-shell */
96
+ the control off the band. why: docs/components.md#the-page-shell */
95
97
  .ui-app__head {
96
98
  display: flex;
97
99
  align-items: center;
@@ -107,18 +109,18 @@
107
109
  anywhere — so the rail's foot is the account block alone. */
108
110
  .ui-app__rail .ui-nav--side { flex: 1 1 auto; }
109
111
 
110
- /* Which row is current is structural here, not chromatic: the resting glyphs
111
- step down so the current one is the brightest whatever --accent is doing, and
112
- the bar beside it carries the hue. A 3x17px bar on a 45x44 tile was the
113
- smallest of three weak signals doing the most work, so it grows with the row.
114
- The current glyph's own ink is nav.css's rule now, not a copy here: since #429
115
- EVERY rail reads it that way, so the rail's copy was the same sentence in a
116
- second file. What is left here is the rail's own resting step. */
112
+ /* Which row is current is structural here, not chromatic: the resting glyphs step
113
+ down so the current one is the brightest whatever --accent is doing, and the
114
+ plate nav.css draws around it carries no hue at all. #475 took the accent bar
115
+ that used to stand beside the glyph off every rail, so the line that grew it to
116
+ the taller tile here went with it. The current glyph's own ink is nav.css's rule
117
+ too, not a copy here: since #429 EVERY rail reads it that way, so the rail's copy
118
+ was the same sentence in a second file. What is left here is the rail's own
119
+ resting step. */
117
120
  /* Re-tuned at #295, which took the light rail down a step with the page. Below 720px
118
121
  the glyph is the whole row, so it is held at text grade rather than at 1.4.11's bar —
119
- stories/apps/shell-states.test.js:1491 `a resting glyph is dimmer but still legible`. */
122
+ stories/apps/shell-states.test.js:1595 `a resting glyph is dimmer but still legible`. */
120
123
  .ui-app__rail .ui-nav__ic svg { opacity: 0.66; }
121
- .ui-app__rail .ui-nav__item.is-active::before { width: 3px; height: 62%; }
122
124
 
123
125
  /* A destructive row of the rail is a navigation row and rests like one; the danger
124
126
  intent is the step nav.css writes for it. Both of nav.css's danger states are
@@ -139,7 +141,7 @@
139
141
  }
140
142
  /* The other thing that can close a rail: where the reader's block is in the band,
141
143
  the fold's control takes its place under the same rule.
142
- why: docs/specification.md#the-page-shell */
144
+ why: docs/components.md#the-page-shell */
143
145
  .ui-app__foot {
144
146
  display: flex;
145
147
  align-items: center;
@@ -152,7 +154,7 @@
152
154
  /* The avatar is the reader block's own glyph, so half the difference between the
153
155
  glyph column and itself is the inset that centres it on the line every glyph above
154
156
  it stands on. The arithmetic and not the 5.5 it comes to: both numbers are declared
155
- elsewhere. why: docs/specification.md#the-page-shell */
157
+ elsewhere. why: docs/components.md#the-page-shell */
156
158
  .ui-app { --ui-app-av: 30px; }
157
159
  .ui-app__av {
158
160
  width: var(--ui-app-av); height: var(--ui-app-av); flex: none;
@@ -173,7 +175,7 @@
173
175
  /* -- the reader's menu ---------------------------------------------------- */
174
176
  /* The account block is a dropdown() trigger, so it takes the rail's own row skin
175
177
  rather than the dropdown's pill — in place it is the last row of the rail. The
176
- dropdown's own ring is left standing. why: docs/specification.md#the-page-shell */
178
+ dropdown's own ring is left standing. why: docs/components.md#the-page-shell */
177
179
  .ui-app__user .ui-dropdown { width: 100%; }
178
180
  .ui-app .ui-app__user-trigger {
179
181
  width: 100%;
@@ -196,7 +198,7 @@
196
198
  .ui-app__user-trigger .ui-app__who { flex: 1 1 auto; text-align: left; }
197
199
  /* The panel is portalled onto <body>, so nothing here may hang off .ui-app: by the
198
200
  time it is open it is no longer inside the rail that drew it.
199
- why: docs/specification.md#the-dropdown-panel */
201
+ why: docs/components.md#the-dropdown-panel */
200
202
  .ui-app__user-panel .ui-dropdown__head { display: flex; flex-direction: column; gap: 2px; }
201
203
  .ui-app__user-panel .ui-dropdown__head b { display: block; color: var(--strong); font-size: calc(var(--text-sm) - 0.5px); font-weight: var(--weight-semibold); }
202
204
  /* The address wraps rather than truncating: nothing bounds this line, so
@@ -208,7 +210,7 @@
208
210
  /* The toggle's cell, at the far end of the brand row: the auto margin puts it there
209
211
  with or without a wordmark beside it, and the offset below rides it back onto the
210
212
  glyph column as the rail closes. `position: relative` and not a transform, because
211
- the chip inside is `position: fixed`. why: docs/specification.md#the-page-shell */
213
+ the chip inside is `position: fixed`. why: docs/components.md#the-page-shell */
212
214
  .ui-app__fold-row {
213
215
  flex: none;
214
216
  margin-inline-start: auto;
@@ -219,12 +221,12 @@
219
221
  /* One box on the glyph column at both widths — --ui-nav-strip, the width nav.css
220
222
  derives the closed rail from. `gap: 0` leaves that column the whole of the
221
223
  content box, so the label below is squeezed to nothing until the chip rule lifts
222
- it out. why: docs/specification.md#the-page-shell */
224
+ it out. why: docs/components.md#the-page-shell */
223
225
  .ui-app__fold { width: var(--ui-nav-strip); gap: 0; }
224
226
  /* The chip rule lifts the name out of the flow, and a 17px glyph is shorter than
225
227
  the line the name occupied — so the button lost 0.39px on hover and moved every
226
228
  row under it. The glyph box carries the line instead, in both states.
227
- why: docs/specification.md#the-page-shell */
229
+ why: docs/components.md#the-page-shell */
228
230
  .ui-app__fold .ui-nav__ic { height: 1lh; }
229
231
  .ui-app .ui-app__fold { color: var(--text); }
230
232
  .ui-app .ui-app__fold:hover,
@@ -243,7 +245,7 @@
243
245
 
244
246
  The width's own clock, not the words', so the mark and the closing edge arrive
245
247
  together; no `!important`, so the reduced-motion net still takes it.
246
- why: docs/specification.md#the-page-shell */
248
+ why: docs/components.md#the-page-shell */
247
249
  .ui-app__fold .ui-app__fold-arrow {
248
250
  transform-box: view-box;
249
251
  transform-origin: 15px 12px;
@@ -253,7 +255,7 @@
253
255
 
254
256
  /* -- The collapsed rail --------------------------------------------------- */
255
257
  /* Each rule here has its twin in the 720px block below; nothing lays a row out
256
- again. why: docs/specification.md#the-page-shell */
258
+ again. why: docs/components.md#the-page-shell */
257
259
  .ui-app.is-collapsed { --ui-rail-w: calc(var(--ui-nav-strip) + 2 * var(--space-4) + var(--ui-rail-rule)); }
258
260
  :where(.ui-app.is-collapsed) .ui-app__rail .ui-nav__cap,
259
261
  :where(.ui-app.is-collapsed) .ui-app__rail .ui-nav__label,
@@ -266,27 +268,23 @@
266
268
  lockup goes whole, since that column is where its mark stood. The mark may only go
267
269
  where the control arrives: below 720px the toggle is not drawn, so the 720px block
268
270
  gives the lockup back rather than leaving an empty band with a hairline under it.
269
- why: docs/specification.md#the-page-shell */
271
+ why: docs/components.md#the-page-shell */
270
272
  :where(.ui-app.is-collapsed) .ui-app__head > .ui-app__fold-row { inset-inline-start: calc(var(--ui-nav-strip) - var(--ui-nav-col)); }
271
273
  /* …and only in the layout where something arrives on the mark's column. In the
272
274
  topbar layout the toggle is at the rail's foot, nothing lands where the mark
273
275
  stands, and a lockup faded out there leaves a band with nothing in it and its
274
276
  hairline still under it — the defect the 720px block below exists to avoid. So
275
277
  that layout keeps the mark and lets the shared rule above fold away its words.
276
- why: docs/specification.md#the-page-shell */
278
+ why: docs/components.md#the-page-shell */
277
279
  :where(.ui-app:not(.ui-app--topbar).is-collapsed) .ui-app__brand { opacity: 0; visibility: hidden; pointer-events: none; }
278
280
  :where(.ui-app.is-collapsed) .ui-app__rail .ui-nav__sub { margin-inline-start: 0; padding-inline-start: 0; border-inline-start-width: 0; }
279
281
  :where(.ui-app.is-collapsed) .ui-app__rail .ui-nav__item:not(:has(.ui-nav__ic))::after { opacity: 0.62; }
280
- /* The current row is ONE plate on the glyph column. Same rules as nav.css writes
281
- for a standalone folded rail, and for the same reasons: the bar stands in the
282
- row's own left padding, which on the strip is the rail's edge; the hairline is
283
- drawn on a row that keeps the open column, so its right edge is off the strip;
284
- and --ui-nav-strip is twice the glyph's centre, so a box that wide at the row's
285
- edge is centred on the glyph. why: docs/specification.md#the-page-shell */
282
+ /* The current row's plate on the glyph column, and beside it the one thing a bare rail
283
+ does not do: an ordinary row drops its wash here, answered by the floating name below.
284
+ nav.css's folded rules, a rung further. why: docs/components.md#the-page-shell */
286
285
  :where(.ui-app.is-collapsed) .ui-app__rail .ui-nav__item.is-active { isolation: isolate; background: none; }
287
- :where(.ui-app.is-collapsed) .ui-app__rail .ui-nav__item.is-active:not(:focus-visible) { box-shadow: none; }
288
- :where(.ui-app.is-collapsed) .ui-app__rail .ui-nav__item.is-active::before { left: 0; width: var(--ui-nav-strip); height: 100%; border-radius: 10px; background: var(--surface-3); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */ z-index: -1; }
289
- :where(.ui-app.is-collapsed) .ui-app__rail .ui-nav__item:hover:not(:focus-visible) { outline: none; }
286
+ :where(.ui-app.is-collapsed) .ui-app__rail .ui-nav__item.is-active::before { content: ""; position: absolute; left: 0; top: 50%; width: var(--ui-nav-strip); height: 100%; border-radius: 10px; transform: translateY(-50%); /* motion: still — a mark on the current row, whose own highlight already transitions; this centres it, it does not travel */ background: var(--surface-3); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */ z-index: -1; }
287
+ :where(.ui-app.is-collapsed) .ui-app__rail .ui-nav__item:not(.is-danger):not(.is-active):hover { background: none; }
290
288
  /* The label IS the chip. `fixed` is the fallback placement, out of the rail's own
291
289
  clip; the @supports branch below pins it to the row and is the only one that
292
290
  survives a scroll. The toggle's selector is unscoped, because it is wordless at
@@ -295,8 +293,8 @@
295
293
  It floats by role — a readout placed over the page for as long as a pointer
296
294
  rests on a row — so it takes the floating treatment the hover readout takes,
297
295
  which is the same shape on the same step: the two-step edge and the drops.
298
- why: docs/specification.md#the-page-shell
299
- why: docs/specification.md#elevation */
296
+ why: docs/components.md#the-page-shell
297
+ why: docs/foundations.md#elevation */
300
298
  :where(.ui-app.is-collapsed) .ui-app__rail .ui-nav__item:is(:hover, :focus-visible) > .ui-nav__label,
301
299
  :where(.ui-app.is-collapsed) .ui-app__rail .ui-app__user-trigger:is(:hover, :focus-visible) > .ui-app__who,
302
300
  .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(--float-edge); box-shadow: inset 0 0 0 1px var(--elev-edge, var(--float-edge-inner)), var(--elev-drop); border-radius: var(--radius-xs); background: var(--surface); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */ color: var(--strong); opacity: 1; pointer-events: none; }
@@ -330,7 +328,7 @@
330
328
  padding: 40px clamp(24px, 4vw, 56px) 72px;
331
329
  }
332
330
  /* The whole of the difference: `centered` is the rule above and `wide` takes its cap
333
- off. `maxWidth` is the number under either name. why: docs/specification.md#widths */
331
+ off. `maxWidth` is the number under either name. why: docs/foundations.md#widths */
334
332
  .ui-app__main--wide { max-width: var(--ui-app-main, none); }
335
333
 
336
334
  /* -- The topbar layout ----------------------------------------------------- */
@@ -338,7 +336,7 @@
338
336
  arrangement where the rail's head band and the band close at the same height — what
339
337
  the three heights below are for. Level, not one continuous stroke: the rail insets
340
338
  its own rule, and in light that half is 1.009:1 on the rail's ground.
341
- why: docs/specification.md#the-second-layout */
339
+ why: docs/components.md#the-second-layout */
342
340
  .ui-app { --ui-app-band: 52px; }
343
341
  .ui-app__well { display: flex; flex-direction: column; min-width: 0; }
344
342
  .ui-app__bar {
@@ -368,7 +366,7 @@
368
366
  like is the point. The STEP is not, and that is the one thing this control does
369
367
  differently from every other field in the kit: the reason is on the declaration
370
368
  below. It gives way before anything else on the band.
371
- why: docs/specification.md#the-page-shell */
369
+ why: docs/components.md#the-page-shell */
372
370
  .ui-app__search {
373
371
  display: flex;
374
372
  align-items: center;
@@ -383,7 +381,7 @@
383
381
  text-align: start;
384
382
  color: var(--text);
385
383
  /* The band search uses the same reading surface as other text controls.
386
- why: docs/specification.md#elevation */
384
+ why: docs/foundations.md#elevation */
387
385
  background: var(--surface); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */
388
386
  border: 1px solid var(--border);
389
387
  border-radius: var(--radius-md);
@@ -394,7 +392,7 @@
394
392
  /* Every other control on the band takes the kit's ring. This one is a <button>
395
393
  without `.ui-btn`, so base.css's shared rule never reached it and focus fell
396
394
  through to the browser's own square outline, drawn around a 12px radius.
397
- why: docs/specification.md#the-focus-ring */
395
+ why: docs/foundations.md#the-focus-ring */
398
396
  .ui-app__search:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); border-color: var(--accent); }
399
397
  /* The same glyph the palette draws in its own search box, at the same box and the
400
398
  same stroke: the trigger and the thing it opens are not two search marks.
@@ -409,7 +407,7 @@
409
407
 
410
408
  /* The mark alone — no room for two lines on a 52px row. No rule over it, since the
411
409
  band's hairline is under the whole of it, and no column, since this is not the rail.
412
- why: docs/specification.md#the-page-shell */
410
+ why: docs/components.md#the-page-shell */
413
411
  .ui-app__user--bar { flex: none; padding-top: 0; border-top: 0; }
414
412
  .ui-app__user--bar .ui-app__av { margin-inline-start: 0; }
415
413
  /* Crumb, then the title block. At --space-2 the crumb sat 8px off the h1 and
@@ -447,7 +445,7 @@
447
445
  .ui-toolbar > .ui-input-group { flex: 1 1 6rem; min-width: 0; }
448
446
  /* At one column the 6rem basis stops being a floor and becomes the field's size:
449
447
  a row with two narrow chips beside it keeps all three on the line and cuts the
450
- field's own placeholder off. why: docs/specification.md#a-toolbar-at-one-column */
448
+ field's own placeholder off. why: docs/components.md#a-toolbar-at-one-column */
451
449
  @media (max-width: 560px) {
452
450
  .ui-toolbar > .ui-input,
453
451
  .ui-toolbar > .ui-input-group { flex-basis: 100%; }
@@ -466,19 +464,19 @@
466
464
  /* …and the foot goes with it, for the reason the head band does below: in the
467
465
  topbar layout that box holds the toggle and nothing else, so at this width it is
468
466
  padding and a hairline drawn over nothing. Scoped to the shell's own rail, like
469
- every other rule in this block. why: docs/specification.md#the-page-shell */
467
+ every other rule in this block. why: docs/components.md#the-page-shell */
470
468
  .ui-app__rail .ui-app__foot { display: none; }
471
469
  /* …and with it the band, when the band has nothing else in it: a head with no
472
470
  wordmark is the toggle alone, so at this width it is padding and a hairline
473
471
  drawn over nothing. The reader's fold cannot take this rule — there the toggle
474
- IS drawn. why: docs/specification.md#the-page-shell */
472
+ IS drawn. why: docs/components.md#the-page-shell */
475
473
  .ui-app__head:not(:has(> .ui-app__brand)) { display: none; }
476
474
  /* And where the band does hold the mark, the mark stays — at this width the press
477
475
  that folded the rail on a desktop still reads out of the cookie, and the control
478
476
  that would undo it is the one above. A head band faded to nothing here is an empty
479
477
  41px box over a rule, with no way home. The reader's fold cannot take this rule —
480
478
  there the toggle lands on the column the mark gives up.
481
- why: docs/specification.md#the-page-shell */
479
+ why: docs/components.md#the-page-shell */
482
480
  .ui-app__brand { opacity: 1; visibility: visible; pointer-events: auto; }
483
481
  .ui-app__rail .ui-nav__cap,
484
482
  .ui-app__rail .ui-nav__label,
@@ -490,13 +488,12 @@
490
488
  .ui-app__rail .ui-nav__sub { margin-inline-start: 0; padding-inline-start: 0; border-inline-start-width: 0; }
491
489
  .ui-app__rail .ui-nav__item:not(:has(.ui-nav__ic))::after { opacity: 0.62; }
492
490
  .ui-app__rail .ui-nav__item.is-active { isolation: isolate; background: none; }
493
- .ui-app__rail .ui-nav__item.is-active:not(:focus-visible) { box-shadow: none; }
494
- .ui-app__rail .ui-nav__item.is-active::before { left: 0; width: var(--ui-nav-strip); height: 100%; border-radius: 10px; background: var(--surface-3); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */ z-index: -1; }
495
- .ui-app__rail .ui-nav__item:hover:not(:focus-visible) { outline: none; }
491
+ .ui-app__rail .ui-nav__item.is-active::before { content: ""; position: absolute; left: 0; top: 50%; width: var(--ui-nav-strip); height: 100%; border-radius: 10px; transform: translateY(-50%); /* motion: still — a mark on the current row, whose own highlight already transitions; this centres it, it does not travel */ background: var(--surface-3); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */ z-index: -1; }
492
+ .ui-app__rail .ui-nav__item:not(.is-danger):not(.is-active):hover { background: none; }
496
493
  /* The one line of this block that is deliberately NOT the reader's fold: a finger
497
494
  is the only pointer this width has, so a row and the account block are held to
498
495
  44px. Neither floor can spread to the press, where growing the box is the one
499
- thing the travel promises nothing does. why: docs/specification.md#the-page-shell */
496
+ thing the travel promises nothing does. why: docs/components.md#the-page-shell */
500
497
  .ui-app__rail .ui-nav__item,
501
498
  .ui-app__rail .ui-app__user-trigger { min-height: 44px; }
502
499
  .ui-app__rail .ui-nav__item:is(:hover, :focus-visible) > .ui-nav__label,
@@ -534,7 +531,7 @@
534
531
  max-width: var(--panel-md);
535
532
  /* Floats over a spotlit page, so it takes the floating step and the floating
536
533
  treatment: the two-step edge in dark, the drop alone in light.
537
- why: docs/specification.md#elevation */
534
+ why: docs/foundations.md#elevation */
538
535
  background: var(--bg-elevated); --ring-gap: var(--bg-elevated);
539
536
  border: 1px solid var(--float-edge);
540
537
  box-shadow: inset 0 0 0 1px var(--elev-edge, var(--float-edge-inner)), var(--elev-drop);
@@ -9,7 +9,7 @@
9
9
  * own red made it read as a fault the reader had committed. The lock says which
10
10
  * of the two it is.
11
11
  *
12
- * why: docs/specification.md#pending-and-denied-states */
12
+ * why: docs/components.md#pending-and-denied-states */
13
13
 
14
14
  /* Visually hidden, still read aloud. The kit had no such utility, and the live
15
15
  region needs one: its message is for assistive tech only — the sighted
@@ -13,7 +13,7 @@
13
13
  * @imported from here — the icon gates read each stylesheet on its own and
14
14
  * refuse a sheet that pulls in another.
15
15
  *
16
- * why: docs/specification.md#motion */
16
+ * why: docs/foundations.md#motion */
17
17
 
18
18
  /* -- Entrances -------------------------------------------------------------
19
19
  * `both` fill so the element starts at the "from" frame (hidden) and holds
@@ -43,7 +43,7 @@
43
43
  .ui-nav__cap {
44
44
  /* rank: label */
45
45
  /* Words, never a control, at BOTH widths — as __label, __badge and __caret below.
46
- why: docs/specification.md#a-folded-rail-takes-the-pointer-only-where-it-draws */
46
+ why: docs/components.md#a-folded-rail-takes-the-pointer-only-where-it-draws */
47
47
  pointer-events: none;
48
48
  font-size: var(--text-sm);
49
49
  font-weight: var(--weight-medium);
@@ -74,35 +74,28 @@
74
74
  font-family: inherit;
75
75
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
76
76
  }
77
- .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 */
78
- .ui-nav__item:hover { outline: 1px solid var(--control-edge); outline-offset: -1px; background: var(--surface); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ color: var(--strong); }
77
+ .ui-nav .ui-nav__item { color: var(--text); } /* (0,2,0): a host sheet's a:link is (0,1,1) — why: docs/components.md#the-page-shell */
78
+ .ui-nav__item:hover { background: color-mix(in srgb, var(--surface) 50%, transparent); /* ring-gap: inherit — translucent wash retains the opaque containing gap. */ color: var(--strong); }
79
79
  .ui-nav__item.is-active {
80
80
  background: var(--surface); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */
81
81
  color: var(--strong);
82
82
  font-weight: 500;
83
83
  }
84
- .ui-nav--side .ui-nav__item.is-active {
85
- box-shadow: inset 0 0 0 1px var(--border);
86
- }
87
- /* Accent rail marker on the active item */
88
- .ui-nav--side .ui-nav__item.is-active::before {
89
- content: "";
90
- position: absolute;
91
- left: 4px; top: 50%;
92
- width: 3px; height: 17px;
93
- border-radius: 3px;
94
- background: var(--accent);
95
- transform: translateY(-50%); /* motion: still — a mark on the current row, whose own highlight already transitions; this centres it, it does not travel */
96
- }
84
+ /* The current row's one mark is a plate, and nothing closes it: on a rail row an
85
+ outline means focus and only focus. The plate is the quiet fill a rung off the
86
+ ground the rail stands on, so layout.css takes it the next rung where the rail
87
+ is itself --surface. #475 r33/r34. why: docs/components.md#the-page-shell */
88
+
97
89
  .ui-nav__item:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); }
98
90
  .ui-nav__item.is-active:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); }
99
91
 
100
92
  .ui-nav__ic { display: inline-flex; flex: none; }
101
93
  .ui-nav__ic svg { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 2.2; opacity: 0.72; }
102
- /* The glyph brightens with its row and takes the row's ink, not the accent. One
103
- accent signal per row: that is the marker above, which is the only thing on a
104
- rail that means "here". A glyph in --accent beside it is the same sentence
105
- said twice, and beside an accent counter it was said three times. See #429. */
94
+ /* The glyph brightens with its row and takes the row's ink, not the accent. A
95
+ rail row carries no accent at all since #475: the plate above is what means
96
+ "here", and the glyph's own step to full ink is the other half of that pair.
97
+ A glyph in --accent beside it was the same sentence said twice, and beside an
98
+ accent counter it was said three times. See #429 and #475. */
106
99
  .ui-nav__item.is-active .ui-nav__ic svg,
107
100
  .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 */
108
101
  .ui-nav__label {
@@ -127,12 +120,6 @@
127
120
  background and pull its label toward the rail behind it (#220). */
128
121
  .ui-nav__item.is-disabled { /* muted-ink: state — unavailable control */ color: var(--disabled-ink); cursor: default; pointer-events: none; }
129
122
 
130
- /* Hover adds an edge without changing the current row's reading surface.
131
- The danger row keeps its own state treatment. */
132
- .ui-nav__item.is-active:not(.is-danger):hover {
133
- background: var(--surface); outline: 1px solid var(--control-edge); outline-offset: -1px; /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */
134
- }
135
-
136
123
  /* Per-item badge / counter */
137
124
  .ui-nav__badge {
138
125
  flex: none;
@@ -160,11 +147,11 @@
160
147
  .ui-nav__item.is-active .ui-nav__badge.is-neutral { background: var(--surface); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */ color: var(--strong); }
161
148
  /* Same shape, one row up: on the current row the accent counter drops both the
162
149
  wash and the accent and reads as the neutral one beside it. The row already
163
- carries the one accent signal it is allowed — the marker — and the count is a
164
- number, not a second answer to "where am I". It also retires the pair #157
150
+ carries the one mark it is allowed — the plate — and the count is a number,
151
+ not a second answer to "where am I". It also retires the pair #157
165
152
  recorded: accent ink on --surface fell under WCAG AA in four of the eight
166
153
  theme x accent cells (4.08 in dark Nebula), and no alpha cleared it. Nothing
167
- now paints that pair. See #429 and docs/specification.md#colour-and-contrast. */
154
+ now paints that pair. See #429 and docs/foundations.md#colour-and-contrast. */
168
155
  .ui-nav__item.is-active .ui-nav__badge.is-accent { background: var(--surface); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */ color: var(--strong); }
169
156
 
170
157
  /* Collapsible group */
@@ -195,12 +182,10 @@
195
182
  animation: m-slide-down var(--dur-fast) var(--ease-out) both;
196
183
  }
197
184
  .ui-nav__item--sub { font-size: calc(var(--text-base) - 0.5px); padding: 7px 12px; }
198
- /* No hairline on a nested current row — but only at rest. box-shadow carries
199
- both the hairline and the focus ring, so a rule about the resting look must
200
- say so, or it silently cancels the ring (WCAG 2.4.7). */
201
- .ui-nav--side .ui-nav__item--sub.is-active:not(:focus-visible) { box-shadow: none; }
202
- /* Hangs back onto the guide hairline the indent above draws. */
203
- .ui-nav--side .ui-nav__item--sub.is-active::before { left: calc(-1 * var(--space-3)); height: 15px; }
185
+ /* A nested current row takes the plate whole, like a top-level one. It used to
186
+ take neither half — the bar hung back onto the guide hairline and the plate's
187
+ own hairline was switched off so the two would not stack — and a fill with no
188
+ edge is not a mark the kit accepts. See #475. */
204
189
 
205
190
  /* Footer slot (divider + trailing links, e.g. sign-out) */
206
191
  .ui-nav__foot { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border); }
@@ -242,7 +227,7 @@
242
227
  /* Every box in the rail closes with it, whether it paints or not. A block that keeps
243
228
  the open column paints nothing and still takes the pointer off 175px of the page
244
229
  beside a positioned rail (#588); clipping it instead cut the rows' ring (#575).
245
- why: docs/specification.md#a-folded-rail-takes-the-pointer-only-where-it-draws */
230
+ why: docs/components.md#a-folded-rail-takes-the-pointer-only-where-it-draws */
246
231
  .ui-nav--side.is-collapsed > * { width: var(--ui-nav-strip); }
247
232
  .ui-nav--side.is-collapsed .ui-nav__item { width: var(--ui-nav-strip); }
248
233
  /* The heading is the one box that keeps the open column: its line breaks are the rail's
@@ -272,27 +257,31 @@
272
257
  /* The current row on a folded rail: one plate on the glyph column, and nothing else.
273
258
  The open rail's 3px marker would stand in the rail's edge at this width, and a
274
259
  nested row would put it outside the rail; the hairline would be the same mark said
275
- twice. The plate is the strip, so it is centred on the glyph, and --surface-3 is the
276
- kit's quiet non-text fill — a step off the rail in both themes. The glyph's own step
277
- from 0.66 to full ink is the second half of that pair. `isolation` is what puts the
278
- plate behind the glyph: a positioned ::before paints over its row's content, and a
279
- negative z-index with no stacking context of its own would drop it behind the rail.
280
- why: #429 */
260
+ twice. The plate is the strip, so it is centred on the glyph, and its fill is the
261
+ rung the open row takes — one off the ground the rail stands on, which layout.css
262
+ takes one further in the shell — so a rail marks the same row with the same
263
+ material at both widths. The glyph's own step from 0.66 to full ink is the second
264
+ half of that pair. `isolation` is what puts the plate behind the glyph: a
265
+ positioned ::before paints over its row's content, and a negative z-index with no
266
+ stacking context of its own would drop it behind the rail.
267
+ why: #429 and #475 */
281
268
  .ui-nav--side.is-collapsed .ui-nav__item.is-active { isolation: isolate; background: none; }
282
- /* At rest only — box-shadow carries the hairline AND the focus ring (WCAG 2.4.7). */
283
- .ui-nav--side.is-collapsed .ui-nav__item.is-active:not(:focus-visible) { box-shadow: none; }
284
269
  .ui-nav--side.is-collapsed .ui-nav__item.is-active::before {
285
- left: 0;
270
+ content: "";
271
+ position: absolute;
272
+ left: 0; top: 50%;
286
273
  width: var(--ui-nav-strip);
287
274
  height: 100%;
288
275
  border-radius: 10px;
289
- background: var(--surface-3); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */
276
+ transform: translateY(-50%); /* motion: still — a mark on the current row, whose own highlight already transitions; this centres it, it does not travel */
277
+ background: var(--surface); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */
290
278
  z-index: -1;
291
279
  }
292
- /* A folded rail answers a pointer with the row's own ink and the name beside it,
293
- which is the whole readout at this width — the hover wash stands, the edge
294
- around it does not. At rest only, so the focus ring is left standing. */
295
- .ui-nav--side.is-collapsed .ui-nav__item:hover:not(:focus-visible) { outline: none; }
280
+ /* Nothing switches the hover wash off here: since #588 a folded row is only as wide
281
+ as the rail draws, so the wash is the box the plate paints, a rung quieter. It is
282
+ the whole answer a pointer gets outside the shell, where the floating name is
283
+ layout.css's. (The `outline: none` that stood here switched off an edge #475
284
+ removed.) #475 */
296
285
 
297
286
  /* -- Horizontal tabs ----------------------------------------------------- */
298
287
  .ui-nav--tabs {