@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/table.css CHANGED
@@ -6,22 +6,10 @@
6
6
  /* 定义 table 样式。 */
7
7
 
8
8
  @import '../family/action-control.css';
9
+ @import '../family/check-mark.css';
9
10
  @import '../family/collection-item.css';
10
-
11
- @layer xihan.motion {
12
- /* 加载态自己冒出来,用户没按任何键:一段极轻的呼吸就够,别抢走正在读表的注意力 */
13
- /* 一次呼吸自带来回:起止两端与中点各写一帧,声明的周期就是一次完整的明暗往返 */
14
- @keyframes xh-table-loading-pulse {
15
- 0%,
16
- 100% {
17
- opacity: var(--xh-state-disabled-opacity);
18
- }
19
-
20
- 50% {
21
- opacity: 1;
22
- }
23
- }
24
- }
11
+ @import '../family/loading-ring.css';
12
+ @import '../family/motion.css';
25
13
 
26
14
  @layer xihan.components {
27
15
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
@@ -35,7 +23,7 @@
35
23
  既不让页面上的层号插进来,也不让它们漏出去压住别人 */
36
24
  isolation: isolate;
37
25
 
38
- /* 作者放进单元格、列头与把手里的图标按档取尺,缺省 md(§6.5);表体行上的由家族随 data-xh-collection-size 下发。
26
+ /* 作者放进单元格、列头与把手里的图标按档取尺,缺省 md;表体行上的由家族随 data-xh-collection-size 下发。
39
27
  表格自绘的状态字形(排序方向、勾、半选杠、展开方向)不读这支:它们是指示符,与所在的方盒同走
40
28
  --xh-control-indicator-* 一族——五颗把手在自己身上把 --xh-icon-size 改接到方盒的尺,排序钮那支叫 --xh-table-sort-size */
41
29
  --xh-icon-size: var(--xh-table-icon-size, var(--xh-glyph-size-md));
@@ -174,23 +162,35 @@
174
162
  display: none;
175
163
  }
176
164
 
165
+ /* 已有行时重新取数(排序、翻页、筛选):不换成占位,表体与表尾保留上一帧,按 micro 淡到禁用透明度,
166
+ 数据到了再淡回。表头留着——它说的是列,不随这一轮取数变 */
167
+ [data-scope='table'][data-part='body'],
168
+ [data-scope='table'][data-part='footer'] {
169
+ transition: opacity var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
170
+ }
171
+
172
+ [data-scope='table'][data-part='root'][data-loading]:not([data-empty]) > :is([data-scope='table'][data-part='body'], [data-scope='table'][data-part='footer']) {
173
+ opacity: var(--xh-state-disabled-opacity);
174
+ }
175
+
177
176
  /* 表体是行级 roving 的兜底 Tab 位:落焦不画环,环归行 */
178
177
  [data-scope='table'][data-part='body']:focus-visible {
179
178
  outline: none;
180
179
  }
181
180
 
182
- /* 表头是淡底承载面:里头的触发器按淡底阶梯走(hover 200 → pressed 300),阶梯由 host 槽向内下发(§7.2);
181
+ /* 表头是淡底承载面:里头的触发器按淡底阶梯走(hover 200 → pressed 300),阶梯由 host 槽向内下发;
183
182
  排序钮是 ghost 图标钮,悬停 / 按下面就从这两支 host 槽取 */
184
183
  [data-scope='table'][data-part='header'] {
185
184
  --xh-action-host-bg-hover: var(--xh-bg-subtle-hover);
186
185
  --xh-action-host-bg-pressed: var(--xh-bg-subtle-active);
187
186
 
188
- border-block-end: var(--xh-stroke-thin) solid var(--xh-table-border, var(--xh-border-default));
189
- background: var(--xh-table-header-bg, var(--xh-bg-subtle));
187
+ /* 表头与表体之间是内部分隔,与行间横线同一支:外边才取 default */
188
+ border-block-end: var(--xh-stroke-thin) solid var(--xh-table-header-border, var(--xh-border-subtle));
189
+ background: var(--xh-table-header-bg, var(--xh-bg-subtle-opaque));
190
190
  }
191
191
 
192
192
  /* 吸顶:滚动容器是 root,表头钉在它的上沿。背景必须是实色——
193
- 透明的表头会让底下滚过的行透出来 */
193
+ 透明的表头会让底下滚过的行透出来,所以表头底取淡底的不透明档 */
194
194
  [data-scope='table'][data-part='header'][data-fixed] {
195
195
  position: sticky;
196
196
  z-index: var(--xh-table-sticky-header-layer, var(--xh-layer-sticky));
@@ -198,22 +198,24 @@
198
198
  }
199
199
 
