@xihan-ui/styles 2.1.0 → 3.1.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 +1261 -0
  2. package/README.md +6 -1
  3. package/component-tokens.d.ts +533 -96
  4. package/components.tokens.json +35461 -26045
  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 +128 -50
  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 +56 -0
  37. package/css/date-picker.css +132 -26
  38. package/css/date-range-picker.css +337 -14
  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 +20 -18
  49. package/css/file-upload.css +125 -135
  50. package/css/flex.css +324 -8
  51. package/css/float-button.css +263 -38
  52. package/css/floating-panel.css +53 -19
  53. package/css/form.css +31 -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 +435 -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 +175 -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 +103 -17
  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 +271 -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 +13824 -4030
  147. package/index.unlayered.css +13403 -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/select.css CHANGED
@@ -7,7 +7,10 @@
7
7
  @import '../family/field-chrome.css';
8
8
  @import '../family/action-control.css';
9
9
  @import '../family/collection-item.css';
10
+ @import '../family/loading-ring.css';
10
11
  @import '../family/motion.css';
12
+ @import '../family/material.css';
13
+ @import '../family/tag-list.css';
11
14
 
12
15
  @layer xihan.components {
13
16
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
@@ -23,8 +26,8 @@
23
26
  /* 落定那一侧的可用宽度,同样由定位引擎经内联自定义属性写进来,行内轴据此夹取;
24
27
  引擎没算(尚未落位 / 无引擎 / 贴边归零)时退到视口宽 */
25
28
  --xh-_select-available-w: 100vw;
26
- /* 触发器实测宽度,由定位引擎经内联自定义属性写进来,内容层拿它当最小宽的下界;
27
- 引擎没算(尚未落位 / 无引擎)时为 0,max 取静态档,等于不抬宽 */
29
+ /* 字段盒实测宽度,由定位引擎经内联自定义属性写进来,内容层与它等宽;
30
+ 引擎没算(尚未落位 / 无引擎)时为 0,面板取下界 */
28
31
  --xh-_select-anchor-w: 0px;
29
32
  /* WC 嵌套层仍在父层 DOM 内;先截断父 positioner 的方向,再按本层 placement 开一侧。 */
30
33
  --xh-_overlay-enter-up: 0;
@@ -212,22 +215,11 @@
212
215
  display: none;
213
216
  }
214
217
 
215
- /* 标签行:触发器里收着可见标签与 +N 那一枚的一行,行里每一枚都是 tag(data-scope="tag"),
216
- 样子归 tag.css,这里只管行怎么排。它是弹性子项,只缩不涨:
217
- 得允许自己被压缩并把溢出裁掉,否则标签会冲出盒的右缘、把展开箭头挤到盒外;
218
+ /* 标签行:触发器里收着可见标签与 +N 那一枚的一行,行里每一枚都是 tag(data-scope="tag"),样子归 tag.css;
219
+ 行怎么排、标签怎么进出归标签行家族配方(family/tag-list.css),这里只把使用者的间距槽接进去。
218
220
  展开箭头靠触发器的 space-between 留在末端,行本身不必撑开 */
219
221
  [data-scope='select'][data-part='tag-list'] {
220
- display: inline-flex;
221
- flex: 0 1 auto;
222
- align-items: center;
223
- gap: var(--xh-select-tag-list-gap, var(--xh-space-1));
224
- min-inline-size: 0;
225
- overflow: hidden;
226
- }
227
-
228
- /* 无选中时连接层给 hidden,占位文字回来 */
229
- [data-scope='select'][data-part='tag-list'][hidden] {
230
- display: none;
222
+ --xh-tag-list-gap: var(--xh-select-tag-list-gap, var(--xh-space-1));
231
223
  }
232
224
 
233
225
  /* 标签行露面时 value-text 让位:两者是触发器里的同一格,占位文字与标签不同屏。
@@ -236,18 +228,6 @@
236
228
  display: none;
237
229
  }
238
230
 
239
- /* 行里的标签:行装不下时各自缩短,截断落在 tag 自己的 label 上;弹性子项默认最小尺寸是内容尺寸,
240
- 不归零则长标签把行撑破、省略号永远轮不到。+N 那一枚(带 data-count)不缩,始终完整可见。
241
- 标签里的删除钮是 tag 的 close-trigger,样子全归 tag.css,这里不画 */
242
- [data-scope='select'][data-part='tag-list'] > [data-scope='tag'][data-part='root'] {
243
- flex: 0 1 auto;
244
- min-inline-size: 0;
245
- }
246
-
247
- [data-scope='select'][data-part='tag-list'] > [data-scope='tag'][data-part='root'][data-count] {
248
- flex: none;
249
- }
250
-
251
231
  /* 环画在盒上,按钮自己不再画:两圈套着的环读起来像两个控件 */
252
232
  [data-scope='select'][data-part='trigger']:focus-visible {
253
233
  outline: none;
@@ -289,7 +269,7 @@
289
269
  inline-size: var(--xh-select-action-size, var(--xh-control-action-size));
290
270
  block-size: var(--xh-select-action-size, var(--xh-control-action-size));
291
271
  color: var(--xh-select-indicator-fg, var(--xh-fg-muted));
292
- transition: rotate var(--xh-motion-duration-micro) var(--xh-motion-ease-enter-strong);
272
+ transition: rotate var(--xh-motion-duration-nudge) var(--xh-motion-ease-enter-strong);
293
273
  }
