@nonoun/native-ui 0.2.4 → 0.2.6

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 (35) hide show
  1. package/dist/components/ui-kbd/index.d.ts +3 -0
  2. package/dist/components/ui-kbd/index.d.ts.map +1 -0
  3. package/dist/components/ui-kbd/ui-kbd-element.d.ts +5 -0
  4. package/dist/components/ui-kbd/ui-kbd-element.d.ts.map +1 -0
  5. package/dist/components/ui-kbd/ui-kbd.d.ts +3 -0
  6. package/dist/components/ui-kbd/ui-kbd.d.ts.map +1 -0
  7. package/dist/components/ui-nav/ui-nav-group-element.d.ts +8 -0
  8. package/dist/components/ui-nav/ui-nav-group-element.d.ts.map +1 -1
  9. package/dist/components/ui-nav/ui-nav-group-header-element.d.ts.map +1 -1
  10. package/dist/components-lean.css +319 -226
  11. package/dist/components.css +321 -233
  12. package/dist/containers/ui-layout-sidebar/index.d.ts +1 -1
  13. package/dist/containers/ui-layout-sidebar/index.d.ts.map +1 -1
  14. package/dist/containers/ui-layout-sidebar/ui-layout-sidebar-element.d.ts +4 -1
  15. package/dist/containers/ui-layout-sidebar/ui-layout-sidebar-element.d.ts.map +1 -1
  16. package/dist/containers/ui-layout-sidebar/ui-layout-sidebar-item-element.d.ts +10 -0
  17. package/dist/containers/ui-layout-sidebar/ui-layout-sidebar-item-element.d.ts.map +1 -0
  18. package/dist/containers/ui-layout-sidebar/ui-layout-sidebar-item.d.ts +3 -0
  19. package/dist/containers/ui-layout-sidebar/ui-layout-sidebar-item.d.ts.map +1 -0
  20. package/dist/containers/ui-layout-sidebar/ui-layout-sidebar.d.ts +1 -1
  21. package/dist/containers/ui-layout-sidebar/ui-layout-sidebar.d.ts.map +1 -1
  22. package/dist/custom-elements.json +2507 -2099
  23. package/dist/foundation.css +79 -1
  24. package/dist/index.d.ts +2 -1
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/native-ui-lean.css +398 -227
  27. package/dist/native-ui.css +400 -234
  28. package/dist/native-ui.js +3 -6
  29. package/dist/register-all.js +1 -1
  30. package/dist/ui-icon-element.js +100 -7
  31. package/package.json +1 -1
  32. package/dist/containers/ui-layout-sidebar/ui-layout-sidebar-trigger-element.d.ts +0 -8
  33. package/dist/containers/ui-layout-sidebar/ui-layout-sidebar-trigger-element.d.ts.map +0 -1
  34. package/dist/containers/ui-layout-sidebar/ui-layout-sidebar-trigger.d.ts +0 -3
  35. package/dist/containers/ui-layout-sidebar/ui-layout-sidebar-trigger.d.ts.map +0 -1
@@ -1267,6 +1267,54 @@
1267
1267
  display: none;
1268
1268
  }
1269
1269
 
1270
+ /* WHY: Custom elements render as unstyled inline text until define() runs.
1271
+ visibility:hidden preserves layout — no reflow when the element upgrades.
1272
+ Only targets elements that call define(); CSS-only containers are excluded
1273
+ (they never become :defined so would be permanently hidden). */
1274
+ :where(
1275
+ ui-accordion, ui-accordion-item,
1276
+ ui-avatar,
1277
+ ui-badge,
1278
+ ui-breadcrumb, ui-breadcrumb-item,
1279
+ ui-button,
1280
+ ui-calendar,
1281
+ ui-card,
1282
+ ui-chat-input,
1283
+ ui-checkbox,
1284
+ ui-combobox,
1285
+ ui-command, ui-command-empty, ui-command-group, ui-command-input, ui-command-item, ui-command-list,
1286
+ ui-controller,
1287
+ ui-dialog,
1288
+ ui-drawer,
1289
+ ui-field,
1290
+ ui-icon,
1291
+ ui-input,
1292
+ ui-input-otp,
1293
+ ui-kbd,
1294
+ ui-layout-chat,
1295
+ ui-layout-inspector,
1296
+ ui-layout-sidebar, ui-layout-sidebar-item,
1297
+ ui-listbox,
1298
+ ui-nav, ui-nav-group, ui-nav-group-header, ui-nav-item,
1299
+ ui-option, ui-option-group, ui-option-group-header,
1300
+ ui-pagination,
1301
+ ui-radio, ui-radio-group,
1302
+ ui-range,
1303
+ ui-section,
1304
+ ui-segment, ui-segmented-control,
1305
+ ui-select,
1306
+ ui-slide, ui-slideshow,
1307
+ ui-switch,
1308
+ ui-tab, ui-tab-panel, ui-tab-panels, ui-tabs,
1309
+ ui-table, ui-table-body, ui-table-cell, ui-table-head, ui-table-header, ui-table-row,
1310
+ ui-textarea,
1311
+ ui-toolbar,
1312
+ ui-tooltip,
1313
+ ui-tree, ui-tree-item
1314
+ ):not(:defined) {
1315
+ visibility: hidden;
1316
+ }
1317
+
1270
1318
  /* ── Document Defaults ── */
1271
1319
 
