@xihan-ui/styles 1.0.0 → 2.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 (171) hide show
  1. package/CHANGELOG.md +5146 -6
  2. package/README.md +22 -2
  3. package/component-tokens.d.ts +4212 -0
  4. package/component-tokens.js +2 -0
  5. package/components.tokens.json +80116 -0
  6. package/css/accordion.css +131 -75
  7. package/css/affix.css +16 -1
  8. package/css/alert.css +114 -67
  9. package/css/anchor.css +99 -27
  10. package/css/approval.css +461 -0
  11. package/css/avatar-group.css +14 -6
  12. package/css/avatar.css +33 -9
  13. package/css/back-top.css +103 -48
  14. package/css/badge.css +42 -14
  15. package/css/bar-code.css +62 -0
  16. package/css/breadcrumb.css +40 -21
  17. package/css/button-group.css +146 -24
  18. package/css/button.css +116 -99
  19. package/css/calendar-picker.css +448 -0
  20. package/css/calendar-range-picker.css +517 -0
  21. package/css/card.css +54 -77
  22. package/css/carousel.css +412 -68
  23. package/css/cascader.css +429 -246
  24. package/css/checkbox-group.css +225 -77
  25. package/css/checkbox.css +174 -37
  26. package/css/clipboard.css +266 -47
  27. package/css/code-view.css +279 -0
  28. package/css/collapsible.css +108 -63
  29. package/css/color-field.css +158 -0
  30. package/css/color-picker.css +172 -184
  31. package/css/color-slider.css +274 -0
  32. package/css/color-swatch-picker.css +243 -0
  33. package/css/color-swatch.css +35 -0
  34. package/css/combobox.css +267 -273
  35. package/css/command.css +399 -0
  36. package/css/context-menu.css +183 -108
  37. package/css/date-field.css +94 -121
  38. package/css/date-picker.css +486 -249
  39. package/css/date-range-picker.css +559 -0
  40. package/css/description.css +51 -0
  41. package/css/descriptions.css +133 -87
  42. package/css/dialog.css +247 -68
  43. package/css/diff-view.css +428 -0
  44. package/css/download-trigger.css +155 -46
  45. package/css/drawer.css +194 -111
  46. package/css/editable.css +172 -152
  47. package/css/empty-state.css +80 -11
  48. package/css/field-array.css +190 -0
  49. package/css/field.css +84 -31
  50. package/css/fieldset.css +51 -18
  51. package/css/file-upload.css +134 -36
  52. package/css/flex.css +20 -1
  53. package/css/float-button.css +238 -57
  54. package/css/floating-panel.css +173 -92
  55. package/css/focus.css +46 -0
  56. package/css/forced-colors.css +93 -0
  57. package/css/form.css +254 -77
  58. package/css/gradient-text.css +34 -10
  59. package/css/grid.css +516 -5
  60. package/css/heatmap.css +87 -29
  61. package/css/highlight.css +20 -5
  62. package/css/hover-card.css +97 -47
  63. package/css/icon-wrapper.css +15 -3
  64. package/css/icon.css +74 -8
  65. package/css/image-cropper.css +143 -29
  66. package/css/image-viewer.css +150 -89
  67. package/css/image.css +30 -1
  68. package/css/infinite-scroll.css +51 -1
  69. package/css/input-group.css +277 -0
  70. package/css/json-viewer.css +102 -45
  71. package/css/kbd.css +72 -0
  72. package/css/label.css +78 -0
  73. package/css/layers.css +5 -0
  74. package/css/layout.css +175 -31
  75. package/css/list.css +24 -3
  76. package/css/listbox.css +208 -67
  77. package/css/loading-bar.css +52 -3
  78. package/css/log.css +144 -9
  79. package/css/markdown-stream.css +149 -0
  80. package/css/marquee.css +27 -1
  81. package/css/masonry.css +7 -0
  82. package/css/matrix-code.css +67 -0
  83. package/css/mention.css +234 -152
  84. package/css/menu.css +180 -83
  85. package/css/menubar.css +204 -136
  86. package/css/message-feed.css +197 -0
  87. package/css/navigation-menu.css +144 -90
  88. package/css/notification.css +178 -100
  89. package/css/number-animation.css +7 -0
  90. package/css/number-field.css +120 -277
  91. package/css/overlay-arrow.css +12 -7
  92. package/css/page-header.css +99 -14
  93. package/css/pagination.css +175 -135
  94. package/css/password-input.css +222 -219
  95. package/css/pin-input.css +101 -67
  96. package/css/pointer.css +36 -0
  97. package/css/popconfirm.css +185 -94
  98. package/css/popover.css +121 -77
  99. package/css/progress.css +60 -12
  100. package/css/prompt-input.css +224 -0
  101. package/css/question-flow.css +513 -0
  102. package/css/radio-group.css +147 -30
  103. package/css/rating.css +193 -43
  104. package/css/reasoning.css +336 -0
  105. package/css/reset.css +84 -6
  106. package/css/resizable.css +237 -0
  107. package/css/scroll-area.css +68 -7
  108. package/css/scrollbar.css +47 -13
  109. package/css/segmented.css +94 -29
  110. package/css/select.css +288 -274
  111. package/css/separator.css +101 -1
  112. package/css/side-nav.css +133 -75
  113. package/css/signature-pad.css +74 -54
  114. package/css/skeleton.css +84 -11
  115. package/css/slider.css +132 -39
  116. package/css/sortable.css +198 -0
  117. package/css/spinner.css +126 -12
  118. package/css/splitter.css +29 -8
  119. package/css/statistic.css +55 -0
  120. package/css/steps.css +206 -72
  121. package/css/switch.css +268 -36
  122. package/css/table.css +695 -118
  123. package/css/tabs.css +604 -42
  124. package/css/tag-group.css +219 -0
  125. package/css/tag.css +118 -28
  126. package/css/tags-input.css +156 -274
  127. package/css/text-field.css +101 -249
  128. package/css/time-field.css +100 -128
  129. package/css/time-picker.css +323 -252
  130. package/css/time-range-picker.css +719 -0
  131. package/css/timeline.css +202 -68
  132. package/css/timer.css +56 -52
  133. package/css/timestamp.css +34 -0
  134. package/css/toast.css +373 -144
  135. package/css/toggle-group.css +309 -76
  136. package/css/toggle.css +116 -85
  137. package/css/tone.css +47 -0
  138. package/css/tool-call.css +421 -0
  139. package/css/toolbar.css +181 -33
  140. package/css/tooltip.css +107 -36
  141. package/css/tour.css +183 -152
  142. package/css/transfer.css +351 -138
  143. package/css/tree-select.css +323 -216
  144. package/css/tree.css +294 -62
  145. package/css/truncate.css +65 -0
  146. package/css/typography.css +228 -5
  147. package/css/undefined.css +10 -1
  148. package/css/virtualizer.css +9 -10
  149. package/css/visually-hidden.css +7 -0
  150. package/css/watermark.css +7 -0
  151. package/family/action-control.css +487 -0
  152. package/family/collection-item.css +721 -0
  153. package/family/field-chrome.css +298 -0
  154. package/family/motion.css +166 -0
  155. package/family/swatch.css +59 -0
  156. package/index.css +41705 -126
  157. package/index.unlayered.css +33692 -17339
  158. package/package.json +51 -17
  159. package/css/calendar.css +0 -367
  160. package/css/code-block.css +0 -91
  161. package/css/composer.css +0 -134
  162. package/css/countdown.css +0 -26
  163. package/css/dynamic-input.css +0 -220
  164. package/css/ellipsis.css +0 -52
  165. package/css/hotkeys.css +0 -90
  166. package/css/popselect.css +0 -391
  167. package/css/qr-code.css +0 -55
  168. package/css/result.css +0 -119
  169. package/css/space.css +0 -117
  170. package/css/thread.css +0 -120
  171. package/css/time.css +0 -25
