@xihan-ui/styles 2.1.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 (152) hide show
  1. package/CHANGELOG.md +1227 -0
  2. package/README.md +6 -1
  3. package/component-tokens.d.ts +505 -96
  4. package/components.tokens.json +34636 -25846
  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 +55 -58
  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 +23 -0
  37. package/css/date-picker.css +57 -22
  38. package/css/date-range-picker.css +301 -12
  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 +16 -18
  49. package/css/file-upload.css +125 -135
  50. package/css/flex.css +324 -8
  51. package/css/float-button.css +165 -38
  52. package/css/floating-panel.css +53 -19
  53. package/css/form.css +23 -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 +429 -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 +158 -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 +44 -15
  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 +201 -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 +22997 -13678
  147. package/index.unlayered.css +12928 -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
package/css/accordion.css CHANGED
@@ -184,8 +184,12 @@
184
184
  flex: none;
185
185
  color: var(--xh-accordion-indicator-fg, var(--xh-fg-muted));
186
186
  line-height: var(--xh-leading-none);
187
- /* 指示器与正文同档转向:正文 enter 档展开,箭头也用同一段时长翻过去 */
188
- transition: rotate var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong);
187
+ }
188
+
189
+ /* 指示器与正文同档转向:收起时取 collapse 档,展开时(下面的打开态)取 expand 档。
190
+ 首帧就在的朝向直接呈现(data-instant),第一次开合起才转 */
191
+ [data-scope='accordion'][data-part='indicator']:not([data-instant]) {
192
+ transition: rotate var(--xh-motion-duration-collapse) var(--xh-motion-ease-enter-strong);
189
193
  }
190
194
 
191
195
  /* 没写内容就由皮肤画;作者往部件里塞了自己的图形,这条规则即不命中 */
@@ -204,6 +208,10 @@
204
208
  rotate: 180deg;
205
209
  }
206
210
 
211
+ [data-scope='accordion'][data-part='indicator'][data-state='open']:not([data-instant]) {
212
+ transition: rotate var(--xh-motion-duration-expand) var(--xh-motion-ease-enter-strong);
213
+ }
214
+
207
215
  /* 这里刻意没有 [data-part='content'][hidden] { display: none }:补了动画一帧都播不出来。
208
216
  收起由适配器在退场结束后写内联 display 落成,退场窗口内靠 connect 发的 inert 挡住焦点 */
209
217
  [data-scope='accordion'][data-part='content'] {
@@ -221,14 +229,32 @@
221
229
  line-height: var(--xh-leading-normal);
222
230
  }
223
231
 
224
- /* 展开那一档才提前备好行高这条通道:属性随 data-state 一起消失,收起后不长期占着 */
225
- [data-scope='accordion'][data-part='content'][data-state='open'] {
226
- will-change: grid-template-rows;
227
- animation: xh-disclosure-expand var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong);
232
+ /* 展开与收起只在开合时播:首帧就在的展开 / 收起直接呈现(data-instant) */
233
+ [data-scope='accordion'][data-part='content'][data-state='open']:not([data-instant]) {
234
+ animation: xh-disclosure-expand var(--xh-motion-duration-expand) var(--xh-motion-ease-enter-strong);
235
+ }
236
+
237
+ [data-scope='accordion'][data-part='content'][data-state='closed']:not([data-instant]) {
238
+ animation: xh-disclosure-collapse var(--xh-motion-duration-collapse) var(--xh-motion-ease-exit) forwards;
228
239
  }
229
240
 
230
- [data-scope='accordion'][data-part='content'][data-state='closed'] {
231
- animation: xh-disclosure-collapse var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
241
+ @media (forced-colors: active) {
242
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
243
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
244
+ 按钮面上取 ButtonText,悬停与按下时家族把按钮涂成 Highlight、字形换 HighlightText;
245
+ 禁用取 GrayText */
246
+ [data-scope='accordion'][data-part='indicator']:empty::before {
247
+ forced-color-adjust: none;
248
+ background-color: ButtonText;
249
+ }
250
+
251
+ [data-scope='accordion'][data-part='trigger']:not([data-disabled]):not([data-loading]):is(:hover, :active, [data-pressed]) [data-scope='accordion'][data-part='indicator']:empty::before {
252
+ background-color: HighlightText;
253
+ }
254
+
255
+ [data-scope='accordion'][data-part='trigger'][data-disabled] [data-scope='accordion'][data-part='indicator']:empty::before {
256
+ background-color: GrayText;
257
+ }
232
258
  }
233
259
  }