1272
1320
  :where(:root) {
@@ -1460,7 +1508,7 @@
1460
1508
  --ui-tooltip-max-width: 20rem;
1461
1509
 
1462
1510
  /* Popover (select / combobox / command) */
1463
- --ui-popover-max-height: 18rem;
1511
+ --ui-popover-max-height: calc(100dvh - 2rem);
1464
1512
  --ui-popover-gap: 0.25rem;
1465
1513
 
1466
1514
  /* Drawer */
@@ -1493,6 +1541,18 @@
1493
1541
  --ui-badge-size-xl: 1.375rem;
1494
1542
  --ui-badge-dot: 0.5rem;
1495
1543
 
1544
+ /* Kbd */
1545
+ --ui-kbd-font-xs: 0.625rem;
1546
+ --ui-kbd-font-sm: 0.6875rem;
1547
+ --ui-kbd-font-md: 0.6875rem;
1548
+ --ui-kbd-font-lg: 0.75rem;
1549
+ --ui-kbd-font-xl: 0.8125rem;
1550
+ --ui-kbd-size-xs: 1rem;
1551
+ --ui-kbd-size-sm: 1.125rem;
1552
+ --ui-kbd-size-md: 1.25rem;
1553
+ --ui-kbd-size-lg: 1.375rem;
1554
+ --ui-kbd-size-xl: 1.5rem;
1555
+
1496
1556
  /* Group header (option-group, table category) */
1497
1557
  --ui-group-header-font: 0.625rem;
1498
1558
 
@@ -1606,6 +1666,8 @@
1606
1666
  /* ── Intent (maps to color token families) ── */
1607
1667
 
1608
1668
  :root {
1669
+ --_body: var(--neutral-body);
1670
+
1609
1671
  --_card: var(--neutral-card);
1610
1672
  --_card-hover: var(--neutral-card-hover);
1611
1673
 
@@ -1657,6 +1719,22 @@
1657
1719
  --_ink-disabled: var(--neutral-ink-disabled);
1658
1720
  }
1659
1721
 
1722
+ /* ── Toggle control defaults ── */
1723
+ /* WHY: Checkboxes, radios, switches, and range sliders use accent-colored
1724
+ surface fills for their checked/active states by default.
1725
+ [intent] selectors below override --_surface when an explicit intent is set. */
1726
+
1727
+ :where(ui-checkbox, ui-radio, ui-switch, ui-range) {
1728
+ --_surface: var(--accent-surface);
1729
+ --_surface-hover: var(--accent-surface-hover);
1730
+ --_surface-active: var(--accent-surface-active);
1731
+ --_surface-disabled: var(--accent-surface-disabled);
1732
+ --_surface-ink: var(--accent-surface-ink);
1733
+ --_surface-ink-hover: var(--accent-surface-ink-hover);
1734
+ --_surface-ink-active: var(--accent-surface-ink-active);
1735
+ --_surface-ink-disabled: var(--accent-surface-ink-disabled);
1736
+ }
1737
+
1660
1738
  :where([intent="neutral"]) {
1661
1739
  --_card: var(--neutral-card);
1662
1740
  --_card-hover: var(--neutral-card-hover);
@@ -2365,7 +2443,7 @@
2365
2443
  font-weight: var(--_font-weight);
2366
2444
 
2367
2445
  border-radius: var(--_radius);
2368
- border: 1px solid var(--_border-color, var(--neutral-border-muted));
2446
+ border: 1px solid var(--_border-color, var(--_border-muted));
2369
2447
 
2370
2448
  background: var(--_background, var(--_button));
2371
2449
  color: var(--_color, var(--_ink));
@@ -2381,16 +2459,16 @@
2381
2459
  /* ── States ── */
2382
2460
 
2383
2461
  :where(ui-button):hover{
2384
- background: var(--_background-hover, var(--neutral-button-hover));
2462
+ background: var(--_background-hover, var(--_button-hover));
2385
2463
  color: var(--_color-hover, var(--_ink-hover));
2386
- border-color: var(--_border-color-hover, var(--neutral-border-hover));
2464
+ border-color: var(--_border-color-hover, var(--_border-hover));
2387
2465
  }
2388
2466
 
2389
2467
  :where(ui-button):active,
2390
2468
  :where(ui-button)[pressed]{
2391
- background: var(--_background-active, var(--neutral-button-active));
2469
+ background: var(--_background-active, var(--_button-active));
2392
2470
  color: var(--_color-active, var(--_ink-active));
2393
- border-color: var(--_border-color-active, var(--neutral-border-active));
2471
+ border-color: var(--_border-color-active, var(--_border-active));
2394
2472
  }
2395
2473
 
2396
2474
  :where(ui-button):focus-visible{
@@ -2399,9 +2477,9 @@
2399
2477
  }
2400
2478
 
2401
2479
  :where(ui-button)[aria-disabled="true"] {
2402
- background: var(--_background-disabled, var(--neutral-button-disabled));
2480
+ background: var(--_background-disabled, var(--_button-disabled));
2403
2481
  color: var(--_color-disabled, var(--_ink-disabled));
2404
- border-color: var(--_border-color-disabled, var(--neutral-border-muted));
2482
+ border-color: var(--_border-color-disabled, var(--_border-muted));
2405
2483
  cursor: not-allowed;
2406
2484
  pointer-events: none;
2407
2485
  }
@@ -2969,34 +3047,25 @@
2969
3047
  }
2970
3048
 
2971
3049
  /* ── Checked State ── */
2972
- /* WHY: Default checked fill uses accent so checkbox is visually distinct
2973
- without needing intent="accent". Unchecked borders stay neutral.
3050
+ /* WHY: Default checked fill uses --_surface (accent by default via
3051
+ intent="accent" being the implicit default for toggle controls).
2974
3052
  When an explicit [intent] is set, the intent selector's --_surface wins. */
2975
3053
 
2976
3054
  :where(ui-checkbox)[aria-checked="true"]::before {
2977
- background: var(--accent-surface);
2978
- border-color: var(--accent-surface);
2979
- }
2980
-
2981
- :where(ui-checkbox)[aria-checked="true"]::after {
2982
- background: var(--accent-surface-ink);
2983
- transform: translateY(-50%) scale(1);
2984
- }
2985
-
2986
- :where(ui-checkbox)[intent][aria-checked="true"]::before {
2987
3055
  background: var(--_surface);
2988
3056
  border-color: var(--_surface);
2989
3057
  }
2990
3058
 
2991
- :where(ui-checkbox)[intent][aria-checked="true"]::after {
3059
+ :where(ui-checkbox)[aria-checked="true"]::after {
2992
3060
  background: var(--_surface-ink);
3061
+ transform: translateY(-50%) scale(1);
2993
3062
  }
2994
3063
 
2995
3064
  /* ── Indeterminate State ── */
2996
3065
 
2997
3066
  :where(ui-checkbox)[aria-checked="mixed"]::before {
2998
- background: var(--accent-surface);
2999
- border-color: var(--accent-surface);
3067
+ background: var(--_surface);
3068
+ border-color: var(--_surface);
3000
3069
  }
3001
3070
 
3002
3071
  :where(ui-checkbox)[aria-checked="mixed"]::after {
@@ -3004,17 +3073,8 @@
3004
3073
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' fill='currentColor' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M228,128a12,12,0,0,1-12,12H40a12,12,0,0,1,0-24H216A12,12,0,0,1,228,128Z'/%3E%3C/svg%3E");
3005
3074
  mask-size: 75%;
3006
3075
  -webkit-mask-size: 75%;
3007
- background: var(--accent-surface-ink);
3008
- transform: translateY(-50%) scale(1);
3009
- }
3010
-
3011
- :where(ui-checkbox)[intent][aria-checked="mixed"]::before {
3012
- background: var(--_surface);
3013
- border-color: var(--_surface);
3014
- }
3015
-
3016
- :where(ui-checkbox)[intent][aria-checked="mixed"]::after {
3017
3076
  background: var(--_surface-ink);
3077
+ transform: translateY(-50%) scale(1);
3018
3078
  }
3019
3079
 
3020
3080
  /* ── Hover ── */
@@ -3031,13 +3091,6 @@
3031
3091
  :where(ui-checkbox)[aria-checked="true"]:hover::before,
3032
3092
  :where(ui-checkbox)[aria-checked="mixed"]:hover::before
3033
3093
  :where(ui-checkbox)[aria-checked="mixed"][force-hover]::before {
3034
- background: var(--accent-surface-hover);
3035
- border-color: var(--accent-surface-hover);
3036
- }
3037
-
3038
- :where(ui-checkbox)[intent][aria-checked="true"]:hover::before,
3039
- :where(ui-checkbox)[intent][aria-checked="mixed"]:hover::before
3040
- :where(ui-checkbox)[intent][aria-checked="mixed"][force-hover]::before {
3041
3094
  background: var(--_surface-hover);
3042
3095
  border-color: var(--_surface-hover);
3043
3096
  }
@@ -3051,12 +3104,6 @@
3051
3104
 
3052
3105
  :where(ui-checkbox)[aria-checked="true"][pressed]::before,
3053
3106
  :where(ui-checkbox)[aria-checked="mixed"][pressed]::before {
3054
- background: var(--accent-surface-active);
3055
- border-color: var(--accent-surface-active);
3056
- }
3057
-
3058
- :where(ui-checkbox)[intent][aria-checked="true"][pressed]::before,
3059
- :where(ui-checkbox)[intent][aria-checked="mixed"][pressed]::before {
3060
3107
  background: var(--_surface-active);
3061
3108
  border-color: var(--_surface-active);
3062
3109
  }
@@ -3703,6 +3750,45 @@
3703
3750
 
3704
3751
  }
3705
3752
 
3753
+ @layer ui {
3754
+
3755
+ /* ╭──────────────────────────────────────────────────────────╮
3756
+ │ ui-kbd │
3757
+ │ Keyboard shortcut indicator. Own font/size scale │
3758
+ │ (like ui-badge) — zero-attribute = md. │
3759
+ ╰──────────────────────────────────────────────────────────╯ */
3760
+
3761
+ :where(ui-kbd) {
3762
+ --_icon-size: 1.125em;
3763
+
3764
+ display: inline-flex;
3765
+ align-items: center;
3766
+ justify-content: center;
3767
+ font-family: ui-monospace, monospace;
3768
+ font-size: var(--ui-kbd-font-md);
3769
+ line-height: 1;
3770
+ min-height: var(--ui-kbd-size-md);
3771
+ padding-inline: 0.4em;
3772
+ border-radius: 0.5em;
3773
+ background: var(--_ground, var(--_body));
3774
+ border: 1px solid var(--_border-muted);
3775
+ color: var(--_ink-muted);
3776
+ white-space: nowrap;
3777
+ user-select: none;
3778
+ flex-shrink: 0;
3779
+ vertical-align: baseline;
3780
+ }
3781
+
3782
+ /* ── Sizes ── */
3783
+
3784
+ :where(ui-kbd[size="xs"]) { font-size: var(--ui-kbd-font-xs); min-height: var(--ui-kbd-size-xs); }
3785
+ :where(ui-kbd[size="sm"]) { font-size: var(--ui-kbd-font-sm); min-height: var(--ui-kbd-size-sm); }
3786
+ :where(ui-kbd[size="md"]) { font-size: var(--ui-kbd-font-md); min-height: var(--ui-kbd-size-md); }
3787
+ :where(ui-kbd[size="lg"]) { font-size: var(--ui-kbd-font-lg); min-height: var(--ui-kbd-size-lg); }
3788
+ :where(ui-kbd[size="xl"]) { font-size: var(--ui-kbd-font-xl); min-height: var(--ui-kbd-size-xl); }
3789
+
3790
+ }
3791
+
3706
3792
  @layer ui {
3707
3793
 
3708
3794
  /* ── Listbox Base ── */
@@ -3840,11 +3926,14 @@
3840
3926
  │ Uses ui-nav-item / ui-nav-group for children. │
3841
3927
  ╰──────────────────────────────────────────────────────────╯ */
3842
3928
 
3929
+ /* WHY: ui-nav is a transparent flex wrapper — no inline padding.
3930
+ Inline padding is owned by leaf items (nav-item, summary).
3931
+ Block padding is owned by nav-group margins. */
3843
3932
  :where(ui-nav) {
3844
3933
  display: flex;
3845
3934
  flex-direction: column;
3846
3935
  gap: 0;
3847
- padding: var(--_space);
3936
+ padding: 0;
3848
3937
  outline: none;
3849
3938
  }
3850
3939
 
@@ -3859,6 +3948,7 @@
3859
3948
  display: flex;
3860
3949
  align-items: center;
3861
3950
  gap: calc(var(--_space) * 2);
3951
+ padding-inline: calc(var(--_space-k) * var(--_space));
3862
3952
 
3863
3953
  min-height: var(--_min-height);
3864
3954
 
@@ -3955,6 +4045,7 @@
3955
4045
  display: flex;
3956
4046
  align-items: center;
3957
4047
  gap: calc(var(--_space) * 2);
4048
+ padding-inline: calc(var(--_space-k) * var(--_space));
3958
4049
  cursor: pointer;
3959
4050
  user-select: none;
3960
4051
  list-style: none;
@@ -3989,6 +4080,18 @@
3989
4080
  user-select: none;
3990
4081
  }
3991
4082
 
4083
+ /* WHY: Fixed-size icon well — matches ui-layout-sidebar-item [slot="icon"].
4084
+ All sidebar icons align on the same 1.5rem column regardless of icon size.
4085
+ JS wraps the first <ui-icon> child in a .icon-well span during setup(). */
4086
+ :where(ui-nav-group-header) > :where(.icon-well) {
4087
+ display: inline-flex;
4088
+ align-items: center;
4089
+ justify-content: center;
4090
+ width: 1.5rem;
4091
+ height: 1.5rem;
4092
+ flex-shrink: 0;
4093
+ }
4094
+
3992
4095
  /* ── Chevron icon (right side of summary) ── */
3993
4096
 
3994
4097
  :where(ui-nav-group) > :where(details) > :where(summary)::after {
@@ -4040,12 +4143,16 @@
4040
4143
  /* WHY: Connector draws from below the header to the bottom of the group.
4041
4144
  Only shown when header contains an icon. Scoped via :has(). */
4042
4145
 
4043
- :where(ui-nav-group):has(:where(ui-nav-group-header) :where(ui-icon)) {
4044
- --_group-line-inset: calc(var(--_icon-size) / 2);
4045
- --_group-child-inset: calc(var(--_icon-size) + var(--_space) * 2);
4146
+ /* WHY: Insets account for the summary's padding-inline so the connector
4147
+ line passes through the icon center and child text aligns with header text. */
4148
+ :where(ui-nav-group):has(:where(ui-nav-group-header) :where(.icon-well)) {
4149
+ --_group-pad: calc(var(--_space-k) * var(--_space));
4150
+ --_group-icon-well: 1.5rem;
4151
+ --_group-line-inset: calc(var(--_group-pad) + var(--_group-icon-well) / 2);
4152
+ --_group-child-inset: calc(var(--_group-icon-well) + var(--_space) * 2);
4046
4153
  }
4047
4154
 
4048
- :where(ui-nav-group):has(:where(ui-nav-group-header) :where(ui-icon)):has(:where(details[open]))::after {
4155
+ :where(ui-nav-group):has(:where(ui-nav-group-header) :where(.icon-well)):has(:where(details[open]))::after {
4049
4156
  content: '';
4050
4157
  position: absolute;
4051
4158
  inset-inline-start: var(--_group-line-inset);
@@ -4060,7 +4167,7 @@
4060
4167
  /* WHY: A ::before pseudo on the group slides along the connector line.
4061
4168
  JS sets --_indicator-index; :state(has-selection) gates visibility. */
4062
4169
 
4063
- :where(ui-nav-group):has(:where(ui-nav-group-header) :where(ui-icon)):has(:where(details[open])):state(has-selection)::before {
4170
+ :where(ui-nav-group):has(:where(ui-nav-group-header) :where(.icon-well)):has(:where(details[open])):state(has-selection)::before {
4064
4171
  content: '';
4065
4172
  position: absolute;
4066
4173
  z-index: 1;
@@ -4087,6 +4194,73 @@
4087
4194
  margin-inline-start: var(--_group-child-inset, 0);
4088
4195
  }
4089
4196
 
4197
+ /* ── Nav Group Flyout (collapsed sidebar) ── */
4198
+ /* WHY: In collapsed mode, summary click opens a ui-listbox popover to the right
4199
+ instead of expanding the <details>. Same pattern as sidebar-trigger menus. */
4200
+
4201
+ :where(ui-nav-group) > :where(ui-listbox.nav-group-flyout[popover]) {
4202
+ position: fixed;
4203
+ position-area: inline-end span-block-end;
4204
+ position-try-fallbacks: --nav-flyout-flip-up;
4205
+ margin: 0 0 0 var(--ui-popover-gap);
4206
+ min-width: 200px;
4207
+ max-height: var(--ui-popover-max-height);
4208
+ overflow-y: auto;
4209
+ }
4210
+
4211
+ @position-try --nav-flyout-flip-up {
4212
+ position-area: inline-end span-block-start;
4213
+ }
4214
+
4215
+ /* ── Container Query: Collapsed Sidebar ── */
4216
+ /* WHY: Nav components own their own collapsed behavior via @container.
4217
+ The sidebar aside declares container-name: sidebar. When it shrinks
4218
+ to 48px (icon rail), nav responds to the width — not to [collapsed].
4219
+ Threshold 80px: collapsed = 48px, min expanded = 160px. */
4220
+
4221
+ @container sidebar (max-width: 80px) {
4222
+
4223
+ /* Nav items hide entirely — only group headers (with icons) remain. */
4224
+ :where(ui-nav-item) {
4225
+ display: none;
4226
+ }
4227
+
4228
+ /* Summary shrinks to icon-only. Reduced inline padding wraps tightly
4229
+ around the icon; parent align-items: center handles horizontal centering. */
4230
+ :where(ui-nav-group) > :where(details) > :where(summary) {
4231
+ padding-inline: var(--_space);
4232
+ border-radius: var(--_radius);
4233
+ }
4234
+
4235
+ /* Hide chevron — no expand/collapse in icon rail (flyout instead). */
4236
+ :where(ui-nav-group) > :where(details) > :where(summary)::after {
4237
+ display: none;
4238
+ }
4239
+
4240
+ /* Hide vertical connector line and sliding indicator. */
4241
+ :where(ui-nav-group)::after,
4242
+ :where(ui-nav-group)::before {
4243
+ display: none;
4244
+ }
4245
+
4246
+ /* Header collapses to icon-only. font-size: 0 hides text nodes
4247
+ (can't be targeted by CSS). Icon overrides back to normal below. */
4248
+ :where(ui-nav-group-header) {
4249
+ flex: 0 0 auto;
4250
+ font-size: 0;
4251
+ gap: 0;
4252
+ }
4253
+
4254
+ :where(ui-nav-group-header) :where(.icon-well) :where(ui-icon) {
4255
+ font-size: var(--_font-size, 1rem);
4256
+ }
4257
+
4258
+ /* Collapse inter-group spacing in icon rail. */
4259
+ :where(ui-nav-group) + :where(ui-nav-group) {
4260
+ margin-block-start: 0;
4261
+ }
4262
+ }
4263
+
4090
4264
  }
4091
4265
 
4092
4266
  @layer ui {
@@ -4227,27 +4401,15 @@
4227
4401
  }
4228
4402
 
4229
4403
  /* ── Selected ── */
4230
- /* WHY: Default selected fill uses accent so radio is visually distinct
4231
- without needing intent="accent". Unchecked borders stay neutral.
4232
- When an explicit [intent] is set, the intent selector's --_surface wins. */
4233
4404
 
4234
4405
  :where(ui-radio)[aria-checked="true"]::before {
4235
- background: var(--accent-surface);
4236
- border-color: var(--accent-surface);
4237
- }
4238
-
4239
- :where(ui-radio)[aria-checked="true"]::after {
4240
- background: var(--accent-surface-ink);
4241
- transform: translateY(-50%) scale(1);
4242
- }
4243
-
4244
- :where(ui-radio-group)[intent] :where(ui-radio)[aria-checked="true"]::before {
4245
4406
  background: var(--_surface);
4246
4407
  border-color: var(--_surface);
4247
4408
  }
4248
4409
 
4249
- :where(ui-radio-group)[intent] :where(ui-radio)[aria-checked="true"]::after {
4410
+ :where(ui-radio)[aria-checked="true"]::after {
4250
4411
  background: var(--_surface-ink);
4412
+ transform: translateY(-50%) scale(1);
4251
4413
  }
4252
4414
 
4253
4415
  /* ── Hover ── */
@@ -4262,11 +4424,6 @@
4262
4424
  }
4263
4425
 
4264
4426
  :where(ui-radio)[aria-checked="true"]:hover::before{
4265
- background: var(--accent-surface-hover);
4266
- border-color: var(--accent-surface-hover);
4267
- }
4268
-
4269
- :where(ui-radio-group)[intent] :where(ui-radio)[aria-checked="true"]:hover::before{
4270
4427
  background: var(--_surface-hover);
4271
4428
  border-color: var(--_surface-hover);
4272
4429
  }
@@ -4279,11 +4436,6 @@
4279
4436
  }
4280
4437
 
4281
4438
  :where(ui-radio)[aria-checked="true"][pressed]::before {
4282
- background: var(--accent-surface-active);
4283
- border-color: var(--accent-surface-active);
4284
- }
4285
-
4286
- :where(ui-radio-group)[intent] :where(ui-radio)[aria-checked="true"][pressed]::before {
4287
4439
  background: var(--_surface-active);
4288
4440
  border-color: var(--_surface-active);
4289
4441
  }
@@ -4381,16 +4533,12 @@
4381
4533
  /* 0% = track-height (circle), 100% = full width */
4382
4534
  width: calc(var(--_track-height) + (100% - var(--_track-height)) * var(--_progress));
4383
4535
  border-radius: calc(var(--_track-height) / 2);
4384
- background: var(--accent-surface);
4536
+ background: var(--_surface);
4385
4537
 
4386
4538
  transition:
4387
4539
  background var(--_duration) var(--_easing);
4388
4540
  }
4389
4541
 
4390
- :where(ui-range)[intent]::after {
4391
- background: var(--_surface);
4392
- }
4393
-
4394
4542
  /* ── Thumb ── */
4395
4543
 
4396
4544
  :where(ui-range) > :where(.ui-range-thumb) {
@@ -4402,8 +4550,8 @@
4402
4550
  width: var(--_thumb-size);
4403
4551
  height: var(--_thumb-size);
4404
4552
  border-radius: 50%;
4405
- background: var(--accent-surface-ink);
4406
- border: 2px solid var(--accent-surface);
4553
+ background: var(--_surface-ink);
4554
+ border: 2px solid var(--_surface);
4407
4555
  box-shadow: var(--ui-shadow-sm);
4408
4556
  pointer-events: none;
4409
4557
 
@@ -4413,11 +4561,6 @@
4413
4561
  border-color var(--_duration) var(--_easing);
4414
4562
  }
4415
4563
 
4416
- :where(ui-range)[intent] > :where(.ui-range-thumb) {
4417
- background: var(--_surface-ink);
4418
- border-color: var(--_surface);
4419
- }
4420
-
4421
4564
  /* ── Focus ── */
4422
4565
 
4423
4566
  :where(ui-range):focus-visible > :where(.ui-range-thumb){
@@ -4428,19 +4571,10 @@
4428
4571
  /* ── Hover ── */
4429
4572
 
4430
4573
  :where(ui-range):hover::after{
4431
- background: var(--accent-surface-hover);
4432
- }
4433
-
4434
- :where(ui-range)[intent]:hover::after{
4435
4574
  background: var(--_surface-hover);
4436
4575
  }
4437
4576
 
4438
4577
  :where(ui-range):hover > :where(.ui-range-thumb){
4439
- border-color: var(--accent-surface-hover);
4440
- box-shadow: var(--ui-shadow-sm), 0 0 0 4px var(--accent-surface);
4441
- }
4442
-
4443
- :where(ui-range)[intent]:hover > :where(.ui-range-thumb){
4444
4578
  border-color: var(--_surface-hover);
4445
4579
  box-shadow: var(--ui-shadow-sm), 0 0 0 4px var(--_surface);
4446
4580
  }
@@ -4448,19 +4582,10 @@
4448
4582
  /* ── Active (dragging) ── */
4449
4583
 
4450
4584
  :where(ui-range)[pressed]::after {
4451
- background: var(--accent-surface-active);
4452
- }
4453
-
4454
- :where(ui-range)[intent][pressed]::after {
4455
4585
  background: var(--_surface-active);
4456
4586
  }
4457
4587
 
4458
4588
  :where(ui-range)[pressed] > :where(.ui-range-thumb) {
4459
- border-color: var(--accent-surface-active);
4460
- box-shadow: var(--ui-shadow-sm), 0 0 0 6px var(--accent-surface);
4461
- }
4462
-
4463
- :where(ui-range)[intent][pressed] > :where(.ui-range-thumb) {
4464
4589
  border-color: var(--_surface-active);
4465
4590
  box-shadow: var(--ui-shadow-sm), 0 0 0 6px var(--_surface);
4466
4591
  }
@@ -5007,22 +5132,13 @@
5007
5132
  When an explicit [intent] is set, the intent selector's --_surface wins. */
5008
5133
 
5009
5134
  :where(ui-switch)[aria-checked="true"]::before {
5010
- background: var(--accent-surface);
5011
- border-color: var(--accent-surface);
5135
+ background: var(--_surface);
5136
+ border-color: var(--_surface);
5012
5137
  }
5013
5138
 
5014
5139
  :where(ui-switch)[aria-checked="true"]::after {
5015
5140
  /* Slide thumb to the right via translateX — GPU composited */
5016
5141
  transform: translateY(-50%) translateX(calc(var(--_track-width) - var(--_thumb-size) - var(--_thumb-offset) * 2));
5017
- background: var(--accent-surface-ink);
5018
- }
5019
-
5020
- :where(ui-switch)[intent][aria-checked="true"]::before {
5021
- background: var(--_surface);
5022
- border-color: var(--_surface);
5023
- }
5024
-
5025
- :where(ui-switch)[intent][aria-checked="true"]::after {
5026
5142
  background: var(--_surface-ink);
5027
5143
  }
5028
5144
 
@@ -5038,11 +5154,6 @@
5038
5154
  }
5039
5155
 
5040
5156
  :where(ui-switch)[aria-checked="true"]:hover::before{
5041
- background: var(--accent-surface-hover);
5042
- border-color: var(--accent-surface-hover);
5043
- }
5044
-
5045
- :where(ui-switch)[intent][aria-checked="true"]:hover::before{
5046
5157
  background: var(--_surface-hover);
5047
5158
  border-color: var(--_surface-hover);
5048
5159
  }
@@ -5055,11 +5166,6 @@
5055
5166
  }
5056
5167
 
5057
5168
  :where(ui-switch)[aria-checked="true"][pressed]::before {
5058
- background: var(--accent-surface-active);
5059
- border-color: var(--accent-surface-active);
5060
- }
5061
-
5062
- :where(ui-switch)[intent][aria-checked="true"][pressed]::before {
5063
5169
  background: var(--_surface-active);
5064
5170
  border-color: var(--_surface-active);
5065
5171
  }
@@ -6498,7 +6604,7 @@
6498
6604
 
6499
6605
  :where(ui-layout-chat) :where(.layout-resize-handle):hover,
6500
6606
  :where(ui-layout-chat[resizing]) :where(.layout-resize-handle) {
6501
- background: var(--neutral-border-muted);
6607
+ background: var(--_border-muted);
6502
6608
  }
6503
6609
 
6504
6610
  }
@@ -6560,7 +6666,7 @@
6560
6666
 
6561
6667
  :where(ui-layout-inspector) :where(.layout-resize-handle):hover,
6562
6668
  :where(ui-layout-inspector[resizing]) :where(.layout-resize-handle) {
6563
- background: var(--neutral-border-muted);
6669
+ background: var(--_border-muted);
6564
6670
  }
6565
6671
 
6566
6672
  }