package/css/spinner.css CHANGED
@@ -1,3 +1,10 @@
1
+ /*
2
+ * Copyright (c) 2021-Present XiHanFun and contributors.
3
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
4
+ */
5
+
6
+ /* 定义 spinner 样式。 */
7
+
1
8
  @layer xihan.motion {
2
9
  /* 转圈的关键帧定义在本文件里。皮肤可以按需单独引入,
3
10
  引用别处文件里的动画名时那份文件不一定在场,动画会静默地整个不跑 */
@@ -6,6 +13,13 @@
6
13
  transform: rotate(1turn);
7
14
  }
8
15
  }
16
+
17
+ /* 三点档的呼吸:一拍里淡到禁用档再回来 */
18
+ @keyframes xh-spinner-dots {
19
+ 50% {
20
+ opacity: var(--xh-state-disabled-opacity);
21
+ }
22
+ }
9
23
  }
10
24
 
11
25
  @layer xihan.components {
@@ -21,6 +35,7 @@
21
35
  align-items: center;
22
36
  gap: var(--xh-spinner-gap, var(--xh-control-gap-md));
23
37
  line-height: var(--xh-leading-none);
38
+ pointer-events: none;
24
39
  vertical-align: middle;
25
40
  }
26
41
 
@@ -30,14 +45,14 @@
30
45
  /* 边框算进直径,换厚度不会把圆环撑大 */