234
260
 
package/css/alert.css CHANGED
@@ -7,6 +7,23 @@
7
7
 
8
8
  /* Alert 独立皮肤同时传递引入关闭钮的动作控件配方;full bundle 递归去重。 */
9
9
  @import '../family/action-control.css';
10
+ /* 淡出的关键帧由 family/motion.css 提供,本皮肤 @import 它,单独引入仍成立。 */
11
+ @import '../family/motion.css';
12
+
13
+ @layer xihan.motion {
14
+ /* 关闭时收起占位:从连接层量下的整块高度收到 0,内缩与描边一起收掉 */
15
+ @keyframes xh-alert-collapse {
16
+ from {
17
+ block-size: var(--xh-_alert-exit-block-size);
18
+ }
19
+
20
+ to {
21
+ block-size: 0;
22
+ padding-block: 0;
23
+ border-block-width: 0;
24
+ }
25
+ }
26
+ }
10
27
 
11
28
  @layer xihan.components {
12
29
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
@@ -22,7 +39,8 @@
22
39
 
23
40
  display: flex;
24
41
  align-items: flex-start;
25
- gap: var(--xh-alert-gap, var(--xh-space-4));
42
+ /* 指示符盒自带 --xh-space-1 内衬:与间距合起来,字形到正文 12px,与 Notification 同一个距离 */
43
+ gap: var(--xh-alert-gap, var(--xh-space-2));
26
44
  padding-block: var(--xh-alert-py, var(--xh-surface-py-sm));
27
45
  padding-inline: var(--xh-alert-px, var(--xh-surface-px-sm));
28
46
  border: var(--xh-stroke-thin) solid var(--xh-alert-border, var(--xh-border-default));
@@ -34,6 +52,27 @@
34
52
  line-height: var(--xh-alert-leading, var(--xh-leading-normal));
35
53
  }
36
54
 
55
+ /* 横幅:页面顶部的通栏,贴着页面或容器的边铺满整行。贴边的三条边是页面自己的边,画出来只会压在边上;
56
+ 边界只留朝向页面内容的块尾一道,与页面内容同一层、同一种描边。贴边铺满的栏不取圆角:
57
+ 圆了会在两个角露出底下的页面。面与语气规则与页内提示相同,横幅不换底色 */
58
+ [data-scope='alert'][data-part='root'][data-banner] {
59
+ inline-size: 100%;
60
+ border-block-start-width: 0;
61
+ border-inline-width: 0;
62
+ border-radius: 0;
63
+ }
64
+
65
+ /* 关闭的退场:先淡出,再把占位收起——下面的内容跟着一路平移上来,而不是整块跳上来。
66
+ 收占位接在淡出之后(延迟一个 exit),播完连接层才写 hidden。收起态常挂 hidden,这条只在退场途中命中;
67
+ 减弱动效下收占位瞬时完成,淡出照常 */
68
+ [data-scope='alert'][data-part='root'][data-state='closed'] {
69
+ box-sizing: border-box;
70
+ overflow: hidden;
71
+ animation:
72
+ xh-fade-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards,
73
+ xh-alert-collapse var(--xh-motion-duration-collapse) var(--xh-motion-ease-exit) var(--xh-motion-duration-exit) forwards;
74
+ }
75
+
37
76
  /* 收起态连接层给的是 hidden 属性,而上面声明了 display——
38
77
  漏掉这条,关掉的提示还会一直摊在页面上 */
