@xihan-ui/styles 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/CHANGELOG.md +1328 -0
  2. package/README.md +6 -1
  3. package/component-tokens.d.ts +505 -111
  4. package/components.tokens.json +34750 -26291
  5. package/css/accordion.css +39 -13
  6. package/css/alert.css +61 -3
  7. package/css/anchor.css +49 -11
  8. package/css/approval.css +124 -84
  9. package/css/avatar-group.css +1 -1
  10. package/css/avatar.css +13 -5
  11. package/css/back-top.css +63 -44
  12. package/css/badge.css +63 -13
  13. package/css/bar-code.css +1 -1
  14. package/css/breadcrumb.css +63 -4
  15. package/css/button-group.css +5 -5
  16. package/css/button.css +69 -21
  17. package/css/calendar-picker.css +27 -8
  18. package/css/calendar-range-picker.css +48 -11
  19. package/css/card.css +116 -2
  20. package/css/carousel.css +153 -48
  21. package/css/cartesian-chart.css +830 -0
  22. package/css/cascader.css +184 -70
  23. package/css/checkbox-group.css +69 -99
  24. package/css/checkbox.css +21 -54
  25. package/css/citation.css +470 -0
  26. package/css/clipboard.css +23 -66
  27. package/css/code-view.css +103 -8
  28. package/css/collapsible.css +37 -11
  29. package/css/color-field.css +18 -0
  30. package/css/color-picker.css +57 -62
  31. package/css/color-slider.css +26 -19
  32. package/css/color-swatch-picker.css +8 -6
  33. package/css/combobox.css +82 -37
  34. package/css/command.css +48 -48
  35. package/css/context-menu.css +43 -28
  36. package/css/date-field.css +23 -0
  37. package/css/date-picker.css +57 -24
  38. package/css/date-range-picker.css +301 -14
  39. package/css/description.css +17 -1
  40. package/css/descriptions.css +43 -1
  41. package/css/dialog.css +99 -43
  42. package/css/diff-view.css +101 -16
  43. package/css/download-trigger.css +46 -80
  44. package/css/drawer.css +168 -133
  45. package/css/editable.css +19 -0
  46. package/css/empty-state.css +8 -7
  47. package/css/field-array.css +36 -0
  48. package/css/field.css +17 -19
  49. package/css/fieldset.css +1 -1
  50. package/css/file-upload.css +126 -136
  51. package/css/flex.css +324 -8
  52. package/css/float-button.css +168 -41
  53. package/css/floating-panel.css +59 -27
  54. package/css/form.css +26 -20
  55. package/css/funnel-chart.css +106 -0
  56. package/css/graph-chart.css +205 -0
  57. package/css/grid-list.css +235 -0
  58. package/css/grid.css +252 -1
  59. package/css/heatmap.css +119 -36
  60. package/css/hierarchy-chart.css +298 -0
  61. package/css/hover-card.css +33 -18
  62. package/css/icon.css +100 -0
  63. package/css/image-cropper.css +34 -8
  64. package/css/image-viewer.css +169 -26
  65. package/css/image.css +23 -4
  66. package/css/infinite-scroll.css +2 -2
  67. package/css/input-group.css +13 -15
  68. package/css/json-viewer.css +55 -7
  69. package/css/kbd.css +1 -1
  70. package/css/layout.css +71 -21
  71. package/css/liquid.css +106 -0
  72. package/css/list.css +2 -2
  73. package/css/listbox.css +72 -23
  74. package/css/loading-bar.css +27 -13
  75. package/css/log.css +131 -35
  76. package/css/markdown-stream.css +1 -1
  77. package/css/marquee.css +163 -7
  78. package/css/matrix-code.css +1 -1
  79. package/css/mention.css +35 -21
  80. package/css/menu.css +40 -28
  81. package/css/menubar.css +42 -28
  82. package/css/message-feed.css +174 -53
  83. package/css/navigation-menu.css +177 -18
  84. package/css/notification.css +431 -110
  85. package/css/number-field.css +22 -0
  86. package/css/page-header.css +4 -2
  87. package/css/pagination.css +129 -29
  88. package/css/password-input.css +40 -8
  89. package/css/pie-chart.css +247 -0
  90. package/css/popconfirm.css +50 -58
  91. package/css/popover.css +55 -21
  92. package/css/progress.css +308 -10
  93. package/css/prompt-input.css +37 -1
  94. package/css/question-flow.css +104 -64
  95. package/css/radar-chart.css +233 -0
  96. package/css/radio-group.css +372 -14
  97. package/css/rating.css +25 -27
  98. package/css/reasoning.css +62 -55
  99. package/css/reset.css +1 -1
  100. package/css/resizable.css +6 -1
  101. package/css/sankey-chart.css +174 -0
  102. package/css/scroll-area.css +32 -0
  103. package/css/scrollbar.css +1 -1
  104. package/css/select.css +76 -59
  105. package/css/side-nav.css +177 -30
  106. package/css/signature-pad.css +23 -8
  107. package/css/skeleton.css +13 -14
  108. package/css/slider.css +39 -17
  109. package/css/sortable.css +38 -8
  110. package/css/sparkline.css +164 -0
  111. package/css/spinner.css +50 -22
  112. package/css/splitter.css +8 -2
  113. package/css/statistic.css +10 -0
  114. package/css/steps.css +257 -38
  115. package/css/switch.css +30 -55
  116. package/css/table.css +168 -128
  117. package/css/tabs.css +243 -31
  118. package/css/tag-group.css +65 -37
  119. package/css/tag.css +55 -14
  120. package/css/tags-input.css +40 -3
  121. package/css/text-field.css +19 -0
  122. package/css/time-field.css +20 -1
  123. package/css/time-picker.css +44 -17
  124. package/css/time-range-picker.css +45 -18
  125. package/css/timeline.css +1 -1
  126. package/css/toggle-group.css +8 -8
  127. package/css/toggle.css +6 -5
  128. package/css/tool-call.css +64 -56
  129. package/css/toolbar.css +105 -7
  130. package/css/tooltip.css +18 -7
  131. package/css/tour.css +49 -21
  132. package/css/transfer.css +104 -81
  133. package/css/tree-select.css +235 -58
  134. package/css/tree.css +240 -147
  135. package/css/truncate.css +55 -12
  136. package/css/typography.css +93 -0
  137. package/css/virtualizer.css +57 -1
  138. package/css/watermark.css +23 -0
  139. package/family/action-control.css +14 -5
  140. package/family/chart.css +480 -0
  141. package/family/check-mark.css +94 -0
  142. package/family/choice-card.css +97 -0
  143. package/family/collection-item.css +70 -27
  144. package/family/field-chrome.css +1 -12
  145. package/family/loading-ring.css +95 -0
  146. package/family/material.css +72 -0
  147. package/family/menu-choice.css +56 -0
  148. package/family/motion.css +208 -11
  149. package/family/tag-list.css +59 -0
  150. package/index.css +23363 -14091
  151. package/index.unlayered.css +23198 -13922
  152. package/package.json +20 -8
  153. package/css/gradient-text.css +0 -91
  154. package/css/icon-wrapper.css +0 -81
  155. package/css/segmented.css +0 -280
  156. package/css/toast.css +0 -499
