@xihan-ui/styles 1.0.0-alpha.0 → 1.0.0-alpha.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.
Files changed (58) hide show
  1. package/CHANGELOG.md +261 -0
  2. package/css/back-top.css +2 -2
  3. package/css/button-group.css +1 -1
  4. package/css/button.css +1 -1
  5. package/css/calendar.css +1 -1
  6. package/css/carousel.css +2 -2
  7. package/css/cascader.css +199 -24
  8. package/css/checkbox-group.css +20 -20
  9. package/css/checkbox.css +1 -1
  10. package/css/clipboard.css +3 -3
  11. package/css/color-picker.css +21 -5
  12. package/css/combobox.css +23 -20
  13. package/css/composer.css +1 -1
  14. package/css/context-menu.css +56 -26
  15. package/css/date-field.css +5 -5
  16. package/css/date-picker.css +66 -2
  17. package/css/drawer.css +3 -3
  18. package/css/dynamic-input.css +2 -2
  19. package/css/editable.css +6 -6
  20. package/css/field.css +32 -1
  21. package/css/file-upload.css +3 -3
  22. package/css/float-button.css +2 -2
  23. package/css/form.css +17 -5
  24. package/css/hover-card.css +15 -11
  25. package/css/image-viewer.css +176 -0
  26. package/css/listbox.css +9 -6
  27. package/css/mention.css +14 -22
  28. package/css/menu.css +38 -23
  29. package/css/menubar.css +28 -17
  30. package/css/number-field.css +16 -16
  31. package/css/pin-input.css +6 -6
  32. package/css/popconfirm.css +23 -0
  33. package/css/popover.css +15 -11
  34. package/css/popselect.css +31 -16
  35. package/css/progress.css +68 -1
  36. package/css/radio-group.css +1 -1
  37. package/css/select.css +148 -17
  38. package/css/side-nav.css +163 -0
  39. package/css/slider.css +55 -0
  40. package/css/steps.css +2 -2
  41. package/css/switch.css +27 -0
  42. package/css/table.css +6 -6
  43. package/css/tags-input.css +7 -7
  44. package/css/text-field.css +23 -10
  45. package/css/thread.css +1 -1
  46. package/css/time-field.css +5 -5
  47. package/css/time-picker.css +36 -26
  48. package/css/toggle-group.css +1 -1
  49. package/css/toggle.css +1 -1
  50. package/css/tone.css +22 -0
  51. package/css/tooltip.css +20 -15
  52. package/css/tour.css +16 -12
  53. package/css/transfer.css +11 -9
  54. package/css/tree-select.css +26 -18
  55. package/css/tree.css +8 -5
  56. package/index.css +6 -2
  57. package/index.unlayered.css +1499 -413
  58. package/package.json +4 -2
@@ -4,11 +4,16 @@
4
4
  * 无层 reset/normalize 时使用。取舍见文档站的「安装与接入」。
5
5
  */
6
6
 
7
+ /* 层序声明必须最先:级联层的先后由首次出现定死,layers.css 打头才立得住序。 */
8
+
9
+ /* styles/layers.css */
10
+ /* 库层序单一真源:级联顺序由首次声明决定,与 @import 顺序解耦。 */
11
+
7
12
  /* 令牌先于一切:皮肤里不写兜底值,令牌缺席就是缺陷,不是降级。
8
13
  tokens.css 自带 @layer xihan.tokens,层序由 layers.css 定。 */
9
14
  @import '@xihan-ui/tokens/tokens.css';
10
15
 
11
- /* 层序声明必须最先,其余按层归属排列。 */
16
+ /* 其余按层归属排列。 */
12
17
 
13
18
  /* styles/affix.css */
14
19
  /* 占位盒就是 content 在常规流里占的那块地方。
@@ -149,7 +154,7 @@
149
154
  inline-size: var(--xh-back-top-trigger-size, var(--xh-_back-top-size));
150
155
  block-size: var(--xh-back-top-trigger-size, var(--xh-_back-top-size));
151
156
  padding: 0;
152
- border: var(--xh-stroke-thin) solid var(--xh-back-top-border, var(--xh-border-default));
157
+ border: var(--xh-stroke-thin) solid var(--xh-back-top-border, var(--xh-border-control));
153
158
  border-radius: var(--xh-back-top-radius, var(--xh-shape-pill));
154
159
  /* 使用者令牌排在语气之前:写了覆盖钩子就以它为准,没写才退到语气,最后才是中性缺省 */
155
160
  background: var(--xh-back-top-bg, var(--xh-bg-surface-raised));
@@ -165,7 +170,7 @@
165
170
  /* 悬停只在能用指针的设备上生效,触摸端不留一个按不掉的悬停态 */
166
171
  @media (hover: hover) {
167
172
  [data-scope='back-top'][data-part='trigger']:hover {
168
- border-color: var(--xh-back-top-border-hover, var(--xh-border-strong));
173
+ border-color: var(--xh-back-top-border-hover, var(--xh-border-control-hover));
169
174
  background: var(--xh-back-top-bg-hover, var(--xh-bg-subtle-hover));
170
175
  }
171
176
  }
@@ -257,7 +262,7 @@
257
262
  /* 段的描边归组画(两个适配器各一条选择器:Vue 的段就是直接子节点,WC 的段在行为宿主里面):合边本来就是组的事,按钮那边没有描边色的槽位可以流 */
258
263
  [data-scope='button-group'][data-part='root'][data-variant='outline'] > *,
259
264
  [data-scope='button-group'][data-part='root'][data-variant='outline'] > * [data-scope='button'][data-part='root'] {
260
- border-color: var(--xh-_tone-border, var(--xh-border-default));
265
+ border-color: var(--xh-_tone-border-control, var(--xh-border-control));
261
266
  }
262
267
 
263
268
  /* —— 合边 —— */
@@ -651,7 +656,7 @@
651
656
  gap: var(--xh-dynamic-input-action-gap, var(--xh-space-1));
652
657
  block-size: var(--xh-dynamic-input-add-height, var(--xh-control-h-md));
653
658
  padding-inline: var(--xh-dynamic-input-add-px, var(--xh-control-px-md));
654
- border: var(--xh-stroke-thin) dashed var(--xh-dynamic-input-add-border, var(--xh-border-default));
659
+ border: var(--xh-stroke-thin) dashed var(--xh-dynamic-input-add-border, var(--xh-border-control));
655
660
  border-radius: var(--xh-dynamic-input-add-radius, var(--xh-shape-control));
656
661
  background: var(--xh-dynamic-input-add-bg, transparent);
657
662
  color: var(--xh-dynamic-input-add-fg, var(--xh-fg-brand));
@@ -669,7 +674,7 @@
669
674
  }
670
675
 
671
676
  [data-scope='dynamic-input'][data-part='add-trigger']:hover:not([aria-disabled='true']) {
672
- border-color: var(--xh-dynamic-input-add-border-hover, var(--xh-border-strong));
677
+ border-color: var(--xh-dynamic-input-add-border-hover, var(--xh-border-control-hover));
673
678
  background: var(--xh-dynamic-input-add-bg-hover, var(--xh-bg-subtle-hover));
674
679
  }
675
680
 
@@ -883,7 +888,7 @@
883
888
  inline-size: var(--xh-_float-button-size);
884
889
  block-size: var(--xh-_float-button-size);
885
890
  padding: 0;
886
- border: var(--xh-stroke-thin) solid var(--xh-float-button-border, var(--xh-border-default));
891
+ border: var(--xh-stroke-thin) solid var(--xh-float-button-border, var(--xh-border-control));
887
892
  border-radius: var(--xh-_float-button-radius);
888
893
  /* 使用者令牌排在缺省之前:写了覆盖钩子就以它为准,没写才退到中性缺省 */
889
894
  background: var(--xh-float-button-bg, var(--xh-bg-surface-raised));
@@ -899,7 +904,7 @@
899
904
  /* 悬停只在能用指针的设备上生效,触摸端不留一个按不掉的悬停态 */
900
905
  @media (hover: hover) {
901
906
  [data-scope='float-button'][data-part='trigger']:not([data-disabled]):hover {
902
- border-color: var(--xh-float-button-border-hover, var(--xh-border-strong));
907
+ border-color: var(--xh-float-button-border-hover, var(--xh-border-control-hover));
903
908
  background: var(--xh-float-button-bg-hover, var(--xh-bg-subtle-hover));
904
909
  }
905
910
  }
@@ -1543,9 +1548,6 @@
1543
1548
  display: none;
1544
1549
  }
1545
1550
 
1546
- /* styles/layers.css */
1547
- /* 库层序单一真源:级联顺序由首次声明决定,与 @import 顺序解耦。 */
1548
-
1549
1551
  /* styles/layout.css */
1550
1552
  /* 页面骨架排成三行两列:头与脚各横贯一整行,侧栏与内容并排占中间那一行。
1551
1553
  缺哪一段,对应的行/列就没有条目,宽高自动收成 0,不必为"少写一段"另立规则。
@@ -2089,14 +2091,11 @@
2089
2091
 
2090
2092
  /* 形态档只换这三个槽:缺省即 outline(描边 + 画布底) */
2091
2093
  --xh-_mention-input-bg: var(--xh-bg-canvas);
2092
- --xh-_mention-input-border: var(--xh-border-default);
2093
- --xh-_mention-input-border-hover: var(--xh-border-strong);
2094
+ --xh-_mention-input-border: var(--xh-border-control);
2095
+ --xh-_mention-input-border-hover: var(--xh-border-control-hover);
2094
2096
 
2095
- /* 强调色随语气走,只落在聚焦与高亮上,正文文字色不动。
2096
- 没写 data-tone 时 --xh-_tone 没被声明,三处都退回原来的取值 */
2097
+ /* 聚焦环恒用固定环色,不随语气走 */
2097
2098
  --xh-_mention-ring: var(--xh-ring-focus);
2098
- --xh-_mention-item-highlight-bg: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover));
2099
- --xh-_mention-item-highlight-fg: var(--xh-_tone-fg, var(--xh-fg-brand-strong));
2100
2099
 
2101
2100
  display: block;
2102
2101
  }
@@ -2127,21 +2126,21 @@
2127
2126
  形态只决定描边与底色怎么用,用哪族颜色由 data-tone 决定 */
2128
2127
  [data-scope='mention'][data-part='root'][data-variant='outline'] {
2129
2128
  --xh-_mention-input-bg: var(--xh-bg-canvas);
2130
- --xh-_mention-input-border: var(--xh-border-default);
2131
- --xh-_mention-input-border-hover: var(--xh-border-strong);
2129
+ --xh-_mention-input-border: var(--xh-border-control);
2130
+ --xh-_mention-input-border-hover: var(--xh-border-control-hover);
2132
2131
  }
2133
2132
 
2134
2133
  /* 填充式:底色顶替描边,悬停时才浮出一道淡边 */
2135
2134
  [data-scope='mention'][data-part='root'][data-variant='subtle'] {
2136
2135
  --xh-_mention-input-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
2137
2136
  --xh-_mention-input-border: transparent;
2138
- --xh-_mention-input-border-hover: var(--xh-_tone-border, var(--xh-border-default));
2137
+ --xh-_mention-input-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
2139
2138
  }
2140
2139
 
2141
2140
  [data-scope='mention'][data-part='root'][data-variant='ghost'] {
2142
2141
  --xh-_mention-input-bg: transparent;
2143
2142
  --xh-_mention-input-border: transparent;
2144
- --xh-_mention-input-border-hover: var(--xh-_tone-border, var(--xh-border-default));
2143
+ --xh-_mention-input-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
2145
2144
  }
2146
2145
 
2147
2146
  /* 框与聚焦环都长在输入框自己身上:这里没有把输入框和别的控件裹成一体的输入行,
@@ -2246,8 +2245,8 @@
2246
2245
  display: flex;
2247
2246
  align-items: center;
2248
2247
  gap: var(--xh-mention-item-gap, var(--xh-_mention-gap));
2249
- padding-block: var(--xh-mention-item-py, var(--xh-space-1));
2250
- padding-inline: var(--xh-mention-item-px, var(--xh-control-px-sm));
2248
+ padding-block: var(--xh-mention-item-py, var(--xh-space-1_5));
2249
+ padding-inline: var(--xh-mention-item-px, var(--xh-control-px-md));
2251
2250
  border-radius: var(--xh-mention-item-radius, var(--xh-shape-control));
2252
2251
  color: var(--xh-mention-item-fg, var(--xh-fg-default));
2253
2252
  font-size: var(--xh-mention-item-font-size, var(--xh-_mention-font-size));
@@ -2256,18 +2255,19 @@
2256
2255
  white-space: nowrap;
2257
2256
  cursor: pointer;
2258
2257
  user-select: none;
2258
+ transition:
2259
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
2260
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
2259
2261
  }
2260
2262
 
2261
2263
  [data-scope='mention'][data-part='item'][hidden] {
2262
2264
  display: none;
2263
2265
  }
2264
2266
 
2265
- /* 只认 data-highlighted,不写 :hover:指针划过时连接层已经把高亮搬过来了,
2267
+ /* 键盘锚点走中性灰轻档。只认 data-highlighted,不写 :hover:指针划过时连接层已经把高亮搬过来了,
2266
2268
  两套底色各画各的会出现"鼠标停在 A 上、回车却插进了 B" */
2267
2269
  [data-scope='mention'][data-part='item'][data-highlighted]:not([data-disabled]) {
2268
- background: var(--xh-mention-item-bg-highlight, var(--xh-_mention-item-highlight-bg));
2269
- color: var(--xh-mention-item-fg-highlight, var(--xh-_mention-item-highlight-fg));
2270
- font-weight: var(--xh-mention-item-font-weight-highlight, var(--xh-font-weight-medium));
2270
+ background: var(--xh-mention-item-bg-hover, var(--xh-bg-subtle));
2271
2271
  }
2272
2272
 
2273
2273
  /* 禁用走 aria-disabled 而非原生 disabled(原生 disabled 不派 click,禁用守卫就走不到),
@@ -2290,12 +2290,6 @@
2290
2290
  display: none;
2291
2291
  }
2292
2292
 
2293
- /* 候选行里除文本以外的部分(作者摆的头像、副标题)由作者自己画;
2294
- 这里只把强调色透出去,作者要用就取它 */
2295
- [data-scope='mention'][data-part='item'][data-highlighted] [data-part='item-text'] {
2296
- color: var(--xh-mention-item-fg-highlight, var(--xh-_mention-item-highlight-fg));
2297
- }
2298
-
2299
2293
  [data-scope='mention'][data-part='item'][data-disabled] [data-part='item-text'] {
2300
2294
  color: var(--xh-fg-disabled);
2301
2295
  }
@@ -2539,6 +2533,29 @@
2539
2533
  color: var(--xh-popconfirm-confirm-fg, var(--xh-_tone-on, var(--xh-fg-on-brand)));
2540
2534
  }
2541
2535
 
2536
+ /* 转圈的关键帧定义在本文件里:引用别处文件的动画名时那份文件不一定在场,动画会静默不跑 */
2537
+ @keyframes xh-popconfirm-rotate {
2538
+ to {
2539
+ transform: rotate(1turn);
2540
+ }
2541
+ }
2542
+
2543
+ /* 异步确认挂起:按钮转圈、指针挂起;不是禁用,焦点与对比度都保持 */
2544
+ [data-scope='popconfirm'][data-part='confirm-trigger'][data-loading] {
2545
+ cursor: wait;
2546
+ }
2547
+
2548
+ [data-scope='popconfirm'][data-part='confirm-trigger'][data-loading]::before {
2549
+ content: '';
2550
+ inline-size: 1em;
2551
+ block-size: 1em;
2552
+ margin-inline-end: var(--xh-space-1);
2553
+ border: var(--xh-stroke-thin) solid transparent;
2554
+ border-block-start-color: currentcolor;
2555
+ border-radius: var(--xh-shape-pill);
2556
+ animation: xh-popconfirm-rotate var(--xh-popconfirm-loading-duration, calc(var(--xh-duration-slow) * 2)) linear infinite;
2557
+ }
2558
+
2542
2559
  [data-scope='popconfirm'][data-part='confirm-trigger']:hover {
2543
2560
  background: var(--xh-_tone-hover, var(--xh-bg-brand-hover));
2544
2561
  }
@@ -2578,14 +2595,13 @@
2578
2595
 
2579
2596
  /* 形态档只换这三个槽:缺省即 outline(描边 + 画布底) */
2580
2597
  --xh-_popselect-trigger-bg: var(--xh-bg-canvas);
2581
- --xh-_popselect-trigger-border: var(--xh-border-default);
2582
- --xh-_popselect-trigger-border-hover: var(--xh-border-strong);
2598
+ --xh-_popselect-trigger-border: var(--xh-border-control);
2599
+ --xh-_popselect-trigger-border-hover: var(--xh-border-control-hover);
2583
2600
 
2584
- /* 强调色随语气走,只落在聚焦与选中上,正文文字色不动。
2585
- 没写 data-tone 时 --xh-_tone 没被声明,三处都退回原来的取值 */
2601
+ /* 聚焦环恒用固定环色;强调色随语气走,只落在勾选标记与选中文字上,正文文字色不动。
2602
+ 没写 data-tone 时 --xh-_tone 没被声明,两处都退回原来的取值 */
2586
2603
  --xh-_popselect-ring: var(--xh-ring-focus);
2587
2604
  --xh-_popselect-accent: var(--xh-_tone, var(--xh-bg-brand));
2588
- --xh-_popselect-item-highlight-bg: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover));
2589
2605
  --xh-_popselect-item-selected-fg: var(--xh-_tone-fg, var(--xh-fg-brand-strong));
2590
2606
 
2591
2607
  display: inline-flex;
@@ -2611,21 +2627,21 @@
2611
2627
  /* 形态只决定描边与底色怎么用,用哪族颜色由 data-tone 决定 */
2612
2628
  [data-scope='popselect'][data-part='root'][data-variant='outline'] {
2613
2629
  --xh-_popselect-trigger-bg: var(--xh-bg-canvas);
2614
- --xh-_popselect-trigger-border: var(--xh-border-default);
2615
- --xh-_popselect-trigger-border-hover: var(--xh-border-strong);
2630
+ --xh-_popselect-trigger-border: var(--xh-border-control);
2631
+ --xh-_popselect-trigger-border-hover: var(--xh-border-control-hover);
2616
2632
  }
2617
2633
 
2618
2634
  /* 填充式:底色顶替描边,悬停时才浮出一道淡边 */
2619
2635
  [data-scope='popselect'][data-part='root'][data-variant='subtle'] {
2620
2636
  --xh-_popselect-trigger-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
2621
2637
  --xh-_popselect-trigger-border: transparent;
2622
- --xh-_popselect-trigger-border-hover: var(--xh-_tone-border, var(--xh-border-default));
2638
+ --xh-_popselect-trigger-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
2623
2639
  }
2624
2640
 
2625
2641
  [data-scope='popselect'][data-part='root'][data-variant='ghost'] {
2626
2642
  --xh-_popselect-trigger-bg: transparent;
2627
2643
  --xh-_popselect-trigger-border: transparent;
2628
- --xh-_popselect-trigger-border-hover: var(--xh-_tone-border, var(--xh-border-default));
2644
+ --xh-_popselect-trigger-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
2629
2645
  }
2630
2646
 
2631
2647
  [data-scope='popselect'][data-part='trigger'] {
@@ -2699,8 +2715,14 @@
2699
2715
  display: none;
2700
2716
  }
2701
2717
 
2702
- /* 容器只是焦点域兜底落点,落焦不画环:高亮永远长在条目上 */
2703
- [data-scope='popselect'][data-part='content']:focus-visible {
2718
+ /* 焦点落在容器上只有一种情形:指针打开、无选中值、还没有任何条目当锚点。
2719
+ 那一刻它认领着 Tab 位,环画在浮层内沿;有锚点时环长在条目上,容器不再画 */
2720
+ [data-scope='popselect'][data-part='content'][tabindex='0']:focus-visible {
2721
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
2722
+ outline-offset: calc(-1 * var(--xh-ring-width));
2723
+ }
2724
+
2725
+ [data-scope='popselect'][data-part='content']:not([tabindex='0']):focus-visible {
2704
2726
  outline: none;
2705
2727
  }
2706
2728
 
@@ -2712,8 +2734,8 @@
2712
2734
  display: flex;
2713
2735
  align-items: center;
2714
2736
  gap: var(--xh-popselect-item-gap, var(--xh-_popselect-gap));
2715
- padding-block: var(--xh-popselect-item-py, var(--xh-space-1));
2716
- padding-inline: var(--xh-popselect-item-px, var(--xh-control-px-sm));
2737
+ padding-block: var(--xh-popselect-item-py, var(--xh-space-1_5));
2738
+ padding-inline: var(--xh-popselect-item-px, var(--xh-control-px-md));
2717
2739
  border-radius: var(--xh-popselect-item-radius, var(--xh-shape-control));
2718
2740
  color: var(--xh-popselect-item-fg, var(--xh-fg-default));
2719
2741
  font-size: var(--xh-popselect-item-font-size, var(--xh-_popselect-font-size));
@@ -2722,11 +2744,14 @@
2722
2744
  white-space: nowrap;
2723
2745
  cursor: pointer;
2724
2746
  user-select: none;
2747
+ transition:
2748
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
2749
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
2725
2750
  }
2726
2751
 
2727
- /* 键盘高亮与指针高亮共用一套底色:条目本身承载焦点,不另画描边 */
2752
+ /* 悬停与键盘锚点共用中性灰轻档:条目本身承载焦点,不另画描边 */
2728
2753
  [data-scope='popselect'][data-part='item']:not([data-disabled]):is(:hover, [data-highlighted]) {
2729
- background: var(--xh-popselect-item-bg-highlight, var(--xh-_popselect-item-highlight-bg));
2754
+ background: var(--xh-popselect-item-bg-hover, var(--xh-bg-subtle));
2730
2755
  }
2731
2756
 
2732
2757
  /* 选中只换文字色与字重,配合行尾的 ✓ 标记;底色整条通道留给高亮,两者因此能同时看见 */
@@ -2735,7 +2760,14 @@
2735
2760
  font-weight: var(--xh-popselect-item-font-weight-selected, var(--xh-font-weight-medium));
2736
2761
  }
2737
2762
 
2738
- [data-scope='popselect'][data-part='item']:focus {
2763
+ /* 键盘走到条目上要画环:轻档底色对白底只有 1.1:1,够不上焦点指示器的对比度要求。
2764
+ 指针悬停走的是程序化聚焦,鼠标模态下 :focus-visible 不匹配,不会误画 */
2765
+ [data-scope='popselect'][data-part='item']:focus-visible {
2766
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
2767
+ outline-offset: calc(-1 * var(--xh-ring-width));
2768
+ }
2769
+
2770
+ [data-scope='popselect'][data-part='item']:focus:not(:focus-visible) {
2739
2771
  outline: none;
2740
2772
  }
2741
2773
 
@@ -3624,6 +3656,9 @@
3624
3656
  --xh-_tone-subtle-hover: color-mix(in oklab, var(--xh-_tone) 20%, var(--xh-bg-surface));
3625
3657
  --xh-_tone-subtle-active: color-mix(in oklab, var(--xh-_tone) 28%, var(--xh-bg-surface));
3626
3658
  --xh-_tone-border: color-mix(in oklab, var(--xh-_tone) 40%, var(--xh-bg-surface));
3659
+ /* 控件的可操作区边界要 3:1,兑了底色的 --xh-_tone-border 六族全在 1.44–2.18,够不着。
3660
+ 这一支直接取主色本体;两族在各自的底上仍不够,在下面按主题单独兜 */
3661
+ --xh-_tone-border-control: var(--xh-_tone);
3627
3662
  /* 在普通背景上表达该语气的文字色:往前景色兑,浅色态变深、深色态变浅 */
3628
3663
  --xh-_tone-fg: color-mix(in oklab, var(--xh-_tone) 72%, var(--xh-fg-default));
3629
3664
  }
@@ -3669,6 +3704,25 @@
3669
3704
  --xh-_tone-shift: var(--xh-color-neutral-0);
3670
3705
  }
3671
3706
 
3707
+ /* 主色本体在各自的底上有两族够不到 3:1:黄在白底上只有 2.70,中性在深色底上只有 2.54。
3708
+ 语气色是固定原语、不随主题翻,所以这两族只能按主题各兜一次。
3709
+ 浅色是缺省态(不写 data-theme 也算浅色),深色靠属性选择器压过它 */
3710
+ [data-tone='warning'] {
3711
+ --xh-_tone-border-control: var(--xh-color-warning-700);
3712
+ }
3713
+
3714
+ [data-theme='dark'] [data-tone='warning'] {
3715
+ --xh-_tone-border-control: var(--xh-color-warning-600);
3716
+ }
3717
+
3718
+ [data-tone='neutral'] {
3719
+ --xh-_tone-border-control: var(--xh-color-neutral-600);
3720
+ }
3721
+
3722
+ [data-theme='dark'] [data-tone='neutral'] {
3723
+ --xh-_tone-border-control: var(--xh-color-neutral-550);
3724
+ }
3725
+
3672
3726
  /* styles/alert.css */
3673
3727
  [data-scope='alert'][data-part='root'] {
3674
3728
  /* 强调色随语气走,底色/描边/图标色都由它推导出来,换语气不必逐条属性各写一遍。
@@ -4552,7 +4606,7 @@
4552
4606
  --xh-_bg-active: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-active));
4553
4607
  --xh-_fg: var(--xh-_tone-fg, var(--xh-fg-default));
4554
4608
 
4555
- border-color: var(--xh-_tone-border, var(--xh-border-default));
4609
+ border-color: var(--xh-_tone-border-control, var(--xh-border-control));
4556
4610
  }
4557
4611
 
4558
4612
  [data-scope='button'][data-part='root'][data-variant='ghost'] {
@@ -4770,7 +4824,7 @@
4770
4824
 
4771
4825
  /* 今天:只描一圈边,不占用选中色——今天与被选中是两件事,得能同时看得出来 */
4772
4826
  [data-scope='calendar'][data-part='cell-trigger'][data-today] {
4773
- border-color: var(--xh-calendar-today-border, var(--xh-border-strong));
4827
+ border-color: var(--xh-calendar-today-border, var(--xh-border-control));
4774
4828
  }
4775
4829
 
4776
4830
  [data-scope='calendar'][data-part='cell-trigger'][data-selected] {
@@ -4999,7 +5053,7 @@
4999
5053
  justify-content: center;
5000
5054
  inline-size: var(--xh-carousel-trigger-size, var(--xh-control-h-md));
5001
5055
  block-size: var(--xh-carousel-trigger-size, var(--xh-control-h-md));
5002
- border: var(--xh-stroke-thin) solid var(--xh-carousel-trigger-border, var(--xh-border-default));
5056
+ border: var(--xh-stroke-thin) solid var(--xh-carousel-trigger-border, var(--xh-border-control));
5003
5057
  border-radius: var(--xh-carousel-trigger-radius, var(--xh-shape-pill));
5004
5058
  background: var(--xh-carousel-trigger-bg, var(--xh-bg-surface));
5005
5059
  color: var(--xh-carousel-trigger-fg, var(--xh-fg-default));
@@ -5081,7 +5135,7 @@
5081
5135
  padding: 0;
5082
5136
  border: 0;
5083
5137
  border-radius: var(--xh-carousel-indicator-radius, var(--xh-radius-full));
5084
- background: var(--xh-carousel-indicator-bg, var(--xh-border-strong));
5138
+ background: var(--xh-carousel-indicator-bg, var(--xh-border-control));
5085
5139
  font-size: 0;
5086
5140
  cursor: pointer;
5087
5141
  transition: background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
@@ -5122,13 +5176,16 @@
5122
5176
 
5123
5177
  /* 形态档只换触发框的描边与底色这三个槽,浮层与列不跟着变:
5124
5178
  变的是「这是个输入位」的表达方式,不是弹出内容的表达方式 */
5125
- --xh-_cascader-border: var(--xh-border-default);
5126
- --xh-_cascader-border-hover: var(--xh-border-strong);
5179
+ --xh-_cascader-border: var(--xh-border-control);
5180
+ --xh-_cascader-border-hover: var(--xh-border-control-hover);
5127
5181
  --xh-_cascader-bg: var(--xh-bg-canvas);
5128
5182
 
5129
5183
  /* 选中条目的文字色随语气走,没写 data-tone 时退回品牌强调色 */
5130
5184
  --xh-_cascader-selected-fg: var(--xh-_tone-fg, var(--xh-fg-brand-strong));
5131
5185
 
5186
+ /* 展开路径的底色:随语气走淡底,没写 data-tone 时退回品牌淡底 */
5187
+ --xh-_cascader-active-bg: var(--xh-_tone-subtle, var(--xh-bg-brand-subtle));
5188
+
5132
5189
  display: inline-flex;
5133
5190
  flex-wrap: wrap;
5134
5191
  align-items: center;
@@ -5144,15 +5201,15 @@
5144
5201
  /* 形态只决定描边与底色怎么用;用哪族颜色由 data-tone 决定,没写 tone 就沿用原来的中性配色 */
5145
5202
  /* outline 就是缺省那一身:描边加画布底,写出来只为让三档并列可读 */
5146
5203
  [data-scope='cascader'][data-part='root'][data-variant='outline'] {
5147
- --xh-_cascader-border: var(--xh-border-default);
5148
- --xh-_cascader-border-hover: var(--xh-border-strong);
5204
+ --xh-_cascader-border: var(--xh-border-control);
5205
+ --xh-_cascader-border-hover: var(--xh-border-control-hover);
5149
5206
  --xh-_cascader-bg: var(--xh-bg-canvas);
5150
5207
  }
5151
5208
 
5152
5209
  /* subtle 是填充式:底色接过「这里能点」的表达,描边随之让位 */
5153
5210
  [data-scope='cascader'][data-part='root'][data-variant='subtle'] {
5154
5211
  --xh-_cascader-border: transparent;
5155
- --xh-_cascader-border-hover: var(--xh-_tone-border, var(--xh-border-default));
5212
+ --xh-_cascader-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
5156
5213
  --xh-_cascader-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
5157
5214
  }
5158
5215
 
@@ -5228,9 +5285,9 @@
5228
5285
  }
5229
5286
 
5230
5287
  /* trigger 是整个控件唯一的 Tab 停靠点(展开后停靠点才移进列里),落焦必须看得见。
5231
- 语气落在环上:没写 data-tone 时 --xh-_tone 没被声明,退回原来的聚焦环色 */
5288
+ 聚焦环恒用固定环色,不随语气走 */
5232
5289
  [data-scope='cascader'][data-part='trigger']:focus-visible {
5233
- outline: var(--xh-ring-width) solid var(--xh-_tone, var(--xh-ring-focus));
5290
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
5234
5291
  outline-offset: var(--xh-ring-offset);
5235
5292
  }
5236
5293
 
@@ -5281,6 +5338,11 @@
5281
5338
  display: none;
5282
5339
  }
