@xihan-ui/styles 1.0.0-alpha.1 → 1.0.0-alpha.3

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 (129) hide show
  1. package/CHANGELOG.md +527 -0
  2. package/README.md +1 -1
  3. package/css/accordion.css +61 -5
  4. package/css/affix.css +5 -0
  5. package/css/alert.css +35 -5
  6. package/css/anchor.css +12 -7
  7. package/css/avatar-group.css +8 -3
  8. package/css/avatar.css +8 -3
  9. package/css/back-top.css +21 -1
  10. package/css/badge.css +21 -2
  11. package/css/breadcrumb.css +29 -4
  12. package/css/button-group.css +19 -13
  13. package/css/button.css +57 -11
  14. package/css/calendar.css +124 -2
  15. package/css/card.css +10 -5
  16. package/css/carousel.css +41 -5
  17. package/css/cascader.css +188 -102
  18. package/css/checkbox-group.css +46 -19
  19. package/css/checkbox.css +59 -8
  20. package/css/clipboard.css +7 -1
  21. package/css/code-block.css +17 -7
  22. package/css/collapsible.css +47 -2
  23. package/css/color-picker.css +117 -46
  24. package/css/combobox.css +132 -52
  25. package/css/composer.css +10 -3
  26. package/css/context-menu.css +101 -75
  27. package/css/countdown.css +5 -0
  28. package/css/date-field.css +96 -14
  29. package/css/date-picker.css +281 -65
  30. package/css/descriptions.css +7 -2
  31. package/css/dialog.css +109 -22
  32. package/css/download-trigger.css +76 -0
  33. package/css/drawer.css +75 -23
  34. package/css/dynamic-input.css +73 -17
  35. package/css/editable.css +17 -10
  36. package/css/ellipsis.css +6 -1
  37. package/css/empty-state.css +12 -7
  38. package/css/field.css +14 -6
  39. package/css/fieldset.css +69 -0
  40. package/css/file-upload.css +50 -7
  41. package/css/flex.css +5 -0
  42. package/css/float-button.css +21 -1
  43. package/css/floating-panel.css +310 -0
  44. package/css/form.css +10 -5
  45. package/css/gradient-text.css +5 -0
  46. package/css/grid.css +5 -0
  47. package/css/heatmap.css +410 -0
  48. package/css/highlight.css +6 -1
  49. package/css/hotkeys.css +90 -0
  50. package/css/hover-card.css +58 -50
  51. package/css/icon-wrapper.css +5 -0
  52. package/css/icon.css +5 -0
  53. package/css/image-cropper.css +231 -0
  54. package/css/image-viewer.css +167 -22
  55. package/css/image.css +7 -2
  56. package/css/infinite-scroll.css +6 -1
  57. package/css/json-viewer.css +241 -0
  58. package/css/layout.css +11 -10
  59. package/css/list.css +13 -8
  60. package/css/listbox.css +25 -4
  61. package/css/loading-bar.css +12 -1
  62. package/css/log.css +6 -1
  63. package/css/marquee.css +14 -0
  64. package/css/masonry.css +63 -0
  65. package/css/mention.css +105 -32
  66. package/css/menu.css +131 -46
  67. package/css/menubar.css +150 -66
  68. package/css/navigation-menu.css +59 -29
  69. package/css/number-animation.css +8 -3
  70. package/css/number-field.css +178 -16
  71. package/css/overlay-arrow.css +51 -0
  72. package/css/page-header.css +10 -5
  73. package/css/pagination.css +18 -13
  74. package/css/password-input.css +356 -0
  75. package/css/pin-input.css +18 -10
  76. package/css/popconfirm.css +73 -16
  77. package/css/popover.css +88 -44
  78. package/css/popselect.css +201 -52
  79. package/css/progress.css +52 -7
  80. package/css/qr-code.css +6 -1
  81. package/css/radio-group.css +19 -8
  82. package/css/rating.css +7 -2
  83. package/css/reset.css +17 -0
  84. package/css/result.css +11 -6
  85. package/css/scroll-area.css +65 -94
  86. package/css/scrollbar.css +167 -0
  87. package/css/segmented.css +215 -0
  88. package/css/select.css +292 -124
  89. package/css/separator.css +7 -2
  90. package/css/side-nav.css +116 -19
  91. package/css/signature-pad.css +144 -0
  92. package/css/skeleton.css +12 -1
  93. package/css/slider.css +15 -10
  94. package/css/space.css +117 -0
  95. package/css/spinner.css +31 -11
  96. package/css/splitter.css +5 -0
  97. package/css/statistic.css +14 -9
  98. package/css/steps.css +30 -8
  99. package/css/switch.css +62 -13
  100. package/css/table.css +136 -37
  101. package/css/tabs.css +9 -4
  102. package/css/tag.css +177 -0
  103. package/css/tags-input.css +83 -40
  104. package/css/text-field.css +154 -36
  105. package/css/thread.css +6 -1
  106. package/css/time-field.css +102 -16
  107. package/css/time-picker.css +264 -69
  108. package/css/time.css +5 -0
  109. package/css/timeline.css +15 -10
  110. package/css/timer.css +119 -0
  111. package/css/toast.css +46 -21
  112. package/css/toaster.css +6 -1
  113. package/css/toggle-group.css +5 -0
  114. package/css/toggle.css +11 -6
  115. package/css/tone.css +10 -5
  116. package/css/toolbar.css +6 -1
  117. package/css/tooltip.css +54 -38
  118. package/css/tour.css +128 -63
  119. package/css/transfer.css +73 -17
  120. package/css/tree-select.css +203 -83
  121. package/css/tree.css +90 -1
  122. package/css/typography.css +9 -4
  123. package/css/undefined.css +11 -1
  124. package/css/virtualizer.css +8 -1
  125. package/css/watermark.css +6 -2
  126. package/index.css +17 -1
  127. package/index.unlayered.css +9394 -2883
  128. package/package.json +19 -4
  129. package/css/motion.css +0 -58
