@xihan-ui/styles 2.1.0 → 3.1.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 (152) hide show
  1. package/CHANGELOG.md +1261 -0
  2. package/README.md +6 -1
  3. package/component-tokens.d.ts +533 -96
  4. package/components.tokens.json +35461 -26045
  5. package/css/accordion.css +34 -8
  6. package/css/alert.css +59 -1
  7. package/css/anchor.css +46 -8
  8. package/css/approval.css +116 -76
  9. package/css/avatar-group.css +1 -1
  10. package/css/avatar.css +13 -5
  11. package/css/back-top.css +61 -42
  12. package/css/badge.css +62 -12
  13. package/css/bar-code.css +1 -1
  14. package/css/breadcrumb.css +62 -3
  15. package/css/button-group.css +4 -4
  16. package/css/button.css +67 -19
  17. package/css/calendar-picker.css +19 -0
  18. package/css/calendar-range-picker.css +39 -2
  19. package/css/card.css +115 -1
  20. package/css/carousel.css +148 -43
  21. package/css/cartesian-chart.css +830 -0
  22. package/css/cascader.css +181 -65
  23. package/css/checkbox-group.css +59 -89
  24. package/css/checkbox.css +16 -49
  25. package/css/citation.css +470 -0
  26. package/css/clipboard.css +20 -63
  27. package/css/code-view.css +101 -6
  28. package/css/collapsible.css +34 -8
  29. package/css/color-field.css +18 -0
  30. package/css/color-picker.css +128 -50
  31. package/css/color-slider.css +22 -15
  32. package/css/color-swatch-picker.css +3 -1
  33. package/css/combobox.css +80 -33
  34. package/css/command.css +45 -43
  35. package/css/context-menu.css +39 -22
  36. package/css/date-field.css +56 -0
  37. package/css/date-picker.css +132 -26
  38. package/css/date-range-picker.css +337 -14
  39. package/css/description.css +17 -1
  40. package/css/descriptions.css +42 -0
  41. package/css/dialog.css +94 -36
  42. package/css/diff-view.css +97 -12
  43. package/css/download-trigger.css +44 -78
  44. package/css/drawer.css +160 -119
  45. package/css/editable.css +19 -0
  46. package/css/empty-state.css +7 -6
  47. package/css/field-array.css +36 -0
  48. package/css/field.css +20 -18
  49. package/css/file-upload.css +125 -135
  50. package/css/flex.css +324 -8
  51. package/css/float-button.css +263 -38
  52. package/css/floating-panel.css +53 -19
  53. package/css/form.css +31 -17
  54. package/css/funnel-chart.css +106 -0
  55. package/css/graph-chart.css +205 -0
  56. package/css/grid-list.css +235 -0
  57. package/css/grid.css +252 -1
  58. package/css/heatmap.css +119 -36
  59. package/css/hierarchy-chart.css +298 -0
  60. package/css/hover-card.css +31 -14
  61. package/css/icon.css +100 -0
  62. package/css/image-cropper.css +33 -7
  63. package/css/image-viewer.css +167 -22
  64. package/css/image.css +23 -4
  65. package/css/input-group.css +11 -13
  66. package/css/json-viewer.css +50 -2
  67. package/css/layout.css +68 -18
  68. package/css/liquid.css +106 -0
  69. package/css/list.css +1 -1
  70. package/css/listbox.css +46 -6
  71. package/css/loading-bar.css +27 -13
  72. package/css/log.css +129 -33
  73. package/css/markdown-stream.css +1 -1
  74. package/css/marquee.css +163 -7
  75. package/css/matrix-code.css +1 -1
  76. package/css/mention.css +35 -19
  77. package/css/menu.css +35 -21
  78. package/css/menubar.css +36 -20
  79. package/css/message-feed.css +172 -51
  80. package/css/navigation-menu.css +172 -11
  81. package/css/notification.css +435 -108
  82. package/css/number-field.css +22 -0
  83. package/css/page-header.css +3 -1
  84. package/css/pagination.css +126 -24
  85. package/css/password-input.css +40 -8
  86. package/css/pie-chart.css +247 -0
  87. package/css/popconfirm.css +44 -50
  88. package/css/popover.css +50 -14
  89. package/css/progress.css +308 -10
  90. package/css/prompt-input.css +36 -0
  91. package/css/question-flow.css +97 -57
  92. package/css/radar-chart.css +233 -0
  93. package/css/radio-group.css +363 -5
  94. package/css/rating.css +21 -23
  95. package/css/reasoning.css +61 -54
  96. package/css/resizable.css +6 -1
  97. package/css/sankey-chart.css +174 -0
  98. package/css/scroll-area.css +32 -0
  99. package/css/scrollbar.css +1 -1
  100. package/css/select.css +74 -55
  101. package/css/side-nav.css +173 -24
  102. package/css/signature-pad.css +16 -1
  103. package/css/skeleton.css +13 -14
  104. package/css/slider.css +35 -13
  105. package/css/sortable.css +37 -7
  106. package/css/sparkline.css +164 -0
  107. package/css/spinner.css +50 -22
  108. package/css/splitter.css +7 -1
  109. package/css/statistic.css +10 -0
  110. package/css/steps.css +248 -29
  111. package/css/switch.css +23 -48
  112. package/css/table.css +175 -118
  113. package/css/tabs.css +231 -19
  114. package/css/tag-group.css +27 -3
  115. package/css/tag.css +53 -12
  116. package/css/tags-input.css +40 -3
  117. package/css/text-field.css +19 -0
  118. package/css/time-field.css +20 -1
  119. package/css/time-picker.css +103 -17
  120. package/css/time-range-picker.css +45 -16
  121. package/css/timeline.css +1 -1
  122. package/css/toggle-group.css +4 -4
  123. package/css/toggle.css +4 -3
  124. package/css/tool-call.css +59 -51
  125. package/css/toolbar.css +101 -3
  126. package/css/tooltip.css +15 -2
  127. package/css/tour.css +42 -12
  128. package/css/transfer.css +98 -75
  129. package/css/tree-select.css +232 -53
  130. package/css/tree.css +271 -55
  131. package/css/truncate.css +55 -12
  132. package/css/typography.css +93 -0
  133. package/css/virtualizer.css +56 -0
  134. package/css/watermark.css +23 -0
  135. package/family/action-control.css +14 -5
  136. package/family/chart.css +480 -0
  137. package/family/check-mark.css +94 -0
  138. package/family/choice-card.css +97 -0
  139. package/family/collection-item.css +9 -15
  140. package/family/field-chrome.css +1 -12
  141. package/family/loading-ring.css +95 -0
  142. package/family/material.css +72 -0
  143. package/family/menu-choice.css +56 -0
  144. package/family/motion.css +207 -10
  145. package/family/tag-list.css +59 -0
  146. package/index.css +13824 -4030
  147. package/index.unlayered.css +13403 -3605
  148. package/package.json +20 -8
  149. package/css/gradient-text.css +0 -91
  150. package/css/icon-wrapper.css +0 -81
  151. package/css/segmented.css +0 -280
  152. package/css/toast.css +0 -499