200
200
  [data-scope='table'][data-part='footer'] {
201
- border-block-start: var(--xh-stroke-thin) solid var(--xh-table-border, var(--xh-border-default));
201
+ /* 表尾与表体之间同样是内部分隔 */
202
+ border-block-start: var(--xh-stroke-thin) solid var(--xh-table-footer-border, var(--xh-border-subtle));
202
203
  background: var(--xh-table-footer-bg, var(--xh-bg-subtle));
203
204
  font-weight: var(--xh-table-footer-font-weight, var(--xh-font-weight-medium));
204
205
  }
205
206
 
206
- /* 表体行走 Collection Item 的 page 语境(页内持久集合,§7.3):悬停 / 高亮 / 按下面(白底承载 hover 100 →
207
+ /* 表体行走 Collection Item 的 page 语境(页内持久集合):悬停 / 高亮 / 按下面(白底承载 hover 100 →
207
208
  pressed 200)与选中面(品牌淡底 + 淡底前景,selected+hover 20% / +pressed 28%)由家族按 aria-selected /
208
209
  aria-disabled 给出,标记由行首的勾选把手承担。这里只把公开槽映射到桥接槽,并逐项盖掉家族的行盒几何:
209
210
  行是 flex 容器(列宽由单元格的内联尺寸决定,不进家族网格)、不内衬、不圆角、字号与光标随表格本身
210
211
  (行不是按钮,selectable 与否都不改光标)。表头 / 脚注行不投影家族,只共用这一副 flex 盒 */
211
212
  [data-scope='table'][data-part='row'] {
212
- /* rest 底要实色:横向吸附的单元格靠 inherit 取它,透明会让滚过的内容透出来 */
213
+ /* 各态的底都要实色:横向吸附的单元格靠 inherit 取它,透明会让滚过的内容透出来。
214
+ 悬停、按下取淡底的不透明档 */
213
215
  --xh-collection-bg-rest: var(--xh-table-row-bg, var(--xh-bg-surface));
214
- --xh-collection-bg-hover: var(--xh-table-row-bg-hover, var(--xh-bg-subtle));
215
- --xh-collection-bg-keyboard-highlight: var(--xh-table-row-bg-hover, var(--xh-bg-subtle));
216
- --xh-collection-bg-pressed: var(--xh-table-row-bg-pressed, var(--xh-bg-subtle-hover));
216
+ --xh-collection-bg-hover: var(--xh-table-row-bg-hover, var(--xh-bg-subtle-opaque));
217
+ --xh-collection-bg-keyboard-highlight: var(--xh-table-row-bg-hover, var(--xh-bg-subtle-opaque));
218
+ --xh-collection-bg-pressed: var(--xh-table-row-bg-pressed, var(--xh-bg-subtle-hover-opaque));
217
219
  --xh-collection-bg-selected: var(--xh-table-row-bg-selected, var(--xh-bg-brand-subtle));
218
220
  --xh-collection-fg-rest: currentColor;
219
221
  --xh-collection-fg-hover: currentColor;
@@ -243,6 +245,12 @@
243
245
  border-radius: 0;
244
246
  /* 文字度量归单元格,行不给行高 */
245
247
  line-height: inherit;
248
+ /* 换面走家族的时间线;translate 是换位提交后的落位:宿主按 ids 重排之后,连接层把行按旧位置反向补偿,
249
+ 这条过渡带它滑到新位置,走完即归零,不常驻变换 */
250
+ transition:
251
+ background-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
252
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
253
+ translate var(--xh-motion-duration-move) var(--xh-motion-ease-continuous);
246
254
  }
247
255
 
248
256
  /* 家族给了 display,UA 的 [hidden]{display:none} 已被盖掉 */
@@ -261,7 +269,8 @@
261
269
  --xh-collection-outline-keyboard-highlight: var(--xh-ring-focus);
262
270
  --xh-collection-outline-selected-highlight: var(--xh-ring-focus);
263
271
 
264
- transition: none;
272
+ /* 换面即时,只留换位:Alt + 方向键搬的正是这一行,它照样从旧位置滑到新位置 */
273
+ transition: translate var(--xh-motion-duration-move) var(--xh-motion-ease-continuous);
265
274
  }
266
275
 
267
276
  /* 行间线画在后一行的上沿,区段里第一个可见行不画:行只负责行与行之间,外边界归 root 与区段容器 */
@@ -311,6 +320,39 @@
311
320
  white-space: nowrap;
312
321
  }
313
322
 