@@ -6587,16 +6693,16 @@
6587
6693
  content: '';
6588
6694
  width: var(--ui-layout-sidebar-width);
6589
6695
  flex-shrink: 0;
6590
- background: var(--neutral-body);
6591
- border-right: 1px solid var(--neutral-border-muted);
6696
+ background: var(--_body);
6697
+ border-right: 1px solid var(--_border-muted);
6592
6698
  }
6593
6699
 
6594
6700
  :where(ui-layout-sidebar):not([data-ready])[collapsed]::before {
6595
6701
  content: '';
6596
6702
  width: 48px;
6597
6703
  flex-shrink: 0;
6598
- background: var(--neutral-body);
6599
- border-right: 1px solid var(--neutral-border-muted);
6704
+ background: var(--_body);
6705
+ border-right: 1px solid var(--_border-muted);
6600
6706
  }
6601
6707
 
6602
6708
  /* ── Content Column ── */
@@ -6612,16 +6718,23 @@
6612
6718
  }
6613
6719
 
6614
6720
  /* ── Sidebar Aside ── */
6721
+ /* WHY: position: relative creates the containing block for absolute
6722
+ header/footer overlays. Content fills the full height and scrolls
6723
+ underneath the pinned header/footer. */
6615
6724
 
6616
6725
  :where(ui-layout-sidebar) > :where([slot="sidebar"]) {
6726
+ --_sidebar-header-height: 0px;
6727
+ --_sidebar-footer-height: 0px;
6728
+
6729
+ container-name: sidebar;
6730
+ container-type: inline-size;
6731
+ position: sticky;
6732
+ top: 0;
6617
6733
  width: var(--ui-layout-sidebar-width);
6618
6734
  min-width: 160px;
6619
6735
  max-width: 400px;
6620
6736
  height: 100dvh;
6621
- position: sticky;
6622
- top: 0;
6623
- display: flex;
6624
- flex-direction: column;
6737
+ display: block;
6625
6738
  transition: width var(--_duration) var(--_easing), min-width var(--_duration) var(--_easing);
6626
6739
  overflow: hidden;
6627
6740
  z-index: 10;
@@ -6633,7 +6746,9 @@
6633
6746
  }