@@ -8,16 +8,8 @@
8
8
  /* 共享关键帧由 family/motion.css 提供,本皮肤 @import 它,单独引入仍成立。 */
9
9
  @import '../family/motion.css';
10
10
  @import '../family/action-control.css';
11
-
12
- @layer xihan.motion {
13
- /* 转圈的关键帧定义在本文件里:引用别处文件的动画名时那份文件不一定在场,动画会静默不跑 */
14
- @keyframes xh-popconfirm-rotate {
15
- to {
16
- transform: rotate(1turn);
17
- }
18
- }
19
- }
20
-
11
+ @import '../family/loading-ring.css';
12
+ @import '../family/material.css';
21
13
 
22
14
  @layer xihan.components {
23
15
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
@@ -90,9 +82,37 @@
90
82
  --xh-_overlay-enter-left: 1;
91
83
  }
92
84
 
85
+ /* 交叉轴原点按对齐取:start / end 对齐时面板与锚点在那一端齐平,缩放从那一端涨开,贴着小触发器的面板
86
+ 才像是从触发器里长出来;居中对齐留在中线。上下两侧的 start / end 是行内轴上的逻辑对齐,RTL 下起始缘在右;
87
+ 左右两侧的 start / end 落在块轴上,不随书写方向翻 */
88
+ /* 起始缘与末尾缘按就近的书写方向取:LTR 起始在左,RTL 起始在右 */
89
+ [data-scope='popconfirm'][data-part='positioner']:is([data-placement='top-start'], [data-placement='bottom-start']) {
90
+ --xh-_overlay-origin-x: var(--xh-direction-start);
91
+ }
92
+
93
+ [data-scope='popconfirm'][data-part='positioner']:is([data-placement='top-end'], [data-placement='bottom-end']) {
94
+ --xh-_overlay-origin-x: var(--xh-direction-end);
95
+ }
96
+
97
+ [data-scope='popconfirm'][data-part='positioner']:is([data-placement='left-start'], [data-placement='right-start']) {
98
+ --xh-_overlay-origin-y: 0;
99
+ }
100
+
101
+ [data-scope='popconfirm'][data-part='positioner']:is([data-placement='left-end'], [data-placement='right-end']) {
102
+ --xh-_overlay-origin-y: 100%;
103
+ }
104
+
93
105
  /* 面板排成三列:左列吃掉剩余宽度,右两列各摆一颗按钮。
94
106
  标题、说明与作者塞进来的内容各占一整行,两颗按钮因此并排落在最后一行的右侧。 */
95
107
  [data-scope='popconfirm'][data-part='content'] {
108
+ /* 材质由家族配方画(family/material.css 的 frosted 面:描边、底、1px 顶光、前景、投影与背景滤镜),
109
+ 这里只把使用者槽接进来 */
110
+ --xh-frosted-bg: var(--xh-popconfirm-bg, var(--xh-_material-bg));
111
+ --xh-frosted-border: var(--xh-popconfirm-border, var(--xh-_material-border));
112
+ --xh-frosted-fg: var(--xh-popconfirm-fg, var(--xh-_material-fg));
113
+ --xh-frosted-shadow: var(--xh-popconfirm-shadow, var(--xh-_material-shadow));
114
+ --xh-frosted-backdrop: var(--xh-popconfirm-backdrop, var(--xh-_material-backdrop));
115
+
96
116
  /* 作者塞进面板里的图标按控件档取;两颗动作钮另由家族配方按 sm 档下发字形尺 */
97
117
  --xh-icon-size: var(--xh-popconfirm-icon-size, var(--xh-glyph-size-md));
98
118
  /* 尺寸档只换这两个私有槽 */
@@ -114,20 +134,7 @@
114
134
  overscroll-behavior: contain;
115
135
  padding-block: var(--xh-popconfirm-py, var(--xh-_popconfirm-pad));
116
136
  padding-inline: var(--xh-popconfirm-px, var(--xh-_popconfirm-pad));
117
- border: var(--xh-stroke-thin) solid var(--xh-popconfirm-border, var(--xh-material-frosted-border));
118
137
  border-radius: var(--xh-popconfirm-radius, var(--xh-shape-overlay));
119
- /* 与 Popover 共用 M2 配方:透明 tint 在底层,内侧一像素顶光只表达材质厚度。 */
120
- background:
121
- linear-gradient(
122
- to bottom,
123
- var(--xh-material-frosted-highlight) 0 var(--xh-stroke-thin),
124
- transparent var(--xh-stroke-thin)
125
- ),
126
- var(--xh-popconfirm-bg, var(--xh-material-frosted-bg));
127
- color: var(--xh-popconfirm-fg, var(--xh-material-frosted-fg));
128
- box-shadow: var(--xh-popconfirm-shadow, var(--xh-material-frosted-shadow));
129
- -webkit-backdrop-filter: var(--xh-popconfirm-backdrop, var(--xh-material-frosted-backdrop));
130
- backdrop-filter: var(--xh-popconfirm-backdrop, var(--xh-material-frosted-backdrop));
131
138
  pointer-events: auto;
132
139
  /* 进退场的缩放锚在贴着锚点的那条边上;两个分量由 positioner 按 placement 写进私有槽 */
133
140
  transform-origin: var(--xh-_overlay-origin-x, 50%) var(--xh-_overlay-origin-y, 50%);
@@ -152,7 +159,8 @@
152
159
  outline: none;
153
160
  }