31
46
  box-sizing: border-box;
32
47
  flex: none;
33
- inline-size: var(--xh-spinner-size, var(--xh-glyph-size-md));
34
- block-size: var(--xh-spinner-size, var(--xh-glyph-size-md));
48
+ inline-size: var(--xh-spinner-size, var(--xh-glyph-size-lg));
49
+ block-size: var(--xh-spinner-size, var(--xh-glyph-size-lg));
35
50
  border: var(--xh-spinner-thickness, var(--xh-stroke-thick)) solid var(--xh-spinner-track, var(--xh-border-default));
36
51
 
37
52
  /* 一整圈是轨道色,只有起始边换成前景色,转起来才看得出方向 */
38
- border-block-start-color: var(--xh-spinner-fg, var(--xh-bg-brand));
39
- border-radius: var(--xh-spinner-radius, var(--xh-shape-pill));
40
- animation: xh-spinner-rotate var(--xh-spinner-duration, var(--xh-spin-duration)) linear infinite;
53
+ border-block-start-color: var(--xh-spinner-fg, currentColor);
54
+ border-radius: var(--xh-spinner-radius, var(--xh-shape-circle));
55
+ animation: xh-spinner-rotate var(--xh-spinner-duration, var(--xh-spin-duration)) var(--xh-motion-ease-loop) infinite;
41
56
  }
42
57
 
43
58
  /* 三档直径走字形尺寸那把尺:圆环与同一行文字里的图标同级,三档同族才不会在 compact 下错位。
@@ -48,8 +63,8 @@
48
63
  }
49
64
 
50
65
  [data-scope='spinner'][data-part='root'][data-size='lg']::before {
51
- inline-size: var(--xh-glyph-size-lg);
52
- block-size: var(--xh-glyph-size-lg);
66
+ inline-size: var(--xh-glyph-size-xl);
67
+ block-size: var(--xh-glyph-size-xl);
53
68
  }
54
69
 
55
70
  /* 语气只换起始边那一段,轨道留在中性描边上:轨道与起始边的反差就是转向的读法,
@@ -57,7 +72,70 @@
57
72
  选择器上带 [data-tone] 是必需的:语气槽会沿继承流下来,不限定在写了语气的
58
73
  那个节点上,放在语气容器里的转圈会跟着一起改色 */