package/css/avatar.css CHANGED
@@ -24,7 +24,8 @@
24
24
  inline-size: var(--xh-avatar-size, var(--xh-control-h-md));
25
25
  block-size: var(--xh-avatar-size, var(--xh-control-h-md));
26
26
  border-radius: var(--xh-avatar-radius, var(--xh-shape-circle));
27
- background: var(--xh-avatar-bg, var(--xh-bg-subtle));
27
+ /* 头像在头像组里层叠:底取淡底的不透明档,半透明会透出压在下面的那一枚 */
28
+ background: var(--xh-avatar-bg, var(--xh-bg-subtle-opaque));
28
29
  color: var(--xh-avatar-fg, var(--xh-fg-muted));
29
30
  font-size: var(--xh-avatar-font-size, var(--xh-text-secondary-size));
30
31
  font-weight: var(--xh-avatar-font-weight, var(--xh-font-weight-medium));
@@ -53,13 +54,13 @@
53
54
  两条声明都把 --xh-avatar-bg / --xh-avatar-fg 排在语气值前面:使用者槽仍是最外那一层,
54
55
  写了 tone 的那一枚照样受外层统一控制。整块带 [data-tone] 限定,没写语气的头像逐值不变 */
55
56
  [data-scope='avatar'][data-part='root'][data-tone] {
56
- background: var(--xh-avatar-bg, var(--xh-_tone-subtle, var(--xh-bg-subtle)));
57
+ background: var(--xh-avatar-bg, var(--xh-_tone-subtle, var(--xh-bg-subtle-opaque)));
57
58
  color: var(--xh-avatar-fg, var(--xh-_tone-fg, var(--xh-fg-muted)));
58
59
  }
59
60
 
60
61
  /* 不声明 display,加载完成前的 hidden 直接交给浏览器的 [hidden] 规则收起。
61
- 载完那一刻 hidden 撤掉、这个节点从不显示变为显示,淡入在那一刻起跑,
62
- 图片不是硬切上来盖掉回退字 */
62
+ 载完那一刻 hidden 撤掉、这个节点从不显示变为显示,淡入在那一刻起跑;
63
+ 回退内容同一刻开始淡出(见下),两者交叉,中间不露一拍底色 */
63
64
  [data-scope='avatar'][data-part='image'] {
64
65
  inline-size: 100%;
65
66
  block-size: 100%;
@@ -77,7 +78,14 @@
77
78
  text-transform: uppercase;
78
79
  }
79
80
 
80
- /* display 会盖掉 hidden 属性的 UA 规则,加载完成后显式让位给图片 */
81
+ /* 图片载好:回退内容浮到图片之上原地淡出,与图片的淡入交叉;播完连接层才写 hidden */
82
+ [data-scope='avatar'][data-part='fallback'][data-state='loaded'] {
83
+ position: absolute;
84
+ inset: 0;
85
+ animation: xh-fade-out var(--xh-motion-duration-enter) var(--xh-motion-ease-enter) forwards;
86
+ }
87
+
88
+ /* display 会盖掉 hidden 属性的 UA 规则:载入中等过 fallbackDelay 之前、以及淡出播完之后收起 */
81
89
  [data-scope='avatar'][data-part='fallback'][hidden] {
82
90
  display: none;
83
91
  }
package/css/back-top.css CHANGED
@@ -6,6 +6,8 @@
6
6
  /* 定义 back top 样式。 */
7
7
 
8
8
  @import '../family/action-control.css';
9
+ @import '../family/motion.css';
10
+ @import '../family/material.css';
9
11
 
10
12
  @layer xihan.components {
11
13
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
@@ -29,21 +31,8 @@
29
31
  z-index: var(--xh-back-top-layer, var(--xh-layer-sticky));
30
32
  }
31
33
 