6634
6747
 
6635
6748
  /* ── Collapsed Icon Rail ── */
6636
- /* WHY: Collapsed = icon rail, not fully hidden. 48px fits icons (1rem) + padding. */
6749
+ /* WHY: Collapsed = icon rail, not fully hidden. 48px fits icons (1rem) + padding.
6750
+ [collapsed] drives the aside to 48px which triggers @container sidebar queries
6751
+ in this file and in child component CSS (ui-nav.css, etc.). */
6637
6752
 
6638
6753
  :where(ui-layout-sidebar)[collapsed] > :where([slot="sidebar"]) {
6639
6754
  width: 48px;
@@ -6654,11 +6769,7 @@
6654
6769
 
6655
6770
  :where(ui-layout-sidebar) :where(.layout-resize-handle):hover,
6656
6771
  :where(ui-layout-sidebar) > :where([slot="sidebar"][resizing]) :where(.layout-resize-handle) {
6657
- background: var(--neutral-border-muted);
6658
- }
6659
-
6660
- :where(ui-layout-sidebar)[collapsed] > :where([slot="sidebar"]) :where(.layout-resize-handle) {
6661
- display: none;
6772
+ background: var(--_border-muted);
6662
6773
  }
6663
6774
 
6664
6775
  /* WHY: When collapsed to icon rail, the sidebar edge meets content — drop left padding. */
@@ -6671,101 +6782,89 @@
6671
6782
  padding-left: 0;
6672
6783
  }