39
78
  [data-scope='alert'][data-part='root'][hidden] {
@@ -186,4 +225,23 @@
186
225
  [data-scope='alert'][data-part='action'][hidden] {
187
226
  display: none;
188
227
  }
228
+
229
+ @media (forced-colors: active) {
230
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
231
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
232
+ 按钮面上取 ButtonText,悬停与按下时家族把按钮涂成 Highlight、字形换 HighlightText;
233
+ 禁用取 GrayText */
234
+ [data-scope='alert'][data-part='close-trigger']:empty::before {
235
+ forced-color-adjust: none;
236
+ background-color: ButtonText;
237
+ }
238
+
239
+ [data-scope='alert'][data-part='close-trigger']:not([data-disabled]):not([data-loading]):is(:hover, :active, [data-pressed]):empty::before {
240
+ background-color: HighlightText;
241
+ }
242
+
243
+ [data-scope='alert'][data-part='close-trigger'][data-disabled]:empty::before {
244
+ background-color: GrayText;
245
+ }
246
+ }
189
247
  }
package/css/anchor.css CHANGED
@@ -127,21 +127,32 @@
127
127
  overflow: hidden;
128
128
  }
129
129
 
130
- /* 主轴上的位置与长度由连接层写成内联样式(它量得到,样式表量不到);
131
- 这里只管交叉轴的贴边与粗细,以及长什么样。它是可选部件——不渲染它时,
130
+ /* 当前链接的四个几何量由机器量好、连接层写成私有槽 --xh-_anchor-indicator-x / -y / -w / -h
131
+ (它量得到,样式表量不到);这里按排布取主轴那两支,落点交给 translate(只走合成),
132
+ 另管交叉轴的贴边与粗细,以及长什么样。它是可选部件——不渲染它时,
132
133
  当前那一节的链接自画一条同规格的静态线(见下) */
133
134
  [data-scope='anchor'][data-part='indicator'] {
135
+ /* 还没量到时的落点,量到即被机器写的私有槽盖过 */
136
+ --xh-_anchor-indicator-x: 0;
137
+ --xh-_anchor-indicator-y: 0;
138
+ --xh-_anchor-indicator-w: 0;
139
+ --xh-_anchor-indicator-h: 0;
140
+ --xh-_anchor-indicator-stretch: 0;
141
+
134
142
  position: absolute;
135
143
  display: block;
136
144
  /* 指示条也是列表项,宿主给 li 的外边距会把绝对定位的它推离条目 */
137
145
  margin: 0;
146
+ inset-block-start: 0;
138
147
  inset-inline-start: calc(var(--xh-anchor-indicator-thickness, var(--xh-stroke-thick)) * -1);
139
148
  inline-size: var(--xh-anchor-indicator-thickness, var(--xh-stroke-thick));
149
+ block-size: var(--xh-_anchor-indicator-h);
140
150
  border-radius: var(--xh-anchor-indicator-radius, var(--xh-shape-pill));
141
151
  background: var(--xh-anchor-indicator-color, var(--xh-_anchor-accent));
152
+ translate: 0 var(--xh-_anchor-indicator-y);
142
153
  transition:
143
- inset-block-start var(--xh-motion-duration-enter) var(--xh-motion-ease-continuous),
144
- block-size var(--xh-motion-duration-enter) var(--xh-motion-ease-continuous);
154
+ translate var(--xh-motion-duration-move) var(--xh-motion-ease-continuous),
155
+ block-size var(--xh-motion-duration-move) var(--xh-motion-ease-continuous);
145
156
  }
146
157
 
147
158
  /* display 会盖掉 hidden 属性的 UA 规则,无激活项时显式让位 */
@@ -151,13 +162,40 @@
151
162
 
152
163
  /* 横排:指示条改躺在底边的轨道上,交叉轴换成块轴 */
