@xihan-ui/styles 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/CHANGELOG.md +1328 -0
  2. package/README.md +6 -1
  3. package/component-tokens.d.ts +505 -111
  4. package/components.tokens.json +34750 -26291
  5. package/css/accordion.css +39 -13
  6. package/css/alert.css +61 -3
  7. package/css/anchor.css +49 -11
  8. package/css/approval.css +124 -84
  9. package/css/avatar-group.css +1 -1
  10. package/css/avatar.css +13 -5
  11. package/css/back-top.css +63 -44
  12. package/css/badge.css +63 -13
  13. package/css/bar-code.css +1 -1
  14. package/css/breadcrumb.css +63 -4
  15. package/css/button-group.css +5 -5
  16. package/css/button.css +69 -21
  17. package/css/calendar-picker.css +27 -8
  18. package/css/calendar-range-picker.css +48 -11
  19. package/css/card.css +116 -2
  20. package/css/carousel.css +153 -48
  21. package/css/cartesian-chart.css +830 -0
  22. package/css/cascader.css +184 -70
  23. package/css/checkbox-group.css +69 -99
  24. package/css/checkbox.css +21 -54
  25. package/css/citation.css +470 -0
  26. package/css/clipboard.css +23 -66
  27. package/css/code-view.css +103 -8
  28. package/css/collapsible.css +37 -11
  29. package/css/color-field.css +18 -0
  30. package/css/color-picker.css +57 -62
  31. package/css/color-slider.css +26 -19
  32. package/css/color-swatch-picker.css +8 -6
  33. package/css/combobox.css +82 -37
  34. package/css/command.css +48 -48
  35. package/css/context-menu.css +43 -28
  36. package/css/date-field.css +23 -0
  37. package/css/date-picker.css +57 -24
  38. package/css/date-range-picker.css +301 -14
  39. package/css/description.css +17 -1
  40. package/css/descriptions.css +43 -1
  41. package/css/dialog.css +99 -43
  42. package/css/diff-view.css +101 -16
  43. package/css/download-trigger.css +46 -80
  44. package/css/drawer.css +168 -133
  45. package/css/editable.css +19 -0
  46. package/css/empty-state.css +8 -7
  47. package/css/field-array.css +36 -0
  48. package/css/field.css +17 -19
  49. package/css/fieldset.css +1 -1
  50. package/css/file-upload.css +126 -136
  51. package/css/flex.css +324 -8
  52. package/css/float-button.css +168 -41
  53. package/css/floating-panel.css +59 -27
  54. package/css/form.css +26 -20
  55. package/css/funnel-chart.css +106 -0
  56. package/css/graph-chart.css +205 -0
  57. package/css/grid-list.css +235 -0
  58. package/css/grid.css +252 -1
  59. package/css/heatmap.css +119 -36
  60. package/css/hierarchy-chart.css +298 -0
  61. package/css/hover-card.css +33 -18
  62. package/css/icon.css +100 -0
  63. package/css/image-cropper.css +34 -8
  64. package/css/image-viewer.css +169 -26
  65. package/css/image.css +23 -4
  66. package/css/infinite-scroll.css +2 -2
  67. package/css/input-group.css +13 -15
  68. package/css/json-viewer.css +55 -7
  69. package/css/kbd.css +1 -1
  70. package/css/layout.css +71 -21
  71. package/css/liquid.css +106 -0
  72. package/css/list.css +2 -2
  73. package/css/listbox.css +72 -23
  74. package/css/loading-bar.css +27 -13
  75. package/css/log.css +131 -35
  76. package/css/markdown-stream.css +1 -1
  77. package/css/marquee.css +163 -7
  78. package/css/matrix-code.css +1 -1
  79. package/css/mention.css +35 -21
  80. package/css/menu.css +40 -28
  81. package/css/menubar.css +42 -28
  82. package/css/message-feed.css +174 -53
  83. package/css/navigation-menu.css +177 -18
  84. package/css/notification.css +431 -110
  85. package/css/number-field.css +22 -0
  86. package/css/page-header.css +4 -2
  87. package/css/pagination.css +129 -29
  88. package/css/password-input.css +40 -8
  89. package/css/pie-chart.css +247 -0
  90. package/css/popconfirm.css +50 -58
  91. package/css/popover.css +55 -21
  92. package/css/progress.css +308 -10
  93. package/css/prompt-input.css +37 -1
  94. package/css/question-flow.css +104 -64
  95. package/css/radar-chart.css +233 -0
  96. package/css/radio-group.css +372 -14
  97. package/css/rating.css +25 -27
  98. package/css/reasoning.css +62 -55
  99. package/css/reset.css +1 -1
  100. package/css/resizable.css +6 -1
  101. package/css/sankey-chart.css +174 -0
  102. package/css/scroll-area.css +32 -0
  103. package/css/scrollbar.css +1 -1
  104. package/css/select.css +76 -59
  105. package/css/side-nav.css +177 -30
  106. package/css/signature-pad.css +23 -8
  107. package/css/skeleton.css +13 -14
  108. package/css/slider.css +39 -17
  109. package/css/sortable.css +38 -8
  110. package/css/sparkline.css +164 -0
  111. package/css/spinner.css +50 -22
  112. package/css/splitter.css +8 -2
  113. package/css/statistic.css +10 -0
  114. package/css/steps.css +257 -38
  115. package/css/switch.css +30 -55
  116. package/css/table.css +168 -128
  117. package/css/tabs.css +243 -31
  118. package/css/tag-group.css +65 -37
  119. package/css/tag.css +55 -14
  120. package/css/tags-input.css +40 -3
  121. package/css/text-field.css +19 -0
  122. package/css/time-field.css +20 -1
  123. package/css/time-picker.css +44 -17
  124. package/css/time-range-picker.css +45 -18
  125. package/css/timeline.css +1 -1
  126. package/css/toggle-group.css +8 -8
  127. package/css/toggle.css +6 -5
  128. package/css/tool-call.css +64 -56
  129. package/css/toolbar.css +105 -7
  130. package/css/tooltip.css +18 -7
  131. package/css/tour.css +49 -21
  132. package/css/transfer.css +104 -81
  133. package/css/tree-select.css +235 -58
  134. package/css/tree.css +240 -147
  135. package/css/truncate.css +55 -12
  136. package/css/typography.css +93 -0
  137. package/css/virtualizer.css +57 -1
  138. package/css/watermark.css +23 -0
  139. package/family/action-control.css +14 -5
  140. package/family/chart.css +480 -0
  141. package/family/check-mark.css +94 -0
  142. package/family/choice-card.css +97 -0
  143. package/family/collection-item.css +70 -27
  144. package/family/field-chrome.css +1 -12
  145. package/family/loading-ring.css +95 -0
  146. package/family/material.css +72 -0
  147. package/family/menu-choice.css +56 -0
  148. package/family/motion.css +208 -11
  149. package/family/tag-list.css +59 -0
  150. package/index.css +23363 -14091
  151. package/index.unlayered.css +23198 -13922
  152. package/package.json +20 -8
  153. package/css/gradient-text.css +0 -91
  154. package/css/icon-wrapper.css +0 -81
  155. package/css/segmented.css +0 -280
  156. package/css/toast.css +0 -499