5283
5340
 
5341
+ /* 没写内容就由皮肤画箭头;作者往部件里塞了自己的图形,这条规则即不命中 */
5342
+ [data-scope='cascader'][data-part='indicator']:empty::before {
5343
+ content: '▾';
5344
+ }
5345
+
5284
5346
  [data-scope='cascader'][data-part='indicator'][data-state='open'] {
5285
5347
  rotate: 180deg;
5286
5348
  }
@@ -5326,7 +5388,7 @@
5326
5388
  /* 清空按钮退出了 Tab 序列(tabindex=-1),正常操作走不到这里;
5327
5389
  留着是给程序化聚焦兜底,落焦时得看得见 */
5328
5390
  [data-scope='cascader'][data-part='clear-trigger']:focus-visible {
5329
- outline: var(--xh-ring-width) solid var(--xh-_tone, var(--xh-ring-focus));
5391
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
5330
5392
  outline-offset: var(--xh-ring-offset);
5331
5393
  }
5332
5394
 
@@ -5378,16 +5440,24 @@
5378
5440
  }
5379
5441
 
5380
5442
  [data-scope='cascader'][data-part='content'][data-state='open'] {
5443
+ /* 入场横移沿书写方向起始缘:位移量经私有槽供给关键帧,rtl 只翻符号 */
5444
+ --xh-_cascader-slide: -0.25rem;
5445
+
5381
5446
  animation: xh-cascader-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
5382
5447
  }
5383
5448
 
5384
- /* 一列:自己定宽、自己滚。列与列之间用一条分隔线,比留白更能说明「这是下一级」 */
5449
+ [dir='rtl'] [data-scope='cascader'][data-part='content'][data-state='open'] {
5450
+ --xh-_cascader-slide: 0.25rem;
5451
+ }
5452
+
5453
+ /* 一列:宽度由内容撑出、设下限防塌,高度定死——切换展开路径时浮层不上下跳动,
5454
+ 溢出的条目在列内自己滚。列与列之间用一条分隔线,比留白更能说明「这是下一级」 */
5385
5455
  [data-scope='cascader'][data-part='column'] {
5386
5456
  display: flex;
5387
5457
  flex: none;
5388
5458
  flex-direction: column;
5389
- inline-size: var(--xh-cascader-column-w, 11rem);
5390
- max-block-size: var(--xh-cascader-column-max-h, 16rem);
5459
+ min-inline-size: var(--xh-cascader-column-min-w, 7rem);
5460
+ block-size: var(--xh-cascader-column-h, 11.25rem);
5391
5461
  padding-block: var(--xh-cascader-column-py, var(--xh-space-1));
5392
5462
  padding-inline: var(--xh-cascader-column-px, var(--xh-space-1));
5393
5463
  overflow-y: auto;
@@ -5405,8 +5475,14 @@
5405
5475
  border-inline-start: var(--xh-stroke-thin) solid var(--xh-cascader-column-divider, var(--xh-border-subtle));
5406
5476
  }
5407
5477
 
5408
- /* 列不进 Tab 序列也不承载焦点,落焦不画环:高亮永远长在条目上 */
5409
- [data-scope='cascader'][data-part='column']:focus-visible {
5478
+ /* 焦点落在根列上只有一种情形:指针打开、无选中值、还没有任何条目当锚点。
5479
+ 那一刻它认领着 Tab 位,环画在列内沿;有锚点时环长在条目上,列不再画 */
5480
+ [data-scope='cascader'][data-part='column'][tabindex='0']:focus-visible {
5481
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
5482
+ outline-offset: calc(-1 * var(--xh-ring-width));
5483
+ }
5484
+
5485
+ [data-scope='cascader'][data-part='column']:not([tabindex='0']):focus-visible {
5410
5486
  outline: none;
5411
5487
  }
5412
5488
 
@@ -5414,8 +5490,9 @@
5414
5490
  display: flex;
5415
5491
  align-items: center;
5416
5492
  gap: var(--xh-cascader-row-gap, var(--xh-_cascader-gap));
5417
- padding-block: var(--xh-cascader-row-py, var(--xh-space-1));
5418
- padding-inline: var(--xh-cascader-row-px, var(--xh-control-px-sm));
5493
+ max-inline-size: var(--xh-cascader-row-max-w, 25rem);
5494
+ padding-block: var(--xh-cascader-row-py, var(--xh-space-1_5));
5495
+ padding-inline: var(--xh-cascader-row-px, var(--xh-control-px-md));
5419
5496
  border-radius: var(--xh-cascader-row-radius, var(--xh-shape-control));
5420
5497
  color: var(--xh-cascader-row-fg, var(--xh-fg-default));
5421
5498
  font-size: var(--xh-cascader-row-font-size, var(--xh-_cascader-font-size));
@@ -5424,6 +5501,9 @@
5424
5501
  white-space: nowrap;
5425
5502
  cursor: pointer;
5426
5503
  user-select: none;
5504
+ transition:
5505
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
5506
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
5427
5507
  }
5428
5508
 
5429
5509
  /* 条目常挂不卸载:不属于当前这一列的那些靠 hidden 收起。
@@ -5432,14 +5512,21 @@
5432
5512
  display: none;
5433
5513
  }
5434
5514
 
5435
- /* 指针悬停、键盘高亮与展开路径共用同一张底:三者都是「此刻停在这儿」,
5436
- data-highlighted 跟的是连接层记的焦点条目 */
5437
- [data-scope='cascader'][data-part='item']:not([data-disabled]):is(:hover, [data-highlighted], [data-active]) {
5438
- background: var(--xh-cascader-row-bg-highlight, var(--xh-bg-subtle-hover));
5515
+ /* 底色分两档、靠色相分家:悬停与键盘锚点是中性灰轻档,展开路径是品牌淡底加粗。
5516
+ hover 恒中性灰、不接语气槽——两档同色相的话路径就认不出来了。
5517
+ 悬停展开把路径挪走后留在原地的锚点降回轻档,每列至多一条路径高亮 */
5518
+ [data-scope='cascader'][data-part='item']:not([data-disabled]):is(:hover, [data-highlighted]) {
5519
+ background: var(--xh-cascader-row-bg-hover, var(--xh-bg-subtle));
5439
5520
  }
5440
5521
 
5441
- /* 选中只换文字色与字重,配合行尾的 ✓ 标记;底色整条通道留给悬停,
5442
- 两者因此能同时看见 */
5522
+ /* 必须排在轻档之后:锚点走到路径上时同特指度靠书写顺序压过轻档 */
5523
+ [data-scope='cascader'][data-part='item']:not([data-disabled])[data-active] {
5524
+ background: var(--xh-cascader-row-bg-active, var(--xh-_cascader-active-bg));
5525
+ font-weight: var(--xh-cascader-row-active-font-weight, var(--xh-font-weight-semibold));
5526
+ }
5527
+
5528
+ /* 选中只换文字色与字重,配合行尾的 ✓ 标记;底色强档归展开路径、轻档归悬停,
5529
+ 三者因此能同时看见 */
5443
5530
  [data-scope='cascader'][data-part='item'][data-selected] {
5444
5531
  color: var(--xh-cascader-row-fg-selected, var(--xh-_cascader-selected-fg));
5445
5532
  font-weight: var(--xh-cascader-row-selected-font-weight, var(--xh-font-weight-medium));
@@ -5447,7 +5534,7 @@
5447
5534
 
5448
5535
  /* 条目自己就是 option,环直接画在它身上;往内收,行是撑满列宽的,环画在外沿会被裁掉 */
5449
5536
  [data-scope='cascader'][data-part='item']:focus-visible {
5450
- outline: var(--xh-ring-width) solid var(--xh-_tone, var(--xh-ring-focus));
5537
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
5451
5538
  outline-offset: calc(-1 * var(--xh-ring-width));
5452
5539
  }
5453
5540
 
@@ -5458,6 +5545,28 @@
5458
5545
  cursor: not-allowed;
5459
5546
  }
5460
5547
 
5548
+ /* 分支条目行尾画右向小箭头:不点开也看得出还有下一级。
5549
+ 全边框描好后抹掉块末与行首两边,剩下的直角旋成箭头;位置由 item-text 的 flex:1 顶到行尾 */
5550
+ [data-scope='cascader'][data-part='item'][data-branch]::after {
5551
+ content: '';
5552
+ flex: none;
5553
+ inline-size: var(--xh-cascader-branch-arrow-size, var(--xh-space-1_5));
5554
+ block-size: var(--xh-cascader-branch-arrow-size, var(--xh-space-1_5));
5555
+ border: var(--xh-glyph-stroke-light) solid var(--xh-cascader-branch-arrow-fg, var(--xh-fg-subtle));
5556
+ border-block-end: 0;
5557
+ border-inline-start: 0;
5558
+ rotate: 45deg;
5559
+ }
5560
+
5561
+ /* rtl 下剩下的是块首与行末(物理左上)两边,反向旋 45 度才指向行进方向 */
5562
+ [dir='rtl'] [data-scope='cascader'][data-part='item'][data-branch]::after {
5563
+ rotate: -45deg;
5564
+ }
5565
+
5566
+ [data-scope='cascader'][data-part='item'][data-branch][data-disabled]::after {
5567
+ border-color: var(--xh-fg-disabled);
5568
+ }
5569
+
5461
5570
  [data-scope='cascader'][data-part='item-text'] {
5462
5571
  flex: 1;
5463
5572
  min-inline-size: 0;
@@ -5472,7 +5581,7 @@
5472
5581
  flex: none;
5473
5582
  align-items: center;
5474
5583
  justify-content: center;
5475
- inline-size: var(--xh-cascader-indicator-size, var(--xh-control-indicator-size));
5584
+ inline-size: var(--xh-cascader-item-indicator-size, var(--xh-control-indicator-size));
5476
5585
  color: var(--xh-cascader-item-indicator-fg, var(--xh-_tone, var(--xh-bg-brand)));
5477
5586
  visibility: hidden;
5478
5587
  }
@@ -5481,20 +5590,140 @@
5481
5590
  display: none;
5482
5591
  }
5483
5592
 
5593
+ /* 没写内容就由皮肤画勾;作者往部件里塞了自己的图形,这条规则即不命中 */
5594
+ [data-scope='cascader'][data-part='item-indicator']:empty::before {
5595
+ content: '✓';
5596
+ }
5597
+
5484
5598
  [data-scope='cascader'][data-part='item-indicator'][data-selected] {
5485
5599
  visibility: visible;
5486
5600
  }
5487
5601
 
5602
+ /* 级联勾选的半选态只在皮肤自绘(:empty)时露面画横杠:
5603
+ 作者塞了自己的选中图形时,半选放出来会顶着一个语义相反的勾,表达交还作者 */
5604
+ [data-scope='cascader'][data-part='item-indicator'][data-indeterminate]:empty {
5605
+ visibility: visible;
5606
+ }
5607
+
5608
+ [data-scope='cascader'][data-part='item-indicator'][data-indeterminate]:not([data-selected]):empty::before {
5609
+ content: '−';
5610
+ font-weight: var(--xh-font-weight-semibold);
5611
+ }
5612
+
5488
5613
  [data-scope='cascader'][data-part='item-indicator'][data-disabled] {
5489
5614
  color: var(--xh-fg-disabled);
5490
5615
  }
5491
5616
 
5617
+ /* —— 搜索视图:输入框横占整行排在列上方,候选列表替换列视图 —— */
5618
+ [data-scope='cascader'][data-part='content']:has([data-part='input']:not([hidden])) {
5619
+ flex-wrap: wrap;
5620
+ }
5621
+
5622
+ [data-scope='cascader'][data-part='input'] {
5623
+ flex-basis: 100%;
5624
+ min-inline-size: 0;
5625
+ padding-block: var(--xh-cascader-input-py, var(--xh-space-2));
5626
+ padding-inline: var(--xh-cascader-input-px, var(--xh-control-px-md));
5627
+ border: none;
5628
+ border-block-end: var(--xh-stroke-thin) solid var(--xh-cascader-content-border, var(--xh-border-default));
5629
+ border-start-start-radius: inherit;
5630
+ border-start-end-radius: inherit;
5631
+ background: none;
5632
+ color: inherit;
5633
+ font-family: inherit;
5634
+ font-size: var(--xh-cascader-input-font-size, var(--xh-_cascader-font-size));
5635
+ line-height: var(--xh-leading-normal);
5636
+ }
5637
+
5638
+ [data-scope='cascader'][data-part='input']:focus-visible {
5639
+ outline: none;
5640
+ }
5641
+
5642
+ [data-scope='cascader'][data-part='input'][hidden] {
5643
+ display: none;
5644
+ }
5645
+
5646
+ [data-scope='cascader'][data-part='search-list'] {
5647
+ display: flex;
5648
+ flex-basis: 100%;
5649
+ flex-direction: column;
5650
+ max-block-size: var(--xh-cascader-search-max-h, 11.25rem);
5651
+ padding: var(--xh-cascader-search-p, var(--xh-space-1));
5652
+ overflow-y: auto;
5653
+ }
5654
+
5655
+ [data-scope='cascader'][data-part='search-list'][hidden] {
5656
+ display: none;
5657
+ }
5658
+
5659
+ /* 无候选时列表自身不占位,空态交给 empty 部件出面 */
5660
+ [data-scope='cascader'][data-part='search-list'][data-empty] {
5661
+ padding: 0;
5662
+ }
5663
+
5664
+ /* 搜索视图里列整体让位 */
5665
+ [data-scope='cascader'][data-part='content'][data-searching] [data-part='column'] {
5666
+ display: none;
5667
+ }
5668
+
5669
+ /* 候选与列内条目同一套行度量,虚拟高亮与悬停同用轻档 */
5670
+ [data-scope='cascader'][data-part='search-item'] {
5671
+ padding-block: var(--xh-cascader-row-py, var(--xh-space-1_5));
5672
+ padding-inline: var(--xh-cascader-row-px, var(--xh-control-px-md));
5673
+ overflow: hidden;
5674
+ border-radius: var(--xh-cascader-row-radius, var(--xh-shape-control));
5675
+ color: var(--xh-cascader-row-fg, var(--xh-fg-default));
5676
+ font-size: var(--xh-cascader-row-font-size, var(--xh-_cascader-font-size));
5677
+ line-height: var(--xh-cascader-row-leading, var(--xh-leading-normal));
5678
+ text-overflow: ellipsis;
5679
+ white-space: nowrap;
5680
+ cursor: pointer;
5681
+ user-select: none;
5682
+ transition: background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
5683
+ }
5684
+
5685
+ [data-scope='cascader'][data-part='search-item']:not([data-disabled]):is(:hover, [data-highlighted]) {
5686
+ background: var(--xh-cascader-row-bg-hover, var(--xh-bg-subtle));
5687
+ }
5688
+
5689
+ [data-scope='cascader'][data-part='search-item'][data-disabled] {
5690
+ color: var(--xh-fg-disabled);
5691
+ cursor: not-allowed;
5692
+ }
5693
+
5694
+ [data-scope='cascader'][data-part='search-item'][hidden] {
5695
+ display: none;
5696
+ }
5697
+
5698
+ /* —— 空态:当前视图没有条目时由 empty 部件出面,文案由适配器或作者供给,皮肤只管排版 —— */
5699
+ /* 根列没有条目时那条空列让位,占位面接管整个浮层 */
5700
+ [data-scope='cascader'][data-part='content'][data-empty] [data-part='column'] {
5701
+ display: none;
5702
+ }
5703
+
5704
+ [data-scope='cascader'][data-part='empty'] {
5705
+ display: flex;
5706
+ flex: 1 1 100%;
5707
+ align-items: center;
5708
+ justify-content: center;
5709
+ min-inline-size: var(--xh-cascader-column-min-w, 7rem);
5710
+ min-block-size: var(--xh-cascader-empty-min-h, 5rem);
5711
+ padding: var(--xh-cascader-empty-p, var(--xh-space-3));
5712
+ color: var(--xh-cascader-empty-fg, var(--xh-fg-subtle));
5713
+ font-size: var(--xh-cascader-row-font-size, var(--xh-_cascader-font-size));
5714
+ }
5715
+
5716
+ [data-scope='cascader'][data-part='empty'][hidden] {
5717
+ display: none;
5718
+ }
5719
+
5492
5720
  /* 浮层是横向长出来的:淡入之外再沿书写方向起始缘推一小段,观感上像列被「拉开」。
5721
+ 位移量从 content 上的私有槽取,rtl 侧翻符号后同一份关键帧两个方向都对。
5493
5722
  关键帧写在本文件里、名字带组件前缀,不动公共的那一份 */