323
+ /* 纵向合并:起点格(cellSpan 合并的单元格、多级表头里较浅的叶子列头)铺满合并的几行,
324
+ 高度由连接层实测行位后写进私有槽;多出自己那一行的部分用负的下外边距让回去,自己那一行不被撑高。
325
+ 它压在下面几行之上,底色随起点行(表头随表头底),挡住那一列里的行间线。量到之前按普通格子排 */
326
+ [data-scope='table']:is([data-part='cell'], [data-part='column-header'])[data-row-span] {
327
+ /* 缺省即不铺开:连接层量到行位后在内联样式里改写这两支 */
328
+ --xh-_table-span-block: auto;
329
+ --xh-_table-span-overhang: 0px;
330
+
331
+ position: relative;
332
+ z-index: var(--xh-table-span-layer, 1);
333
+ align-self: flex-start;
334
+ block-size: var(--xh-_table-span-block);
335
+ margin-block-end: calc(-1 * var(--xh-_table-span-overhang));
336
+ background: inherit;
337
+ }
338
+
339
+ /* 被合并掉的格子只留位置(带着列宽与冻结):内容与底色都不画,对读屏已隐藏 */
340
+ [data-scope='table']:is([data-part='cell'], [data-part='column-header'])[data-covered] {
341
+ visibility: hidden;
342
+ }
343
+
344
+ /* 高对比档里合并格的底被换成系统底色,盖住行间线这件事照旧成立;补一圈系统描边标出合并区 */
345
+ @media (forced-colors: active) {
346
+ [data-scope='table']:is([data-part='cell'], [data-part='column-header'])[data-row-span] {
347
+ outline: var(--xh-stroke-thin) solid CanvasText;
348
+ outline-offset: calc(-1 * var(--xh-stroke-thin));
349
+ }
350
+ }
351
+ /* 分组表头横跨它的几列,名字居中压在这几列正上方 */
352
+ [data-scope='table'][data-part='column-header'][data-group] {
353
+ justify-content: center;
354
+ }
355
+
314
356
  /* 横向吸附:钉在滚动容器的一侧。背景继承自行,跟着选中/高亮一起变,
315
357
  滚过去时不会露出一块与所在行不同色的补丁。
316
358
  同侧多列时的偏移由连接层按前面各列的宽度累加好写进 --xh-table-sticky-inset */
@@ -334,7 +376,43 @@
334
376
  /* 表头行本身透明(露出表头底),吸附的列头继承到的就是透明——得自己带表头底,
335
377
  否则横向滚动时其它列头从它下面穿过去 */
336
378
  [data-scope='table'][data-part='column-header'][data-frozen] {
337
- background: var(--xh-table-header-bg, var(--xh-bg-subtle));
379
+ background: var(--xh-table-header-bg, var(--xh-bg-subtle-opaque));
380
+ }
381
+
382
+ /* 冻结列的边界:紧挨滚动区的那一列在朝向滚动区的一侧画一道描边,只在确有内容被压住时出现——
383
+ 行首冻结列在滚离起始端之后、行尾冻结列在还没滚到末端时。冻结列与被压住的内容同在一个平面上,
384
+ 层级靠描边而不是阴影;线常驻、按 micro 淡入淡出,不改格子的盒尺寸 */
385
+ [data-scope='table']:is([data-part='column-header'], [data-part='cell'])[data-frozen-edge]::after {
386
+ content: '';
387
+ position: absolute;
388
+ inset-block: 0;
389
+ inline-size: var(--xh-stroke-thin);
390
+ background: var(--xh-table-frozen-edge, var(--xh-border-default));
391
+ opacity: 0;
392
+ pointer-events: none;
393
+ transition: opacity var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
394
+ }
395
+
396
+ [data-scope='table']:is([data-part='column-header'], [data-part='cell'])[data-frozen-edge][data-frozen='start']::after {
397
+ inset-inline-end: 0;
398
+ }
399
+
400
+ [data-scope='table']:is([data-part='column-header'], [data-part='cell'])[data-frozen-edge][data-frozen='end']::after {
401
+ inset-inline-start: 0;
402
+ }
403
+
404
+ /* root 恒报两端(没有冻结列时两端都算贴着);虚拟滚动的行隔着视口,按后代认 */
405
+ [data-scope='table'][data-part='root']:not([data-at-min-horizontal]) :is([data-part='column-header'], [data-part='cell'])[data-frozen-edge][data-frozen='start']::after,
406
+ [data-scope='table'][data-part='root']:not([data-at-max-horizontal]) :is([data-part='column-header'], [data-part='cell'])[data-frozen-edge][data-frozen='end']::after {
407
+ opacity: 1;
408
+ }
409
+
410
+ /* 边界线是一块底色,系统色下会被抹成 Canvas:换成 CanvasText 并只让这条线不参与强制换色 */
411
+ @media (forced-colors: active) {
412
+ [data-scope='table']:is([data-part='column-header'], [data-part='cell'])[data-frozen-edge]::after {
413
+ background: CanvasText;
414
+ forced-color-adjust: none;
415
+ }
338
416
  }