package/css/marquee.css CHANGED
@@ -5,6 +5,9 @@
5
5
 
6
6
  /* 定义 marquee 样式。 */
7
7
 
8
+ /* 暂停开关走动作控件配方;独立皮肤同时传递引入,full bundle 递归去重。 */
9
+ @import '../family/action-control.css';
10
+
8
11
  @layer xihan.motion {
9
12
  /*
10
13
  * 横轴一条、纵轴一条。四个方向乘两种铺法共八组起止点,若不外提就要写八份几乎一样的关键帧。
@@ -46,11 +49,13 @@
46
49
  * 后代式会一路匹配到别人家的节点上去。
47
50
  *
48
51
  * 一圈的时长 = 一份内容的长度 ÷ 每秒像素数。--xh-_marquee-span 是一份内容在滚动轴上的长度,
49
- * 单位像素、写成纯数字。CSS 读不到布局尺寸,这里给的是缺省值;内容明显长于或短于它时,
50
- * 由使用者改写 --xh-marquee-span 对齐真实长度,速度才逐字等于每秒那么多像素。
52
+ * 单位像素、写成纯数字:连接层挂载后实测、写成内联的 --xh-_marquee-measured-span,量到之前
53
+ * (无布局宿主、首帧)退回缺省值。使用者写了 --xh-marquee-span 以它为准。
51
54
  */
52
55
  [data-scope='marquee'][data-part='root'] {
53
- --xh-_marquee-span: var(--xh-marquee-span, 600);
56
+ /* 暂停开关的边长:开关用它定尺,两端渐隐按它给开关留出位置,两处同源 */
57
+ --xh-_marquee-trigger-size: var(--xh-control-h-md);
58
+ --xh-_marquee-span: var(--xh-marquee-span, var(--xh-_marquee-measured-span, 600));
54
59
  --xh-_marquee-speed: var(--xh-marquee-speed, 60);
55
60
 
56
61
  /* 铺一份时一圈走两倍自身长度:从窗口一侧整段进来,再整段出到另一侧 */
@@ -71,6 +76,9 @@
71
76
  --xh-_marquee-from: 100%;
72
77
  --xh-_marquee-to: -100%;
73
78
 
79
+ /* 暂停开关压在窗口一端,定位基准是窗口;isolation 把开关的层号关在本组件内,不参与页面级层序 */
80
+ position: relative;
81
+ isolation: isolate;
74
82
  display: flex;
75
83
  overflow: hidden;
76
84
  max-inline-size: 100%;
@@ -134,15 +142,65 @@
134
142
  --xh-_marquee-to: 0%;
135
143
  }
136
144
 
137
- /* 悬停暂停:只在能用指针的设备上生效,触摸端不留一个按不掉的悬停态 */
145
+ /*
146
+ * 两端渐隐:窗口沿滚动方向两端各留一段遮罩,内容从一端淡入、从另一端淡出。
147
+ * 遮罩只画在窗口上,轨道照常整段移动;遮罩是静态的,不参与动画。
148
+ * 遮罩的几层取值都经私有槽下发,真正写 mask 的只有这一条:减弱动效与打印只需在同一特指度上盖掉它。
149
+ */
150
+ [data-scope='marquee'][data-part='root'][data-fade] {
151
+ --xh-_marquee-fade-size: var(--xh-marquee-fade-size, var(--xh-space-6));
152
+ /* 横排沿行内方向:角度乘方向符号,rtl 下行首在右,两端各自仍是淡入与淡出 */
153
+ --xh-_marquee-fade-dir: calc(90deg * var(--xh-direction-sign));
154
+ /* 行尾那一段收到哪里为止:没有暂停开关时收到窗口尽头 */
155
+ --xh-_marquee-fade-reserve: 0px;
156
+ --xh-_marquee-fade-reserve-size: 0 0;
157
+ --xh-_marquee-fade-reserve-at: var(--xh-direction-end) center;
158
+ /* 两层:沿滚动方向的 alpha 坡道,与给暂停开关留出的实心块;两种写法共用这一份 */
159
+ --xh-_marquee-fade-mask:
160
+ linear-gradient(var(--xh-_marquee-fade-dir), transparent 0, black var(--xh-_marquee-fade-size), black calc(100% - var(--xh-_marquee-fade-reserve) - var(--xh-_marquee-fade-size)), transparent calc(100% - var(--xh-_marquee-fade-reserve))),
161
+ linear-gradient(black, black);
162
+
163
+ -webkit-mask-image: var(--xh-_marquee-fade-mask);
164
+ mask-image: var(--xh-_marquee-fade-mask);
165
+ -webkit-mask-size: 100% 100%, var(--xh-_marquee-fade-reserve-size);
166
+ mask-size: 100% 100%, var(--xh-_marquee-fade-reserve-size);
167
+ -webkit-mask-position: 0 0, var(--xh-_marquee-fade-reserve-at);
168
+ mask-position: 0 0, var(--xh-_marquee-fade-reserve-at);
169
+ -webkit-mask-repeat: no-repeat;
170
+ mask-repeat: no-repeat;
171
+ }
172
+
173
+ [data-scope='marquee'][data-part='root'][data-fade][data-orientation='vertical'] {
174
+ --xh-_marquee-fade-dir: to bottom;
175
+ }
176
+
177
+ /*
178
+ * 暂停开关压在行尾一端:行尾那段渐隐收到开关之前,第二层实心遮罩把开关所在那一块整块露出来,
179
+ * 开关不被淡掉,开关与淡出段之间也不留一条没淡的缝。竖排时开关在块末那一角,露出的是那一角的方块。
180
+ */
181
+ [data-scope='marquee'][data-part='root'][data-fade]:has(> [data-scope='marquee'][data-part='autoplay-trigger']:not([hidden])) {
182
+ --xh-_marquee-fade-reserve: calc(var(--xh-marquee-trigger-inset, var(--xh-space-1)) + var(--xh-_marquee-trigger-size));
183
+ --xh-_marquee-fade-reserve-size: var(--xh-_marquee-fade-reserve) 100%;
184
+ }
185
+
186
+ [data-scope='marquee'][data-part='root'][data-fade][data-orientation='vertical']:has(> [data-scope='marquee'][data-part='autoplay-trigger']:not([hidden])) {
187
+ --xh-_marquee-fade-reserve-size: var(--xh-_marquee-fade-reserve) var(--xh-_marquee-fade-reserve);
188
+ --xh-_marquee-fade-reserve-at: var(--xh-direction-end) bottom;
189
+ }
190
+
191
+ /*
192
+ * 悬停暂停:只在能用指针的设备上生效,触摸端不留一个按不掉的悬停态。
193
+ * 指针停在暂停开关上不算:那里显示的是开关自己的状态,按下「继续」时指针还在开关上,
194
+ * 若照样算悬停,轨道要等指针挪开才动,这一下按了像没按。
195
+ */
138
196
  @media (hover: hover) {
139
- [data-scope='marquee'][data-part='root'][data-pause-on-hover]:hover {
197
+ [data-scope='marquee'][data-part='root'][data-pause-on-hover]:hover:not(:has(> [data-scope='marquee'][data-part='autoplay-trigger']:hover)) {
140
198
  --xh-_marquee-play: paused;
141
199
  }
142
200
  }
143
201
 
144
- /* 键盘用户没有悬停:焦点落进窗口时同样停住,否则轨道里的链接刚聚焦就被带走了 */
145
- [data-scope='marquee'][data-part='root'][data-pause-on-hover]:focus-within {
202
+ /* 键盘用户没有悬停:焦点落进窗口时同样停住,否则轨道里的链接刚聚焦就被带走了;焦点在暂停开关上同理不算 */
203
+ [data-scope='marquee'][data-part='root'][data-pause-on-hover]:focus-within:not(:has(> [data-scope='marquee'][data-part='autoplay-trigger']:focus)) {
146
204
  --xh-_marquee-play: paused;
147
205
  }
148
206
 
@@ -151,6 +209,56 @@
151
209
  --xh-_marquee-play: paused;
152
210
  }
153
211
 
212
+ /*
213
+ * 暂停开关:压在窗口行尾一端、上下居中;竖排时落在块末那一角。
214
+ * 窗口是裁切内容的单行,开关放进去会被轨道挤出视野,只能浮在上面。
215
+ * 盒型、描边、字色与 0.97 按压由动作控件配方按 icon 档的描边档给出;描边档静息不填底,
216
+ * 这里把底换成不透明的面,悬停 / 按下走同一承载面的不透明阶梯,后面走过的内容才透不上来。
217
+ */
218
+ [data-scope='marquee'][data-part='autoplay-trigger'] {
219
+ --xh-action-bg-rest: var(--xh-marquee-trigger-bg, var(--xh-bg-surface));
220
+ --xh-action-bg-hover: var(--xh-marquee-trigger-bg-hover, var(--xh-bg-subtle-opaque));
221
+ --xh-action-bg-pressed: var(--xh-marquee-trigger-bg-active, var(--xh-bg-subtle-hover-opaque));
222
+ --xh-action-bg-focus-visible: var(--xh-marquee-trigger-bg, var(--xh-bg-surface));
223
+ /* 作者塞进来的图标跟着 icon 档走,与皮肤画的兜底字形同一把尺 */
224
+ --xh-icon-size: var(--xh-marquee-icon-size, var(--xh-_action-profile-glyph-size));
225
+ /* 边长与两端渐隐给开关留的位置同源,都读根上那一支 */
226
+ --xh-action-visual-size: var(--xh-_marquee-trigger-size);
227
+
228
+ position: absolute;
229
+ z-index: 1;
230
+ inset-block: 0;
231
+ inset-inline-end: var(--xh-marquee-trigger-inset, var(--xh-space-1));
232
+ margin-block: auto;
233
+ }
234
+
235
+ [data-scope='marquee'][data-part='root'][data-orientation='vertical'] > [data-scope='marquee'][data-part='autoplay-trigger'] {
236
+ inset-block-start: auto;
237
+ inset-block-end: var(--xh-marquee-trigger-inset, var(--xh-space-1));
238
+ margin-block: 0;
239
+ }
240
+
241
+ [data-scope='marquee'][data-part='autoplay-trigger'][hidden] {
242
+ display: none;
243
+ }
244
+
245
+ /* 不给内容时画图标:在走画暂停,停住画播放;图标随动作,与可及名同一口径 */
246
+ [data-scope='marquee'][data-part='autoplay-trigger']:empty::before {
247
+ content: '';
248
+ display: inline-block;
249
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-md));
250
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-md));
251
+ vertical-align: var(--xh-glyph-baseline-shift);
252
+ background-color: currentColor;
253
+ -webkit-mask: var(--xh-glyph-mark-pause) center / contain no-repeat;
254
+ mask: var(--xh-glyph-mark-pause) center / contain no-repeat;
255
+ }
256
+
257
+ [data-scope='marquee'][data-part='autoplay-trigger'][data-state='paused']:empty::before {
258
+ -webkit-mask-image: var(--xh-glyph-mark-play);
259
+ mask-image: var(--xh-glyph-mark-play);
260
+ }
261
+
154
262
  /* 轨道:走的是这一层,一圈走完立刻回到起点接着走 */