6673
6784
 
6674
- /* ── Collapsed Icon-Rail Content ── */
6675
- /* WHY: When collapsed, sidebar slot becomes a narrow icon rail.
6676
- Text and trailing slots are hidden; only icons remain visible.
6677
- Nav items hide entirely — only group headers (with icons) stay. */
6678
-
6679
- :where(ui-layout-sidebar)[collapsed] :where([slot="sidebar"]) :where(ui-layout-sidebar-content) {
6680
- align-items: center;
6681
- padding: 0.5rem;
6682
- }
6683
-
6684
- :where(ui-layout-sidebar)[collapsed] :where([slot="sidebar"]) :where(ui-layout-sidebar-header),
6685
- :where(ui-layout-sidebar)[collapsed] :where([slot="sidebar"]) :where(ui-layout-sidebar-footer) {
6686
- justify-content: center;
6687
- padding-inline: 0.5rem;
6688
- }
6689
-
6690
- :where(ui-layout-sidebar)[collapsed] :where([slot="sidebar"]) :where(ui-layout-sidebar-trigger) :where([slot="label"]),
6691
- :where(ui-layout-sidebar)[collapsed] :where([slot="sidebar"]) :where(ui-layout-sidebar-trigger) :where([slot="trailing"]) {
6692
- display: none;
6693
- }
6694
-
6695
- :where(ui-layout-sidebar)[collapsed] :where([slot="sidebar"]) :where(ui-nav-item) {
6696
- display: none;
6697
- }
6698
-
6699
- :where(ui-layout-sidebar)[collapsed] :where([slot="sidebar"]) :where(ui-nav-group) > :where(details) > :where(summary) {
6700
- justify-content: center;
6701
- }
6702
-
6703
- :where(ui-layout-sidebar)[collapsed] :where([slot="sidebar"]) :where(ui-nav-group) > :where(details) > :where(summary)::after {
6704
- display: none;
6705
- }
6706
-
6707
- :where(ui-layout-sidebar)[collapsed] :where([slot="sidebar"]) :where(ui-nav-group)::after,
6708
- :where(ui-layout-sidebar)[collapsed] :where([slot="sidebar"]) :where(ui-nav-group)::before {
6709
- display: none;
6710
- }
6711
-
6712
- :where(ui-layout-sidebar)[collapsed] :where([slot="sidebar"]) :where(ui-nav-group-header) {
6713
- justify-content: center;
6714
- font-size: 0;
6715
- gap: 0;
6716
- }
6717
-
6718
- /* WHY: Icon keeps its size even though parent font-size is 0. */
6719
- :where(ui-layout-sidebar)[collapsed] :where([slot="sidebar"]) :where(ui-nav-group-header) :where(ui-icon) {
6720
- font-size: var(--_font-size, 1rem);
6721
- }
6722
-
6723
- :where(ui-layout-sidebar)[collapsed] :where([slot="sidebar"]) :where(ui-nav-group) + :where(ui-nav-group) {
6724
- margin-block-start: 0;
6725
- }
6726
-
6727
6785
  /* ── Sidebar Header ── */