32
- /* —— 形态:缺省 outline 的磨砂面 ——
33
- 触发器接 Action Control floating 档,solid / subtle / ghost 的面由形态矩阵按 data-xh-action-variant 给;
34
- 缺省 outline(连接层不传 variant 时显式落)与悬浮动作钮同属 M2 磨砂面:背景、边缘、顶光、投影与磨砂
35
- 从一份配方取,高对比、减少透明度、打印和强制色才能沿同一令牌通道降级。钮浮在正文之上,透空的面在
36
- 任何内容上都读不出边界,所以 outline 这一档面留着。三支私有槽写在壳上:check-elevation-role 按赋值点
37
- 认海拔角色,float-button 同款 */
38
- [data-scope='back-top'][data-part='root'][data-variant='outline'] {
39
- --xh-_back-top-bg: var(--xh-material-frosted-bg);
40
- --xh-_back-top-fg: var(--xh-material-frosted-fg);
41
- --xh-_back-top-border: var(--xh-material-frosted-border);
42
- --xh-_back-top-shadow: var(--xh-material-frosted-shadow);
43
- }
44
-
45
34
  /* —— 触发器 ——
46
- 浮在内容之上的单图标圆钮,接 Action Control floating 档(§4.1):盒型(md = box-lg 48px,圆形)、
35
+ 浮在内容之上的单图标圆钮,接 Action Control floating 档:盒型(md = box-lg 48px,圆形)、
47
36
  四态面、0.97 按压与 44px 命中区由家族配方按 data-xh-action-variant / -size 给出。
48
37
  这里只把公开槽桥接到矩阵之前(使用者槽 → 形态矩阵 → 家族缺省)与几何槽映射。
49
38
  整件只有这一颗按钮,槽名与其余一样不带部件段(照 float-button 的 --xh-float-button-size) */
@@ -62,7 +51,7 @@
62
51
  --xh-action-border-pressed: var(--xh-back-top-border-hover, var(--xh-_action-variant-border-pressed));
63
52
  --xh-action-border-focus-visible: var(--xh-back-top-border, var(--xh-_action-variant-border-focus-visible));
64
53
  --xh-action-border-disabled: var(--xh-back-top-border, var(--xh-_action-variant-border-disabled));
65
- /* 矩阵各态无影(§8.3 淡底与阴影互斥);outline 那一档的磨砂投影在下面另桥接 */
54
+ /* 矩阵各态无影(淡底与阴影互斥);outline 那一档的磨砂投影在下面另桥接 */
66
55
  --xh-action-shadow-rest: var(--xh-back-top-shadow, none);
67
56
  --xh-action-shadow-hover: var(--xh-back-top-shadow, none);
68
57
  --xh-action-shadow-pressed: var(--xh-back-top-shadow, none);
@@ -74,38 +63,39 @@
74
63
  --xh-icon-size: var(--xh-back-top-icon-size, var(--xh-_action-profile-glyph-size));
75
64
  }
76
65
 
77
- /* 缺省 outline 是 M2 磨砂面:背景、边缘、顶光、投影与磨砂从同一份配方取。
78
- 悬停 / 按下的底走矩阵 outline 列的画布阶梯(100 → 200),边缘与投影不动;
79
- 键盘聚焦时铺配方的实体 focus surface——透明面上的内收环不能依赖页面底色;
80
- 置灰只换字色(矩阵 disabled 列的前景),面仍留着 */
66
+ /* 缺省 outline 是 frosted 材质面:连接层只在这一档投影 data-xh-material,面由材质家族配方给——
67
+ 底、描边、投影、1px 顶光与背景滤镜一份取值,高对比、减少透明度、打印和强制色沿同一令牌通道降级。
68
+ 悬停 / 按下换不透明的淡底一档、二档,边缘与投影不动;键盘聚焦时铺实体 focus surface——透明面上的
69
+ 内收环不能依赖页面底色;置灰只换字色,面仍留着 */
81
70
  [data-scope='back-top'][data-part='root'][data-variant='outline']