339
417
 
340
418
  /* —— 三个可叠加的外观开关 —— */
@@ -345,7 +423,7 @@
345
423
  :where([data-scope='table'][data-part='root'][data-striped] [data-scope='table'][data-part='body']) > [data-scope='table'][data-part='row']:where(:nth-of-type(even)) {
346
424
  /* 改的是家族的 rest 槽而不是 background 本身:悬停、选中、按下各态由家族在同一支私有槽上解算,
347
425
  直接写 background 会把它们全压掉 */
348
- --xh-collection-bg-rest: var(--xh-table-row-bg-striped, var(--xh-bg-subtle));
426
+ --xh-collection-bg-rest: var(--xh-table-row-bg-striped, var(--xh-bg-subtle-opaque));
349
427
  }
350
428
 
351
429
  /* 形态:outline 画一圈描边加圆角;ghost 即基础规则,只留行间横线,圆角也随之无意义 */
@@ -355,8 +433,9 @@
355
433
  background: var(--xh-table-bg, var(--xh-bg-surface));
356
434
  }
357
435
 
358
- /* 淡底档:不画描边,surface 圆角 + subtle 底 */
436
+ /* 淡底档:描边退成透明占位(不去掉),与 outline 档同盒尺寸,换档不跳 1px;surface 圆角 + subtle 底 */
359
437
  [data-scope='table'][data-part='root'][data-variant='subtle'] {
438
+ border: var(--xh-stroke-thin) solid transparent;
360
439
  border-radius: var(--xh-table-radius, var(--xh-shape-surface));
361
440
  background: var(--xh-table-bg, var(--xh-bg-subtle));
362
441
  }
@@ -372,7 +451,7 @@
372
451
  }
373
452
 
374
453
  /* 五个把手共用一副方形盒子,两种行的文字起点才对得齐;列设置区那颗与表头里的选择框同款,一眼看得出是同一件事。
375
- 五颗都接 Action Control icon 档(§9.1 定尺方框):盒型、悬停 / 按下 / 禁用面、0.97 缩放与换底、焦点环与过渡由家族
454
+ 五颗都接 Action Control icon 档(定尺方框):盒型、悬停 / 按下 / 禁用面、0.97 缩放与换底、焦点环与过渡由家族
376
455
  配方给,这里只把使用者槽映射到桥接槽;边长仍按 --xh-control-indicator-size 走。三颗勾选框是 outline 形态
377
456
  (字段静息形态:透明底 + --xh-border-control 描边),展开箭头与排序钮是 ghost;空框与箭头坐在白底行上,
378
457
  按下落 200 档,已勾选的品牌实心面按下派生 active 面;排序钮坐在表头淡底上,阶梯另由 host 槽给(见下) */
@@ -400,8 +479,7 @@
400
479
  }
401
480
 
402
481
  /* 家族 icon 档在粗指针下用 ::after 扩热区到 44px:五颗把手密排在行首与列头里(coarse-target 登记的
403
- A 档存量),外扩会与相邻把手和上下行叠上;热区不外扩,全选框的 ::after 又是半选那道横杠、
404
- 排序钮的 ::after 是多列排序的序号角标 */
482
+ A 档存量),外扩会与相邻把手和上下行叠上;热区不外扩,排序钮的 ::after 是多列排序的序号角标 */
405
483
  @media (pointer: coarse) {
406
484
  [data-scope='table'][data-part='select-all-trigger']::after,
407
485
  [data-scope='table'][data-part='row-select-trigger']::after,
@@ -422,7 +500,7 @@
422
500
  }
423
501
  }
424
502
 
425
- /* 盒里的字形是指示符(§6.5):这四颗把手各自把 --xh-icon-size 改接到方盒的尺上,兜底字形与作者塞进盒里的
503
+ /* 盒里的字形是指示符:这四颗把手各自把 --xh-icon-size 改接到方盒的尺上,兜底字形与作者塞进盒里的
426
504
  XhIcon 读同一把尺,root 那支只管作者放进单元格与把手里图标的 --xh-table-icon-size 不流进来。
427
505
  三颗勾选框的勾与半选杠按方盒边长 × 0.75,与 Checkbox 的 --xh-_checkbox-glyph、Tree 勾选把手同一比例;
428
506
  展开箭头的 chevron 与方盒同边长,同 Tree 的 branch-trigger */
@@ -461,8 +539,8 @@
461
539
  display: none;
462
540
  }
463
541
 
464
- /* 三个勾选把手:未选中时只剩一个空框(描边画出的透明底空框),勾选标记靠前景色显隐,
465
- 切换选中不引起行宽抖动 */
542
+ /* 三个勾选把手:未选中时只剩一个空框(描边画出的透明底空框)。框里的勾与半选杠由勾选标记配方画:
543
+ 常驻、按状态淡变并轻缩放,标记色恒为面上的前景,切换选中不引起行宽抖动 */
466
544
  [data-scope='table'][data-part='select-all-trigger'],