155
263
  [data-scope='marquee'][data-part='content'] {
156
264
  display: flex;
@@ -196,6 +304,17 @@
196
304
  [data-scope='marquee'][data-part='root'] {
197
305
  overflow: auto;
198
306
  }
307
+
308
+ /* 停下后窗口改成可滚:两端再淡就把可滚内容的首尾藏起来了 */
309
+ [data-scope='marquee'][data-part='root'][data-fade] {
310
+ -webkit-mask-image: none;
311
+ mask-image: none;
312
+ }
313
+
314
+ /* 轨道已经停在起点:没有可停的东西,开关只会挡住可滚的内容 */
315
+ [data-scope='marquee'][data-part='autoplay-trigger'] {
316
+ display: none;
317
+ }
199
318
  }
200
319
 
201
320
  :where([data-motion='reduce']) [data-scope='marquee'][data-part='content'] {
@@ -206,6 +325,15 @@
206
325
  overflow: auto;
207
326
  }
208
327
 
328
+ :where([data-motion='reduce']) [data-scope='marquee'][data-part='root'][data-fade] {
329
+ -webkit-mask-image: none;
330
+ mask-image: none;
331
+ }
332
+
333
+ :where([data-motion='reduce']) [data-scope='marquee'][data-part='autoplay-trigger'] {
334
+ display: none;
335
+ }
336
+
209
337
  @media print {
210
338
  /* 打印:动画会被定格在任意一帧——转了一半的圈、扫到一半的微光。这里换成静止的表达,
211
339
  取值与减弱动效那一档相同。两档各写一块、不并进一条媒体查询列表:减弱动效另有一份
@@ -218,5 +346,33 @@
218
346
  [data-scope='marquee'][data-part='root'] {
219
347
  overflow: auto;
220
348
  }
349
+
350
+ [data-scope='marquee'][data-part='root'][data-fade] {
351
+ -webkit-mask-image: none;
352
+ mask-image: none;
353
+ }
354
+
355
+ [data-scope='marquee'][data-part='autoplay-trigger'] {
356
+ display: none;
357
+ }
358
+ }
359
+
360
+ @media (forced-colors: active) {
361
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
362
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
363
+ 按钮面上取 ButtonText,悬停与按下时家族把按钮涂成 Highlight、字形换 HighlightText;
364
+ 禁用取 GrayText */
365
+ [data-scope='marquee'][data-part='autoplay-trigger']:empty::before {
366
+ forced-color-adjust: none;
367
+ background-color: ButtonText;
368
+ }
369
+
370
+ [data-scope='marquee'][data-part='autoplay-trigger']:not([data-disabled]):not([data-loading]):is(:hover, :active, [data-pressed]):empty::before {
371
+ background-color: HighlightText;
372
+ }
373
+
374
+ [data-scope='marquee'][data-part='autoplay-trigger'][data-disabled]:empty::before {
375
+ background-color: GrayText;
376
+ }
221
377
  }