82
71
  [data-scope='back-top'][data-part='trigger'] {
83
- --xh-action-bg-rest: var(--xh-back-top-bg, var(--xh-_back-top-bg));
84
- --xh-action-bg-focus-visible: var(--xh-back-top-bg, var(--xh-material-frosted-focus-surface));
85
- --xh-action-bg-disabled: var(--xh-back-top-bg, var(--xh-_back-top-bg));
86
- --xh-action-fg-rest: var(--xh-back-top-fg, var(--xh-_back-top-fg));
87
- --xh-action-fg-hover: var(--xh-back-top-fg, var(--xh-_back-top-fg));
88
- --xh-action-fg-pressed: var(--xh-back-top-fg, var(--xh-_back-top-fg));
89
- --xh-action-fg-focus-visible: var(--xh-back-top-fg, var(--xh-_back-top-fg));
90
- --xh-action-border-rest: var(--xh-back-top-border, var(--xh-_back-top-border));
91
- --xh-action-border-hover: var(--xh-back-top-border-hover, var(--xh-_back-top-border));
92
- --xh-action-border-pressed: var(--xh-back-top-border-hover, var(--xh-_back-top-border));
93
- --xh-action-border-focus-visible: var(--xh-back-top-border, var(--xh-_back-top-border));
94
- --xh-action-border-disabled: var(--xh-back-top-border, var(--xh-_back-top-border));
95
- --xh-action-shadow-rest: var(--xh-back-top-shadow, var(--xh-_back-top-shadow));
96
- --xh-action-shadow-hover: var(--xh-back-top-shadow, var(--xh-_back-top-shadow));
97
- --xh-action-shadow-pressed: var(--xh-back-top-shadow, var(--xh-_back-top-shadow));
98
- --xh-action-shadow-focus-visible: var(--xh-back-top-shadow, var(--xh-_back-top-shadow));
99
- --xh-action-shadow-disabled: var(--xh-back-top-shadow, var(--xh-_back-top-shadow));
72
+ /* 面走材质家族配方(family/material.css):frosted 档的底、交互阶梯、描边、投影、1px 顶光与背景滤镜,
73
+ liquid 档由配方换成液态面;这里把使用者槽接到 Action Control 的桥接槽 */
74
+ --xh-action-bg-rest: var(--xh-back-top-bg, var(--xh-_material-bg));
75
+ --xh-action-bg-hover: var(--xh-back-top-bg-hover, var(--xh-_material-bg-hover));
76
+ --xh-action-bg-pressed: var(--xh-back-top-bg-active, var(--xh-_material-bg-pressed));
77
+ --xh-action-bg-focus-visible: var(--xh-back-top-bg, var(--xh-_material-bg-focus));
78
+ --xh-action-bg-disabled: var(--xh-back-top-bg, var(--xh-_material-bg));
79
+ --xh-action-fg-rest: var(--xh-back-top-fg, var(--xh-_material-fg));
80
+ --xh-action-fg-hover: var(--xh-back-top-fg, var(--xh-_material-fg));
81
+ --xh-action-fg-pressed: var(--xh-back-top-fg, var(--xh-_material-fg));
82
+ --xh-action-fg-focus-visible: var(--xh-back-top-fg, var(--xh-_material-fg));
83
+ --xh-action-border-rest: var(--xh-back-top-border, var(--xh-_material-border));
84
+ --xh-action-border-hover: var(--xh-back-top-border-hover, var(--xh-_material-border));
85
+ --xh-action-border-pressed: var(--xh-back-top-border-hover, var(--xh-_material-border));
86
+ --xh-action-border-focus-visible: var(--xh-back-top-border, var(--xh-_material-border));
87
+ --xh-action-border-disabled: var(--xh-back-top-border, var(--xh-_material-border));
88
+ --xh-action-shadow-rest: var(--xh-back-top-shadow, var(--xh-_material-shadow));
89
+ --xh-action-shadow-hover: var(--xh-back-top-shadow, var(--xh-_material-shadow));
90
+ --xh-action-shadow-pressed: var(--xh-back-top-shadow, var(--xh-_material-shadow));
91
+ --xh-action-shadow-focus-visible: var(--xh-back-top-shadow, var(--xh-_material-shadow));
92
+ --xh-action-shadow-disabled: var(--xh-back-top-shadow, var(--xh-_material-shadow));
93
+ --xh-action-highlight-rest: var(--xh-_material-highlight);
94
+ --xh-action-highlight-hover: var(--xh-_material-highlight);
95
+ --xh-action-highlight-pressed: var(--xh-_material-highlight);
96
+ --xh-action-highlight-focus-visible: var(--xh-_material-highlight);
97
+ --xh-action-highlight-disabled: var(--xh-_material-highlight);
100
98
  /* M2 顶光走配方的高光通道:实心、淡色与透空形态各有自身表面,不能把磨砂高光误铺上去 */
101
- --xh-action-highlight-rest: var(--xh-material-frosted-highlight);
102
- --xh-action-highlight-hover: var(--xh-material-frosted-highlight);
103
- --xh-action-highlight-pressed: var(--xh-material-frosted-highlight);
104
- --xh-action-highlight-focus-visible: var(--xh-material-frosted-highlight);
105
- --xh-action-highlight-disabled: var(--xh-material-frosted-highlight);
106
-
107
- -webkit-backdrop-filter: var(--xh-material-frosted-backdrop);
108
- backdrop-filter: var(--xh-material-frosted-backdrop);
109
99
  }
110
100
 
111
101
  /* 面是实心的,环色与面同族,画在面里就看不出来;环改取面自己的前景色——
@@ -127,6 +117,16 @@
127
117
  mask: var(--xh-glyph-mark-chevron-up) center / contain no-repeat;
128
118
  }
129
119
 
120
+ /* 过线露面时弹出;退回线内时先播完退场、连接层才给根写 hidden 收起。退场途中不再接指针 */
121
+ [data-scope='back-top'][data-part='trigger'][data-state='visible'] {
122
+ animation: xh-pop-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong);
123
+ }
124
+
125
+ [data-scope='back-top'][data-part='trigger'][data-state='hidden'] {
126
+ pointer-events: none;
127
+ animation: xh-pop-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
128
+ }
129
+
130
130
  /* 配方声明过 display,会盖掉 UA 的 [hidden]{display:none},收起态得自己还回去。
131
131
  写在文件末尾:兜底必须排在该部件所有 display 声明之后 */
132
132
  [data-scope='back-top'][data-part='trigger'][hidden] {
@@ -139,4 +139,23 @@
139
139
  display: none;
140
140
  }
141
141
  }
142
+
143
+ @media (forced-colors: active) {
144
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
145
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
146
+ 按钮面上取 ButtonText,悬停与按下时家族把按钮涂成 Highlight、字形换 HighlightText;
147
+ 禁用取 GrayText */
148
+ [data-scope='back-top'][data-part='trigger']:empty::before {
149
+ forced-color-adjust: none;
150
+ background-color: ButtonText;
151
+ }
152
+
153
+ [data-scope='back-top'][data-part='trigger']:not([data-disabled]):not([data-loading]):is(:hover, :active, [data-pressed]):empty::before {
154
+ background-color: HighlightText;
155
+ }
156
+
157
+ [data-scope='back-top'][data-part='trigger'][data-disabled]:empty::before {
158
+ background-color: GrayText;
159
+ }
160
+ }
142
161
  }
package/css/badge.css CHANGED
@@ -5,6 +5,9 @@
5
5
 
6
6
  /* 定义 badge 样式。 */
7
7
 