294
274
 
295
275
  /* 没写内容就由皮肤画箭头;作者往部件里塞了自己的图形,这条规则即不命中 */
@@ -311,44 +291,34 @@
311
291
  /* 浮层外壳:描边、底色、阴影与整体尺寸归它。它自己不滚——滚的是里面的 list,
312
292
  底部操作区因此留在视口里,不随条目滚走 */
313
293
  [data-scope='select'][data-part='content'] {
294
+ /* 材质由家族配方画(family/material.css 的 frosted 面:描边、底、1px 顶光、前景、投影与背景滤镜),
295
+ 这里只把使用者槽接进来 */
296
+ --xh-frosted-bg: var(--xh-select-content-bg, var(--xh-_material-bg));
297
+ --xh-frosted-border: var(--xh-select-content-border, var(--xh-_material-border));
298
+ --xh-frosted-fg: var(--xh-select-content-fg, var(--xh-_material-fg));
299
+ --xh-frosted-shadow: var(--xh-select-content-shadow, var(--xh-_material-shadow));
300
+ --xh-frosted-backdrop: var(--xh-select-content-backdrop, var(--xh-_material-backdrop));
301
+ --xh-frosted-highlight: var(--xh-select-content-highlight, var(--xh-_material-highlight));
302
+
314
303
  position: relative;
315
304
  display: flex;
316
305
  flex-direction: column;
317
- /* 下界跟着触发器走,面板不窄于它;可用宽度的夹取写在这一侧:
318
- min-inline-size 恒压过 max-inline-size,锚点比可用区还宽时只夹 max 等于没夹 */
319
- min-inline-size: min(
306
+ /* 列表型浮层与字段盒等宽:长选项在条目里截断,面板不随最长的一条变宽。锚点比下界还窄时取下界,
307
+ 比可用区还宽时收成可用宽度;要封顶由作者另写上界槽 */
308
+ inline-size: min(
320
309
  max(var(--xh-select-content-min-w, var(--xh-overlay-menu-min-w)), var(--xh-_select-anchor-w)),
321
310
  var(--xh-_select-available-w)
322
311
  );
323
- /* 静态档与落位后的可用宽度取小:窄视口下面板伸出去,最右侧的内容就点不到了 */
324
- max-inline-size: min(var(--xh-select-content-max-w, var(--xh-overlay-max-w)), var(--xh-_select-available-w));
312
+ max-inline-size: var(--xh-select-content-max-w, none);
325
313
  max-block-size: min(var(--xh-select-content-max-h, var(--xh-overlay-menu-max-h)), var(--xh-_select-available-h));
326
314
  padding-block: var(--xh-select-content-py, var(--xh-space-1));
327
315
  padding-inline: var(--xh-select-content-px, var(--xh-space-1));
328
- border: var(--xh-stroke-thin) solid var(--xh-select-content-border, var(--xh-material-frosted-border));
329
316
  border-radius: var(--xh-select-content-radius, var(--xh-shape-overlay));
330
- background: var(--xh-select-content-bg, var(--xh-material-frosted-bg));
331
- color: var(--xh-select-content-fg, var(--xh-material-frosted-fg));
332
- box-shadow: var(--xh-select-content-shadow, var(--xh-material-frosted-shadow));
333
- -webkit-backdrop-filter: var(--xh-select-content-backdrop, var(--xh-material-frosted-backdrop));
334
- backdrop-filter: var(--xh-select-content-backdrop, var(--xh-material-frosted-backdrop));
335
317
  /* 条目的悬停底色是方角,滚到两端时会探出外壳的圆角 */
336
318
  overflow: hidden;
337
319
  pointer-events: auto;
338
320
  }
339
321
 
340
- /* 顶光单独叠在真实四边 border 内侧;强制色归零后轮廓仍在。 */
341
- [data-scope='select'][data-part='content']::before {
342
- content: '';
343
- position: absolute;
344
- inset-block-start: 0;
345
- inset-inline: 0;
346
- block-size: var(--xh-stroke-thin);
347
- border-radius: inherit;
348
- background: var(--xh-select-content-highlight, var(--xh-material-frosted-highlight));
349
- pointer-events: none;
350
- }
351
-
352
322
  /* 列表框本体:只管滚动,留白归外壳。
353
323
  min-block-size: 0 是 flex 子项能真正滚动的前提——不给的话它按内容撑高,
354
324
  外壳的 max-block-size 就只会把溢出的部分裁掉而不是交给它滚 */
@@ -361,6 +331,12 @@
361
331
  overflow-y: auto;
362
332
  /* 浮层滚动面:滚到头不把页面一起带走 */
363
333
  overscroll-behavior: contain;
334
+ transition: opacity var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
335
+ }
336
+
337
+ /* 刷新:已有候选时后台在取,保留上一帧,列表按 micro 淡下,数据到了再淡回;在途占位此时让位 */
338
+ [data-scope='select'][data-part='list'][aria-busy='true'] {
339
+ opacity: var(--xh-state-disabled-opacity);
364
340
  }
365
341
 
366
342
  /* 底部操作区:贴在列表下沿,用一道分隔线与条目断开。
@@ -392,7 +368,8 @@
392
368
  outline: none;
393
369
  }
394
370
 
395
- [data-scope='select'][data-part='content'][data-state='open'] {
371
+ /* 挂载时就开着的那一段(data-instant)属于首帧,直接呈现 */
372
+ [data-scope='select'][data-part='content'][data-state='open']:not([data-instant]) {
396
373
  animation: xh-overlay-slide-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong);