153
164
  [data-scope='anchor'][data-part='indicator'][data-orientation='horizontal'] {
154
- inset-inline-start: auto;
165
+ inset-block-start: auto;
166
+ inset-inline-start: 0;
155
167
  inset-block-end: calc(var(--xh-anchor-indicator-thickness, var(--xh-stroke-thick)) * -1);
156
- inline-size: auto;
168
+ inline-size: var(--xh-_anchor-indicator-w);
157
169
  block-size: var(--xh-anchor-indicator-thickness, var(--xh-stroke-thick));
170
+ translate: var(--xh-_anchor-indicator-x) 0;
158
171
  transition:
159
- inset-inline-start var(--xh-motion-duration-enter) var(--xh-motion-ease-continuous),
160
- inline-size var(--xh-motion-duration-enter) var(--xh-motion-ease-continuous);
172
+ translate var(--xh-motion-duration-move) var(--xh-motion-ease-continuous),
173
+ inline-size var(--xh-motion-duration-move) var(--xh-motion-ease-continuous);
174
+ }
175
+
176
+ /* translate 只认物理方向:RTL 下行首在右边,起始缘量出的距离往左推 */
177
+ [data-scope='anchor'][data-part='indicator'][data-orientation='horizontal']:dir(rtl) {
178
+ translate: calc(-1 * var(--xh-_anchor-indicator-x)) 0;
179
+ }
180
+
181
+ /* 首次落位与同一项的重量(窗口缩放、换上正式字体)直接到位,只有换项才滑:连接层在这一落点投影
182
+ data-instant。写在排布那几条之后,同一特异性按先后压过它们单写的过渡 */
183
+ [data-scope='anchor'][data-part='indicator'][data-instant] {
184
+ transition: none;
185
+ }
186
+
187
+ /* 液态档:两沿由弹簧逐帧推着走(连接层每一帧写好盒子),几何量不再补过渡;
188
+ 被拉长时在另一个方向上压扁(竖排缺省压行向,横排压块向),表达体积守恒,拉长到目标的两倍时压到 --xh-motion-scale-squash */
189
+ :where([data-material='liquid']) [data-scope='anchor'][data-part='indicator'] {
190
+ --xh-_anchor-indicator-squash: max(var(--xh-motion-scale-squash), calc(1 - var(--xh-_anchor-indicator-stretch) * (1 - var(--xh-motion-scale-squash))));
191
+
192
+ scale: var(--xh-_anchor-indicator-squash) 1;
193
+ transition: none;
194
+ }
195
+
196
+ :where([data-material='liquid']) [data-scope='anchor'][data-part='indicator'][data-orientation='horizontal'] {
197
+ scale: 1 var(--xh-_anchor-indicator-squash);
198
+ transition: none;
161
199
  }
162
200
 
163
201
  /* 目录里没放 indicator 部件时,当前那一节的链接自画一条静态指示线(导航当前页):
package/css/approval.css CHANGED
@@ -5,44 +5,11 @@
5
5
 
6
6
  /* 定义 approval 样式。 */
7
7
 
8
+ /* 共享关键帧由 family/motion.css 提供,本皮肤 @import 它,单独引入仍成立。 */
9
+ @import '../family/motion.css';
8
10
  /* 独立皮肤同时传递引入授权行与两颗钮的动作控件配方,full bundle 递归去重。 */
9
11
  @import '../family/action-control.css';
10
-
11
- @layer xihan.motion {
12
- /* 动画名的查找只认「文档里有没有这个名字」:引用别处文件里的名字,那份文件不一定在场,
13
- 动画会静默地整个不跑。每份皮肤都能单独引入,所以它用到的关键帧都写在自己身上。 */
14
-
15
- /* 幅度走令牌,减弱动效时由令牌层归零;位移用独立属性而非 transform 简写 */
16
- @keyframes xh-approval-in {
17
- from {
18
- opacity: 0;
19
- translate: 0 var(--xh-motion-distance-md);
20
- }
21
-
22
- to {
23
- opacity: 1;
24
- translate: 0 0;
25
- }
26
- }
27
-
28
- @keyframes xh-approval-result-in {
29
- from {
30
- opacity: 0;
31
- scale: var(--xh-motion-scale-enter);
32
- }
33
-
34
- to {
35
- opacity: 1;
36
- scale: 1;
37
- }
38
- }
39
-
40
- @keyframes xh-approval-rotate {
41
- to {
42
- rotate: 360deg;
43
- }
44
- }
45
- }
12
+ @import '../family/loading-ring.css';
46
13
 