154
161
 
155
- [data-scope='popconfirm'][data-part='content'][data-state='open'] {
162
+ /* 挂载时就开着的那一段(data-instant)属于首帧,直接呈现 */
163
+ [data-scope='popconfirm'][data-part='content'][data-state='open']:not([data-instant]) {
156
164
  animation: xh-overlay-pop-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong);
157
165
  }
158
166
 
@@ -254,16 +262,18 @@
254
262
  --xh-action-ring-color-focus-visible: currentColor;
255
263
  }
256
264
 
257
- /* 异步确认挂起:确认文案前转一枚圆环;不是禁用,焦点与对比度都保持,在途指针由家族 loading 面给 */
258
- [data-scope='popconfirm'][data-part='confirm-trigger'][data-loading]::before {
259
- content: '';
260
- inline-size: 1em;
261
- block-size: 1em;
262
- margin-inline-end: var(--xh-space-1);
263
- border: var(--xh-stroke-thin) solid transparent;
264
- border-block-start-color: currentcolor;
265
- border-radius: var(--xh-shape-circle);
266
- animation: xh-popconfirm-rotate var(--xh-popconfirm-loading-duration, var(--xh-spin-duration)) var(--xh-motion-ease-loop) infinite;
265
+ /* 异步确认挂起:环由加载环配方压在确认钮正中(连接层投影 data-xh-loading-ring="overlay"),直径取钮的图标档,
266
+ 起始边取钮的真实前景、转速认使用者槽。不是禁用,焦点与对比度都保持,在途指针由家族 loading 面给。
267
+ 确认文案随前景淡到透明留位:进入挂起等一个 micro 才起淡(快速落定什么都不闪),落定时不等、按 micro 淡回 */
268
+ [data-scope='popconfirm'][data-part='confirm-trigger'] {
269
+ --xh-_loading-ring-fg: var(--xh-popconfirm-confirm-fg, var(--xh-_action-variant-fg-loading));
270
+ --xh-_loading-ring-duration: var(--xh-popconfirm-loading-duration, var(--xh-motion-loop-spin));
271
+ }
272
+
273
+ [data-scope='popconfirm'][data-part='confirm-trigger'][data-loading] {
274
+ --xh-action-fg-loading: transparent;
275
+
276
+ transition-delay: var(--xh-motion-duration-micro);
267
277
  }
268
278
 
269
279
  /* 取消是中性的次要出口,不参与语气:content 上的 data-tone 会把 --xh-_tone-* 一路继承下来,
@@ -285,22 +295,6 @@
285
295
  --xh-action-border-focus-visible: var(--xh-popconfirm-cancel-border, var(--xh-border-control));
286
296
  }
287
297
 
288
- /* 减少动效:圆环停下并整圈换成虚线,静止的形状仍然读得出"还没好"。
289
- 两条规则是同一件事的两个触发条件:系统偏好与作者打的 data-motion */
290
- @media (prefers-reduced-motion: reduce) {
291
- [data-scope='popconfirm'][data-part='confirm-trigger'][data-loading]::before {
292
- border-style: dotted;
293
- border-color: currentColor;
294
- animation: none;
295
- }
296
- }
297
-
298
- :where([data-motion='reduce']) [data-scope='popconfirm'][data-part='confirm-trigger'][data-loading]::before {
299
- border-style: dotted;
300
- border-color: currentColor;
301
- animation: none;
302
- }
303
-
304
298
  @media (forced-colors: active) {
305
299
  /* 系统色接管内容边界;装饰顶光在这一档没有额外信息。两颗按钮的强制色由家族配方给 */
306
300
  [data-scope='popconfirm'][data-part='content'] {
package/css/popover.css CHANGED
@@ -8,6 +8,7 @@
8
8
  /* 共享关键帧由 family/motion.css 提供,本皮肤 @import 它,单独引入仍成立。 */
9
9
  @import '../family/motion.css';
10
10
  @import '../family/action-control.css';
11
+ @import '../family/material.css';
11
12
 
12
13
  @layer xihan.components {
13
14
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
@@ -61,6 +62,26 @@
61
62
  --xh-_overlay-enter-left: 1;
62
63
  }
63
64
 
65
+ /* 交叉轴原点按对齐取:start / end 对齐时面板与锚点在那一端齐平,缩放从那一端涨开,贴着小触发器的面板
66
+ 才像是从触发器里长出来;居中对齐留在中线。上下两侧的 start / end 是行内轴上的逻辑对齐,RTL 下起始缘在右;
67
+ 左右两侧的 start / end 落在块轴上,不随书写方向翻 */
68
+ /* 起始缘与末尾缘按就近的书写方向取:LTR 起始在左,RTL 起始在右 */
69
+ [data-scope='popover'][data-part='positioner']:is([data-placement='top-start'], [data-placement='bottom-start']) {
70
+ --xh-_overlay-origin-x: var(--xh-direction-start);
71
+ }
72
+
73
+ [data-scope='popover'][data-part='positioner']:is([data-placement='top-end'], [data-placement='bottom-end']) {
74
+ --xh-_overlay-origin-x: var(--xh-direction-end);
75
+ }
76
+
77
+ [data-scope='popover'][data-part='positioner']:is([data-placement='left-start'], [data-placement='right-start']) {
78
+ --xh-_overlay-origin-y: 0;
79
+ }
80
+
81
+ [data-scope='popover'][data-part='positioner']:is([data-placement='left-end'], [data-placement='right-end']) {
82
+ --xh-_overlay-origin-y: 100%;
83
+ }
84
+
64
85
  /*
65
86
  * 触发器是页面上的独立文字按钮:盒型、md 档几何、outline 四态面、0.97 按压并换底与粗指针命中区
66
87
  * 由家族配方按连接层投影的 data-xh-action-variant / -size 给出。皮肤没有自己的外观规则,
@@ -76,6 +97,14 @@
76
97
  }
77
98
 
78
99
  [data-scope='popover'][data-part='content'] {
100
+ /* 材质由家族配方画(family/material.css 的 frosted 面:描边、底、1px 顶光、前景、投影与背景滤镜),
101
+ 这里只把使用者槽接进来 */
102
+ --xh-frosted-bg: var(--xh-popover-bg, var(--xh-_material-bg));
103
+ --xh-frosted-border: var(--xh-popover-border, var(--xh-_material-border));
104
+ --xh-frosted-fg: var(--xh-popover-fg, var(--xh-_material-fg));
105
+ --xh-frosted-shadow: var(--xh-popover-shadow, var(--xh-_material-shadow));
106
+ --xh-frosted-backdrop: var(--xh-popover-backdrop, var(--xh-_material-backdrop));
107
+
79
108
  /* 作者塞进面板里的图标按控件档取;关闭钮另由家族配方按 sm 档下发字形尺 */
80
109
  --xh-icon-size: var(--xh-popover-icon-size, var(--xh-glyph-size-md));
81
110
 
@@ -98,20 +127,7 @@
98
127
  overscroll-behavior: contain;
99
128
  padding-block: var(--xh-popover-py, var(--xh-_popover-pad));
100
129
  padding-inline: var(--xh-popover-px, var(--xh-_popover-pad));
101
- border: var(--xh-stroke-thin) solid var(--xh-popover-border, var(--xh-material-frosted-border));
102
130
  border-radius: var(--xh-popover-radius, var(--xh-shape-overlay));
103
- /* 顶光只占内侧一像素;透明 tint 是底层,正文自己保持不透明。 */
104
- background:
105
- linear-gradient(
106
- to bottom,
107
- var(--xh-material-frosted-highlight) 0 var(--xh-stroke-thin),
108
- transparent var(--xh-stroke-thin)
109
- ),
110
- var(--xh-popover-bg, var(--xh-material-frosted-bg));
111
- color: var(--xh-popover-fg, var(--xh-material-frosted-fg));
112
- box-shadow: var(--xh-popover-shadow, var(--xh-material-frosted-shadow));
113
- -webkit-backdrop-filter: var(--xh-popover-backdrop, var(--xh-material-frosted-backdrop));
114
- backdrop-filter: var(--xh-popover-backdrop, var(--xh-material-frosted-backdrop));
115
131
  pointer-events: auto;
116
132
  /* 进退场的缩放锚在贴着锚点的那条边上;两个分量由 positioner 按 placement 写进私有槽 */
117
133
  transform-origin: var(--xh-_overlay-origin-x, 50%) var(--xh-_overlay-origin-y, 50%);
@@ -127,7 +143,8 @@
127
143
  outline: none;
128
144
  }
129
145
 
130
- [data-scope='popover'][data-part='content'][data-state='open'] {
146
+ /* 挂载时就开着的那一段(data-instant)属于首帧,直接呈现 */
147
+ [data-scope='popover'][data-part='content'][data-state='open']:not([data-instant]) {
131
148
  animation: xh-overlay-pop-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong);
132
149
  }
133
150
 
@@ -240,4 +257,23 @@
240
257
  display: none;
241
258
  }
242
259
  }