package/css/splitter.css CHANGED
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='splitter'] {
4
+ --xh-splitter-skin: 1;
5
+ }
6
+
2
7
  /* 分工:**面板在排布轴上的尺寸**由连接层每帧写进内联样式(flex-basis / flex-grow / flex-shrink)。
3
8
  这份皮肤一条都不碰——内联优先级高于样式表,写了不但是死声明,
4
9
  还会在切换 orientation 的那一帧跟连接层抢位置。皮肤只管排布方向、跨轴尺寸、
package/css/statistic.css CHANGED
@@ -1,9 +1,14 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='statistic'] {
4
+ --xh-statistic-skin: 1;
5
+ }
6
+
2
7
  /* 三个尺寸档只换这几个私有槽,各段从 root 继承它们,换档不必给每个部件各写一条选择器 */
3
8
  [data-scope='statistic'][data-part='root'] {
4
- --xh-_statistic-label-size: var(--xh-font-size-sm);
5
- --xh-_statistic-value-size: var(--xh-font-size-2xl);
6
- --xh-_statistic-affix-size: var(--xh-font-size-md);
9
+ --xh-_statistic-label-size: var(--xh-control-caption-md);
10
+ --xh-_statistic-value-size: var(--xh-text-heading-2-size);
11
+ --xh-_statistic-affix-size: var(--xh-control-font-md);
7
12
  --xh-_statistic-row-gap: var(--xh-space-1);
8
13
 
9
14
  /* 折行排布:标签独占一行,前缀、数值、后缀在下一行按基线对齐排在一起 */
@@ -23,16 +28,16 @@
23
28
 
24
29
  /* —— 尺寸 —— */
25
30
  [data-scope='statistic'][data-part='root'][data-size='sm'] {
26
- --xh-_statistic-label-size: var(--xh-font-size-xs);
27
- --xh-_statistic-value-size: var(--xh-font-size-xl);
28
- --xh-_statistic-affix-size: var(--xh-font-size-sm);
31
+ --xh-_statistic-label-size: var(--xh-control-caption-sm);
32
+ --xh-_statistic-value-size: var(--xh-text-heading-3-size);
33
+ --xh-_statistic-affix-size: var(--xh-control-font-sm);
29
34
  --xh-_statistic-row-gap: var(--xh-space-0_5);
30
35
  }
31
36
 
32
37
  [data-scope='statistic'][data-part='root'][data-size='lg'] {
33
- --xh-_statistic-label-size: var(--xh-font-size-md);
34
- --xh-_statistic-value-size: var(--xh-font-size-3xl);
35
- --xh-_statistic-affix-size: var(--xh-font-size-lg);
38
+ --xh-_statistic-label-size: var(--xh-control-caption-lg);
39
+ --xh-_statistic-value-size: var(--xh-text-heading-1-size);
40
+ --xh-_statistic-affix-size: var(--xh-control-font-lg);
36
41
  --xh-_statistic-row-gap: var(--xh-space-1_5);
37
42
  }
38
43
 
package/css/steps.css CHANGED
@@ -1,10 +1,18 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='steps'] {
4
+ --xh-steps-skin: 1;
5
+ }
6
+
2
7
  [data-scope='steps'][data-part='root'] {
8
+ /* 作者塞进指示符槽的图标跟着字号走,与皮肤画的兜底字形同一把尺 */
9
+ --xh-icon-size: var(--xh-steps-icon-size, var(--xh-glyph-size-text));
10
+
3
11
  /* 尺寸档只换这三个私有槽,序号圆点、标题与说明都从 root 继承它们,
4
12
  换档不必给每个部件各写一条选择器 */
5
13
  --xh-_steps-indicator-size: var(--xh-control-h-sm);
6
14
  --xh-_steps-title-font-size: var(--xh-text-label-size);
7
- --xh-_steps-caption-font-size: var(--xh-font-size-xs);
15
+ --xh-_steps-caption-font-size: var(--xh-text-caption-size);
8
16
 
9
17
  /* 语气落在走过与当前这两步的标记和连接线上:强调色画描边与填充,
10
18
  accent-text 是普通底上的强调文字(当前步的序号),on-accent 是实心底上的前景。
@@ -32,14 +40,14 @@
32
40
  两侧改从 space 档取,缺省档才留得住 */
33
41
  [data-scope='steps'][data-part='root'][data-size='sm'] {
34
42
  --xh-_steps-indicator-size: var(--xh-space-6);
35
- --xh-_steps-title-font-size: var(--xh-font-size-sm);
43
+ --xh-_steps-title-font-size: var(--xh-control-font-sm);
36
44
  /* 字号档最小就是 xs,序号与说明不再往下走,故不重声明 caption */
37
45
  }
38
46
 
39
47
  [data-scope='steps'][data-part='root'][data-size='lg'] {
40
48
  --xh-_steps-indicator-size: var(--xh-space-8);
41
- --xh-_steps-title-font-size: var(--xh-font-size-lg);
42
- --xh-_steps-caption-font-size: var(--xh-font-size-sm);
49
+ --xh-_steps-title-font-size: var(--xh-control-font-lg);
50
+ --xh-_steps-caption-font-size: var(--xh-text-secondary-size);
43
51
  }
44
52
 
45
53
  [data-scope='steps'][data-part='list'] {
@@ -100,7 +108,9 @@
100
108
  grid-template-columns: auto minmax(0, 1fr);
101
109
  align-items: center;
102
110
  column-gap: var(--xh-steps-trigger-gap, var(--xh-control-gap-md));
103
- padding: var(--xh-steps-trigger-p, var(--xh-space-1));
111
+ /* 纵向留一圈给悬停底色与焦点环,横向走控件内边距尺的小档;一个槽同时盖住两轴 */
112
+ padding-block: var(--xh-steps-trigger-p, var(--xh-space-1));
113
+ padding-inline: var(--xh-steps-trigger-p, var(--xh-control-px-sm));
104
114
  border: 0;
105
115
  border-radius: var(--xh-steps-trigger-radius, var(--xh-shape-control));
106
116
  background: transparent;
@@ -144,7 +154,7 @@
144
154
  inline-size: var(--xh-steps-indicator-size, var(--xh-_steps-indicator-size));
145
155
  block-size: var(--xh-steps-indicator-size, var(--xh-_steps-indicator-size));
146
156
  border: var(--xh-stroke-thin) solid var(--xh-steps-indicator-border, var(--xh-border-strong));
147
- border-radius: var(--xh-radius-full);
157
+ border-radius: var(--xh-shape-pill);
148
158
  background: var(--xh-steps-indicator-bg, transparent);
149
159
  color: var(--xh-steps-indicator-fg, var(--xh-fg-muted));
150
160
  font-size: var(--xh-steps-indicator-font-size, var(--xh-_steps-caption-font-size));
@@ -212,7 +222,7 @@
212
222
  flex: 1;
213
223
  min-inline-size: var(--xh-steps-separator-min-length, var(--xh-space-4));
214
224
  block-size: var(--xh-steps-separator-thickness, var(--xh-stroke-thick));
215
- border-radius: var(--xh-radius-full);
225
+ border-radius: var(--xh-shape-pill);
216
226
  background: var(--xh-steps-separator-bg, var(--xh-border-default));
217
227
  transition: background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
218
228
  }
@@ -233,7 +243,7 @@
233
243
  /* 圆点半径减去线宽的一半:calc 里的除法要写成乘 0.5,除号在这儿是无效语法 */
234
244
  margin-inline-start: calc(
235
245
  var(--xh-steps-indicator-size, var(--xh-_steps-indicator-size)) * 0.5
236
- + var(--xh-steps-trigger-p, var(--xh-space-1))
246
+ + var(--xh-steps-trigger-p, var(--xh-control-px-sm))
237
247
  - var(--xh-steps-separator-thickness, var(--xh-stroke-thick)) * 0.5
238
248
  );
239
249
  }