467
545
  [data-scope='table'][data-part='row-select-trigger'],
468
546
  [data-scope='table'][data-part='column-visibility-trigger'] {
@@ -480,7 +558,7 @@
480
558
  /* 已勾选的方框是品牌实心面,按下在它之上派生 active 面 */
481
559
  [data-scope='table'][data-part='select-all-trigger']:is([data-state='checked'], [data-state='indeterminate']),
482
560
  [data-scope='table'][data-part='column-visibility-trigger'][data-state='checked'],
483
- [data-scope='table'][data-part='row-select-trigger'][data-selected] {
561
+ [data-scope='table'][data-part='row-select-trigger']:is([data-selected], [data-indeterminate]) {
484
562
  --xh-_table-trigger-bg: var(--xh-table-trigger-bg-checked, var(--xh-bg-brand));
485
563
  --xh-_table-trigger-bg-pressed: var(--xh-table-trigger-bg-checked-pressed, var(--xh-bg-brand-active));
486
564
  --xh-_table-trigger-border: var(--xh-table-trigger-border-checked, var(--xh-table-trigger-bg-checked, var(--xh-bg-brand)));
@@ -488,50 +566,21 @@
488
566
 
489
567
  /* 面是实心的,环色与面同族,画在面里就看不出来;环改取面自己的前景色——
490
568
  那一族色本来就要在这块面上把字读清楚,与面的对比是按可读性定的。
491
- 置灰那档不在其列:底已换成淡底、勾也藏成透明,currentColor 取到的是透明,环整个消失;
492
- aria-disabled 的把手仍能落焦,环退回默认那一支 */
569
+ 置灰那档不在其列:底已换成淡底,aria-disabled 的把手仍能落焦,环退回默认那一支 */
493
570
  [data-scope='table'][data-part='select-all-trigger']:is([data-state='checked'], [data-state='indeterminate']):not([data-disabled]),
494
571
  [data-scope='table'][data-part='column-visibility-trigger'][data-state='checked']:not([data-disabled]),
495
- [data-scope='table'][data-part='row-select-trigger'][data-selected]:not([data-disabled]) {
572
+ [data-scope='table'][data-part='row-select-trigger']:is([data-selected], [data-indeterminate]):not([data-disabled]) {
496
573
  --xh-action-ring-color-focus-visible: currentColor;
497
574
  }
498
575
 
499
- /* 半选档的前景是透明的(勾藏起来、横杠由伪元素画),currentColor 取到的是透明;
500
- 环直接取面配对的那支前景色 */
501
- [data-scope='table'][data-part='select-all-trigger'][data-state='indeterminate']:not([data-disabled]) {
502
- --xh-action-ring-color-focus-visible: var(--xh-table-trigger-fg, var(--xh-fg-on-brand));
503
- }
504
-
505
- /* 未选中时勾不出现,但盒子还在:它是边框画出来的空框 */
506
- [data-scope='table'][data-part='select-all-trigger'][data-state='unchecked'],
507
- [data-scope='table'][data-part='column-visibility-trigger'][data-state='unchecked'],
508
- [data-scope='table'][data-part='row-select-trigger']:not([data-selected]) {
509
- --xh-_table-trigger-fg: transparent;
510
- }
511
-
512
- /* 半选换一枚横杠字形盖住勾:aria-checked=mixed 在视觉上也得与"全选"分得开。
513
- 横杠与勾同走字形令牌,作者换掉标记时两态一起换 */
514
- [data-scope='table'][data-part='select-all-trigger'][data-state='indeterminate'] {
515
- --xh-_table-trigger-fg: transparent;
516
- }
517
-
518
- [data-scope='table'][data-part='select-all-trigger'][data-state='indeterminate']::after {
519
- position: absolute;
520
- inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
521
- block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
522
- background-color: var(--xh-table-trigger-fg, var(--xh-fg-on-brand));
523
- content: '';
524
- -webkit-mask: var(--xh-glyph-mark-minus) center / contain no-repeat;
525
- mask: var(--xh-glyph-mark-minus) center / contain no-repeat;
526
- }
527
-
528
- /* 禁用(§7.2 第 9 条):--xh-border-default 描边 + --xh-bg-subtle 底,勾藏起来;家族的禁用块按映射给 */
576
+ /* 禁用:--xh-border-default 描边 + --xh-bg-subtle 底 + --xh-fg-disabled 勾,与独立 Checkbox 同一档;
577
+ 家族的禁用块按映射给 */
529
578
  [data-scope='table'][data-part='select-all-trigger'][data-disabled],
530
579
  [data-scope='table'][data-part='column-visibility-trigger'][data-disabled],
531
580
  [data-scope='table'][data-part='row-select-trigger'][data-disabled] {
532
581
  --xh-action-border-disabled: var(--xh-border-default);
533
582
  --xh-action-bg-disabled: var(--xh-bg-subtle);
534
- --xh-action-fg-disabled: transparent;
583
+ --xh-action-fg-disabled: var(--xh-fg-disabled);
535
584
  }
536
585
 
537
586
  /* 行选择把手不占 Tab 位(键盘那一路走 Space),只服务指针 */
@@ -550,7 +599,7 @@
550
599
 
551
600
  /* 这条整份改写家族的过渡列表,按压的 scale 与换底要一起带回来 */
552
601
  transition:
553
- rotate var(--xh-motion-duration-micro) var(--xh-motion-ease-enter-strong),
602
+ rotate var(--xh-motion-duration-nudge) var(--xh-motion-ease-enter-strong),
554
603
  background-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
555
604
  scale var(--xh-motion-duration-release) var(--xh-motion-ease-release);
556
605
  }
@@ -576,7 +625,7 @@
576
625
  outline: none;
577
626
  }
578
627
 
579
- /* 排序钮是列头里独立的定尺图标钮(§9.1):不包列名,列名留在列头上、它排在列名之后,auto 外边距把它
628
+ /* 排序钮是列头里独立的定尺图标钮:不包列名,列名留在列头上、它排在列名之后,auto 外边距把它
580
629
  推到行尾侧与列宽把手并排。盒型与 0.97 按压由家族 icon 档给(见五颗把手共用块);坐在表头淡底上,
581
630
  悬停 / 按下面按 header 的 host 槽下发的阶梯走(hover 200 → pressed 300),不取白底行那档。
582
631
  箭头静息取 subtle 前景,参与排序的列换成默认前景(见字形块) */
@@ -846,8 +895,8 @@
846
895
  悬停恒真,而悬停那条是 0-6-0、选中那条是 0-3-0,少写一层祖先这块淡底就会被那两档盖掉。
847
896
  禁用的行不接换父落点,:not([data-disabled]) 不会漏画任何一处高亮 */
848
897
  [data-scope='table'][data-part='root'] [data-scope='table'][data-part='body'] > [data-scope='table'][data-part='row'][data-drop='inside']:not([data-disabled]) {
849
- /* 落点不是选中:品牌淡底专属选中(§7.1),落点面取悬停之上一档的中性面 */
850
- background: var(--xh-table-drop-inside-bg, var(--xh-bg-subtle-hover));
898
+ /* 落点不是选中:品牌淡底专属选中,落点面取悬停之上一档的中性面 */
899
+ background: var(--xh-table-drop-inside-bg, var(--xh-bg-subtle-hover-opaque));
851
900
  }
852
901
 
853
902
  /* 换父那一档的描边:与底色分两层画,因为它得压在横向吸附的格子之上——
@@ -880,7 +929,7 @@
880
929
  }
881
930
 
882
931
  /* 排序方向用箭头字形表出来:aria-sort 只报给读屏,视觉侧也得有对应的东西。
883
- 箭头是指示符(§6.5):尺寸读钮自己改接过的 --xh-icon-size(--xh-table-sort-size,缺省与方盒同边长),
932
+ 箭头是指示符:尺寸读钮自己改接过的 --xh-icon-size(--xh-table-sort-size,缺省与方盒同边长),
884
933
  与同一表头里的勾选框方盒、列拖拽把手同走 --xh-control-indicator-size,按图标档取的 20px 会比
885
934
  16px 的方盒与列头文字都大一圈。:empty 守卫同展开箭头:作者塞了自己的图标就整个顶掉兜底;
886
935
  flex: none 是因为 ghost 那圈透明描边仍占位,不钉住的话与盒同边长的箭头会被挤窄一个描边。
@@ -940,11 +989,13 @@
940
989
  content: none;
941
990
  }
942
991
 
943
- /* 详情行:靠 data-state 收起,不卸载作者节点。内容长在它的单元格里,行本身只画底色 */
992
+ /* 详情行:靠 data-state 收起,不卸载作者节点。内容长在它的单元格里,行本身只画底色;
993
+ 换位时随它的数据行一起从旧位置滑到新位置 */
944
994
  [data-scope='table'][data-part='expanded-row'] {
945
995
  display: flex;
946
996
  align-items: stretch;
947
997
  background: var(--xh-table-detail-bg, var(--xh-bg-subtle));
998
+ transition: translate var(--xh-motion-duration-move) var(--xh-motion-ease-continuous);
948
999
  }
949
1000
 
950
1001
  /* 详情行里的格子铺满整行,且不按数据格那样截断成一行 */
@@ -962,17 +1013,17 @@
962
1013
  display: none;
963
1014
  }
964
1015
 
965
- /* 两个状态节点常挂,只在表体为空时才显形 */
1016
+ /* 两个占位节点常挂,只在表体为空时才显形,占同一个位置、同一副排版:一行文案居中,字号随表格的尺寸档
1017
+ (从 root 继承),次要文字色,块向内距一档、行内与单元格文字对齐,不设最小高——占位不撑出空白 */
966
1018
  [data-scope='table'][data-part='empty'],
967
1019
  [data-scope='table'][data-part='loading'] {
968
1020
  display: flex;
969
- flex-direction: column;
970
1021
  align-items: center;
971
1022
  justify-content: center;
972
- gap: var(--xh-table-state-gap, var(--xh-space-2));
973
- min-block-size: var(--xh-table-state-min-h, 8rem);
974
- padding-block: var(--xh-table-state-py, var(--xh-space-6));
975
- padding-inline: var(--xh-table-state-px, var(--xh-space-4));
1023
+ gap: var(--xh-table-state-gap, var(--xh-control-gap-md));
1024
+ min-block-size: var(--xh-table-state-min-h, 0);
1025
+ padding-block: var(--xh-table-state-py, var(--xh-space-3));
1026
+ padding-inline: var(--xh-table-state-px, var(--xh-control-px-sm));
976
1027
  color: var(--xh-table-state-fg, var(--xh-fg-muted));
977
1028
  text-align: center;
978
1029
  }
@@ -982,13 +1033,18 @@
982
1033
  display: none;
983
1034
  }