47
14
  @layer xihan.components {
48
15
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
@@ -66,6 +33,8 @@
66
33
  --xh-_approval-item-font-size: var(--xh-control-font-md);
67
34
  --xh-_approval-note-font-size: var(--xh-control-font-md);
68
35
 
36
+ /* 定位基准:待决的呼吸点钉在右上角 */
37
+ position: relative;
69
38
  display: flex;
70
39
  flex-direction: column;
71
40
  gap: var(--xh-approval-gap, var(--xh-_approval-gap));
@@ -74,8 +43,13 @@
74
43
  border-radius: var(--xh-approval-radius, var(--xh-shape-surface));
75
44
  background: var(--xh-approval-bg, var(--xh-bg-surface));
76
45
  box-shadow: var(--xh-approval-shadow, none);
77
- transition: border-color var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong);
78
- animation: xh-approval-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong);
46
+ /* 判定落定时描边随语气换色:属状态换色,与别处悬停、选中、校验的换色同一档 */
47
+ transition: border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
48
+ }
49
+
50
+ /* 卡片出现时滑入;落在首帧就在的列表条目里(历史消息)时直接呈现,不跟着整屏一起入场 */
51
+ [data-scope='approval'][data-part='root']:not([data-scope][data-part='item'][data-instant] *) {
52
+ animation: xh-item-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong);
79
53
  }
80
54
 
81
55
  /* 上面声明的 display 会盖掉 UA 的 [hidden]{display:none},这里补回 */
@@ -83,6 +57,48 @@
83
57
  display: none;
84
58
  }
85
59
 
60
+ /* 待决的呼吸点:钉在右上角(RTL 下左上),不占作者排的版面;颜色随语气,没写语气时取警示色。
61
+ 呼吸只表达「正在进行、需要被察觉」:明暗与缩放的起伏持续到判定落定,外扩光环同周期播 3 轮后停下;
62
+ 判过了连接层就收起它,由结果那一格接着表达 */
63
+ [data-scope='approval'][data-part='pending-indicator'] {
64
+ position: absolute;
65
+ inset-block-start: var(--xh-approval-p, var(--xh-_approval-p));
66
+ inset-inline-end: var(--xh-approval-p, var(--xh-_approval-p));
67
+ inline-size: var(--xh-approval-pending-indicator-size, var(--xh-space-2));
68
+ block-size: var(--xh-approval-pending-indicator-size, var(--xh-space-2));
69
+ border-radius: var(--xh-approval-pending-indicator-radius, var(--xh-shape-circle));
70
+ background: var(--xh-approval-pending-indicator-color, var(--xh-_tone, var(--xh-fg-warning)));
71
+ animation: xh-breathe var(--xh-motion-loop-breathe) var(--xh-motion-ease-breathe) infinite;
72
+ }
73
+
74
+ [data-scope='approval'][data-part='pending-indicator']::after {
75
+ content: '';
76
+ position: absolute;
77
+ inset: 0;
78
+ border-radius: inherit;
79
+ background: inherit;
80
+ opacity: 0;
81
+ pointer-events: none;
82
+ animation: xh-breathe-halo var(--xh-motion-loop-breathe) var(--xh-motion-ease-breathe) 3;
83
+ }
84
+
85
+ /* 减弱动效:两者都停,圆点停在满不透明度,光环不出现 */
86
+ @media (prefers-reduced-motion: reduce) {
87
+ [data-scope='approval'][data-part='pending-indicator'],
88
+ [data-scope='approval'][data-part='pending-indicator']::after {
89
+ animation: none;
90
+ }
91
+ }
92
+
93
+ :where([data-motion='reduce']) [data-scope='approval'][data-part='pending-indicator'],
94
+ :where([data-motion='reduce']) [data-scope='approval'][data-part='pending-indicator']::after {
95
+ animation: none;
96
+ }
97
+
98
+ [data-scope='approval'][data-part='pending-indicator'][hidden] {
99
+ display: none;
100
+ }
101
+
86
102
  /* 语气只染描边:作者定的强调,待决时把这块面从正文里提出来 */
