@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/log.css CHANGED
@@ -5,25 +5,11 @@
5
5
 
6
6
  /* 定义 log 样式。 */
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
- @keyframes xh-log-button-in {
16
- from {
17
- opacity: 0;
18
- scale: var(--xh-motion-scale-enter);
19
- }
20
-
21
- to {
22
- opacity: 1;
23
- scale: 1;
24
- }
25
- }
26
- }
12
+ @import '../family/material.css';
27
13
 
28
14
  @layer xihan.components {
29
15
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
@@ -34,7 +20,7 @@
34
20
  /* 行高是本组件的定尺基准:视口按行数定高、每行排版,都从这一个槽位来。
35
21
  它的兜底值须与连接层内联高度里用的那份逐字一致,否则 rows 算出来的高度对不上行。 */
36
22
  [data-scope='log'][data-part='root'] {
37
- --xh-_log-line-h: var(--xh-log-line-height, 1.25rem);
23
+ --xh-_log-line-h: var(--xh-log-line-height, var(--xh-text-code-leading));
38
24
 
39
25
  /* 尺寸缺省档:不写 data-size 时的取值。三档只改这两个私有槽,
40
26
  公开槽留在读处当首选,作者在祖先上设的覆盖才赢得过档位声明。
@@ -79,6 +65,26 @@
79
65
  scrollbar-gutter: stable;
80
66
  }
81
67
 
68
+ /* 接了虚拟滚动:视口只定高、不滚动,里面那层 Virtualizer 视口铺满它、由它滚;
69
+ 行放在 Virtualizer 的条目里没有 content 包裹层,等宽排版落在视口上由行继承 */
70
+ [data-scope='log'][data-part='viewport'][data-virtualized] {
71
+ display: grid;
72
+ grid-template-rows: minmax(0, 1fr);
73
+ overflow: hidden;
74
+ /* 不滚动的视口不留滚动条空道:空道由里面那层 Virtualizer 视口自己管 */
75
+ scrollbar-gutter: auto;
76
+ font-family: var(--xh-log-font, var(--xh-font-family-mono));
77
+ font-size: var(--xh-log-font-size, var(--xh-_log-font-size));
78
+ }
79
+
80
+ [data-scope='log'][data-part='viewport'][hidden] {
81
+ display: none;
82
+ }
83
+
84
+ [data-scope='log'][data-part='viewport'][data-virtualized] [data-scope='log'][data-part='line'] {
85
+ padding-inline: var(--xh-log-line-px, var(--xh-log-content-px, var(--xh-_log-content-px)));
86
+ }
87
+
82
88
  /* 取行期间指针给出忙的反馈 */
83
89
  [data-scope='log'][data-part='root'][data-loading] [data-scope='log'][data-part='viewport'] {
84
90
  cursor: progress;
@@ -121,31 +127,96 @@
121
127
  color: var(--xh-log-level-error-fg, var(--xh-fg-danger));
122
128
  }
123
129
 
130
+ /* ANSI 着色:八种颜色映射到语义令牌(红、绿、黄、蓝落在语气前景,品红、青借代码着色的关键字与字符串色,
131
+ 黑与白落在正文与次要前景),高亮色(90–97)与基础色同一档,只由 data-bright 记下。
132
+ 颜色盖过行的级别色;粗体、斜体、下划线与暗淡各自一个开关 */
133
+ [data-scope='log'][data-part='segment'][data-fg='black'] {
134
+ color: var(--xh-log-ansi-black, var(--xh-fg-default));
135
+ }
136
+
137
+ [data-scope='log'][data-part='segment'][data-fg='red'] {
138
+ color: var(--xh-log-ansi-red, var(--xh-fg-danger));
139
+ }
140
+
141
+ [data-scope='log'][data-part='segment'][data-fg='green'] {
142
+ color: var(--xh-log-ansi-green, var(--xh-fg-success));
143
+ }
144
+
145
+ [data-scope='log'][data-part='segment'][data-fg='yellow'] {
146
+ color: var(--xh-log-ansi-yellow, var(--xh-fg-warning));
147
+ }
148
+
149
+ [data-scope='log'][data-part='segment'][data-fg='blue'] {
150
+ color: var(--xh-log-ansi-blue, var(--xh-fg-info));
151
+ }
152
+
153
+ [data-scope='log'][data-part='segment'][data-fg='magenta'] {
154
+ color: var(--xh-log-ansi-magenta, var(--xh-syntax-keyword));
155
+ }
156
+
157
+ [data-scope='log'][data-part='segment'][data-fg='cyan'] {
158
+ color: var(--xh-log-ansi-cyan, var(--xh-syntax-string));
159
+ }
160
+
161
+ [data-scope='log'][data-part='segment'][data-fg='white'] {
162
+ color: var(--xh-log-ansi-white, var(--xh-fg-muted));
163
+ }
164
+
165
+ [data-scope='log'][data-part='segment'][data-bold] {
166
+ font-weight: var(--xh-log-ansi-bold-weight, var(--xh-font-weight-semibold));
167
+ }
168
+
169
+ /* 暗淡只在没指定颜色时换成次要前景:指定了颜色的那一段保留自己的颜色 */
170
+ [data-scope='log'][data-part='segment'][data-dim]:not([data-fg]) {
171
+ color: var(--xh-log-ansi-dim-fg, var(--xh-fg-subtle));
172
+ }
173
+
174
+ [data-scope='log'][data-part='segment'][data-italic] {
175
+ font-style: italic;
176
+ }
177
+
178
+ [data-scope='log'][data-part='segment'][data-underline] {
179
+ text-decoration: underline;
180
+ }
181
+
124
182
  /* 回到底部:浮在视口右下角,收起时不卸载只隐藏。盒型、悬停 / 按下面与缩放由 Action Control 的
125
183
  floating 档给(circle 正方盒、ghost 形态:白底承载 hover 100 → pressed 200);材质随角落浮钮族走
126
184
  frosted 四件套(底 + 边 + 影 + 磨砂),这里只把使用者槽映射到桥接槽:
127
185
  字形按 xs 档取 --xh-_action-profile-glyph-size(16px),与 back-top 同一把尺 */
128
186
  [data-scope='log'][data-part='scroll-to-end-trigger'] {
129
- --xh-_log-scroll-to-end-trigger-shadow: var(--xh-log-scroll-to-end-trigger-shadow, var(--xh-material-frosted-shadow));
187
+ /* 面走材质家族配方(family/material.css):frosted 档的底、交互阶梯、描边、投影、1px 顶光与背景滤镜,
188
+ liquid 档由配方换成液态面;这里把使用者槽接到 Action Control 的桥接槽 */
189
+ --xh-action-bg-rest: var(--xh-log-scroll-to-end-trigger-bg, var(--xh-_material-bg));
190
+ --xh-action-bg-hover: var(--xh-log-scroll-to-end-trigger-bg-hover, var(--xh-_material-bg-hover));
191
+ --xh-action-bg-pressed: var(--xh-_material-bg-pressed);
192
+ --xh-action-bg-focus-visible: var(--xh-log-scroll-to-end-trigger-bg, var(--xh-_material-bg-focus));
193
+ --xh-action-bg-disabled: var(--xh-log-scroll-to-end-trigger-bg, var(--xh-_material-bg));
194
+ --xh-action-fg-rest: var(--xh-log-scroll-to-end-trigger-fg, var(--xh-_material-fg));
195
+ --xh-action-fg-hover: var(--xh-log-scroll-to-end-trigger-fg, var(--xh-_material-fg));
196
+ --xh-action-fg-pressed: var(--xh-log-scroll-to-end-trigger-fg, var(--xh-_material-fg));
197
+ --xh-action-fg-focus-visible: var(--xh-log-scroll-to-end-trigger-fg, var(--xh-_material-fg));
198
+ --xh-action-border-rest: var(--xh-log-scroll-to-end-trigger-border, var(--xh-_material-border));
199
+ --xh-action-border-hover: var(--xh-log-scroll-to-end-trigger-border, var(--xh-_material-border));
200
+ --xh-action-border-pressed: var(--xh-log-scroll-to-end-trigger-border, var(--xh-_material-border));
201
+ --xh-action-border-focus-visible: var(--xh-log-scroll-to-end-trigger-border, var(--xh-_material-border));
202
+ --xh-action-border-disabled: var(--xh-log-scroll-to-end-trigger-border, var(--xh-_material-border));
203
+ --xh-action-shadow-rest: var(--xh-log-scroll-to-end-trigger-shadow, var(--xh-_material-shadow));
204
+ --xh-action-shadow-hover: var(--xh-log-scroll-to-end-trigger-shadow, var(--xh-_material-shadow));
205
+ --xh-action-shadow-pressed: var(--xh-log-scroll-to-end-trigger-shadow, var(--xh-_material-shadow));
206
+ --xh-action-shadow-focus-visible: var(--xh-log-scroll-to-end-trigger-shadow, var(--xh-_material-shadow));
207
+ --xh-action-shadow-disabled: var(--xh-log-scroll-to-end-trigger-shadow, var(--xh-_material-shadow));
208
+ --xh-action-highlight-rest: var(--xh-_material-highlight);
209
+ --xh-action-highlight-hover: var(--xh-_material-highlight);
210
+ --xh-action-highlight-pressed: var(--xh-_material-highlight);
211
+ --xh-action-highlight-focus-visible: var(--xh-_material-highlight);
212
+ --xh-action-highlight-disabled: var(--xh-_material-highlight);
130
213
  --xh-action-visual-size: var(--xh-log-scroll-to-end-trigger-size, var(--xh-_action-profile-visual-size));
131
214
  --xh-action-radius: var(--xh-log-scroll-to-end-trigger-radius, var(--xh-shape-circle));
132
- --xh-action-bg-rest: var(--xh-log-scroll-to-end-trigger-bg, var(--xh-material-frosted-bg));
133
- --xh-action-bg-hover: var(--xh-log-scroll-to-end-trigger-bg-hover, var(--xh-_action-variant-bg-hover));
134
- --xh-action-border-rest: var(--xh-log-scroll-to-end-trigger-border, var(--xh-material-frosted-border));
135
- --xh-action-border-hover: var(--xh-log-scroll-to-end-trigger-border, var(--xh-material-frosted-border));
136
- --xh-action-border-pressed: var(--xh-log-scroll-to-end-trigger-border, var(--xh-material-frosted-border));
137
- --xh-action-border-focus-visible: var(--xh-log-scroll-to-end-trigger-border, var(--xh-material-frosted-border));
138
- --xh-action-shadow-rest: var(--xh-_log-scroll-to-end-trigger-shadow);
139
- --xh-action-shadow-hover: var(--xh-_log-scroll-to-end-trigger-shadow);
140
- --xh-action-shadow-pressed: var(--xh-_log-scroll-to-end-trigger-shadow);
141
- --xh-action-fg-rest: var(--xh-log-scroll-to-end-trigger-fg, var(--xh-material-frosted-fg));
142
215
  --xh-icon-size: var(--xh-log-icon-size, var(--xh-_action-profile-glyph-size));
143
216
 
144
217
  position: absolute;
145
218
  inset-block-end: var(--xh-log-scroll-to-end-trigger-inset, var(--xh-space-3));
146
219
  inset-inline-end: var(--xh-log-scroll-to-end-trigger-inset, var(--xh-space-3));
147
- -webkit-backdrop-filter: var(--xh-material-frosted-backdrop);
148
- backdrop-filter: var(--xh-material-frosted-backdrop);
149
220
  }
150
221
 
151
222
  /* 连接层收起按钮时写 hidden 属性,上面的 display 会盖掉 UA 规则,这里补回 */
@@ -153,9 +224,15 @@
153
224
  display: none;
154
225
  }
155
226
 
156
- /* 收起不卸载,冒出来时靠这条进场动画;不写 display,可见性归上面那条 [hidden] 管 */
227
+ /* 收起不卸载:离底时冒出来播进场,回底时先播完退场、连接层才写 hidden 藏起。
228
+ 不写 display,可见性归上面那条 [hidden] 管;退场途中不再接指针 */
157
229
  [data-scope='log'][data-part='scroll-to-end-trigger'][data-state='visible'] {
158
- animation: xh-log-button-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong);
230
+ animation: xh-pop-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong);
231
+ }
232
+
233
+ [data-scope='log'][data-part='scroll-to-end-trigger'][data-state='hidden']:not([hidden]) {
234
+ pointer-events: none;
235
+ animation: xh-pop-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
159
236
  }