59
74
  [data-scope='spinner'][data-part='root'][data-tone]::before {
60
- border-block-start-color: var(--xh-spinner-fg, var(--xh-_tone, var(--xh-bg-brand)));
75
+ border-block-start-color: var(--xh-spinner-fg, var(--xh-_tone));
76
+ }
77
+
78
+ /*
79
+ * 渐隐弧:一整圈的色相由角度扫过来,再用一圈环形遮罩把中心挖空,剩下的就是一段
80
+ * 由浓到无的弧。转的还是同一条 xh-spinner-rotate——这一档只换画法,不换节拍。
81
+ * 遮罩只吃 alpha,用 currentColor 当不透明的那一端即可。
82
+ */
83
+ [data-scope='spinner'][data-part='root'][data-variant='arc']::before {
84
+ border: 0;
85
+ background: conic-gradient(transparent, var(--xh-spinner-fg, currentColor));
86
+ -webkit-mask: radial-gradient(
87
+ farthest-side,
88
+ transparent calc(100% - var(--xh-spinner-thickness, var(--xh-stroke-thick))),
89
+ currentColor 0
90
+ );
91
+ mask: radial-gradient(
92
+ farthest-side,
93
+ transparent calc(100% - var(--xh-spinner-thickness, var(--xh-stroke-thick))),
94
+ currentColor 0
95
+ );
96
+ }
97
+
98
+ [data-scope='spinner'][data-part='root'][data-variant='arc'][data-tone]::before {
99
+ background: conic-gradient(transparent, var(--xh-spinner-fg, var(--xh-_tone)));
100
+ }
101
+
102
+ /*
103
+ * 三点:一行三枚圆点,整组一起呼吸。它不转,因此圆角与描边都撤掉,
104
+ * 图形改由三层径向渐变画;点的颜色走 currentColor,语气档只换这一处。
105
+ */
106
+ [data-scope='spinner'][data-part='root'][data-variant='dots']::before {
107
+ border: 0;
108
+ border-radius: 0;
109
+ background:
110
+ radial-gradient(circle closest-side, var(--xh-spinner-fg, currentColor) 100%, transparent) 0 50% / 25% 25% no-repeat,
111
+ radial-gradient(circle closest-side, var(--xh-spinner-fg, currentColor) 100%, transparent) 50% 50% / 25% 25% no-repeat,
112
+ radial-gradient(circle closest-side, var(--xh-spinner-fg, currentColor) 100%, transparent) 100% 50% / 25% 25% no-repeat;
113
+ animation: xh-spinner-dots var(--xh-spinner-duration, var(--xh-spin-duration)) var(--xh-motion-ease-sweep) infinite;
114
+ }
115
+
116
+ [data-scope='spinner'][data-part='root'][data-variant='dots'][data-tone]::before {
117
+ background:
118
+ radial-gradient(circle closest-side, var(--xh-spinner-fg, var(--xh-_tone)) 100%, transparent) 0 50% / 25% 25% no-repeat,
119
+ radial-gradient(circle closest-side, var(--xh-spinner-fg, var(--xh-_tone)) 100%, transparent) 50% 50% / 25% 25% no-repeat,
120
+ radial-gradient(circle closest-side, var(--xh-spinner-fg, var(--xh-_tone)) 100%, transparent) 100% 50% / 25% 25% no-repeat;
121
+ }
122
+
123
+ /* 三点档的呼吸自带一条动画,压不住上面那两块基础降级里的 animation: none,
124
+ 因此这一档各自再写一份。两组规则是同一件事的两个触发条件 */
125
+ @media (prefers-reduced-motion: reduce) {
126
+ [data-scope='spinner'][data-part='root'][data-variant='dots']::before {
127
+ animation: none;
128
+ }
129
+ }
130
+
131
+ :where([data-motion='reduce']) [data-scope='spinner'][data-part='root'][data-variant='dots']::before {
132
+ animation: none;
133
+ }
134
+
135
+ @media print {
136
+ [data-scope='spinner'][data-part='root'][data-variant='dots']::before {
137
+ animation: none;
138
+ }
61
139
  }
62
140
 