87
103
  [data-scope='approval'][data-part='root'][data-tone] {
88
104
  border-color: var(--xh-approval-border, var(--xh-_tone, var(--xh-border-default)));
@@ -198,7 +214,7 @@
198
214
  inline-size: var(--xh-approval-indicator-size, var(--xh-control-indicator-size));
199
215
  block-size: var(--xh-approval-indicator-size, var(--xh-control-indicator-size));
200
216
  transition:
201
- background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
217
+ background-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
202
218
  border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
203
219
  color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
204
220
  }
@@ -272,6 +288,13 @@
272
288
  color: var(--xh-fg-disabled);
273
289
  }
274
290
 
291
+ /* 要求写理由而空着就按了拒绝:描边换成无效色;焦点也已带到这里,焦点环压在描边外面,一并换色 */
292
+ [data-scope='approval'][data-part='note'][data-invalid] {
293
+ --xh-_ring-color: var(--xh-approval-note-ring-invalid, var(--xh-ring-invalid));
294
+
295
+ border-color: var(--xh-approval-note-border-invalid, var(--xh-border-invalid));
296
+ }
297
+
275
298
  /* 剩余时间:等宽数字,逐秒跳字时不左右晃 */
276
299
  [data-scope='approval'][data-part='timer'] {
277
300
  display: block;
@@ -289,25 +312,31 @@
289
312
  display: inline-flex;
290
313
  align-self: flex-start;
291
314
  align-items: center;
292
- gap: var(--xh-approval-result-gap, var(--xh-space-1_5));
293
- padding-inline: var(--xh-approval-result-px, var(--xh-space-2));
294
- padding-block: var(--xh-approval-result-py, var(--xh-space-1));
315
+ gap: var(--xh-approval-result-gap, var(--xh-space-1));
316
+ padding-inline: var(--xh-approval-result-px, var(--xh-space-1_5));
317
+ padding-block: var(--xh-approval-result-py, var(--xh-space-0_5));
318
+ border: var(--xh-stroke-thin) solid transparent;
295
319
  border-radius: var(--xh-approval-result-radius, var(--xh-shape-pill));
296
- background: var(--xh-approval-result-bg, color-mix(in oklab, var(--xh-fg-success) 12%, var(--xh-bg-surface)));
297
- color: var(--xh-approval-result-fg, var(--xh-fg-success));
320
+ background: var(--xh-approval-result-bg, var(--xh-_tone-subtle));
321
+ color: var(--xh-approval-result-fg, var(--xh-_tone-fg));
298
322
  font-size: var(--xh-approval-result-font-size, var(--xh-text-caption-size));
299
- font-weight: var(--xh-approval-result-font-weight, var(--xh-text-label-weight));
300
- animation: xh-approval-result-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong);
323
+ font-weight: var(--xh-approval-result-font-weight, var(--xh-font-weight-medium));
324
+ line-height: var(--xh-control-indicator-md);
325
+ }
326
+
327
+ /* 落定结果弹出;落在首帧就在的列表条目里(历史消息)时直接呈现,不跟着整屏一起入场 */
328
+ [data-scope='approval'][data-part='result']:not([data-scope][data-part='item'][data-instant] *) {
329
+ animation: xh-pop-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong);
301
330
  }
302
331
 
303
332
  [data-scope='approval'][data-part='result'][hidden] {
304
333
  display: none;
305
334
  }
306
335
 
307
- /* 超时按拒绝收口,两者同档:视觉上不该看着像第三种结果 */
336
+ /* 超时按拒绝收口,两者同档(连接层同投 danger 语气):视觉上不该看着像第三种结果 */
308
337
  [data-scope='approval'][data-part='result']:is([data-state='denied'], [data-state='expired']) {
309
- background: var(--xh-approval-result-bg-denied, color-mix(in oklab, var(--xh-fg-danger) 12%, var(--xh-bg-surface)));
310
- color: var(--xh-approval-result-fg-denied, var(--xh-fg-danger));
338
+ background: var(--xh-approval-result-bg-denied, var(--xh-_tone-subtle));
339
+ color: var(--xh-approval-result-fg-denied, var(--xh-_tone-fg));
311
340
  }
312
341
 