160
237
 
161
238
  /* 没写内容就由皮肤画一枚向下的字形;作者往按钮里塞了自己的图形,这条规则即不命中。
@@ -207,4 +284,23 @@
207
284
  display: none;
208
285
  }
209
286
  }
287
+
288
+ @media (forced-colors: active) {
289
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
290
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
291
+ 按钮面上取 ButtonText,悬停与按下时家族把按钮涂成 Highlight、字形换 HighlightText;
292
+ 禁用取 GrayText */
293
+ [data-scope='log'][data-part='scroll-to-end-trigger']:empty::before {
294
+ forced-color-adjust: none;
295
+ background-color: ButtonText;
296
+ }
297
+
298
+ [data-scope='log'][data-part='scroll-to-end-trigger']:not([data-disabled]):not([data-loading]):is(:hover, :active, [data-pressed]):empty::before {
299
+ background-color: HighlightText;
300
+ }
301
+
302
+ [data-scope='log'][data-part='scroll-to-end-trigger'][data-disabled]:empty::before {
303
+ background-color: GrayText;
304
+ }
305
+ }
210
306
  }
@@ -93,7 +93,7 @@
93
93
 
94
94
  /* 还没出字的那一段页面上只有这一竖,让它闪着,等的人才分得清是在等还是卡住了 */