5494
5723
  @keyframes xh-cascader-in {
5495
5724
  from {
5496
5725
  opacity: 0;
5497
- translate: -0.25rem 0;
5726
+ translate: var(--xh-_cascader-slide, -0.25rem) 0;
5498
5727
  }
5499
5728
 
5500
5729
  to {
@@ -5523,7 +5752,7 @@
5523
5752
  inline-size: var(--xh-_checkbox-box);
5524
5753
  block-size: var(--xh-_checkbox-box);
5525
5754
  padding: 0;
5526
- border: 1px solid var(--xh-checkbox-border, var(--xh-border-default));
5755
+ border: 1px solid var(--xh-checkbox-border, var(--xh-border-control));
5527
5756
  border-radius: var(--xh-radius-sm);
5528
5757
  background: var(--xh-checkbox-bg, var(--xh-bg-canvas));
5529
5758
  color: var(--xh-checkbox-fg, var(--xh-_checkbox-on-accent));
@@ -5666,19 +5895,19 @@
5666
5895
  /* 全选格与条目格共用同一套盒子,并成一条规则写:分开写迟早只改到一半,两个方框就此走形。
5667
5896
  尺寸与选中态照 checkbox 的 root 搬,同一份视觉语言在单体与集合里必须一致。
5668
5897
  trigger 那格没有对应的角色节点(anatomy 到 trigger 为止),只能由伪元素补出来 */
5669
- [data-scope='checkbox-group'][data-part='item-control'],
5898
+ [data-scope='checkbox-group'][data-part='indicator'],
5670
5899
  [data-scope='checkbox-group'][data-part='trigger']::before {
5671
5900
  display: inline-flex;
5672
5901
  flex: none;
5673
5902
  align-items: center;
5674
5903
  justify-content: center;
5675
- inline-size: var(--xh-checkbox-group-control-size, 16px);
5676
- block-size: var(--xh-checkbox-group-control-size, 16px);
5677
- border: var(--xh-stroke-thin) solid var(--xh-checkbox-group-control-border, var(--xh-border-default));
5678
- border-radius: var(--xh-checkbox-group-control-radius, var(--xh-radius-sm));
5679
- background: var(--xh-checkbox-group-control-bg, var(--xh-bg-canvas));
5680
- color: var(--xh-checkbox-group-control-fg, var(--xh-fg-on-brand));
5681
- font-size: var(--xh-checkbox-group-control-font-size, 12px);
5904
+ inline-size: var(--xh-checkbox-group-indicator-size, 16px);
5905
+ block-size: var(--xh-checkbox-group-indicator-size, 16px);
5906
+ border: var(--xh-stroke-thin) solid var(--xh-checkbox-group-indicator-border, var(--xh-border-control));
5907
+ border-radius: var(--xh-checkbox-group-indicator-radius, var(--xh-radius-sm));
5908
+ background: var(--xh-checkbox-group-indicator-bg, var(--xh-bg-canvas));
5909
+ color: var(--xh-checkbox-group-indicator-fg, var(--xh-fg-on-brand));
5910
+ font-size: var(--xh-checkbox-group-indicator-font-size, 12px);
5682
5911
  line-height: var(--xh-leading-none);
5683
5912
  transition:
5684
5913
  background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
@@ -5686,7 +5915,7 @@
5686
5915
  }
5687
5916
 
5688
5917
  /* content 只能给伪元素,绝不能并进上面那条:content 落在普通元素上会顶掉它的全部子节点,
5689
- item-control 里作者写的图标就此消失 */
5918
+ indicator 里作者写的图标就此消失 */
5690
5919
  [data-scope='checkbox-group'][data-part='trigger']::before {
5691
5920
  content: '';
5692
5921
  }
@@ -5694,19 +5923,19 @@
5694
5923
  /* 悬停只加深空方框的描边。已选中的那一格必须排除:这条选择器的权重高过下面那条填色规则,
5695
5924
  不排除就会在悬停时把 brand 描边换回灰色,方框看起来"缺了一圈" */
5696
5925
  [data-scope='checkbox-group'][data-part='item']:not([data-disabled]):hover
5697
- [data-scope='checkbox-group'][data-part='item-control']:not([data-state='checked']) {
5698
- border-color: var(--xh-checkbox-group-control-border-hover, var(--xh-border-strong));
5926
+ [data-scope='checkbox-group'][data-part='indicator']:not([data-state='checked']) {
5927
+ border-color: var(--xh-checkbox-group-indicator-border-hover, var(--xh-border-control-hover));
5699
5928
  }
5700
5929
 
5701
5930
  /* 选中即填色:条目是二态,全选格三态里 all 与 some 都算"有值",共用同一副填色 */
5702
- [data-scope='checkbox-group'][data-part='item-control'][data-state='checked'],
5931
+ [data-scope='checkbox-group'][data-part='indicator'][data-state='checked'],
5703
5932
  [data-scope='checkbox-group'][data-part='trigger']:is([data-state='all'], [data-state='some'])::before {
5704
- border-color: var(--xh-checkbox-group-control-border-checked, var(--xh-bg-brand));
5705
- background: var(--xh-checkbox-group-control-bg-checked, var(--xh-bg-brand));
5933
+ border-color: var(--xh-checkbox-group-indicator-border-checked, var(--xh-bg-brand));
5934
+ background: var(--xh-checkbox-group-indicator-bg-checked, var(--xh-bg-brand));
5706
5935
  }
5707
5936
 
5708
- /* 勾号由皮肤补,作者不必自己往 item-control 里塞字符 */
5709
- [data-scope='checkbox-group'][data-part='item-control'][data-state='checked']::before {
5937
+ /* 勾号由皮肤补,作者不必自己往 indicator 里塞字符 */
5938
+ [data-scope='checkbox-group'][data-part='indicator'][data-state='checked']::before {
5710
5939
  content: '✓';
5711
5940
  }
5712
5941
 
@@ -5722,8 +5951,8 @@
5722
5951
 
5723
5952
  /* 校验失败标在每个方框上:root 上的 data-invalid 是唯一出处,条目自己不带这个属性 */
5724
5953
  [data-scope='checkbox-group'][data-part='root'][data-invalid]
5725
- [data-scope='checkbox-group'][data-part='item-control'] {
5726
- border-color: var(--xh-checkbox-group-control-border-invalid, var(--xh-color-danger-500));
5954
+ [data-scope='checkbox-group'][data-part='indicator'] {
5955
+ border-color: var(--xh-checkbox-group-indicator-border-invalid, var(--xh-color-danger-500));
5727
5956
  }
5728
5957
 
5729
5958
  [data-scope='checkbox-group'][data-part='item-text'] {
@@ -5766,7 +5995,7 @@
5766
5995
  /* 表单出口:连接层已经把整套视觉隐藏写进内联 style(absolute + 1px + clip-path),
5767
5996
  这里绝不能再补 display:none——那份输入必须留在布局里,原生校验提示要有处可贴。
5768
5997
  只补一条不吃指针,免得它挡住条目的点击 */
5769
- [data-scope='checkbox-group'][data-part='item-hidden-input'] {
5998
+ [data-scope='checkbox-group'][data-part='hidden-input'] {
5770
5999
  pointer-events: none;
5771
6000
  }
5772
6001
 
@@ -5774,7 +6003,7 @@
5774
6003
  不补的话作者给节点加 hidden 后它照样显示 */
5775
6004
  [data-scope='checkbox-group'][data-part='root'][hidden],
5776
6005
  [data-scope='checkbox-group'][data-part='item'][hidden],
5777
- [data-scope='checkbox-group'][data-part='item-control'][hidden],
6006
+ [data-scope='checkbox-group'][data-part='indicator'][hidden],
5778
6007
  [data-scope='checkbox-group'][data-part='trigger'][hidden] {
5779
6008
  display: none;
5780
6009
  }
@@ -5819,7 +6048,7 @@
5819
6048
  block-size: var(--xh-clipboard-input-h, var(--xh-control-h-md));
5820
6049
  padding-inline: var(--xh-clipboard-input-px, var(--xh-control-px-md));
5821
6050
  padding-block: 0;
5822
- border: var(--xh-stroke-thin) solid var(--xh-clipboard-input-border, var(--xh-border-default));
6051
+ border: var(--xh-stroke-thin) solid var(--xh-clipboard-input-border, var(--xh-border-control));
5823
6052
  border-radius: var(--xh-clipboard-input-radius, var(--xh-shape-control));
5824
6053
  /* 只读态压低底色表示"改不动",但仍可聚焦、可选中复制 */
5825
6054
  background: var(--xh-clipboard-input-bg, var(--xh-bg-subtle));
@@ -5847,7 +6076,7 @@
5847
6076
  block-size: var(--xh-clipboard-trigger-h, var(--xh-control-h-md));
5848
6077
  padding-inline: var(--xh-clipboard-trigger-px, var(--xh-control-px-md));
5849
6078
  padding-block: 0;
5850
- border: var(--xh-stroke-thin) solid var(--xh-clipboard-trigger-border, var(--xh-border-default));
6079
+ border: var(--xh-stroke-thin) solid var(--xh-clipboard-trigger-border, var(--xh-border-control));
5851
6080
  border-radius: var(--xh-clipboard-trigger-radius, var(--xh-shape-control));
5852
6081
  background: var(--xh-clipboard-trigger-bg, var(--xh-bg-canvas));
5853
6082
  color: var(--xh-clipboard-trigger-fg, var(--xh-fg-default));
@@ -5870,7 +6099,7 @@
5870
6099
  }
5871
6100
 
5872
6101
  [data-scope='clipboard'][data-part='trigger']:hover {
5873
- border-color: var(--xh-clipboard-trigger-border-hover, var(--xh-border-strong));
6102
+ border-color: var(--xh-clipboard-trigger-border-hover, var(--xh-border-control-hover));
5874
6103
  background: var(--xh-clipboard-trigger-bg-hover, var(--xh-bg-subtle-hover));
5875
6104
  }
5876
6105
 
@@ -6099,7 +6328,7 @@
6099
6328
  min-inline-size: var(--xh-color-picker-trigger-min-w, 9rem);
6100
6329
  block-size: var(--xh-color-picker-trigger-h, var(--xh-control-h-md));
6101
6330
  padding-inline: var(--xh-color-picker-trigger-px, var(--xh-control-px-md));
6102
- border: var(--xh-stroke-thin) solid var(--xh-color-picker-trigger-border, var(--xh-border-default));
6331
+ border: var(--xh-stroke-thin) solid var(--xh-color-picker-trigger-border, var(--xh-border-control));
6103
6332
  border-radius: var(--xh-color-picker-trigger-radius, var(--xh-shape-control));
6104
6333
  background: var(--xh-color-picker-trigger-bg, var(--xh-bg-canvas));
6105
6334
  color: var(--xh-color-picker-trigger-fg, var(--xh-fg-default));
@@ -6111,7 +6340,7 @@
6111
6340
  }
6112
6341
 
6113
6342
  [data-scope='color-picker'][data-part='trigger']:hover:not(:disabled) {
6114
- border-color: var(--xh-color-picker-trigger-border-hover, var(--xh-border-strong));
6343
+ border-color: var(--xh-color-picker-trigger-border-hover, var(--xh-border-control-hover));
6115
6344
  }
6116
6345
 
6117
6346
  [data-scope='color-picker'][data-part='trigger']:focus-visible {
@@ -6357,7 +6586,7 @@
6357
6586
  min-inline-size: 0;
6358
6587
  block-size: var(--xh-color-picker-input-h, var(--xh-control-h-sm));
6359
6588
  padding-inline: var(--xh-color-picker-input-px, var(--xh-control-px-sm));
6360
- border: var(--xh-stroke-thin) solid var(--xh-color-picker-input-border, var(--xh-border-default));
6589
+ border: var(--xh-stroke-thin) solid var(--xh-color-picker-input-border, var(--xh-border-control));
6361
6590
  border-radius: var(--xh-color-picker-input-radius, var(--xh-shape-control));
6362
6591
  background: var(--xh-color-picker-input-bg, var(--xh-bg-canvas));
6363
6592
  color: inherit;
@@ -6397,7 +6626,7 @@
6397
6626
  inline-size: var(--xh-color-picker-action-size, 1.75rem);
6398
6627
  block-size: var(--xh-color-picker-action-size, 1.75rem);
6399
6628
  padding: 0;
6400
- border: var(--xh-stroke-thin) solid var(--xh-color-picker-action-border, var(--xh-border-default));
6629
+ border: var(--xh-stroke-thin) solid var(--xh-color-picker-action-border, var(--xh-border-control));
6401
6630
  border-radius: var(--xh-color-picker-action-radius, var(--xh-shape-control));
6402
6631
  background: var(--xh-color-picker-action-bg, transparent);
6403
6632
  color: var(--xh-color-picker-action-fg, var(--xh-fg-muted));
@@ -6441,7 +6670,7 @@
6441
6670
  inline-size: var(--xh-color-picker-swatch-item-size, 1.25rem);
6442
6671
  block-size: var(--xh-color-picker-swatch-item-size, 1.25rem);
6443
6672
  padding: 0;
6444
- border: var(--xh-stroke-thin) solid var(--xh-color-picker-swatch-border, var(--xh-border-default));
6673
+ border: var(--xh-stroke-thin) solid var(--xh-color-picker-swatch-border, var(--xh-border-control));
6445
6674
  border-radius: var(--xh-color-picker-swatch-radius, var(--xh-radius-sm));
6446
6675
  cursor: pointer;
6447
6676
  }
@@ -6478,6 +6707,22 @@
6478
6707
  display: none;
6479
6708
  }
6480
6709
 
6710
+ /* 连接层把它写成 type=hidden,UA 自己就不渲染;这里的视觉隐藏是给元素升级前那一瞬兜底
6711
+ (作者手写的是一个普通 input,升级完成前它是可见的文本框)。
6712
+ 刻意不写 display:写了反而会盖掉 UA 对 type=hidden 的收起,把它显出来 */
6713
+ [data-scope='color-picker'][data-part='hidden-input'] {
6714
+ position: absolute;
6715
+ inline-size: 1px;
6716
+ block-size: 1px;
6717
+ padding: 0;
6718
+ margin: -1px;
6719
+ border: 0;
6720
+ overflow: hidden;
6721
+ clip-path: inset(50%);
6722
+ white-space: nowrap;
6723
+ pointer-events: none;
6724
+ }
6725
+
6481
6726
  /* styles/combobox.css */
6482
6727
  [data-scope='combobox'][data-part='root'] {
6483
6728
  /* 尺寸档只换这五个私有槽,输入行、标题与候选都从 root 继承它们,
@@ -6490,14 +6735,13 @@
6490
6735
 
6491
6736
  /* 形态档只换这三个槽:缺省即 outline(描边 + 画布底) */
6492
6737
  --xh-_combobox-control-bg: var(--xh-bg-canvas);
6493
- --xh-_combobox-control-border: var(--xh-border-default);
6494
- --xh-_combobox-control-border-hover: var(--xh-border-strong);
6738
+ --xh-_combobox-control-border: var(--xh-border-control);
6739
+ --xh-_combobox-control-border-hover: var(--xh-border-control-hover);
6495
6740
 
6496
- /* 强调色随语气走,只落在聚焦与选中上,正文文字色不动。
6497
- 没写 data-tone 时 --xh-_tone 没被声明,三处都退回原来的取值 */
6741
+ /* 聚焦环恒用固定环色;强调色随语气走,只落在勾选标记与选中文字上,正文文字色不动。
6742
+ 没写 data-tone 时 --xh-_tone 没被声明,两处都退回原来的取值 */
6498
6743
  --xh-_combobox-ring: var(--xh-ring-focus);
6499
6744
  --xh-_combobox-accent: var(--xh-_tone, var(--xh-bg-brand));
6500
- --xh-_combobox-item-highlight-bg: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover));
6501
6745
  --xh-_combobox-item-selected-fg: var(--xh-_tone-fg, var(--xh-fg-brand-strong));
6502
6746
 
6503
6747
  display: inline-flex;
@@ -6533,21 +6777,21 @@
6533
6777
  形态只决定描边与底色怎么用,用哪族颜色由 data-tone 决定 */
6534
6778
  [data-scope='combobox'][data-part='root'][data-variant='outline'] {
6535
6779
  --xh-_combobox-control-bg: var(--xh-bg-canvas);
6536
- --xh-_combobox-control-border: var(--xh-border-default);
6537
- --xh-_combobox-control-border-hover: var(--xh-border-strong);
6780
+ --xh-_combobox-control-border: var(--xh-border-control);
6781
+ --xh-_combobox-control-border-hover: var(--xh-border-control-hover);
6538
6782
  }
6539
6783
 
6540
6784
  /* 填充式:底色顶替描边,悬停时才浮出一道淡边 */
6541
6785
  [data-scope='combobox'][data-part='root'][data-variant='subtle'] {
6542
6786
  --xh-_combobox-control-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
6543
6787
  --xh-_combobox-control-border: transparent;
6544
- --xh-_combobox-control-border-hover: var(--xh-_tone-border, var(--xh-border-default));
6788
+ --xh-_combobox-control-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
6545
6789
  }
6546
6790
 
6547
6791
  [data-scope='combobox'][data-part='root'][data-variant='ghost'] {
6548
6792
  --xh-_combobox-control-bg: transparent;
6549
6793
  --xh-_combobox-control-border: transparent;
6550
- --xh-_combobox-control-border-hover: var(--xh-_tone-border, var(--xh-border-default));
6794
+ --xh-_combobox-control-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
6551
6795
  }
6552
6796
 
6553
6797
  [data-scope='combobox'][data-part='label'] {
@@ -6685,11 +6929,11 @@
6685
6929
  display: none;
6686
6930
  }
6687
6931
 
6688
- /* 展开时箭头翻个面,指向与列表的去向一致;transition 要重写全份,只补一条会覆盖掉上面那条 */
6689
- [data-scope='combobox'][data-part='trigger'] {
6690
- transition:
6691
- background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
6692
- rotate var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
6932
+ /* 展开时箭头翻个面,指向与列表的去向一致。转的是字形或作者塞的图形,不转按钮本体:
6933
+ 按钮自带悬停底色,整个按钮转会带着底色一起转 */
6934
+ [data-scope='combobox'][data-part='trigger']:empty::before,
6935
+ [data-scope='combobox'][data-part='trigger'] > * {
6936
+ transition: rotate var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
6693
6937
  }
6694
6938
 
6695
6939
  /* 没写内容就由皮肤画箭头;作者往部件里塞了自己的图形,这条规则即不命中 */
@@ -6702,7 +6946,8 @@
6702
6946
  content: '✕';
6703
6947
  }
6704
6948
 
6705
- [data-scope='combobox'][data-part='trigger'][data-state='open'] {
6949
+ [data-scope='combobox'][data-part='trigger'][data-state='open']:empty::before,
6950
+ [data-scope='combobox'][data-part='trigger'][data-state='open'] > * {
6706
6951
  rotate: 180deg;
6707
6952
  }
6708
6953
 
@@ -6795,7 +7040,7 @@
6795
7040
  [data-scope='combobox'][data-part='item-group-label'] {
6796
7041
  display: block;
6797
7042
  padding-block: var(--xh-combobox-group-label-py, var(--xh-space-1));
6798
- padding-inline: var(--xh-combobox-group-label-px, var(--xh-control-px-sm));
7043
+ padding-inline: var(--xh-combobox-group-label-px, var(--xh-control-px-md));
6799
7044
  color: var(--xh-combobox-group-label-fg, var(--xh-fg-subtle));
6800
7045
  font-size: var(--xh-combobox-group-label-font-size, var(--xh-font-size-xs));
6801
7046
  font-weight: var(--xh-combobox-group-label-font-weight, var(--xh-font-weight-medium));
@@ -6811,8 +7056,8 @@
6811
7056
  display: flex;
6812
7057
  align-items: center;
6813
7058
  gap: var(--xh-combobox-item-gap, var(--xh-_combobox-gap));
6814
- padding-block: var(--xh-combobox-item-py, var(--xh-space-1));
6815
- padding-inline: var(--xh-combobox-item-px, var(--xh-control-px-sm));
7059
+ padding-block: var(--xh-combobox-item-py, var(--xh-space-1_5));
7060
+ padding-inline: var(--xh-combobox-item-px, var(--xh-control-px-md));
6816
7061
  border-radius: var(--xh-combobox-item-radius, var(--xh-shape-control));
6817
7062
  color: var(--xh-combobox-item-fg, var(--xh-fg-default));
6818
7063
  font-size: var(--xh-combobox-item-font-size, var(--xh-_combobox-font-size));
@@ -6821,6 +7066,9 @@
6821
7066
  white-space: nowrap;
6822
7067
  cursor: pointer;
6823
7068
  user-select: none;
7069
+ transition:
7070
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
7071
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
6824
7072
  }
6825
7073
 
6826
7074
  /* 候选按需收起(作者自己藏掉某几条)时要让位,有 display 就得补这条 */
@@ -6828,10 +7076,10 @@
6828
7076
  display: none;
6829
7077
  }
6830
7078
 
6831
- /* 只认 data-highlighted,不写 :hover:指针划过时连接层已经把高亮搬过来了,
7079
+ /* 键盘锚点走中性灰轻档。只认 data-highlighted,不写 :hover:指针划过时连接层已经把高亮搬过来了,
6832
7080
  两套底色各画各的会出现"鼠标停在 A 上、回车提交的却是 B" */
6833
7081
  [data-scope='combobox'][data-part='item'][data-highlighted]:not([data-disabled]) {
6834
- background: var(--xh-combobox-item-bg-highlight, var(--xh-_combobox-item-highlight-bg));
7082
+ background: var(--xh-combobox-item-bg-hover, var(--xh-bg-subtle));
6835
7083
  }
6836
7084
 
6837
7085
  /* 选中只换文字色与字重,配合行尾的 ✓ 标记;底色整条通道留给高亮,
@@ -6921,7 +7169,7 @@
6921
7169
  align-items: flex-end;
6922
7170
  gap: var(--xh-composer-gap, var(--xh-control-gap-md));
6923
7171
  padding: var(--xh-composer-p, var(--xh-space-2));
6924
- border: var(--xh-stroke-thin) solid var(--xh-composer-border, var(--xh-border-default));
7172
+ border: var(--xh-stroke-thin) solid var(--xh-composer-border, var(--xh-border-control));
6925
7173
  border-radius: var(--xh-composer-radius, var(--xh-shape-surface));
6926
7174
  background: var(--xh-composer-bg, var(--xh-bg-surface));
6927
7175
  }
@@ -7044,13 +7292,16 @@
7044
7292
  /* 尺寸档只换这四个私有槽,条目与分组标题都从 root 继承它们,
7045
7293
  换档不必给每个部件各写一条选择器。浮层在 positioner 里、也是 root 的后代,一并继承
7046
7294
  (display:contents 不影响继承:自定义属性沿 DOM 树传) */
7047
- --xh-_context-menu-item-py: var(--xh-space-2);
7048
- --xh-_context-menu-item-px: var(--xh-control-px-sm);
7295
+ --xh-_context-menu-item-py: var(--xh-space-1_5);
7296
+ --xh-_context-menu-item-px: var(--xh-control-px-md);
7049
7297
  --xh-_context-menu-item-gap: var(--xh-control-gap-md);
7050
7298
  --xh-_context-menu-font-size: var(--xh-text-body-size);
7051
7299
 
7052
- /* 条目高亮底与标记位强调色随语气走。没写 data-tone 时这两个语气槽没被声明,各自退回原来的取值 */
7053
- --xh-_context-menu-item-highlight-bg: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover));
7300
+ /* 打开子菜单的触发条目走品牌淡底强档,随语气换色相;没写 data-tone 时退回品牌淡底。
7301
+ 悬停与键盘焦点恒中性灰轻档、不接语气槽——两档同色相的话打开路径就认不出来了 */
7302
+ --xh-_context-menu-item-active-bg: var(--xh-_tone-subtle, var(--xh-bg-brand-subtle));
7303
+
7304
+ /* 标记位强调色随语气走,没写 data-tone 时退回原来的取值 */
7054
7305
  --xh-_context-menu-accent: var(--xh-_tone, var(--xh-fg-subtle));
7055
7306
 
7056
7307
  display: contents;
@@ -7061,18 +7312,18 @@
7061
7312
  }
7062
7313
 
7063
7314
  /* —— 尺寸 —— */
7064
- /* 缺省档写在上面的基础规则里。条目内外边距缺省档本就不在 control-h/px 那把尺上
7065
- (高度由 padding 撑出来、行内内缩取的是 sm 档),故按 space 档上下各挪一级 */
7315
+ /* 缺省档写在上面的基础规则里。条目高度由 padding 撑出来:
7316
+ 纵向走 space 档、行内内缩走 control-px 档(随密度轴),换档各挪一级 */
7066
7317
  [data-scope='context-menu'][data-part='root'][data-size='sm'] {
7067
7318
  --xh-_context-menu-item-py: var(--xh-space-1);
7068
- --xh-_context-menu-item-px: var(--xh-space-1);
7319
+ --xh-_context-menu-item-px: var(--xh-control-px-sm);
7069
7320
  --xh-_context-menu-item-gap: var(--xh-control-gap-sm);
7070
7321
  --xh-_context-menu-font-size: var(--xh-font-size-sm);
7071
7322
  }
7072
7323
 
7073
7324
  [data-scope='context-menu'][data-part='root'][data-size='lg'] {
7074
- --xh-_context-menu-item-py: var(--xh-space-3);
7075
- --xh-_context-menu-item-px: var(--xh-space-3);
7325
+ --xh-_context-menu-item-py: var(--xh-space-2);
7326
+ --xh-_context-menu-item-px: var(--xh-control-px-lg);
7076
7327
  --xh-_context-menu-item-gap: var(--xh-control-gap-lg);
7077
7328
  --xh-_context-menu-font-size: var(--xh-font-size-lg);
7078
7329
  }
@@ -7122,8 +7373,14 @@
7122
7373
  display: none;
7123
7374
  }
7124
7375
 
7125
- /* 容器只是焦点域兜底落点,落焦不画环:高亮永远长在条目上 */
7126
- [data-scope='context-menu'][data-part='content']:focus-visible {
7376
+ /* 焦点落在容器上只有一种情形:指针打开、还没有任何条目当锚点。那一刻它认领着 Tab 位,
7377
+ 环画在浮层内沿,用户才看得出键盘此刻在哪儿;有锚点时环长在条目上,容器不再画 */
7378
+ [data-scope='context-menu'][data-part='content'][tabindex='0']:focus-visible {
7379
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
7380
+ outline-offset: calc(-1 * var(--xh-ring-width));
7381
+ }
7382
+
7383
+ [data-scope='context-menu'][data-part='content']:not([tabindex='0']):focus-visible {
7127
7384
  outline: none;
7128
7385
  }
7129
7386
 
@@ -7175,10 +7432,13 @@
7175
7432
  border-radius: var(--xh-context-menu-item-radius, var(--xh-shape-control));
7176
7433
  color: var(--xh-context-menu-item-fg, var(--xh-fg-default));
7177
7434
  font-size: var(--xh-context-menu-item-font-size, var(--xh-_context-menu-font-size));
7178
- line-height: var(--xh-leading-none);
7435
+ line-height: var(--xh-context-menu-item-leading, var(--xh-leading-normal));
7179
7436
  white-space: nowrap;
7180
7437
  cursor: pointer;
7181
7438
  user-select: none;
7439
+ transition:
7440
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
7441
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
7182
7442
  }
7183
7443
 
7184
7444
  /* 条目可以按需收起(按上下文过滤掉的命令),有 display 就得补这条 */
@@ -7186,14 +7446,28 @@
7186
7446
  display: none;
7187
7447
  }
7188
7448
 
7189
- /* 键盘高亮与指针高亮共用一套底色:条目本身承载焦点,不另画描边。
7449
+ /* 悬停与键盘焦点共用中性灰轻档:条目本身承载焦点,不另画描边。
7190
7450
  data-highlighted 跟的是连接层记的焦点条目,与 :focus 说的是同一件事,
7191
7451
  子部件够不着那个伪类,只能读这个标记 */
7192
7452
  [data-scope='context-menu'][data-part='item']:not([data-disabled]):is(:hover, :focus, [data-highlighted]) {
7193
- background: var(--xh-context-menu-item-bg-highlight, var(--xh-_context-menu-item-highlight-bg));
7453
+ background: var(--xh-context-menu-item-bg-hover, var(--xh-bg-subtle));
7454
+ }
7455
+
7456
+ /* 打开子菜单的触发条目是展开路径,走品牌淡底加粗强档。
7457
+ 必须排在轻档之后:悬停或焦点回到它身上时同特指度靠书写顺序压过轻档 */
7458
+ [data-scope='context-menu'][data-part='item']:not([data-disabled])[data-state='open'] {
7459
+ background: var(--xh-context-menu-item-bg-active, var(--xh-_context-menu-item-active-bg));
7460
+ font-weight: var(--xh-context-menu-item-active-font-weight, var(--xh-font-weight-semibold));
7461
+ }
7462
+
7463
+ /* 键盘走到条目上要画环:轻档底色只有 1.1:1,够不上焦点指示器的对比度要求。
7464
+ 指针悬停走的是程序化聚焦,鼠标模态下 :focus-visible 不匹配,不会误画 */
7465
+ [data-scope='context-menu'][data-part='item']:focus-visible {
7466
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
7467
+ outline-offset: calc(-1 * var(--xh-ring-width));
7194
7468
  }
7195
7469
 
7196
- [data-scope='context-menu'][data-part='item']:focus {
7470
+ [data-scope='context-menu'][data-part='item']:focus:not(:focus-visible) {
7197
7471
  outline: none;
7198
7472
  }
7199
7473
 
@@ -7244,11 +7518,15 @@
7244
7518
  }
7245
7519
 
7246
7520
  /* 箭头是旋转 45° 的方块,只保留朝向锚点那两条边的描边;
7247
- 引擎不产出箭头偏移,因此沿交叉轴固定居中 */
7521
+ 交叉轴落点由定位引擎经内联自定义属性写进来,没给时退回居中 */
7248
7522
  [data-scope='context-menu'][data-part='arrow'] {
7523
+ --xh-_context-menu-arrow-size: var(--xh-context-menu-arrow-size, var(--xh-overlay-arrow-size));
7524
+ --xh-_context-menu-arrow-x: 50%;
7525
+ --xh-_context-menu-arrow-y: 50%;
7526
+
7249
7527
  position: absolute;
7250
- inline-size: var(--xh-context-menu-arrow-size, var(--xh-overlay-arrow-size));
7251
- block-size: var(--xh-context-menu-arrow-size, var(--xh-overlay-arrow-size));
7528
+ inline-size: var(--xh-_context-menu-arrow-size);
7529
+ block-size: var(--xh-_context-menu-arrow-size);
7252
7530
  background: var(--xh-context-menu-bg, var(--xh-bg-surface));
7253
7531
  border: var(--xh-stroke-thin) solid var(--xh-context-menu-border, var(--xh-border-default));
7254
7532
  rotate: 45deg;
@@ -7259,29 +7537,29 @@
7259
7537
  }
7260
7538
 
7261
7539
  [data-scope='context-menu'][data-part='arrow'][data-placement^='bottom'] {
7262
- inset-block-start: calc(var(--xh-context-menu-arrow-size, var(--xh-overlay-arrow-size)) / -2);
7263
- inset-inline-start: calc(50% - var(--xh-context-menu-arrow-size, var(--xh-overlay-arrow-size)) / 2);
7540
+ inset-block-start: calc(var(--xh-_context-menu-arrow-size) / -2);
7541
+ inset-inline-start: calc(var(--xh-_context-menu-arrow-x) - var(--xh-_context-menu-arrow-size) / 2);
7264
7542
  border-block-end: 0;
7265
7543
  border-inline-end: 0;
7266
7544
  }
7267
7545
 
7268
7546
  [data-scope='context-menu'][data-part='arrow'][data-placement^='top'] {
7269
- inset-block-end: calc(var(--xh-context-menu-arrow-size, var(--xh-overlay-arrow-size)) / -2);
7270
- inset-inline-start: calc(50% - var(--xh-context-menu-arrow-size, var(--xh-overlay-arrow-size)) / 2);
7547
+ inset-block-end: calc(var(--xh-_context-menu-arrow-size) / -2);
7548
+ inset-inline-start: calc(var(--xh-_context-menu-arrow-x) - var(--xh-_context-menu-arrow-size) / 2);
7271
7549
  border-block-start: 0;
7272
7550
  border-inline-start: 0;
7273
7551
  }
7274
7552
 
7275
7553
  [data-scope='context-menu'][data-part='arrow'][data-placement^='right'] {
7276
- inset-inline-start: calc(var(--xh-context-menu-arrow-size, var(--xh-overlay-arrow-size)) / -2);
7277
- inset-block-start: calc(50% - var(--xh-context-menu-arrow-size, var(--xh-overlay-arrow-size)) / 2);
7554
+ inset-inline-start: calc(var(--xh-_context-menu-arrow-size) / -2);
7555
+ inset-block-start: calc(var(--xh-_context-menu-arrow-y) - var(--xh-_context-menu-arrow-size) / 2);
7278
7556
  border-block-start: 0;
7279
7557
  border-inline-end: 0;
7280
7558
  }
7281
7559
 
7282
7560
  [data-scope='context-menu'][data-part='arrow'][data-placement^='left'] {
7283
- inset-inline-end: calc(var(--xh-context-menu-arrow-size, var(--xh-overlay-arrow-size)) / -2);
7284
- inset-block-start: calc(50% - var(--xh-context-menu-arrow-size, var(--xh-overlay-arrow-size)) / 2);
7561
+ inset-inline-end: calc(var(--xh-_context-menu-arrow-size) / -2);
7562
+ inset-block-start: calc(var(--xh-_context-menu-arrow-y) - var(--xh-_context-menu-arrow-size) / 2);
7285
7563
  border-block-end: 0;
7286
7564
  border-inline-start: 0;
7287
7565
  }
@@ -7309,8 +7587,8 @@
7309
7587
  --xh-_date-field-label-font-size: var(--xh-text-label-size);
7310
7588
  /* 形态档只换这三个私有槽:底色与两档描边 */
7311
7589
  --xh-_date-field-control-bg: var(--xh-bg-canvas);
7312
- --xh-_date-field-control-border: var(--xh-border-default);
7313
- --xh-_date-field-control-border-hover: var(--xh-border-strong);
7590
+ --xh-_date-field-control-border: var(--xh-border-control);
7591
+ --xh-_date-field-control-border-hover: var(--xh-border-control-hover);
7314
7592
  /* 聚焦环与段位反白随语气走。没写 data-tone 时这几个 --xh-_tone* 没被声明,退回原来的焦点色与品牌色 */
7315
7593
  --xh-_date-field-ring: var(--xh-ring-focus);
7316
7594
  --xh-_date-field-segment-bg: var(--xh-_tone, var(--xh-bg-brand));
@@ -7340,15 +7618,15 @@
7340
7618
  /* 描边式:与缺省等价,写出来是为了三档形态在皮肤里都看得见 */
7341
7619
  [data-scope='date-field'][data-part='root'][data-variant='outline'] {
7342
7620
  --xh-_date-field-control-bg: var(--xh-bg-canvas);
7343
- --xh-_date-field-control-border: var(--xh-border-default);
7344
- --xh-_date-field-control-border-hover: var(--xh-border-strong);
7621
+ --xh-_date-field-control-border: var(--xh-border-control);
7622
+ --xh-_date-field-control-border-hover: var(--xh-border-control-hover);
7345
7623
  }
7346
7624
 
7347
7625
  /* 填充式:底色顶替描边,悬停时才浮出一道淡边 */
7348
7626
  [data-scope='date-field'][data-part='root'][data-variant='subtle'] {
7349
7627
  --xh-_date-field-control-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
7350
7628
  --xh-_date-field-control-border: transparent;
7351
- --xh-_date-field-control-border-hover: var(--xh-_tone-border, var(--xh-border-default));
7629
+ --xh-_date-field-control-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
7352
7630
  }
7353
7631
 
7354
7632
  /* 无框式:底与边都让开,可见反馈只剩聚焦那一档 */
@@ -7540,7 +7818,7 @@
7540
7818
  block-size: var(--xh-date-picker-control-h, var(--xh-control-h-md));
7541
7819
  padding-inline: var(--xh-date-picker-control-px, var(--xh-control-px-md));
7542
7820
  padding-block: 0;
7543
- border: var(--xh-stroke-thin) solid var(--xh-date-picker-control-border, var(--xh-border-default));
7821
+ border: var(--xh-stroke-thin) solid var(--xh-date-picker-control-border, var(--xh-border-control));
7544
7822
  border-radius: var(--xh-date-picker-control-radius, var(--xh-shape-control));
7545
7823
  background: var(--xh-date-picker-control-bg, var(--xh-bg-canvas));
7546
7824
  color: var(--xh-date-picker-control-fg, var(--xh-fg-default));
@@ -7553,7 +7831,7 @@
7553
7831
 
7554
7832
  /* hover 只在中性态换描边:invalid 正拿边框颜色说事,被 hover 盖掉那条信息就没了 */
7555
7833
  [data-scope='date-picker'][data-part='control']:hover:not([data-disabled], [data-invalid]) {
7556
- border-color: var(--xh-date-picker-control-border-hover, var(--xh-border-strong));
7834
+ border-color: var(--xh-date-picker-control-border-hover, var(--xh-border-control-hover));
7557
7835
  }
7558
7836
 