984
1035
 
985
- [data-scope='table'][data-part='loading'] {
986
- animation: xh-table-loading-pulse var(--xh-table-loading-duration, var(--xh-shimmer-duration))
987
- var(--xh-motion-ease-sweep) infinite;
1036
+ /* 还没有行、正在取:一枚加载环排在文案之前。环由加载环配方画成(轨道 + 起始边、随 data-loading 淡入并转),
1037
+ 这里只给它盒子:直径取表格尺寸档的图标档;转一圈的时长走公开槽 */
1038
+ [data-scope='table'][data-part='loading']::before {
1039
+ content: '';
1040
+ flex: none;
1041
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-md));
1042
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-md));
1043
+ animation-duration: var(--xh-table-loading-duration, var(--xh-motion-loop-spin));
988
1044
  }
989
1045
 
990
1046
  /* 取下一页的按钮:铺满一行摆在表尾——铺满一行的独立动作条目,接 Action Control 的 row 档、
991
- ghost 形态(§9.2):宽度由容器给、高度随内容、按下只换面不缩放;盒几何、悬停 / 按下 / 禁用面、
1047
+ ghost 形态:宽度由容器给、高度随内容、按下只换面不缩放;盒几何、悬停 / 按下 / 禁用面、
992
1048
  焦点环与粗指针热区全由家族给,白底承载 hover 100 → pressed 200。
993
1049
  这里只把使用者槽映射到桥接槽并把文案居中:它是一行里唯一的东西,靠起始缘会像一条没对齐的行 */
