@xihan-ui/styles 3.0.0 → 3.2.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 (221) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +1 -1
  3. package/component-tokens.d.ts +29 -0
  4. package/components.tokens.json +683 -27
  5. package/css/alert.css +2 -2
  6. package/css/approval.css +4 -4
  7. package/css/button.css +9 -5
  8. package/css/calendar-picker.css +3 -3
  9. package/css/calendar-range-picker.css +7 -7
  10. package/css/carousel.css +45 -13
  11. package/css/cartesian-chart.css +11 -11
  12. package/css/cascader.css +10 -10
  13. package/css/citation.css +2 -2
  14. package/css/code-view.css +6 -6
  15. package/css/color-picker.css +81 -0
  16. package/css/color-swatch-picker.css +9 -4
  17. package/css/combobox.css +12 -4
  18. package/css/command.css +1 -1
  19. package/css/date-field.css +39 -0
  20. package/css/date-picker.css +86 -9
  21. package/css/date-range-picker.css +47 -7
  22. package/css/diff-view.css +8 -8
  23. package/css/field-array.css +1 -1
  24. package/css/field.css +4 -0
  25. package/css/file-upload.css +1 -1
  26. package/css/float-button.css +140 -27
  27. package/css/form.css +14 -6
  28. package/css/graph-chart.css +2 -2
  29. package/css/highlight.css +1 -1
  30. package/css/image.css +1 -1
  31. package/css/mention.css +1 -1
  32. package/css/message-feed.css +1 -1
  33. package/css/notification.css +26 -20
  34. package/css/number-field.css +18 -18
  35. package/css/page-header.css +2 -2
  36. package/css/password-input.css +2 -2
  37. package/css/popconfirm.css +3 -3
  38. package/css/question-flow.css +8 -8
  39. package/css/radar-chart.css +6 -6
  40. package/css/rating.css +1 -1
  41. package/css/select.css +2 -2
  42. package/css/separator.css +2 -2
  43. package/css/side-nav.css +13 -13
  44. package/css/skeleton.css +5 -5
  45. package/css/slider.css +6 -6
  46. package/css/sortable.css +2 -2
  47. package/css/table.css +19 -2
  48. package/css/tags-input.css +1 -1
  49. package/css/time-picker.css +59 -2
  50. package/css/tool-call.css +7 -7
  51. package/css/tooltip.css +1 -1
  52. package/css/tree-select.css +2 -2
  53. package/css/tree.css +81 -11
  54. package/dist/css/accordion.css +269 -0
  55. package/dist/css/affix.css +47 -0
  56. package/dist/css/alert.css +247 -0
  57. package/dist/css/anchor.css +252 -0
  58. package/dist/css/approval.css +501 -0
  59. package/dist/css/avatar-group.css +99 -0
  60. package/dist/css/avatar.css +98 -0
  61. package/dist/css/back-top.css +161 -0
  62. package/dist/css/badge.css +205 -0
  63. package/dist/css/bar-code.css +62 -0
  64. package/dist/css/breadcrumb.css +230 -0
  65. package/dist/css/button-group.css +263 -0
  66. package/dist/css/button.css +219 -0
  67. package/dist/css/calendar-picker.css +467 -0
  68. package/dist/css/calendar-range-picker.css +554 -0
  69. package/dist/css/card.css +221 -0
  70. package/dist/css/carousel.css +710 -0
  71. package/dist/css/cartesian-chart.css +830 -0
  72. package/dist/css/cascader.css +961 -0
  73. package/dist/css/checkbox-group.css +358 -0
  74. package/dist/css/checkbox.css +257 -0
  75. package/dist/css/citation.css +470 -0
  76. package/dist/css/clipboard.css +315 -0
  77. package/dist/css/code-view.css +374 -0
  78. package/dist/css/collapsible.css +185 -0
  79. package/dist/css/color-field.css +176 -0
  80. package/dist/css/color-picker.css +595 -0
  81. package/dist/css/color-slider.css +281 -0
  82. package/dist/css/color-swatch-picker.css +250 -0
  83. package/dist/css/color-swatch.css +35 -0
  84. package/dist/css/combobox.css +574 -0
  85. package/dist/css/command.css +399 -0
  86. package/dist/css/context-menu.css +420 -0
  87. package/dist/css/date-field.css +317 -0
  88. package/dist/css/date-picker.css +868 -0
  89. package/dist/css/date-range-picker.css +886 -0
  90. package/dist/css/description.css +67 -0
  91. package/dist/css/descriptions.css +299 -0
  92. package/dist/css/dialog.css +455 -0
  93. package/dist/css/diff-view.css +513 -0
  94. package/dist/css/download-trigger.css +151 -0
  95. package/dist/css/drawer.css +552 -0
  96. package/dist/css/editable.css +295 -0
  97. package/dist/css/empty-state.css +163 -0
  98. package/dist/css/field-array.css +226 -0
  99. package/dist/css/field.css +166 -0
  100. package/dist/css/fieldset.css +102 -0
  101. package/dist/css/file-upload.css +429 -0
  102. package/dist/css/flex.css +441 -0
  103. package/dist/css/float-button.css +569 -0
  104. package/dist/css/floating-panel.css +429 -0
  105. package/dist/css/focus.css +46 -0
  106. package/dist/css/forced-colors.css +93 -0
  107. package/dist/css/form.css +393 -0
  108. package/dist/css/funnel-chart.css +106 -0
  109. package/dist/css/graph-chart.css +205 -0
  110. package/dist/css/grid-list.css +235 -0
  111. package/dist/css/grid.css +1201 -0
  112. package/dist/css/heatmap.css +552 -0
  113. package/dist/css/hierarchy-chart.css +298 -0
  114. package/dist/css/highlight.css +41 -0
  115. package/dist/css/hover-card.css +226 -0
  116. package/dist/css/icon.css +225 -0
  117. package/dist/css/image-cropper.css +373 -0
  118. package/dist/css/image-viewer.css +538 -0
  119. package/dist/css/image.css +98 -0
  120. package/dist/css/infinite-scroll.css +77 -0
  121. package/dist/css/input-group.css +275 -0
  122. package/dist/css/json-viewer.css +379 -0
  123. package/dist/css/kbd.css +72 -0
  124. package/dist/css/label.css +78 -0
  125. package/dist/css/layers.css +7 -0
  126. package/dist/css/layout.css +372 -0
  127. package/dist/css/liquid.css +106 -0
  128. package/dist/css/list.css +152 -0
  129. package/dist/css/listbox.css +390 -0
  130. package/dist/css/loading-bar.css +141 -0
  131. package/dist/css/log.css +306 -0
  132. package/dist/css/markdown-stream.css +149 -0
  133. package/dist/css/marquee.css +378 -0
  134. package/dist/css/masonry.css +70 -0
  135. package/dist/css/matrix-code.css +67 -0
  136. package/dist/css/mention.css +393 -0
  137. package/dist/css/menu.css +364 -0
  138. package/dist/css/menubar.css +456 -0
  139. package/dist/css/message-feed.css +318 -0
  140. package/dist/css/navigation-menu.css +513 -0
  141. package/dist/css/notification.css +782 -0
  142. package/dist/css/number-animation.css +44 -0
  143. package/dist/css/number-field.css +252 -0
  144. package/dist/css/overlay-arrow.css +56 -0
  145. package/dist/css/page-header.css +184 -0
  146. package/dist/css/pagination.css +450 -0
  147. package/dist/css/password-input.css +391 -0
  148. package/dist/css/pie-chart.css +247 -0
  149. package/dist/css/pin-input.css +199 -0
  150. package/dist/css/pointer.css +36 -0
  151. package/dist/css/popconfirm.css +320 -0
  152. package/dist/css/popover.css +279 -0
  153. package/dist/css/progress.css +501 -0
  154. package/dist/css/prompt-input.css +260 -0
  155. package/dist/css/question-flow.css +553 -0
  156. package/dist/css/radar-chart.css +233 -0
  157. package/dist/css/radio-group.css +598 -0
  158. package/dist/css/rating.css +294 -0
  159. package/dist/css/reasoning.css +343 -0
  160. package/dist/css/reset.css +117 -0
  161. package/dist/css/resizable.css +242 -0
  162. package/dist/css/sankey-chart.css +174 -0
  163. package/dist/css/scroll-area.css +240 -0
  164. package/dist/css/scrollbar.css +204 -0
  165. package/dist/css/select.css +612 -0
  166. package/dist/css/separator.css +122 -0
  167. package/dist/css/side-nav.css +476 -0
  168. package/dist/css/signature-pad.css +179 -0
  169. package/dist/css/skeleton.css +161 -0
  170. package/dist/css/slider.css +374 -0
  171. package/dist/css/sortable.css +228 -0
  172. package/dist/css/sparkline.css +164 -0
  173. package/dist/css/spinner.css +236 -0
  174. package/dist/css/splitter.css +159 -0
  175. package/dist/css/statistic.css +137 -0
  176. package/dist/css/steps.css +627 -0
  177. package/dist/css/switch.css +351 -0
  178. package/dist/css/table.css +1145 -0
  179. package/dist/css/tabs.css +944 -0
  180. package/dist/css/tag-group.css +247 -0
  181. package/dist/css/tag.css +310 -0
  182. package/dist/css/tags-input.css +322 -0
  183. package/dist/css/text-field.css +197 -0
  184. package/dist/css/time-field.css +277 -0
  185. package/dist/css/time-picker.css +707 -0
  186. package/dist/css/time-range-picker.css +746 -0
  187. package/dist/css/timeline.css +423 -0
  188. package/dist/css/timer.css +123 -0
  189. package/dist/css/timestamp.css +34 -0
  190. package/dist/css/toggle-group.css +424 -0
  191. package/dist/css/toggle.css +146 -0
  192. package/dist/css/tone.css +189 -0
  193. package/dist/css/tool-call.css +429 -0
  194. package/dist/css/toolbar.css +365 -0
  195. package/dist/css/tooltip.css +218 -0
  196. package/dist/css/tour.css +439 -0
  197. package/dist/css/transfer.css +638 -0
  198. package/dist/css/tree-select.css +878 -0
  199. package/dist/css/tree.css +733 -0
  200. package/dist/css/truncate.css +108 -0
  201. package/dist/css/typography.css +472 -0
  202. package/dist/css/undefined.css +58 -0
  203. package/dist/css/virtualizer.css +166 -0
  204. package/dist/css/visually-hidden.css +20 -0
  205. package/dist/css/watermark.css +82 -0
  206. package/dist/family/action-control.css +487 -0
  207. package/dist/family/chart.css +480 -0
  208. package/dist/family/check-mark.css +94 -0
  209. package/dist/family/choice-card.css +97 -0
  210. package/dist/family/collection-item.css +764 -0
  211. package/dist/family/field-chrome.css +287 -0
  212. package/dist/family/loading-ring.css +95 -0
  213. package/dist/family/material.css +72 -0
  214. package/dist/family/menu-choice.css +56 -0
  215. package/dist/family/motion.css +363 -0
  216. package/dist/family/swatch.css +59 -0
  217. package/dist/family/tag-list.css +59 -0
  218. package/family/action-control.css +3 -12
  219. package/index.css +7307 -6759
  220. package/index.unlayered.css +7309 -6761
  221. package/package.json +165 -164