7559
7837
  /* 焦点落在某一段或某个按钮上,而边框长在 control 上:焦点环得由内向外借一层。
@@ -7715,6 +7993,70 @@
7715
7993
  display: none;
7716
7994
  }
7717
7995
 
7996
+ /* —— showTime:时间列贴在日历右侧、逐列滚动,确认按钮收口 ——
7997
+ content 是 block,作者把日历与时间列包进一行排布;列自身只管滚与选项画法 */
7998
+ [data-scope='date-picker'][data-part='time-column'] {
7999
+ display: flex;
8000
+ flex-direction: column;
8001
+ gap: 2px;
8002
+ max-block-size: var(--xh-date-picker-time-column-h, 14rem);
8003
+ padding: var(--xh-space-1);
8004
+ overflow-y: auto;
8005
+ border-inline-start: var(--xh-stroke-thin) solid var(--xh-date-picker-content-border, var(--xh-border-default));
8006
+ }
8007
+
8008
+ [data-scope='date-picker'][data-part='time-column'][hidden] {
8009
+ display: none;
8010
+ }
8011
+
8012
+ [data-scope='date-picker'][data-part='time-item'] {
8013
+ padding-block: var(--xh-space-1);
8014
+ padding-inline: var(--xh-space-3);
8015
+ border-radius: var(--xh-shape-control);
8016
+ font-size: var(--xh-font-size-sm);
8017
+ line-height: var(--xh-leading-normal);
8018
+ text-align: center;
8019
+ cursor: pointer;
8020
+ user-select: none;
8021
+ }
8022
+
8023
+ [data-scope='date-picker'][data-part='time-item']:hover {
8024
+ background: var(--xh-date-picker-time-item-bg-hover, var(--xh-bg-subtle));
8025
+ }
8026
+
8027
+ [data-scope='date-picker'][data-part='time-item'][data-selected] {
8028
+ background: var(--xh-date-picker-time-item-bg-selected, var(--xh-bg-brand-subtle));
8029
+ color: var(--xh-date-picker-time-item-fg-selected, var(--xh-fg-brand));
8030
+ font-weight: var(--xh-font-weight-medium);
8031
+ }
8032
+
8033
+ [data-scope='date-picker'][data-part='confirm-trigger'] {
8034
+ display: inline-flex;
8035
+ align-items: center;
8036
+ justify-content: center;
8037
+ block-size: var(--xh-control-h-sm);
8038
+ padding-inline: var(--xh-control-px-sm);
8039
+ border: none;
8040
+ border-radius: var(--xh-shape-control);
8041
+ background: var(--xh-date-picker-confirm-bg, var(--xh-bg-brand));
8042
+ color: var(--xh-date-picker-confirm-fg, var(--xh-fg-on-brand));
8043
+ font-size: var(--xh-font-size-sm);
8044
+ cursor: pointer;
8045
+ }
8046
+
8047
+ [data-scope='date-picker'][data-part='confirm-trigger']:hover {
8048
+ background: var(--xh-date-picker-confirm-bg-hover, var(--xh-bg-brand-hover));
8049
+ }
8050
+
8051
+ [data-scope='date-picker'][data-part='confirm-trigger']:focus-visible {
8052
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
8053
+ outline-offset: var(--xh-ring-offset);
8054
+ }
8055
+
8056
+ [data-scope='date-picker'][data-part='confirm-trigger'][hidden] {
8057
+ display: none;
8058
+ }
8059
+
7718
8060
  /* 浮层进场:淡入 + 轻微上浮。名字带组件前缀,不与公共关键帧抢名 */
7719
8061
  @keyframes xh-date-picker-in {
7720
8062
  from {
@@ -7967,7 +8309,7 @@
7967
8309
  gap: var(--xh-drawer-trigger-gap, var(--xh-control-gap-md));
7968
8310
  block-size: var(--xh-drawer-trigger-h, var(--xh-control-h-md));
7969
8311
  padding-inline: var(--xh-drawer-trigger-px, var(--xh-control-px-md));
7970
- border: var(--xh-stroke-thin) solid var(--xh-drawer-trigger-border, var(--xh-border-default));
8312
+ border: var(--xh-stroke-thin) solid var(--xh-drawer-trigger-border, var(--xh-border-control));
7971
8313
  border-radius: var(--xh-drawer-trigger-radius, var(--xh-shape-control));
7972
8314
  background: var(--xh-drawer-trigger-bg, var(--xh-bg-canvas));
7973
8315
  color: var(--xh-drawer-trigger-fg, var(--xh-fg-default));
@@ -7987,14 +8329,14 @@
7987
8329
 
7988
8330
  [data-scope='drawer'][data-part='trigger']:hover:not(:disabled) {
7989
8331
  background: var(--xh-drawer-trigger-bg-hover, var(--xh-bg-subtle-hover));
7990
- border-color: var(--xh-drawer-trigger-border-hover, var(--xh-border-strong));
8332
+ border-color: var(--xh-drawer-trigger-border-hover, var(--xh-border-control-hover));
7991
8333
  }
7992
8334
 
7993
8335
  /* 抽屉展开期间触发按钮压住不弹起:面板贴在视口边上,它和按钮往往离得很远,
7994
8336
  按钮自己得说明"这一扇是我开的"。 */
7995
8337
  [data-scope='drawer'][data-part='trigger'][data-state='open'] {
7996
8338
  background: var(--xh-drawer-trigger-bg-open, var(--xh-bg-subtle-active));
7997
- border-color: var(--xh-drawer-trigger-border-open, var(--xh-border-strong));
8339
+ border-color: var(--xh-drawer-trigger-border-open, var(--xh-border-control-hover));
7998
8340
  }
7999
8341
 
8000
8342
  [data-scope='drawer'][data-part='trigger']:focus-visible {
@@ -8330,7 +8672,7 @@
8330
8672
  block-size: var(--xh-editable-input-h, var(--xh-control-h-md));
8331
8673
  padding-inline: var(--xh-editable-input-px, var(--xh-control-px-md));
8332
8674
  padding-block: 0;
8333
- border: var(--xh-stroke-thin) solid var(--xh-editable-input-border, var(--xh-border-default));
8675
+ border: var(--xh-stroke-thin) solid var(--xh-editable-input-border, var(--xh-border-control));
8334
8676
  border-radius: var(--xh-editable-input-radius, var(--xh-shape-control));
8335
8677
  background: var(--xh-editable-input-bg, var(--xh-bg-canvas));
8336
8678
  color: var(--xh-editable-input-fg, var(--xh-fg-default));
@@ -8358,7 +8700,7 @@
8358
8700
 
8359
8701
  /* hover 只在中性态换描边:invalid 正拿边框颜色说事,被 hover 盖掉那条信息就没了 */
8360
8702
  [data-scope='editable'][data-part='input']:hover:not(:disabled, [data-invalid]) {
8361
- border-color: var(--xh-editable-input-border-hover, var(--xh-border-strong));
8703
+ border-color: var(--xh-editable-input-border-hover, var(--xh-border-control-hover));
8362
8704
  }
8363
8705
 
8364
8706
  [data-scope='editable'][data-part='input']:focus-visible {
@@ -8405,7 +8747,7 @@
8405
8747
  block-size: var(--xh-editable-trigger-h, var(--xh-control-h-sm));
8406
8748
  padding-inline: var(--xh-editable-trigger-px, var(--xh-control-px-sm));
8407
8749
  padding-block: 0;
8408
- border: var(--xh-stroke-thin) solid var(--xh-editable-trigger-border, var(--xh-border-default));
8750
+ border: var(--xh-stroke-thin) solid var(--xh-editable-trigger-border, var(--xh-border-control));
8409
8751
  border-radius: var(--xh-editable-trigger-radius, var(--xh-shape-control));
8410
8752
  background: var(--xh-editable-trigger-bg, var(--xh-bg-subtle));
8411
8753
  color: var(--xh-editable-trigger-fg, var(--xh-fg-default));
@@ -8429,7 +8771,7 @@
8429
8771
  [data-scope='editable'][data-part='edit-trigger']:hover:not(:disabled),
8430
8772
  [data-scope='editable'][data-part='submit-trigger']:hover:not(:disabled),
8431
8773
  [data-scope='editable'][data-part='cancel-trigger']:hover:not(:disabled) {
8432
- border-color: var(--xh-editable-trigger-border-hover, var(--xh-border-strong));
8774
+ border-color: var(--xh-editable-trigger-border-hover, var(--xh-border-control-hover));
8433
8775
  background: var(--xh-editable-trigger-bg-hover, var(--xh-bg-subtle-hover));
8434
8776
  }
8435
8777
 
@@ -8465,12 +8807,12 @@
8465
8807
  }
8466
8808
 
8467
8809
  [data-scope='editable'][data-part='submit-trigger']:hover:not(:disabled) {
8468
- border-color: var(--xh-editable-submit-bg-hover, var(--xh-bg-brand-hover));
8810
+ border-color: var(--xh-editable-submit-border-hover, var(--xh-editable-submit-bg-hover, var(--xh-bg-brand-hover)));
8469
8811
  background: var(--xh-editable-submit-bg-hover, var(--xh-bg-brand-hover));
8470
8812
  }
8471
8813
 
8472
8814
  [data-scope='editable'][data-part='submit-trigger']:active:not(:disabled) {
8473
- border-color: var(--xh-editable-submit-bg-active, var(--xh-bg-brand-active));
8815
+ border-color: var(--xh-editable-submit-border-active, var(--xh-editable-submit-bg-active, var(--xh-bg-brand-active)));
8474
8816
  background: var(--xh-editable-submit-bg-active, var(--xh-bg-brand-active));
8475
8817
  }
8476
8818
 
@@ -8481,23 +8823,54 @@
8481
8823
  gap: var(--xh-field-gap, var(--xh-space-1));
8482
8824
  }
8483
8825
 
8484
- [data-scope='field'][data-part='label'] {
8485
- color: var(--xh-field-label-fg, var(--xh-fg-default));
8486
- font-size: var(--xh-field-label-font-size, var(--xh-text-label-size));
8487
- font-weight: var(--xh-field-label-font-weight, var(--xh-text-label-weight));
8488
- line-height: var(--xh-leading-none);
8826
+ /* 表单横排(标签左置):字段转两列网格,标签列宽由表单的 --xh-form-label-w 统一给,
8827
+ 整表据此对齐;描述与错误文案跟着控件走第二列 */
8828
+ [data-scope='form'][data-part='root'][data-layout='horizontal'] [data-scope='field'][data-part='root'] {
8829
+ display: grid;
8830
+ grid-template-columns: var(--xh-form-label-w) minmax(0, 1fr);
8831
+ column-gap: var(--xh-field-label-gap, var(--xh-space-3));
8832
+ align-items: start;
8489
8833
  }
8490
8834
 
8491
- [data-scope='field'][data-part='label'][data-disabled] {
8492
- color: var(--xh-field-label-fg-disabled, var(--xh-fg-subtle));
8835
+ [data-scope='form'][data-part='root'][data-layout='horizontal'] [data-scope='field'][data-part='label'] {
8836
+ grid-column: 1;
8837
+ justify-self: end;
8838
+ /* 与控件的首行文字对齐:按控件高度撑一行再居中 */
8839
+ line-height: var(--xh-control-h-md);
8493
8840
  }
8494
8841
 
8495
- /* control 是作者自己的输入控件,这里只给一套可被覆盖的基础皮肤 */
8842
+ [data-scope='form'][data-part='root'][data-layout='horizontal'][data-label-align='start'] [data-scope='field'][data-part='label'] {
8843
+ justify-self: start;
8844
+ }
8845
+
8846
+ [data-scope='form'][data-part='root'][data-layout='horizontal'] [data-scope='field'][data-part='root'] > :not([data-part='label']) {
8847
+ grid-column: 2;
8848
+ }
8849
+
8850
+ [data-scope='field'][data-part='label'] {
8851
+ color: var(--xh-field-label-fg, var(--xh-fg-default));
8852
+ font-size: var(--xh-field-label-font-size, var(--xh-text-label-size));
8853
+ font-weight: var(--xh-field-label-font-weight, var(--xh-text-label-weight));
8854
+ line-height: var(--xh-leading-none);
8855
+ }
8856
+
8857
+ [data-scope='field'][data-part='label'][data-disabled] {
8858
+ color: var(--xh-field-label-fg-disabled, var(--xh-fg-subtle));
8859
+ }
8860
+
8861
+ /* 必填星号:跟着根上的 data-required 亮,选填不标 */
8862
+ [data-scope='field'][data-part='root'][data-required] [data-part='label']::after {
8863
+ content: '*';
8864
+ margin-inline-start: var(--xh-space-1);
8865
+ color: var(--xh-field-label-star, var(--xh-fg-danger));
8866
+ }
8867
+
8868
+ /* control 是作者自己的输入控件,这里只给一套可被覆盖的基础皮肤 */
8496
8869
  [data-scope='field'][data-part='control'] {
8497
8870
  inline-size: 100%;
8498
8871
  block-size: var(--xh-field-control-h, var(--xh-control-h-md));
8499
8872
  padding-inline: var(--xh-field-control-px, var(--xh-control-px-md));
8500
- border: var(--xh-stroke-thin) solid var(--xh-field-control-border, var(--xh-border-default));
8873
+ border: var(--xh-stroke-thin) solid var(--xh-field-control-border, var(--xh-border-control));
8501
8874
  border-radius: var(--xh-field-control-radius, var(--xh-shape-control));
8502
8875
  background: var(--xh-field-control-bg, var(--xh-bg-canvas));
8503
8876
  color: var(--xh-field-control-fg, var(--xh-fg-default));
@@ -8573,7 +8946,7 @@
8573
8946
  min-block-size: var(--xh-file-upload-dropzone-min-h, 8rem);
8574
8947
  padding-block: var(--xh-file-upload-dropzone-py, var(--xh-space-5));
8575
8948
  padding-inline: var(--xh-file-upload-dropzone-px, var(--xh-space-4));
8576
- border: var(--xh-stroke-thick) dashed var(--xh-file-upload-dropzone-border, var(--xh-border-default));
8949
+ border: var(--xh-stroke-thick) dashed var(--xh-file-upload-dropzone-border, var(--xh-border-control));
8577
8950
  border-radius: var(--xh-file-upload-dropzone-radius, var(--xh-shape-surface));
8578
8951
  background: var(--xh-file-upload-dropzone-bg, var(--xh-bg-canvas));
8579
8952
  color: var(--xh-file-upload-dropzone-fg, var(--xh-fg-muted));
@@ -8592,7 +8965,7 @@
8592
8965
 
8593
8966
  /* hover 只在中性态换描边:invalid 与 dragging 正拿边框颜色说事,被 hover 盖掉那条信息就没了 */
8594
8967
  [data-scope='file-upload'][data-part='dropzone']:hover:not([data-disabled], [data-invalid], [data-dragging]) {
8595
- border-color: var(--xh-file-upload-dropzone-border-hover, var(--xh-border-strong));
8968
+ border-color: var(--xh-file-upload-dropzone-border-hover, var(--xh-border-control-hover));
8596
8969
  }
8597
8970
 
8598
8971
  /* 投放区自己承载焦点(它就是那个大按钮),所以画环而不是 outline:none */
@@ -8627,7 +9000,7 @@
8627
9000
  gap: var(--xh-file-upload-trigger-gap, var(--xh-control-gap-md));
8628
9001
  block-size: var(--xh-file-upload-trigger-h, var(--xh-control-h-md));
8629
9002
  padding-inline: var(--xh-file-upload-trigger-px, var(--xh-control-px-md));
8630
- border: var(--xh-stroke-thin) solid var(--xh-file-upload-trigger-border, var(--xh-border-default));
9003
+ border: var(--xh-stroke-thin) solid var(--xh-file-upload-trigger-border, var(--xh-border-control));
8631
9004
  border-radius: var(--xh-file-upload-trigger-radius, var(--xh-shape-control));
8632
9005
  background: var(--xh-file-upload-trigger-bg, var(--xh-bg-surface));
8633
9006
  color: var(--xh-file-upload-trigger-fg, var(--xh-fg-default));
@@ -8851,6 +9224,18 @@
8851
9224
  display: none;
8852
9225
  }
8853
9226
 
9227
+ /* 标签左置:标签列宽的缺省档在这儿,labelWidth 会以行内样式盖过它 */
9228
+ [data-scope='form'][data-part='root'][data-layout='horizontal'] {
9229
+ --xh-form-label-w: 30%;
9230
+ }
9231
+
9232
+ /* 横排一行流:字段并肩排,放不下自动折行 */
9233
+ [data-scope='form'][data-part='root'][data-layout='inline'] {
9234
+ flex-flow: row wrap;
9235
+ align-items: start;
9236
+ column-gap: var(--xh-form-inline-gap, var(--xh-space-4));
9237
+ }
9238
+
8854
9239
  /* 字段容器只管排版与状态标记:标题、控件、描述、错误文案都是里面那个 Field 的活儿 */
8855
9240
  [data-scope='form'][data-part='field-group'] {
8856
9241
  display: flex;
@@ -8943,7 +9328,7 @@
8943
9328
  block-size: var(--xh-form-trigger-h, var(--xh-control-h-md));
8944
9329
  padding-block: 0;
8945
9330
  padding-inline: var(--xh-form-trigger-px, var(--xh-control-px-md));
8946
- border: var(--xh-stroke-thin) solid var(--xh-form-trigger-border, var(--xh-border-default));
9331
+ border: var(--xh-stroke-thin) solid var(--xh-form-trigger-border, var(--xh-border-control));
8947
9332
  border-radius: var(--xh-form-trigger-radius, var(--xh-shape-control));
8948
9333
  background: var(--xh-form-trigger-bg, var(--xh-bg-subtle));
8949
9334
  color: var(--xh-form-trigger-fg, var(--xh-fg-default));
@@ -8964,7 +9349,7 @@
8964
9349
 
8965
9350
  [data-scope='form'][data-part='submit-trigger']:hover:not(:disabled),
8966
9351
  [data-scope='form'][data-part='reset-trigger']:hover:not(:disabled) {
8967
- border-color: var(--xh-form-trigger-border-hover, var(--xh-border-strong));
9352
+ border-color: var(--xh-form-trigger-border-hover, var(--xh-border-control-hover));
8968
9353
  background: var(--xh-form-trigger-bg-hover, var(--xh-bg-subtle-hover));
8969
9354
  }
8970
9355
 
@@ -8991,18 +9376,18 @@
8991
9376
 
8992
9377
  /* 提交是这一组里的主操作,给它一层实心底色区分开 */
8993
9378
  [data-scope='form'][data-part='submit-trigger']:not(:disabled) {
8994
- border-color: var(--xh-form-submit-bg, var(--xh-bg-brand));
9379
+ border-color: var(--xh-form-submit-border, var(--xh-form-submit-bg, var(--xh-bg-brand)));
8995
9380
  background: var(--xh-form-submit-bg, var(--xh-bg-brand));
8996
9381
  color: var(--xh-form-submit-fg, var(--xh-fg-on-brand));
8997
9382
  }
8998
9383
 
8999
9384
  [data-scope='form'][data-part='submit-trigger']:hover:not(:disabled) {
9000
- border-color: var(--xh-form-submit-bg-hover, var(--xh-bg-brand-hover));
9385
+ border-color: var(--xh-form-submit-border-hover, var(--xh-form-submit-bg-hover, var(--xh-bg-brand-hover)));
9001
9386
  background: var(--xh-form-submit-bg-hover, var(--xh-bg-brand-hover));
9002
9387
  }
9003
9388
 
9004
9389
  [data-scope='form'][data-part='submit-trigger']:active:not(:disabled) {
9005
- border-color: var(--xh-form-submit-bg-active, var(--xh-bg-brand-active));
9390
+ border-color: var(--xh-form-submit-border-active, var(--xh-form-submit-bg-active, var(--xh-bg-brand-active)));
9006
9391
  background: var(--xh-form-submit-bg-active, var(--xh-bg-brand-active));
9007
9392
  }
9008
9393
 
@@ -9122,40 +9507,44 @@
9122
9507
  }
9123
9508
 
9124
9509
  /* 箭头是旋转 45° 的方块,只保留朝向锚点那两条边的描边;
9125
- 引擎不产出箭头偏移,因此沿交叉轴固定居中 */
9510
+ 交叉轴落点由定位引擎经内联自定义属性写进来,没给时退回居中 */
9126
9511
  [data-scope='hover-card'][data-part='arrow'] {
9512
+ --xh-_hover-card-arrow-size: var(--xh-hover-card-arrow-size, var(--xh-overlay-arrow-size));
9513
+ --xh-_hover-card-arrow-x: 50%;
9514
+ --xh-_hover-card-arrow-y: 50%;
9515
+
9127
9516
  position: absolute;
9128
- inline-size: var(--xh-hover-card-arrow-size, var(--xh-overlay-arrow-size));
9129
- block-size: var(--xh-hover-card-arrow-size, var(--xh-overlay-arrow-size));
9517
+ inline-size: var(--xh-_hover-card-arrow-size);
9518
+ block-size: var(--xh-_hover-card-arrow-size);
9130
9519
  border: var(--xh-stroke-thin) solid var(--xh-hover-card-border, var(--xh-border-default));
9131
9520
  background: var(--xh-hover-card-bg, var(--xh-bg-surface));
9132
9521
  rotate: 45deg;
9133
9522
  }
9134
9523
 
9135
9524
  [data-scope='hover-card'][data-part='arrow'][data-placement^='bottom'] {
9136
- inset-block-start: calc(var(--xh-hover-card-arrow-size, var(--xh-overlay-arrow-size)) / -2);
9137
- inset-inline-start: calc(50% - var(--xh-hover-card-arrow-size, var(--xh-overlay-arrow-size)) / 2);
9525
+ inset-block-start: calc(var(--xh-_hover-card-arrow-size) / -2);
9526
+ inset-inline-start: calc(var(--xh-_hover-card-arrow-x) - var(--xh-_hover-card-arrow-size) / 2);
9138
9527
  border-block-end: 0;
9139
9528
  border-inline-end: 0;
9140
9529
  }
9141
9530
 
9142
9531
  [data-scope='hover-card'][data-part='arrow'][data-placement^='top'] {
9143
- inset-block-end: calc(var(--xh-hover-card-arrow-size, var(--xh-overlay-arrow-size)) / -2);
9144
- inset-inline-start: calc(50% - var(--xh-hover-card-arrow-size, var(--xh-overlay-arrow-size)) / 2);
9532
+ inset-block-end: calc(var(--xh-_hover-card-arrow-size) / -2);
9533
+ inset-inline-start: calc(var(--xh-_hover-card-arrow-x) - var(--xh-_hover-card-arrow-size) / 2);
9145
9534
  border-block-start: 0;
9146
9535
  border-inline-start: 0;
9147
9536
  }
9148
9537
 
9149
9538
  [data-scope='hover-card'][data-part='arrow'][data-placement^='right'] {
9150
- inset-inline-start: calc(var(--xh-hover-card-arrow-size, var(--xh-overlay-arrow-size)) / -2);
9151
- inset-block-start: calc(50% - var(--xh-hover-card-arrow-size, var(--xh-overlay-arrow-size)) / 2);
9539
+ inset-inline-start: calc(var(--xh-_hover-card-arrow-size) / -2);
9540
+ inset-block-start: calc(var(--xh-_hover-card-arrow-y) - var(--xh-_hover-card-arrow-size) / 2);
9152
9541
  border-block-start: 0;
9153
9542
  border-inline-end: 0;
9154
9543
  }
9155
9544
 
9156
9545
  [data-scope='hover-card'][data-part='arrow'][data-placement^='left'] {
9157
- inset-inline-end: calc(var(--xh-hover-card-arrow-size, var(--xh-overlay-arrow-size)) / -2);
9158
- inset-block-start: calc(50% - var(--xh-hover-card-arrow-size, var(--xh-overlay-arrow-size)) / 2);
9546
+ inset-inline-end: calc(var(--xh-_hover-card-arrow-size) / -2);
9547
+ inset-block-start: calc(var(--xh-_hover-card-arrow-y) - var(--xh-_hover-card-arrow-size) / 2);
9159
9548
  border-block-end: 0;
9160
9549
  border-inline-start: 0;
9161
9550
  }
@@ -9265,6 +9654,182 @@
9265
9654
  display: none;
9266
9655
  }
9267
9656
 
9657
+ /* styles/image-viewer.css */
9658
+ [data-scope='image-viewer'][data-part='backdrop'] {
9659
+ position: fixed;
9660
+ inset: 0;
9661
+ z-index: var(--xh-layer-modal);
9662
+ /* 看片要压住页面底色,遮罩比普通对话框更深 */
9663
+ background: var(--xh-image-viewer-backdrop-bg, color-mix(in oklab, var(--xh-color-neutral-950) 78%, transparent));
9664
+ }
9665
+
9666
+ [data-scope='image-viewer'][data-part='backdrop'][data-state='open'] {
9667
+ animation: xh-fade-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
9668
+ }
9669
+
9670
+ [data-scope='image-viewer'][data-part='backdrop'][data-state='closed'] {
9671
+ animation: xh-fade-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit);
9672
+ }
9673
+
9674
+ [data-scope='image-viewer'][data-part='positioner'] {
9675
+ position: fixed;
9676
+ inset: 0;
9677
+ z-index: var(--xh-layer-modal);
9678
+ display: flex;
9679
+ align-items: center;
9680
+ justify-content: center;
9681
+ }
9682
+
9683
+ [data-scope='image-viewer'][data-part='content'] {
9684
+ position: relative;
9685
+ inline-size: 100%;
9686
+ block-size: 100%;
9687
+ display: flex;
9688
+ align-items: center;
9689
+ justify-content: center;
9690
+ outline: none;
9691
+ /* 深色底上的工具条与计数固定用浅字,不随主题翻 */
9692
+ color: var(--xh-image-viewer-fg, var(--xh-color-neutral-0));
9693
+ }
9694
+
9695
+ [data-scope='image-viewer'][data-part='content'][data-state='open'] {
9696
+ animation: xh-fade-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
9697
+ }
9698
+
9699
+ [data-scope='image-viewer'][data-part='content'][data-state='closed'] {
9700
+ animation: xh-fade-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit);
9701
+ }
9702
+
9703
+ [data-scope='image-viewer'][data-part='content'][hidden] {
9704
+ display: none;
9705
+ }
9706
+
9707
+ [data-scope='image-viewer'][data-part='viewport'] {
9708
+ position: absolute;
9709
+ inset: 0;
9710
+ display: flex;
9711
+ align-items: center;
9712
+ justify-content: center;
9713
+ overflow: hidden;
9714
+ cursor: grab;
9715
+ /* 平移与滚轮都归指针会话,浏览器手势不抢 */
9716
+ touch-action: none;
9717
+ }
9718
+
9719
+ [data-scope='image-viewer'][data-part='viewport'][data-panning] {
9720
+ cursor: grabbing;
9721
+ }
9722
+
9723
+ [data-scope='image-viewer'][data-part='image'] {
9724
+ max-inline-size: 90%;
9725
+ max-block-size: 85%;
9726
+ user-select: none;
9727
+ /* 变换是内联 style 写的,只给缩放旋转一点缓动;拖拽平移要跟手,不缓 */
9728
+ transition: transform var(--xh-duration-fast) var(--xh-ease-out);
9729
+ }
9730
+
9731
+ [data-scope='image-viewer'][data-part='image'][data-panning] {
9732
+ transition: none;
9733
+ }
9734
+
9735
+ /* —— 悬浮件共用:半透明深底加浅字 —— */
9736
+ [data-scope='image-viewer'][data-part='toolbar'],
9737
+ [data-scope='image-viewer'][data-part='counter'],
9738
+ [data-scope='image-viewer'][data-part='prev-trigger'],
9739
+ [data-scope='image-viewer'][data-part='next-trigger'],
9740
+ [data-scope='image-viewer'][data-part='close-trigger'] {
9741
+ position: absolute;
9742
+ z-index: 1;
9743
+ background: var(--xh-image-viewer-chrome-bg, color-mix(in oklab, var(--xh-color-neutral-950) 55%, transparent));
9744
+ color: inherit;
9745
+ border: none;
9746
+ border-radius: var(--xh-shape-pill);
9747
+ }
9748
+
9749
+ [data-scope='image-viewer'][data-part='toolbar'] {
9750
+ inset-block-end: var(--xh-space-6);
9751
+ inset-inline-start: 50%;
9752
+ translate: -50% 0;
9753
+ display: flex;
9754
+ align-items: center;
9755
+ gap: var(--xh-space-1);
9756
+ padding: var(--xh-space-1_5) var(--xh-space-3);
9757
+ }
9758
+
9759
+ /* 工具条里的按钮:底色交给工具条,本体只管命中区 */
9760
+ [data-scope='image-viewer'][data-part='toolbar'] > [data-part] {
9761
+ position: static;
9762
+ display: inline-flex;
9763
+ align-items: center;
9764
+ justify-content: center;
9765
+ inline-size: var(--xh-control-action-size);
9766
+ block-size: var(--xh-control-action-size);
9767
+ background: none;
9768
+ border: none;
9769
+ border-radius: var(--xh-shape-control);
9770
+ color: inherit;
9771
+ font-size: var(--xh-glyph-size-sm);
9772
+ cursor: pointer;
9773
+ }
9774
+
9775
+ [data-scope='image-viewer'][data-part='counter'] {
9776
+ inset-block-start: var(--xh-space-6);
9777
+ inset-inline-start: 50%;
9778
+ translate: -50% 0;
9779
+ padding: var(--xh-space-1) var(--xh-space-3);
9780
+ font-size: var(--xh-font-size-sm);
9781
+ }
9782
+
9783
+ [data-scope='image-viewer'][data-part='prev-trigger'],
9784
+ [data-scope='image-viewer'][data-part='next-trigger'],
9785
+ [data-scope='image-viewer'][data-part='close-trigger'] {
9786
+ display: inline-flex;
9787
+ align-items: center;
9788
+ justify-content: center;
9789
+ inline-size: var(--xh-control-h-lg);
9790
+ block-size: var(--xh-control-h-lg);
9791
+ font-size: var(--xh-glyph-size-md);
9792
+ cursor: pointer;
9793
+ }
9794
+
9795
+ [data-scope='image-viewer'][data-part='prev-trigger'] {
9796
+ inset-inline-start: var(--xh-space-6);
9797
+ inset-block-start: 50%;
9798
+ translate: 0 -50%;
9799
+ }
9800
+
9801
+ [data-scope='image-viewer'][data-part='next-trigger'] {
9802
+ inset-inline-end: var(--xh-space-6);
9803
+ inset-block-start: 50%;
9804
+ translate: 0 -50%;
9805
+ }
9806
+
9807
+ [data-scope='image-viewer'][data-part='close-trigger'] {
9808
+ inset-block-start: var(--xh-space-6);
9809
+ inset-inline-end: var(--xh-space-6);
9810
+ }
9811
+
9812
+ [data-scope='image-viewer'] button[data-part]:focus-visible {
9813
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
9814
+ outline-offset: var(--xh-ring-offset);
9815
+ }
9816
+
9817
+ [data-scope='image-viewer'] button[data-part]:disabled {
9818
+ opacity: var(--xh-state-disabled-opacity);
9819
+ cursor: not-allowed;
9820
+ }
9821
+
9822
+ @media (prefers-reduced-motion: reduce) {
9823
+ [data-scope='image-viewer'][data-part='backdrop'],
9824
+ [data-scope='image-viewer'][data-part='content'] {
9825
+ animation-duration: 0.01ms;
9826
+ }
9827
+
9828
+ [data-scope='image-viewer'][data-part='image'] {
9829
+ transition: none;
9830
+ }
9831
+ }
9832
+
9268
9833
  /* styles/listbox.css */
9269
9834
  [data-scope='listbox'][data-part='root'] {
9270
9835
  display: flex;
@@ -9341,7 +9906,7 @@
9341
9906
  [data-scope='listbox'][data-part='item-group-label'] {
9342
9907
  display: block;
9343
9908
  padding-block: var(--xh-listbox-group-label-py, var(--xh-space-1));
9344
- padding-inline: var(--xh-listbox-group-label-px, var(--xh-control-px-sm));
9909
+ padding-inline: var(--xh-listbox-group-label-px, var(--xh-control-px-md));
9345
9910
  color: var(--xh-listbox-group-label-fg, var(--xh-fg-subtle));
9346
9911
  font-size: var(--xh-listbox-group-label-font-size, var(--xh-font-size-xs));
9347
9912
  font-weight: var(--xh-listbox-group-label-font-weight, var(--xh-font-weight-medium));
@@ -9361,8 +9926,8 @@
9361
9926
  display: flex;
9362
9927
  align-items: center;
9363
9928
  gap: var(--xh-listbox-item-gap, var(--xh-control-gap-md));
9364
- padding-block: var(--xh-listbox-item-py, var(--xh-space-1));
9365
- padding-inline: var(--xh-listbox-item-px, var(--xh-control-px-sm));
9929
+ padding-block: var(--xh-listbox-item-py, var(--xh-space-1_5));
9930
+ padding-inline: var(--xh-listbox-item-px, var(--xh-control-px-md));
9366
9931
  border-radius: var(--xh-listbox-item-radius, var(--xh-shape-control));
9367
9932
  color: var(--xh-listbox-item-fg, var(--xh-fg-default));
9368
9933
  font-size: var(--xh-listbox-item-font-size, var(--xh-text-body-size));
@@ -9371,6 +9936,9 @@
9371
9936
  white-space: nowrap;
9372
9937
  cursor: pointer;
9373
9938
  user-select: none;
9939
+ transition:
9940
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
9941
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
9374
9942
  }
9375
9943
 
9376
9944
  /* 条目可以按需收起(过滤掉的选项),有 display 就得补这条 */
@@ -9378,10 +9946,10 @@
9378
9946
  display: none;
9379
9947
  }
9380
9948
 
9381
- /* 键盘高亮与指针高亮共用一套底色:条目本身承载焦点,不另画描边。
9382
- data-highlighted 跟的是连接层记的焦点条目,与选中互相独立 */
9949
+ /* 悬停与键盘锚点共用中性灰轻档:data-highlighted 跟的是连接层记的焦点条目,
9950
+ 与选中互相独立 */
9383
9951
  [data-scope='listbox'][data-part='item']:not([data-disabled]):is(:hover, [data-highlighted]) {
9384
- background: var(--xh-listbox-item-bg-highlight, var(--xh-bg-subtle-hover));
9952
+ background: var(--xh-listbox-item-bg-hover, var(--xh-bg-subtle));
9385
9953
  }
9386
9954
 
9387
9955
  /* 选中只换文字色与字重,配合行尾的 ✓ 标记;底色整条通道留给高亮,
@@ -9522,13 +10090,14 @@
9522
10090
  [data-scope='menu'][data-part='content'] {
9523
10091
  /* 尺寸档只换这四个私有槽,条目从 content 继承它们,换档不必给每个部件各写一条选择器。
9524
10092
  菜单没有 root 部件(浮层树的最外层就是 content),三轴属性都落在这里 */
9525
- --xh-_menu-item-py: var(--xh-space-2);
9526
- --xh-_menu-item-px: var(--xh-control-px-sm);
10093
+ --xh-_menu-item-py: var(--xh-space-1_5);
10094
+ --xh-_menu-item-px: var(--xh-control-px-md);
9527
10095
  --xh-_menu-item-gap: var(--xh-control-gap-md);
9528
10096
  --xh-_menu-font-size: var(--xh-text-body-size);
9529
10097
 
9530
- /* 条目高亮底随语气走。没写 data-tone 时 --xh-_tone-subtle-hover 没被声明,退回原来的取值 */
9531
- --xh-_menu-item-highlight-bg: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover));
10098
+ /* 打开子菜单的触发条目走品牌淡底强档,随语气换色相;没写 data-tone 时退回品牌淡底。
10099
+ 悬停与键盘焦点恒中性灰轻档、不接语气槽——两档同色相的话打开路径就认不出来了 */
10100
+ --xh-_menu-item-active-bg: var(--xh-_tone-subtle, var(--xh-bg-brand-subtle));
9532
10101
 