260
+
261
+ @media (forced-colors: active) {
262
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
263
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
264
+ 按钮面上取 ButtonText,悬停与按下时家族把按钮涂成 Highlight、字形换 HighlightText;
265
+ 禁用取 GrayText */
266
+ [data-scope='popover'][data-part='close-trigger']:empty::before {
267
+ forced-color-adjust: none;
268
+ background-color: ButtonText;
269
+ }
270
+
271
+ [data-scope='popover'][data-part='close-trigger']:not([data-disabled]):not([data-loading]):is(:hover, :active, [data-pressed]):empty::before {
272
+ background-color: HighlightText;
273
+ }
274
+
275
+ [data-scope='popover'][data-part='close-trigger'][data-disabled]:empty::before {
276
+ background-color: GrayText;
277
+ }
278
+ }
243
279
  }
package/css/progress.css CHANGED
@@ -8,13 +8,26 @@
8
8
  @layer xihan.motion {
9
9
  /* 动画名的查找只认「文档里有没有这个名字」:引用别处文件里的名字,那份文件不一定在场,
10
10
  动画会静默地整个不跑。每份皮肤都能单独引入,所以它用到的关键帧都写在自己身上。 */
11
+ /* 固定宽度的一段从轨道行首外扫到行尾外:只动 translate,不动布局。
12
+ 百分比参照这一段自身的宽度(轨道的 40%),RTL 下两端由私有槽换号 */
11
13
  @keyframes xh-progress-indeterminate {
12
14
  from {
13
- inset-inline-start: -40%;
15
+ translate: var(--xh-_progress-sweep-from, -100%) 0;
14
16
  }
15
17
 
16
18
  to {
17
- inset-inline-start: 100%;
19
+ translate: var(--xh-_progress-sweep-to, 250%) 0;
20
+ }
21
+ }
22
+
23
+ /* 条纹沿行向流动:每轮挪过一整格纹理,首尾两帧的图案重合,循环无缝。RTL 下行尾在左,位移随方向符号翻转 */
24
+ @keyframes xh-progress-stripes {
25
+ from {
26
+ background-position: 0 0;
27
+ }
28
+
29
+ to {
30
+ background-position: calc(var(--xh-_progress-stripe-size) * var(--xh-direction-sign)) 0;
18
31
  }
19
32
  }
20
33
  }