8
+ /* 共享关键帧由 family/motion.css 提供,本皮肤 @import 它,单独引入仍成立。 */
9
+ @import '../family/motion.css';
10
+
8
11
  @layer xihan.components {
9
12
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
10
13
  [data-scope='badge'] {
@@ -30,9 +33,11 @@
30
33
  不是药丸那套内衬与行高:那是 tag 的活 */
31
34
  [data-scope='badge'][data-part='indicator'] {
32
35
  /* 每一档的盒都要大于字号:两位数在等高的盒里会被撑破。
33
- 字号刻度最小就是 12px,所以往上抬盒子而不是往下压字 */
36
+ 字号刻度最小就是 12px,所以往上抬盒子而不是往下压字。
37
+ 盒只比字高一档:20px 扣掉两道 1px 描边还剩 18px 内高,13px 的两位数与「99+」立得住,
38
+ 角标又不会大过它贴着的宿主文字 */
34
39
  --xh-_badge-dot: var(--xh-space-2);
35
- --xh-_badge-min: var(--xh-space-7);
40
+ --xh-_badge-min: var(--xh-space-5);
36
41
  --xh-_badge-px: var(--xh-space-1);
37
42
  --xh-_badge-font: var(--xh-text-secondary-size);
38
43
 
@@ -66,7 +71,7 @@
66
71
  }
67
72
 
68
73
  /* 圆点形态:只表示「有」,不表示「有几个」——收掉内边距与文字盒。
69
- 宽高同槽的正方盒按身份取 circle(§6.3),不沿用计数档的胶囊冒充圆:
74
+ 宽高同槽的正方盒按身份取 circle,不沿用计数档的胶囊冒充圆:
70
75
  胶囊那 9999px 在正方盒上虽也画成圆,形状身份却写错了档,作者按圆点覆盖圆角时无处落手。
71
76
  圆点直径只有六到十像素,一像素的内高光会占去顶上一大截、读成缺了一圈的描边,这里收掉 */
72
77
  [data-scope='badge'][data-part='indicator'][data-dot] {
@@ -79,29 +84,73 @@
79
84
  font-size: 0;
80
85
  }
81
86
 
87
+ /* 呼吸:圆点表达正在进行、给不出进度的状态(直播、录制、通话中)。
88
+ 明暗与缩放的起伏持续到状态结束;外扩光环画在 ::after 上,同周期播 3 轮后停在不可见。
89
+ 数字角标不呼吸:连接层只在圆点档投影 data-pulse,明暗起伏会压低数字的对比度 */
90
+ [data-scope='badge'][data-part='indicator'][data-pulse] {
91
+ animation: xh-breathe var(--xh-motion-loop-breathe) var(--xh-motion-ease-breathe) infinite;
92
+ }
93
+
94
+ [data-scope='badge'][data-part='indicator'][data-pulse]::after {
95
+ content: '';
96
+ position: absolute;
97
+ inset: 0;
98
+ border-radius: inherit;
99
+ background: inherit;
100
+ opacity: 0;
101
+ pointer-events: none;
102
+ animation: xh-breathe-halo var(--xh-motion-loop-breathe) var(--xh-motion-ease-breathe) 3;
103
+ }
104
+
105
+ /* 减弱动效:两者都停,圆点停在满不透明度,光环不出现;状态仍由圆点本身与宿主的文字表达 */
106
+ @media (prefers-reduced-motion: reduce) {
107
+ [data-scope='badge'][data-part='indicator'][data-pulse],
108
+ [data-scope='badge'][data-part='indicator'][data-pulse]::after {
109
+ animation: none;
110
+ }
111
+ }
112
+
113
+ :where([data-motion='reduce']) [data-scope='badge'][data-part='indicator'][data-pulse],
114
+ :where([data-motion='reduce']) [data-scope='badge'][data-part='indicator'][data-pulse]::after {
115
+ animation: none;
116
+ }
117
+
118
+ /* 出现与消失:计数从无到有时原地弹出;清零时缩小淡出,播完连接层才写 hidden,退场那几帧照清零前的数字写。
119
+ 首帧就在的角标直接呈现(data-instant);呼吸的圆点不叠进场,两段明暗会打架。
120
+ 写在呼吸之后:退场压过呼吸。减弱动效下缩放归一、只剩淡变 */
121
+ [data-scope='badge'][data-part='indicator'][data-state='visible']:not([data-instant], [data-pulse]) {
122
+ animation: xh-pop-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong);
123
+ }
124
+
125
+ [data-scope='badge'][data-part='indicator'][data-state='hidden'] {
126
+ animation: xh-pop-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
127
+ }
128
+
82
129
  /* —— 落点 —— */
83
- /* 用 inset 的逻辑属性写,rtl 下 end 自动落到左边;translate 让角标压住宿主的角 */
130
+ /* 用 inset 的逻辑属性写,rtl 下 end 自动落到左边;translate 让角标压住宿主的角。
131
+ 两个偏移槽微调角标离角多远:正值朝行内末端、块末端挪(rtl 下行内末端在左),
132
+ 宿主是圆形头像、角上留白多时往里收一点。四个角各按贴哪条边换正负号,同一个值在哪个角都朝同一个方向挪 */
84
133
  [data-scope='badge'][data-part='indicator'][data-placement='top-end'] {
85
- inset-block-start: 0;
86
- inset-inline-end: 0;
134
+ inset-block-start: var(--xh-badge-offset-block, var(--xh-space-0));
135
+ inset-inline-end: calc(-1 * var(--xh-badge-offset-inline, var(--xh-space-0)));
87
136
  translate: 25% -25%;
88
137
  }
89
138
 
90
139
  [data-scope='badge'][data-part='indicator'][data-placement='top-start'] {
91
- inset-block-start: 0;
92
- inset-inline-start: 0;
140
+ inset-block-start: var(--xh-badge-offset-block, var(--xh-space-0));
141
+ inset-inline-start: var(--xh-badge-offset-inline, var(--xh-space-0));
93
142
  translate: -25% -25%;
94
143
  }
95
144
 
96
145
  [data-scope='badge'][data-part='indicator'][data-placement='bottom-end'] {
97
- inset-block-end: 0;
98
- inset-inline-end: 0;
146
+ inset-block-end: calc(-1 * var(--xh-badge-offset-block, var(--xh-space-0)));
147
+ inset-inline-end: calc(-1 * var(--xh-badge-offset-inline, var(--xh-space-0)));
99
148
  translate: 25% 25%;
100
149
  }
101
150
 
102
151
  [data-scope='badge'][data-part='indicator'][data-placement='bottom-start'] {
103
- inset-block-end: 0;
104
- inset-inline-start: 0;
152
+ inset-block-end: calc(-1 * var(--xh-badge-offset-block, var(--xh-space-0)));
153
+ inset-inline-start: var(--xh-badge-offset-inline, var(--xh-space-0));
105
154
  translate: -25% 25%;
106
155
  }
107
156
 
@@ -132,9 +181,10 @@
132
181
  --xh-_badge-font: var(--xh-text-caption-size);
133
182
  }