@@ -249,4 +259,16 @@
249
259
  outline: var(--xh-ring-width) solid var(--xh-ring-focus);
250
260
  outline-offset: var(--xh-ring-offset);
251
261
  }
262
+
263
+ /* 没写内容就由皮肤画兜底字形;作者往部件里塞了自己的图形,这几条即不命中 */
264
+ [data-scope='steps'][data-part='indicator'][data-state='completed']:empty::before {
265
+ content: '';
266
+ display: inline-block;
267
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
268
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
269
+ vertical-align: var(--xh-glyph-baseline-shift);
270
+ background-color: currentColor;
271
+ -webkit-mask: var(--xh-glyph-mark-check) center / contain no-repeat;
272
+ mask: var(--xh-glyph-mark-check) center / contain no-repeat;
273
+ }
252
274
  }
package/css/switch.css CHANGED
@@ -1,9 +1,23 @@
1
+ @layer xihan.motion {
2
+ /* 转圈的关键帧定义在本文件里:引用别处文件的动画名时那份文件不一定在场,动画会静默不跑 */
3
+ @keyframes xh-switch-rotate {
4
+ to {
5
+ transform: rotate(1turn);
6
+ }
7
+ }
8
+ }
9
+
1
10
  @layer xihan.components {
11
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
12
+ [data-scope='switch'] {
13
+ --xh-switch-skin: 1;
14
+ }
15
+
2
16
  [data-scope='switch'][data-part='root'] {
3
17
  /* 尺寸档只换轨道高度这一个私有槽,其余几何都从它算出来:
4
18
  滑块边长 = 轨道高 − 两条留白,轨道宽 = 两倍轨道高 − 两条留白,滑块行程因此正好等于自身边长 */
5
- --xh-_switch-track-h: var(--xh-font-size-2xl);
6
- --xh-_switch-pad: 2px;
19
+ --xh-_switch-track-h: var(--xh-switch-track-h-md);
20
+ --xh-_switch-pad: var(--xh-space-0_5);
7
21
  --xh-_switch-thumb-size: calc(var(--xh-_switch-track-h) - var(--xh-_switch-pad) * 2);
8
22
 
9
23
  /* 选中态轨道色随语气走。没写 data-tone 时 --xh-_tone 没被声明,退回原来的品牌色 */
@@ -26,6 +40,11 @@
26
40
  background: var(--xh-switch-bg-checked, var(--xh-_switch-accent));
27
41
  }
28
42
 
43
+ /* 校验失败:轨道描一圈警示色。轨道本身没有边框,这里补出来 */
44
+ [data-scope='switch'][data-part='root'][data-invalid] {
45
+ box-shadow: inset 0 0 0 var(--xh-stroke-thin) var(--xh-switch-border-invalid, var(--xh-border-invalid));
46
+ }
47
+
29
48
  [data-scope='switch'][data-part='root'][data-disabled] {
30
49
  cursor: not-allowed;
31
50
  opacity: var(--xh-state-disabled-opacity);
@@ -39,11 +58,11 @@
39
58
  /* —— 尺寸 —— */
40
59
  /* 缺省档写在上面的基础规则里 */
41
60
  [data-scope='switch'][data-part='root'][data-size='sm'] {
42
- --xh-_switch-track-h: var(--xh-font-size-xl);
61
+ --xh-_switch-track-h: var(--xh-switch-track-h-sm);
43
62
  }
44
63
 
45
64
  [data-scope='switch'][data-part='root'][data-size='lg'] {
46
- --xh-_switch-track-h: var(--xh-font-size-3xl);
65
+ --xh-_switch-track-h: var(--xh-switch-track-h-lg);
47
66
  }
48
67
 
49
68
  [data-scope='switch'][data-part='thumb'] {
@@ -51,7 +70,7 @@
51
70
  block-size: var(--xh-_switch-thumb-size);
52
71
  border-radius: var(--xh-shape-pill);
53
72
  background: var(--xh-switch-thumb, var(--xh-bg-canvas));
54
- box-shadow: var(--xh-elevation-1);
73
+ box-shadow: var(--xh-switch-thumb-shadow, var(--xh-elevation-raised));
55
74
  transition: translate var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
56
75
  }
57
76
 
@@ -59,13 +78,6 @@
59
78
  translate: var(--xh-_switch-thumb-size) 0;
60
79
  }
61
80
 
62
- /* 转圈的关键帧定义在本文件里:引用别处文件的动画名时那份文件不一定在场,动画会静默不跑 */
63
- @keyframes xh-switch-rotate {
64
- to {
65
- transform: rotate(1turn);
66
- }
67
- }
68
-
69
81
  /* 提交中:指针挂起、滑块里转圈;不是禁用,焦点与对比度都保持 */
70
82
  [data-scope='switch'][data-part='root'][data-loading] {
71
83
  cursor: wait;
@@ -83,6 +95,43 @@
83
95
  border: var(--xh-stroke-thin) solid transparent;
84
96
  border-block-start-color: var(--xh-switch-loading-fg, var(--xh-bg-brand));
85
97
  border-radius: var(--xh-shape-pill);
86
- animation: xh-switch-rotate var(--xh-switch-loading-duration, calc(var(--xh-duration-slow) * 2)) linear infinite;
98
+ animation: xh-switch-rotate var(--xh-switch-loading-duration, var(--xh-spin-duration)) linear infinite;
99
+ }
100
+
101
+ /* —— 带文字的整行:<label> 包住控件与文字,与 checkbox-group 的条目同一副样子 —— */
102
+ [data-scope='switch'][data-part='label'] {
103
+ display: inline-flex;
104
+ align-items: center;
105
+ gap: var(--xh-switch-label-gap, var(--xh-control-gap-md));
106
+ color: var(--xh-switch-label-fg, var(--xh-fg-default));
107
+ font-size: var(--xh-switch-label-font-size, var(--xh-text-label-size));
108
+ line-height: var(--xh-leading-none);
109
+ cursor: pointer;
110
+ }
111
+
112
+ [data-scope='switch'][data-part='label'][data-disabled] {
113
+ color: var(--xh-switch-label-fg-disabled, var(--xh-fg-disabled));
114
+ cursor: not-allowed;
115
+ }
116
+
117
+ [data-scope='switch'][data-part='text'] {
118
+ color: inherit;
119
+ user-select: none;
120
+ }
121
+
122
+ /* 减少动效:圆环停下并整圈换成虚线,静止的形状仍然读得出"还没好"。
123
+ 两条规则是同一件事的两个触发条件:系统偏好与作者打的 data-motion */
124
+ @media (prefers-reduced-motion: reduce) {
125
+ [data-scope='switch'][data-part='thumb'][data-loading]::after {
126
+ border-style: dotted;
127
+ border-color: var(--xh-switch-loading-fg, var(--xh-bg-brand));
128
+ animation: none;
129
+ }
130
+ }
131
+
132
+ :where([data-motion='reduce']) [data-scope='switch'][data-part='thumb'][data-loading]::after {
133
+ border-style: dotted;
134
+ border-color: var(--xh-switch-loading-fg, var(--xh-bg-brand));
135
+ animation: none;
87
136
  }
88
137
  }