63
141
  [data-scope='spinner'][data-part='label'] {
@@ -72,23 +150,59 @@
72
150
  @media (prefers-reduced-motion: reduce) {
73
151
  [data-scope='spinner'][data-part='root']::before {
74
152
  border-style: dotted;
75
- border-color: var(--xh-spinner-fg, var(--xh-bg-brand));
153
+ border-color: var(--xh-spinner-fg, currentColor);
76
154
  animation: none;
77
155
  }
78
156
 
79
157
  /* 静止态整圈同色,语气跟着铺满整圈 */
80
158
  [data-scope='spinner'][data-part='root'][data-tone]::before {
81
- border-color: var(--xh-spinner-fg, var(--xh-_tone, var(--xh-bg-brand)));
159
+ border-color: var(--xh-spinner-fg, var(--xh-_tone));
82
160
  }
83
161
  }
84
162
 
85
163
  :where([data-motion='reduce']) [data-scope='spinner'][data-part='root']::before {
86
164
  border-style: dotted;
87
- border-color: var(--xh-spinner-fg, var(--xh-bg-brand));
165
+ border-color: var(--xh-spinner-fg, currentColor);
88
166
  animation: none;
89
167
  }
90
168
 
91
169
  :where([data-motion='reduce']) [data-scope='spinner'][data-part='root'][data-tone]::before {
92
- border-color: var(--xh-spinner-fg, var(--xh-_tone, var(--xh-bg-brand)));
170
+ border-color: var(--xh-spinner-fg, var(--xh-_tone));
171
+ }
172
+
173
+ /* 声明过任何 display 就盖掉了 UA 的 [hidden]{display:none},收起态得自己还回去。
174
+ 写在文件末尾:兜底必须排在该部件所有 display 声明之后 */
175
+ [data-scope='spinner'][data-part='root'][hidden] {
176
+ display: none;
177
+ }
178
+
179
+ @media print {
180
+ /* 打印:动画会被定格在任意一帧——转了一半的圈、扫到一半的微光。这里换成静止的表达,
181
+ 取值与减弱动效那一档相同。两档各写一块、不并进一条媒体查询列表:减弱动效另有一份
182
+ :where([data-motion='reduce']) 的钩子版本与它成对,打印没有对应的钩子可配,
183
+ 并成一条之后这层配对关系在文本上就断了。 */
184
+ [data-scope='spinner'][data-part='root']::before {
185
+ border-style: dotted;
186
+ border-color: var(--xh-spinner-fg, currentColor);
187
+ animation: none;
188
+ }
189
+
190
+ [data-scope='spinner'][data-part='root'][data-tone]::before {
191
+ border-color: var(--xh-spinner-fg, var(--xh-_tone));
192
+ }
193
+ }
194
+
195
+ /*
196
+ * 高对比档:这一档把整层背景图丢掉,而渐隐弧与三点都画在背景上——屏幕上会一片空白。
197
+ * 两档一并退回描边画法:一圈系统轨道色加一段高亮的起始边,转的还是同一条动画。
198
+ */
199
+ @media (forced-colors: active) {
200
+ [data-scope='spinner'][data-part='root'][data-variant='arc']::before,
201
+ [data-scope='spinner'][data-part='root'][data-variant='dots']::before {
202
+ border: var(--xh-spinner-thickness, var(--xh-stroke-thick)) solid CanvasText;
203
+ border-block-start-color: Highlight;
204
+ border-radius: var(--xh-spinner-radius, var(--xh-shape-circle));
205
+ background-image: none;
206
+ }
93
207
  }
94
208
  }