95
95
  [data-scope='markdown-stream'][data-part='root'][data-caret]::after {
96
- animation: xh-markdown-stream-caret var(--xh-markdown-stream-caret-duration, var(--xh-caret-duration)) steps(1, end) infinite;
96
+ animation: xh-markdown-stream-caret var(--xh-markdown-stream-caret-duration, var(--xh-motion-loop-caret)) steps(1, end) infinite;
97
97
  }
98
98
 
99
99
  /* 减少动效:等待时的光标停在实心不闪,「还在等」这件事仍然看得出来。
package/css/marquee.css CHANGED
@@ -5,6 +5,9 @@
5
5
 
6
6
  /* 定义 marquee 样式。 */
7
7
 
8
+ /* 暂停开关走动作控件配方;独立皮肤同时传递引入,full bundle 递归去重。 */
9
+ @import '../family/action-control.css';
10
+
8
11
  @layer xihan.motion {
9
12
  /*
10
13
  * 横轴一条、纵轴一条。四个方向乘两种铺法共八组起止点,若不外提就要写八份几乎一样的关键帧。
@@ -46,11 +49,13 @@
46
49
  * 后代式会一路匹配到别人家的节点上去。
47
50
  *
48
51
  * 一圈的时长 = 一份内容的长度 ÷ 每秒像素数。--xh-_marquee-span 是一份内容在滚动轴上的长度,
49
- * 单位像素、写成纯数字。CSS 读不到布局尺寸,这里给的是缺省值;内容明显长于或短于它时,
50
- * 由使用者改写 --xh-marquee-span 对齐真实长度,速度才逐字等于每秒那么多像素。
52
+ * 单位像素、写成纯数字:连接层挂载后实测、写成内联的 --xh-_marquee-measured-span,量到之前
53
+ * (无布局宿主、首帧)退回缺省值。使用者写了 --xh-marquee-span 以它为准。
51
54
  */
52
55
  [data-scope='marquee'][data-part='root'] {
53
- --xh-_marquee-span: var(--xh-marquee-span, 600);
56
+ /* 暂停开关的边长:开关用它定尺,两端渐隐按它给开关留出位置,两处同源 */
57
+ --xh-_marquee-trigger-size: var(--xh-control-h-md);
58
+ --xh-_marquee-span: var(--xh-marquee-span, var(--xh-_marquee-measured-span, 600));
54
59
  --xh-_marquee-speed: var(--xh-marquee-speed, 60);
55
60
 
56
61
  /* 铺一份时一圈走两倍自身长度:从窗口一侧整段进来,再整段出到另一侧 */
@@ -71,6 +76,9 @@
71
76
  --xh-_marquee-from: 100%;
72
77
  --xh-_marquee-to: -100%;
73
78
 
79
+ /* 暂停开关压在窗口一端,定位基准是窗口;isolation 把开关的层号关在本组件内,不参与页面级层序 */
80
+ position: relative;
81
+ isolation: isolate;
74
82
  display: flex;
75
83
  overflow: hidden;
76
84
  max-inline-size: 100%;
@@ -134,15 +142,65 @@
134
142
  --xh-_marquee-to: 0%;
135
143
  }
136
144
 
137
- /* 悬停暂停:只在能用指针的设备上生效,触摸端不留一个按不掉的悬停态 */
145
+ /*
146
+ * 两端渐隐:窗口沿滚动方向两端各留一段遮罩,内容从一端淡入、从另一端淡出。
147
+ * 遮罩只画在窗口上,轨道照常整段移动;遮罩是静态的,不参与动画。
148
+ * 遮罩的几层取值都经私有槽下发,真正写 mask 的只有这一条:减弱动效与打印只需在同一特指度上盖掉它。
149
+ */
150
+ [data-scope='marquee'][data-part='root'][data-fade] {
151
+ --xh-_marquee-fade-size: var(--xh-marquee-fade-size, var(--xh-space-6));
152
+ /* 横排沿行内方向:角度乘方向符号,rtl 下行首在右,两端各自仍是淡入与淡出 */
153
+ --xh-_marquee-fade-dir: calc(90deg * var(--xh-direction-sign));
154
+ /* 行尾那一段收到哪里为止:没有暂停开关时收到窗口尽头 */
155
+ --xh-_marquee-fade-reserve: 0px;
156
+ --xh-_marquee-fade-reserve-size: 0 0;
157
+ --xh-_marquee-fade-reserve-at: var(--xh-direction-end) center;
158
+ /* 两层:沿滚动方向的 alpha 坡道,与给暂停开关留出的实心块;两种写法共用这一份 */
159
+ --xh-_marquee-fade-mask:
160
+ linear-gradient(var(--xh-_marquee-fade-dir), transparent 0, black var(--xh-_marquee-fade-size), black calc(100% - var(--xh-_marquee-fade-reserve) - var(--xh-_marquee-fade-size)), transparent calc(100% - var(--xh-_marquee-fade-reserve))),
161
+ linear-gradient(black, black);
162
+
163
+ -webkit-mask-image: var(--xh-_marquee-fade-mask);
164
+ mask-image: var(--xh-_marquee-fade-mask);
165
+ -webkit-mask-size: 100% 100%, var(--xh-_marquee-fade-reserve-size);
166
+ mask-size: 100% 100%, var(--xh-_marquee-fade-reserve-size);
167
+ -webkit-mask-position: 0 0, var(--xh-_marquee-fade-reserve-at);
168
+ mask-position: 0 0, var(--xh-_marquee-fade-reserve-at);
169
+ -webkit-mask-repeat: no-repeat;
170
+ mask-repeat: no-repeat;
171
+ }
172
+
173
+ [data-scope='marquee'][data-part='root'][data-fade][data-orientation='vertical'] {
174
+ --xh-_marquee-fade-dir: to bottom;
175
+ }
176
+
177
+ /*
178
+ * 暂停开关压在行尾一端:行尾那段渐隐收到开关之前,第二层实心遮罩把开关所在那一块整块露出来,
179
+ * 开关不被淡掉,开关与淡出段之间也不留一条没淡的缝。竖排时开关在块末那一角,露出的是那一角的方块。
180
+ */
181
+ [data-scope='marquee'][data-part='root'][data-fade]:has(> [data-scope='marquee'][data-part='autoplay-trigger']:not([hidden])) {
182
+ --xh-_marquee-fade-reserve: calc(var(--xh-marquee-trigger-inset, var(--xh-space-1)) + var(--xh-_marquee-trigger-size));
183
+ --xh-_marquee-fade-reserve-size: var(--xh-_marquee-fade-reserve) 100%;
184
+ }
185
+
186
+ [data-scope='marquee'][data-part='root'][data-fade][data-orientation='vertical']:has(> [data-scope='marquee'][data-part='autoplay-trigger']:not([hidden])) {
187
+ --xh-_marquee-fade-reserve-size: var(--xh-_marquee-fade-reserve) var(--xh-_marquee-fade-reserve);
188
+ --xh-_marquee-fade-reserve-at: var(--xh-direction-end) bottom;
189
+ }
190
+
191
+ /*
192
+ * 悬停暂停:只在能用指针的设备上生效,触摸端不留一个按不掉的悬停态。
193
+ * 指针停在暂停开关上不算:那里显示的是开关自己的状态,按下「继续」时指针还在开关上,
194
+ * 若照样算悬停,轨道要等指针挪开才动,这一下按了像没按。
195
+ */
138
196
  @media (hover: hover) {
139
- [data-scope='marquee'][data-part='root'][data-pause-on-hover]:hover {
197
+ [data-scope='marquee'][data-part='root'][data-pause-on-hover]:hover:not(:has(> [data-scope='marquee'][data-part='autoplay-trigger']:hover)) {
140
198
  --xh-_marquee-play: paused;
141
199
  }
142
200
  }
143
201
 
144
- /* 键盘用户没有悬停:焦点落进窗口时同样停住,否则轨道里的链接刚聚焦就被带走了 */
145
- [data-scope='marquee'][data-part='root'][data-pause-on-hover]:focus-within {
202
+ /* 键盘用户没有悬停:焦点落进窗口时同样停住,否则轨道里的链接刚聚焦就被带走了;焦点在暂停开关上同理不算 */
203
+ [data-scope='marquee'][data-part='root'][data-pause-on-hover]:focus-within:not(:has(> [data-scope='marquee'][data-part='autoplay-trigger']:focus)) {
146
204
  --xh-_marquee-play: paused;
147
205
  }
148
206
 
@@ -151,6 +209,56 @@
151
209
  --xh-_marquee-play: paused;
152
210
  }
153
211
 
212
+ /*
213
+ * 暂停开关:压在窗口行尾一端、上下居中;竖排时落在块末那一角。
214
+ * 窗口是裁切内容的单行,开关放进去会被轨道挤出视野,只能浮在上面。
215
+ * 盒型、描边、字色与 0.97 按压由动作控件配方按 icon 档的描边档给出;描边档静息不填底,
216
+ * 这里把底换成不透明的面,悬停 / 按下走同一承载面的不透明阶梯,后面走过的内容才透不上来。
217
+ */
218
+ [data-scope='marquee'][data-part='autoplay-trigger'] {
219
+ --xh-action-bg-rest: var(--xh-marquee-trigger-bg, var(--xh-bg-surface));
220
+ --xh-action-bg-hover: var(--xh-marquee-trigger-bg-hover, var(--xh-bg-subtle-opaque));
221
+ --xh-action-bg-pressed: var(--xh-marquee-trigger-bg-active, var(--xh-bg-subtle-hover-opaque));
222
+ --xh-action-bg-focus-visible: var(--xh-marquee-trigger-bg, var(--xh-bg-surface));
223
+ /* 作者塞进来的图标跟着 icon 档走,与皮肤画的兜底字形同一把尺 */
224
+ --xh-icon-size: var(--xh-marquee-icon-size, var(--xh-_action-profile-glyph-size));
225
+ /* 边长与两端渐隐给开关留的位置同源,都读根上那一支 */
226
+ --xh-action-visual-size: var(--xh-_marquee-trigger-size);
227
+
228
+ position: absolute;
229
+ z-index: 1;
230
+ inset-block: 0;
231
+ inset-inline-end: var(--xh-marquee-trigger-inset, var(--xh-space-1));
232
+ margin-block: auto;
233
+ }
234
+
235
+ [data-scope='marquee'][data-part='root'][data-orientation='vertical'] > [data-scope='marquee'][data-part='autoplay-trigger'] {
236
+ inset-block-start: auto;
237
+ inset-block-end: var(--xh-marquee-trigger-inset, var(--xh-space-1));
238
+ margin-block: 0;
239
+ }
240
+
241
+ [data-scope='marquee'][data-part='autoplay-trigger'][hidden] {
242
+ display: none;
243
+ }
244
+
245
+ /* 不给内容时画图标:在走画暂停,停住画播放;图标随动作,与可及名同一口径 */
246
+ [data-scope='marquee'][data-part='autoplay-trigger']:empty::before {
247
+ content: '';
248
+ display: inline-block;
249
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-md));
250
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-md));
251
+ vertical-align: var(--xh-glyph-baseline-shift);
252
+ background-color: currentColor;
253
+ -webkit-mask: var(--xh-glyph-mark-pause) center / contain no-repeat;
254
+ mask: var(--xh-glyph-mark-pause) center / contain no-repeat;
255
+ }
256
+
257
+ [data-scope='marquee'][data-part='autoplay-trigger'][data-state='paused']:empty::before {
258
+ -webkit-mask-image: var(--xh-glyph-mark-play);
259
+ mask-image: var(--xh-glyph-mark-play);
260
+ }
261
+
154
262
  /* 轨道:走的是这一层,一圈走完立刻回到起点接着走 */