9533
10102
  display: flex;
9534
10103
  flex-direction: column;
@@ -9559,18 +10128,18 @@
9559
10128
  }
9560
10129
 
9561
10130
  /* —— 尺寸 —— */
9562
- /* 缺省档写在 content 的基础规则里。条目内外边距缺省档本就不在 control-h/px 那把尺上
9563
- (高度由 padding 撑出来、行内内缩取的是 sm 档),故按 space 档上下各挪一级 */
10131
+ /* 缺省档写在 content 的基础规则里。条目高度由 padding 撑出来:
10132
+ 纵向走 space 档、行内内缩走 control-px 档(随密度轴),换档各挪一级 */
9564
10133
  [data-scope='menu'][data-part='content'][data-size='sm'] {
9565
10134
  --xh-_menu-item-py: var(--xh-space-1);
9566
- --xh-_menu-item-px: var(--xh-space-1);
10135
+ --xh-_menu-item-px: var(--xh-control-px-sm);
9567
10136
  --xh-_menu-item-gap: var(--xh-control-gap-sm);
9568
10137
  --xh-_menu-font-size: var(--xh-font-size-sm);
9569
10138
  }
9570
10139
 
9571
10140
  [data-scope='menu'][data-part='content'][data-size='lg'] {
9572
- --xh-_menu-item-py: var(--xh-space-3);
9573
- --xh-_menu-item-px: var(--xh-space-3);
10141
+ --xh-_menu-item-py: var(--xh-space-2);
10142
+ --xh-_menu-item-px: var(--xh-control-px-lg);
9574
10143
  --xh-_menu-item-gap: var(--xh-control-gap-lg);
9575
10144
  --xh-_menu-font-size: var(--xh-font-size-lg);
9576
10145
  }
@@ -9584,15 +10153,25 @@
9584
10153
  border-radius: var(--xh-menu-item-radius, var(--xh-shape-control));
9585
10154
  color: var(--xh-menu-item-fg, var(--xh-fg-default));
9586
10155
  font-size: var(--xh-menu-item-font-size, var(--xh-_menu-font-size));
9587
- line-height: var(--xh-leading-none);
10156
+ line-height: var(--xh-menu-item-leading, var(--xh-leading-normal));
9588
10157
  white-space: nowrap;
9589
10158
  cursor: pointer;
9590
10159
  user-select: none;
10160
+ transition:
10161
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
10162
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
9591
10163
  }
9592
10164
 
9593
- /* 键盘高亮与指针高亮共用一套底色:条目本身承载焦点,不另画描边 */
10165
+ /* 悬停与键盘焦点共用中性灰轻档:条目本身承载焦点,不另画描边 */
9594
10166
  [data-scope='menu'][data-part='item']:not([data-disabled]):is(:hover, :focus) {
9595
- background: var(--xh-menu-item-bg-highlight, var(--xh-_menu-item-highlight-bg));
10167
+ background: var(--xh-menu-item-bg-hover, var(--xh-bg-subtle));
10168
+ }
10169
+
10170
+ /* 打开子菜单的触发条目是展开路径,走品牌淡底加粗强档。
10171
+ 必须排在轻档之后:悬停或焦点回到它身上时同特指度靠书写顺序压过轻档 */
10172
+ [data-scope='menu'][data-part='item']:not([data-disabled])[data-state='open'] {
10173
+ background: var(--xh-menu-item-bg-active, var(--xh-_menu-item-active-bg));
10174
+ font-weight: var(--xh-menu-item-active-font-weight, var(--xh-font-weight-semibold));
9596
10175
  }
9597
10176
 
9598
10177
  [data-scope='menu'][data-part='item']:focus {
@@ -9611,40 +10190,44 @@
9611
10190
  }
9612
10191
 
9613
10192
  /* 箭头是旋转 45° 的方块,只保留朝向锚点那两条边的描边;
9614
- 引擎不产出箭头偏移,因此沿交叉轴固定居中 */
10193
+ 交叉轴落点由定位引擎经内联自定义属性写进来,没给时退回居中 */
9615
10194
  [data-scope='menu'][data-part='arrow'] {
10195
+ --xh-_menu-arrow-size: var(--xh-menu-arrow-size, var(--xh-overlay-arrow-size));
10196
+ --xh-_menu-arrow-x: 50%;
10197
+ --xh-_menu-arrow-y: 50%;
10198
+
9616
10199
  position: absolute;
9617
- inline-size: var(--xh-menu-arrow-size, var(--xh-overlay-arrow-size));
9618
- block-size: var(--xh-menu-arrow-size, var(--xh-overlay-arrow-size));
10200
+ inline-size: var(--xh-_menu-arrow-size);
10201
+ block-size: var(--xh-_menu-arrow-size);
9619
10202
  background: var(--xh-menu-bg, var(--xh-bg-surface));
9620
10203
  border: var(--xh-stroke-thin) solid var(--xh-menu-border, var(--xh-border-default));
9621
10204
  rotate: 45deg;
9622
10205
  }
9623
10206
 
9624
10207
  [data-scope='menu'][data-part='arrow'][data-placement^='bottom'] {
9625
- inset-block-start: calc(var(--xh-menu-arrow-size, var(--xh-overlay-arrow-size)) / -2);
9626
- inset-inline-start: calc(50% - var(--xh-menu-arrow-size, var(--xh-overlay-arrow-size)) / 2);
10208
+ inset-block-start: calc(var(--xh-_menu-arrow-size) / -2);
10209
+ inset-inline-start: calc(var(--xh-_menu-arrow-x) - var(--xh-_menu-arrow-size) / 2);
9627
10210
  border-block-end: 0;
9628
10211
  border-inline-end: 0;
9629
10212
  }
9630
10213
 
9631
10214
  [data-scope='menu'][data-part='arrow'][data-placement^='top'] {
9632
- inset-block-end: calc(var(--xh-menu-arrow-size, var(--xh-overlay-arrow-size)) / -2);
9633
- inset-inline-start: calc(50% - var(--xh-menu-arrow-size, var(--xh-overlay-arrow-size)) / 2);
10215
+ inset-block-end: calc(var(--xh-_menu-arrow-size) / -2);
10216
+ inset-inline-start: calc(var(--xh-_menu-arrow-x) - var(--xh-_menu-arrow-size) / 2);
9634
10217
  border-block-start: 0;
9635
10218
  border-inline-start: 0;
9636
10219
  }
9637
10220
 
9638
10221
  [data-scope='menu'][data-part='arrow'][data-placement^='right'] {
9639
- inset-inline-start: calc(var(--xh-menu-arrow-size, var(--xh-overlay-arrow-size)) / -2);
9640
- inset-block-start: calc(50% - var(--xh-menu-arrow-size, var(--xh-overlay-arrow-size)) / 2);
10222
+ inset-inline-start: calc(var(--xh-_menu-arrow-size) / -2);
10223
+ inset-block-start: calc(var(--xh-_menu-arrow-y) - var(--xh-_menu-arrow-size) / 2);
9641
10224
  border-block-start: 0;
9642
10225
  border-inline-end: 0;
9643
10226
  }
9644
10227
 
9645
10228
  [data-scope='menu'][data-part='arrow'][data-placement^='left'] {
9646
- inset-inline-end: calc(var(--xh-menu-arrow-size, var(--xh-overlay-arrow-size)) / -2);
9647
- inset-block-start: calc(50% - var(--xh-menu-arrow-size, var(--xh-overlay-arrow-size)) / 2);
10229
+ inset-inline-end: calc(var(--xh-_menu-arrow-size) / -2);
10230
+ inset-block-start: calc(var(--xh-_menu-arrow-y) - var(--xh-_menu-arrow-size) / 2);
9648
10231
  border-block-end: 0;
9649
10232
  border-inline-start: 0;
9650
10233
  }
@@ -9656,14 +10239,14 @@
9656
10239
  换档不必给每个部件各写一条选择器。菜单浮层在 positioner 里、也是 root 的后代,一并继承 */
9657
10240
  --xh-_menubar-trigger-py: var(--xh-space-1);
9658
10241
  --xh-_menubar-trigger-px: var(--xh-control-px-sm);
9659
- --xh-_menubar-item-py: var(--xh-space-2);
9660
- --xh-_menubar-item-px: var(--xh-control-px-sm);
10242
+ --xh-_menubar-item-py: var(--xh-space-1_5);
10243
+ --xh-_menubar-item-px: var(--xh-control-px-md);
9661
10244
  --xh-_menubar-font-size: var(--xh-text-body-size);
9662
10245
 
9663
- /* 高亮底随语气走:展开着的那张菜单的 trigger 与菜单里的高亮条目共用这一档。
9664
- 没写 data-tone 时 --xh-_tone-subtle-hover 没被声明,退回原来的中性底。
10246
+ /* 展开着的那张菜单的 trigger 走品牌淡底强档,随语气换色相;没写 data-tone 时退回品牌淡底。
10247
+ 悬停与键盘焦点恒中性灰轻档、不接语气槽——两档同色相的话哪张开着就认不出来了。
9665
10248
  聚焦环不在其列,它恒是 --xh-ring-focus */
9666
- --xh-_menubar-highlight-bg: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover));
10249
+ --xh-_menubar-active-bg: var(--xh-_tone-subtle, var(--xh-bg-brand-subtle));
9667
10250
 
9668
10251
  display: flex;
9669
10252
  align-items: center;
@@ -9685,21 +10268,21 @@
9685
10268
  }
9686
10269
 
9687
10270
  /* —— 尺寸 —— */
9688
- /* 缺省档写在上面的基础规则里。内边距的缺省档已经落在控件内边距尺的 sm 那一格,
9689
- 往下没有更小的一格,sm 档改取空间尺;lg 档回到控件尺的下一格 */
10271
+ /* 缺省档写在上面的基础规则里。trigger 的行内内缩缺省落在控件尺 sm 档、再往下取空间尺;
10272
+ 条目纵向走 space 档、行内内缩走 control-px 档(随密度轴),换档各挪一级 */
9690
10273
  [data-scope='menubar'][data-part='root'][data-size='sm'] {
9691
10274
  --xh-_menubar-trigger-py: var(--xh-space-0_5);
9692
10275
  --xh-_menubar-trigger-px: var(--xh-space-1_5);
9693
- --xh-_menubar-item-py: var(--xh-space-1_5);
9694
- --xh-_menubar-item-px: var(--xh-space-1_5);
10276
+ --xh-_menubar-item-py: var(--xh-space-1);
10277
+ --xh-_menubar-item-px: var(--xh-control-px-sm);
9695
10278
  --xh-_menubar-font-size: var(--xh-font-size-sm);
9696
10279
  }
9697
10280
 
9698
10281
  [data-scope='menubar'][data-part='root'][data-size='lg'] {
9699
10282
  --xh-_menubar-trigger-py: var(--xh-space-2);
9700
10283
  --xh-_menubar-trigger-px: var(--xh-control-px-md);
9701
- --xh-_menubar-item-py: var(--xh-space-3);
9702
- --xh-_menubar-item-px: var(--xh-control-px-md);
10284
+ --xh-_menubar-item-py: var(--xh-space-2);
10285
+ --xh-_menubar-item-px: var(--xh-control-px-lg);
9703
10286
  --xh-_menubar-font-size: var(--xh-font-size-lg);
9704
10287
  }
9705
10288
 
@@ -9729,15 +10312,23 @@
9729
10312
  white-space: nowrap;
9730
10313
  cursor: pointer;
9731
10314
  user-select: none;
10315
+ transition: background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
9732
10316
  }
9733
10317
 
9734
10318
  [data-scope='menubar'][data-part='trigger'][hidden] {
9735
10319
  display: none;
9736
10320
  }
9737
10321
 
9738
- /* 展开着的那一项与指针悬停共用一套底色:菜单栏里"哪一张开着"必须一眼看得出来 */
9739
- [data-scope='menubar'][data-part='trigger']:not([data-disabled]):is(:hover, [data-state='open']) {
9740
- background: var(--xh-menubar-trigger-bg-active, var(--xh-_menubar-highlight-bg));
10322
+ /* 指针悬停走中性灰轻档 */
10323
+ [data-scope='menubar'][data-part='trigger']:not([data-disabled]):hover {
10324
+ background: var(--xh-menubar-trigger-bg-hover, var(--xh-bg-subtle));
10325
+ }
10326
+
10327
+ /* 展开着的那一张是打开路径,走品牌淡底强档:菜单栏里"哪一张开着"必须一眼看得出来。
10328
+ 不加粗:横排的栏加粗会推挤相邻触发器。
10329
+ 必须排在轻档之后:指针回到它身上时同特指度靠书写顺序压过轻档 */
10330
+ [data-scope='menubar'][data-part='trigger']:not([data-disabled])[data-state='open'] {
10331
+ background: var(--xh-menubar-trigger-bg-active, var(--xh-_menubar-active-bg));
9741
10332
  }
9742
10333
 
9743
10334
  /* trigger 自带 Tab 位(整条菜单栏唯一的那个),必须画得出焦点 */
@@ -9840,10 +10431,13 @@
9840
10431
  border-radius: var(--xh-menubar-item-radius, var(--xh-shape-control));
9841
10432
  color: var(--xh-menubar-item-fg, var(--xh-fg-default));
9842
10433
  font-size: var(--xh-menubar-item-font-size, var(--xh-_menubar-font-size));
9843
- line-height: var(--xh-leading-none);
10434
+ line-height: var(--xh-menubar-item-leading, var(--xh-leading-normal));
9844
10435
  white-space: nowrap;
9845
10436
  cursor: pointer;
9846
10437
  user-select: none;
10438
+ transition:
10439
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
10440
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
9847
10441
  }
9848
10442
 
9849
10443
  /* 条目可以按需收起(按上下文过滤掉的命令),有 display 就得补这条 */
@@ -9851,11 +10445,11 @@
9851
10445
  display: none;
9852
10446
  }
9853
10447
 
9854
- /* 键盘高亮与指针高亮共用一套底色:条目本身承载焦点,不另画描边。
10448
+ /* 悬停与键盘焦点共用中性灰轻档:条目本身承载焦点,不另画描边。
9855
10449
  data-highlighted 跟的是连接层记的焦点条目,与 :focus 说的是同一件事,
9856
10450
  子部件够不着那个伪类,只能读这个标记 */
9857
10451
  [data-scope='menubar'][data-part='item']:not([data-disabled]):is(:hover, :focus, [data-highlighted]) {
9858
- background: var(--xh-menubar-item-bg-highlight, var(--xh-_menubar-highlight-bg));
10452
+ background: var(--xh-menubar-item-bg-hover, var(--xh-bg-subtle));
9859
10453
  }
9860
10454
 
9861
10455
  [data-scope='menubar'][data-part='item']:focus {
@@ -10200,18 +10794,18 @@
10200
10794
 
10201
10795
  /* 形态档只换这几个私有槽:输入框的底与描边,加减钮的底与描边 */
10202
10796
  --xh-_number-field-bg: var(--xh-bg-canvas);
10203
- --xh-_number-field-border: var(--xh-border-default);
10204
- --xh-_number-field-border-hover: var(--xh-border-strong);
10797
+ --xh-_number-field-border: var(--xh-border-control);
10798
+ --xh-_number-field-border-hover: var(--xh-border-control-hover);
10205
10799
  --xh-_number-field-trigger-bg: var(--xh-bg-subtle);
10206
10800
  --xh-_number-field-trigger-bg-hover: var(--xh-bg-subtle-hover);
10207
10801
  --xh-_number-field-trigger-bg-active: var(--xh-bg-subtle-active);
10208
- --xh-_number-field-trigger-border: var(--xh-border-default);
10209
- --xh-_number-field-trigger-border-hover: var(--xh-border-strong);
10802
+ --xh-_number-field-trigger-border: var(--xh-border-control);
10803
+ --xh-_number-field-trigger-border-hover: var(--xh-border-control-hover);
10210
10804
 
10211
10805
  /* 语气只落在聚焦这一处:环与聚焦态描边。框里的数字不归它管。
10212
10806
  没写 data-tone 时 --xh-_tone 没被声明,退回原来的聚焦环色与常态描边 */
10213
10807
  --xh-_number-field-ring: var(--xh-ring-focus);
10214
- --xh-_number-field-border-focus: var(--xh-number-field-input-border, var(--xh-_tone, var(--xh-border-default)));
10808
+ --xh-_number-field-border-focus: var(--xh-number-field-input-border, var(--xh-_tone, var(--xh-border-control)));
10215
10809
 
10216
10810
  display: inline-flex;
10217
10811
  flex-direction: column;
@@ -10246,14 +10840,14 @@
10246
10840
  输入框没有实心档:填满一个要往里打字的框,字与底就没法同时读 */
10247
10841
  [data-scope='number-field'][data-part='root'][data-variant='outline'] {
10248
10842
  --xh-_number-field-bg: var(--xh-bg-canvas);
10249
- --xh-_number-field-border: var(--xh-border-default);
10250
- --xh-_number-field-border-hover: var(--xh-border-strong);
10843
+ --xh-_number-field-border: var(--xh-border-control);
10844
+ --xh-_number-field-border-hover: var(--xh-border-control-hover);
10251
10845
  --xh-_number-field-trigger-bg: var(--xh-bg-subtle);
10252
10846
  --xh-_number-field-trigger-bg-hover: var(--xh-bg-subtle-hover);
10253
10847
  --xh-_number-field-trigger-bg-active: var(--xh-bg-subtle-active);
10254
- --xh-_number-field-trigger-border: var(--xh-border-default);
10255
- --xh-_number-field-trigger-border-hover: var(--xh-border-strong);
10256
- --xh-_number-field-border-focus: var(--xh-number-field-input-border, var(--xh-_tone, var(--xh-border-default)));
10848
+ --xh-_number-field-trigger-border: var(--xh-border-control);
10849
+ --xh-_number-field-trigger-border-hover: var(--xh-border-control-hover);
10850
+ --xh-_number-field-border-focus: var(--xh-number-field-input-border, var(--xh-_tone, var(--xh-border-control)));
10257
10851
  }
10258
10852
 
10259
10853
  /* 填充式:底色顶替描边表达边界。描边只退成透明而不去掉,1px 的位置留着,
@@ -10261,26 +10855,26 @@
10261
10855
  [data-scope='number-field'][data-part='root'][data-variant='subtle'] {
10262
10856
  --xh-_number-field-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
10263
10857
  --xh-_number-field-border: transparent;
10264
- --xh-_number-field-border-hover: var(--xh-_tone-border, var(--xh-border-default));
10858
+ --xh-_number-field-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
10265
10859
  --xh-_number-field-trigger-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
10266
10860
  --xh-_number-field-trigger-bg-hover: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover));
10267
10861
  --xh-_number-field-trigger-bg-active: var(--xh-_tone-subtle-active, var(--xh-bg-subtle-active));
10268
10862
  --xh-_number-field-trigger-border: transparent;
10269
- --xh-_number-field-trigger-border-hover: var(--xh-_tone-border, var(--xh-border-default));
10270
- --xh-_number-field-border-focus: var(--xh-_tone, var(--xh-border-default));
10863
+ --xh-_number-field-trigger-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
10864
+ --xh-_number-field-border-focus: var(--xh-_tone, var(--xh-border-control));
10271
10865
  }
10272
10866
 
10273
10867
  /* 无框:底与描边都透明,平时只看得见数字,悬停与聚焦才浮出边界 */
10274
10868
  [data-scope='number-field'][data-part='root'][data-variant='ghost'] {
10275
10869
  --xh-_number-field-bg: transparent;
10276
10870
  --xh-_number-field-border: transparent;
10277
- --xh-_number-field-border-hover: var(--xh-_tone-border, var(--xh-border-default));
10871
+ --xh-_number-field-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
10278
10872
  --xh-_number-field-trigger-bg: transparent;
10279
10873
  --xh-_number-field-trigger-bg-hover: var(--xh-_tone-subtle, var(--xh-bg-subtle-hover));
10280
10874
  --xh-_number-field-trigger-bg-active: var(--xh-_tone-subtle-active, var(--xh-bg-subtle-active));
10281
10875
  --xh-_number-field-trigger-border: transparent;
10282
- --xh-_number-field-trigger-border-hover: var(--xh-_tone-border, var(--xh-border-default));
10283
- --xh-_number-field-border-focus: var(--xh-_tone, var(--xh-border-default));
10876
+ --xh-_number-field-trigger-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
10877
+ --xh-_number-field-border-focus: var(--xh-_tone, var(--xh-border-control));
10284
10878
  }
10285
10879
 
10286
10880
  [data-scope='number-field'][data-part='label'] {
@@ -10563,8 +11157,8 @@
10563
11157
  --xh-_pin-input-label-font-size: var(--xh-text-label-size);
10564
11158
  /* 形态档只换这三个私有槽,缺省这一份就是描边式(描边 + 画布底) */
10565
11159
  --xh-_pin-input-box-bg: var(--xh-bg-canvas);
10566
- --xh-_pin-input-box-border: var(--xh-border-default);
10567
- --xh-_pin-input-box-border-hover: var(--xh-border-strong);
11160
+ --xh-_pin-input-box-border: var(--xh-border-control);
11161
+ --xh-_pin-input-box-border-hover: var(--xh-border-control-hover);
10568
11162
  /* 聚焦环与填满态的强调色随语气走。没写 data-tone 时 --xh-_tone 没被声明,退回原来的取值 */
10569
11163
  --xh-_pin-input-ring: var(--xh-ring-focus);
10570
11164
  --xh-_pin-input-accent: var(--xh-_tone, var(--xh-bg-brand));
@@ -10584,22 +11178,22 @@
10584
11178
  不做 solid:实心填充盖住了"这里能打字"这件事,输入框用不上它 */
10585
11179
  [data-scope='pin-input'][data-part='root'][data-variant='outline'] {
10586
11180
  --xh-_pin-input-box-bg: var(--xh-bg-canvas);
10587
- --xh-_pin-input-box-border: var(--xh-border-default);
10588
- --xh-_pin-input-box-border-hover: var(--xh-border-strong);
11181
+ --xh-_pin-input-box-border: var(--xh-border-control);
11182
+ --xh-_pin-input-box-border-hover: var(--xh-border-control-hover);
10589
11183
  }
10590
11184
 
10591
11185
  /* 填充式:底色说明这里是一格输入位,描边让位给底色 */
10592
11186
  [data-scope='pin-input'][data-part='root'][data-variant='subtle'] {
10593
11187
  --xh-_pin-input-box-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
10594
11188
  --xh-_pin-input-box-border: transparent;
10595
- --xh-_pin-input-box-border-hover: var(--xh-_tone-border, var(--xh-border-default));
11189
+ --xh-_pin-input-box-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
10596
11190
  }
10597
11191
 
10598
11192
  /* 无边框、透明底:静息时格子完全隐入背景,可见反馈全靠悬停描边与聚焦环 */
10599
11193
  [data-scope='pin-input'][data-part='root'][data-variant='ghost'] {
10600
11194
  --xh-_pin-input-box-bg: transparent;
10601
11195
  --xh-_pin-input-box-border: transparent;
10602
- --xh-_pin-input-box-border-hover: var(--xh-_tone-border, var(--xh-border-default));
11196
+ --xh-_pin-input-box-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
10603
11197
  }
10604
11198
 
10605
11199
  /* —— 尺寸 —— */
@@ -10822,40 +11416,44 @@
10822
11416
  }
10823
11417
 
10824
11418
  /* 箭头是旋转 45° 的方块,只保留朝向锚点那两条边的描边;
10825
- 引擎不产出箭头偏移,因此沿交叉轴固定居中 */
11419
+ 交叉轴落点由定位引擎经内联自定义属性写进来,没给时退回居中 */
10826
11420
  [data-scope='popover'][data-part='arrow'] {
11421
+ --xh-_popover-arrow-size: var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size));
11422
+ --xh-_popover-arrow-x: 50%;
11423
+ --xh-_popover-arrow-y: 50%;
11424
+
10827
11425
  position: absolute;