134
183
 
184
+ /* 大号只往上抬一级:盒 24px、字 14px,仍是角标,不长成与宿主同级的一枚按钮 */
135
185
  [data-scope='badge'][data-part='indicator'][data-size='lg'] {
136
186
  --xh-_badge-dot: var(--xh-space-2_5);
137
- --xh-_badge-min: var(--xh-space-8);
187
+ --xh-_badge-min: var(--xh-space-6);
138
188
  --xh-_badge-px: var(--xh-space-1_5);
139
189
  --xh-_badge-font: var(--xh-text-body-size);
140
190
  }
package/css/bar-code.css CHANGED
@@ -32,7 +32,7 @@
32
32
 
33
33
  /* 占位与码面之间来回时,底色与那圈描边淡变 */
34
34
  transition:
35
- background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
35
+ background-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
36
36
  box-shadow var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
37
37
  }
38
38
 
@@ -76,7 +76,7 @@
76
76
  }
77
77
 
78
78
  /* 链接的几何、字号与面全经家族桥接槽:静息 muted / regular,悬停走白底承载 hover 100 + 随语气的字色,
79
- 按下 pressed 200 只换面(§7.2 / §9.2);当前页那条不再是"可去的地方"(§7.3):连接层投影
79
+ 按下 pressed 200 只换面;当前页那条不再是"可去的地方":连接层投影
80
80
  data-xh-collection-terminal,家族给透明面 + 随语气的 fg-default + medium + cursor default,不响应悬停与按下,
81
81
  并压过它同时带的 aria-disabled 禁用面。本条 (0,2,0) 压过家族基础块 (0,1,0),不能再写 color / background */
82
82
  [data-scope='breadcrumb'][data-part='link'] {
@@ -124,7 +124,7 @@
124
124
  display: none;
125
125
  }
126
126
 
127
- /* 分隔符与省略号都是纯视觉占位,读屏那边已被 aria-hidden 摘掉 */
127
+ /* 分隔符是纯视觉占位,读屏那边已被 aria-hidden 摘掉;折叠位是一个列表项,只给里面的触发器排位 */
128
128
  [data-scope='breadcrumb'][data-part='separator'],
129
129
  [data-scope='breadcrumb'][data-part='ellipsis'] {
130
130
  display: inline-flex;
@@ -148,8 +148,46 @@
148
148
  }
149
149
 
150
150
  [data-scope='breadcrumb'][data-part='ellipsis'] {
151
- inline-size: var(--xh-breadcrumb-ellipsis-size, var(--xh-space-5));
152
- cursor: default;
151
+ min-inline-size: var(--xh-breadcrumb-ellipsis-size, var(--xh-space-5));
152
+ }
153
+
154
+ /* 被收起的层级与展开后的折叠位都带 hidden:紧跟在它后面的分隔符一并收起,路径里不留两个挨着的分隔符 */
155
+ [data-scope='breadcrumb'][data-part='item'][hidden] + [data-scope='breadcrumb'][data-part='separator'],
156
+ [data-scope='breadcrumb'][data-part='ellipsis'][hidden] + [data-scope='breadcrumb'][data-part='separator'] {
157
+ display: none;
158
+ }
159
+
160
+ /* 折叠位里的触发器与链接同属一行导航条目(nav 语境):面、字色、按压时间线由家族给,几何与链接同一把尺——
161
+ 行内衬一格、control 圆角、字号随档;不写内容时由下面画一枚省略号字形 */
162
+ [data-scope='breadcrumb'][data-part='ellipsis-trigger'] {
163
+ --xh-collection-block-padding: var(--xh-space-0);
164
+ --xh-collection-inline-padding: var(--xh-space-1);
165
+ --xh-collection-radius: var(--xh-shape-control);
166
+ --xh-collection-font-size: var(--xh-breadcrumb-font-size, var(--xh-_breadcrumb-font-size));
167
+ --xh-collection-glyph-size: var(--xh-breadcrumb-icon-size, var(--xh-glyph-size-text));
168
+ --xh-collection-fg-rest: var(--xh-breadcrumb-fg, var(--xh-fg-muted));
169
+ --xh-collection-bg-hover: var(--xh-bg-subtle);
170
+ --xh-collection-fg-hover: var(--xh-_breadcrumb-accent-text);
171
+ --xh-collection-bg-pressed: var(--xh-bg-subtle-hover);
172
+
173
+ display: inline-flex;
174
+ align-items: center;
175
+ line-height: var(--xh-breadcrumb-leading, var(--xh-leading-tight));
176
+ }
177
+
178
+ [data-scope='breadcrumb'][data-part='ellipsis-trigger']:empty::before {
179
+ content: '';
180
+ display: inline-block;
181
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
182
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
183
+ vertical-align: var(--xh-glyph-baseline-shift);
184
+ background-color: currentColor;
185
+ -webkit-mask: var(--xh-glyph-mark-ellipsis) center / contain no-repeat;
186
+ mask: var(--xh-glyph-mark-ellipsis) center / contain no-repeat;
187
+ }
188
+
189
+ [data-scope='breadcrumb'][data-part='ellipsis-trigger'][hidden] {
190
+ display: none;
153
191
  }
154
192
 
155
193
  /* 没写内容就由皮肤画兜底字形(向前的尖角);作者写了斜杠或自己的图形,这条即不命中 */