@@ -0,0 +1,782 @@
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
+ /* 定义 notification 样式。 */
7
+
8
+ /* 共享关键帧由 family/motion.css 提供,本皮肤 @import 它,单独引入仍成立。 */
9
+ @import '../family/motion.css';
10
+ /* Notification 独立皮肤同时传递引入操作 / 关闭钮的动作控件配方与加载环配方;full bundle 递归去重。 */
11
+ @import '../family/action-control.css';
12
+ @import '../family/loading-ring.css';
13
+
14
+ @layer xihan.motion {
15
+ /* 叠放的一摞:新卡片从视口边缘外进入,同时落到自己的折叠层级。整幅位移取令牌,减弱动效下归零,只剩淡入 */
16
+ @keyframes xh-notification-stack-in {
17
+ from {
18
+ opacity: var(--xh-_enter-from-opacity, 0);
19
+ translate: 0 calc(var(--xh-_notification-stack-dir, 1) * (var(--xh-_notification-offset, 0px) - var(--xh-motion-travel)));
20
+ scale: var(--xh-notification-stack-scale, var(--xh-_notification-stack-scale));
21
+ }
22
+
23
+ to {
24
+ opacity: 1;
25
+ translate: 0 calc(var(--xh-_notification-stack-dir, 1) * var(--xh-_notification-offset, 0px));
26
+ scale: var(--xh-notification-stack-scale, var(--xh-_notification-stack-scale));
27
+ }
28
+ }
29
+
30
+ @keyframes xh-notification-stack-out {
31
+ from {
32
+ opacity: 1;
33
+ translate: 0 calc(var(--xh-_notification-stack-dir, 1) * var(--xh-_notification-y, 0px));
34
+ scale: var(--xh-_notification-scale, 1);
35
+ }
36
+
37
+ to {
38
+ opacity: 0;
39
+ translate: 0 calc(var(--xh-_notification-stack-dir, 1) * (var(--xh-_notification-y, 0px) - var(--xh-motion-travel)));
40
+ scale: var(--xh-_notification-scale, 1);
41
+ }
42
+ }
43
+ }
44
+
45
+ @layer xihan.components {
46
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
47
+ .xh-scope-notification {
48
+ --xh-notification-skin: 1;
49
+ }
50
+
51
+ /* 作用域包装不占布局:通知落在哪儿由九个 group 各自的 fixed 说了算,
52
+ root 只是把队列的作用域圈起来(地标挂在那一摞上,不在这里)。
53
+ 它若留下一个块盒,写在页面中段的用法(按钮和队列摆在一起)就会凭空多出一行空白。
54
+ 另一件必须提防的事:fixed 认的是视口,祖先上一旦出现
55
+ transform / filter / backdrop-filter / contain,包含块就被抢走,整摞通知立刻贴错位置——
56
+ 所以这里连 position 都不碰,也别往覆盖钩子里加这几个属性。 */
57
+ .xh-scope-notification[data-part='root'] {
58
+ display: contents;
59
+ }
60
+
61
+ /* 只要声明过 display,就盖掉了 UA 的 [hidden]{display:none},收起态得自己补回来 */
62
+ .xh-scope-notification[data-part='root'][hidden] {
63
+ display: none;
64
+ }
65
+
66
+ /*
67
+ * 逐条排开的一摞 = 一整面视口大小的定位面:贴边靠 padding,九个位靠 justify/align 表达。
68
+ * 不用四角 inset 逐个摆,是因为 column flex 的 align-items 走的是行内轴,
69
+ * RTL 下 start/end 自动跟着翻;写死 inset-inline-start 的话居中那三个位
70
+ * 还得再配一次 translate,方向一变就错。
71
+ * 条目次序按队列原样(旧的在前):贴底那三个位于是新的离屏幕边最近,符合直觉。
72
+ */
73
+ .xh-scope-notification[data-part='group'] {
74
+ position: fixed;
75
+ inset-block: 0;
76
+ inset-inline: 0;
77
+ z-index: var(--xh-notification-layer, var(--xh-layer-toast));
78
+ display: flex;
79
+ flex-direction: column;
80
+ /* 基线按卡片的默认落位 bottom-end 写,下面只覆盖跟它不同的那几个位 */
81
+ justify-content: flex-end;
82
+ align-items: center;
83
+ /* 贴边与安全区取大的一头:刘海、圆角与底部横条压住的那一段由系统给出,
84
+ 贴边比它窄时卡片会被切掉一角。桌面上 env() 恒为 0,取值与贴边槽完全一致。
85
+ 两条内联的 env 一起取大值:安全区只有物理方向的四个名字,
86
+ 分左右写在 RTL 下会翻错边,两侧同取较宽的那一段。 */
87
+ padding-block-start: max(var(--xh-notification-inset, var(--xh-space-6)), env(safe-area-inset-top));
88
+ padding-block-end: max(var(--xh-notification-inset, var(--xh-space-6)), env(safe-area-inset-bottom));
89
+ padding-inline: max(
90
+ var(--xh-notification-inset, var(--xh-space-6)),
91
+ env(safe-area-inset-left),
92
+ env(safe-area-inset-right)
93
+ );
94
+ /* 摞内间距由连接层写进内联 style(三个适配器共用同一份),这里绝不重复写:
95
+ 写了也会被内联盖掉,只会让人误以为间距归样式层管 */
96
+ /* 整面铺开就等于罩住了整块视口。面本身不吃指针,只有卡片吃(见下),
97
+ 否则九个位一叠,底下的页面就再也点不动了 */
98
+ pointer-events: none;
99
+ }
100
+
101
+ /* 轻提示离视口边更近 */
102
+ .xh-scope-notification[data-part='group'][data-preset='toast']:not([data-stacked]) {
103
+ padding-block-start: max(var(--xh-notification-inset, var(--xh-space-4)), env(safe-area-inset-top));
104
+ padding-block-end: max(var(--xh-notification-inset, var(--xh-space-4)), env(safe-area-inset-bottom));
105
+ padding-inline: max(
106
+ var(--xh-notification-inset, var(--xh-space-4)),
107
+ env(safe-area-inset-left),
108
+ env(safe-area-inset-right)
109
+ );
110
+ }
111
+
112
+ .xh-scope-notification[data-part='group'][hidden] {
113
+ display: none;
114
+ }
115
+
116
+ /* 空摞直接撤掉:九个位同时挂九张全屏定位面,白白多九个合成层 */
117
+ .xh-scope-notification[data-part='group'][data-empty] {
118
+ display: none;
119
+ }
120
+
121
+ /* 块轴落位:^= 一次吃掉某一行的三个位(top / top-start / top-end) */
122
+ .xh-scope-notification[data-part='group'][data-placement^='top'] {
123
+ justify-content: flex-start;
124
+ }
125
+
126
+ .xh-scope-notification[data-part='group'][data-placement^='middle'] {
127
+ justify-content: center;
128
+ }
129
+
130
+ /* 行内轴落位:不带后缀的 top / middle / bottom 用基线的居中 */
131
+ .xh-scope-notification[data-part='group'][data-placement$='-start'] {
132
+ align-items: flex-start;
133
+ }
134
+
135
+ .xh-scope-notification[data-part='group'][data-placement$='-end'] {
136
+ align-items: flex-end;
137
+ }
138
+
139
+ /*
140
+ * 叠成一摞:定位面收成一条固定宽度、零高度的线,贴在落位那一角;卡片在里面绝对定位,
141
+ * 按叠摞测量写进来的层深与偏移错开。贴边改由 inset 表达,定位面不再整面铺开。
142
+ */
143
+ .xh-scope-notification[data-part='group'][data-stacked] {
144
+ inset: auto;
145
+ inline-size: min(var(--xh-notification-item-w, var(--xh-overlay-max-w-lg)), calc(100vw - var(--xh-space-8)));
146
+ block-size: 0;
147
+ padding: 0;
148
+ }
149
+
150
+ /* 定位面不再是弹性列:卡片各自绝对定位。收起与空摞照旧撤掉 */
151
+ .xh-scope-notification[data-part='group'][data-stacked]:not([hidden]):not([data-empty]) {
152
+ display: block;
153
+ }
154
+
155
+ .xh-scope-notification[data-part='group'][data-stacked][data-preset='toast'] {
156
+ inline-size: min(var(--xh-notification-item-w, var(--xh-overlay-toast-w)), calc(100vw - var(--xh-space-8)));
157
+ }
158
+
159
+ .xh-scope-notification[data-part='group'][data-stacked][data-placement^='bottom'] {
160
+ inset-block-end: max(var(--xh-notification-inset, var(--xh-space-6)), env(safe-area-inset-bottom));
161
+ }
162
+
163
+ .xh-scope-notification[data-part='group'][data-stacked][data-placement^='top'] {
164
+ inset-block-start: max(var(--xh-notification-inset, var(--xh-space-6)), env(safe-area-inset-top));
165
+ }
166
+
167
+ .xh-scope-notification[data-part='group'][data-stacked][data-preset='toast'][data-placement^='bottom'] {
168
+ inset-block-end: max(var(--xh-notification-inset, var(--xh-space-4)), env(safe-area-inset-bottom));
169
+ }
170
+
171
+ .xh-scope-notification[data-part='group'][data-stacked][data-preset='toast'][data-placement^='top'] {
172
+ inset-block-start: max(var(--xh-notification-inset, var(--xh-space-4)), env(safe-area-inset-top));
173
+ }
174
+
175
+ /* 中间那一行从视口中线往下叠 */
176
+ .xh-scope-notification[data-part='group'][data-stacked][data-placement^='middle'] {
177
+ inset-block-start: 50%;
178
+ }
179
+
180
+ /* 起点是逻辑属性、平移是物理通道:平移乘书写方向的符号,rtl 下居中的那一摞才落回视口正中 */
181
+ .xh-scope-notification[data-part='group'][data-stacked]:is([data-placement='top'], [data-placement='middle'], [data-placement='bottom']) {
182
+ inset-inline-start: 50%;
183
+ translate: calc(-50% * var(--xh-direction-sign)) 0;
184
+ }
185
+
186
+ .xh-scope-notification[data-part='group'][data-stacked][data-placement$='-start'] {
187
+ inset-inline-start: max(
188
+ var(--xh-notification-inset, var(--xh-space-6)),
189
+ env(safe-area-inset-left),
190
+ env(safe-area-inset-right)
191
+ );
192
+ }
193
+
194
+ .xh-scope-notification[data-part='group'][data-stacked][data-placement$='-end'] {
195
+ inset-inline-end: max(
196
+ var(--xh-notification-inset, var(--xh-space-6)),
197
+ env(safe-area-inset-left),
198
+ env(safe-area-inset-right)
199
+ );
200
+ }
201
+
202
+ .xh-scope-notification[data-part='group'][data-stacked][data-preset='toast'][data-placement$='-start'] {
203
+ inset-inline-start: max(
204
+ var(--xh-notification-inset, var(--xh-space-4)),
205
+ env(safe-area-inset-left),
206
+ env(safe-area-inset-right)
207
+ );
208
+ }
209
+
210
+ .xh-scope-notification[data-part='group'][data-stacked][data-preset='toast'][data-placement$='-end'] {
211
+ inset-inline-end: max(
212
+ var(--xh-notification-inset, var(--xh-space-4)),
213
+ env(safe-area-inset-left),
214
+ env(safe-area-inset-right)
215
+ );
216
+ }
217
+
218
+ /*
219
+ * 一张通知:底与描边一律中性,面走 sheet 三件套(--xh-material-elevated-bg / -border / -shadow,
220
+ * 亮暗两档同有 1px 描边)。两种预设共用这张面,排版分开写在下面:
221
+ * 卡片是两列网格,左列一枚类型字形、右列「标题 + 说明 + 操作」、叉钉在右上角;
222
+ * 轻提示是一行,行首字形、文本列、操作钮与叉依次排开。
223
+ *
224
+ * 卡片的槽带 item 段(--xh-notification-item-*),与摞的 --xh-notification-inset 分开。
225
+ */
226
+ .xh-scope-notification[data-part='item'] {
227
+ /* 作者塞进卡片里的图标与皮肤画的兜底字形同一把尺:Feedback 指示符统一 md 档;
228
+ 左列那枚类型字形另有自己的尺寸槽,叉与操作钮的字形各按按钮档取值 */
229
+ --xh-icon-size: var(--xh-notification-icon-size, var(--xh-glyph-size-md));
230
+
231
+ position: relative;
232
+ display: grid;
233
+ /* 左列宽度由字形自己决定,作者不放指示符时它就是 0,正文自然贴到左内衬 */
234
+ grid-template-columns: auto 1fr;
235
+ align-items: start;
236
+ column-gap: var(--xh-notification-item-gap, var(--xh-space-3));
237
+ row-gap: var(--xh-notification-item-row-gap, var(--xh-space-2));
238
+ inline-size: var(--xh-notification-item-w, var(--xh-overlay-max-w-lg));
239
+ /* 摞是整面视口宽的定位面且带内边距,取 100% 就等于自动让出两侧留白,
240
+ 窄屏上不必再算一遍 viewport 减法 */
241
+ max-inline-size: 100%;
242
+ padding-block: var(--xh-notification-item-py, var(--xh-surface-pad-lg));
243
+ padding-inline: var(--xh-notification-item-px, var(--xh-surface-pad-lg));
244
+ border: var(--xh-stroke-thin) solid var(--xh-notification-item-border, var(--xh-material-elevated-border));
245
+ border-radius: var(--xh-notification-item-radius, var(--xh-shape-overlay));
246
+ background: var(--xh-notification-item-bg, var(--xh-material-elevated-bg));
247
+ color: var(--xh-notification-item-fg, var(--xh-material-elevated-fg));
248
+ box-shadow: var(--xh-notification-item-shadow, var(--xh-material-elevated-shadow));
249
+ font-size: var(--xh-notification-item-font-size, var(--xh-text-body-size));
250
+ line-height: var(--xh-notification-item-leading, var(--xh-text-body-leading));
251
+ /* 摞的定位面整面铺开且不吃指针,卡片必须自己吃回来:
252
+ 悬停按住计时、按钮可点、焦点进出,全指着这一条 */
253
+ pointer-events: auto;
254
+ /* 换位:一张卡收起或新卡插进来时,列表动效先按旧位置反向补偿,再由这条过渡带回新位置 */
255
+ transition: translate var(--xh-motion-duration-move) var(--xh-motion-ease-continuous);
256
+ }
257
+
258
+ /* 轻提示:一行排开,行首那一格贴着内容盒的起始角,文本列与两颗钮在行里垂直居中。
259
+ 指示符盒自带 --xh-space-1 内衬:与间距合起来,字形到正文 12px,与卡片、Alert 同一个距离 */
260
+ .xh-scope-notification[data-part='item'][data-preset='toast'] {
261
+ flex: none;
262
+ display: flex;
263
+ align-items: flex-start;
264
+ column-gap: var(--xh-notification-item-gap, var(--xh-space-2));
265
+ inline-size: var(--xh-notification-item-w, var(--xh-overlay-toast-w));
266
+ padding-block: var(--xh-notification-item-py, var(--xh-space-3));
267
+ padding-inline: var(--xh-notification-item-px, var(--xh-space-4));
268
+ font-size: var(--xh-notification-item-font-size, var(--xh-text-label-size));
269
+ /* 叠摞几何常驻:单独摆放时是恒等位移与比例,放进叠放的一摞后由那边换成层深,
270
+ 两处用同一套画法,卡片进出一摞时栅格不跳 */
271
+ translate: 0 calc(var(--xh-_notification-stack-dir, 1) * var(--xh-_notification-y, 0px));
272
+ scale: var(--xh-_notification-scale, 1);
273
+ transform-origin: bottom center;
274
+ }
275
+
276
+ /* 卡片:除指示符外一律落第二列,作者往卡片里塞的任何东西都跟着正文走 */
277
+ .xh-scope-notification[data-part='item'][data-preset='card'] > *:not([data-part='item-indicator']) {
278
+ grid-column: 2;
279
+ }
280
+
281
+ /* 退场动画播完连接层给的是 hidden 属性,而上面声明了 display——
282
+ 漏掉这条,已经"卸载"的通知会一直杵在屏幕上,且再也没人来收 */
283
+ .xh-scope-notification[data-part='item'][hidden] {
284
+ display: none;
285
+ }
286
+
287
+ /* 生命周期只有三段,unmounted 那段由上面的 [hidden] 收走,这里只管前两段。
288
+ 逐条排开的那几摞共用同一条进出场,位移不跟着落位走:要按位置分方向,就得从 group 的
289
+ data-placement 反向选到卡片上,落位一改名整条动效静默失效。竖向一小段位移在九个位上都说得通。
290
+ 同一批新到的几条按到达顺序错开开幕,不按它在那一摞里排第几(序号由条目到达的追踪写进
291
+ --xh-_stagger-index,封顶)。backwards 不能省:等待期间起帧不生效的话,
292
+ 卡片会先以全不透明度露一下再跳回起帧 */
293
+ .xh-scope-notification[data-part='item'][data-state='visible'] {
294
+ animation: xh-sheet-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong) backwards;
295
+ animation-delay: calc(var(--xh-_stagger-index, 0) * var(--xh-motion-stagger-step));
296
+ }
297
+
298
+ /* forwards 不能省:动画播完到卡片真正收起之间还隔着一次提交,
299
+ 不定住末帧的话动画跑完会弹回全不透明,卡片在被收起前先闪一下 */
300
+ .xh-scope-notification[data-part='item'][data-state='dismissing'] {
301
+ animation: xh-sheet-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
302
+ }
303
+
304
+ /*
305
+ * 叠放的一摞:最新一条在最前,后层按层深逐层收拢——层深由叠摞测量写进来,每往后一层乘一次
306
+ * --xh-motion-scale-stack;减弱动效下那支令牌为 1,层与层只靠位移分开。
307
+ * 收拢比例作者可以用 --xh-notification-stack-scale 整摞改写。
308
+ */
309
+ .xh-scope-notification[data-part='group'][data-stacked] .xh-scope-notification[data-part='item'] {
310
+ --xh-_notification-stack-scale: max(0, 1 - var(--xh-_notification-depth, 0) * (1 - var(--xh-motion-scale-stack)));
311
+ --xh-_notification-y: var(--xh-_notification-offset, 0px);
312
+ --xh-_notification-scale: var(--xh-notification-stack-scale, var(--xh-_notification-stack-scale));
313
+
314
+ position: absolute;
315
+ inset-inline: 0;
316
+ inline-size: 100%;
317
+ translate: 0 calc(var(--xh-_notification-stack-dir, 1) * var(--xh-_notification-y));
318
+ scale: var(--xh-_notification-scale);
319
+ transform-origin: bottom center;
320
+ transition:
321
+ translate var(--xh-motion-duration-move) var(--xh-motion-ease-continuous),
322
+ scale var(--xh-motion-duration-move) var(--xh-motion-ease-continuous),
323
+ block-size var(--xh-motion-duration-move) var(--xh-motion-ease-continuous),
324
+ opacity var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
325
+ }
326
+
327
+ /* 贴底的几摞往上叠,贴顶与中间那一行往下叠 */
328
+ .xh-scope-notification[data-part='group'][data-stacked][data-placement^='bottom'] .xh-scope-notification[data-part='item'] {
329
+ --xh-_notification-stack-dir: -1;
330
+
331
+ inset-block-end: 0;
332
+ }
333
+
334
+ .xh-scope-notification[data-part='group'][data-stacked]:is([data-placement^='top'], [data-placement^='middle']) .xh-scope-notification[data-part='item'] {
335
+ --xh-_notification-stack-dir: 1;
336
+
337
+ inset-block-start: 0;
338
+ transform-origin: top center;
339
+ }
340
+
341
+ /* 叠放的卡片从视口边缘推入、沿叠放方向退出,进出场都带着自己那一层的位移与收拢比例 */
342
+ .xh-scope-notification[data-part='group'][data-stacked] .xh-scope-notification[data-part='item'][data-state='visible'] {
343
+ animation: xh-notification-stack-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong) backwards;
344
+ }
345
+
346
+ .xh-scope-notification[data-part='group'][data-stacked] .xh-scope-notification[data-part='item'][data-state='dismissing'] {
347
+ animation: xh-notification-stack-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
348
+ }
349
+
350
+ /* 收拢的后层只露出一截边:高度压成最前那一条,内容淡出,免得几层字叠在一起 */
351
+ .xh-scope-notification[data-part='group'][data-stacked] .xh-scope-notification[data-part='item'][data-stack-index]:not([data-frontmost]):not([data-expanded]) {
352
+ block-size: var(--xh-_notification-front-height, auto);
353
+ overflow: hidden;
354
+ }
355
+
356
+ .xh-scope-notification[data-part='group'][data-stacked] .xh-scope-notification[data-part='item'] > :not([data-part='item-close-trigger']) {
357
+ transition: opacity var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
358
+ }
359
+
360
+ .xh-scope-notification[data-part='group'][data-stacked] .xh-scope-notification[data-part='item'][data-stack-index]:not([data-frontmost]):not([data-expanded])
361
+ > :not([data-part='item-close-trigger']) {
362
+ opacity: 0;
363
+ }
364
+
365
+ /* 展开:按真实高度排成一列,收拢比例归 1 */
366
+ .xh-scope-notification[data-part='group'][data-stacked] .xh-scope-notification[data-part='item'][data-expanded] {
367
+ --xh-_notification-y: var(--xh-_notification-offset-expanded, 0px);
368
+ --xh-_notification-scale: 1;
369
+
370
+ block-size: var(--xh-_notification-height, auto);
371
+ }
372
+
373
+ /*
374
+ * 语气的非颜色通道。行首字形按 data-tone 区分信息、成功、警告与失败;加载中另由 data-loading 说。
375
+ * 行首那一格叠着两层:::before 是加载环(加载环家族配方画成与 Spinner 环档同一副环,随 data-loading
376
+ * 淡入淡出、转与停),::after 是语气字形,加载中淡出让位——加载落定时环淡出、字形淡入,同一格里交叉淡变。
377
+ * 两层的颜色都照语气走。轻提示没渲染 item-indicator 时由卡片自己的伪元素补上。
378
+ */
379
+ .xh-scope-notification[data-part='item'][data-preset='toast'] {
380
+ --xh-_loading-ring-fg: var(--xh-notification-indicator-fg, var(--xh-_tone-fg));
381
+ }
382
+
383
+ /* 环那一层占着行首那一格,字形那一层叠在它上面 */
384
+ .xh-scope-notification[data-part='item'][data-preset='toast']::before {
385
+ content: '';
386
+ flex: none;
387
+ display: inline-block;
388
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
389
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
390
+ vertical-align: var(--xh-glyph-baseline-shift);
391
+ }
392
+
393
+ /* 字形那一层按卡片的内衬落在同一格上:行首那一格贴着内容盒的起始角(一行按 flex-start 排列) */
394
+ .xh-scope-notification[data-part='item'][data-preset='toast']::after {
395
+ content: '';
396
+ position: absolute;
397
+ inset-block-start: var(--xh-notification-item-py, var(--xh-space-3));
398
+ inset-inline-start: var(--xh-notification-item-px, var(--xh-space-4));
399
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
400
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
401
+ background-color: var(--xh-notification-indicator-fg, var(--xh-_tone-fg));
402
+ -webkit-mask: var(--xh-glyph-mark-info) center / contain no-repeat;
403
+ mask: var(--xh-glyph-mark-info) center / contain no-repeat;
404
+ transition: opacity var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
405
+ }
406
+
407
+ .xh-scope-notification[data-part='item'][data-preset='toast'][data-tone='success']::after {
408
+ -webkit-mask-image: var(--xh-glyph-mark-check);
409
+ mask-image: var(--xh-glyph-mark-check);
410
+ }
411
+
412
+ .xh-scope-notification[data-part='item'][data-preset='toast'][data-tone='warning']::after {
413
+ -webkit-mask-image: var(--xh-glyph-mark-warning);
414
+ mask-image: var(--xh-glyph-mark-warning);
415
+ }
416
+
417
+ .xh-scope-notification[data-part='item'][data-preset='toast'][data-tone='danger']::after {
418
+ -webkit-mask-image: var(--xh-glyph-mark-close);
419
+ mask-image: var(--xh-glyph-mark-close);
420
+ }
421
+
422
+ /* 加载中不是语气:字形淡出让位给环,颜色仍照语气走 */
423
+ .xh-scope-notification[data-part='item'][data-preset='toast'][data-loading]::after {
424
+ opacity: 0;
425
+ }
426
+
427
+ /* 作者渲染了指示符部件,行首那两层兜底就让位给它,一行里不出现两枚图形 */
428
+ .xh-scope-notification[data-part='item'][data-preset='toast']:has([data-part='item-indicator'])::before,
429
+ .xh-scope-notification[data-part='item'][data-preset='toast']:has([data-part='item-indicator'])::after {
430
+ content: none;
431
+ }
432
+
433
+ /* 卡片:独占第一列第一行,并与标题首行顶对齐:盒高取一行标题的行高,
434
+ 字形在盒里居中,标题字号改了也不会错位 */
435
+ .xh-scope-notification[data-part='item-indicator'] {
436
+ --xh-icon-size: var(--xh-notification-indicator-size, var(--xh-glyph-size-md));
437
+
438
+ grid-column: 1;
439
+ grid-row: 1;
440
+ position: relative;
441
+ display: inline-flex;
442
+ align-items: center;
443
+ justify-content: center;
444
+ block-size: calc(var(--xh-notification-title-font-size, var(--xh-text-label-size)) * var(--xh-leading-tight));
445
+ inline-size: var(--xh-notification-indicator-size, var(--xh-glyph-size-md));
446
+ color: var(--xh-notification-indicator-fg, var(--xh-_tone-fg));
447
+ }
448
+
449
+ /* 轻提示:方盒自带一圈内衬,与间距合起来让出字形到正文的那段距离 */
450
+ .xh-scope-notification[data-part='item'][data-preset='toast'] [data-part='item-indicator']:where(.xh-scope-notification) {
451
+ flex: none;
452
+ box-sizing: content-box;
453
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
454
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
455
+ padding: var(--xh-notification-indicator-p, var(--xh-space-1));
456
+ }
457
+
458
+ .xh-scope-notification[data-part='item-indicator'][hidden] {
459
+ display: none;
460
+ }
461
+
462
+ /* 作者没往指示符里写内容就由皮肤画兜底的环与字形,塞了自己的图形这几条即不命中。
463
+ 两层叠在同一格:::before 是加载环(加载环配方画成与 Spinner 环档同一副环,随 data-loading 淡入淡出、
464
+ 转与停,起始边取指示符的语气色),::after 是按语气各画一枚的字形,加载中淡出让位——
465
+ 加载落定时环淡出、字形淡入,同一格里交叉淡变。「在等什么」由卡片上的 role=status 与可见文案承担 */
466
+ .xh-scope-notification[data-part='item-indicator']:empty::before {
467
+ content: '';
468
+ display: inline-block;
469
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
470
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
471
+ vertical-align: var(--xh-glyph-baseline-shift);
472
+ }
473
+
474
+ .xh-scope-notification[data-part='item-indicator']:empty::after {
475
+ content: '';
476
+ position: absolute;
477
+ inset: 0;
478
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
479
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
480
+ margin: auto;
481
+ background-color: currentColor;
482
+ -webkit-mask: var(--xh-glyph-mark-info) center / contain no-repeat;
483
+ mask: var(--xh-glyph-mark-info) center / contain no-repeat;
484
+ transition: opacity var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
485
+ }
486
+
487
+ .xh-scope-notification[data-part='item'][data-tone='success'] [data-part='item-indicator']:where(.xh-scope-notification):empty::after {
488
+ -webkit-mask-image: var(--xh-glyph-mark-check);
489
+ mask-image: var(--xh-glyph-mark-check);
490
+ }
491
+
492
+ .xh-scope-notification[data-part='item'][data-tone='warning'] [data-part='item-indicator']:where(.xh-scope-notification):empty::after {
493
+ -webkit-mask-image: var(--xh-glyph-mark-warning);
494
+ mask-image: var(--xh-glyph-mark-warning);
495
+ }
496
+
497
+ .xh-scope-notification[data-part='item'][data-tone='danger'] [data-part='item-indicator']:where(.xh-scope-notification):empty::after {
498
+ -webkit-mask-image: var(--xh-glyph-mark-close);
499
+ mask-image: var(--xh-glyph-mark-close);
500
+ }
501
+
502
+ /* 加载中不是语气:字形淡出让位给环,颜色仍照语气走 */
503
+ .xh-scope-notification[data-part='item-indicator'][data-loading]:empty::after {
504
+ opacity: 0;
505
+ }
506
+
507
+ /* 标题与说明的文本列。卡片里它落在第二列,两行之间与网格同一个行距;
508
+ 轻提示里它占满行首字形与两颗钮之间的那一段,在行里垂直居中 */
509
+ .xh-scope-notification[data-part='item-content'] {
510
+ display: flex;
511
+ flex-direction: column;
512
+ row-gap: var(--xh-notification-item-row-gap, var(--xh-space-2));
513
+ min-inline-size: 0;
514
+ }
515
+
516
+ .xh-scope-notification[data-part='item'][data-preset='toast'] [data-part='item-content']:where(.xh-scope-notification) {
517
+ flex: 1 1 auto;
518
+ align-self: center;
519
+ row-gap: 0;
520
+ }
521
+
522
+ .xh-scope-notification[data-part='item-content'][hidden] {
523
+ display: none;
524
+ }
525
+
526
+ .xh-scope-notification[data-part='item-title'] {
527
+ /* 作者可能把它写成 h2/p,外边距得先归零,否则卡片内的行距对不上 */
528
+ margin: 0;
529
+ color: var(--xh-notification-title-fg, var(--xh-fg-default));
530
+ font-size: var(--xh-notification-title-font-size, var(--xh-text-label-size));
531
+ font-weight: var(--xh-notification-title-font-weight, var(--xh-font-weight-semibold));
532
+ line-height: var(--xh-notification-title-leading, var(--xh-leading-tight));
533
+ }
534
+
535
+ /* 轻提示的语气落到标题与行首字形:一句话的结果,颜色本身就是信息 */
536
+ .xh-scope-notification[data-part='item'][data-preset='toast'] [data-part='item-title']:where(.xh-scope-notification) {
537
+ min-inline-size: 0;
538
+ color: var(--xh-notification-title-fg, var(--xh-_tone-fg));
539
+ }
540
+
541
+ /* 卡片的叉钉在右上角,标题得给它让位;说明与操作在第二行往下,不受影响。
542
+ 让位量与叉的贴边取同一个值,作者改了贴边槽,标题让出的那段跟着走 */
543
+ .xh-scope-notification[data-part='item'][data-preset='card']:has([data-part='item-close-trigger']) [data-part='item-title']:where(.xh-scope-notification) {
544
+ padding-inline-end: calc(
545
+ var(--xh-notification-close-size, var(--xh-control-h-sm)) +
546
+ var(--xh-notification-close-inset, var(--xh-surface-action-inset))
547
+ );
548
+ }
549
+
550
+ /* 正文有上限:推一段长文(公告全文)进来时卡片不被撑到整屏高——摞是整面视口的定位面、不吃指针也不裁切,
551
+ 撑出视口的那截既看不到也滚不到。超出的部分在正文自己里面滚,标题与操作钮留在卡片上;
552
+ 滚到头不把滚动量传给页面 */
553
+ .xh-scope-notification[data-part='item-description'] {
554
+ margin: 0;
555
+ max-block-size: var(--xh-notification-description-max-h, var(--xh-viewport-h-md));
556
+ overflow-y: auto;
557
+ overscroll-behavior: contain;
558
+ color: var(--xh-notification-description-fg, var(--xh-fg-muted));
559
+ font-size: var(--xh-notification-description-font-size, var(--xh-text-secondary-size));
560
+ }
561
+
562
+ /* 轻提示的说明只是一行短上下文,行高收紧到正文之下 */
563
+ .xh-scope-notification[data-part='item'][data-preset='toast'] [data-part='item-description']:where(.xh-scope-notification) {
564
+ min-inline-size: 0;
565
+ line-height: var(--xh-notification-description-leading, var(--xh-leading-normal));
566
+ }
567
+
568
+ /* 没给文案的那一个仍然要留在 DOM 里(aria-labelledby / aria-describedby 指着它),
569
+ 但空盒子会照吃一份 gap,卡片看着就多出一截莫名的留白 */
570
+ .xh-scope-notification[data-part='item-title']:empty,
571
+ .xh-scope-notification[data-part='item-description']:empty {
572
+ display: none;
573
+ }
574
+
575
+ .xh-scope-notification[data-part='item-title'][hidden],
576
+ .xh-scope-notification[data-part='item-description'][hidden] {
577
+ display: none;
578
+ }
579
+
580
+ /*
581
+ * 倒计时条。总时长由连接层写进 --xh-notification-progress-duration(不自动消失的那些
582
+ * 整条收起),计时被按住时动画一并停住——停表这件事此前没有任何可画的地方。
583
+ * 取装饰档 --xh-_tone-soft:压在卡片底色上仍看得出来。
584
+ * 裁切只认物理方向:收起的那一侧取书写方向的行尾,rtl 下从左往右收。
585
+ */
586
+ .xh-scope-notification[data-part='item-progress'] {
587
+ --xh-_countdown-cut-left: var(--xh-direction-start);
588
+ --xh-_countdown-cut-right: var(--xh-direction-end);
589
+
590
+ overflow: hidden;
591
+ inline-size: 100%;
592
+ block-size: var(--xh-notification-progress-thickness, var(--xh-space-0_5));
593
+ background: var(--xh-notification-progress-bg, var(--xh-_tone-soft));
594
+ animation: xh-countdown var(--xh-notification-progress-duration, var(--xh-motion-duration-slide)) var(--xh-motion-ease-loop) forwards;
595
+ }
596
+
597
+ /* 卡片:横跨整张卡片的一行,它不是正文的一部分,不跟着第二列走;两端取胶囊 */
598
+ .xh-scope-notification[data-part='item'][data-preset='card'] [data-part='item-progress']:where(.xh-scope-notification) {
599
+ --xh-_countdown-radius: var(--xh-notification-progress-radius, var(--xh-shape-pill));
600
+
601
+ grid-column: 1 / -1;
602
+ border-radius: var(--xh-_countdown-radius);
603
+ }
604
+
605
+ /* 轻提示:贴着卡片底边走 */
606
+ .xh-scope-notification[data-part='item'][data-preset='toast'] [data-part='item-progress']:where(.xh-scope-notification) {
607
+ position: absolute;
608
+ inset-block-end: 0;
609
+ inset-inline-start: 0;
610
+ }
611
+
612
+ /* 减弱动效:倒计时不连续地缩,按秒一格一格走,段数由连接层随时长交出。
613
+ 两条规则是同一件事的两个触发条件:系统偏好与作者打的 data-motion */
614
+ @media (prefers-reduced-motion: reduce) {
615
+ .xh-scope-notification[data-part='item-progress'] {
616
+ animation-timing-function: steps(var(--xh-_notification-progress-steps, 1), end);
617
+ }
618
+ }
619
+
620
+ :where([data-motion='reduce']) .xh-scope-notification[data-part='item-progress'] {
621
+ animation-timing-function: steps(var(--xh-_notification-progress-steps, 1), end);
622
+ }
623
+
624
+ .xh-scope-notification[data-part='item'][data-paused] [data-part='item-progress']:where(.xh-scope-notification) {
625
+ animation-play-state: paused;
626
+ }
627
+
628
+ .xh-scope-notification[data-part='item-progress'][hidden] {
629
+ display: none;
630
+ }
631
+
632
+ /* 操作钮是带文案的离散动作钮:盒几何、悬停 / 按下与 0.97 按压、粗指针热区、焦点环、禁用面全由 Action Control
633
+ 的 text outline 档给(连接层投 sm;非 Button 的触发器缺省中性描边、透明底),组件只映射既有使用者槽。
634
+ 语气只落到字形与轻提示的标题:这颗钮坐在带 data-tone 的卡片里,底 / 边 / 字钉在中性语义面上,不随语气;
635
+ 卡片是 sheet 白面承载:hover --xh-bg-subtle(100)→ pressed --xh-bg-subtle-hover(200) */
636
+ .xh-scope-notification[data-part='item-action-trigger'] {
637
+ --xh-action-visual-size: var(--xh-notification-action-h, var(--xh-_action-profile-visual-size));
638
+ --xh-action-padding-inline: var(--xh-notification-action-px, var(--xh-_action-profile-padding-inline));
639
+ --xh-action-radius: var(--xh-notification-action-radius, var(--xh-shape-control));
640
+ --xh-action-bg-rest: var(--xh-notification-action-bg, transparent);
641
+ --xh-action-bg-hover: var(--xh-notification-action-bg-hover, var(--xh-bg-subtle));
642
+ --xh-action-bg-pressed: var(--xh-notification-action-bg-active, var(--xh-bg-subtle-hover));
643
+ --xh-action-fg-rest: var(--xh-notification-action-fg, var(--xh-fg-default));
644
+ --xh-action-fg-hover: var(--xh-notification-action-fg, var(--xh-fg-default));
645
+ --xh-action-fg-pressed: var(--xh-notification-action-fg, var(--xh-fg-default));
646
+ --xh-action-border-rest: var(--xh-notification-action-border, var(--xh-border-control));
647
+ --xh-action-border-hover: var(--xh-notification-action-border, var(--xh-border-control-hover));
648
+ --xh-action-border-pressed: var(--xh-notification-action-border, var(--xh-border-control-hover));
649
+ --xh-action-highlight-rest: transparent;
650
+ --xh-action-shadow-rest: none;
651
+ --xh-action-shadow-hover: none;
652
+ --xh-action-shadow-pressed: none;
653
+ /* 作者塞进钮里的图标按按钮档取字形尺寸 */
654
+ --xh-icon-size: var(--xh-notification-icon-size, var(--xh-_action-profile-glyph-size));
655
+
656
+ /* 网格列默认拉满,按钮要贴着起始侧收成自己的宽度 */
657
+ justify-self: start;
658
+ font-weight: var(--xh-notification-action-font-weight, var(--xh-font-weight-medium));
659
+ }
660
+
661
+ /* 轻提示是单行 flex,按钮不参与伸缩:文案长了该换行的是文字 */
662
+ .xh-scope-notification[data-part='item'][data-preset='toast'] [data-part='item-action-trigger']:where(.xh-scope-notification) {
663
+ flex: none;
664
+ align-self: center;
665
+ }
666
+
667
+ /* 家族那条 display 会把 UA 的 [hidden]{display:none} 盖掉 */
668
+ .xh-scope-notification[data-part='item-action-trigger'][hidden] {
669
+ display: none;
670
+ }
671
+
672
+ /* 关闭钮是只有字形的离散动作钮:盒几何、悬停 / 按下与 0.97 按压、粗指针热区、焦点环、禁用面全由 Action Control
673
+ 的 icon ghost 档给(卡片投 sm:32px 正方盒;轻提示投 xs:24px,走行级动作钮那一档),组件只映射既有使用者槽。
674
+ 静息透明,sheet 白面承载 hover 100 → pressed 200,写了语气时悬停底跟着语气走(--xh-_tone-subtle) */
675
+ .xh-scope-notification[data-part='item-close-trigger'] {
676
+ --xh-action-visual-size: var(--xh-notification-close-size, var(--xh-_action-profile-visual-size));
677
+ --xh-action-min-inline-size: var(--xh-notification-close-size, var(--xh-_action-profile-visual-size));
678
+ --xh-action-radius: var(--xh-notification-close-radius, var(--xh-shape-control));
679
+ --xh-action-bg-rest: var(--xh-notification-close-bg, var(--xh-_action-variant-bg-rest));
680
+ --xh-action-bg-hover: var(--xh-notification-close-bg-hover, var(--xh-_action-variant-bg-hover));
681
+ --xh-action-bg-pressed: var(--xh-notification-close-bg-active, var(--xh-_action-variant-bg-pressed));
682
+ --xh-action-fg-rest: var(--xh-notification-close-fg, var(--xh-fg-muted));
683
+ --xh-action-fg-hover: var(--xh-notification-close-fg-hover, var(--xh-fg-default));
684
+ --xh-action-fg-pressed: var(--xh-notification-close-fg-hover, var(--xh-fg-default));
685
+ --xh-action-border-rest: var(--xh-notification-close-border, var(--xh-_action-variant-border-rest));
686
+ --xh-action-highlight-rest: transparent;
687
+ --xh-action-shadow-rest: none;
688
+ --xh-action-shadow-hover: none;
689
+ --xh-action-shadow-pressed: none;
690
+ /* 叉的字形按钮档取值,与行首那枚 md 档的类型字形分开 */
691
+ --xh-icon-size: var(--xh-notification-icon-size, var(--xh-_action-profile-glyph-size));
692
+ }
693
+
694
+ /* 卡片:钉在右上角的内衬处,不参与网格排布——留在流里它会变成正文下面的第三行 */
695
+ .xh-scope-notification[data-part='item'][data-preset='card'] [data-part='item-close-trigger']:where(.xh-scope-notification) {
696
+ position: absolute;
697
+ inset-block-start: var(--xh-notification-close-inset, var(--xh-surface-action-inset));
698
+ inset-inline-end: var(--xh-notification-close-inset, var(--xh-surface-action-inset));
699
+ }
700
+
701
+ /* 轻提示:排在一行短消息的行尾,与文本列一起垂直居中 */
702
+ .xh-scope-notification[data-part='item'][data-preset='toast'] [data-part='item-close-trigger']:where(.xh-scope-notification) {
703
+ flex: none;
704
+ align-self: center;
705
+ }
706
+
707
+ /* 轻提示的叉在有悬停能力的设备上默认静默、指针进入卡片或焦点落进卡片后显现——不走家族的
708
+ hover-focus 显示策略:那一档用 visibility 收起,这颗叉占 Tab 位,收成 visibility: hidden
709
+ 后键盘就够不到它了;这里只压 opacity 与 pointer-events,焦点仍能落进来 */
710
+ @media (hover: hover) {
711
+ .xh-scope-notification[data-part='item'][data-preset='toast'] [data-part='item-close-trigger']:where(.xh-scope-notification) {
712
+ opacity: 0;
713
+ pointer-events: none;
714
+ }
715
+
716
+ .xh-scope-notification[data-part='item'][data-preset='toast']:hover [data-part='item-close-trigger']:where(.xh-scope-notification),
717
+ .xh-scope-notification[data-part='item'][data-preset='toast']:focus-within [data-part='item-close-trigger']:where(.xh-scope-notification) {
718
+ opacity: 1;
719
+ pointer-events: auto;
720
+ }
721
+ }
722
+
723
+ /* 收拢的后层只允许最前一条露出关闭入口;触屏没有 hover,必须显式收起后层的叉 */
724
+ .xh-scope-notification[data-part='group'][data-stacked] .xh-scope-notification[data-part='item'][data-stack-index]:not([data-frontmost]):not([data-expanded])
725
+ [data-part='item-close-trigger']:where(.xh-scope-notification) {
726
+ opacity: 0;
727
+ pointer-events: none;
728
+ }
729
+
730
+ /* closable=false 时连接层同时给了 hidden 与 disabled,这条是它真正收起的那一半:
731
+ 家族那条 display 会把 UA 的 [hidden]{display:none} 盖掉,不补回来就会留下一个按不动的叉 */
732
+ .xh-scope-notification[data-part='item-close-trigger'][hidden] {
733
+ display: none;
734
+ }
735
+
736
+ /* 没写内容就由皮肤画兜底字形;作者往部件里塞了自己的图形,这几条即不命中 */
737
+ .xh-scope-notification[data-part='item-close-trigger']:empty::before {
738
+ content: '';
739
+ display: inline-block;
740
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
741
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
742
+ vertical-align: var(--xh-glyph-baseline-shift);
743
+ background-color: currentColor;
744
+ -webkit-mask: var(--xh-glyph-mark-close) center / contain no-repeat;
745
+ mask: var(--xh-glyph-mark-close) center / contain no-repeat;
746
+ }
747
+
748
+ @media print {
749
+ /* 通知组固定在视口上,打印那一刻在场就会盖住正文,且它已经错过了被读到的时机。 */
750
+ .xh-scope-notification[data-part='group'] {
751
+ display: none;
752
+ }
753
+
754
+ /* 作者把卡片摆在摞外的用法:加载环的静止替代由加载环配方在打印档给出 */
755
+ }
756
+
757
+ @media (forced-colors: active) {
758
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
759
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
760
+ 按钮面上取 ButtonText,悬停与按下时家族把按钮涂成 Highlight、字形换 HighlightText;
761
+ 落在 Canvas 面上的取 CanvasText;
762
+ 禁用取 GrayText */
763
+ .xh-scope-notification[data-part='item'][data-preset='toast']::after,
764
+ .xh-scope-notification[data-part='item-indicator']:empty::after {
765
+ forced-color-adjust: none;
766
+ background-color: CanvasText;
767
+ }
768
+
769
+ .xh-scope-notification[data-part='item-close-trigger']:empty::before {
770
+ forced-color-adjust: none;
771
+ background-color: ButtonText;
772
+ }
773
+
774
+ .xh-scope-notification[data-part='item-close-trigger']:not([data-disabled]):not([data-loading]):is(:hover, :active, [data-pressed]):empty::before {
775
+ background-color: HighlightText;
776
+ }
777
+
778
+ .xh-scope-notification[data-part='item-close-trigger'][data-disabled]:empty::before {
779
+ background-color: GrayText;
780
+ }
781
+ }
782
+ }