6786
+ /* WHY: Absolute-positioned overlay pinned to top of aside. Content scrolls
6787
+ underneath it. z-index: 2 sits above content (z-index: 0). */
6728
6788
 
6729
6789
  :where(ui-layout-sidebar-header) {
6790
+ position: absolute;
6791
+ top: 0;
6792
+ left: 0;
6793
+ right: 0;
6794
+ z-index: 2;
6730
6795
  display: flex;
6731
- align-items: center;
6732
- min-height: var(--ui-layout-bar-height);
6733
- gap: calc(var(--_space) * 2);
6734
- padding-block: var(--_space);
6735
- padding-inline: calc(var(--_space-k) * var(--_space));
6736
- flex-shrink: 0;
6796
+ flex-direction: column;
6737
6797
  }
6738
6798
 
6739
6799
  /* ── Sidebar Content ── */
6800
+ /* WHY: Full height of the aside, scrollable. Padding-block offsets keep
6801
+ content from starting behind header / ending behind footer.
6802
+ Fade-out alpha mask dissolves content edges under the overlays. */
6740
6803
 
6741
6804
  :where(ui-layout-sidebar-content) {
6742
6805
  display: flex;
6743
6806
  flex-direction: column;
6744
- gap: calc(var(--_space) * 2);
6745
- padding-block: var(--_space);
6746
- padding-inline: calc(var(--_space-k) * var(--_space));
6747
- flex: 1;
6748
- min-height: 0;
6807
+ padding-block-start: var(--_sidebar-header-height);
6808
+ padding-block-end: var(--_sidebar-footer-height);
6749
6809
  width: 100%;
6810
+ height: 100%;
6811
+ overflow-y: auto;
6812
+ scrollbar-width: none;
6813
+ }
6814
+
6815
+ /* WHY: When header is present, fade top edge. Content dissolves as it
6816
+ scrolls under the header — transparent at top, fully visible by 1.25×
6817
+ the header height. No opaque header background needed. */
6818
+ :where(ui-layout-sidebar-content)[data-has-header] {
6819
+ mask-image: linear-gradient(
6820
+ to bottom,
6821
+ transparent 0,
6822
+ black calc(var(--_sidebar-header-height) * 1.25),
6823
+ black 100%
6824
+ );
6825
+ }
6826
+
6827
+ /* WHY: When footer is present, fade bottom edge. */
6828
+ :where(ui-layout-sidebar-content)[data-has-footer] {
6829
+ mask-image: linear-gradient(
6830
+ to bottom,
6831
+ black 0,
6832
+ black calc(100% - var(--_sidebar-footer-height) * 1.25),
6833
+ transparent 100%
6834
+ );
6835
+ }
6836
+
6837
+ /* WHY: When both header AND footer are present, fade both edges. */
6838
+ :where(ui-layout-sidebar-content)[data-has-header][data-has-footer] {
6839
+ mask-image: linear-gradient(
6840
+ to bottom,
6841
+ transparent 0,
6842
+ black calc(var(--_sidebar-header-height) * 1.25),
6843
+ black calc(100% - var(--_sidebar-footer-height) * 1.25),
6844
+ transparent 100%
6845
+ );
6750
6846
  }