@@ -168,4 +206,25 @@
168
206
  [dir='rtl'] [data-scope='breadcrumb'][data-part='separator']:empty::before {
169
207
  rotate: 180deg;
170
208
  }
209
+
210
+ @media (forced-colors: active) {
211
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
212
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
213
+ 条目面上取 CanvasText;按下与没被高亮时的悬停,家族把条目涂成 Highlight,字形换 HighlightText;
214
+ 落在 Canvas 面上的取 CanvasText;
215
+ 禁用取 GrayText */
216
+ [data-scope='breadcrumb'][data-part='ellipsis-trigger']:empty::before,
217
+ [data-scope='breadcrumb'][data-part='separator']:empty::before {
218
+ forced-color-adjust: none;
219
+ background-color: CanvasText;
220
+ }
221
+
222
+ [data-scope='breadcrumb'][data-part='ellipsis-trigger']:not([aria-disabled='true'], [data-disabled], [aria-busy='true'], [data-error]):is(:active, [data-pressed], :hover:not(:focus-visible, [data-highlighted])):empty::before {
223
+ background-color: HighlightText;
224
+ }
225
+
226
+ [data-scope='breadcrumb'][data-part='ellipsis-trigger']:is([aria-disabled='true'], [data-disabled]):empty::before {
227
+ background-color: GrayText;
228
+ }
229
+ }
171
230
  }
@@ -204,17 +204,17 @@
204
204
  position: relative;
205
205
  }
206
206
 
207
- /* 后一段往回挪一个描边宽度,相邻的两条边重合成一条。
207
+ /* 共边只画一次:后一段不画起始边,由前一段的末端边承担。墨色描边半透明,两条重合会加深。
208
208
  方向跟着 data-orientation 走,横排收 inline 轴、竖排收 block 轴。
209
- 分隔线两侧不合边:那两段本来就是被这条线分开的,再压一次会把线盖掉一半 */
209
+ 分隔线两侧不合边:那两段本来就是被这条线分开的 */
210
210
  [data-scope='button-group'][data-part='root'][data-orientation='horizontal']
211
211
  > *:not([data-xh-button-group-separator]) + *:not([data-xh-button-group-separator]) {
212
- margin-inline-start: calc(-1 * var(--xh-stroke-thin));
212
+ border-inline-start-width: 0;
213
213
  }
214
214
 
215
215
  [data-scope='button-group'][data-part='root'][data-orientation='vertical']
216
216
  > *:not([data-xh-button-group-separator]) + *:not([data-xh-button-group-separator]) {
217
- margin-block-start: calc(-1 * var(--xh-stroke-thin));
217
+ border-block-start-width: 0;
218
218
  }
219
219
 
220
220
  /* 悬停或拿到焦点的那一段抬到最上层:焦点环画在元素外侧,
@@ -225,7 +225,7 @@
225
225
  }
226
226
 
227
227
  /* 独立 Button 按下时缩一档;组内段却靠共边重叠一像素,单段缩放会同时撕开两侧接缝。
228
- 组内仍保留 active 换底与撤高光,只把会改变外轮廓几何的 scale 收掉(§9.2 允许的只换面);
228
+ 组内仍保留 active 换底与撤高光,只把会改变外轮廓几何的 scale 收掉(允许的只换面);
229
229
  键盘与触屏的按压由 Headless 投影 data-pressed,与指针 :active 同一档。 */
230
230
  [data-scope='button-group'][data-part='root']
231
231
  > [data-scope='button'][data-part='root']:not([data-disabled]):not([data-loading]):is(:active, [data-pressed]),
package/css/button.css CHANGED
@@ -5,15 +5,8 @@
5
5
 
6
6
  /* 独立 Button 皮肤与 full bundle 都从同一份 Family Recipe 取得布局、状态和命中区。 */
7
7
  @import '../family/action-control.css';
8
-
9
- @layer xihan.motion {
10
- /* 独立皮肤可单独引入,spinner 动画名不能依赖别的组件文件。 */
11
- @keyframes xh-spin {
12
- to {
13
- transform: rotate(360deg);
14
- }
15
- }
16
- }
8
+ @import '../family/loading-ring.css';
9
+ @import '../family/motion.css';
17
10
 
