@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/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,19 +31,6 @@
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
35
  浮在内容之上的单图标圆钮,接 Action Control floating 档:盒型(md = box-lg 48px,圆形)、
47
36
  四态面、0.97 按压与 44px 命中区由家族配方按 data-xh-action-variant / -size 给出。
@@ -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
 
@@ -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
 
@@ -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
  /* 悬停或拿到焦点的那一段抬到最上层:焦点环画在元素外侧,
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'] {
@@ -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
  }
@@ -445,4 +445,23 @@
445
445
  font-weight: var(--xh-font-weight-bold);
446
446
  }
447
447
  }
448
+
449
+ @media (forced-colors: active) {
450
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
451
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
452
+ 按钮面上取 ButtonText,悬停与按下时家族把按钮涂成 Highlight、字形换 HighlightText;
453
+ 禁用取 GrayText */
454
+ [data-scope='calendar-picker']:is([data-part='prev-trigger'], [data-part='next-trigger'], [data-part='prev-year-trigger'], [data-part='next-year-trigger']):empty::before {
455
+ forced-color-adjust: none;
456
+ background-color: ButtonText;
457
+ }
458
+
459
+ [data-scope='calendar-picker']:is([data-part='prev-trigger'], [data-part='next-trigger'], [data-part='prev-year-trigger'], [data-part='next-year-trigger']):not([data-disabled]):not([data-loading]):is(:hover, :active, [data-pressed]):empty::before {
460
+ background-color: HighlightText;
461
+ }
462
+
463
+ [data-scope='calendar-picker']:is([data-part='prev-trigger'], [data-part='next-trigger'], [data-part='prev-year-trigger'], [data-part='next-year-trigger'])[data-disabled]:empty::before {
464
+ background-color: GrayText;
465
+ }
466
+ }
448
467
  }
@@ -245,14 +245,21 @@
245
245
  display: none;
246
246
  }
247
247
 
248
- /* 区间底色:横向铺满格子,相邻两格接成一条;纵向收在格子内衬里,行与行之间留出空当 */
248
+ /* 区间底色:横向铺满格子,相邻两格接成一条;纵向收在格子内衬里,行与行之间留出空当。
249
+ 轨道随 data-in-range 生灭,出现与消失都是当场的;过渡只在同一段轨道换底时走——
250
+ 挑到一半的预览落定成区间,中性淡底淡变到品牌淡底 */
249
251
  [data-scope='calendar-range-picker'][data-part='cell'][data-in-range]:not([data-outside-month])::before {
250
252
  content: '';
251
253
  position: absolute;
252
254
  inset-block: var(--xh-calendar-range-picker-cell-gap, var(--xh-space-0_5));
253
255
  inset-inline: 0;
254
256
  background: var(--xh-calendar-range-picker-range-bg, var(--xh-bg-brand-subtle));
255
- transition: background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
257
+ transition: background-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
258
+ }
259
+
260
+ /* 挑到一半的预览还不是选中:品牌淡底专属选中 / 当前,预览铺中性淡底,与已落定的区间分开 */
261
+ [data-scope='calendar-range-picker'][data-part='cell'][data-in-range][data-range-preview]:not([data-outside-month])::before {
262
+ background: var(--xh-calendar-range-picker-range-preview-bg, var(--xh-bg-subtle));
256
263
  }
257
264
 
258
265
  /* 行首与行尾收圆:区间跨周时每一行各是一条两头圆的横杠。
@@ -354,6 +361,12 @@
354
361
  --xh-_calendar-range-picker-cell-bg-pressed: var(--xh-calendar-range-picker-range-cell-bg-pressed, var(--xh-bg-brand-subtle-active));
355
362
  }
356
363
 
364
+ /* 预览中段坐在中性淡底上:交互阶梯按淡底承载走,悬停 200、按下 300 */
365
+ [data-scope='calendar-range-picker'][data-part='cell-trigger'][data-in-range][data-range-preview]:not([data-outside-month], [data-disabled], [data-range-start], [data-range-end]) {
366
+ --xh-_calendar-range-picker-cell-bg-hover: var(--xh-calendar-range-picker-range-preview-cell-bg-hover, var(--xh-bg-subtle-hover));
367
+ --xh-_calendar-range-picker-cell-bg-pressed: var(--xh-calendar-range-picker-range-preview-cell-bg-pressed, var(--xh-bg-subtle-active));
368
+ }
369
+
357
370
  /* 今天:1px 品牌环 + 品牌字(brand-subtle 退出 today 语义),底透明、悬停走普通格的阶梯;
358
371
  落在区间里时环压在轨道上,选中仍用后面的实心强调面,状态叠加时不会混成同一种反馈 */
359
372
  [data-scope='calendar-range-picker'][data-part='cell-trigger'][data-today] {
@@ -393,6 +406,11 @@
393
406
  border-color: Highlight;
394
407
  }
395
408
 
409
+ /* 预览与落定的区间在这一档没有底色可分:还没确认的那一段在轨道上下沿画虚线 */
410
+ [data-scope='calendar-range-picker'][data-part='cell'][data-in-range][data-range-preview]:not([data-outside-month])::before {
411
+ border-block: var(--xh-stroke-thin) dashed Highlight;
412
+ }
413
+
396
414
  /* 按下换的底在这一档由家族的按压块换成 Highlight(格子、方向钮与标题钮同走),这里不再另画 */
397
415
  }
398
416
 
@@ -514,4 +532,23 @@
514
532
  border-block-end: var(--xh-stroke-thick) solid currentColor;
515
533
  }
516
534
  }
535
+
536
+ @media (forced-colors: active) {
537
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
538
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
539
+ 按钮面上取 ButtonText,悬停与按下时家族把按钮涂成 Highlight、字形换 HighlightText;
540
+ 禁用取 GrayText */
541
+ [data-scope='calendar-range-picker']:is([data-part='prev-trigger'], [data-part='next-trigger'], [data-part='prev-year-trigger'], [data-part='next-year-trigger']):empty::before {
542
+ forced-color-adjust: none;
543
+ background-color: ButtonText;
544
+ }
545
+
546
+ [data-scope='calendar-range-picker']:is([data-part='prev-trigger'], [data-part='next-trigger'], [data-part='prev-year-trigger'], [data-part='next-year-trigger']):not([data-disabled]):not([data-loading]):is(:hover, :active, [data-pressed]):empty::before {
547
+ background-color: HighlightText;
548
+ }
549
+
550
+ [data-scope='calendar-range-picker']:is([data-part='prev-trigger'], [data-part='next-trigger'], [data-part='prev-year-trigger'], [data-part='next-year-trigger'])[data-disabled]:empty::before {
551
+ background-color: GrayText;
552
+ }
553
+ }
517
554
  }