155
263
  [data-scope='marquee'][data-part='content'] {
156
264
  display: flex;
@@ -196,6 +304,17 @@
196
304
  [data-scope='marquee'][data-part='root'] {
197
305
  overflow: auto;
198
306
  }
307
+
308
+ /* 停下后窗口改成可滚:两端再淡就把可滚内容的首尾藏起来了 */
309
+ [data-scope='marquee'][data-part='root'][data-fade] {
310
+ -webkit-mask-image: none;
311
+ mask-image: none;
312
+ }
313
+
314
+ /* 轨道已经停在起点:没有可停的东西,开关只会挡住可滚的内容 */
315
+ [data-scope='marquee'][data-part='autoplay-trigger'] {
316
+ display: none;
317
+ }
199
318
  }
200
319
 
201
320
  :where([data-motion='reduce']) [data-scope='marquee'][data-part='content'] {
@@ -206,6 +325,15 @@
206
325
  overflow: auto;
207
326
  }
208
327
 
328
+ :where([data-motion='reduce']) [data-scope='marquee'][data-part='root'][data-fade] {
329
+ -webkit-mask-image: none;
330
+ mask-image: none;
331
+ }
332
+
333
+ :where([data-motion='reduce']) [data-scope='marquee'][data-part='autoplay-trigger'] {
334
+ display: none;
335
+ }
336
+
209
337
  @media print {
210
338
  /* 打印:动画会被定格在任意一帧——转了一半的圈、扫到一半的微光。这里换成静止的表达,
211
339
  取值与减弱动效那一档相同。两档各写一块、不并进一条媒体查询列表:减弱动效另有一份
@@ -218,5 +346,33 @@
218
346
  [data-scope='marquee'][data-part='root'] {
219
347
  overflow: auto;
220
348
  }
349
+
350
+ [data-scope='marquee'][data-part='root'][data-fade] {
351
+ -webkit-mask-image: none;
352
+ mask-image: none;
353
+ }
354
+
355
+ [data-scope='marquee'][data-part='autoplay-trigger'] {
356
+ display: none;
357
+ }
358
+ }
359
+
360
+ @media (forced-colors: active) {
361
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
362
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
363
+ 按钮面上取 ButtonText,悬停与按下时家族把按钮涂成 Highlight、字形换 HighlightText;
364
+ 禁用取 GrayText */
365
+ [data-scope='marquee'][data-part='autoplay-trigger']:empty::before {
366
+ forced-color-adjust: none;
367
+ background-color: ButtonText;
368
+ }
369
+
370
+ [data-scope='marquee'][data-part='autoplay-trigger']:not([data-disabled]):not([data-loading]):is(:hover, :active, [data-pressed]):empty::before {
371
+ background-color: HighlightText;
372
+ }
373
+
374
+ [data-scope='marquee'][data-part='autoplay-trigger'][data-disabled]:empty::before {
375
+ background-color: GrayText;
376
+ }
221
377
  }