package/css/table.css CHANGED
@@ -9,26 +9,28 @@
9
9
  opacity: 1;
10
10
  }
11
11
  }
12
-
13
- @media (prefers-reduced-motion: reduce) {
14
- [data-scope='table'][data-part='loading-state'] {
15
- animation-duration: 0.01ms !important;
16
- }
17
- }
18
12
  }
19
13
 
20
14
  @layer xihan.components {
15
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
16
+ [data-scope='table'] {
17
+ --xh-table-skin: 1;
18
+ }
19
+
21
20
  /* 表格自己滚动:吸顶表头与横向吸附列都要一个确定的滚动容器才钉得住 */
22
21
  [data-scope='table'][data-part='root'] {
22
+ /* 作者塞进指示符槽的图标跟着字号走,与皮肤画的兜底字形同一把尺 */
23
+ --xh-icon-size: var(--xh-table-icon-size, var(--xh-glyph-size-text));
24
+
23
25
  /* 密度档只换这两个私有槽:单元格与列标题从 root 继承前者,字号靠 font-size 本身继承下去,
24
26
  换档不必给每个部件各写一条选择器,列宽那套算法也一点不碰 */
25
- --xh-_table-cell-py: var(--xh-space-2);
27
+ --xh-_table-cell-py: var(--xh-table-cell-py-md);
26
28
  --xh-_table-font-size: var(--xh-text-body-size);
27
29
 
28
30
  display: flex;
29
31
  position: relative;
30
32
  flex-direction: column;
31
- max-block-size: var(--xh-table-max-h, 24rem);
33
+ max-block-size: var(--xh-table-max-h, var(--xh-viewport-h-lg));
32
34
  border: var(--xh-stroke-thin) solid var(--xh-table-border, var(--xh-border-default));
33
35
  border-radius: var(--xh-table-radius, var(--xh-shape-surface));
34
36
  background: var(--xh-table-bg, var(--xh-bg-surface));
@@ -42,6 +44,15 @@
42
44
  display: none;
43
45
  }
44
46
 
47
+ /* 放进滚动区的表格:滚动交给滚动区的视口,表格自己不再定高、不再滚;
48
+ 吸顶表头与吸附列钉的是最近的滚动容器,于是也就钉在视口上。
49
+ 自定义元素那条路上 <xh-table> 宿主夹在内容层与表格根之间,两种嵌法都认 */
50
+ [data-scope='scroll-area'][data-part='content'] > [data-scope='table'][data-part='root'],
51
+ [data-scope='scroll-area'][data-part='content'] > xh-table > [data-scope='table'][data-part='root'] {
52
+ max-block-size: none;
53
+ overflow: visible;
54
+ }
55
+
45
56
  /* 纯容器:焦点落它身上不画环,高亮永远长在行上 */
46
57
  [data-scope='table'][data-part='root']:focus-visible {
47
58
  outline: none;
@@ -50,13 +61,13 @@
50
61
  /* —— 密度 —— */
51
62
  /* 中间档不写规则,缺省即中档 */
52
63
  [data-scope='table'][data-part='root'][data-size='sm'] {
53
- --xh-_table-cell-py: var(--xh-space-1);
54
- --xh-_table-font-size: var(--xh-font-size-sm);
64
+ --xh-_table-cell-py: var(--xh-table-cell-py-sm);
65
+ --xh-_table-font-size: var(--xh-control-font-sm);
55
66
  }
56
67
 
57
68
  [data-scope='table'][data-part='root'][data-size='lg'] {
58
- --xh-_table-cell-py: var(--xh-space-3);
59
- --xh-_table-font-size: var(--xh-font-size-lg);
69
+ --xh-_table-cell-py: var(--xh-table-cell-py-lg);
70
+ --xh-_table-font-size: var(--xh-control-font-lg);
60
71
  }
61
72
 
62
73
  [data-scope='table'][data-part='caption'] {
@@ -103,7 +114,7 @@
103
114
  透明的表头会让底下滚过的行透出来 */
104
115
  [data-scope='table'][data-part='header'][data-sticky] {
105
116
  position: sticky;
106
- z-index: var(--xh-table-sticky-header-z, var(--xh-layer-sticky));
117
+ z-index: var(--xh-table-sticky-header-layer, var(--xh-layer-sticky));
107
118
  inset-block-start: 0;
108
119
  }
109
120
 
@@ -116,7 +127,6 @@
116
127
  [data-scope='table'][data-part='row'] {
117
128
  display: flex;
118
129
  align-items: stretch;
119
- border-block-end: var(--xh-stroke-thin) solid var(--xh-table-row-border, var(--xh-border-subtle));
120
130
  /* 背景要实色:横向吸附的单元格靠 inherit 取它,透明会让滚过的内容透出来 */
121
131
  background: var(--xh-table-row-bg, var(--xh-bg-surface));
122
132
  }
@@ -130,9 +140,9 @@
130
140
  background: transparent;
131
141
  }
132
142
 
133
- /* 末行不画分隔线:容器自己有边框,两条叠在一起会显出一道粗边 */
134
- [data-scope='table'][data-part='body'] > [data-scope='table'][data-part='row']:last-child {
135
- border-block-end: none;
143
+ /* 行间线画在后一行的上沿,区段里第一个可见行不画:行只负责行与行之间,外边界归 root 与区段容器 */
144
+ [data-scope='table']:is([data-part='header'], [data-part='body'], [data-part='footer']) > :not([hidden]) ~ [data-scope='table']:is([data-part='row'], [data-part='expanded-row']) {
145
+ border-block-start: var(--xh-stroke-thin) solid var(--xh-table-row-border, var(--xh-border-subtle));
136
146
  }
137
147
 
138
148
  /* 悬停与键盘锚点共用中性灰轻档。data-highlighted 跟的是连接层记的焦点行,
@@ -185,16 +195,50 @@
185
195
  user-select: none;
186
196
  }
187
197
 
188
- /* 横向吸附:钉在滚动容器的行首侧。背景继承自行,跟着选中/高亮一起变,
189
- 滚过去时不会露出一块与所在行不同色的补丁 */
190
- [data-scope='table'][data-part='column-header'][data-sticky],
191
- [data-scope='table'][data-part='cell'][data-sticky] {
198
+ /* 横向吸附:钉在滚动容器的一侧。背景继承自行,跟着选中/高亮一起变,
199
+ 滚过去时不会露出一块与所在行不同色的补丁。
200
+ 同侧多列时的偏移由连接层按前面各列的宽度累加好写进 --xh-table-sticky-inset */
201
+ [data-scope='table'][data-part='column-header'][data-frozen],
202
+ [data-scope='table'][data-part='cell'][data-frozen] {
192
203
  position: sticky;
193
- z-index: var(--xh-table-sticky-column-z, 1);
194
- inset-inline-start: 0;
204
+ z-index: var(--xh-table-sticky-column-layer, 1);
195
205
  background: inherit;
196
206
  }
197
207
 
208
+ [data-scope='table'][data-part='column-header'][data-frozen='start'],
209
+ [data-scope='table'][data-part='cell'][data-frozen='start'] {
210
+ inset-inline-start: var(--xh-table-sticky-inset, 0);
211
+ }
212
+
213
+ [data-scope='table'][data-part='column-header'][data-frozen='end'],
214
+ [data-scope='table'][data-part='cell'][data-frozen='end'] {
215
+ inset-inline-end: var(--xh-table-sticky-inset, 0);
216
+ }
217
+
218
+ /* 表头行本身透明(露出表头底),吸附的列头继承到的就是透明——得自己带表头底,
219
+ 否则横向滚动时其它列头从它下面穿过去 */
220
+ [data-scope='table'][data-part='column-header'][data-frozen] {
221
+ background: var(--xh-table-header-bg, var(--xh-bg-subtle));
222
+ }
223
+
224
+ /* —— 三个可叠加的外观开关 —— */
225
+ /* 斑马纹:表体偶数行换一层浅底;悬停与选中的底色仍按原规则盖过它 */
226
+ [data-scope='table'][data-part='root'][data-striped] [data-scope='table'][data-part='body'] > [data-scope='table'][data-part='row']:nth-of-type(even) {
227
+ background: var(--xh-table-row-bg-striped, var(--xh-bg-subtle));
228
+ }
229
+
230
+ /* 无外框:只留行间横线,圆角也随之无意义 */
231
+ [data-scope='table'][data-part='root'][data-borderless] {
232
+ border: 0;
233
+ border-radius: 0;
234
+ }
235
+
236
+ /* 竖分隔线:列与列之间一道细线,末列不画 */
237
+ [data-scope='table'][data-part='root'][data-ruled] [data-scope='table'][data-part='column-header']:not(:last-child),
238
+ [data-scope='table'][data-part='root'][data-ruled] [data-scope='table'][data-part='cell']:not(:last-child) {
239
+ border-inline-end: var(--xh-stroke-thin) solid var(--xh-table-row-border, var(--xh-border-subtle));
240
+ }
241
+
198
242
  [data-scope='table'][data-part='cell'][data-disabled] {
199
243
  color: var(--xh-fg-disabled);
200
244
  }
@@ -207,8 +251,8 @@
207
251
  flex: none;
208
252
  align-items: center;
209
253
  justify-content: center;
210
- inline-size: var(--xh-table-trigger-size, 1rem);
211
- block-size: var(--xh-table-trigger-size, 1rem);
254
+ inline-size: var(--xh-table-trigger-size, var(--xh-control-indicator-size));
255
+ block-size: var(--xh-table-trigger-size, var(--xh-control-indicator-size));
212
256
  border-radius: var(--xh-table-trigger-radius, var(--xh-shape-control));
213
257
  cursor: pointer;
214
258
  }
@@ -228,29 +272,29 @@
228
272
  visibility: visible;
229
273
  }
230
274
 
231
- [data-scope='table'][data-part='select-all-trigger']:is([data-state='all'], [data-state='some']),
275
+ [data-scope='table'][data-part='select-all-trigger']:is([data-state='checked'], [data-state='indeterminate']),
232
276
  [data-scope='table'][data-part='row-select-trigger'][data-selected] {
233
277
  border-color: var(--xh-table-trigger-border-checked, var(--xh-table-trigger-bg-checked, var(--xh-bg-brand)));
234
278
  background: var(--xh-table-trigger-bg-checked, var(--xh-bg-brand));
235
279
  }
236
280
 
237
281
  /* 未选中时勾不出现,但盒子还在:它是边框画出来的空框 */
238
- [data-scope='table'][data-part='select-all-trigger'][data-state='none'],
282
+ [data-scope='table'][data-part='select-all-trigger'][data-state='unchecked'],
239
283
  [data-scope='table'][data-part='row-select-trigger']:not([data-selected]) {
240
284
  color: transparent;
241
285
  }
242
286
 
243
287
  /* 半选画一条横杠盖住勾:aria-checked=mixed 在视觉上也得与"全选"分得开 */
244
- [data-scope='table'][data-part='select-all-trigger'][data-state='some'] {
288
+ [data-scope='table'][data-part='select-all-trigger'][data-state='indeterminate'] {
245
289
  position: relative;
246
290
  color: transparent;
247
291
  }
248
292
 
249
- [data-scope='table'][data-part='select-all-trigger'][data-state='some']::after {
293
+ [data-scope='table'][data-part='select-all-trigger'][data-state='indeterminate']::after {
250
294
  position: absolute;
251
295
  inline-size: 60%;
252
296
  block-size: var(--xh-stroke-thick);
253
- border-radius: var(--xh-radius-full);
297
+ border-radius: var(--xh-shape-pill);
254
298
  background: var(--xh-table-trigger-fg, var(--xh-fg-on-brand));
255
299
  content: '';
256
300
  }
@@ -323,24 +367,45 @@
323
367
  /* 排序方向用箭头字形表出来:aria-sort 只报给读屏,视觉侧也得有对应的东西 */
324
368
  [data-scope='table'][data-part='sort-trigger']::after {
325
369
  color: var(--xh-table-sort-fg, var(--xh-fg-subtle));
326
- content: '↕';
370
+ content: '';
371
+ display: inline-block;
372
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
373
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
374
+ vertical-align: var(--xh-glyph-baseline-shift);
375
+ background-color: currentColor;
376
+ -webkit-mask: var(--xh-glyph-mark-sort) center / contain no-repeat;
377
+ mask: var(--xh-glyph-mark-sort) center / contain no-repeat;
327
378
  }
328
379
 
329
380
  [data-scope='table'][data-part='sort-trigger'][data-sort='asc']::after {
330
381
  color: var(--xh-table-sort-fg-active, var(--xh-fg-default));
331
- content: '↑';
382
+ content: '';
383
+ display: inline-block;
384
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
385
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
386
+ vertical-align: var(--xh-glyph-baseline-shift);
387
+ background-color: currentColor;
388
+ -webkit-mask: var(--xh-glyph-mark-sort-asc) center / contain no-repeat;
389
+ mask: var(--xh-glyph-mark-sort-asc) center / contain no-repeat;
332
390
  }
333
391
 
334
392
  [data-scope='table'][data-part='sort-trigger'][data-sort='desc']::after {
335
393
  color: var(--xh-table-sort-fg-active, var(--xh-fg-default));
336
- content: '↓';
394
+ content: '';
395
+ display: inline-block;
396
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
397
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
398
+ vertical-align: var(--xh-glyph-baseline-shift);
399
+ background-color: currentColor;
400
+ -webkit-mask: var(--xh-glyph-mark-sort-desc) center / contain no-repeat;
401
+ mask: var(--xh-glyph-mark-sort-desc) center / contain no-repeat;
337
402
  }
338
403
 
339
404
  /* 多字段排序的优先级角标:链里第几环就显几,只在参与排序时才有 data-sort-index */
340
405
  [data-scope='table'][data-part='sort-trigger'][data-sort-index]::before {
341
406
  order: 1;
342
407
  color: var(--xh-table-sort-fg-active, var(--xh-fg-default));
343
- font-size: var(--xh-font-size-xs);
408
+ font-size: var(--xh-text-caption-size);
344
409
  content: attr(data-sort-index);
345
410
  }
346
411
 
@@ -357,11 +422,10 @@
357
422
  outline-offset: calc(-1 * var(--xh-ring-width));
358
423
  }
359
424
 
360
- /* 详情行:靠 hidden 收起,不卸载作者节点。内容长在它的单元格里,行本身只画底色与分隔线 */
425
+ /* 详情行:靠 hidden 收起,不卸载作者节点。内容长在它的单元格里,行本身只画底色 */
361
426
  [data-scope='table'][data-part='expanded-row'] {
362
427
  display: flex;
363
428
  align-items: stretch;
364
- border-block-end: var(--xh-stroke-thin) solid var(--xh-table-row-border, var(--xh-border-subtle));
365
429
  background: var(--xh-table-detail-bg, var(--xh-bg-subtle));
366
430
  }
367
431
 
@@ -399,6 +463,41 @@
399
463
  }
400
464
 
401
465
  [data-scope='table'][data-part='loading-state'] {
402
- animation: xh-table-loading-pulse var(--xh-duration-slow) var(--xh-ease-standard) infinite alternate;
466
+ animation: xh-table-loading-pulse calc(var(--xh-spin-duration) / 2) var(--xh-ease-standard) infinite alternate;
467
+ }
468
+
469
+ /* 减少动效:呼吸停下,加载态只留静止的文案。两条规则是同一件事的两个触发条件:系统偏好与作者打的 data-motion */
470
+ @media (prefers-reduced-motion: reduce) {
471
+ [data-scope='table'][data-part='loading-state'] {
472
+ animation: none;
473
+ }
474
+ }
475
+
476
+ :where([data-motion='reduce']) [data-scope='table'][data-part='loading-state'] {
477
+ animation: none;
478
+ }
479
+
480
+ /* 没写内容就由皮肤画兜底字形;作者往部件里塞了自己的图形,这几条即不命中 */
481
+ [data-scope='table'][data-part='select-all-trigger']:empty::before,
482
+ [data-scope='table'][data-part='row-select-trigger']:empty::before {
483
+ content: '';
484
+ display: inline-block;
485
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
486
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
487
+ vertical-align: var(--xh-glyph-baseline-shift);
488
+ background-color: currentColor;
489
+ -webkit-mask: var(--xh-glyph-mark-check) center / contain no-repeat;
490
+ mask: var(--xh-glyph-mark-check) center / contain no-repeat;
491
+ }
492
+
493
+ [data-scope='table'][data-part='expand-trigger']:empty::before {
494
+ content: '';
495
+ display: inline-block;
496
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
497
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
498
+ vertical-align: var(--xh-glyph-baseline-shift);
499
+ background-color: currentColor;
500
+ -webkit-mask: var(--xh-glyph-mark-chevron-right) center / contain no-repeat;
501
+ mask: var(--xh-glyph-mark-chevron-right) center / contain no-repeat;
403
502
  }
404
503
  }
package/css/tabs.css CHANGED
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='tabs'] {
4
+ --xh-tabs-skin: 1;
5
+ }
6
+
2
7
  [data-scope='tabs'][data-part='root'] {
3
8
  /* 尺寸档只换这三个私有槽,标签从 root 继承它们,换档不必给每个部件各写一条选择器 */
4
9
  --xh-_tabs-trigger-h: var(--xh-control-h-md);
@@ -10,7 +15,7 @@
10
15
  --xh-_tabs-list-gap: var(--xh-control-gap-md);
11
16
  --xh-_tabs-list-bg: transparent;
12
17
  --xh-_tabs-list-p: var(--xh-space-0);
13
- --xh-_tabs-list-radius: var(--xh-radius-none);
18
+ --xh-_tabs-list-radius: 0;
14
19
  --xh-_tabs-list-border-width: var(--xh-stroke-thin);
15
20
  --xh-_tabs-trigger-border-width: 0;
16
21
  --xh-_tabs-trigger-border: transparent;
@@ -39,13 +44,13 @@
39
44
  [data-scope='tabs'][data-part='root'][data-size='sm'] {
40
45
  --xh-_tabs-trigger-h: var(--xh-control-h-sm);
41
46
  --xh-_tabs-trigger-px: var(--xh-control-px-sm);
42
- --xh-_tabs-trigger-font-size: var(--xh-font-size-sm);
47
+ --xh-_tabs-trigger-font-size: var(--xh-control-font-sm);
43
48
  }
44
49
 
45
50
  [data-scope='tabs'][data-part='root'][data-size='lg'] {
46
51
  --xh-_tabs-trigger-h: var(--xh-control-h-lg);
47
52
  --xh-_tabs-trigger-px: var(--xh-control-px-lg);
48
- --xh-_tabs-trigger-font-size: var(--xh-font-size-lg);
53
+ --xh-_tabs-trigger-font-size: var(--xh-control-font-lg);
49
54
  }
50
55
 
51
56
  /* —— 形态 —— */
@@ -71,7 +76,7 @@
71
76
  --xh-_tabs-list-border-width: 0;
72
77
  --xh-_tabs-trigger-bg-active: var(--xh-_tone-subtle, var(--xh-bg-surface));
73
78
  --xh-_tabs-trigger-bg-active-hover: var(--xh-_tone-subtle-hover, var(--xh-bg-surface));
74
- --xh-_tabs-trigger-shadow-active: var(--xh-elevation-1);
79
+ --xh-_tabs-trigger-shadow-active: var(--xh-elevation-raised);
75
80
  }
76
81
 
77
82
  [data-scope='tabs'][data-part='list'] {