package/css/splitter.css CHANGED
@@ -1,3 +1,10 @@
1
+ /*
2
+ * Copyright (c) 2021-Present XiHanFun and contributors.
3
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
4
+ */
5
+
6
+ /* 定义 splitter 样式。 */
7
+
1
8
  @layer xihan.components {
2
9
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
10
  [data-scope='splitter'] {
@@ -33,9 +40,12 @@
33
40
  outline: none;
34
41
  }
35
42
 
36
- /* 禁用的整体压暗只做一次,落在最外层:面板与分隔条各自再叠一层 opacity 会越叠越黑 */
43
+ /* 禁用的整体压暗只做一次,落在最外层:面板与分隔条各自再叠一层 opacity 会越叠越黑。
44
+ 这一档压的是宿主塞进面板里的正文,不是控件自己的图形,所以地板比 --xh-state-disabled-opacity
45
+ 高:那一档是 0.5,黑字白底压完只有 3.94:1,读不出字。压暗仍要表达「这块动不了」,
46
+ 所以留一个槽让使用者按自己的配色调,而不是直接不压。 */
37
47
  [data-scope='splitter'][data-part='root'][data-disabled] {
38
- opacity: 0.6;
48
+ opacity: var(--xh-splitter-disabled-opacity, 0.6);
39
49
  }
40
50
 
41
51
  /* 拖动期间整块不许选中文字:手在分隔条上来回蹭时会顺带把两侧的正文刷成高亮 */
@@ -101,8 +111,10 @@
101
111
  cursor: row-resize;
102
112
  }
103
113
 
114
+ /* 分隔条是一根线不是一块面:悬停不走面阶梯(300 只留给 pressed,§7.2 第 3 条),
115
+ 按线取——与字段描边 rest → hover 同向加深一档 */
104
116
  [data-scope='splitter'][data-part='resize-trigger']:hover {
105
- background: var(--xh-splitter-trigger-bg-hover, var(--xh-bg-subtle-active));
117
+ background: var(--xh-splitter-trigger-bg-hover, var(--xh-border-control));
106
118
  }
107
119
 
108
120
  /* 只有正被拖的那条带 data-dragging:多条分隔条时全高亮就分不出手在哪一条上 */
@@ -110,14 +122,15 @@
110
122
  background: var(--xh-splitter-trigger-bg-dragging, var(--xh-bg-brand));
111
123
  }
112
124
 