222
378
  }
@@ -33,7 +33,7 @@
33
33
 
34
34
  /* 占位与码面之间来回时,底色与那圈描边淡变 */
35
35
  transition:
36
- background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
36
+ background-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
37
37
  box-shadow var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
38
38
  }
39
39
 
package/css/mention.css CHANGED
@@ -6,9 +6,11 @@
6
6
  /* Mention 独立皮肤同时传递引入字段视觉盒与浮层集合行;full bundle 递归去重。 */
7
7
  @import '../family/field-chrome.css';
8
8
  @import '../family/collection-item.css';
9
+ @import '../family/loading-ring.css';
9
10
 
10
11
  /* 共享关键帧由 family/motion.css 提供,本皮肤 @import 它,单独引入仍成立。 */
11
12
  @import '../family/motion.css';
13
+ @import '../family/material.css';
12
14
 
13
15
  @layer xihan.components {
14
16
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
@@ -165,6 +167,13 @@
165
167
  display: none;
166
168
  }
167
169
 
170
+ /* 多行形态:正文可以换行,框随 rows 定起始高度、不钉单行控件高(家族的多行布局给);
171
+ 框自己就是 chrome,家族多行布局把上下内衬归零,这里按文本字段多行档同一支令牌补回,只许纵向拉伸 */
172
+ [data-scope='mention'][data-part='input'][data-xh-field-layout='textarea'] {
173
+ padding-block: var(--xh-mention-textarea-py, var(--xh-space-2));
174
+ resize: vertical;
175
+ }
176
+
168
177
  /* 输入框自身即 chrome,家族按 data-xh-field-input 给的占位色命不中它,占位色仍由本皮肤给 */