994
1050
  [data-scope='table'][data-part='load-more-trigger'] {
@@ -1004,7 +1060,7 @@
1004
1060
 
1005
1061
  justify-content: center;
1006
1062
  text-align: center;
1007
- /* row 档允许换行、高度随内容,折行的文案走正文行高(§6.4) */
1063
+ /* row 档允许换行、高度随内容,折行的文案走正文行高 */
1008
1064
  line-height: var(--xh-leading-normal);
1009
1065
  }
1010
1066
 
@@ -1012,33 +1068,9 @@
1012
1068
  display: none;
1013
1069
  }
1014
1070
 
1015
- /* 减少动效:呼吸停下,加载态只留静止的文案。两条规则是同一件事的两个触发条件:系统偏好与作者打的 data-motion */
1016
- @media (prefers-reduced-motion: reduce) {
1017
- [data-scope='table'][data-part='loading'] {
1018
- animation: none;
1019
- }
1020
- }
1021
-
1022
- :where([data-motion='reduce']) [data-scope='table'][data-part='loading'] {
1023
- animation: none;
1024
- }
1025
-
1026
- /* 没写内容就由皮肤画兜底字形;作者往部件里塞了自己的图形,这几条即不命中。
1027
- 尺寸读把手自己改接过的 --xh-icon-size:勾是方盒边长 × 0.75、chevron 与方盒同边长(见把手块) */
1028
- [data-scope='table'][data-part='select-all-trigger']:empty::before,
1029
- [data-scope='table'][data-part='column-visibility-trigger']:empty::before,
1030
- [data-scope='table'][data-part='row-select-trigger']:empty::before {
1031
- content: '';
1032
- display: inline-block;
1033
- inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
1034
- block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
1035
- vertical-align: var(--xh-glyph-baseline-shift);
1036
- background-color: currentColor;
1037
- -webkit-mask: var(--xh-glyph-mark-check) center / contain no-repeat;
1038
- mask: var(--xh-glyph-mark-check) center / contain no-repeat;
1039
- }
1040
-
1041
- /* flex: none:方盒是 ghost 形态,家族那圈透明描边仍占位,不钉住的话与盒同边长的 chevron 会被挤窄一个描边 */
1071
+ /* 没写内容就由皮肤画兜底字形;作者往部件里塞了自己的图形,这条即不命中。三颗勾选框里的勾由勾选标记配方画;
1072
+ 尺寸读把手自己改接过的 --xh-icon-size:chevron 与方盒同边长(见把手块)。
1073
+ flex: none:方盒是 ghost 形态,家族那圈透明描边仍占位,不钉住的话与盒同边长的 chevron 会被挤窄一个描边 */
1042
1074
  [data-scope='table'][data-part='expand-trigger']:empty::before {
1043
1075
  content: '';
1044
1076
  display: inline-block;
@@ -1052,13 +1084,7 @@
1052
1084
  }