113
- /* 焦点环往里画:分隔条只有几像素粗,往外画的那圈会被 root 的 overflow: hidden 切掉 */
114
- [data-scope='splitter'][data-part='resize-trigger']:focus-visible {
115
- outline: var(--xh-ring-width) solid var(--xh-ring-focus);
116
- outline-offset: calc(var(--xh-ring-offset) * -1);
125
+ /* 常态灰面与公共品牌环已经过线;拖动态才改用品牌面配对前景,避免同色环。 */
126
+ [data-scope='splitter'][data-part='resize-trigger'][data-dragging]:focus-visible {
127
+ --xh-_ring-color: var(--xh-fg-on-brand);
117
128
  }
118
129
 
119
130
  /* 分隔条是 div、role=separator,原生 disabled 在它身上不生效,
120
- 禁用只表达成 aria-disabled 与 data-disabled,写 :disabled 永远不命中 */
131
+ 禁用只表达成 aria-disabled 与 data-disabled,写 :disabled 永远不命中。
132
+ 光标不交给公共层:上面两条按走向给的 col-resize / row-resize 特异性相同又排在后面,
133
+ 公共层那条压不过它们 */
121
134
  [data-scope='splitter'][data-part='resize-trigger'][data-disabled] {
122
135
  background: var(--xh-splitter-trigger-bg-disabled, var(--xh-border-subtle));
123
136
  cursor: not-allowed;
@@ -129,4 +142,12 @@
129
142
  [data-scope='splitter'][data-part='resize-trigger'][hidden] {
130
143
  display: none;
131
144
  }
145
+
146
+ @media (forced-colors: active) {
147
+ /* 分隔条画的是一块纯底色,这一档里底色被统一换成 Canvas,那条线整根消失,
148
+ 两侧面板就糊成一块。换成前景色画回来 */
149
+ [data-scope='splitter'][data-part='resize-trigger'] {
150
+ background: CanvasText;
151
+ }
152
+ }
132
153
  }
package/css/statistic.css CHANGED
@@ -1,3 +1,10 @@
1
+ /*
2
+ * Copyright (c) 2021-Present XiHanFun and contributors.
3
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
4
+ */
5
+
6
+ /* 定义 statistic 样式。 */
7
+
1
8
  @layer xihan.components {
2
9
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
10
  [data-scope='statistic'] {
@@ -10,6 +17,9 @@
10
17
  --xh-_statistic-value-size: var(--xh-text-heading-2-size);
11
18
  --xh-_statistic-affix-size: var(--xh-control-font-md);
12
19
  --xh-_statistic-row-gap: var(--xh-space-1);
20
+ /* 涨跌箭头与作者塞进来的图标同一把尺:跟着前后缀那一档字(14px)走,取 sm 字形 16px,大档抬到 md */
21
+ --xh-_statistic-icon-size: var(--xh-glyph-size-sm);
22
+ --xh-icon-size: var(--xh-statistic-icon-size, var(--xh-_statistic-icon-size));
13
23
 
14
24
  /* 折行排布:标签独占一行,前缀、数值、后缀在下一行按基线对齐排在一起 */
15
25
  display: flex;
@@ -39,6 +49,7 @@
39
49
  --xh-_statistic-value-size: var(--xh-text-heading-1-size);
40
50
  --xh-_statistic-affix-size: var(--xh-control-font-lg);
41
51
  --xh-_statistic-row-gap: var(--xh-space-1_5);
52
+ --xh-_statistic-icon-size: var(--xh-glyph-size-md);
42
53
  }
43
54
 
44
55
  /* —— 各段 —— */
@@ -69,4 +80,48 @@
69
80
  font-weight: var(--xh-font-weight-regular);
70
81
  line-height: var(--xh-leading-tight);
71
82
  }
83
+
84
+ /* —— 涨跌 —— */
85
+ /* 与数值同一行,中间空开一档;比数(同比、环比)由作者写进来 */
86
+ [data-scope='statistic'][data-part='trend'] {
87
+ display: inline-flex;
88
+ align-items: center;
89
+ gap: var(--xh-statistic-trend-gap, var(--xh-space-0_5));
90
+ margin-inline-start: var(--xh-statistic-trend-offset, var(--xh-space-2));
91
+ color: var(--xh-statistic-trend-fg, var(--xh-_tone-fg, var(--xh-fg-muted)));
92
+ font-size: var(--xh-statistic-trend-font-size, var(--xh-_statistic-affix-size));
93
+ font-variant-numeric: tabular-nums;
94
+ font-weight: var(--xh-font-weight-regular);
95
+ line-height: var(--xh-leading-tight);
96
+ }
97
+
98
+ /* 方向逐档换字形,箭头由皮肤画,示例里不必手打 */
99
+ [data-scope='statistic'][data-part='trend'][data-direction='up'] {
100
+ --xh-_statistic-trend-mark: var(--xh-glyph-mark-arrow-up);
101
+ }
102
+
103
+ [data-scope='statistic'][data-part='trend'][data-direction='down'] {
104
+ --xh-_statistic-trend-mark: var(--xh-glyph-mark-arrow-down);
105
+ }
106
+
107
+ [data-scope='statistic'][data-part='trend'][data-direction='flat'] {
108
+ --xh-_statistic-trend-mark: var(--xh-glyph-mark-minus);
109
+ }
110
+
111
+ [data-scope='statistic'][data-part='trend'][data-direction]::before {
112
+ content: '';
113
+ display: inline-block;
114
+ flex: none;
115
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-sm));
116
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-sm));
117
+ background-color: currentColor;
118
+ -webkit-mask: var(--xh-_statistic-trend-mark) center / contain no-repeat;
119
+ mask: var(--xh-_statistic-trend-mark) center / contain no-repeat;
120
+ }
121
+
122
+ /* 声明过任何 display 就盖掉了 UA 的 [hidden]{display:none},收起态得自己还回去。
123
+ 写在文件末尾:兜底必须排在该部件所有 display 声明之后 */
124
+ [data-scope='statistic'][data-part='trend'][hidden] {
125
+ display: none;
126
+ }
72
127
  }