10828
- inline-size: var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size));
10829
- block-size: var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size));
11426
+ inline-size: var(--xh-_popover-arrow-size);
11427
+ block-size: var(--xh-_popover-arrow-size);
10830
11428
  background: var(--xh-popover-bg, var(--xh-bg-surface));
10831
11429
  border: var(--xh-stroke-thin) solid var(--xh-popover-border, var(--xh-border-default));
10832
11430
  rotate: 45deg;
10833
11431
  }
10834
11432
 
10835
11433
  [data-scope='popover'][data-part='arrow'][data-placement^='bottom'] {
10836
- inset-block-start: calc(var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size)) / -2);
10837
- inset-inline-start: calc(50% - var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size)) / 2);
11434
+ inset-block-start: calc(var(--xh-_popover-arrow-size) / -2);
11435
+ inset-inline-start: calc(var(--xh-_popover-arrow-x) - var(--xh-_popover-arrow-size) / 2);
10838
11436
  border-block-end: 0;
10839
11437
  border-inline-end: 0;
10840
11438
  }
10841
11439
 
10842
11440
  [data-scope='popover'][data-part='arrow'][data-placement^='top'] {
10843
- inset-block-end: calc(var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size)) / -2);
10844
- inset-inline-start: calc(50% - var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size)) / 2);
11441
+ inset-block-end: calc(var(--xh-_popover-arrow-size) / -2);
11442
+ inset-inline-start: calc(var(--xh-_popover-arrow-x) - var(--xh-_popover-arrow-size) / 2);
10845
11443
  border-block-start: 0;
10846
11444
  border-inline-start: 0;
10847
11445
  }
10848
11446
 
10849
11447
  [data-scope='popover'][data-part='arrow'][data-placement^='right'] {
10850
- inset-inline-start: calc(var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size)) / -2);
10851
- inset-block-start: calc(50% - var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size)) / 2);
11448
+ inset-inline-start: calc(var(--xh-_popover-arrow-size) / -2);
11449
+ inset-block-start: calc(var(--xh-_popover-arrow-y) - var(--xh-_popover-arrow-size) / 2);
10852
11450
  border-block-start: 0;
10853
11451
  border-inline-end: 0;
10854
11452
  }
10855
11453
 
10856
11454
  [data-scope='popover'][data-part='arrow'][data-placement^='left'] {
10857
- inset-inline-end: calc(var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size)) / -2);
10858
- inset-block-start: calc(50% - var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size)) / 2);
11455
+ inset-inline-end: calc(var(--xh-_popover-arrow-size) / -2);
11456
+ inset-block-start: calc(var(--xh-_popover-arrow-y) - var(--xh-_popover-arrow-size) / 2);
10859
11457
  border-block-end: 0;
10860
11458
  border-inline-start: 0;
10861
11459
  }
@@ -10888,7 +11486,7 @@
10888
11486
  }
10889
11487
 
10890
11488
  /* —— 尺寸 —— */
10891
- /* 只有厚度分档;中间档不写规则,缺省即中档 */
11489
+ /* 线形只有厚度分档;中间档不写规则,缺省即中档 */
10892
11490
  [data-scope='progress'][data-part='root'][data-size='sm'] {
10893
11491
  --xh-_thickness: var(--xh-progress-thickness, var(--xh-space-1));
10894
11492
  }
@@ -10897,6 +11495,73 @@
10897
11495
  --xh-_thickness: var(--xh-progress-thickness, var(--xh-space-2));
10898
11496
  }
10899
11497
 
11498
+ /* —— 环形 —— */
11499
+ /* 环画在固定的 100×100 viewBox 里,直径由这里的尺寸决定;
11500
+ 画面与环心文字叠在同一格上,文字因此恒在圆心 */
11501
+ [data-scope='progress'][data-part='root'][data-variant='circle'],
11502
+ [data-scope='progress'][data-part='root'][data-variant='dashboard'] {
11503
+ --xh-_diameter: var(--xh-progress-size, 7.5rem);
11504
+
11505
+ display: inline-grid;
11506
+ place-items: center;
11507
+ inline-size: var(--xh-_diameter);
11508
+ block-size: var(--xh-_diameter);
11509
+ }
11510
+
11511
+ [data-scope='progress'][data-part='root'][data-variant='circle'][data-size='sm'],
11512
+ [data-scope='progress'][data-part='root'][data-variant='dashboard'][data-size='sm'] {
11513
+ --xh-_diameter: var(--xh-progress-size, 5rem);
11514
+ }
11515
+
11516
+ [data-scope='progress'][data-part='root'][data-variant='circle'][data-size='lg'],
11517
+ [data-scope='progress'][data-part='root'][data-variant='dashboard'][data-size='lg'] {
11518
+ --xh-_diameter: var(--xh-progress-size, 10rem);
11519
+ }
11520
+
11521
+ [data-scope='progress'][data-part='canvas'],
11522
+ [data-scope='progress'][data-part='label'] {
11523
+ grid-area: 1 / 1;
11524
+ }
11525
+
11526
+ [data-scope='progress'][data-part='canvas'] {
11527
+ inline-size: 100%;
11528
+ block-size: 100%;
11529
+ }
11530
+
11531
+ /* 环心文字:占位不撑格子,长内容自己换行由作者定 */
11532
+ [data-scope='progress'][data-part='label'] {
11533
+ color: var(--xh-progress-label-fg, var(--xh-fg-default));
11534
+ font-size: var(--xh-progress-label-font-size, var(--xh-text-body-size));
11535
+ line-height: var(--xh-leading-none);
11536
+ text-align: center;
11537
+ }
11538
+
11539
+ /* 圆心、半径与那两段虚线都由连接层算好写进内联样式,这里只上色。
11540
+ 底槽不跟着语气换族:只有已完成的那段换色,才读得出"完成了多少" */
11541
+ [data-scope='progress'][data-part='track'][data-variant='circle'],
11542
+ [data-scope='progress'][data-part='track'][data-variant='dashboard'] {
11543
+ stroke: var(--xh-progress-track, var(--xh-bg-subtle-active));
11544
+ }
11545
+
11546
+ [data-scope='progress'][data-part='range'][data-variant='circle'],
11547
+ [data-scope='progress'][data-part='range'][data-variant='dashboard'] {
11548
+ stroke: var(--xh-progress-range, var(--xh-_tone, var(--xh-bg-brand)));
11549
+ stroke-linecap: var(--xh-progress-linecap, round);
11550
+ transition: stroke-dashoffset var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
11551
+ }
11552
+
11553
+ /* 零进度:圆角端点会在长度为 0 处画出一个圆点,收掉它 */
11554
+ [data-scope='progress'][data-part='range'][data-empty] {
11555
+ visibility: hidden;
11556
+ }
11557
+
11558
+ /* 关掉动效的用户不该被进度条的补间晃一下;这里只管本组件 */
11559
+ @media (prefers-reduced-motion: reduce) {
11560
+ [data-scope='progress'][data-part='range'] {
11561
+ transition-duration: 0.01ms;
11562
+ }
11563
+ }
11564
+
10900
11565
  /* styles/radio-group.css */
10901
11566
  [data-scope='radio-group'][data-part='root'] {
10902
11567
  /* 尺寸档只换这两个私有槽,标题与条目都从 root 继承它们,换档不必给每个部件各写一条选择器 */
@@ -10968,7 +11633,7 @@
10968
11633
  justify-content: center;
10969
11634
  inline-size: 16px;
10970
11635
  block-size: 16px;
10971
- border: var(--xh-stroke-thin) solid var(--xh-radio-group-indicator-border, var(--xh-border-default));
11636
+ border: var(--xh-stroke-thin) solid var(--xh-radio-group-indicator-border, var(--xh-border-control));
10972
11637
  border-radius: var(--xh-shape-pill);
10973
11638
  background: var(--xh-radio-group-indicator-bg, var(--xh-bg-canvas));
10974
11639
  transition: border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
@@ -11325,14 +11990,13 @@
11325
11990
 
11326
11991
  /* 形态档只换这三个槽:缺省即 outline(描边 + 画布底) */
11327
11992
  --xh-_select-trigger-bg: var(--xh-bg-canvas);
11328
- --xh-_select-trigger-border: var(--xh-border-default);
11329
- --xh-_select-trigger-border-hover: var(--xh-border-strong);
11993
+ --xh-_select-trigger-border: var(--xh-border-control);
11994
+ --xh-_select-trigger-border-hover: var(--xh-border-control-hover);
11330
11995
 
11331
- /* 强调色随语气走,只落在聚焦与选中上,正文文字色不动。
11332
- 没写 data-tone 时 --xh-_tone 没被声明,三处都退回原来的取值 */
11996
+ /* 聚焦环恒用固定环色;强调色随语气走,只落在勾选标记与选中文字上,正文文字色不动。
11997
+ 没写 data-tone 时 --xh-_tone 没被声明,两处都退回原来的取值 */
11333
11998
  --xh-_select-ring: var(--xh-ring-focus);
11334
11999
  --xh-_select-accent: var(--xh-_tone, var(--xh-bg-brand));
11335
- --xh-_select-item-highlight-bg: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover));
11336
12000
  --xh-_select-item-selected-fg: var(--xh-_tone-fg, var(--xh-fg-brand-strong));
11337
12001
 
11338
12002
  display: inline-flex;
@@ -11365,21 +12029,21 @@
11365
12029
  形态只决定描边与底色怎么用,用哪族颜色由 data-tone 决定 */
11366
12030
  [data-scope='select'][data-part='root'][data-variant='outline'] {
11367
12031
  --xh-_select-trigger-bg: var(--xh-bg-canvas);
11368
- --xh-_select-trigger-border: var(--xh-border-default);
11369
- --xh-_select-trigger-border-hover: var(--xh-border-strong);
12032
+ --xh-_select-trigger-border: var(--xh-border-control);
12033
+ --xh-_select-trigger-border-hover: var(--xh-border-control-hover);
11370
12034
  }
11371
12035
 
11372
12036
  /* 填充式:底色顶替描边,悬停时才浮出一道淡边 */
11373
12037
  [data-scope='select'][data-part='root'][data-variant='subtle'] {
11374
12038
  --xh-_select-trigger-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
11375
12039
  --xh-_select-trigger-border: transparent;
11376
- --xh-_select-trigger-border-hover: var(--xh-_tone-border, var(--xh-border-default));
12040
+ --xh-_select-trigger-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
11377
12041
  }
11378
12042
 
11379
12043
  [data-scope='select'][data-part='root'][data-variant='ghost'] {
11380
12044
  --xh-_select-trigger-bg: transparent;
11381
12045
  --xh-_select-trigger-border: transparent;
11382
- --xh-_select-trigger-border-hover: var(--xh-_tone-border, var(--xh-border-default));
12046
+ --xh-_select-trigger-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
11383
12047
  }
11384
12048
 
11385
12049
  [data-scope='select'][data-part='label'] {
@@ -11413,10 +12077,126 @@
11413
12077
  cursor: pointer;
11414
12078
  }
11415
12079
 
11416
- [data-scope='select'][data-part='trigger']:hover:not(:disabled) {
12080
+ /* hover 只在中性态换描边:invalid 正拿边框颜色说事,被 hover 盖掉那条信息就没了 */
12081
+ [data-scope='select'][data-part='trigger']:hover:not(:disabled, [data-invalid]) {
11417
12082
  border-color: var(--xh-select-trigger-border-hover, var(--xh-_select-trigger-border-hover));
11418
12083
  }
11419
12084
 
12085
+ [data-scope='select'][data-part='trigger'][data-invalid] {
12086
+ border-color: var(--xh-select-trigger-border-invalid, var(--xh-color-danger-500));
12087
+ }
12088
+
12089
+ /* 收纳容器:触发器与清空按钮的定位基准,触发器在里面占满一行 */
12090
+ [data-scope='select'][data-part='control'] {
12091
+ position: relative;
12092
+ display: flex;
12093
+ align-items: center;
12094
+ }
12095
+
12096
+ [data-scope='select'][data-part='control'] > [data-part='trigger'] {
12097
+ flex: 1;
12098
+ }
12099
+
12100
+ /* 清空按钮:trigger 的兄弟节点,幽灵小圆钮;没选中或禁用时连接层给 hidden */
12101
+ [data-scope='select'][data-part='clear-trigger'] {
12102
+ display: inline-grid;
12103
+ place-items: center;
12104
+ inline-size: var(--xh-select-clear-size, var(--xh-control-indicator-size));
12105
+ block-size: var(--xh-select-clear-size, var(--xh-control-indicator-size));
12106
+ padding: 0;
12107
+ border: none;
12108
+ border-radius: var(--xh-shape-pill);
12109
+ background: none;
12110
+ color: var(--xh-select-clear-fg, var(--xh-fg-subtle));
12111
+
12112
+ /* 字号行高钉死:继承页面字号会让行框高过按钮,字形被基线顶离正中 */
12113
+ font-size: var(--xh-select-clear-font-size, var(--xh-font-size-xs));
12114
+ line-height: var(--xh-leading-none);
12115
+ cursor: pointer;
12116
+ }
12117
+
12118
+ [data-scope='select'][data-part='clear-trigger']:hover {
12119
+ background: var(--xh-select-clear-bg-hover, var(--xh-bg-subtle-hover));
12120
+ color: var(--xh-select-clear-fg-hover, var(--xh-fg-default));
12121
+ }
12122
+
12123
+ [data-scope='select'][data-part='clear-trigger']:focus-visible {
12124
+ outline: var(--xh-ring-width) solid var(--xh-_select-ring);
12125
+ outline-offset: var(--xh-ring-offset);
12126
+ }
12127
+
12128
+ [data-scope='select'][data-part='clear-trigger'][hidden] {
12129
+ display: none;
12130
+ }
12131
+
12132
+ /* 进了 control 就嵌进触发器右端(与展开指示符同位),纵向钉在正中;平时不占位也不接指针 */
12133
+ [data-scope='select'][data-part='control'] [data-part='clear-trigger'] {
12134
+ position: absolute;
12135
+ inset-block-start: 50%;
12136
+ inset-inline-end: var(--xh-select-trigger-px, var(--xh-_select-px));
12137
+ translate: 0 -50%;
12138
+ opacity: 0;
12139
+ pointer-events: none;
12140
+ }
12141
+
12142
+ /* 悬停或键盘聚到清空钮上时显形 */
12143
+ [data-scope='select'][data-part='control']:hover [data-part='clear-trigger']:not([hidden]),
12144
+ [data-scope='select'][data-part='control'] [data-part='clear-trigger']:focus-visible {
12145
+ opacity: 1;
12146
+ pointer-events: auto;
12147
+ }
12148
+
12149
+ /* 清空钮显形期间展开指示符让位,两个图标不叠在一起 */
12150
+ [data-scope='select'][data-part='control']:hover:has([data-part='clear-trigger']:not([hidden])) [data-part='indicator'],
12151
+ [data-scope='select'][data-part='control']:has([data-part='clear-trigger']:focus-visible) [data-part='indicator'] {
12152
+ opacity: 0;
12153
+ }
12154
+
12155
+ /* 多选标签:淡底小药丸;放触发器里是纯展示,放外面配 tag-remove 可删 */
12156
+ [data-scope='select'][data-part='tag'] {
12157
+ display: inline-flex;
12158
+ align-items: center;
12159
+ gap: var(--xh-select-tag-gap, var(--xh-space-1));
12160
+ padding-block: 0;
12161
+ padding-inline: var(--xh-select-tag-px, var(--xh-space-2));
12162
+ border-radius: var(--xh-select-tag-radius, var(--xh-shape-pill));
12163
+ background: var(--xh-select-tag-bg, var(--xh-bg-subtle));
12164
+ color: var(--xh-select-tag-fg, var(--xh-fg-default));
12165
+ font-size: var(--xh-select-tag-font-size, var(--xh-font-size-xs));
12166
+ line-height: var(--xh-leading-normal);
12167
+ white-space: nowrap;
12168
+ }
12169
+
12170
+ [data-scope='select'][data-part='tag'][data-disabled] {
12171
+ color: var(--xh-fg-disabled);
12172
+ }
12173
+
12174
+ [data-scope='select'][data-part='tag-remove'] {
12175
+ display: inline-grid;
12176
+ place-items: center;
12177
+ padding: 0;
12178
+ border: none;
12179
+ border-radius: var(--xh-shape-pill);
12180
+ background: none;
12181
+ color: var(--xh-select-tag-remove-fg, var(--xh-fg-subtle));
12182
+ font-size: inherit;
12183
+ cursor: pointer;
12184
+ }
12185
+
12186
+ [data-scope='select'][data-part='tag-remove']:hover {
12187
+ color: var(--xh-select-tag-remove-fg-hover, var(--xh-fg-default));
12188
+ }
12189
+
12190
+ [data-scope='select'][data-part='tag-remove']:focus-visible {
12191
+ outline: var(--xh-ring-width) solid var(--xh-_select-ring);
12192
+ outline-offset: var(--xh-ring-offset);
12193
+ }
12194
+
12195
+ [data-scope='select'][data-part='tag-remove'][data-disabled] {
12196
+ color: var(--xh-fg-disabled);
12197
+ cursor: not-allowed;
12198
+ }
12199
+
11420
12200
  [data-scope='select'][data-part='trigger']:focus-visible {
11421
12201
  outline: var(--xh-ring-width) solid var(--xh-_select-ring);
11422
12202
  outline-offset: var(--xh-ring-offset);
@@ -11496,8 +12276,14 @@
11496
12276
  display: none;
11497
12277
  }
11498
12278
 
11499
- /* 容器只是焦点域兜底落点,落焦不画环:高亮永远长在条目上 */
11500
- [data-scope='select'][data-part='content']:focus-visible {
12279
+ /* 焦点落在容器上只有一种情形:指针打开、无选中值、还没有任何条目当锚点。
12280
+ 那一刻它认领着 Tab 位,环画在浮层内沿;有锚点时环长在条目上,容器不再画 */
12281
+ [data-scope='select'][data-part='content'][tabindex='0']:focus-visible {
12282
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
12283
+ outline-offset: calc(-1 * var(--xh-ring-width));
12284
+ }
12285
+
12286
+ [data-scope='select'][data-part='content']:not([tabindex='0']):focus-visible {
11501
12287
  outline: none;
11502
12288
  }
11503
12289
 
@@ -11509,8 +12295,8 @@
11509
12295
  display: flex;
11510
12296
  align-items: center;
11511
12297
  gap: var(--xh-select-item-gap, var(--xh-_select-gap));
11512
- padding-block: var(--xh-select-item-py, var(--xh-space-1));
11513
- padding-inline: var(--xh-select-item-px, var(--xh-control-px-sm));
12298
+ padding-block: var(--xh-select-item-py, var(--xh-space-1_5));
12299
+ padding-inline: var(--xh-select-item-px, var(--xh-control-px-md));
11514
12300
  border-radius: var(--xh-select-item-radius, var(--xh-shape-control));
11515
12301
  color: var(--xh-select-item-fg, var(--xh-fg-default));
11516
12302
  font-size: var(--xh-select-item-font-size, var(--xh-_select-font-size));
@@ -11519,11 +12305,14 @@
11519
12305
  white-space: nowrap;
11520
12306
  cursor: pointer;
11521
12307
  user-select: none;
12308
+ transition:
12309
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
12310
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
11522
12311
  }
11523
12312
 
11524
- /* 键盘高亮与指针高亮共用一套底色:条目本身承载焦点,不另画描边 */
12313
+ /* 悬停与键盘锚点共用中性灰轻档:条目本身承载焦点,不另画描边 */
11525
12314
  [data-scope='select'][data-part='item']:not([data-disabled]):is(:hover, [data-highlighted]) {
11526
- background: var(--xh-select-item-bg-highlight, var(--xh-_select-item-highlight-bg));
12315
+ background: var(--xh-select-item-bg-hover, var(--xh-bg-subtle));
11527
12316
  }
11528
12317
 
11529
12318
  /* 选中只换文字色与字重,配合行尾的 ✓ 标记;底色整条通道留给高亮,
@@ -11533,7 +12322,14 @@
11533
12322
  font-weight: var(--xh-select-item-font-weight-selected, var(--xh-font-weight-medium));
11534
12323
  }
11535
12324
 
11536
- [data-scope='select'][data-part='item']:focus {
12325
+ /* 键盘走到条目上要画环:轻档底色对白底只有 1.1:1,够不上焦点指示器的对比度要求。
12326
+ 指针悬停走的是程序化聚焦,鼠标模态下 :focus-visible 不匹配,不会误画 */
12327
+ [data-scope='select'][data-part='item']:focus-visible {
12328
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
12329
+ outline-offset: calc(-1 * var(--xh-ring-width));
12330
+ }
12331
+
12332
+ [data-scope='select'][data-part='item']:focus:not(:focus-visible) {
11537
12333
  outline: none;
11538
12334
  }
11539
12335
 
@@ -11594,6 +12390,169 @@
11594
12390
  block-size: 100%;
11595
12391
  }
11596
12392
 
12393
+ /* styles/side-nav.css */
12394
+ [data-scope='side-nav'][data-part='root'] {
12395
+ inline-size: var(--xh-side-nav-w, 240px);
12396
+ display: flex;
12397
+ flex-direction: column;
12398
+ gap: var(--xh-side-nav-gap, var(--xh-space-1));
12399
+ padding: var(--xh-side-nav-p, var(--xh-space-2));
12400
+ color: var(--xh-side-nav-fg, var(--xh-fg-default));
12401
+ }
12402
+
12403
+ /* 折叠成图标栏:整栏收窄,文字随行宽被裁掉 */
12404
+ [data-scope='side-nav'][data-part='root'][data-collapsed] {
12405
+ inline-size: var(--xh-side-nav-collapsed-w, 56px);
12406
+ }
12407
+
12408
+ [data-scope='side-nav'][data-part='list'],
12409
+ [data-scope='side-nav'][data-part='branch-content'] {
12410
+ display: flex;
12411
+ flex-direction: column;
12412
+ gap: var(--xh-side-nav-gap, var(--xh-space-1));
12413
+ margin: 0;
12414
+ padding: 0;
12415
+ list-style: none;
12416
+ }
12417
+
12418
+ /* 子层靠嵌套缩进,层级越深自然越靠里 */
12419
+ [data-scope='side-nav'][data-part='branch-content'] {
12420
+ padding-inline-start: var(--xh-side-nav-indent, var(--xh-space-4));
12421
+ }
12422
+
12423
+ [data-scope='side-nav'][data-part='branch-content'][hidden] {
12424
+ display: none;
12425
+ }
12426
+
12427
+ [data-scope='side-nav'][data-part='group'] {
12428
+ display: flex;
12429
+ flex-direction: column;
12430
+ gap: var(--xh-side-nav-gap, var(--xh-space-1));
12431
+ list-style: none;
12432
+ }
12433
+
12434
+ [data-scope='side-nav'][data-part='group-label'] {
12435
+ padding-block: var(--xh-space-1);
12436
+ padding-inline: var(--xh-control-px-md);
12437
+ font-size: var(--xh-font-size-xs);
12438
+ color: var(--xh-fg-subtle);
12439
+ user-select: none;
12440
+ }
12441
+
12442
+ /* 折叠时分组标题没有落点,藏掉 */
12443
+ [data-scope='side-nav'][data-part='group-label'][data-collapsed] {
12444
+ display: none;
12445
+ }
12446
+
12447
+ [data-scope='side-nav'][data-part='branch'] {
12448
+ display: flex;
12449
+ flex-direction: column;
12450
+ gap: var(--xh-side-nav-gap, var(--xh-space-1));
12451
+ list-style: none;
12452
+ }
12453
+
12454
+ /* —— 行:分支按钮与链接同一副长相 —— */
12455
+ [data-scope='side-nav'][data-part='branch-trigger'],
12456
+ [data-scope='side-nav'][data-part='link'] {
12457
+ display: flex;
12458
+ align-items: center;
12459
+ gap: var(--xh-control-gap-md);
12460
+ inline-size: 100%;
12461
+ min-block-size: var(--xh-control-h-md);
12462
+ padding-inline: var(--xh-control-px-md);
12463
+ border: none;
12464
+ border-radius: var(--xh-shape-control);
12465
+ background: none;
12466
+ color: inherit;
12467
+ font-size: var(--xh-text-body-size);
12468
+ text-align: start;
12469
+ text-decoration: none;
12470
+ cursor: pointer;
12471
+ }
12472
+
12473
+ [data-scope='side-nav'][data-part='branch-trigger']:hover,
12474
+ [data-scope='side-nav'][data-part='link']:hover {
12475
+ background: var(--xh-bg-subtle-hover);
12476
+ }
12477
+
12478
+ [data-scope='side-nav'][data-part='branch-trigger']:focus-visible,
12479
+ [data-scope='side-nav'][data-part='link']:focus-visible {
12480
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
12481
+ outline-offset: var(--xh-ring-offset);
12482
+ }
12483
+
12484
+ /* 选中的那条:品牌淡底 + 强调字 */
12485
+ [data-scope='side-nav'][data-part='link'][data-selected] {
12486
+ background: var(--xh-bg-brand-subtle);
12487
+ color: var(--xh-fg-brand);
12488
+ font-weight: var(--xh-font-weight-medium);
12489
+ }
12490
+
12491
+ /* 选中项的祖先枝:只染字色,一路指到当前页 */
12492
+ [data-scope='side-nav'][data-part='branch-trigger'][data-active] {
12493
+ color: var(--xh-fg-brand);
12494
+ }
12495
+
12496
+ [data-scope='side-nav'][data-part='branch-trigger'][data-disabled],
12497
+ [data-scope='side-nav'][data-part='link'][data-disabled] {
12498
+ color: var(--xh-fg-disabled);
12499
+ cursor: not-allowed;
12500
+ }
12501
+
12502
+ [data-scope='side-nav'][data-part='branch-text'],
12503
+ [data-scope='side-nav'][data-part='link-text'] {
12504
+ flex: 1;
12505
+ min-inline-size: 0;
12506
+ overflow: hidden;
12507
+ text-overflow: ellipsis;
12508
+ white-space: nowrap;
12509
+ }
12510
+
12511
+ /* 折叠成图标栏:文字与展开指示整个藏掉、行内容居中,不裁半个字。
12512
+ 弹出面板里的行不在此列——面板是正常宽度的浮层,文字照常显示 */
12513
+ [data-scope='side-nav'][data-part='root'][data-collapsed] [data-part='branch-text']:not([data-popout] *),
12514
+ [data-scope='side-nav'][data-part='root'][data-collapsed] [data-part='link-text']:not([data-popout] *),
12515
+ [data-scope='side-nav'][data-part='root'][data-collapsed] [data-part='branch-indicator']:not([data-popout] *) {
12516
+ display: none;
12517
+ }
12518
+
12519
+ [data-scope='side-nav'][data-part='root'][data-collapsed] [data-part='branch-trigger']:not([data-popout] *),
12520
+ [data-scope='side-nav'][data-part='root'][data-collapsed] [data-part='link']:not([data-popout] *) {
12521
+ justify-content: center;
12522
+ padding-inline: 0;
12523
+ }
12524
+
12525
+ /* 折叠态的弹出面板:贴着触发按钮旁侧的浮层卡片,坐标由连接层写在行内样式上。
12526
+ 面板自己不吃子层缩进(padding 整圈盖掉),面板内的嵌套子层照常缩进 */
12527
+ [data-scope='side-nav'][data-part='branch-content'][data-popout] {
12528
+ z-index: var(--xh-side-nav-popout-layer, var(--xh-layer-popover));
12529
+ min-inline-size: var(--xh-side-nav-popout-min-w, var(--xh-overlay-menu-min-w));
12530
+ max-inline-size: var(--xh-side-nav-popout-max-w, var(--xh-overlay-max-w));
12531
+ padding: var(--xh-side-nav-popout-p, var(--xh-space-1));
12532
+ border: var(--xh-stroke-thin) solid var(--xh-side-nav-popout-border, var(--xh-border-default));
12533
+ border-radius: var(--xh-side-nav-popout-radius, var(--xh-shape-surface));
12534
+ background: var(--xh-side-nav-popout-bg, var(--xh-bg-surface));
12535
+ box-shadow: var(--xh-side-nav-popout-shadow, var(--xh-elevation-2));
12536
+ pointer-events: auto;
12537
+ }
12538
+
12539
+ [data-scope='side-nav'][data-part='branch-indicator'] {
12540
+ margin-inline-start: auto;
12541
+ display: inline-flex;
12542
+ flex: none;
12543
+ transition: rotate var(--xh-duration-fast) var(--xh-ease-standard);
12544
+ }
12545
+
12546
+ [data-scope='side-nav'][data-part='branch-indicator'][data-state='open'] {
12547
+ rotate: 90deg;
12548
+ }
12549
+
12550
+ @media (prefers-reduced-motion: reduce) {
12551
+ [data-scope='side-nav'][data-part='branch-indicator'] {
12552
+ transition: none;
12553
+ }
12554
+ }
12555
+
11597
12556
  /* styles/slider.css */