169
178
  [data-scope='mention'][data-part='input']::placeholder {
170
179
  color: var(--xh-mention-placeholder-fg, var(--xh-fg-subtle));
@@ -188,6 +197,15 @@
188
197
  }
189
198
 
190
199
  [data-scope='mention'][data-part='content'] {
200
+ /* 材质由家族配方画(family/material.css 的 frosted 面:描边、底、1px 顶光、前景、投影与背景滤镜),
201
+ 这里只把使用者槽接进来 */
202
+ --xh-frosted-bg: var(--xh-mention-content-bg, var(--xh-_material-bg));
203
+ --xh-frosted-border: var(--xh-mention-content-border, var(--xh-_material-border));
204
+ --xh-frosted-fg: var(--xh-mention-content-fg, var(--xh-_material-fg));
205
+ --xh-frosted-shadow: var(--xh-mention-content-shadow, var(--xh-_material-shadow));
206
+ --xh-frosted-backdrop: var(--xh-mention-content-backdrop, var(--xh-_material-backdrop));
207
+ --xh-frosted-highlight: var(--xh-mention-content-highlight, var(--xh-_material-highlight));
208
+
191
209
  position: relative;
192
210
  display: flex;
193
211
  flex-direction: column;
@@ -201,31 +219,13 @@
201
219
  max-block-size: min(var(--xh-mention-content-max-h, var(--xh-overlay-max-h)), var(--xh-_mention-available-h));
202
220
  padding-block: var(--xh-mention-content-py, var(--xh-space-1));
203
221
  padding-inline: var(--xh-mention-content-px, var(--xh-space-1));
204
- border: var(--xh-stroke-thin) solid var(--xh-mention-content-border, var(--xh-material-frosted-border));
205
222
  border-radius: var(--xh-mention-content-radius, var(--xh-shape-overlay));
206
- background: var(--xh-mention-content-bg, var(--xh-material-frosted-bg));
207
- color: var(--xh-mention-content-fg, var(--xh-material-frosted-fg));
208
- box-shadow: var(--xh-mention-content-shadow, var(--xh-material-frosted-shadow));
209
- -webkit-backdrop-filter: var(--xh-mention-content-backdrop, var(--xh-material-frosted-backdrop));
210
- backdrop-filter: var(--xh-mention-content-backdrop, var(--xh-material-frosted-backdrop));
211
223
  overflow-y: auto;
212
224
  /* 浮层滚动面:滚到头不把页面一起带走 */
213
225
  overscroll-behavior: contain;
214
226
  pointer-events: auto;
215
227
  }