@@ -30,11 +43,17 @@
30
43
  [data-scope='progress'][data-part='root'] {
31
44
  --xh-_progress-thickness: var(--xh-progress-thickness, var(--xh-track-thickness));
32
45
 
46
+ /* 目标刻度与环形的刻度值按根落位 */
47
+ position: relative;
33
48
  display: inline-block;
34
49
  inline-size: 100%;
35
50
  }
36
51
 
52
+ /* 线形轨道把填充放在交叉轴正中:子弹图的填充比轨道窄,压在色带正中 */
37
53
  [data-scope='progress'][data-part='track'] {
54
+ position: relative;
55
+ display: flex;
56
+ align-items: center;
38
57
  overflow: hidden;
39
58
  inline-size: 100%;
40
59
  block-size: var(--xh-_progress-thickness);
@@ -45,10 +64,73 @@
45
64
  /* 语气槽由 root 上的 data-tone 声明、经继承发下来;没写 data-tone 时退回原来的品牌色。
46
65
  轨道不跟着换族:只有已完成的那段换色,底槽保持中性才读得出"完成了多少"。 */
47
66
  [data-scope='progress'][data-part='range'] {
48
- block-size: 100%;
67
+ /* 填充铺满轨道,按未完成的比例往行首平移、由轨道裁掉:进度变化只走合成,不重排也不重绘。
68
+ 露出来的前端保留自己的圆角,行首那一侧由轨道的圆角裁出 */
69
+ /* 定位后才压得住同在轨道里的色带 */
70
+ position: relative;
71
+ flex: none;
72
+ inline-size: 100%;
73
+ block-size: var(--xh-_progress-range-size, 100%);
49
74
  border-radius: var(--xh-progress-range-radius, var(--xh-shape-pill));
50
75
  background: var(--xh-progress-range, var(--xh-_tone, var(--xh-bg-brand)));
51
- transition: inline-size var(--xh-motion-duration-enter) var(--xh-motion-ease-continuous);
76
+ translate: calc((var(--xh-_progress-value, 0) - 1) * 100%) 0;
77
+ transition: translate var(--xh-motion-duration-move) var(--xh-motion-ease-continuous);
78
+ }
79
+
80
+ /* translate 只认物理方向:RTL 下行首在右边,平移与不定进度的扫向一起翻转 */
81
+ [data-scope='progress'][data-part='range'][data-variant='line']:dir(rtl) {
82
+ --xh-_progress-sweep-from: 100%;
83
+ --xh-_progress-sweep-to: -250%;
84
+
85
+ translate: calc((1 - var(--xh-_progress-value, 0)) * 100%) 0;
86
+ }
87
+
88
+ /* —— 线形的三样外观:分段、条纹与缓冲 —— */
89
+ /* 分段:遮罩把轨道切成等宽的格,格间留一道间隙,填充、缓冲与色带随轨道一起被切开。
90
+ 周期是(全长 + 一道间隙)÷ 格数,最后一格恰好收在行尾,图案左右对称,RTL 下不必翻转 */
91
+ [data-scope='progress'][data-part='track'][data-stepped] {
92
+ --xh-_progress-step-gap: var(--xh-progress-step-gap, var(--xh-space-0_5));
93
+ --xh-_progress-step-period: calc((100% + var(--xh-_progress-step-gap)) / var(--xh-_progress-steps, 1));
94
+ --xh-_progress-step-mask: repeating-linear-gradient(
95
+ to right,
96
+ black 0 calc(var(--xh-_progress-step-period) - var(--xh-_progress-step-gap)),
97
+ transparent 0 var(--xh-_progress-step-period)
98
+ );
99
+
100
+ -webkit-mask-image: var(--xh-_progress-step-mask);
101
+ mask-image: var(--xh-_progress-step-mask);
102
+ }
103
+
104
+ /* 条纹:填充上铺一层 45° 斜纹,颜色取填充同族更深一档。纹理是正方形平铺,45° 才能首尾相接;
105
+ RTL 下斜向随方向符号镜像 */
106
+ [data-scope='progress'][data-part='range'][data-striped] {
107
+ --xh-_progress-stripe: var(--xh-progress-stripe-color, var(--xh-_tone-active, var(--xh-bg-brand-active)));
108
+ --xh-_progress-stripe-size: var(--xh-progress-stripe-size, var(--xh-space-4));
109
+
110
+ background-image: linear-gradient(
111
+ calc(45deg * var(--xh-direction-sign)),
112
+ var(--xh-_progress-stripe) 25%,
113
+ transparent 25% 50%,
114
+ var(--xh-_progress-stripe) 50% 75%,
115
+ transparent 75%
116
+ );
117
+ background-size: var(--xh-_progress-stripe-size) var(--xh-_progress-stripe-size);
118
+ }
119
+
120
+ /* 只在进行中流动:完成了就停下;进度未知时整段已在往复扫,不再叠一层流动 */
121
+ [data-scope='progress'][data-part='range'][data-striped][data-state='loading'] {
122
+ animation: xh-progress-stripes var(--xh-progress-stripe-duration, var(--xh-motion-loop-shimmer)) var(--xh-motion-ease-loop) infinite;
123
+ }
124
+
125
+ /* 缓冲段与填充同一种画法:铺满轨道、按缓冲比例往行首平移、由轨道裁掉,只走合成。
126
+ 颜色取填充那一族的淡色,排在填充之前、被它压住。物理方向的平移量乘方向符号,RTL 下往右收 */
127
+ [data-scope='progress'][data-part='buffer'] {
128
+ position: absolute;
129
+ inset: 0;
130
+ border-radius: var(--xh-progress-buffer-radius, var(--xh-progress-range-radius, var(--xh-shape-pill)));
131
+ background: var(--xh-progress-buffer, var(--xh-_tone-subtle-active));
132
+ translate: calc((var(--xh-_progress-buffer, 0) - 1) * 100% * var(--xh-direction-sign)) 0;
133
+ transition: translate var(--xh-motion-duration-move) var(--xh-motion-ease-continuous);
52
134
  }
53
135
 
54
136
  /* —— 尺寸 —— */
@@ -113,11 +195,157 @@
113
195
  [data-scope='progress'][data-part='range'][data-variant='dashboard'] {
114
196
  stroke: var(--xh-progress-range, var(--xh-_tone, var(--xh-bg-brand)));
115
197
  stroke-linecap: var(--xh-progress-linecap, round);
116
- transition: stroke-dashoffset var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
198
+ /* 环形的走完多少由虚线偏移表达,线形那一套按比例的平移在这里归零:SVG 图元的百分比平移按整个 viewBox 算,会把整段弧挪出画面 */
199
+ translate: none;
200
+ transition: stroke-dashoffset var(--xh-motion-duration-move) var(--xh-motion-ease-continuous);
201
+ }
202
+
203
+ /* —— 量的刻画:分段色带、目标刻度、量程刻度与指针 —— */
204
+ /* 子弹图:有分段时轨道加厚一档,填充收窄成中间一条压在色带上,色带在填充两侧照样读得出。
205
+ 三档厚度各比普通线形高一档,阶梯次序不变 */
206
+ [data-scope='progress'][data-part='root'][data-variant='line'][data-banded] {
207
+ --xh-_progress-thickness: var(--xh-progress-thickness, var(--xh-space-3));
208
+ --xh-_progress-range-size: 50%;
117
209
  }
118
210
 
119
- /* 进度未知:线形条改为一小段来回扫。宽度交给动画,连接层算出的 inline-size 与
120
- 那条过渡都得让开,否则每帧都在跟动画抢同一个属性 */
211
+ [data-scope='progress'][data-part='root'][data-variant='line'][data-banded][data-size='sm'] {
212
+ --xh-_progress-thickness: var(--xh-progress-thickness, var(--xh-space-2));
213
+ }
214
+
215
+ [data-scope='progress'][data-part='root'][data-variant='line'][data-banded][data-size='lg'] {
216
+ --xh-_progress-thickness: var(--xh-progress-thickness, var(--xh-space-4));
217
+ }
218
+
219
+ /* 色带取本段语气的淡色:同一族颜色里填充是实色、色带浅一档,读得出填充落在哪一段 */
220
+ [data-scope='progress'][data-part='threshold'] {
221
+ --xh-_progress-threshold: var(--xh-progress-threshold-color, var(--xh-_tone-subtle-active));
222
+ }
223
+
224
+ [data-scope='progress'][data-part='threshold'][data-variant='line'] {
225
+ position: absolute;
226
+ inset-block: 0;
227
+ inset-inline-start: calc(var(--xh-_progress-from, 0) * 100%);
228
+ inline-size: calc((var(--xh-_progress-to, 0) - var(--xh-_progress-from, 0)) * 100%);
229
+ background: var(--xh-_progress-threshold);
230
+ }
231
+
232
+ [data-scope='progress'][data-part='threshold'][data-variant='circle'],
233
+ [data-scope='progress'][data-part='threshold'][data-variant='dashboard'] {
234
+ stroke: var(--xh-_progress-threshold);
235
+ }
236
+
237
+ /* 目标刻度:一道竖线横穿轨道、上下各探出一点,读者一眼看出离目标还差多少 */
238
+ [data-scope='progress'][data-part='target'] {
239
+ --xh-_progress-target: var(--xh-progress-target-color, var(--xh-fg-default));
240
+ }
241
+
242
+ [data-scope='progress'][data-part='target'][data-variant='line'] {
243
+ position: absolute;
244
+ inset-block-start: calc(-1 * var(--xh-space-1));
245
+ inset-inline-start: calc(var(--xh-_progress-at, 0) * 100%);
246
+ inline-size: var(--xh-stroke-thick);
247
+ block-size: calc(var(--xh-_progress-thickness) + 2 * var(--xh-space-1));
248
+ border-radius: var(--xh-progress-target-radius, var(--xh-shape-pill));
249
+ background: var(--xh-_progress-target);
250
+ translate: -50% 0;
251
+ }
252
+
253
+ [data-scope='progress'][data-part='target'][data-variant='line']:dir(rtl) {
254
+ translate: 50% 0;
255
+ }
256
+
257
+ [data-scope='progress'][data-part='target'][data-variant='circle'],
258
+ [data-scope='progress'][data-part='target'][data-variant='dashboard'] {
259
+ stroke: var(--xh-_progress-target);
260
+ stroke-width: var(--xh-stroke-thick);
261
+ stroke-linecap: round;
262
+ vector-effect: non-scaling-stroke;
263
+ }
264
+
265
+ /* 量程刻度:图表家具,线与字都取图表的标注色,数字等宽,不随进度的语气换色 */
266
+ [data-scope='progress'][data-part='scale'] {
267
+ color: var(--xh-chart-label);
268
+ font-size: var(--xh-text-caption-size);
269
+ font-variant-numeric: tabular-nums;
270
+ line-height: var(--xh-leading-tight);
271
+ pointer-events: none;
272
+ }
273
+
274
+ [data-scope='progress'][data-part='scale'][data-variant='line'] {
275
+ position: relative;
276
+ block-size: calc(var(--xh-chart-tick-length) + var(--xh-chart-label-gap) + var(--xh-text-caption-size) * var(--xh-leading-tight));
277
+ margin-block-start: var(--xh-space-1);
278
+ }
279
+
280
+ [data-scope='progress'][data-part='scale-tick'][data-variant='line'] {
281
+ position: absolute;
282
+ inset-block-start: 0;
283
+ inset-inline-start: calc(var(--xh-_progress-at, 0) * 100%);
284
+ inline-size: var(--xh-stroke-thin);
285
+ block-size: var(--xh-chart-tick-length);
286
+ background: var(--xh-chart-axis);
287
+ translate: -50% 0;
288
+ }
289
+
290
+ [data-scope='progress'][data-part='scale-tick'][data-variant='line']:dir(rtl) {
291
+ translate: 50% 0;
292
+ }
293
+
294
+ /* 两端的刻度值贴着轨道两端对齐,中间的居中在刻度线上:连接层给出对齐比例 */
295
+ [data-scope='progress'][data-part='scale-label'][data-variant='line'] {
296
+ position: absolute;
297
+ inset-block-start: calc(var(--xh-chart-tick-length) + var(--xh-chart-label-gap));
298
+ inset-inline-start: calc(var(--xh-_progress-at, 0) * 100%);
299
+ white-space: nowrap;
300
+ translate: calc(var(--xh-_progress-label-align) * -100%) 0;
301
+ }
302
+
303
+ [data-scope='progress'][data-part='scale-label'][data-variant='line']:dir(rtl) {
304
+ translate: calc(var(--xh-_progress-label-align) * 100%) 0;
305
+ }
306
+
307
+ [data-scope='progress'][data-part='scale-tick'][data-variant='circle'],
308
+ [data-scope='progress'][data-part='scale-tick'][data-variant='dashboard'] {
309
+ stroke: var(--xh-chart-axis);
310
+ stroke-width: var(--xh-stroke-thin);
311
+ vector-effect: non-scaling-stroke;
312
+ }
313
+
314
+ /* 环形的刻度值叠在环上:与画面同一格,按连接层给出的百分比坐标落位。环形不随 RTL 镜像 */
315
+ [data-scope='progress'][data-part='scale'][data-variant='circle'],
316
+ [data-scope='progress'][data-part='scale'][data-variant='dashboard'] {
317
+ position: relative;
318
+ grid-area: 1 / 1;
319
+ inline-size: 100%;
320
+ block-size: 100%;
321
+ }
322
+
323
+ [data-scope='progress'][data-part='scale-label'][data-variant='circle'],
324
+ [data-scope='progress'][data-part='scale-label'][data-variant='dashboard'] {
325
+ position: absolute;
326
+ inset-block-start: var(--xh-_progress-y, 50%);
327
+ /* stylelint-disable-next-line property-disallowed-list -- 环形不随 RTL 镜像,刻度值横坐标由连接层按物理左缘写入 */
328
+ left: var(--xh-_progress-x, 50%);
329
+ white-space: nowrap;
330
+ translate: -50% -50%;
331
+ }
332
+
333
+ /* 仪表盘的指针:朝 3 点钟画好,按当前值的角度绕环心转过去。转动是数值变化,不是部件原地形变:
334
+ 与圆弧填充同一档时长与曲线,同一次值变化两者一起起步一起落定 */
335
+ [data-scope='progress'][data-part='needle'] {
336
+ fill: var(--xh-progress-needle-color, var(--xh-fg-default));
337
+ transform-box: view-box;
338
+ transform-origin: 50% 50%;
339
+ rotate: var(--xh-_progress-needle-angle, 0deg);
340
+ transition: rotate var(--xh-motion-duration-move) var(--xh-motion-ease-continuous);
341
+ }
342
+
343
+ /* 指针从环心伸出:环心内容让到缺口那一侧 */
344
+ [data-scope='progress'][data-part='label'][data-indicator='needle'] {
345
+ align-self: end;
346
+ }
347
+
348
+ /* 进度未知:线形条改为固定宽度的一小段来回扫,位移交给动画,按比例的平移与它的过渡让开 */
121
349
  [data-scope='progress'][data-part='track'][data-state='indeterminate'] {
122
350
  position: relative;
123
351
  overflow: hidden;
@@ -125,9 +353,10 @@
125
353
 
126
354
  [data-scope='progress'][data-part='range'][data-state='indeterminate'] {
127
355
  position: absolute;
356
+ inset-inline-start: 0;
128
357
  inline-size: 40%;
129
358
  transition: none;
130
- animation: xh-progress-indeterminate var(--xh-progress-indeterminate-duration, var(--xh-shimmer-duration)) var(--xh-motion-ease-loop) infinite;
359
+ animation: xh-progress-indeterminate var(--xh-progress-indeterminate-duration, var(--xh-motion-loop-shimmer)) var(--xh-motion-ease-loop) infinite;
131
360
  }
132
361
 
133
362
  /* 减弱动效档:不来回扫,改成一条铺满的静态条——进度仍然未知,只是不再动。
@@ -137,6 +366,12 @@
137
366
  position: static;
138
367
  inline-size: 100%;
139
368
  opacity: 0.5;
369
+ translate: none;
370
+ animation: none;
371
+ }
372
+
373
+ /* 条纹留着、不再流动:纹理本身是静态的非颜色通道 */
374
+ [data-scope='progress'][data-part='range'][data-striped][data-state='loading'] {
140
375
  animation: none;
141
376
  }
142
377
  }
@@ -145,10 +380,15 @@
145
380
  position: static;
146
381
  inline-size: 100%;
147
382
  opacity: 0.5;
383
+ translate: none;
148
384
  animation: none;
149
385
  }