11598
12557
  /* 定位分工:已选区间与拇指在**主轴**上的起点与长度,由连接层每帧写进内联样式——
11599
12558
  水平是 inset-inline-start / inline-size,竖直是 inset-block-end / block-size。
@@ -11782,6 +12741,61 @@
11782
12741
  --xh-_track-thickness: var(--xh-slider-track-thickness, var(--xh-space-2));
11783
12742
  }
11784
12743
 
12744
+ /* —— 刻度:圆点钉在轨道上、文案排在轨道外侧,坐标由连接层按百分比内联给出 —— */
12745
+ [data-scope='slider'][data-part='marks'] {
12746
+ position: absolute;
12747
+ inset: 0;
12748
+ pointer-events: none;
12749
+ }
12750
+
12751
+ [data-scope='slider'][data-part='mark'] {
12752
+ position: absolute;
12753
+ inset-block-start: 50%;
12754
+ inline-size: var(--xh-slider-mark-size, var(--xh-space-1));
12755
+ block-size: var(--xh-slider-mark-size, var(--xh-space-1));
12756
+ border-radius: var(--xh-shape-pill);
12757
+ background: var(--xh-slider-mark-bg, var(--xh-border-strong));
12758
+ translate: -50% -50%;
12759
+ }
12760
+
12761
+ /* 落进已选区间的刻度点跟 range 同色 */
12762
+ [data-scope='slider'][data-part='mark'][data-active] {
12763
+ background: var(--xh-slider-mark-bg-active, var(--xh-_tone, var(--xh-bg-brand)));
12764
+ }
12765
+
12766
+ [data-scope='slider'][data-part='mark-label'] {
12767
+ position: absolute;
12768
+ inset-block-start: 100%;
12769
+ margin-block-start: var(--xh-slider-mark-label-gap, var(--xh-space-1));
12770
+ color: var(--xh-slider-mark-label-fg, var(--xh-fg-subtle));
12771
+ font-size: var(--xh-slider-mark-label-font-size, var(--xh-font-size-xs));
12772
+ line-height: var(--xh-leading-none);
12773
+ white-space: nowrap;
12774
+ translate: -50% 0;
12775
+ cursor: pointer;
12776
+ pointer-events: auto;
12777
+ user-select: none;
12778
+ }
12779
+
12780
+ [data-scope='slider'][data-part='mark-label'][data-active] {
12781
+ color: var(--xh-slider-mark-label-fg-active, var(--xh-fg-default));
12782
+ }
12783
+
12784
+ /* 竖直轨道:点与文案换轴,坐标由连接层写在 block 轴上 */
12785
+ [data-scope='slider'][data-part='root'][data-orientation='vertical'] [data-part='mark'] {
12786
+ inset-block-start: auto;
12787
+ inset-inline-start: 50%;
12788
+ translate: -50% 50%;
12789
+ }
12790
+
12791
+ [data-scope='slider'][data-part='root'][data-orientation='vertical'] [data-part='mark-label'] {
12792
+ inset-block-start: auto;
12793
+ inset-inline-start: 100%;
12794
+ margin-block-start: 0;
12795
+ margin-inline-start: var(--xh-slider-mark-label-gap, var(--xh-space-1));
12796
+ translate: 0 50%;
12797
+ }
12798
+
11785
12799
  /* display 会盖掉 UA 的 [hidden]{display:none},凡是被赋了 display 的角色节点都要显式让位 */
11786
12800
  [data-scope='slider'][data-part='root'][hidden],
11787
12801
  [data-scope='slider'][data-part='control'][hidden] {
@@ -12096,8 +13110,8 @@
12096
13110
  }
12097
13111
 
12098
13112
  [data-scope='steps'][data-part='item'][data-disabled] [data-scope='steps'][data-part='indicator'] {
12099
- border-color: var(--xh-fg-disabled);
12100
- color: var(--xh-fg-disabled);
13113
+ border-color: var(--xh-steps-indicator-border-disabled, var(--xh-fg-disabled));
13114
+ color: var(--xh-steps-indicator-fg-disabled, var(--xh-fg-disabled));
12101
13115
  }
12102
13116
 
12103
13117
  /* 标题与说明都在右列:不给 display,网格布局会替它们块级化,
@@ -12231,6 +13245,33 @@
12231
13245
  translate: var(--xh-_switch-thumb-size) 0;
12232
13246
  }
12233
13247
 
13248
+ /* 转圈的关键帧定义在本文件里:引用别处文件的动画名时那份文件不一定在场,动画会静默不跑 */
13249
+ @keyframes xh-switch-rotate {
13250
+ to {
13251
+ transform: rotate(1turn);
13252
+ }
13253
+ }
13254
+
13255
+ /* 提交中:指针挂起、滑块里转圈;不是禁用,焦点与对比度都保持 */
13256
+ [data-scope='switch'][data-part='root'][data-loading] {
13257
+ cursor: wait;
13258
+ }
13259
+
13260
+ [data-scope='switch'][data-part='thumb'][data-loading] {
13261
+ display: grid;
13262
+ place-items: center;
13263
+ }
13264
+
13265
+ [data-scope='switch'][data-part='thumb'][data-loading]::after {
13266
+ content: '';
13267
+ inline-size: 60%;
13268
+ block-size: 60%;
13269
+ border: var(--xh-stroke-thin) solid transparent;
13270
+ border-block-start-color: var(--xh-switch-loading-fg, var(--xh-bg-brand));
13271
+ border-radius: var(--xh-shape-pill);
13272
+ animation: xh-switch-rotate var(--xh-switch-loading-duration, calc(var(--xh-duration-slow) * 2)) linear infinite;
13273
+ }
13274
+
12234
13275
  /* styles/table.css */
12235
13276
  /* 加载态自己冒出来,用户没按任何键:一段极轻的呼吸就够,别抢走正在读表的注意力 */
12236
13277
  @keyframes xh-table-loading-pulse {
@@ -12366,10 +13407,10 @@
12366
13407
  border-block-end: none;
12367
13408
  }
12368
13409
 
12369
- /* 键盘高亮与指针高亮共用一套底色。data-highlighted 跟的是连接层记的焦点行,
13410
+ /* 悬停与键盘锚点共用中性灰轻档。data-highlighted 跟的是连接层记的焦点行,
12370
13411
  与选中互相独立:可以停在一个没被选中的行上 */
12371
13412
  [data-scope='table'][data-part='body'] > [data-scope='table'][data-part='row']:not([data-disabled]):is(:hover, [data-highlighted]) {
12372
- background: var(--xh-table-row-bg-highlight, var(--xh-bg-subtle-hover));
13413
+ background: var(--xh-table-row-bg-hover, var(--xh-bg-subtle));
12373
13414
  }
12374
13415
 
12375
13416
  /* 选中比高亮更"实":它表达的是数据状态,翻页回来还在 */
@@ -12454,14 +13495,14 @@
12454
13495
  切换选中不引起行宽抖动 */
12455
13496
  [data-scope='table'][data-part='select-all-trigger'],
12456
13497
  [data-scope='table'][data-part='row-select-trigger'] {
12457
- border: var(--xh-stroke-thin) solid var(--xh-table-trigger-border, var(--xh-border-strong));
13498
+ border: var(--xh-stroke-thin) solid var(--xh-table-trigger-border, var(--xh-border-control));
12458
13499
  color: var(--xh-table-trigger-fg, var(--xh-fg-on-brand));
12459
13500
  visibility: visible;
12460
13501
  }
12461
13502
 
12462
13503
  [data-scope='table'][data-part='select-all-trigger']:is([data-state='all'], [data-state='some']),
12463
13504
  [data-scope='table'][data-part='row-select-trigger'][data-selected] {
12464
- border-color: var(--xh-table-trigger-bg-checked, var(--xh-bg-brand));
13505
+ border-color: var(--xh-table-trigger-border-checked, var(--xh-table-trigger-bg-checked, var(--xh-bg-brand)));
12465
13506
  background: var(--xh-table-trigger-bg-checked, var(--xh-bg-brand));
12466
13507
  }
12467
13508
 
@@ -12610,7 +13651,7 @@
12610
13651
  }
12611
13652
 
12612
13653
  /* 两个状态节点常挂,只在表体为空时才显形 */
12613
- [data-scope='table'][data-part='empty-state'],
13654
+ [data-scope='table'][data-part='empty'],
12614
13655
  [data-scope='table'][data-part='loading-state'] {
12615
13656
  display: flex;
12616
13657
  flex-direction: column;
@@ -12624,7 +13665,7 @@
12624
13665
  text-align: center;
12625
13666
  }
12626
13667
 
12627
- [data-scope='table'][data-part='empty-state'][hidden],
13668
+ [data-scope='table'][data-part='empty'][hidden],
12628
13669
  [data-scope='table'][data-part='loading-state'][hidden] {
12629
13670
  display: none;
12630
13671
  }
@@ -12804,8 +13845,8 @@
12804
13845
  --xh-_tags-input-label-font-size: var(--xh-text-label-size);
12805
13846
  /* 形态档只换这三个私有槽,缺省这一份就是描边式(描边 + 画布底) */
12806
13847
  --xh-_tags-input-control-bg: var(--xh-bg-canvas);
12807
- --xh-_tags-input-control-border: var(--xh-border-default);
12808
- --xh-_tags-input-control-border-hover: var(--xh-border-strong);
13848
+ --xh-_tags-input-control-border: var(--xh-border-control);
13849
+ --xh-_tags-input-control-border-hover: var(--xh-border-control-hover);
12809
13850
  /* 聚焦环与标签块的强调色随语气走。没写 data-tone 时 --xh-_tone 没被声明,退回原来的取值 */
12810
13851
  --xh-_tags-input-ring: var(--xh-ring-focus);
12811
13852
  --xh-_tags-input-accent: var(--xh-_tone, var(--xh-bg-brand));
@@ -12827,22 +13868,22 @@
12827
13868
  不做 solid:实心填充盖住了"这里能打字"这件事,输入框用不上它 */
12828
13869
  [data-scope='tags-input'][data-part='root'][data-variant='outline'] {
12829
13870
  --xh-_tags-input-control-bg: var(--xh-bg-canvas);
12830
- --xh-_tags-input-control-border: var(--xh-border-default);
12831
- --xh-_tags-input-control-border-hover: var(--xh-border-strong);
13871
+ --xh-_tags-input-control-border: var(--xh-border-control);
13872
+ --xh-_tags-input-control-border-hover: var(--xh-border-control-hover);
12832
13873
  }
12833
13874
 
12834
13875
  /* 填充式:底色圈出可输入的范围,描边让位给底色 */
12835
13876
  [data-scope='tags-input'][data-part='root'][data-variant='subtle'] {
12836
13877
  --xh-_tags-input-control-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
12837
13878
  --xh-_tags-input-control-border: transparent;
12838
- --xh-_tags-input-control-border-hover: var(--xh-_tone-border, var(--xh-border-default));
13879
+ --xh-_tags-input-control-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
12839
13880
  }
12840
13881
 
12841
13882
  /* 无边框、透明底:静息时框完全隐入背景,可见反馈全靠悬停描边与聚焦环 */
12842
13883
  [data-scope='tags-input'][data-part='root'][data-variant='ghost'] {
12843
13884
  --xh-_tags-input-control-bg: transparent;
12844
13885
  --xh-_tags-input-control-border: transparent;
12845
- --xh-_tags-input-control-border-hover: var(--xh-_tone-border, var(--xh-border-default));
13886
+ --xh-_tags-input-control-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
12846
13887
  }
12847
13888
 
12848
13889
  /* —— 尺寸 —— */
@@ -13082,7 +14123,7 @@
13082
14123
  max-inline-size: 100%;
13083
14124
  padding-block: var(--xh-tags-input-item-py, var(--xh-space-1));
13084
14125
  padding-inline: var(--xh-tags-input-item-px, var(--xh-space-2));
13085
- border: var(--xh-stroke-thin) solid var(--xh-tags-input-item-input-border, var(--xh-border-strong));
14126
+ border: var(--xh-stroke-thin) solid var(--xh-tags-input-item-input-border, var(--xh-border-control));
13086
14127
  border-radius: var(--xh-tags-input-item-radius, var(--xh-shape-pill));
13087
14128
  background: var(--xh-tags-input-item-input-bg, var(--xh-bg-canvas));
13088
14129
  color: var(--xh-tags-input-item-input-fg, var(--xh-fg-default));
@@ -13165,13 +14206,13 @@
13165
14206
 
13166
14207
  /* 形态档只换这三个私有槽:底、常态描边、悬停描边 */
13167
14208
  --xh-_text-field-bg: var(--xh-bg-canvas);
13168
- --xh-_text-field-border: var(--xh-border-default);
13169
- --xh-_text-field-border-hover: var(--xh-border-strong);
14209
+ --xh-_text-field-border: var(--xh-border-control);
14210
+ --xh-_text-field-border-hover: var(--xh-border-control-hover);
13170
14211
 
13171
14212
  /* 语气只落在聚焦这一处:环与聚焦态描边。正文颜色不归它管。
13172
14213
  没写 data-tone 时 --xh-_tone 没被声明,退回原来的聚焦环色与常态描边 */
13173
14214
  --xh-_text-field-ring: var(--xh-ring-focus);
13174
- --xh-_text-field-border-focus: var(--xh-text-field-input-border, var(--xh-_tone, var(--xh-border-default)));
14215
+ --xh-_text-field-border-focus: var(--xh-text-field-input-border, var(--xh-_tone, var(--xh-border-control)));
13175
14216
 
13176
14217
  display: inline-flex;
13177
14218
  flex-direction: column;
@@ -13206,9 +14247,9 @@
13206
14247
  输入框没有实心档:填满一个要往里打字的框,字与底就没法同时读 */
13207
14248
  [data-scope='text-field'][data-part='root'][data-variant='outline'] {
13208
14249
  --xh-_text-field-bg: var(--xh-bg-canvas);
13209
- --xh-_text-field-border: var(--xh-border-default);
13210
- --xh-_text-field-border-hover: var(--xh-border-strong);
13211
- --xh-_text-field-border-focus: var(--xh-text-field-input-border, var(--xh-_tone, var(--xh-border-default)));
14250
+ --xh-_text-field-border: var(--xh-border-control);
14251
+ --xh-_text-field-border-hover: var(--xh-border-control-hover);
14252
+ --xh-_text-field-border-focus: var(--xh-text-field-input-border, var(--xh-_tone, var(--xh-border-control)));
13212
14253
  }
13213
14254
 
13214
14255
  /* 填充式:底色顶替描边表达边界。描边只退成透明而不去掉,1px 的位置留着,
@@ -13216,16 +14257,16 @@
13216
14257
  [data-scope='text-field'][data-part='root'][data-variant='subtle'] {
13217
14258
  --xh-_text-field-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
13218
14259
  --xh-_text-field-border: transparent;
13219
- --xh-_text-field-border-hover: var(--xh-_tone-border, var(--xh-border-default));
13220
- --xh-_text-field-border-focus: var(--xh-_tone, var(--xh-border-default));
14260
+ --xh-_text-field-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
14261
+ --xh-_text-field-border-focus: var(--xh-_tone, var(--xh-border-control));
13221
14262
  }
13222
14263
 
13223
14264
  /* 无框:底与描边都透明,平时只看得见字,悬停与聚焦才浮出边界 */
13224
14265
  [data-scope='text-field'][data-part='root'][data-variant='ghost'] {
13225
14266
  --xh-_text-field-bg: transparent;
13226
14267
  --xh-_text-field-border: transparent;
13227
- --xh-_text-field-border-hover: var(--xh-_tone-border, var(--xh-border-default));
13228
- --xh-_text-field-border-focus: var(--xh-_tone, var(--xh-border-default));
14268
+ --xh-_text-field-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
14269
+ --xh-_text-field-border-focus: var(--xh-_tone, var(--xh-border-control));
13229
14270
  }
13230
14271
 
13231
14272
  [data-scope='text-field'][data-part='label'] {
@@ -13254,6 +14295,19 @@
13254
14295
  transition: border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
13255
14296
  }
13256
14297
 
14298
+ /* 多行宿主:定高换成行数撑高,纵向留出内边距;autoSize 接管高度后拉柄与滚动都收掉 */
14299
+ [data-scope='text-field'][data-part='input'][data-multiline] {
14300
+ block-size: auto;
14301
+ min-block-size: var(--xh-text-field-input-h, var(--xh-_text-field-h));
14302
+ padding-block: var(--xh-text-field-textarea-py, var(--xh-space-2));
14303
+ resize: vertical;
14304
+ }
14305
+
14306
+ [data-scope='text-field'][data-part='input'][data-multiline][data-autosize] {
14307
+ overflow: hidden;
14308
+ resize: none;
14309
+ }
14310
+
13257
14311
  [data-scope='text-field'][data-part='input']::placeholder {
13258
14312
  color: var(--xh-text-field-placeholder-fg, var(--xh-fg-subtle));
13259
14313
  }
@@ -13404,7 +14458,7 @@
13404
14458
  min-inline-size: var(--xh-thread-scroll-button-h, var(--xh-control-h-md));
13405
14459
  block-size: var(--xh-thread-scroll-button-h, var(--xh-control-h-md));
13406
14460
  padding-inline: var(--xh-thread-scroll-button-px, var(--xh-control-px-md));
13407
- border: var(--xh-stroke-thin) solid var(--xh-thread-scroll-button-border, var(--xh-border-default));
14461
+ border: var(--xh-stroke-thin) solid var(--xh-thread-scroll-button-border, var(--xh-border-control));
13408
14462
  border-radius: var(--xh-thread-scroll-button-radius, var(--xh-shape-pill));
13409
14463
  background: var(--xh-thread-scroll-button-bg, var(--xh-bg-surface));
13410
14464
  color: var(--xh-thread-scroll-button-fg, var(--xh-fg-default));
@@ -13462,8 +14516,8 @@
13462
14516
  --xh-_time-field-label-font-size: var(--xh-text-label-size);
13463
14517
  /* 形态档只换这三个私有槽:底色与两档描边 */
13464
14518
  --xh-_time-field-control-bg: var(--xh-bg-canvas);
13465
- --xh-_time-field-control-border: var(--xh-border-default);
13466
- --xh-_time-field-control-border-hover: var(--xh-border-strong);
14519
+ --xh-_time-field-control-border: var(--xh-border-control);
14520
+ --xh-_time-field-control-border-hover: var(--xh-border-control-hover);
13467
14521
  /* 聚焦描边与段位反白随语气走。没写 data-tone 时这几个 --xh-_tone* 没被声明,退回原来的焦点色与品牌色 */
13468
14522
  --xh-_time-field-ring: var(--xh-ring-focus);
13469
14523
  --xh-_time-field-segment-bg: var(--xh-_tone, var(--xh-bg-brand));
@@ -13493,15 +14547,15 @@
13493
14547
  /* 描边式:与缺省等价,写出来是为了三档形态在皮肤里都看得见 */
13494
14548
  [data-scope='time-field'][data-part='root'][data-variant='outline'] {
13495
14549
  --xh-_time-field-control-bg: var(--xh-bg-canvas);
13496
- --xh-_time-field-control-border: var(--xh-border-default);
13497
- --xh-_time-field-control-border-hover: var(--xh-border-strong);
14550
+ --xh-_time-field-control-border: var(--xh-border-control);
14551
+ --xh-_time-field-control-border-hover: var(--xh-border-control-hover);
13498
14552
  }
13499
14553
 
13500
14554
  /* 填充式:底色顶替描边,悬停时才浮出一道淡边 */
13501
14555
  [data-scope='time-field'][data-part='root'][data-variant='subtle'] {
13502
14556
  --xh-_time-field-control-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
13503
14557
  --xh-_time-field-control-border: transparent;
13504
- --xh-_time-field-control-border-hover: var(--xh-_tone-border, var(--xh-border-default));
14558
+ --xh-_time-field-control-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
13505
14559
  }
13506
14560
 
13507
14561
  /* 无框式:底与边都让开,可见反馈只剩聚焦那一档 */
@@ -13689,7 +14743,7 @@
13689
14743
  block-size: var(--xh-time-picker-control-h, var(--xh-control-h-md));
13690
14744
  padding-inline: var(--xh-time-picker-control-px, var(--xh-control-px-md));
13691
14745
  padding-block: 0;
13692
- border: var(--xh-stroke-thin) solid var(--xh-time-picker-control-border, var(--xh-border-default));
14746
+ border: var(--xh-stroke-thin) solid var(--xh-time-picker-control-border, var(--xh-border-control));
13693
14747
  border-radius: var(--xh-time-picker-control-radius, var(--xh-shape-control));
13694
14748
  background: var(--xh-time-picker-control-bg, var(--xh-bg-canvas));
13695
14749
  color: var(--xh-time-picker-control-fg, var(--xh-fg-default));
@@ -13708,7 +14762,7 @@
13708
14762
 
13709
14763
  /* hover 只在中性态换描边:invalid 正拿边框颜色说事,被 hover 盖掉那条信息就没了 */
13710
14764
  [data-scope='time-picker'][data-part='control']:hover:not([data-disabled], [data-invalid]) {
13711
- border-color: var(--xh-time-picker-control-border-hover, var(--xh-border-strong));
14765
+ border-color: var(--xh-time-picker-control-border-hover, var(--xh-border-control-hover));
13712
14766
  }
13713
14767
 
13714
14768
  /* 焦点真正落在段或按钮上,但整块看起来才是一个控件:内部有焦点时整框亮起来。
@@ -13896,8 +14950,7 @@
13896
14950
  max-block-size: var(--xh-time-picker-column-max-h, 12rem);
13897
14951
  padding-inline: var(--xh-time-picker-column-px, var(--xh-space-1));
13898
14952
  overflow-y: auto;
13899
- /* 滚动容器在某些浏览器里会被自动塞进 Tab 序列,落焦时不画环——
13900
- 焦点该落在格上,列只在整列被界卡空时才兜底占一个 Tab 位 */
14953
+ /* 滚动容器在某些浏览器里会被自动塞进 Tab 序列,是否画环见下方按 tabindex 分档的两条 */
13901
14954
  scrollbar-width: thin;
13902
14955
  }
13903
14956
 
@@ -13910,51 +14963,62 @@
13910
14963
  border-inline-start: var(--xh-stroke-thin) solid var(--xh-time-picker-column-divider, var(--xh-border-subtle));
13911
14964
  }
13912
14965
 