397
374
  }
398
375
 
@@ -422,7 +399,10 @@
422
399
 
423
400
  /* 在途占位:与空态占位同一个位置、同一副观感,两者不同屏 */
424
401
  [data-scope='select'][data-part='loading'] {
425
- display: block;
402
+ display: flex;
403
+ align-items: center;
404
+ justify-content: center;
405
+ gap: var(--xh-_select-gap);
426
406
  flex: none;
427
407
  padding-block: var(--xh-select-loading-py, var(--xh-space-3));
428
408
  padding-inline: var(--xh-select-loading-px, var(--xh-_select-item-px));
@@ -436,6 +416,15 @@
436
416
  display: none;
437
417
  }
438
418
 
419
+ /* 首次加载:文案前一枚加载环,由加载环配方画(连接层投影 data-xh-loading-ring 与 data-loading),
420
+ 直径取所在尺寸档的图标档 */
421
+ [data-scope='select'][data-part='loading']::before {
422
+ content: '';
423
+ flex: none;
424
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-md));
425
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-md));
426
+ }
427
+
439
428
  [data-scope='select'][data-part='group'] {
440
429
  display: flex;
441
430
  flex-direction: column;
@@ -476,7 +465,7 @@
476
465
  --xh-collection-inline-padding: var(--xh-select-item-px, var(--xh-_select-item-px));
477
466
  --xh-collection-gap: var(--xh-select-item-gap, var(--xh-_select-gap));
478
467
  --xh-collection-font-size: var(--xh-select-item-font-size, var(--xh-_select-font-size));
479
- --xh-collection-radius: var(--xh-select-item-radius, var(--xh-shape-control));
468
+ --xh-collection-radius: var(--xh-select-item-radius, var(--xh-shape-inset));
480
469
  --xh-collection-fg-rest: var(--xh-select-item-fg, var(--xh-material-frosted-fg));
481
470
  --xh-collection-fg-hover: var(--xh-select-item-fg, var(--xh-material-frosted-fg));
482
471
  --xh-collection-fg-keyboard-highlight: var(--xh-select-item-fg, var(--xh-material-frosted-fg));
@@ -590,4 +579,34 @@
590
579
  display: none;
591
580
  }
592
581
  }
582
+
583
+ @media (forced-colors: active) {
584
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
585
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
586
+ 按钮面上取 ButtonText,悬停与按下时家族把按钮涂成 Highlight、字形换 HighlightText;
587
+ 条目面上取 CanvasText;按下与没被高亮时的悬停,家族把条目涂成 Highlight,字形换 HighlightText;
588
+ 字段面上取 CanvasText;
589
+ 禁用取 GrayText */
590
+ [data-scope='select'][data-part='indicator']:empty::before,
591
+ [data-scope='select'][data-part='item-indicator']:empty::before {
592
+ forced-color-adjust: none;
593
+ background-color: CanvasText;
594
+ }
595
+
596
+ [data-scope='select'][data-part='clear-trigger']:empty::before {
597
+ forced-color-adjust: none;
598
+ background-color: ButtonText;
599
+ }
600
+
601
+ [data-scope='select'][data-part='item']:not([aria-disabled='true'], [data-disabled], [aria-busy='true'], [data-error]):is(:active, [data-pressed], :hover:not(:focus-visible, [data-highlighted])) [data-scope='select'][data-part='item-indicator']:empty::before,
602
+ [data-scope='select'][data-part='clear-trigger']:not([data-disabled]):not([data-loading]):is(:hover, :active, [data-pressed]):empty::before {
603
+ background-color: HighlightText;
604
+ }
605
+
606
+ [data-scope='select'][data-part='control'][data-disabled] [data-scope='select'][data-part='indicator']:empty::before,
607
+ [data-scope='select'][data-part='item']:is([aria-disabled='true'], [data-disabled]) [data-scope='select'][data-part='item-indicator']:empty::before,
608
+ [data-scope='select'][data-part='clear-trigger'][data-disabled]:empty::before {
609
+ background-color: GrayText;
610
+ }
611
+ }
593
612
  }
package/css/side-nav.css CHANGED
@@ -5,10 +5,16 @@
5
5
 
6
6
  /* 定义 side nav 样式。 */
7
7
 
8
- /* 共享关键帧由 family/motion.css 提供,行走 Collection Item 家族;本皮肤 @import 它们,单独引入仍成立。 */
8
+ /* 共享关键帧由 family/motion.css 提供,行走 Collection Item 家族,搜索框的重置与占位前景走字段家族;
9
+ 本皮肤 @import 它们,单独引入仍成立。 */
10
+ @import '../family/field-chrome.css';
9
11
  @import '../family/collection-item.css';
10
12
  @import '../family/motion.css';
11
13
 