222
378
  }
@@ -33,7 +33,7 @@
33
33
 
34
34
  /* 占位与码面之间来回时,底色与那圈描边淡变 */
35
35
  transition:
36
- background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
36
+ background-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
37
37
  box-shadow var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
38
38
  }
39
39
 
package/css/mention.css CHANGED
@@ -6,9 +6,11 @@
6
6
  /* Mention 独立皮肤同时传递引入字段视觉盒与浮层集合行;full bundle 递归去重。 */
7
7
  @import '../family/field-chrome.css';
8
8
  @import '../family/collection-item.css';
9
+ @import '../family/loading-ring.css';
9
10
 
10
11
  /* 共享关键帧由 family/motion.css 提供,本皮肤 @import 它,单独引入仍成立。 */
11
12
  @import '../family/motion.css';
13
+ @import '../family/material.css';
12
14
 
13
15
  @layer xihan.components {
14
16
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
@@ -165,6 +167,13 @@
165
167
  display: none;
166
168
  }
167
169
 
170
+ /* 多行形态:正文可以换行,框随 rows 定起始高度、不钉单行控件高(家族的多行布局给);
171
+ 框自己就是 chrome,家族多行布局把上下内衬归零,这里按文本字段多行档同一支令牌补回,只许纵向拉伸 */
172
+ [data-scope='mention'][data-part='input'][data-xh-field-layout='textarea'] {
173
+ padding-block: var(--xh-mention-textarea-py, var(--xh-space-2));
174
+ resize: vertical;
175
+ }
176
+
168
177
  /* 输入框自身即 chrome,家族按 data-xh-field-input 给的占位色命不中它,占位色仍由本皮肤给 */
169
178
  [data-scope='mention'][data-part='input']::placeholder {
170
179
  color: var(--xh-mention-placeholder-fg, var(--xh-fg-subtle));
@@ -188,6 +197,15 @@
188
197
  }
189
198
 
190
199
  [data-scope='mention'][data-part='content'] {
200
+ /* 材质由家族配方画(family/material.css 的 frosted 面:描边、底、1px 顶光、前景、投影与背景滤镜),
201
+ 这里只把使用者槽接进来 */
202
+ --xh-frosted-bg: var(--xh-mention-content-bg, var(--xh-_material-bg));
203
+ --xh-frosted-border: var(--xh-mention-content-border, var(--xh-_material-border));
204
+ --xh-frosted-fg: var(--xh-mention-content-fg, var(--xh-_material-fg));
205
+ --xh-frosted-shadow: var(--xh-mention-content-shadow, var(--xh-_material-shadow));
206
+ --xh-frosted-backdrop: var(--xh-mention-content-backdrop, var(--xh-_material-backdrop));
207
+ --xh-frosted-highlight: var(--xh-mention-content-highlight, var(--xh-_material-highlight));
208
+
191
209
  position: relative;
192
210
  display: flex;
193
211
  flex-direction: column;
@@ -201,31 +219,13 @@
201
219
  max-block-size: min(var(--xh-mention-content-max-h, var(--xh-overlay-max-h)), var(--xh-_mention-available-h));
202
220
  padding-block: var(--xh-mention-content-py, var(--xh-space-1));
203
221
  padding-inline: var(--xh-mention-content-px, var(--xh-space-1));
204
- border: var(--xh-stroke-thin) solid var(--xh-mention-content-border, var(--xh-material-frosted-border));
205
222
  border-radius: var(--xh-mention-content-radius, var(--xh-shape-overlay));
206
- background: var(--xh-mention-content-bg, var(--xh-material-frosted-bg));
207
- color: var(--xh-mention-content-fg, var(--xh-material-frosted-fg));
208
- box-shadow: var(--xh-mention-content-shadow, var(--xh-material-frosted-shadow));
209
- -webkit-backdrop-filter: var(--xh-mention-content-backdrop, var(--xh-material-frosted-backdrop));
210
- backdrop-filter: var(--xh-mention-content-backdrop, var(--xh-material-frosted-backdrop));
211
223
  overflow-y: auto;
212
224
  /* 浮层滚动面:滚到头不把页面一起带走 */
213
225
  overscroll-behavior: contain;
214
226
  pointer-events: auto;
215
227
  }
216
228
 