18
11
  @layer xihan.components {
19
12
  [data-scope='button'] {
@@ -36,7 +29,7 @@
36
29
  --xh-action-fg-loading: var(--xh-button-fg, var(--xh-_action-variant-fg-loading));
37
30
  --xh-action-highlight-rest: transparent;
38
31
  --xh-action-highlight-pressed: transparent;
39
- /* 海拔只有 solid 一档有:其余形态按 §8.3 无影,私有槽在下面的 solid 块里才被赋值 */
32
+ /* 海拔只有 solid 一档有:其余形态无影,私有槽在下面的 solid 块里才被赋值 */
40
33
  --xh-_button-shadow-rest: var(--xh-button-shadow, none);
41
34
  --xh-_button-shadow-hover: var(--xh-button-shadow-hover, none);
42
35
  --xh-_button-shadow-pressed: none;
@@ -57,7 +50,7 @@
57
50
  font-weight: var(--xh-button-font-weight, var(--xh-text-label-weight));
58
51
  }
59
52
 
60
- /* Button soft 是 §5.3 登记的 raised 消费者:solid 一档静息带顶光与贴地软影,悬停抬到 raised,按下只留顶光。
53
+ /* Button soft 是登记在册的 raised 消费者:solid 一档静息带顶光与贴地软影,悬停抬到 raised,按下只留顶光。
61
54
  成组时段与段共边焊成一条,静息不落软影、悬停不抬起——投影会压到左右两段的面上,
62
55
  ButtonGroup 用 --xh-_button-group-shadow-rest / -hover 只留顶光 */
63
56
  [data-scope='button'][data-part='root'][data-variant='solid'] {
@@ -81,6 +74,36 @@
81
74
  pointer-events: none;
82
75
  }
83
76
 
77
+ /* liquid 档的交互光:实心钮进入悬停时,一道光沿 1px 描边环扫过一次。环与焦点环同法,用透明描边画,
78
+ 遮罩只留描边那一圈。光只走描边、不进面——
79
+ 面内叠一层白光,品牌实心面上白字的对比就从 5.08:1 往下掉。光用面上前景色(currentColor),
80
+ 各语气自动跟随;只在细指针可悬停的设备上播,粗指针下 ::after 另有用处(44px 热区)。
81
+ 减弱动效下时长归 1ms,一闪即过、看不见;强制色下系统配色,不画装饰光 */
82
+ @media (hover: hover) and (pointer: fine) and (forced-colors: none) {
83
+ :where([data-material='liquid'])
84
+ [data-scope='button'][data-part='root'][data-xh-action-variant='solid']:not([data-disabled]):not([data-loading]):hover::after {
85
+ content: '';
86
+ position: absolute;
87
+ inset: calc(-1 * var(--xh-stroke-thin));
88
+ border: var(--xh-stroke-thin) solid transparent;
89
+ border-radius: inherit;
90
+ background: linear-gradient(100deg, transparent 42%, currentColor 50%, transparent 58%) border-box no-repeat;
91
+ background-size: 300% 100%;
92
+ background-position: 0% 0;
93
+ pointer-events: none;
94
+ mask:
95
+ linear-gradient(currentColor 0 0) padding-box exclude,
96
+ linear-gradient(currentColor 0 0);
97
+ animation: xh-glint var(--xh-button-glint-duration, var(--xh-motion-duration-glint)) var(--xh-motion-ease-enter) both;
98
+ }
99
+
100
+ /* 从右往左书写时光从行首(右缘)扫起 */
101
+ :where([data-material='liquid'])
102
+ [data-scope='button'][data-part='root'][data-xh-action-variant='solid']:not([data-disabled]):not([data-loading]):dir(rtl):hover::after {
103
+ animation-direction: reverse;
104
+ }
105
+ }
106
+
84
107
  /* icon profile 已经给出正方视觉盒;Button 的公开 px 槽不能把它重新撑开。 */
85
108
  [data-scope='button'][data-part='root'][data-icon-only] {
86
109
  --xh-action-padding-inline: 0;
@@ -119,25 +142,50 @@
119
142
  block-size: var(--xh-icon-size);
120
143
  }
121
144
 
122
- /* indicator 常驻占位;loading 只切换绘制,不靠删节点改变按钮宽度。 */
145
+ /* 在途指示压在钮上、不进排布,钮宽不变。部件空着时环由加载环配方画在 ::before 上(连接层投影
146
+ data-xh-loading-ring="overlay"),进入在途一个 micro 之后才淡入,退出不等 */
123
147
  [data-scope='button'][data-part='indicator'] {
124
- visibility: hidden;
148
+ position: absolute;
149
+ inset: 0;
150
+ margin: auto;
151
+ pointer-events: none;
152
+ }
153
+
154
+ /* 作者往部件里放了自己的图形:环让位,作者图形按同一节拍淡入淡出并转 */
155
+ [data-scope='button'][data-part='indicator']:not(:empty)::before {
156
+ content: none;
157
+ }
158
+
159
+ [data-scope='button'][data-part='indicator'] > * {
160
+ opacity: 0;
161
+ transition: opacity var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
162
+ animation: xh-spin var(--xh-button-spin-duration, var(--xh-motion-loop-spin)) var(--xh-motion-ease-loop) infinite paused;
163
+ }
164
+
165
+ [data-scope='button'][data-part='root'][data-loading] > [data-scope='button'][data-part='indicator'] > * {
166
+ opacity: 1;
167
+ transition-delay: var(--xh-motion-duration-micro);
168
+ animation-play-state: running;
169
+ }
170
+
171
+ /* 钮里原有的内容与环交叉:进入在途同样等一个 micro 才淡出、留位不塌,退出不等 */
172
+ [data-scope='button']:is([data-part='label'], [data-part='prefix'], [data-part='suffix']) {
173
+ transition: opacity var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
125
174
  }
126
175
 
127
- [data-scope='button'][data-part='root'][data-loading] [data-scope='button'][data-part='indicator'] {
128
- visibility: visible;
129
- animation: xh-spin var(--xh-button-spin-duration, var(--xh-spin-duration)) var(--xh-motion-ease-loop) infinite;
176
+ [data-scope='button'][data-part='root'][data-loading]:has(> [data-scope='button'][data-part='indicator'])
177
+ > [data-scope='button']:is([data-part='label'], [data-part='prefix'], [data-part='suffix']) {
178
+ opacity: 0;
179
+ transition-delay: var(--xh-motion-duration-micro);
130
180
  }
131
181
 
132
182
  @media (prefers-reduced-motion: reduce) {
133
- [data-scope='button'][data-part='root'][data-loading] [data-scope='button'][data-part='indicator'] {
183
+ [data-scope='button'][data-part='indicator'] > * {
134
184
  animation: none;
135
185
  }
136
186
  }
137
187
 
138
- :where([data-motion='reduce'])
139
- [data-scope='button'][data-part='root'][data-loading]
140
- [data-scope='button'][data-part='indicator'] {
188
+ :where([data-motion='reduce']) [data-scope='button'][data-part='indicator'] > * {
141
189
  animation: none;
142
190
  }
143
191
 
@@ -160,7 +208,7 @@
160
208
  box-shadow: none;
161
209
  }
162
210
 
163
- [data-scope='button'][data-part='root'][data-loading] [data-scope='button'][data-part='indicator'] {
211
+ [data-scope='button'][data-part='indicator'] > * {
164
212
  animation: none;
165
213
  }
166
214
  }