14
+ /* 图标栏的名称提示就是库内的 Tooltip(data-scope="tooltip" 的 positioner 与 content),
15
+ 反白面、进退场与各环境档都由它的皮肤给;本皮肤 @import 它,单独引入仍成立 */
16
+ @import './tooltip.css';
17
+
12
18
  @layer xihan.components {
13
19
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
14
20
  [data-scope='side-nav'] {
@@ -49,21 +55,83 @@
49
55
  --xh-_side-nav-row-font-size: var(--xh-control-font-lg);
50
56
  }
51
57
 
58
+ /* 展开与折叠两档宽度缺省取侧栏令牌,与 Layout 的侧栏同源 */
52
59
  [data-scope='side-nav'][data-part='root'] {
53
- inline-size: var(--xh-side-nav-w, 240px);
60
+ inline-size: var(--xh-side-nav-w, var(--xh-sider-w));
54
61
  display: flex;
55
62
  flex-direction: column;
56
63
  gap: var(--xh-side-nav-gap, var(--xh-space-1));
57
64
  padding: var(--xh-side-nav-p, var(--xh-space-2));
58
65
  color: var(--xh-side-nav-fg, var(--xh-fg-default));
66
+ /* 占位式侧栏折叠是屏内的尺寸变化:与 Layout 侧栏同一段 move、同一条曲线,放在侧栏里时两者一起收放 */
67
+ transition: inline-size var(--xh-motion-duration-move) var(--xh-motion-ease-continuous);
59
68
  }
60
69
 
61
- /* 折叠成图标栏:整栏收窄,文字随行宽被裁掉 */
70
+ /* 折叠成图标栏:整栏收窄;行文字先淡出,收窄落定之后才裁掉(见下) */
62
71
  [data-scope='side-nav'][data-part='root'][data-collapsed] {
63
- inline-size: var(--xh-side-nav-collapsed-w, 56px);
72
+ inline-size: var(--xh-side-nav-collapsed-w, var(--xh-sider-collapsed-w));
73
+ }
74
+
75
+ /* 搜索框:面板内嵌搜索的写法——不画字段外壳,通栏一行,高与字号随尺寸档,只画一道面内分隔的下划线;
76
+ 重置与占位前景由字段家族按 data-xh-field-input 给,行内内衬与行同一支槽 */
77
+ [data-scope='side-nav'][data-part='input'] {
78
+ --xh-field-placeholder-fg: var(--xh-side-nav-placeholder-fg, var(--xh-fg-subtle));
79
+
80
+ flex: none;
81
+ min-inline-size: 0;
82
+ block-size: var(--xh-side-nav-input-h, var(--xh-_side-nav-row-h));
83
+ padding-block: 0;
84
+ padding-inline: var(--xh-side-nav-input-px, var(--xh-_side-nav-row-px));
85
+ border: none;
86
+ border-block-end: var(--xh-stroke-thin) solid var(--xh-side-nav-search-divider, var(--xh-material-solid-separator));
87
+ background: none;
88
+ color: inherit;
89
+ font-family: inherit;
90
+ font-size: var(--xh-side-nav-input-font-size, var(--xh-_side-nav-row-font-size));
91
+ line-height: var(--xh-leading-normal);
92
+ transition: opacity var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
93
+ }
94
+
95
+ /* 聚焦不画环:插入符就是焦点指示。框没有外壳,全局那圈键盘焦点环只会压在通栏一行上 */
96
+ [data-scope='side-nav'][data-part='input']:focus-visible {
97
+ outline: none;
98
+ }
99
+
100
+ /* 自动填充:引擎给填过的框强加一套系统色,盖得过 background 与 color。
101
+ 底改用 inset 阴影铺回来,字改用 -webkit-text-fill-color 接回来;
102
+ 两个选择器名分写两条,一条列表里只要有一个名字不被认识整条就被丢弃 */
103
+ [data-scope='side-nav'][data-part='input']:autofill {
104
+ box-shadow: inset 0 0 0 100vmax var(--xh-side-nav-input-autofill-bg, var(--xh-bg-surface));
105
+ -webkit-text-fill-color: var(--xh-side-nav-input-autofill-fg, var(--xh-fg-default));
106
+ }
107
+
108
+ [data-scope='side-nav'][data-part='input']:-webkit-autofill {
109
+ box-shadow: inset 0 0 0 100vmax var(--xh-side-nav-input-autofill-bg, var(--xh-bg-surface));
110
+ -webkit-text-fill-color: var(--xh-side-nav-input-autofill-fg, var(--xh-fg-default));
111
+ }
112
+
113
+ /* 折叠一开始搜索框就与行文字一起淡出,展开等整栏宽度落定才淡入;落成图标栏之后让位:
114
+ 留着高度、不可见也不可聚焦(过滤此时暂停),下面的行不上下跳 */
115
+ [data-scope='side-nav'][data-part='root']:is([data-collapsed], [data-animating]) [data-part='input'] {
116
+ opacity: 0;
117
+ }
118
+
119
+ [data-scope='side-nav'][data-part='root'][data-collapsed]:not([data-animating]) [data-part='input'] {
120
+ visibility: hidden;
64
121
  }
65
122
 
123
+ /* 搜索一条都没命中的占位:list 的兄弟,只一句次要文字,面归侧栏所在的宿主 */
124
+ [data-scope='side-nav'][data-part='empty'] {
125
+ padding-block: var(--xh-side-nav-empty-py, var(--xh-space-3));
126
+ padding-inline: var(--xh-side-nav-empty-px, var(--xh-_side-nav-row-px));
127
+ color: var(--xh-side-nav-empty-fg, var(--xh-fg-muted));
128
+ font-size: var(--xh-side-nav-empty-font-size, var(--xh-_side-nav-row-font-size));
129
+ text-align: center;
130
+ }
131
+
132
+ /* 三层列表同一副排法:顶层、分组里的与分支的子层都是 ul,行与行之间同一个 gap,不带 UA 的缩进与外边距 */
66
133
  [data-scope='side-nav'][data-part='list'],
134
+ [data-scope='side-nav'][data-part='group-list'],
67
135
  [data-scope='side-nav'][data-part='branch-content'] {
68
136
  display: flex;
69
137
  flex-direction: column;
@@ -82,6 +150,7 @@
82
150
  display: none;
83
151
  }
84
152
 
153
+ /* 分组是上一层列表里的一条:标题与组内列表上下排,间距与行距同一个 gap */
85
154
  [data-scope='side-nav'][data-part='group'] {
86
155
  display: flex;
87
156
  flex-direction: column;
@@ -90,17 +159,31 @@
90
159
  }
91
160
 
92
161
  [data-scope='side-nav'][data-part='group-label'] {
162
+ position: relative;
93
163
  padding-block: var(--xh-side-nav-group-label-py, var(--xh-space-1));
94
164
  padding-inline: var(--xh-side-nav-group-label-px, var(--xh-_side-nav-row-px));
95
165
  font-size: var(--xh-text-caption-size);
96
166
  font-weight: var(--xh-text-label-weight);
97
167
  color: var(--xh-fg-subtle);
98
168
  user-select: none;
169
+ transition: color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
99
170
  }
100
171
 
101
- /* 折叠时分组标题没有落点,藏掉 */
102
- [data-scope='side-nav'][data-part='group-label'][data-collapsed] {
103
- display: none;
172
+ /* 图标栏里分组标题没有落点:字淡成透明、仍参与播报,标题留着自己的高度,下面的行不上下跳;
173
+ 折叠一开始就淡出,展开等整栏宽度落定才淡入。落成图标栏之后在这一格里画一道细分隔,分组仍看得出来 */
174
+ [data-scope='side-nav'][data-part='root']:is([data-collapsed], [data-animating]) [data-part='group-label'] {
175
+ overflow: hidden;
176
+ color: transparent;
177
+ white-space: nowrap;
178
+ }
179
+
180
+ [data-scope='side-nav'][data-part='root'][data-collapsed]:not([data-animating]) [data-part='group-label']::after {
181
+ content: '';
182
+ position: absolute;
183
+ inset-inline: var(--xh-_side-nav-row-px);
184
+ inset-block-start: calc(50% - var(--xh-stroke-thin) / 2);
185
+ block-size: var(--xh-stroke-thin);
186
+ background: var(--xh-border-subtle);
104
187
  }
105
188
 
106
189
  [data-scope='side-nav'][data-part='branch'] {
@@ -140,16 +223,16 @@
140
223
  --xh-collection-bg-hover: var(--xh-side-nav-row-bg-hover, var(--xh-bg-subtle));
141
224
  --xh-collection-bg-keyboard-highlight: var(--xh-side-nav-row-bg-hover, var(--xh-bg-subtle));
142
225
  --xh-collection-bg-pressed: var(--xh-side-nav-row-bg-pressed, var(--xh-bg-subtle-hover));
143
- /* 当前页:公开槽名不变,缺省前景由品牌深字改为淡底前景,字重保持 medium;2px 指示条取品牌色 */
226
+ /* 当前页:页内持久集合的唯一标记是品牌淡底行面 + 淡底前景,字重不变(与静息同为 regular) */
144
227
  --xh-collection-bg-current: var(--xh-side-nav-row-bg-active, var(--xh-bg-brand-subtle));
145
228
  --xh-collection-fg-current: var(--xh-side-nav-row-fg-active, var(--xh-fg-on-brand-subtle));
146
229
  --xh-collection-fg-current-hover: var(--xh-side-nav-row-fg-active, var(--xh-fg-on-brand-subtle));
147
230
  --xh-collection-fg-current-highlight: var(--xh-side-nav-row-fg-active, var(--xh-fg-on-brand-subtle));
148
231
  --xh-collection-fg-current-pressed: var(--xh-side-nav-row-fg-active, var(--xh-fg-on-brand-subtle));
149
- --xh-collection-font-weight-current: var(--xh-side-nav-row-font-weight-active, var(--xh-font-weight-medium));
150
- --xh-collection-font-weight-current-hover: var(--xh-side-nav-row-font-weight-active, var(--xh-font-weight-medium));
151
- --xh-collection-font-weight-current-highlight: var(--xh-side-nav-row-font-weight-active, var(--xh-font-weight-medium));
152
- --xh-collection-font-weight-current-pressed: var(--xh-side-nav-row-font-weight-active, var(--xh-font-weight-medium));
232
+ --xh-collection-font-weight-current: var(--xh-side-nav-row-font-weight-active, var(--xh-font-weight-regular));
233
+ --xh-collection-font-weight-current-hover: var(--xh-side-nav-row-font-weight-active, var(--xh-font-weight-regular));
234
+ --xh-collection-font-weight-current-highlight: var(--xh-side-nav-row-font-weight-active, var(--xh-font-weight-regular));
235
+ --xh-collection-font-weight-current-pressed: var(--xh-side-nav-row-font-weight-active, var(--xh-font-weight-regular));
153
236
  --xh-collection-indicator-fg-current: var(--xh-side-nav-indicator-color, var(--xh-fg-brand));
154
237
  --xh-collection-indicator-fg-current-hover: var(--xh-side-nav-indicator-color, var(--xh-fg-brand));
155
238
  --xh-collection-indicator-fg-current-highlight: var(--xh-side-nav-indicator-color, var(--xh-fg-brand));
@@ -187,15 +270,24 @@
187
270
  overflow: hidden;
188
271
  text-overflow: ellipsis;
189
272
  white-space: nowrap;
273
+ transition: opacity var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
190
274
  }
191
275
 
192
- /* 折叠成图标栏:行文字裁到看不见但仍参与播报,行内容居中,不裁半个字。
276
+ /* 行文字与展开箭头的淡变:折叠一开始就淡出,此时仍在行里、由收窄的行宽裁着;展开时等整栏宽度落定才淡入。
277
+ 弹出面板里的行不在此列 */
278
+ [data-scope='side-nav'][data-part='root']:is([data-collapsed], [data-animating]) [data-part='branch-text']:not([data-popout] *),
279
+ [data-scope='side-nav'][data-part='root']:is([data-collapsed], [data-animating]) [data-part='link-text']:not([data-popout] *),
280
+ [data-scope='side-nav'][data-part='root']:is([data-collapsed], [data-animating]) [data-part='branch-indicator']:not([data-popout] *) {
281
+ opacity: 0;
282
+ }
283
+
284
+ /* 落成图标栏(收窄落定):行文字裁到看不见但仍参与播报,行内容居中,不裁半个字。
193
285
  用 clip-path 而不是 display:none——图标栏里按钮与链接的可及名就是这段文字,
194
286
  藏成 display:none 它们就一个名字都没有了。
195
287
  white-space: nowrap 防止 1px 宽把文本挤成竖排。
196
288
  弹出面板里的行不在此列——面板是正常宽度的浮层,文字照常显示 */
197
- [data-scope='side-nav'][data-part='root'][data-collapsed] [data-part='branch-text']:not([data-popout] *),
198
- [data-scope='side-nav'][data-part='root'][data-collapsed] [data-part='link-text']:not([data-popout] *) {
289
+ [data-scope='side-nav'][data-part='root'][data-collapsed]:not([data-animating]) [data-part='branch-text']:not([data-popout] *),
290
+ [data-scope='side-nav'][data-part='root'][data-collapsed]:not([data-animating]) [data-part='link-text']:not([data-popout] *) {
199
291
  position: absolute;
200
292
  inline-size: 1px;
201
293
  block-size: 1px;
@@ -207,13 +299,13 @@
207
299
  border: 0;
208
300
  }
209
301
 
210
- /* 展开指示是纯装饰(aria-hidden),折叠态整个不占位 */
211
- [data-scope='side-nav'][data-part='root'][data-collapsed] [data-part='branch-indicator']:not([data-popout] *) {
302
+ /* 展开指示是纯装饰(aria-hidden),落成图标栏之后整个不占位 */
303
+ [data-scope='side-nav'][data-part='root'][data-collapsed]:not([data-animating]) [data-part='branch-indicator']:not([data-popout] *) {
212
304
  display: none;
213
305
  }
214
306
 
215
- [data-scope='side-nav'][data-part='root'][data-collapsed] [data-part='branch-trigger']:not([data-popout] *),
216
- [data-scope='side-nav'][data-part='root'][data-collapsed] [data-part='link']:not([data-popout] *) {
307
+ [data-scope='side-nav'][data-part='root'][data-collapsed]:not([data-animating]) [data-part='branch-trigger']:not([data-popout] *),
308
+ [data-scope='side-nav'][data-part='root'][data-collapsed]:not([data-animating]) [data-part='link']:not([data-popout] *) {
217
309
  --xh-collection-inline-padding: 0;
218
310
 
219
311
  justify-content: center;
@@ -230,11 +322,34 @@
230
322
  引擎没算(尚未落位 / 无引擎 / 贴边归零)时退到视口宽 */
231
323
  --xh-_side-nav-available-w: 100vw;
232
324
 
325
+ /* 弹出面板从锚点一侧短移入,方向按实际落位只激活一支;先清零,截断父层定位层的方向继承 */
326
+ --xh-_overlay-enter-up: 0;
327
+ --xh-_overlay-enter-down: 0;
328
+ --xh-_overlay-enter-left: 0;
329
+ --xh-_overlay-enter-right: 0;
330
+
233
331
  position: fixed;
234
332
  z-index: var(--xh-side-nav-popout-layer, var(--xh-_layer, var(--xh-layer-popover)));
235
333
  pointer-events: auto;
236
334
  }
237
335
 
336
+ /* 物理 placement 与定位引擎同一套,不随书写方向翻 */
337
+ [data-scope='side-nav'][data-part='positioner'][data-placement^='top'] {
338
+ --xh-_overlay-enter-down: 1;
339
+ }
340
+
341
+ [data-scope='side-nav'][data-part='positioner'][data-placement^='bottom'] {
342
+ --xh-_overlay-enter-up: 1;
343
+ }
344
+
345
+ [data-scope='side-nav'][data-part='positioner'][data-placement^='left'] {
346
+ --xh-_overlay-enter-right: 1;
347
+ }
348
+
349
+ [data-scope='side-nav'][data-part='positioner'][data-placement^='right'] {
350
+ --xh-_overlay-enter-left: 1;
351
+ }
352
+
238
353
  /* 还没落位就先藏着:visibility 而非 display,display:none 会让引擎量不到面板尺寸 */
239
354
  [data-scope='side-nav'][data-part='positioner'][data-hidden] {
240
355
  visibility: hidden;
@@ -259,14 +374,15 @@
259
374
  overscroll-behavior: contain;
260
375
  }
261
376
 
377
+ /* 弹出分支是锚定在分支行旁侧的一列导航,与菜单同属锚定列表:从锚点一侧短移淡入,不缩放 */
262
378
  [data-scope='side-nav'][data-part='branch-content'][data-popout][data-state='open'] {
263
- animation: xh-pop-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong);
379
+ animation: xh-overlay-slide-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong);
264
380
  }
265
381
 
266
382
  /* 退场:收起那一帧 hidden 由退场闸门押后,这里才播得出来。forwards 把末态钉住,
267
383
  免得动画结束到收起落下来之间闪一下原样 */
268
384
  [data-scope='side-nav'][data-part='branch-content'][data-popout][data-state='closed'] {
269
- animation: xh-pop-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
385
+ animation: xh-overlay-slide-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
270
386
  }
271
387
 
272
388
  /* 箭头是行尾的后缀:auto 外边距把它顶到行尾,家族按 suffix 槽下发的外边距已在行上归零。
@@ -284,7 +400,12 @@
284
400
  justify-content: center;
285
401
  inline-size: var(--xh-side-nav-branch-indicator-size, var(--xh-control-indicator-size));
286
402
  block-size: var(--xh-side-nav-branch-indicator-size, var(--xh-control-indicator-size));
287
- transition: rotate var(--xh-motion-duration-micro) var(--xh-motion-ease-enter-strong);
403
+ /* 收起时指向行尾、展开时转向下方,与逻辑书写方向一致:rtl 下行尾在左侧,收起的箭头转半圈;
404
+ 旋转角乘书写方向的符号(ltr 0deg、rtl 180deg),按就近的 dir 走 */
405
+ rotate: calc(90deg - 90deg * var(--xh-direction-sign));
406
+ transition:
407
+ rotate var(--xh-motion-duration-nudge) var(--xh-motion-ease-enter-strong),
408
+ opacity var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
288
409
  }
289
410
 
290
411
  [data-scope='side-nav'][data-part='branch-indicator'][data-state='open'] {
@@ -309,6 +430,7 @@
309
430
  [data-scope='side-nav'][data-part='root'][hidden],
310
431
  [data-scope='side-nav'][data-part='list'][hidden],
311
432
  [data-scope='side-nav'][data-part='group'][hidden],
433
+ [data-scope='side-nav'][data-part='group-list'][hidden],
312
434
  [data-scope='side-nav'][data-part='branch'][hidden],
313
435
  [data-scope='side-nav'][data-part='branch-trigger'][hidden],
314
436
  [data-scope='side-nav'][data-part='item'][hidden],
@@ -317,11 +439,38 @@
317
439
  display: none;
318
440
  }
319
441
 
442
+ @media (forced-colors: active) {
443
+ /* 图标栏里分组之间那道细分隔画的是一块底色,这一档里底色被统一换成 Canvas、整道消失;换成 GrayText 画回来 */
444
+ [data-scope='side-nav'][data-part='root'][data-collapsed]:not([data-animating]) [data-part='group-label']::after {
445
+ background: GrayText;
446
+ }
447
+ }
448
+
320
449
  @media print {
321
- /* 打印:定位层与遮罩是交互期才存在的东西,落到纸上只会盖住底下的正文。
450
+ /* 打印:定位层与遮罩是交互期才存在的东西,落到纸上只会盖住底下的正文;搜索框同理,纸上没法输入。
322
451
  收起写在这份皮肤里而不是 reset:reset 层排在组件层之前,组件层里任何一条 display 都压得住它。 */
323
- [data-scope='side-nav'][data-part='positioner'] {
452
+ [data-scope='side-nav'][data-part='positioner'],
453
+ [data-scope='side-nav'][data-part='input'] {
324
454
  display: none;
325
455
  }
326
456
  }
457
+
458
+ @media (forced-colors: active) {
459
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
460
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
461
+ 条目面上取 CanvasText;按下与没被高亮时的悬停,家族把条目涂成 Highlight,字形换 HighlightText;
462
+ 禁用取 GrayText */
463
+ [data-scope='side-nav'][data-part='branch-indicator']:empty::before {
464
+ forced-color-adjust: none;
465
+ background-color: CanvasText;
466
+ }
467
+
468
+ [data-scope='side-nav'][data-part='branch-trigger']:not([aria-disabled='true'], [data-disabled], [aria-busy='true'], [data-error]):is(:active, [data-pressed], :hover:not(:focus-visible, [data-highlighted])) [data-scope='side-nav'][data-part='branch-indicator']:empty::before {
469
+ background-color: HighlightText;
470
+ }
471
+
472
+ [data-scope='side-nav'][data-part='branch-trigger']:is([aria-disabled='true'], [data-disabled]) [data-scope='side-nav'][data-part='branch-indicator']:empty::before {
473
+ background-color: GrayText;
474
+ }
475
+ }
327
476
  }
@@ -57,7 +57,7 @@
57
57
  /* 落笔、校验不过与禁用三档各换一次描边或底色,换的过程要看得出来 */
58
58
  transition:
59
59
  border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
60
- background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
60
+ background-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
61
61
  }
62
62
 
63
63
  [data-scope='signature-pad'][data-part='control'][data-invalid] {
@@ -137,6 +137,19 @@
137
137
  --xh-action-fg-rest: var(--xh-signature-pad-clear-fg-empty, var(--xh-fg-muted));
138
138
  }
139
139
 
140
+ /*
141
+ * 撤销与重做按钮与清空按钮同一身份:盒型、sm 档几何、outline 四态面、0.97 按压与粗指针命中区全由家族配方给出。
142
+ * 没有可撤销或可重做的一步时连接层投影 data-disabled + aria-disabled,家族给置灰面,焦点仍留在钮上。
143
+ */
144
+ [data-scope='signature-pad'][data-part='undo-trigger'],
145
+ [data-scope='signature-pad'][data-part='redo-trigger'] {
146
+ /* 作者塞进来的图标随档取字形尺(sm = 16px) */
147
+ --xh-icon-size: var(--xh-_action-profile-glyph-size);
148
+
149
+ /* 竖排容器里不铺满一行:按钮只占自己的宽 */
150
+ align-self: flex-start;
151
+ }
152
+
140
153
  /* 面是实心的,环色与面同族,画在面里就看不出来;环改取面自己的前景色 */
141
154
  [data-scope='signature-pad'][data-part='clear-trigger'][data-xh-action-variant='solid']:focus-visible {
142
155
  --xh-action-ring-color-focus-visible: currentColor;
@@ -158,6 +171,8 @@
158
171
  /* display 会盖掉 UA 的 [hidden]{display:none},凡是被赋了 display 的角色节点都要显式让位 */
159
172
  [data-scope='signature-pad'][data-part='root'][hidden],
160
173
  [data-scope='signature-pad'][data-part='control'][hidden],
174
+ [data-scope='signature-pad'][data-part='undo-trigger'][hidden],
175
+ [data-scope='signature-pad'][data-part='redo-trigger'][hidden],
161
176
  [data-scope='signature-pad'][data-part='clear-trigger'][hidden] {
162
177
  display: none;
163
178
  }
package/css/skeleton.css CHANGED
@@ -5,19 +5,10 @@
5
5
 
6
6
  /* 定义 skeleton 样式。 */
7
7
 
8
- @layer xihan.motion {
9
- /* 一条比底色略亮的窄带从头扫到尾。背景图比自身宽一倍,靠移动背景位置扫过去,
10
- 不动 transform 也不动布局,整段动画只在合成层上跑 */
11
- @keyframes xh-skeleton-shimmer {
12
- from {
13
- background-position: 200% 0;
14
- }
15
-
16
- to {
17
- background-position: -200% 0;
18
- }
19
- }
8
+ /* 共享关键帧由 family/motion.css 提供,本皮肤 @import 它,单独引入仍成立。 */
9
+ @import '../family/motion.css';
20
10
 
11
+ @layer xihan.motion {
21
12
  /* 呼吸档:整根条子在原色与禁用档之间来回淡。往返由 alternate 给,帧里只写一端 */
22
13
  @keyframes xh-skeleton-pulse {
23
14
  to {
@@ -44,6 +35,13 @@
44
35
  display: none;
45
36
  }
46
37
 
38
+ /* 刚加载完:机器已把骨架原地抬出文档流、钉住原来的尺寸,真实内容补上它空出来的位置;
39
+ 骨架盖在内容之上淡出,播完才收起,不挡内容的指针。挂载时就已加载完的直接收起,不播这一段 */
40
+ [data-scope='skeleton'][data-part='root'][data-state='loaded']:not([hidden]) {
41
+ pointer-events: none;
42
+ animation: xh-fade-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
43
+ }
44
+
47
45
  /*
48
46
  * 单根骨架条。底色与微光两层叠在一起:
49
47
  * background-color 是静止时看到的那层,background-image 是扫过去的那道光。
@@ -63,7 +61,8 @@
63
61
  );
64
62
  background-repeat: no-repeat;
65
63
  background-size: 200% 100%;
66
- animation: xh-skeleton-shimmer var(--xh-skeleton-duration, var(--xh-shimmer-duration)) var(--xh-motion-ease-loop) infinite;
64
+ /* 扫光与思考中文字、工具调用进行中同一段共享关键帧:同屏时亮带扫过的距离与节奏一致 */
65
+ animation: xh-shimmer var(--xh-skeleton-duration, var(--xh-motion-loop-shimmer)) var(--xh-motion-ease-loop) infinite;
67
66
  }
68
67
 
69
68
  /* 一行文字的高度取正文字号,圆角拉满,看起来就是一行还没排出来的字 */
@@ -93,7 +92,7 @@
93
92
  */
94
93
  [data-scope='skeleton'][data-part='root'][data-animation='pulse'] [data-part='item'] {
95
94
  background-image: none;
96
- animation: xh-skeleton-pulse var(--xh-skeleton-pulse-duration, var(--xh-shimmer-duration)) var(--xh-motion-ease-sweep) infinite alternate;
95
+ animation: xh-skeleton-pulse var(--xh-skeleton-pulse-duration, var(--xh-motion-loop-shimmer)) var(--xh-motion-ease-sweep) infinite alternate;
97
96
  }
98
97
 
99
98
  /* 静止档:底色留着,两层动效都撤掉 */