217
- /* 光效仅属于唯一外壳,不拦截候选或同级状态文字。 */
218
- [data-scope='mention'][data-part='content']::before {
219
- content: '';
220
- position: absolute;
221
- inset-block-start: 0;
222
- inset-inline: 0;
223
- block-size: var(--xh-stroke-thin);
224
- border-radius: inherit;
225
- background: var(--xh-mention-content-highlight, var(--xh-material-frosted-highlight));
226
- pointer-events: none;
227
- }
228
-
229
229
  /* 这里刻意没有 [data-part='content'][hidden] { display: none },与 dialog 一致:
230
230
  收起态连接层会给 content 打 hidden,而上面那条 display 正是要盖掉 UA 的
231
231
  [hidden]{display:none}——退场动画得先能播。presence 读的是 content 的 animationName,
@@ -240,8 +240,6 @@
240
240
 
241
241
  [data-scope='mention'][data-part='content'][data-state='open'] {
242
242
  animation: xh-overlay-slide-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong);
243
- /* 只在开着的这一档挂:合成层随 data-state 一起收走,收起后不再占着 */
244
- will-change: opacity, translate;
245
243
  }
246
244
 
247
245
  /* 退场:收起那一帧 hidden 由退场闸门押后,这里才播得出来。forwards 把末态钉住,
@@ -258,7 +256,7 @@
258
256
  --xh-collection-inline-padding: var(--xh-mention-item-px, var(--xh-_mention-item-px));
259
257
  --xh-collection-gap: var(--xh-mention-item-gap, var(--xh-_mention-gap));
260
258
  --xh-collection-font-size: var(--xh-mention-item-font-size, var(--xh-_mention-font-size));
261
- --xh-collection-radius: var(--xh-mention-item-radius, var(--xh-shape-control));
259
+ --xh-collection-radius: var(--xh-mention-item-radius, var(--xh-shape-inset));
262
260
  --xh-collection-fg-rest: var(--xh-mention-item-fg, var(--xh-material-frosted-fg));
263
261
  --xh-collection-fg-hover: var(--xh-mention-item-fg, var(--xh-material-frosted-fg));
264
262
  --xh-collection-fg-keyboard-highlight: var(--xh-mention-item-fg, var(--xh-material-frosted-fg));
@@ -340,6 +338,7 @@
340
338
  display: flex;
341
339
  align-items: center;
342
340
  justify-content: center;
341
+ gap: var(--xh-_mention-gap);
343
342
  grid-area: 1 / 1;
344
343
  padding-block: var(--xh-mention-loading-py, var(--xh-space-3));
345
344
  padding-inline: var(--xh-mention-loading-px, var(--xh-_mention-item-px));
@@ -353,11 +352,26 @@
353
352
  display: none;
354
353
  }
355
354
 
355
+ /* 首次加载:文案前一枚加载环,由加载环配方画(连接层投影 data-xh-loading-ring 与 data-loading),
356
+ 直径取所在尺寸档的图标档 */
357
+ [data-scope='mention'][data-part='loading']::before {
358
+ content: '';
359
+ flex: none;
360
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-md));
361
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-md));
362
+ }
363
+
356
364
  [data-scope='mention'][data-part='empty']:empty,
357
365
  [data-scope='mention'][data-part='loading']:empty {
358
366
  display: none;
359
367
  }
360
368
 
369
+ /* 刷新:已有候选时后台在取,保留上一帧,候选按 micro 淡下,数据到了再淡回;在途占位此时让位。
370
+ 淡的是候选不是面:content 是整块磨砂面,整块淡下去面就透了 */
371
+ [data-scope='mention'][data-part='content'][aria-busy='true'] [data-scope='mention'][data-part='item'] {
372
+ opacity: var(--xh-state-disabled-opacity);
373
+ }
374
+
361
375
  /* 手写结构可以把全部候选留在 DOM 里只加 hidden;没有可见 item 也没有状态文案时,
362
376
  打开的空 listbox 不画无意义框。closed 不命中,content 仍能完成 Presence 退场。 */