6751
6847
 
6752
6848
  /* ── Sidebar Footer ── */
6849
+ /* WHY: Absolute-positioned overlay pinned to bottom of aside. */
6753
6850
 
6754
6851
  :where(ui-layout-sidebar-footer) {
6852
+ position: absolute;
6853
+ bottom: 0;
6854
+ left: 0;
6855
+ right: 0;
6856
+ z-index: 2;
6755
6857
  display: flex;
6756
- align-items: center;
6757
- min-height: var(--ui-layout-bar-height);
6758
- gap: calc(var(--_space) * 2);
6759
- padding-block: var(--_space);
6760
- padding-inline: calc(var(--_space-k) * var(--_space));
6761
- flex-shrink: 0;
6858
+ flex-direction: column;
6762
6859
  }
6763
6860
 
6764
- /* ── Sidebar Trigger ── */
6765
- /* WHY: Menu trigger for sidebar header/footer. Styled like ui-nav-group-header:
6766
- bold text, icon support, hover color. Opens a popover on click. */
6861
+ /* ── Sidebar Item ── */
6862
+ /* WHY: Universal sidebar row element. Provides inline padding for any content
6863
+ (buttons, links, icons). When a child ui-listbox[popover] is present, JS
6864
+ wires PopoverController for click-to-toggle menu behavior.
6865
+ Absorbs the old ui-layout-sidebar-trigger role — one element for all rows. */
6767
6866
 