13913
- [data-scope='time-picker'][data-part='column']:focus-visible {
14966
+ /* 焦点落在列上有两种情形:指针打开、这一段还空着、还没有任何格当锚点,或整列被界裁空。
14967
+ 那一刻它认领着 Tab 位,环画在列内沿;有锚点时环长在格上,列不再画 */
14968
+ [data-scope='time-picker'][data-part='column'][tabindex='0']:focus-visible {
14969
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
14970
+ outline-offset: calc(-1 * var(--xh-ring-width));
14971
+ }
14972
+
14973
+ [data-scope='time-picker'][data-part='column']:not([tabindex='0']):focus-visible {
13914
14974
  outline: none;
13915
14975
  }
13916
14976
 
13917
- [data-scope='time-picker'][data-part='option'] {
14977
+ [data-scope='time-picker'][data-part='item'] {
13918
14978
  display: flex;
13919
14979
  flex: none;
13920
14980
  align-items: center;
13921
14981
  justify-content: center;
13922
- padding-block: var(--xh-time-picker-option-py, var(--xh-space-1));
13923
- padding-inline: var(--xh-time-picker-option-px, var(--xh-space-2));
13924
- border-radius: var(--xh-time-picker-option-radius, var(--xh-shape-control));
13925
- color: var(--xh-time-picker-option-fg, var(--xh-fg-default));
13926
- font-size: var(--xh-time-picker-option-font-size, var(--xh-text-body-size));
14982
+ padding-block: var(--xh-time-picker-item-py, var(--xh-space-1_5));
14983
+ padding-inline: var(--xh-time-picker-item-px, var(--xh-control-px-md));
14984
+ border-radius: var(--xh-time-picker-item-radius, var(--xh-shape-control));
14985
+ color: var(--xh-time-picker-item-fg, var(--xh-fg-default));
14986
+ font-size: var(--xh-time-picker-item-font-size, var(--xh-text-body-size));
14987
+ /* 居中的单个数字格没有截断层,行高保持紧排,列内多露几格 */
13927
14988
  line-height: var(--xh-leading-none);
13928
14989
  white-space: nowrap;
13929
14990
  cursor: pointer;
13930
14991
  user-select: none;
14992
+ transition:
14993
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
14994
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
13931
14995
  }
13932
14996
 
13933
14997
  /* 作者自己藏掉某几格时要让位,有 display 就得补这条 */
13934
- [data-scope='time-picker'][data-part='option'][hidden] {
14998
+ [data-scope='time-picker'][data-part='item'][hidden] {
13935
14999
  display: none;
13936
15000
  }
13937
15001
 
13938
- /* 焦点所在与选中互相独立:可以停在一个没被选中的格上 */
13939
- [data-scope='time-picker'][data-part='option'][data-highlighted]:not([data-disabled]),
13940
- [data-scope='time-picker'][data-part='option']:hover:not([data-disabled]) {
13941
- background: var(--xh-time-picker-option-bg-highlight, var(--xh-bg-subtle-hover));
15002
+ /* 悬停与键盘锚点共用中性灰轻档;焦点所在与选中互相独立:可以停在一个没被选中的格上 */
15003
+ [data-scope='time-picker'][data-part='item'][data-highlighted]:not([data-disabled]),
15004
+ [data-scope='time-picker'][data-part='item']:hover:not([data-disabled]) {
15005
+ background: var(--xh-time-picker-item-bg-hover, var(--xh-bg-subtle));
13942
15006
  }
13943
15007
 
13944
- [data-scope='time-picker'][data-part='option'][data-state='checked'] {
13945
- background: var(--xh-time-picker-option-bg-checked, var(--xh-bg-brand));
13946
- color: var(--xh-time-picker-option-fg-checked, var(--xh-fg-on-brand));
13947
- font-weight: var(--xh-time-picker-option-weight-checked, var(--xh-font-weight-medium));
15008
+ [data-scope='time-picker'][data-part='item'][data-state='checked'] {
15009
+ background: var(--xh-time-picker-item-bg-checked, var(--xh-bg-brand));
15010
+ color: var(--xh-time-picker-item-fg-checked, var(--xh-fg-on-brand));
15011
+ font-weight: var(--xh-time-picker-item-weight-checked, var(--xh-font-weight-medium));
13948
15012
  }
13949
15013
 
13950
- /* 选中格上再落焦点或悬停时,底色留在品牌色一侧:上面那条中性高亮的选择器更具体,
15014
+ /* 选中格上再落焦点或悬停时,底色留在品牌色一侧:上面那条中性轻档的选择器更具体,
13951
15015
  不写这条就只有底色被换走、文字仍是品牌底上的反白色 */
13952
- [data-scope='time-picker'][data-part='option'][data-state='checked'][data-highlighted]:not([data-disabled]),
13953
- [data-scope='time-picker'][data-part='option'][data-state='checked']:hover:not([data-disabled]) {
13954
- background: var(--xh-time-picker-option-bg-checked-highlight, var(--xh-bg-brand-hover));
15016
+ [data-scope='time-picker'][data-part='item'][data-state='checked'][data-highlighted]:not([data-disabled]),
15017
+ [data-scope='time-picker'][data-part='item'][data-state='checked']:hover:not([data-disabled]) {
15018
+ background: var(--xh-time-picker-item-bg-checked-hover, var(--xh-bg-brand-hover));
13955
15019
  }
13956
15020
 
13957
- [data-scope='time-picker'][data-part='option']:focus-visible {
15021
+ [data-scope='time-picker'][data-part='item']:focus-visible {
13958
15022
  outline: var(--xh-ring-width) solid var(--xh-ring-focus);
13959
15023
  /* 往内收:选项在滚动列里挨着排,环画在外沿会被列的溢出裁掉 */
13960
15024
  outline-offset: calc(-1 * var(--xh-ring-offset));
@@ -13962,7 +15026,7 @@
13962
15026
 
13963
15027
  /* 禁用走 aria-disabled 而非原生 disabled(原生 disabled 不派 click,禁用守卫就走不到),
13964
15028
  所以只能靠 data-disabled 表达,写 :disabled 永远不命中 */
13965
- [data-scope='time-picker'][data-part='option'][data-disabled] {
15029
+ [data-scope='time-picker'][data-part='item'][data-disabled] {
13966
15030
  color: var(--xh-fg-disabled);
13967
15031
  cursor: not-allowed;
13968
15032
  }
@@ -14329,7 +15393,7 @@
14329
15393
  --xh-_bg-pressed: var(--xh-_tone-subtle-active, var(--xh-bg-subtle-active));
14330
15394
  --xh-_fg-pressed: var(--xh-_tone-fg, var(--xh-fg-default));
14331
15395
 
14332
- border-color: var(--xh-_tone-border, var(--xh-border-default));
15396
+ border-color: var(--xh-_tone-border-control, var(--xh-border-control));
14333
15397
  }
14334
15398
 
14335
15399
  [data-scope='toggle'][data-part='root'][data-variant='ghost'] {
@@ -14395,7 +15459,7 @@
14395
15459
  --xh-_bg-hover: var(--xh-toggle-group-item-bg-hover, var(--xh-bg-subtle-hover));
14396
15460
  --xh-_bg-active: var(--xh-toggle-group-item-bg-active, var(--xh-bg-subtle-active));
14397
15461
  --xh-_fg: var(--xh-toggle-group-item-fg, var(--xh-fg-default));
14398
- --xh-_border: var(--xh-toggle-group-item-border, var(--xh-border-default));
15462
+ --xh-_border: var(--xh-toggle-group-item-border, var(--xh-border-control));
14399
15463
 
14400
15464
  /* 相邻两段靠负外边距压掉一条重边,谁的描边浮在上面必须能指定,
14401
15465
  因此条目必须是定位元素——静态元素的 z-index 不生效 */
@@ -14710,33 +15774,38 @@
14710
15774
  --xh-_tooltip-font-size: var(--xh-font-size-md);
14711
15775
  }
14712
15776
 
14713
- /* 箭头是旋转 45° 的方块,按落定朝向贴在 content 对侧边的中点 */
15777
+ /* 箭头是旋转 45° 的方块,按自报的落定朝向贴在 content 对侧边上;
15778
+ 交叉轴落点由定位引擎经内联自定义属性写进来,没给时退回居中 */
14714
15779
  [data-scope='tooltip'][data-part='arrow'] {
15780
+ --xh-_tooltip-arrow-size: var(--xh-tooltip-arrow-size, var(--xh-overlay-arrow-size));
15781
+ --xh-_tooltip-arrow-x: 50%;
15782
+ --xh-_tooltip-arrow-y: 50%;
15783
+
14715
15784
  position: absolute;
14716
- inline-size: var(--xh-tooltip-arrow-size, var(--xh-overlay-arrow-size));
14717
- block-size: var(--xh-tooltip-arrow-size, var(--xh-overlay-arrow-size));
15785
+ inline-size: var(--xh-_tooltip-arrow-size);
15786
+ block-size: var(--xh-_tooltip-arrow-size);
14718
15787
  background: var(--xh-tooltip-bg, var(--xh-_tone, var(--xh-fg-default)));
14719
15788
  transform: rotate(45deg);
14720
15789
  }
14721
15790
 
14722
- [data-scope='tooltip'][data-part='positioner'][data-placement^='bottom'] [data-scope='tooltip'][data-part='arrow'] {
14723
- inset-block-start: calc(var(--xh-tooltip-arrow-size, var(--xh-overlay-arrow-size)) / -2);
14724
- inset-inline-start: calc(50% - var(--xh-tooltip-arrow-size, var(--xh-overlay-arrow-size)) / 2);
15791
+ [data-scope='tooltip'][data-part='arrow'][data-placement^='bottom'] {
15792
+ inset-block-start: calc(var(--xh-_tooltip-arrow-size) / -2);
15793
+ inset-inline-start: calc(var(--xh-_tooltip-arrow-x) - var(--xh-_tooltip-arrow-size) / 2);
14725
15794
  }
14726
15795
 
14727
- [data-scope='tooltip'][data-part='positioner'][data-placement^='top'] [data-scope='tooltip'][data-part='arrow'] {
14728
- inset-block-end: calc(var(--xh-tooltip-arrow-size, var(--xh-overlay-arrow-size)) / -2);
14729
- inset-inline-start: calc(50% - var(--xh-tooltip-arrow-size, var(--xh-overlay-arrow-size)) / 2);
15796
+ [data-scope='tooltip'][data-part='arrow'][data-placement^='top'] {
15797
+ inset-block-end: calc(var(--xh-_tooltip-arrow-size) / -2);
15798
+ inset-inline-start: calc(var(--xh-_tooltip-arrow-x) - var(--xh-_tooltip-arrow-size) / 2);
14730
15799
  }
14731
15800
 
14732
- [data-scope='tooltip'][data-part='positioner'][data-placement^='left'] [data-scope='tooltip'][data-part='arrow'] {
14733
- inset-block-start: calc(50% - var(--xh-tooltip-arrow-size, var(--xh-overlay-arrow-size)) / 2);
14734
- inset-inline-end: calc(var(--xh-tooltip-arrow-size, var(--xh-overlay-arrow-size)) / -2);
15801
+ [data-scope='tooltip'][data-part='arrow'][data-placement^='left'] {
15802
+ inset-block-start: calc(var(--xh-_tooltip-arrow-y) - var(--xh-_tooltip-arrow-size) / 2);
15803
+ inset-inline-end: calc(var(--xh-_tooltip-arrow-size) / -2);
14735
15804
  }
14736
15805
 
14737
- [data-scope='tooltip'][data-part='positioner'][data-placement^='right'] [data-scope='tooltip'][data-part='arrow'] {
14738
- inset-block-start: calc(50% - var(--xh-tooltip-arrow-size, var(--xh-overlay-arrow-size)) / 2);
14739
- inset-inline-start: calc(var(--xh-tooltip-arrow-size, var(--xh-overlay-arrow-size)) / -2);
15806
+ [data-scope='tooltip'][data-part='arrow'][data-placement^='right'] {
15807
+ inset-block-start: calc(var(--xh-_tooltip-arrow-y) - var(--xh-_tooltip-arrow-size) / 2);
15808
+ inset-inline-start: calc(var(--xh-_tooltip-arrow-size) / -2);
14740
15809
  }
14741
15810
 
14742
15811
  @media (prefers-reduced-motion: reduce) {
@@ -14956,7 +16025,7 @@
14956
16025
  }
14957
16026
 
14958
16027
  [data-scope='tour'][data-part='prev-trigger'] {
14959
- border-color: var(--xh-border-default);
16028
+ border-color: var(--xh-border-control);
14960
16029
  color: var(--xh-fg-default);
14961
16030
  }
14962
16031
 
@@ -15006,11 +16075,15 @@
15006
16075
  }
15007
16076
 
15008
16077
  /* 箭头是旋转 45° 的方块,只保留朝向目标那两条边的描边;
15009
- 引擎不产出箭头偏移,因此沿交叉轴固定居中。居中步没有锚点,连接层会给它发 hidden */
16078
+ 交叉轴落点由定位引擎经内联自定义属性写进来,没给时退回居中。居中步没有锚点,连接层会给它发 hidden */
15010
16079
  [data-scope='tour'][data-part='arrow'] {
16080
+ --xh-_tour-arrow-size: var(--xh-tour-arrow-size, var(--xh-overlay-arrow-size));
16081
+ --xh-_tour-arrow-x: 50%;
16082
+ --xh-_tour-arrow-y: 50%;
16083
+
15011
16084
  position: absolute;
15012
- inline-size: var(--xh-tour-arrow-size, var(--xh-overlay-arrow-size));
15013
- block-size: var(--xh-tour-arrow-size, var(--xh-overlay-arrow-size));
16085
+ inline-size: var(--xh-_tour-arrow-size);
16086
+ block-size: var(--xh-_tour-arrow-size);
15014
16087
  background: var(--xh-tour-bg, var(--xh-bg-surface));
15015
16088
  border: var(--xh-stroke-thin) solid var(--xh-tour-border, var(--xh-border-default));
15016
16089
  rotate: 45deg;
@@ -15021,29 +16094,29 @@
15021
16094
  }
15022
16095
 
15023
16096
  [data-scope='tour'][data-part='arrow'][data-placement^='bottom'] {
15024
- inset-block-start: calc(var(--xh-tour-arrow-size, var(--xh-overlay-arrow-size)) / -2);
15025
- inset-inline-start: calc(50% - var(--xh-tour-arrow-size, var(--xh-overlay-arrow-size)) / 2);
16097
+ inset-block-start: calc(var(--xh-_tour-arrow-size) / -2);
16098
+ inset-inline-start: calc(var(--xh-_tour-arrow-x) - var(--xh-_tour-arrow-size) / 2);
15026
16099
  border-block-end: 0;
15027
16100
  border-inline-end: 0;
15028
16101
  }
15029
16102
 
15030
16103
  [data-scope='tour'][data-part='arrow'][data-placement^='top'] {
15031
- inset-block-end: calc(var(--xh-tour-arrow-size, var(--xh-overlay-arrow-size)) / -2);
15032
- inset-inline-start: calc(50% - var(--xh-tour-arrow-size, var(--xh-overlay-arrow-size)) / 2);
16104
+ inset-block-end: calc(var(--xh-_tour-arrow-size) / -2);
16105
+ inset-inline-start: calc(var(--xh-_tour-arrow-x) - var(--xh-_tour-arrow-size) / 2);
15033
16106
  border-block-start: 0;
15034
16107
  border-inline-start: 0;
15035
16108
  }
15036
16109
 
15037
16110
  [data-scope='tour'][data-part='arrow'][data-placement^='right'] {
15038
- inset-inline-start: calc(var(--xh-tour-arrow-size, var(--xh-overlay-arrow-size)) / -2);
15039
- inset-block-start: calc(50% - var(--xh-tour-arrow-size, var(--xh-overlay-arrow-size)) / 2);
16111
+ inset-inline-start: calc(var(--xh-_tour-arrow-size) / -2);
16112
+ inset-block-start: calc(var(--xh-_tour-arrow-y) - var(--xh-_tour-arrow-size) / 2);
15040
16113
  border-block-start: 0;
15041
16114
  border-inline-end: 0;
15042
16115
  }
15043
16116
 
15044
16117
  [data-scope='tour'][data-part='arrow'][data-placement^='left'] {
15045
- inset-inline-end: calc(var(--xh-tour-arrow-size, var(--xh-overlay-arrow-size)) / -2);
15046
- inset-block-start: calc(50% - var(--xh-tour-arrow-size, var(--xh-overlay-arrow-size)) / 2);
16118
+ inset-inline-end: calc(var(--xh-_tour-arrow-size) / -2);
16119
+ inset-block-start: calc(var(--xh-_tour-arrow-y) - var(--xh-_tour-arrow-size) / 2);
15047
16120
  border-block-end: 0;
15048
16121
  border-inline-start: 0;
15049
16122
  }
@@ -15109,7 +16182,7 @@
15109
16182
  gap: var(--xh-transfer-header-gap, var(--xh-control-gap-md));
15110
16183
  align-items: center;
15111
16184
  padding-block: var(--xh-transfer-header-py, var(--xh-space-2));
15112
- padding-inline: var(--xh-transfer-header-px, var(--xh-control-px-sm));
16185
+ padding-inline: var(--xh-transfer-header-px, var(--xh-control-px-md));
15113
16186
  border-block-end: var(--xh-stroke-thin) solid var(--xh-transfer-panel-border, var(--xh-border-default));
15114
16187
  }
15115
16188
 
@@ -15145,9 +16218,9 @@
15145
16218
  display: block;
15146
16219
  inline-size: 100%;
15147
16220
  block-size: var(--xh-transfer-search-h, var(--xh-control-h-sm));
15148
- padding-inline: var(--xh-transfer-search-px, var(--xh-control-px-sm));
16221
+ padding-inline: var(--xh-transfer-search-px, var(--xh-control-px-md));
15149
16222
  border: 0;
15150
- border-block-end: var(--xh-stroke-thin) solid var(--xh-transfer-panel-border, var(--xh-border-default));
16223
+ border-block-end: var(--xh-stroke-thin) solid var(--xh-transfer-search-border, var(--xh-border-control));
15151
16224
  background: var(--xh-transfer-search-bg, transparent);
15152
16225
  color: var(--xh-transfer-search-fg, var(--xh-fg-default));
15153
16226
  font: inherit;
@@ -15186,8 +16259,8 @@
15186
16259
  flex: none;
15187
16260
  gap: var(--xh-transfer-item-gap, var(--xh-control-gap-md));
15188
16261
  align-items: center;
15189
- padding-block: var(--xh-transfer-item-py, var(--xh-space-1));
15190
- padding-inline: var(--xh-transfer-item-px, var(--xh-control-px-sm));
16262
+ padding-block: var(--xh-transfer-item-py, var(--xh-space-1_5));
16263
+ padding-inline: var(--xh-transfer-item-px, var(--xh-control-px-md));
15191
16264
  border-radius: var(--xh-transfer-item-radius, var(--xh-shape-control));
15192
16265
  color: var(--xh-transfer-item-fg, var(--xh-fg-default));
15193
16266
  font-size: var(--xh-transfer-item-font-size, var(--xh-text-body-size));
@@ -15196,13 +16269,15 @@
15196
16269
  white-space: nowrap;
15197
16270
  cursor: pointer;
15198
16271
  user-select: none;
15199
- transition: background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
16272
+ transition:
16273
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
16274
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
15200
16275
  }
15201
16276
 
15202
- /* 键盘高亮与指针悬停共用一套底色:条目本身承载焦点,不另画描边。
16277
+ /* 悬停与键盘锚点共用中性灰轻档:条目本身承载焦点,不另画描边。
15203
16278
  data-highlighted 跟的是连接层记的焦点条目,与勾选互相独立 */
15204
16279
  [data-scope='transfer'][data-part='item']:not([data-disabled]):is(:hover, [data-highlighted]) {
15205
- background: var(--xh-transfer-item-bg-highlight, var(--xh-bg-subtle-hover));
16280
+ background: var(--xh-transfer-item-bg-hover, var(--xh-bg-subtle));
15206
16281
  }
15207
16282
 
15208
16283
  [data-scope='transfer'][data-part='item']:focus-visible {
@@ -15235,7 +16310,7 @@
15235
16310
  justify-content: center;
15236
16311
  inline-size: var(--xh-transfer-checkbox-size, 16px);
15237
16312
  block-size: var(--xh-transfer-checkbox-size, 16px);
15238
- border: var(--xh-stroke-thin) solid var(--xh-transfer-checkbox-border, var(--xh-border-default));
16313
+ border: var(--xh-stroke-thin) solid var(--xh-transfer-checkbox-border, var(--xh-border-control));
15239
16314
  border-radius: var(--xh-transfer-checkbox-radius, var(--xh-radius-sm));
15240
16315
  background: var(--xh-transfer-checkbox-bg, var(--xh-bg-canvas));
15241
16316
  color: var(--xh-transfer-checkbox-fg, var(--xh-fg-on-brand));
@@ -15438,8 +16513,8 @@
15438
16513
  display: flex;
15439
16514
  align-items: center;
15440
16515
  gap: var(--xh-tree-row-gap, var(--xh-control-gap-md));
15441
- padding-block: var(--xh-tree-row-py, var(--xh-space-1));
15442
- padding-inline: var(--xh-tree-row-px, var(--xh-control-px-sm));
16516
+ padding-block: var(--xh-tree-row-py, var(--xh-space-1_5));
16517
+ padding-inline: var(--xh-tree-row-px, var(--xh-control-px-md));
15443
16518
  border-radius: var(--xh-tree-row-radius, var(--xh-shape-control));
15444
16519
  color: var(--xh-tree-row-fg, var(--xh-fg-default));
15445
16520
  font-size: var(--xh-tree-row-font-size, var(--xh-text-body-size));
@@ -15448,6 +16523,9 @@
15448
16523
  white-space: nowrap;
15449
16524
  cursor: pointer;
15450
16525
  user-select: none;
16526
+ transition:
16527
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
16528
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
15451
16529
  }
15452
16530
 
15453
16531
  [data-scope='tree'][data-part='item'][hidden],
@@ -15455,11 +16533,11 @@
15455
16533
  display: none;
15456
16534
  }
15457
16535
 
15458
- /* 键盘高亮与指针高亮共用一套底色。data-highlighted 跟的是连接层记的焦点行,
15459
- 与选中互相独立:可以停在一个没被选中的节点上 */
16536
+ /* 悬停与键盘锚点共用中性灰轻档:data-highlighted 跟的是连接层记的焦点行,
16537
+ 与选中互相独立,键盘落焦另有聚焦环标记位置 */
15460
16538
  [data-scope='tree'][data-part='item']:not([data-disabled]):is(:hover, [data-highlighted]),
15461
16539
  [data-scope='tree'][data-part='branch-control']:not([data-disabled]):is(:hover, [data-highlighted]) {
15462
- background: var(--xh-tree-row-bg-highlight, var(--xh-bg-subtle-hover));
16540
+ background: var(--xh-tree-row-bg-hover, var(--xh-bg-subtle));
15463
16541
  }
15464
16542
 
15465
16543
  /* 选中只换文字色与字重,配合行尾的 ✓ 标记;底色整条通道留给高亮,
@@ -15584,8 +16662,8 @@
15584
16662
 
15585
16663
  /* 形态档只换触发框的描边与底色这三个槽,浮层与树不跟着变:
15586
16664
  变的是「这是个输入位」的表达方式,不是弹出内容的表达方式 */
15587
- --xh-_tree-select-border: var(--xh-border-default);
15588
- --xh-_tree-select-border-hover: var(--xh-border-strong);
16665
+ --xh-_tree-select-border: var(--xh-border-control);
16666
+ --xh-_tree-select-border-hover: var(--xh-border-control-hover);
15589
16667
  --xh-_tree-select-bg: var(--xh-bg-canvas);
15590
16668
 
15591
16669
  /* 选中行的文字色随语气走,没写 data-tone 时退回品牌强调色 */
@@ -15606,15 +16684,15 @@
15606
16684
  /* 形态只决定描边与底色怎么用;用哪族颜色由 data-tone 决定,没写 tone 就沿用原来的中性配色 */
15607
16685
  /* outline 就是缺省那一身:描边加画布底,写出来只为让三档并列可读 */
15608
16686
  [data-scope='tree-select'][data-part='root'][data-variant='outline'] {
15609
- --xh-_tree-select-border: var(--xh-border-default);
15610
- --xh-_tree-select-border-hover: var(--xh-border-strong);
16687
+ --xh-_tree-select-border: var(--xh-border-control);
16688
+ --xh-_tree-select-border-hover: var(--xh-border-control-hover);
15611
16689
  --xh-_tree-select-bg: var(--xh-bg-canvas);
15612
16690
  }
15613
16691
 
15614
16692
  /* subtle 是填充式:底色接过「这里能点」的表达,描边随之让位 */
15615
16693
  [data-scope='tree-select'][data-part='root'][data-variant='subtle'] {
15616
16694
  --xh-_tree-select-border: transparent;
15617
- --xh-_tree-select-border-hover: var(--xh-_tone-border, var(--xh-border-default));
16695
+ --xh-_tree-select-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
15618
16696
  --xh-_tree-select-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
15619
16697
  }
15620
16698
 
@@ -15689,10 +16767,9 @@
15689
16767
  border-color: var(--xh-tree-select-trigger-border-hover, var(--xh-_tree-select-border-hover));
15690
16768
  }
15691
16769
 
15692
- /* trigger 是整个控件唯一的 Tab 停靠点(展开后停靠点才移进树里),落焦必须看得见。
15693
- 语气落在环上:没写 data-tone 时 --xh-_tone 没被声明,退回原来的聚焦环色 */
16770
+ /* trigger 是整个控件唯一的 Tab 停靠点(展开后停靠点才移进树里),落焦必须看得见 */
15694
16771
  [data-scope='tree-select'][data-part='trigger']:focus-visible {
15695
- outline: var(--xh-ring-width) solid var(--xh-_tone, var(--xh-ring-focus));
16772
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
15696
16773
  outline-offset: var(--xh-ring-offset);
15697
16774
  }
15698
16775
 
@@ -15791,7 +16868,7 @@
15791
16868
  /* 清空按钮退出了 Tab 序列(tabindex=-1),正常操作走不到这里;
15792
16869
  留着是给程序化聚焦兜底,落焦时得看得见 */
15793
16870
  [data-scope='tree-select'][data-part='clear-trigger']:focus-visible {
15794
- outline: var(--xh-ring-width) solid var(--xh-_tone, var(--xh-ring-focus));
16871
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
15795
16872
  outline-offset: var(--xh-ring-offset);
15796
16873
  }
15797
16874
 
@@ -15857,8 +16934,14 @@
15857
16934
  display: none;
15858
16935
  }
15859
16936
 
15860
- /* 没有锚点时容器自己进 Tab 序列兜底,落焦不画环:高亮永远长在行上 */
15861
- [data-scope='tree-select'][data-part='tree']:focus-visible {
16937
+ /* 焦点落在树容器上只有一种情形:指针打开、还没有任何行当锚点。那一刻它认领着 Tab 位,
16938
+ 环画在内沿,用户才看得出键盘此刻在哪儿;有锚点时环长在行上,容器不再画 */
16939
+ [data-scope='tree-select'][data-part='tree'][tabindex='0']:focus-visible {
16940
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
16941
+ outline-offset: calc(-1 * var(--xh-ring-width));
16942
+ }
16943
+
16944
+ [data-scope='tree-select'][data-part='tree']:not([tabindex='0']):focus-visible {
15862
16945
  outline: none;
15863
16946
  }
15864
16947
 
@@ -15869,7 +16952,7 @@
15869
16952
  }
15870
16953
 
15871
16954
  [data-scope='tree-select'][data-part='branch']:focus-visible > [data-scope='tree-select'][data-part='branch-control'] {
15872
- outline: var(--xh-ring-width) solid var(--xh-_tone, var(--xh-ring-focus));
16955
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
15873
16956
  /* 往内收:行是撑满宽度的,环画在外沿会被容器裁掉 */
15874
16957
  outline-offset: calc(-1 * var(--xh-ring-width));
15875
16958
  }
@@ -15880,8 +16963,8 @@
15880
16963
  display: flex;
15881
16964
  align-items: center;
15882
16965
  gap: var(--xh-tree-select-row-gap, var(--xh-_tree-select-gap));
15883
- padding-block: var(--xh-tree-select-row-py, var(--xh-space-1));
15884
- padding-inline: var(--xh-tree-select-row-px, var(--xh-control-px-sm));
16966
+ padding-block: var(--xh-tree-select-row-py, var(--xh-space-1_5));
16967
+ padding-inline: var(--xh-tree-select-row-px, var(--xh-control-px-md));
15885
16968
  border-radius: var(--xh-tree-select-row-radius, var(--xh-shape-control));
15886
16969
  color: var(--xh-tree-select-row-fg, var(--xh-fg-default));
15887
16970
  font-size: var(--xh-tree-select-row-font-size, var(--xh-_tree-select-font-size));
@@ -15890,6 +16973,9 @@
15890
16973
  white-space: nowrap;
15891
16974
  cursor: pointer;
15892
16975
  user-select: none;
16976
+ transition:
16977
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
16978
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
15893
16979
  }
15894
16980
 
15895
16981
  [data-scope='tree-select'][data-part='item'][hidden],
@@ -15897,11 +16983,11 @@
15897
16983
  display: none;
15898
16984
  }
15899
16985
 
15900
- /* 键盘高亮与指针高亮共用一套底色。data-highlighted 跟的是连接层记的焦点行,
15901
- 与选中互相独立:可以停在一个没被选中的节点上 */
16986
+ /* 悬停与键盘锚点共用中性灰轻档:data-highlighted 跟的是连接层记的焦点行,
16987
+ 与选中互相独立,键盘落焦另有聚焦环标记位置 */
15902
16988
  [data-scope='tree-select'][data-part='item']:not([data-disabled]):is(:hover, [data-highlighted]),
15903
16989
  [data-scope='tree-select'][data-part='branch-control']:not([data-disabled]):is(:hover, [data-highlighted]) {
15904
- background: var(--xh-tree-select-row-bg-highlight, var(--xh-bg-subtle-hover));
16990
+ background: var(--xh-tree-select-row-bg-hover, var(--xh-bg-subtle));
15905
16991
  }
15906
16992
 
15907
16993
  /* 选中只换文字色与字重,配合行尾的 ✓ 标记;底色整条通道留给高亮,
@@ -15914,7 +17000,7 @@
15914
17000
 
15915
17001
  /* 叶子自己就是 treeitem,环直接画在它身上 */
15916
17002
  [data-scope='tree-select'][data-part='item']:focus-visible {
15917
- outline: var(--xh-ring-width) solid var(--xh-_tone, var(--xh-ring-focus));
17003
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
15918
17004
  outline-offset: calc(-1 * var(--xh-ring-width));
15919
17005
  }
15920
17006