363
377
  [data-scope='mention'][data-part='positioner']:not(:has(
package/css/menu.css CHANGED
@@ -7,7 +7,9 @@
7
7
 
8
8
  /* 共享关键帧由 family/motion.css 提供,条目走 Collection Item 家族;本皮肤 @import 它们,单独引入仍成立。 */
9
9
  @import '../family/collection-item.css';
10
+ @import '../family/menu-choice.css';
10
11
  @import '../family/motion.css';
12
+ @import '../family/material.css';
11
13
 
12
14
  @layer xihan.components {
13
15
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
@@ -61,14 +63,23 @@
61
63
  }
62
64
 
63
65
  /* 触发器的长相归作者,皮肤只表达展开态:展开着的那一张是打开路径,落与家族 hover 同档的中性面——
64
- 打开中不是选中,不用品牌色、不接语气(§7.3);哪一张开着由浮层本身说明。不加粗:加粗会推挤相邻内容 */
66
+ 打开中不是选中,不用品牌色、不接语气;哪一张开着由浮层本身说明。不加粗:加粗会推挤相邻内容 */
65
67
  [data-scope='menu'][data-part='trigger']:not([data-disabled])[data-state='open'] {
66
68
  background: var(--xh-menu-trigger-bg-active, var(--xh-bg-subtle));
67
69
  }
68
70
 
69
71
  [data-scope='menu'][data-part='content'] {
72
+ /* 材质由家族配方画(family/material.css 的 frosted 面:描边、底、1px 顶光、前景、投影与背景滤镜),
73
+ 这里只把使用者槽接进来 */
74
+ --xh-frosted-bg: var(--xh-menu-content-bg, var(--xh-_material-bg));
75
+ --xh-frosted-border: var(--xh-menu-border, var(--xh-_material-border));
76
+ --xh-frosted-fg: var(--xh-menu-content-fg, var(--xh-_material-fg));
77
+ --xh-frosted-shadow: var(--xh-menu-content-shadow, var(--xh-_material-shadow));
78
+ --xh-frosted-backdrop: var(--xh-menu-backdrop, var(--xh-_material-backdrop));
79
+ --xh-frosted-highlight: var(--xh-menu-highlight, var(--xh-_material-highlight));
80
+
70
81
  /* 条目外的字形(分组标题等)按档取尺,缺省 md,随 data-size 换档;条目里作者放的图标由家族随
71
- data-xh-collection-size 下发(§6.5)。菜单自绘的状态字形(标记位的盒、子菜单的展开 chevron)不读这支:
82
+ data-xh-collection-size 下发。菜单自绘的状态字形(标记位的盒、子菜单的展开 chevron)不读这支:
72
83
  它们是指示符,走 --xh-control-indicator-* 一族——标记位在自己身上把 --xh-icon-size 改接到盒的尺,
73
84
  子菜单箭头走 --xh-menu-submenu-indicator-size */
74
85
  --xh-icon-size: var(--xh-menu-icon-size, var(--xh-glyph-size-md));
@@ -92,32 +103,14 @@
92
103
  max-block-size: min(var(--xh-menu-max-h, var(--xh-overlay-menu-max-h)), var(--xh-_menu-available-h));
93
104
  padding-block: var(--xh-menu-content-py, var(--xh-surface-pad-xs));
94
105
  padding-inline: var(--xh-menu-content-px, var(--xh-surface-pad-xs));
95
- border: var(--xh-stroke-thin) solid var(--xh-menu-border, var(--xh-material-frosted-border));
96
106
  border-radius: var(--xh-menu-content-radius, var(--xh-shape-overlay));
97
- background: var(--xh-menu-content-bg, var(--xh-material-frosted-bg));
98
- color: var(--xh-menu-content-fg, var(--xh-material-frosted-fg));
99
- box-shadow: var(--xh-menu-content-shadow, var(--xh-material-frosted-shadow));
100
- -webkit-backdrop-filter: var(--xh-menu-backdrop, var(--xh-material-frosted-backdrop));
101
- backdrop-filter: var(--xh-menu-backdrop, var(--xh-material-frosted-backdrop));
102
107
  /* 条目超过上限就在菜单内部滚。条目的焦点环是内收的,滚动容器裁在内边距缘上,环切不着。
103
- 浮层滚动面:到底后不把滚动链穿透给页面(§6.6) */
108
+ 浮层滚动面:到底后不把滚动链穿透给页面 */
104
109
  overflow-y: auto;
105
110
  overscroll-behavior: contain;
106
111
  pointer-events: auto;
107
112
  }
108
113
 
109
- /* 顶光是独立装饰层,不替换真实边框;高对比/强制色归零后四边轮廓仍完整。 */
110
- [data-scope='menu'][data-part='content']::before {
111
- content: '';
112
- position: absolute;
113
- inset-block-start: 0;
114
- inset-inline: 0;
115
- block-size: var(--xh-stroke-thin);
116
- border-radius: inherit;
117
- background: var(--xh-menu-highlight, var(--xh-material-frosted-highlight));
118
- pointer-events: none;
119
- }
120
-
121
114
  /* 这里刻意没有 [data-part='content'][hidden] { display: none },与 dialog 一致:
122
115
  收起态连接层会给 content 打 hidden,而上面那条 display 正是要盖掉 UA 的
123
116
  [hidden]{display:none}——退场动画得先能播。presence 读的是 content 的 animationName,
@@ -129,9 +122,8 @@
129
122
  outline: none;
130
123
  }
131
124
 
132
- /* 在场那一档才提前备好合成层:属性随 data-state 一起消失,收起后不长期占着 */
133
- [data-scope='menu'][data-part='content'][data-state='open'] {
134
- will-change: opacity, translate;
125
+ /* 挂载时就开着的那一段(data-instant)属于首帧,直接呈现 */
126
+ [data-scope='menu'][data-part='content'][data-state='open']:not([data-instant]) {
135
127
  animation: xh-overlay-slide-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong);
136
128
  }
137
129
 
@@ -170,7 +162,7 @@
170
162
  --xh-collection-inline-padding: var(--xh-menu-item-px, var(--xh-_menu-item-px));
171
163
  --xh-collection-gap: 0;
172
164
  --xh-collection-font-size: var(--xh-menu-item-font-size, var(--xh-_menu-font-size));
173
- --xh-collection-radius: var(--xh-menu-item-radius, var(--xh-shape-control));
165
+ --xh-collection-radius: var(--xh-menu-item-radius, var(--xh-shape-inset));
174
166
  --xh-collection-fg-rest: var(--xh-menu-item-fg, var(--xh-material-frosted-fg));
175
167
  --xh-collection-fg-hover: var(--xh-menu-item-fg, var(--xh-material-frosted-fg));
176
168
  --xh-collection-fg-keyboard-highlight: var(--xh-menu-item-fg, var(--xh-material-frosted-fg));
@@ -211,7 +203,7 @@
211
203
  display: none;
212
204
  }
213
205
 
214
- /* 标记位(勾选符号 / 图标):定宽占位,切换内容不引起行宽抖动。盒是指示符(§6.5):按指示符档取尺,
206
+ /* 标记位(勾选符号 / 图标):定宽占位,切换内容不引起行宽抖动。盒是指示符:按指示符档取尺,
215
207
  与 Cascader / Select 的标记位同走 --xh-control-indicator-size,不读家族按档下发的 --xh-icon-size——
216
208
  按图标档取的 20px 会比同一行 16px 的指示符档大一圈。盒在自己身上把 --xh-icon-size 改接到盒的尺,
217
209
  作者塞进标记位的 XhIcon 与盒同尺;条目上那支只管作者直接放进条目里的图标 */
@@ -241,7 +233,8 @@
241
233
  order: 2;
242
234
  min-inline-size: 0;
243
235
  color: var(--xh-menu-item-description-fg, var(--xh-material-frosted-fg-muted));
244
- font-size: var(--xh-menu-item-description-font-size, var(--xh-text-caption-size));
236
+ /* 与同一行的快捷键、Select 等列表的说明同档:控件次级字号,不取 caption 令牌 */
237
+ font-size: var(--xh-menu-item-description-font-size, var(--xh-control-caption-md));
245
238
  white-space: nowrap;
246
239
  text-overflow: ellipsis;
247
240
  overflow: hidden;
@@ -261,7 +254,7 @@
261
254
  }
262
255
 
263
256
  /* 带子菜单的条目行尾画右向小箭头:不点开也看得出还有下一级。
264
- 箭头与作者内容同排,用自动外距推到行尾。箭头是指示符(§6.5):与标记位的盒同走
257
+ 箭头与作者内容同排,用自动外距推到行尾。箭头是指示符:与标记位的盒同走
265
258
  --xh-control-indicator-size,不读 --xh-icon-size——按图标档取的 20px 会比 16px 的标记位盒大一圈 */
