@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,538 @@
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
+ /* 定义 image viewer 样式。 */
7
+
8
+ @import '../family/action-control.css';
9
+ @import '../family/loading-ring.css';
10
+
11
+ /* 共享关键帧由 family/motion.css 提供,本皮肤 @import 它,单独引入仍成立。 */
12
+ @import '../family/motion.css';
13
+
14
+ @layer xihan.components {
15
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
16
+ .xh-scope-image-viewer {
17
+ --xh-image-viewer-skin: 1;
18
+ }
19
+
20
+ .xh-scope-image-viewer[data-part='backdrop'] {
21
+ position: fixed;
22
+ inset: 0;
23
+ z-index: var(--xh-image-viewer-backdrop-layer, var(--xh-_layer, var(--xh-layer-modal)));
24
+ /* 看片要压住页面底色,遮罩比普通对话框更深 */
25
+ background: var(--xh-image-viewer-backdrop-bg, color-mix(in oklab, var(--xh-color-neutral-950) 78%, transparent));
26
+ }
27
+
28
+ /* 同一层底色之上再糊背后的页面 */
29
+ .xh-scope-image-viewer[data-part='backdrop'][data-variant='blur'] {
30
+ -webkit-backdrop-filter: blur(var(--xh-image-viewer-backdrop-blur, var(--xh-overlay-backdrop-blur)));
31
+ backdrop-filter: blur(var(--xh-image-viewer-backdrop-blur, var(--xh-overlay-backdrop-blur)));
32
+ }
33
+
34
+ /* 去掉底色,只留下这一层吃指针:交互外关闭与滚动锁定照旧 */
35
+ .xh-scope-image-viewer[data-part='backdrop'][data-variant='transparent'] {
36
+ background: transparent;
37
+ }
38
+
39
+ .xh-scope-image-viewer[data-part='backdrop'][data-state='open']:not([data-instant]) {
40
+ animation: xh-fade-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
41
+ }
42
+
43
+ .xh-scope-image-viewer[data-part='backdrop'][data-state='closed'] {
44
+ animation: xh-fade-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
45
+ }
46
+
47
+ .xh-scope-image-viewer[data-part='positioner'] {
48
+ position: fixed;
49
+ inset: 0;
50
+ z-index: var(--xh-image-viewer-layer, var(--xh-_layer, var(--xh-layer-modal)));
51
+ display: flex;
52
+ align-items: center;
53
+ justify-content: center;
54
+ }
55
+
56
+ .xh-scope-image-viewer[data-part='content'] {
57
+ /* 组件内那几档层号只在自己人之间排序:建一个层叠上下文把它们关起来,
58
+ 既不让页面上的层号插进来,也不让它们漏出去压住别人 */
59
+ isolation: isolate;
60
+
61
+ /* 作者塞进浮层的图标:内容层给一档 md 的尺,十颗钮各自再按 Action Control 档位覆盖 */
62
+ --xh-icon-size: var(--xh-image-viewer-icon-size, var(--xh-glyph-size-md));
63
+ /* 十颗钮共用的深色 chrome:压在使用者的图片上的半透明深底,是这一层自己的面,不取页面语义面 */
64
+ --xh-_image-viewer-chrome-bg: var(--xh-image-viewer-chrome-bg, color-mix(in oklab, var(--xh-color-neutral-950) 55%, transparent));
65
+ --xh-_image-viewer-chrome-bg-hover: var(--xh-image-viewer-action-bg-hover, color-mix(in oklab, var(--xh-color-neutral-950) 70%, transparent));
66
+ --xh-_image-viewer-chrome-bg-active: var(--xh-image-viewer-action-bg-active, color-mix(in oklab, var(--xh-color-neutral-950) 80%, transparent));
67
+
68
+ position: relative;
69
+ inline-size: 100%;
70
+ block-size: 100%;
71
+ display: flex;
72
+ align-items: center;
73
+ justify-content: center;
74
+ outline: none;
75
+ /* 深色底上的工具条与计数固定用浅字,不随主题翻 */
76
+ color: var(--xh-image-viewer-fg, var(--xh-color-neutral-0));
77
+ }
78
+
79
+ /* 挂载时就开着的那一段(data-instant)属于首帧,直接呈现 */
80
+ .xh-scope-image-viewer[data-part='content'][data-state='open']:not([data-instant]) {
81
+ animation: xh-fade-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
82
+ }
83
+
84
+ .xh-scope-image-viewer[data-part='content'][data-state='closed'] {
85
+ animation: xh-fade-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
86
+ }
87
+
88
+ .xh-scope-image-viewer[data-part='viewport'] {
89
+ position: absolute;
90
+ inset: 0;
91
+ display: flex;
92
+ align-items: center;
93
+ justify-content: center;
94
+ overflow: hidden;
95
+ cursor: grab;
96
+ /* 平移与滚轮都归指针会话,浏览器手势不抢 */
97
+ touch-action: none;
98
+ }
99
+
100
+ .xh-scope-image-viewer[data-part='viewport'][data-dragging] {
101
+ cursor: grabbing;
102
+ }
103
+
104
+ /* 原图还在路上:指针先报出来,此刻拖也没有东西可拖 */
105
+ .xh-scope-image-viewer[data-part='viewport'][data-loading] {
106
+ cursor: progress;
107
+ }
108
+
109
+ /* 取图期间画面正中一枚加载环:不给的话打开一张几 MB 的原图,台前是一整块什么都没有的暗底,
110
+ 用户分不清是在加载还是这张图坏了。环由加载环配方画(连接层投影 data-xh-loading-ring),
111
+ 随 data-loading 淡入淡出;前景随看片层的白墨域 */
112
+ .xh-scope-image-viewer[data-part='viewport'] {
113
+ /* 画面正中那两枚状态图形(加载环与警示字形)的尺:看片台是整幅画面,取控件图标档的最大一档 */
114
+ --xh-icon-size: var(--xh-image-viewer-status-size, var(--xh-glyph-size-lg));
115
+ }
116
+
117
+ .xh-scope-image-viewer[data-part='viewport']::before {
118
+ content: '';
119
+ position: absolute;
120
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-md));
121
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-md));
122
+ }
123
+
124
+ /* 取图失败:画面正中一枚警示字形(取危险色,非颜色通道),替掉浏览器画的破图与替代文字 */
125
+ .xh-scope-image-viewer[data-part='viewport'][data-error]::after {
126
+ content: '';
127
+ position: absolute;
128
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-md));
129
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-md));
130
+ background-color: var(--xh-image-viewer-error-fg, var(--xh-fg-danger));
131
+ -webkit-mask: var(--xh-glyph-mark-warning) center / contain no-repeat;
132
+ mask: var(--xh-glyph-mark-warning) center / contain no-repeat;
133
+ }
134
+
135
+ .xh-scope-image-viewer[data-part='image'] {
136
+ max-inline-size: 90%;
137
+ max-block-size: 85%;
138
+ user-select: none;
139
+ /* 位移、旋转与缩放都是内联 style 写的,只给按钮带来的缩放旋转一点缓动;拖拽平移要跟手,不缓。
140
+ 新图取到之后淡入 */
141
+ transition:
142
+ translate var(--xh-motion-duration-nudge) var(--xh-motion-ease-continuous),
143
+ rotate var(--xh-motion-duration-nudge) var(--xh-motion-ease-continuous),
144
+ scale var(--xh-motion-duration-nudge) var(--xh-motion-ease-continuous),
145
+ opacity var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
146
+ }
147
+
148
+ /* 取图期间(打开与换下标):换图那一刻变换直接归位、不补间——转过、放大过的旧图不当着用户转回、缩回;
149
+ 旧图随即让位,视口垫着的占位面顶上,不露底色闪一拍。取到之后撤掉这一档,按上面的进场档淡入 */
150
+ .xh-scope-image-viewer[data-part='image'][data-loading] {
151
+ opacity: 0;
152
+ transition: none;
153
+ }
154
+
155
+ /* 载不出来的图不露浏览器的破图:整张让位给视口正中的警示字形,替代文字仍在可及树里 */
156
+ .xh-scope-image-viewer[data-part='image'][data-error] {
157
+ visibility: hidden;
158
+ }
159
+
160
+ /* 拖拽期间平移每帧都在改:只在这一档备好合成层,松手后随 data-dragging 一起撤掉 */
161
+ .xh-scope-image-viewer[data-part='image'][data-dragging] {
162
+ transition: none;
163
+ will-change: translate;
164
+ }
165
+
166
+ /* 松手后弹簧带着平移惯性滑行、越出范围时回弹,同样逐帧写变换,不能再叠一层过渡;落定即撤 */
167
+ .xh-scope-image-viewer[data-part='image'][data-animating] {
168
+ transition: none;
169
+ will-change: translate;
170
+ }
171
+
172
+ /* —— 悬浮件共用:半透明深底加浅字 ——
173
+ 四件都钉在铺满视口的那一层的边上,贴边一律与安全区取大的一头:刘海、圆角与
174
+ 底部横条压住的那一段由系统给出,贴边比它窄时钮会被切掉一角;桌面上 env() 恒为 0。
175
+ 安全区只有物理方向的四个名字,行内轴上两侧同取较宽的那一段,RTL 下不会翻错边。
176
+ 工具条是容器、计数是一行字的气泡,各按身份取圆角(surface / control),
177
+ --xh-image-viewer-overlay-radius 仍是两者共用的使用者入口;翻页钮的面走下面的桥接槽 */
178
+ .xh-scope-image-viewer[data-part='toolbar'],
179
+ .xh-scope-image-viewer[data-part='counter'] {
180
+ position: absolute;
181
+ z-index: 1;
182
+ background: var(--xh-_image-viewer-chrome-bg);
183
+ color: inherit;
184
+ border: none;
185
+ }
186
+
187
+ .xh-scope-image-viewer[data-part='toolbar'] {
188
+ inset-block-end: max(var(--xh-space-6), env(safe-area-inset-bottom));
189
+ inset-inline-start: 50%;
190
+ translate: -50% 0;
191
+ display: flex;
192
+ align-items: center;
193
+ gap: var(--xh-image-viewer-toolbar-gap, var(--xh-space-1));
194
+ padding: var(--xh-image-viewer-toolbar-padding, var(--xh-space-1_5) var(--xh-space-3));
195
+ border-radius: var(--xh-image-viewer-toolbar-radius-outer, var(--xh-image-viewer-overlay-radius, var(--xh-shape-surface)));
196
+ }
197
+
198
+ /* 起点是逻辑属性、平移是物理通道:rtl 下行内分量掉头,工具条与计数才落回中线 */
199
+ .xh-scope-image-viewer[data-part='toolbar']:dir(rtl) {
200
+ translate: 50% 0;
201
+ }
202
+
203
+ /* 工具条里的七颗钮:接 Action Control icon 档 xs(24px 视觉盒),底色交给工具条,本体只管命中区与按压。
204
+ 悬停 / 按下的底从 chrome 那层深底派生,不取页面的 --xh-bg-subtle-* 阶梯。
205
+ position 交给配方的 relative:粗指针下 44px 命中区 ::after 以钮自身为包含块;
206
+ 这里若写 static,七块命中区会一起以工具条为包含块、彼此盖住,触屏上只剩最后一颗可点 */
207
+ .xh-scope-image-viewer[data-part='toolbar'] > [data-part] {
208
+ --xh-action-bg-rest: transparent;
209
+ --xh-action-bg-hover: var(--xh-_image-viewer-chrome-bg-hover);
210
+ --xh-action-bg-pressed: var(--xh-_image-viewer-chrome-bg-active);
211
+ --xh-action-bg-focus-visible: transparent;
212
+ --xh-action-bg-disabled: transparent;
213
+ --xh-action-fg-rest: currentColor;
214
+ --xh-action-fg-hover: currentColor;
215
+ --xh-action-fg-pressed: currentColor;
216
+ --xh-action-fg-focus-visible: currentColor;
217
+ --xh-action-fg-disabled: var(--xh-fg-disabled);
218
+ --xh-action-border-rest: transparent;
219
+ --xh-action-border-hover: transparent;
220
+ --xh-action-border-pressed: transparent;
221
+ --xh-action-border-focus-visible: transparent;
222
+ --xh-action-border-disabled: transparent;
223
+ --xh-action-radius: var(--xh-image-viewer-toolbar-radius, var(--xh-_action-profile-radius));
224
+ --xh-icon-size: var(--xh-image-viewer-icon-size, var(--xh-_action-profile-glyph-size));
225
+ }
226
+
227
+ .xh-scope-image-viewer[data-part='counter'] {
228
+ inset-block-start: max(var(--xh-space-6), env(safe-area-inset-top));
229
+ inset-inline-start: 50%;
230
+ translate: -50% 0;
231
+ padding: var(--xh-image-viewer-counter-padding, var(--xh-space-1) var(--xh-space-3));
232
+ border-radius: var(--xh-image-viewer-counter-radius, var(--xh-image-viewer-overlay-radius, var(--xh-shape-control)));
233
+ font-size: var(--xh-text-secondary-size);
234
+ }
235
+
236
+ .xh-scope-image-viewer[data-part='counter']:dir(rtl) {
237
+ translate: 50% 0;
238
+ }
239
+
240
+ /* 两颗翻页钮:浮在图上的单图标圆钮,接 Action Control floating 档 md(48px,正方盒取 circle)。
241
+ 面桥接到自家深色 chrome:跟的是图上那层半透明深底不是页面底色,所以不取 frosted 缺省面,
242
+ 也不取语义档的 --xh-bg-subtle-*;到头时是原生 disabled,压不动的东西不给回应 */
243
+ .xh-scope-image-viewer[data-part='prev-trigger'],
244
+ .xh-scope-image-viewer[data-part='next-trigger'] {
245
+ --xh-action-bg-rest: var(--xh-_image-viewer-chrome-bg);
246
+ --xh-action-bg-hover: var(--xh-_image-viewer-chrome-bg-hover);
247
+ --xh-action-bg-pressed: var(--xh-_image-viewer-chrome-bg-active);
248
+ --xh-action-bg-focus-visible: var(--xh-_image-viewer-chrome-bg);
249
+ --xh-action-bg-disabled: var(--xh-_image-viewer-chrome-bg);
250
+ --xh-action-fg-rest: currentColor;
251
+ --xh-action-fg-hover: currentColor;
252
+ --xh-action-fg-pressed: currentColor;
253
+ --xh-action-fg-focus-visible: currentColor;
254
+ --xh-action-fg-disabled: var(--xh-fg-disabled);
255
+ --xh-action-border-rest: transparent;
256
+ --xh-action-border-hover: transparent;
257
+ --xh-action-border-pressed: transparent;
258
+ --xh-action-border-focus-visible: transparent;
259
+ --xh-action-border-disabled: transparent;
260
+ --xh-action-radius: var(--xh-image-viewer-overlay-radius, var(--xh-_action-profile-radius));
261
+ --xh-icon-size: var(--xh-image-viewer-icon-size, var(--xh-_action-profile-glyph-size));
262
+
263
+ position: absolute;
264
+ z-index: 1;
265
+ }
266
+
267
+ .xh-scope-image-viewer[data-part='prev-trigger'] {
268
+ inset-inline-start: max(var(--xh-space-6), env(safe-area-inset-left), env(safe-area-inset-right));
269
+ inset-block-start: 50%;
270
+ translate: 0 -50%;
271
+ }
272
+
273
+ .xh-scope-image-viewer[data-part='next-trigger'] {
274
+ inset-inline-end: max(var(--xh-space-6), env(safe-area-inset-left), env(safe-area-inset-right));
275
+ inset-block-start: 50%;
276
+ translate: 0 -50%;
277
+ }
278
+
279
+ /* 右上角的关闭钮:悬浮在媒体上的单图标动作,与翻页钮同走 Action Control floating 档(sm = 40px、圆形);
280
+ 面与翻页钮同一层深色 chrome */
281
+ .xh-scope-image-viewer[data-part='close-trigger'] {
282
+ --xh-action-bg-rest: var(--xh-_image-viewer-chrome-bg);
283
+ --xh-action-bg-hover: var(--xh-image-viewer-close-bg-hover, var(--xh-_image-viewer-chrome-bg-hover));
284
+ --xh-action-bg-pressed: var(--xh-image-viewer-close-bg-active, var(--xh-_image-viewer-chrome-bg-active));
285
+ --xh-action-bg-focus-visible: var(--xh-_image-viewer-chrome-bg);
286
+ --xh-action-bg-disabled: var(--xh-_image-viewer-chrome-bg);
287
+ /* 看图时整块 chrome 盖住页面,叉的颜色随那层继承;使用者要单独换叉的颜色走这支槽 */
288
+ --xh-action-fg-rest: var(--xh-image-viewer-close-fg, currentColor);
289
+ --xh-action-fg-hover: var(--xh-image-viewer-close-fg, currentColor);
290
+ --xh-action-fg-pressed: var(--xh-image-viewer-close-fg, currentColor);
291
+ --xh-action-fg-focus-visible: var(--xh-image-viewer-close-fg, currentColor);
292
+ --xh-action-fg-disabled: var(--xh-fg-disabled);
293
+ --xh-action-border-rest: transparent;
294
+ --xh-action-border-hover: transparent;
295
+ --xh-action-border-pressed: transparent;
296
+ --xh-action-border-focus-visible: transparent;
297
+ --xh-action-border-disabled: transparent;
298
+ --xh-action-radius: var(--xh-image-viewer-close-radius, var(--xh-shape-circle));
299
+ --xh-action-visual-size: var(--xh-image-viewer-close-size, var(--xh-_action-profile-visual-size));
300
+ --xh-icon-size: var(--xh-image-viewer-icon-size, var(--xh-_action-profile-glyph-size));
301
+
302
+ position: absolute;
303
+ z-index: 1;
304
+ inset-block-start: max(var(--xh-space-6), env(safe-area-inset-top));
305
+ inset-inline-end: max(var(--xh-space-6), env(safe-area-inset-left), env(safe-area-inset-right));
306
+ }
307
+
308
+ /* liquid 档:data-material="liquid" 下控制层换成液态面,深色纱那一套只属于 standard 档。
309
+ 液态面按下层(使用者的图片、遮罩)写墨色域,前景跟着墨色走,不再钉在浅字上。
310
+ 工具条是不贴边的一维栏、计数是一行字的 chip,取 pill;翻页与关闭是媒体控制钮,取 circle */
311
+ :where([data-material='liquid']) .xh-scope-image-viewer[data-part='toolbar'][data-xh-liquid],
312
+ :where([data-material='liquid']) .xh-scope-image-viewer[data-part='counter'][data-xh-liquid] {
313
+ border: var(--xh-stroke-thin) solid var(--xh-material-liquid-border);
314
+ background: var(--xh-image-viewer-chrome-bg, var(--xh-_liquid-bg));
315
+ color: var(--xh-material-liquid-fg);
316
+ box-shadow: var(--xh-_liquid-shadow);
317
+ -webkit-backdrop-filter: var(--xh-material-liquid-backdrop);
318
+ backdrop-filter: var(--xh-material-liquid-backdrop);
319
+ /* 换到深浅不同的图上时色调翻转:底色、字色、细线与投影一起淡变 */
320
+ transition:
321
+ background-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
322
+ border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
323
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
324
+ box-shadow var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
325
+ }
326
+
327
+ :where([data-material='liquid']) .xh-scope-image-viewer[data-part='toolbar'][data-xh-liquid] {
328
+ border-radius: var(--xh-image-viewer-toolbar-radius-outer, var(--xh-image-viewer-overlay-radius, var(--xh-shape-pill)));
329
+ }
330
+
331
+ :where([data-material='liquid']) .xh-scope-image-viewer[data-part='counter'][data-xh-liquid] {
332
+ border-radius: var(--xh-image-viewer-counter-radius, var(--xh-image-viewer-overlay-radius, var(--xh-shape-pill)));
333
+ }
334
+
335
+ /* 工具条里的钮:悬停 / 按下取墨色淡底阶梯,工具条这块液态面就是它们的墨色域 */
336
+ :where([data-material='liquid']) .xh-scope-image-viewer[data-part='toolbar'][data-xh-liquid] > [data-part] {
337
+ --xh-action-bg-hover: var(--xh-image-viewer-action-bg-hover, var(--xh-bg-subtle));
338
+ --xh-action-bg-pressed: var(--xh-image-viewer-action-bg-active, var(--xh-bg-subtle-hover));
339
+ }
340
+
341
+ :where([data-material='liquid']) .xh-scope-image-viewer[data-part='prev-trigger'][data-xh-liquid],
342
+ :where([data-material='liquid']) .xh-scope-image-viewer[data-part='next-trigger'][data-xh-liquid],
343
+ :where([data-material='liquid']) .xh-scope-image-viewer[data-part='close-trigger'][data-xh-liquid] {
344
+ --xh-action-bg-rest: var(--xh-image-viewer-chrome-bg, var(--xh-_liquid-bg));
345
+ --xh-action-bg-hover: var(--xh-image-viewer-action-bg-hover, var(--xh-_liquid-bg-hover));
346
+ --xh-action-bg-pressed: var(--xh-image-viewer-action-bg-active, var(--xh-_liquid-bg-pressed));
347
+ --xh-action-bg-focus-visible: var(--xh-image-viewer-chrome-bg, var(--xh-material-liquid-focus-surface));
348
+ --xh-action-bg-disabled: var(--xh-image-viewer-chrome-bg, var(--xh-_liquid-bg));
349
+ --xh-action-fg-rest: var(--xh-material-liquid-fg);
350
+ --xh-action-fg-hover: var(--xh-material-liquid-fg);
351
+ --xh-action-fg-pressed: var(--xh-material-liquid-fg);
352
+ --xh-action-fg-focus-visible: var(--xh-material-liquid-fg);
353
+ --xh-action-border-rest: var(--xh-material-liquid-border);
354
+ --xh-action-border-hover: var(--xh-material-liquid-border);
355
+ --xh-action-border-pressed: var(--xh-material-liquid-border);
356
+ --xh-action-border-focus-visible: var(--xh-material-liquid-border);
357
+ --xh-action-border-disabled: var(--xh-material-liquid-border);
358
+ --xh-action-fg-disabled: var(--xh-fg-disabled);
359
+ --xh-action-shadow-rest: var(--xh-_liquid-shadow);
360
+ --xh-action-shadow-hover: var(--xh-_liquid-shadow);
361
+ --xh-action-shadow-pressed: var(--xh-_liquid-shadow);
362
+ --xh-action-shadow-focus-visible: var(--xh-_liquid-shadow);
363
+ --xh-action-shadow-disabled: var(--xh-_liquid-shadow);
364
+
365
+ color: var(--xh-material-liquid-fg);
366
+ -webkit-backdrop-filter: var(--xh-material-liquid-backdrop);
367
+ backdrop-filter: var(--xh-material-liquid-backdrop);
368
+ transform: var(--xh-_liquid-deform);
369
+ }
370
+
371
+ /* 叉的前景仍认使用者的那支槽;圆钮取 circle */
372
+ :where([data-material='liquid']) .xh-scope-image-viewer[data-part='close-trigger'][data-xh-liquid] {
373
+ --xh-action-fg-rest: var(--xh-image-viewer-close-fg, var(--xh-material-liquid-fg));
374
+ --xh-action-fg-hover: var(--xh-image-viewer-close-fg, var(--xh-material-liquid-fg));
375
+ --xh-action-fg-pressed: var(--xh-image-viewer-close-fg, var(--xh-material-liquid-fg));
376
+ --xh-action-fg-focus-visible: var(--xh-image-viewer-close-fg, var(--xh-material-liquid-fg));
377
+ --xh-action-bg-hover: var(--xh-image-viewer-close-bg-hover, var(--xh-_liquid-bg-hover));
378
+ --xh-action-bg-pressed: var(--xh-image-viewer-close-bg-active, var(--xh-_liquid-bg-pressed));
379
+ --xh-action-radius: var(--xh-image-viewer-close-radius, var(--xh-shape-circle));
380
+ }
381
+
382
+ /* 三颗钮的面是一层深色纱,纱底下是使用者的图片,环色算不出稳定的取值。
383
+ content 把前景钉死在浅字上、三颗钮的前景随它继承,环取 currentColor 即是那支浅字,
384
+ 不随图片内容也不随主题翻 */
385
+ .xh-scope-image-viewer[data-part='close-trigger']:focus-visible,
386
+ .xh-scope-image-viewer[data-part='prev-trigger']:focus-visible,
387
+ .xh-scope-image-viewer[data-part='next-trigger']:focus-visible {
388
+ --xh-action-ring-color-focus-visible: currentColor;
389
+ }
390
+
391
+ .xh-scope-image-viewer[data-part='close-trigger'][hidden] {
392
+ display: none;
393
+ }
394
+
395
+ /* 没写内容就由皮肤画兜底字形;作者往部件里塞了自己的图形,这几条即不命中 */
396
+ .xh-scope-image-viewer[data-part='zoom-in-trigger']:empty::before {
397
+ content: '';
398
+ display: inline-block;
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
+ vertical-align: var(--xh-glyph-baseline-shift);
402
+ background-color: currentColor;
403
+ -webkit-mask: var(--xh-glyph-mark-zoom-in) center / contain no-repeat;
404
+ mask: var(--xh-glyph-mark-zoom-in) center / contain no-repeat;
405
+ }
406
+
407
+ .xh-scope-image-viewer[data-part='zoom-out-trigger']:empty::before {
408
+ content: '';
409
+ display: inline-block;
410
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
411
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
412
+ vertical-align: var(--xh-glyph-baseline-shift);
413
+ background-color: currentColor;
414
+ -webkit-mask: var(--xh-glyph-mark-zoom-out) center / contain no-repeat;
415
+ mask: var(--xh-glyph-mark-zoom-out) center / contain no-repeat;
416
+ }
417
+
418
+ .xh-scope-image-viewer[data-part='rotate-left-trigger']:empty::before {
419
+ content: '';
420
+ display: inline-block;
421
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
422
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
423
+ vertical-align: var(--xh-glyph-baseline-shift);
424
+ background-color: currentColor;
425
+ -webkit-mask: var(--xh-glyph-mark-rotate-left) center / contain no-repeat;
426
+ mask: var(--xh-glyph-mark-rotate-left) center / contain no-repeat;
427
+ }
428
+
429
+ .xh-scope-image-viewer[data-part='rotate-right-trigger']:empty::before {
430
+ content: '';
431
+ display: inline-block;
432
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
433
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
434
+ vertical-align: var(--xh-glyph-baseline-shift);
435
+ background-color: currentColor;
436
+ -webkit-mask: var(--xh-glyph-mark-rotate-right) center / contain no-repeat;
437
+ mask: var(--xh-glyph-mark-rotate-right) center / contain no-repeat;
438
+ }
439
+
440
+ .xh-scope-image-viewer[data-part='flip-horizontal-trigger']:empty::before {
441
+ content: '';
442
+ display: inline-block;
443
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
444
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
445
+ vertical-align: var(--xh-glyph-baseline-shift);
446
+ background-color: currentColor;
447
+ -webkit-mask: var(--xh-glyph-mark-flip-horizontal) center / contain no-repeat;
448
+ mask: var(--xh-glyph-mark-flip-horizontal) center / contain no-repeat;
449
+ }
450
+
451
+ .xh-scope-image-viewer[data-part='flip-vertical-trigger']:empty::before {
452
+ content: '';
453
+ display: inline-block;
454
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
455
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
456
+ vertical-align: var(--xh-glyph-baseline-shift);
457
+ background-color: currentColor;
458
+ -webkit-mask: var(--xh-glyph-mark-flip-vertical) center / contain no-repeat;
459
+ mask: var(--xh-glyph-mark-flip-vertical) center / contain no-repeat;
460
+ }
461
+
462
+ .xh-scope-image-viewer[data-part='prev-trigger']:empty::before {
463
+ content: '';
464
+ display: inline-block;
465
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
466
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
467
+ vertical-align: var(--xh-glyph-baseline-shift);
468
+ background-color: currentColor;
469
+ -webkit-mask: var(--xh-glyph-mark-chevron-left) center / contain no-repeat;
470
+ mask: var(--xh-glyph-mark-chevron-left) center / contain no-repeat;
471
+ }
472
+
473
+ .xh-scope-image-viewer[data-part='next-trigger']:empty::before {
474
+ content: '';
475
+ display: inline-block;
476
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
477
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
478
+ vertical-align: var(--xh-glyph-baseline-shift);
479
+ background-color: currentColor;
480
+ -webkit-mask: var(--xh-glyph-mark-chevron-right) center / contain no-repeat;
481
+ mask: var(--xh-glyph-mark-chevron-right) center / contain no-repeat;
482
+ }
483
+
484
+ .xh-scope-image-viewer[data-part='close-trigger']:empty::before {
485
+ content: '';
486
+ display: inline-block;
487
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
488
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
489
+ vertical-align: var(--xh-glyph-baseline-shift);
490
+ background-color: currentColor;
491
+ -webkit-mask: var(--xh-glyph-mark-close) center / contain no-repeat;
492
+ mask: var(--xh-glyph-mark-close) center / contain no-repeat;
493
+ }
494
+
495
+ /* 声明过任何 display 就盖掉了 UA 的 [hidden]{display:none},收起态得自己还回去。
496
+ 写在文件末尾:兜底必须排在该部件所有 display 声明之后 */
497
+ .xh-scope-image-viewer[data-part='positioner'][hidden],
498
+ .xh-scope-image-viewer[data-part='viewport'][hidden],
499
+ .xh-scope-image-viewer[data-part='toolbar'][hidden],
500
+ .xh-scope-image-viewer[data-part='prev-trigger'][hidden],
501
+ .xh-scope-image-viewer[data-part='next-trigger'][hidden] {
502
+ display: none;
503
+ }
504
+
505
+ @media print {
506
+ /* 打印:定位层与遮罩是交互期才存在的东西,落到纸上只会盖住底下的正文。
507
+ 收起写在这份皮肤里而不是 reset:reset 层排在组件层之前,组件层里任何一条 display 都压得住它。 */
508
+ .xh-scope-image-viewer[data-part='backdrop'],
509
+ .xh-scope-image-viewer[data-part='positioner'] {
510
+ display: none;
511
+ }
512
+ }
513
+
514
+ @media (forced-colors: active) {
515
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
516
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
517
+ 按钮面上取 ButtonText,悬停与按下时家族把按钮涂成 Highlight、字形换 HighlightText;
518
+ 落在 Canvas 面上的取 CanvasText;
519
+ 禁用取 GrayText */
520
+ .xh-scope-image-viewer[data-part='viewport'][data-error]::after {
521
+ forced-color-adjust: none;
522
+ background-color: CanvasText;
523
+ }
524
+
525
+ .xh-scope-image-viewer:is([data-part='zoom-in-trigger'], [data-part='zoom-out-trigger'], [data-part='rotate-left-trigger'], [data-part='rotate-right-trigger'], [data-part='flip-horizontal-trigger'], [data-part='flip-vertical-trigger'], [data-part='prev-trigger'], [data-part='next-trigger'], [data-part='close-trigger']):empty::before {
526
+ forced-color-adjust: none;
527
+ background-color: ButtonText;
528
+ }
529
+
530
+ .xh-scope-image-viewer:is([data-part='zoom-in-trigger'], [data-part='zoom-out-trigger'], [data-part='rotate-left-trigger'], [data-part='rotate-right-trigger'], [data-part='flip-horizontal-trigger'], [data-part='flip-vertical-trigger'], [data-part='prev-trigger'], [data-part='next-trigger'], [data-part='close-trigger']):not([data-disabled]):not([data-loading]):is(:hover, :active, [data-pressed]):empty::before {
531
+ background-color: HighlightText;
532
+ }
533
+
534
+ .xh-scope-image-viewer:is([data-part='zoom-in-trigger'], [data-part='zoom-out-trigger'], [data-part='rotate-left-trigger'], [data-part='rotate-right-trigger'], [data-part='flip-horizontal-trigger'], [data-part='flip-vertical-trigger'], [data-part='prev-trigger'], [data-part='next-trigger'], [data-part='close-trigger'])[data-disabled]:empty::before {
535
+ background-color: GrayText;
536
+ }
537
+ }
538
+ }
@@ -0,0 +1,98 @@
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
+ /* 定义 image 样式。 */
7
+
8
+ /* 共享关键帧由 family/motion.css 提供,本皮肤 @import 它,单独引入仍成立。 */
9
+ @import '../family/motion.css';
10
+
11
+ @layer xihan.components {
12
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
13
+ .xh-scope-image {
14
+ --xh-image-skin: 1;
15
+ }
16
+
17
+ /* 尺寸交给三个变量而不是写死:这个组件既要能当封面图,也要能当头图与缩略图。
18
+ 给了 --xh-image-ratio 才有确定高度,回退内容与图片在同一个盒子里换人时不会跳。 */
19
+ .xh-scope-image[data-part='root'] {
20
+ position: relative;
21
+ display: block;
22
+ overflow: hidden;
23
+ inline-size: var(--xh-image-w, 100%);
24
+ block-size: var(--xh-image-h, auto);
25
+ aspect-ratio: var(--xh-image-ratio, auto);
26
+ border-radius: var(--xh-image-radius, var(--xh-shape-control));
27
+ background: var(--xh-image-bg, var(--xh-bg-subtle));
28
+ }
29
+
30
+ /* 声明了 display 就盖掉了 UA 的 [hidden]{display:none},收起态得自己还回去 */
31
+ .xh-scope-image[data-part='root'][hidden] {
32
+ display: none;
33
+ }
34
+
35
+ /* 不声明 display:加载完成前的 hidden 直接交给浏览器的 [hidden] 规则收起。
36
+ 载完那一刻 hidden 撤掉、这个节点从不显示变为显示,淡入在那一刻起跑,
37
+ 图片不是硬切上来盖掉占位层 */
38
+ .xh-scope-image[data-part='image'] {
39
+ inline-size: 100%;
40
+ block-size: 100%;
41
+ object-fit: var(--xh-image-fit, cover);
42
+ animation: xh-fade-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
43
+ }
44
+
45
+ /* 占位层还在(正淡出)时图片不再自己淡入:它在占位层底下直接落位,由占位层的淡出揭开,
46
+ 模糊小图与图片交叉,不先透出一拍底色 */
47
+ .xh-scope-image[data-part='root']:has(> [data-part='placeholder']:not([hidden])) > [data-part='image']:where(.xh-scope-image) {
48
+ animation: none;
49
+ }
50
+
51
+ /* 铺满图位的占位层:加载期间占住画面,作者往里放骨架屏或模糊小图。它叠在图位自己的淡底上,
52
+ 取不透明档,不与底下那层叠深 */
53
+ .xh-scope-image[data-part='placeholder'] {
54
+ position: absolute;
55
+ inset: 0;
56
+ display: flex;
57
+ align-items: center;
58
+ justify-content: center;
59
+ background: var(--xh-image-placeholder-bg, var(--xh-bg-subtle-hover-opaque));
60
+ color: var(--xh-image-placeholder-fg, var(--xh-fg-subtle));
61
+ }
62
+
63
+ /* 图片落位或失败:占位层原地淡出(它盖在图位之上),播完连接层才写 hidden */
64
+ .xh-scope-image[data-part='placeholder']:is([data-state='loaded'], [data-state='error']) {
65
+ animation: xh-fade-out var(--xh-motion-duration-enter) var(--xh-motion-ease-enter) forwards;
66
+ }
67
+
68
+ /* display 会盖掉 hidden 属性的 UA 规则:淡出播完之后靠这条收起 */
69
+ .xh-scope-image[data-part='placeholder'][hidden] {
70
+ display: none;
71
+ }
72
+
73
+ .xh-scope-image[data-part='fallback'] {
74
+ display: flex;
75
+ align-items: center;
76
+ justify-content: center;
77
+ inline-size: 100%;
78
+ block-size: 100%;
79
+ /* 没给 ratio 时根盒子高度是 auto,回退内容自己撑出一个不塌的高度 */
80
+ min-block-size: var(--xh-image-fallback-min-h, var(--xh-control-h-lg));
81
+ color: var(--xh-image-fallback-fg, var(--xh-fg-muted));
82
+ font-size: var(--xh-image-fallback-font-size, var(--xh-text-secondary-size));
83
+ line-height: var(--xh-leading-none);
84
+ user-select: none;
85
+ }
86
+
87
+ /* 图片载好:回退内容浮到图片之上原地淡出,与图片的淡入交叉;播完连接层才写 hidden */
88
+ .xh-scope-image[data-part='fallback'][data-state='loaded'] {
89
+ position: absolute;
90
+ inset: 0;
91
+ animation: xh-fade-out var(--xh-motion-duration-enter) var(--xh-motion-ease-enter) forwards;
92
+ }
93
+
94
+ /* display 会盖掉 hidden 属性的 UA 规则:延迟窗口内与淡出播完之后靠这条收起 */
95
+ .xh-scope-image[data-part='fallback'][hidden] {
96
+ display: none;
97
+ }
98
+ }