6768
- :where(ui-layout-sidebar-trigger) {
6867
+ :where(ui-layout-sidebar-item) {
6769
6868
  display: flex;
6770
6869
  align-items: center;
6771
6870
  flex: 1;
@@ -6781,29 +6880,59 @@
6781
6880
  user-select: none;
6782
6881
  border: none;
6783
6882
  background: none;
6784
- padding: var(--_space);
6883
+ padding-block: var(--_space);
6884
+ padding-inline: calc(var(--_space-k) * var(--_space));
6785
6885
  border-radius: var(--_radius);
6786
6886
  transition: color var(--_duration) var(--_easing);
6787
6887
  }
6788
6888
 
6789
- :where(ui-layout-sidebar-trigger):hover{
6889
+ /* WHY: Items in header/footer match the breadcrumb bar height so the
6890
+ first sidebar row aligns horizontally with the breadcrumb. */
6891
+ :where(ui-layout-sidebar-header) > :where(ui-layout-sidebar-item),
6892
+ :where(ui-layout-sidebar-footer) > :where(ui-layout-sidebar-item) {
6893
+ min-height: var(--ui-layout-bar-height);
6894
+ }
6895
+
6896
+ :where(ui-layout-sidebar-item):hover{
6790
6897
  color: var(--_ink-strong);
6791
6898
  }
6792
6899
 
6793
- :where(ui-layout-sidebar-trigger):focus-visible{
6900
+ :where(ui-layout-sidebar-item):focus-visible{
6794
6901
  outline: 2px solid var(--ui-focus-ring);
6795
6902
  outline-offset: -2px;
6796
6903
  }
6797
6904
 
6905
+ /* WHY: Fixed-size icon well so all sidebar icons (header logo, nav group icons,
6906
+ standalone icons) center-align on the same column width regardless of intrinsic
6907
+ icon size. 1.5rem matches the sidebar's visual rhythm at default density.
6908
+ Consumer wraps the icon: <span slot="icon"><ui-icon name="..."></ui-icon></span>
6909
+ The wrapper is the well; the icon keeps its own --ui-icon-size dimensions. */
6910
+ :where(ui-layout-sidebar-item) > :where([slot="icon"]) {
6911
+ display: inline-flex;
6912
+ align-items: center;
6913
+ justify-content: center;
6914
+ width: 1.5rem;
6915
+ height: 1.5rem;
6916
+ flex-shrink: 0;
6917
+ }
6918
+
6798
6919
  /* WHY: Trailing caret pushes to end — same pattern as button justify="spread". */
6799
- :where(ui-layout-sidebar-trigger) > :where([slot="trailing"]) {
6920
+ :where(ui-layout-sidebar-item) > :where([slot="trailing"]) {
6800
6921
  margin-inline-start: auto;
6801
6922
  flex-shrink: 0;
6802
6923
  color: var(--_ink-muted);
6803
6924
  }
6804
6925
 
6926
+ /* WHY: When the item wraps a sub-component (e.g. ui-button) that provides
6927
+ its own icon, hide [slot="icon"] in expanded mode — it only serves as the
6928
+ bare collapsed-rail representation. Items without sub-components (just
6929
+ icon + label + trailing) keep [slot="icon"] visible in both modes. */
6930
+ :where(ui-layout-sidebar-item:has(> ui-button)) > :where([slot="icon"]) {
6931
+ display: none;
6932
+ }
6933
+
6805
6934
  /* WHY: Title text truncates when sidebar narrows during resize. */
6806
- :where(ui-layout-sidebar-trigger) > :where([slot="label"]) {
6935
+ :where(ui-layout-sidebar-item) > :where([slot="label"]) {
6807
6936
  flex: 1;
6808
6937
  min-width: 0;
6809
6938
  white-space: nowrap;
@@ -6811,25 +6940,67 @@
6811
6940
  text-overflow: ellipsis;
6812
6941
  }
6813
6942
 
6814
- /* ── Trigger Popover ── */
6815
- /* WHY: Popover opens to the right of the sidebar trigger.
6943
+ /* ── Item Popover ── */
6944
+ /* WHY: Popover opens to the right of the sidebar item.
6816
6945
  Default: top-aligned, grows downward (span-block-end).
6817
6946
  Flip: bottom-aligned, grows upward (span-block-start). */
6818
6947
 
6819
- :where(ui-layout-sidebar-trigger) > :where(ui-listbox[popover]) {
6948
+ :where(ui-layout-sidebar-item) > :where(ui-listbox[popover]) {
6820
6949
  position: fixed;
6821
6950
  position-area: inline-end span-block-end;
6822
- position-try-fallbacks: --flip-up;
6951
+ position-try-fallbacks: --sidebar-item-flip-up;
6823
6952
  margin: 0 0 0 var(--ui-popover-gap);
6824
6953
  min-width: 200px;
6825
6954
  max-height: var(--ui-popover-max-height);
6826
6955
  overflow-y: auto;
6827
6956
  }
6828
6957
 
6829
- @position-try --flip-up {
6958
+ @position-try --sidebar-item-flip-up {
6830
6959
  position-area: inline-end span-block-start;
6831
6960
  }
6832
6961
 
6962
+ /* ── Container Query: Collapsed Sidebar ── */
6963
+ /* WHY: Each component owns its own collapsed behavior via @container.
6964
+ The aside is the container (container-name: sidebar). When it shrinks
6965
+ to 48px (icon rail), components respond to the width, not to [collapsed].
6966
+ Threshold 80px: collapsed = 48px, min expanded = 160px. */
6967
+
6968
+ @container sidebar (max-width: 80px) {
6969
+
6970
+ /* Resize handle — no dragging in icon rail */
6971
+ :where(.layout-resize-handle) {
6972
+ display: none;
6973
+ }
6974
+
6975
+ /* Header/footer — center content horizontally */
6976
+ :where(ui-layout-sidebar-header),
6977
+ :where(ui-layout-sidebar-footer) {
6978
+ align-items: center;
6979
+ }
6980
+
6981
+ /* Content — center items in the 48px rail */
6982
+ :where(ui-layout-sidebar-content) {
6983
+ align-items: center;
6984
+ }
6985
+
6986
+ /* Item — shrink to icon-only. Show [slot="icon"], hide everything else.
6987
+ WHY: Sidebar items can contain complex children (buttons with slots,
6988
+ links, etc.). Rather than overriding sub-component styles, the item
6989
+ provides its own bare icon via [slot="icon"] for the collapsed rail. */
6990
+ :where(ui-layout-sidebar-item) {
6991
+ flex: 0 0 auto;
6992
+ padding-inline: var(--_space);
6993
+ }
6994
+
6995
+ :where(ui-layout-sidebar-item) > :where([slot="icon"]) {
6996
+ display: inline-flex;
6997
+ }
6998
+
6999
+ :where(ui-layout-sidebar-item) > :where(:not([slot="icon"]):not(ui-listbox[popover]):not(.nav-group-flyout)) {
7000
+ display: none;
7001
+ }
7002
+ }
7003
+
6833
7004
  }
6834
7005
 
6835
7006
  @layer ui {