266
259
  [data-scope='menu'][data-part='item'][aria-haspopup='menu']::after {
267
260
  content: '';
@@ -349,4 +342,23 @@
349
342
  display: none;
350
343
  }
351
344
  }
345
+
346
+ @media (forced-colors: active) {
347
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
348
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
349
+ 条目面上取 CanvasText;按下与没被高亮时的悬停,家族把条目涂成 Highlight,字形换 HighlightText;
350
+ 禁用取 GrayText */
351
+ [data-scope='menu'][data-part='item'][aria-haspopup='menu']::after {
352
+ forced-color-adjust: none;
353
+ background-color: CanvasText;
354
+ }
355
+
356
+ [data-scope='menu'][data-part='item'][aria-haspopup='menu']:not([aria-disabled='true'], [data-disabled], [aria-busy='true'], [data-error]):is(:active, [data-pressed], :hover:not(:focus-visible, [data-highlighted]))::after {
357
+ background-color: HighlightText;
358
+ }
359
+
360
+ [data-scope='menu'][data-part='item'][aria-haspopup='menu']:is([aria-disabled='true'], [data-disabled])::after {
361
+ background-color: GrayText;
362
+ }
363
+ }
352
364
  }
package/css/menubar.css CHANGED
@@ -7,7 +7,9 @@
7
7
 
8
8
  /* 共享关键帧由 family/motion.css 提供,条目走 Collection Item 家族;本皮肤 @import 它们,单独引入仍成立。 */
9
9
  @import '../family/collection-item.css';
10
+ @import '../family/menu-choice.css';
10
11
  @import '../family/motion.css';
12
+ @import '../family/material.css';
11
13
 