1053
1085
 
1054
1086
  @media print {
1055
- /* 打印:动画会被定格在任意一帧——转了一半的圈、扫到一半的微光。这里换成静止的表达,
1056
- 取值与减弱动效那一档相同。两档各写一块、不并进一条媒体查询列表:减弱动效另有一份
1057
- :where([data-motion='reduce']) 的钩子版本与它成对,打印没有对应的钩子可配,
1058
- 并成一条之后这层配对关系在文本上就断了。 */
1059
- [data-scope='table'][data-part='loading'] {
1060
- animation: none;
1061
- }
1087
+ /* 加载环的静止替代(点线环)由加载环配方给,减弱动效与打印同一档 */
1062
1088
 
1063
1089
  /* 底色被丢掉:选中行那层淡底没了,与别的行一模一样。改画一条内收的实线框——
1064
1090
  用 outline 不用 border:行是横排 flex,加边会把单元格整体往里推,没选中的行不加边,
@@ -1071,18 +1097,32 @@
1071
1097
  /* 两个勾选框的实心底也被丢掉,配它的浅色勾落在白纸上就整个不见了——
1072
1098
  勾与框都换成正文色,勾没勾在纸上才分得出 */
1073
1099
  [data-scope='table'][data-part='select-all-trigger']:is([data-state='checked'], [data-state='indeterminate']),
1074
- [data-scope='table'][data-part='row-select-trigger'][data-selected] {
1100
+ [data-scope='table'][data-part='row-select-trigger']:is([data-selected], [data-indeterminate]) {
1075
1101
  border-color: var(--xh-fg-default);
1076
1102
  }
1077
1103
 
1078
- [data-scope='table'][data-part='select-all-trigger'][data-state='checked'],
1079
- [data-scope='table'][data-part='row-select-trigger'][data-selected] {
1104
+ [data-scope='table'][data-part='select-all-trigger']:is([data-state='checked'], [data-state='indeterminate']),
1105
+ [data-scope='table'][data-part='row-select-trigger']:is([data-selected], [data-indeterminate]) {
1080
1106
  color: var(--xh-fg-default);
1081
1107
  }
1108
+ }
1109
+
1110
+ @media (forced-colors: active) {
1111
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
1112
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
1113
+ 按钮面上取 ButtonText,悬停与按下时家族把按钮涂成 Highlight、字形换 HighlightText;
1114
+ 禁用取 GrayText */
1115
+ [data-scope='table']:is([data-part='sort-trigger'], [data-part='expand-trigger']):empty::before {
1116
+ forced-color-adjust: none;
1117
+ background-color: ButtonText;
1118
+ }
1119
+
1120
+ [data-scope='table']:is([data-part='sort-trigger'], [data-part='expand-trigger']):not([data-disabled]):not([data-loading]):is(:hover, :active, [data-pressed]):empty::before {
1121
+ background-color: HighlightText;
1122
+ }
1082
1123
 
1083
- /* 半选那枚横杠是伪元素画的,字色照旧留成透明把勾压住,只换横杠自己的颜色 */
1084
- [data-scope='table'][data-part='select-all-trigger'][data-state='indeterminate']::after {
1085
- background-color: var(--xh-fg-default);
1124
+ [data-scope='table']:is([data-part='sort-trigger'], [data-part='expand-trigger'])[data-disabled]:empty::before {
1125
+ background-color: GrayText;
1086
1126
  }
1087
1127
  }
1088
1128
  }