216
228
 
217
- /* 光效仅属于唯一外壳,不拦截候选或同级状态文字。 */
218
- [data-scope='mention'][data-part='content']::before {
219
- content: '';
220
- position: absolute;
221
- inset-block-start: 0;
222
- inset-inline: 0;
223
- block-size: var(--xh-stroke-thin);
224
- border-radius: inherit;
225
- background: var(--xh-mention-content-highlight, var(--xh-material-frosted-highlight));
226
- pointer-events: none;
227
- }
228
-
229
229
  /* 这里刻意没有 [data-part='content'][hidden] { display: none },与 dialog 一致:
230
230
  收起态连接层会给 content 打 hidden,而上面那条 display 正是要盖掉 UA 的
231
231
  [hidden]{display:none}——退场动画得先能播。presence 读的是 content 的 animationName,
@@ -256,7 +256,7 @@
256
256
  --xh-collection-inline-padding: var(--xh-mention-item-px, var(--xh-_mention-item-px));
257
257
  --xh-collection-gap: var(--xh-mention-item-gap, var(--xh-_mention-gap));
258
258
  --xh-collection-font-size: var(--xh-mention-item-font-size, var(--xh-_mention-font-size));
259
- --xh-collection-radius: var(--xh-mention-item-radius, var(--xh-shape-control));
259
+ --xh-collection-radius: var(--xh-mention-item-radius, var(--xh-shape-inset));
260
260
  --xh-collection-fg-rest: var(--xh-mention-item-fg, var(--xh-material-frosted-fg));
261
261
  --xh-collection-fg-hover: var(--xh-mention-item-fg, var(--xh-material-frosted-fg));
262
262
  --xh-collection-fg-keyboard-highlight: var(--xh-mention-item-fg, var(--xh-material-frosted-fg));
@@ -338,6 +338,7 @@
338
338
  display: flex;
339
339
  align-items: center;
340
340
  justify-content: center;
341
+ gap: var(--xh-_mention-gap);
341
342
  grid-area: 1 / 1;
342
343
  padding-block: var(--xh-mention-loading-py, var(--xh-space-3));
343
344
  padding-inline: var(--xh-mention-loading-px, var(--xh-_mention-item-px));
@@ -351,11 +352,26 @@
351
352
  display: none;
352
353
  }
353
354
 
355
+ /* 首次加载:文案前一枚加载环,由加载环配方画(连接层投影 data-xh-loading-ring 与 data-loading),
356
+ 直径取所在尺寸档的图标档 */
357
+ [data-scope='mention'][data-part='loading']::before {
358
+ content: '';
359
+ flex: none;
360
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-md));
361
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-md));
362
+ }
363
+
354
364
  [data-scope='mention'][data-part='empty']:empty,
355
365
  [data-scope='mention'][data-part='loading']:empty {
356
366
  display: none;
357
367
  }
358
368
 
369
+ /* 刷新:已有候选时后台在取,保留上一帧,候选按 micro 淡下,数据到了再淡回;在途占位此时让位。
370
+ 淡的是候选不是面:content 是整块磨砂面,整块淡下去面就透了 */
371
+ [data-scope='mention'][data-part='content'][aria-busy='true'] [data-scope='mention'][data-part='item'] {
372
+ opacity: var(--xh-state-disabled-opacity);
373
+ }
374
+
359
375
  /* 手写结构可以把全部候选留在 DOM 里只加 hidden;没有可见 item 也没有状态文案时,
360
376
  打开的空 listbox 不画无意义框。closed 不命中,content 仍能完成 Presence 退场。 */
361
377
  [data-scope='mention'][data-part='positioner']:not(:has(