12
14
  @layer xihan.components {
13
15
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
@@ -51,8 +53,8 @@
51
53
 
52
54
  /* 入口归 Collection Item 展开路径 / 打开中(nav 语境):几何、字号与面全经家族桥接槽。静息字色取 default
53
55
  (菜单名不是次要文字,不取 nav 缺省的 muted),悬停走白底承载 hover 100,展开着的那一张由连接层投影
54
- data-in-path,家族给与悬停同档的中性面——打开中不是选中,不用品牌色、不接语气、不加粗(§7.3);
55
- 按下 pressed 200 只换面不缩放(§9.2);禁用走 aria-disabled,家族给禁用字色与 not-allowed。
56
+ data-in-path,家族给与悬停同档的中性面——打开中不是选中,不用品牌色、不接语气、不加粗;
57
+ 按下 pressed 200 只换面不缩放;禁用走 aria-disabled,家族给禁用字色与 not-allowed。
56
58
  本条 (0,2,0) 压过家族基础块 (0,1,0),不能再写 background / color / cursor */
57
59
  [data-scope='menubar'][data-part='trigger'] {
58
60
  --xh-collection-block-padding: var(--xh-menubar-trigger-py, var(--xh-_menubar-trigger-py));
@@ -132,7 +134,7 @@
132
134
  缺省档写在第一条:trigger 与条目的行内内缩都走 control-px 档(随密度轴)、纵向走 space 档,换档各挪一级 */
133
135
  [data-scope='menubar']:is([data-part='root'], [data-part='positioner']) {
134
136
  /* 条目外的字形(分组标题、入口)按档取尺,缺省 md,随 data-size 换档;同样声明在 positioner 上,被搬走的
135
- 浮层才拿得到;条目里作者放的图标由家族随 data-xh-collection-size 下发(§6.5)。菜单栏自绘的状态字形(标记位的盒、
137
+ 浮层才拿得到;条目里作者放的图标由家族随 data-xh-collection-size 下发。菜单栏自绘的状态字形(标记位的盒、
136
138
  盒里兜底的勾、子菜单的展开 chevron)不读这支:它们是指示符,走 --xh-control-indicator-* 一族——标记位在
137
139
  自己身上把 --xh-icon-size 改接到盒的尺,子菜单箭头走 --xh-menubar-submenu-indicator-size */
138
140
  --xh-icon-size: var(--xh-menubar-icon-size, var(--xh-glyph-size-md));
@@ -166,6 +168,15 @@
166
168
  }
167
169
 
168
170
  [data-scope='menubar'][data-part='content'] {
171
+ /* 材质由家族配方画(family/material.css 的 frosted 面:描边、底、1px 顶光、前景、投影与背景滤镜),
172
+ 这里只把使用者槽接进来 */
173
+ --xh-frosted-bg: var(--xh-menubar-content-bg, var(--xh-_material-bg));
174
+ --xh-frosted-border: var(--xh-menubar-border, var(--xh-_material-border));
175
+ --xh-frosted-fg: var(--xh-menubar-content-fg, var(--xh-_material-fg));
176
+ --xh-frosted-shadow: var(--xh-menubar-content-shadow, var(--xh-_material-shadow));
177
+ --xh-frosted-backdrop: var(--xh-menubar-backdrop, var(--xh-_material-backdrop));
178
+ --xh-frosted-highlight: var(--xh-menubar-highlight, var(--xh-_material-highlight));
179
+
169
180
  position: relative;
170
181
  display: flex;
171
182
  flex-direction: column;
@@ -178,32 +189,14 @@
178
189
  max-block-size: min(var(--xh-menubar-max-h, var(--xh-overlay-menu-max-h)), var(--xh-_menubar-available-h));
179
190
  padding-block: var(--xh-menubar-content-py, var(--xh-surface-pad-xs));
180
191
  padding-inline: var(--xh-menubar-content-px, var(--xh-surface-pad-xs));
181
- border: var(--xh-stroke-thin) solid var(--xh-menubar-border, var(--xh-material-frosted-border));
182
192
  border-radius: var(--xh-menubar-content-radius, var(--xh-shape-overlay));
183
- background: var(--xh-menubar-content-bg, var(--xh-material-frosted-bg));
184
- color: var(--xh-menubar-content-fg, var(--xh-material-frosted-fg));
185
- box-shadow: var(--xh-menubar-content-shadow, var(--xh-material-frosted-shadow));
186
- -webkit-backdrop-filter: var(--xh-menubar-backdrop, var(--xh-material-frosted-backdrop));
187
- backdrop-filter: var(--xh-menubar-backdrop, var(--xh-material-frosted-backdrop));
188
193
  /* 条目超过上限就在菜单内部滚。条目的焦点环是内收的,滚动容器裁在内边距缘上,环切不着。
189
- 浮层滚动面:到底后不把滚动链穿透给页面(§6.6) */
194
+ 浮层滚动面:到底后不把滚动链穿透给页面 */
190
195
  overflow-y: auto;
191
196
  overscroll-behavior: contain;
192
197
  pointer-events: auto;
193
198
  }
194
199
 
195
- /* 顶光叠在真实四边 border 内侧;强制色撤掉装饰后,完整轮廓仍然存在。 */
196
- [data-scope='menubar'][data-part='content']::before {
197
- content: '';
198
- position: absolute;
199
- inset-block-start: 0;
200
- inset-inline: 0;
201
- block-size: var(--xh-stroke-thin);
202
- border-radius: inherit;
203
- background: var(--xh-menubar-highlight, var(--xh-material-frosted-highlight));
204
- pointer-events: none;
205
- }
206
-
207
200
  /* display 会盖掉 hidden 属性的 UA 规则,收起态显式让位。
208
201
  连接层给 content 发 hidden,这一条漏了整张菜单就永远收不起来 */
209
202
  /* 这里刻意没有 [data-part='content'][hidden] { display: none },与 dialog 一致:
@@ -217,8 +210,6 @@
217
210
 
218
211
  /* 首开从锚点一侧淡入短移,不缩放整张菜单面。 */
219
212
  [data-scope='menubar'][data-part='content'][data-state='open'] {
220
- /* 在场那一档才提前备好合成层:属性随 data-state 一起消失,收起后不长期占着 */
221
- will-change: opacity, translate;
222
213
  animation: xh-overlay-slide-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong);
223
214
  }
224
215
 
@@ -270,7 +261,7 @@
270
261
  --xh-collection-inline-padding: var(--xh-menubar-item-px, var(--xh-_menubar-item-px));
271
262
  --xh-collection-gap: 0;
272
263
  --xh-collection-font-size: var(--xh-menubar-item-font-size, var(--xh-_menubar-font-size));
273
- --xh-collection-radius: var(--xh-menubar-item-radius, var(--xh-shape-control));
264
+ --xh-collection-radius: var(--xh-menubar-item-radius, var(--xh-shape-inset));
274
265
  --xh-collection-fg-rest: var(--xh-menubar-item-fg, var(--xh-material-frosted-fg));
275
266
  --xh-collection-fg-hover: var(--xh-menubar-item-fg, var(--xh-material-frosted-fg));
276
267
  --xh-collection-fg-keyboard-highlight: var(--xh-menubar-item-fg, var(--xh-material-frosted-fg));
@@ -305,7 +296,7 @@
305
296
 
306
297
  /* 带子菜单的条目行尾画右向小箭头:不点开也看得出还有下一级。
307
298
  description 按 order 独占下一行,箭头仍留在主行末端。
308
- 箭头是指示符(§6.5):与标记位的盒同走 --xh-control-indicator-size,不读 --xh-icon-size——
299
+ 箭头是指示符:与标记位的盒同走 --xh-control-indicator-size,不读 --xh-icon-size——
309
300
  按图标档取的 20px 会比 16px 的标记位盒大一圈 */
310
301
  [data-scope='menubar'][data-part='item'][aria-haspopup='menu']::after {
311
302
  content: '';
@@ -337,7 +328,7 @@
337
328
  display: none;
338
329
  }
339
330
 
340
- /* 标记位(勾选符号 / 图标)按作者写入的位置留在主行,不为没写的条目强制占列。盒是指示符(§6.5):按指示符档
331
+ /* 标记位(勾选符号 / 图标)按作者写入的位置留在主行,不为没写的条目强制占列。盒是指示符:按指示符档
341
332
  取尺,与 Menu / ContextMenu 的标记位同走 --xh-control-indicator-size,不读家族按档下发的 --xh-icon-size——
342
333
  按图标档取的 20px 会比同一行 16px 的指示符档大一圈。盒在自己身上把 --xh-icon-size 改接到盒的尺,
343
334
  皮肤画的兜底勾与作者塞进标记位的 XhIcon 读同一把尺、与盒同边长(行尾标记不是勾选格,不取 × 0.75);
@@ -368,7 +359,8 @@
368
359
  order: 2;
369
360
  min-inline-size: 0;
370
361
  color: var(--xh-menubar-item-description-fg, var(--xh-material-frosted-fg-muted));
371
- font-size: var(--xh-menubar-item-description-font-size, var(--xh-text-caption-size));
362
+ /* 与同一行的快捷键、Select 等列表的说明同档:控件次级字号,不取 caption 令牌 */
363
+ font-size: var(--xh-menubar-item-description-font-size, var(--xh-control-caption-md));
372
364
  white-space: nowrap;
373
365
  text-overflow: ellipsis;
374
366
  overflow: hidden;
@@ -439,4 +431,26 @@
439
431
  background: CanvasText;
440
432
  }
441
433
  }
434
+
435
+ @media (forced-colors: active) {
436
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
437
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
438
+ 条目面上取 CanvasText;按下与没被高亮时的悬停,家族把条目涂成 Highlight,字形换 HighlightText;
439
+ 禁用取 GrayText */
440
+ [data-scope='menubar'][data-part='item'][aria-haspopup='menu']::after,
441
+ [data-scope='menubar'][data-part='item-indicator']:empty::before {
442
+ forced-color-adjust: none;
443
+ background-color: CanvasText;
444
+ }
445
+
446
+ [data-scope='menubar'][data-part='item'][aria-haspopup='menu']:not([aria-disabled='true'], [data-disabled], [aria-busy='true'], [data-error]):is(:active, [data-pressed], :hover:not(:focus-visible, [data-highlighted]))::after,
447
+ [data-scope='menubar'][data-part='item']:not([aria-disabled='true'], [data-disabled], [aria-busy='true'], [data-error]):is(:active, [data-pressed], :hover:not(:focus-visible, [data-highlighted])) [data-scope='menubar'][data-part='item-indicator']:empty::before {
448
+ background-color: HighlightText;
449
+ }
450
+
451
+ [data-scope='menubar'][data-part='item'][aria-haspopup='menu']:is([aria-disabled='true'], [data-disabled])::after,
452
+ [data-scope='menubar'][data-part='item']:is([aria-disabled='true'], [data-disabled]) [data-scope='menubar'][data-part='item-indicator']:empty::before {
453
+ background-color: GrayText;
454
+ }
455
+ }
442
456
  }