313
342
  /* 动作行:两颗按钮靠右排,间距归库管。
@@ -397,35 +426,17 @@
397
426
  圆环挂在这一行上而不是某一颗钮上:连接层只拿到「有一条判定在途」,
398
427
  拿不到是哪一颗按下的,画在两颗钮上会有一颗在说假话 */
399
428
 
400
- [data-scope='approval'][data-part='root'][data-loading] [data-scope='approval'][data-part='footer']::before {
401
- content: '';
402
- flex: none;
403
-
404
- /* 圆环那一枚跟着两颗钮的字号走,不跟页面的:download-trigger 与 clipboard 的圆环
405
- 1em 量的都是它所在那颗钮,这里的宿主是整行,不锁字号就会比按钮上的大一圈 */
406
- font-size: var(--xh-approval-action-font-size, var(--xh-text-label-size));
407
- inline-size: 1em;
408
- block-size: 1em;
409
- border: var(--xh-stroke-thin) solid transparent;
410
- border-block-start-color: currentColor;
411
- border-radius: var(--xh-shape-circle);
412
- animation: xh-approval-rotate var(--xh-approval-loading-duration, var(--xh-spin-duration)) var(--xh-motion-ease-loop) infinite;
413
- }
414
-
415
- /* 减少动效:圆环停下并整圈换成虚线,静止的形状仍然读得出"还没好"。
416
- 两条规则是同一件事的两个触发条件:系统偏好与作者打的 data-motion */
417
- @media (prefers-reduced-motion: reduce) {
418
- [data-scope='approval'][data-part='root'][data-loading] [data-scope='approval'][data-part='footer']::before {
419
- border-style: dotted;
420
- border-color: currentColor;
421
- animation: none;
422
- }
429
+ /* 环由加载环配方画在这一行的 ::before 上(连接层投影 data-xh-loading-ring 与 data-loading),
430
+ 随在途淡入淡出、转与停;直径取所在尺寸档的图标档,转速认使用者槽 */
431
+ [data-scope='approval'][data-part='footer'] {
432
+ --xh-_loading-ring-duration: var(--xh-approval-loading-duration, var(--xh-motion-loop-spin));
423
433
  }
424
434
 
425
- :where([data-motion='reduce']) [data-scope='approval'][data-part='root'][data-loading] [data-scope='approval'][data-part='footer']::before {
426
- border-style: dotted;
427
- border-color: currentColor;
428
- animation: none;
435
+ [data-scope='approval'][data-part='footer']::before {
436
+ content: '';
437
+ flex: none;
438
+ inline-size: var(--xh-_approval-icon-size);
439
+ block-size: var(--xh-_approval-icon-size);
429
440
  }
430
441
 
431
442
  /* 尺寸档改内衬、间距、按钮高度、行内字号与图标档 */
@@ -458,4 +469,33 @@
458
469
  --xh-_approval-item-font-size: var(--xh-control-font-lg);
459
470
  --xh-_approval-note-font-size: var(--xh-control-font-lg);
460
471
  }
472
+
473
+ /* 强制色:底色会被系统背景吃掉,呼吸点改用系统前景色 */
474
+ @media (forced-colors: active) {
475
+ [data-scope='approval'][data-part='pending-indicator'] {
476
+ background: CanvasText;
477
+ }
478
+ }
479
+
480
+ @media print {
481
+ /* 打印:待决的呼吸点是一瞬间的状态,纸上没有「等着人来判」 */
482
+ [data-scope='approval'][data-part='pending-indicator'] {
483
+ display: none;
484
+ }
485
+ }
486
+
487
+ @media (forced-colors: active) {
488
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
489
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
490
+ 落在勾选盒自己的实心面上(这一档里是 Canvas),整行悬停不换面,取 CanvasText;
491
+ 禁用取 GrayText */
492
+ [data-scope='approval'][data-part='item-indicator']:not([data-state='unchecked']):empty::before {
493
+ forced-color-adjust: none;
494
+ background-color: CanvasText;
495
+ }
496
+
497
+ [data-scope='approval'][data-part='item'][data-disabled] [data-scope='approval'][data-part='item-indicator']:not([data-state='unchecked']):empty::before {
498
+ background-color: GrayText;
499
+ }
500
+ }
461
501
  }