150
386
 
151
- /* 零进度:圆角端点会在长度为 0 处画出一个圆点,收掉它 */
387
+ :where([data-motion='reduce']) [data-scope='progress'][data-part='range'][data-striped][data-state='loading'] {
388
+ animation: none;
389
+ }
390
+
391
+ /* 零进度:整段不画,从零起步的那一帧也不留一粒圆点 */
152
392
  [data-scope='progress'][data-part='range'][data-empty] {
153
393
  visibility: hidden;
154
394
  }
@@ -156,7 +396,20 @@
156
396
 
157
397
  /* 声明过任何 display 就盖掉了 UA 的 [hidden]{display:none},收起态得自己还回去。
158
398
  写在文件末尾:兜底必须排在该部件所有 display 声明之后 */
159
- [data-scope='progress'][data-part='root'][hidden] {
399
+ [data-scope='progress'][data-part='root'][hidden],
400
+ [data-scope='progress'][data-part='track'][hidden] {
401
+ display: none;
402
+ }
403
+
404
+ /* 指针档由指针指出当前值,弧上的填充收起、只留色带 */
405
+ [data-scope='progress'][data-part='range'][data-indicator='needle'] {
406
+ display: none;
407
+ }
408
+
409
+ /* SVG 里的部件不吃 UA 的 [hidden] 规则:作者自己写的目标刻度、指针在不适用时得自己收 */
410
+ [data-scope='progress'][data-part='target'][hidden],
411
+ [data-scope='progress'][data-part='scale'][hidden],
412
+ [data-scope='progress'][data-part='needle'][hidden] {
160
413
  display: none;
161
414
  }
162
415
 
@@ -186,6 +439,46 @@
186
439
  [data-scope='progress'][data-part='range'][data-variant='dashboard'] {
187
440
  stroke: Highlight;
188
441
  }
442
+
443
+ /* 色带在这一档里只剩分界:线形每段末端画一道灰线,环形的色带退掉,分段名仍由读屏文字给出 */
444
+ [data-scope='progress'][data-part='threshold'][data-variant='line'] {
445
+ forced-color-adjust: none;
446
+ border-inline-end: var(--xh-stroke-thin) solid GrayText;
447
+ background: Canvas;
448
+ }
449
+
450
+ [data-scope='progress'][data-part='threshold'][data-variant='circle'],
451
+ [data-scope='progress'][data-part='threshold'][data-variant='dashboard'] {
452
+ stroke: transparent;
453
+ }
454
+
455
+ [data-scope='progress'][data-part='target'][data-variant='line'],
456
+ [data-scope='progress'][data-part='scale-tick'][data-variant='line'] {
457
+ forced-color-adjust: none;
458
+ background: CanvasText;
459
+ }
460
+
461
+ [data-scope='progress'][data-part='target'][data-variant='circle'],
462
+ [data-scope='progress'][data-part='target'][data-variant='dashboard'],
463
+ [data-scope='progress'][data-part='scale-tick'][data-variant='circle'],
464
+ [data-scope='progress'][data-part='scale-tick'][data-variant='dashboard'] {
465
+ stroke: CanvasText;
466
+ }
467
+
468
+ [data-scope='progress'][data-part='needle'] {
469
+ fill: CanvasText;
470
+ }
471
+
472
+ /* 条纹在这一档里退掉:填充整段取高亮色才读得出走到哪儿,斜纹会把它切碎 */
473
+ [data-scope='progress'][data-part='range'][data-striped] {
474
+ background-image: none;
475
+ }
476
+
477
+ /* 缓冲段取灰:与底槽的 Canvas、填充的高亮色三者分得开 */
478
+ [data-scope='progress'][data-part='buffer'] {
479
+ forced-color-adjust: none;
480
+ background: GrayText;
481
+ }
189
482
  }
190
483
 
191
484
  @media print {
@@ -197,6 +490,11 @@
197
490
  position: static;
198
491
  inline-size: 100%;
199
492
  opacity: 0.5;
493
+ translate: none;
494
+ animation: none;
495
+ }
496
+
497
+ [data-scope='progress'][data-part='range'][data-striped][data-state='loading'] {
200
498
  animation: none;
201
499
  }
202
500
  }