@@ -85,7 +85,7 @@
85
85
  inline-size: var(--xh-_avatar-group-size);
86
86
  block-size: var(--xh-_avatar-group-size);
87
87
  border-radius: var(--xh-avatar-group-radius, var(--xh-shape-circle));
88
- background: var(--xh-avatar-group-overflow-item-bg, var(--xh-bg-muted));
88
+ background: var(--xh-avatar-group-overflow-item-bg, var(--xh-bg-muted-opaque));
89
89
  color: var(--xh-avatar-group-overflow-item-fg, var(--xh-fg-muted));
90
90
  font-size: var(--xh-_avatar-group-font-size);
91
91
  font-weight: var(--xh-avatar-group-font-weight, var(--xh-font-weight-medium));
package/css/avatar.css CHANGED
@@ -24,7 +24,8 @@
24
24
  inline-size: var(--xh-avatar-size, var(--xh-control-h-md));
25
25
  block-size: var(--xh-avatar-size, var(--xh-control-h-md));
26
26
  border-radius: var(--xh-avatar-radius, var(--xh-shape-circle));
27
- background: var(--xh-avatar-bg, var(--xh-bg-subtle));
27
+ /* 头像在头像组里层叠:底取淡底的不透明档,半透明会透出压在下面的那一枚 */
28
+ background: var(--xh-avatar-bg, var(--xh-bg-subtle-opaque));
28
29
  color: var(--xh-avatar-fg, var(--xh-fg-muted));
29
30
  font-size: var(--xh-avatar-font-size, var(--xh-text-secondary-size));
30
31
  font-weight: var(--xh-avatar-font-weight, var(--xh-font-weight-medium));
@@ -53,13 +54,13 @@
53
54
  两条声明都把 --xh-avatar-bg / --xh-avatar-fg 排在语气值前面:使用者槽仍是最外那一层,
54
55
  写了 tone 的那一枚照样受外层统一控制。整块带 [data-tone] 限定,没写语气的头像逐值不变 */
55
56
  [data-scope='avatar'][data-part='root'][data-tone] {
56
- background: var(--xh-avatar-bg, var(--xh-_tone-subtle, var(--xh-bg-subtle)));
57
+ background: var(--xh-avatar-bg, var(--xh-_tone-subtle, var(--xh-bg-subtle-opaque)));
57
58
  color: var(--xh-avatar-fg, var(--xh-_tone-fg, var(--xh-fg-muted)));
58
59
  }
59
60
 
60
61
  /* 不声明 display,加载完成前的 hidden 直接交给浏览器的 [hidden] 规则收起。
61
- 载完那一刻 hidden 撤掉、这个节点从不显示变为显示,淡入在那一刻起跑,
62
- 图片不是硬切上来盖掉回退字 */
62
+ 载完那一刻 hidden 撤掉、这个节点从不显示变为显示,淡入在那一刻起跑;
63
+ 回退内容同一刻开始淡出(见下),两者交叉,中间不露一拍底色 */
63
64
  [data-scope='avatar'][data-part='image'] {
64
65
  inline-size: 100%;
65
66
  block-size: 100%;
@@ -77,7 +78,14 @@
77
78
  text-transform: uppercase;
78
79
  }
79
80
 
80
- /* display 会盖掉 hidden 属性的 UA 规则,加载完成后显式让位给图片 */
81
+ /* 图片载好:回退内容浮到图片之上原地淡出,与图片的淡入交叉;播完连接层才写 hidden */
82
+ [data-scope='avatar'][data-part='fallback'][data-state='loaded'] {
83
+ position: absolute;
84
+ inset: 0;
85
+ animation: xh-fade-out var(--xh-motion-duration-enter) var(--xh-motion-ease-enter) forwards;
86
+ }
87
+
88
+ /* display 会盖掉 hidden 属性的 UA 规则:载入中等过 fallbackDelay 之前、以及淡出播完之后收起 */
81
89
  [data-scope='avatar'][data-part='fallback'][hidden] {
82
90
  display: none;
83
91
  }