@xihan-ui/styles 1.0.0 → 2.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 (171) hide show
  1. package/CHANGELOG.md +5146 -6
  2. package/README.md +22 -2
  3. package/component-tokens.d.ts +4212 -0
  4. package/component-tokens.js +2 -0
  5. package/components.tokens.json +80116 -0
  6. package/css/accordion.css +131 -75
  7. package/css/affix.css +16 -1
  8. package/css/alert.css +114 -67
  9. package/css/anchor.css +99 -27
  10. package/css/approval.css +461 -0
  11. package/css/avatar-group.css +14 -6
  12. package/css/avatar.css +33 -9
  13. package/css/back-top.css +103 -48
  14. package/css/badge.css +42 -14
  15. package/css/bar-code.css +62 -0
  16. package/css/breadcrumb.css +40 -21
  17. package/css/button-group.css +146 -24
  18. package/css/button.css +116 -99
  19. package/css/calendar-picker.css +448 -0
  20. package/css/calendar-range-picker.css +517 -0
  21. package/css/card.css +54 -77
  22. package/css/carousel.css +412 -68
  23. package/css/cascader.css +429 -246
  24. package/css/checkbox-group.css +225 -77
  25. package/css/checkbox.css +174 -37
  26. package/css/clipboard.css +266 -47
  27. package/css/code-view.css +279 -0
  28. package/css/collapsible.css +108 -63
  29. package/css/color-field.css +158 -0
  30. package/css/color-picker.css +172 -184
  31. package/css/color-slider.css +274 -0
  32. package/css/color-swatch-picker.css +243 -0
  33. package/css/color-swatch.css +35 -0
  34. package/css/combobox.css +267 -273
  35. package/css/command.css +399 -0
  36. package/css/context-menu.css +183 -108
  37. package/css/date-field.css +94 -121
  38. package/css/date-picker.css +486 -249
  39. package/css/date-range-picker.css +559 -0
  40. package/css/description.css +51 -0
  41. package/css/descriptions.css +133 -87
  42. package/css/dialog.css +247 -68
  43. package/css/diff-view.css +428 -0
  44. package/css/download-trigger.css +155 -46
  45. package/css/drawer.css +194 -111
  46. package/css/editable.css +172 -152
  47. package/css/empty-state.css +80 -11
  48. package/css/field-array.css +190 -0
  49. package/css/field.css +84 -31
  50. package/css/fieldset.css +51 -18
  51. package/css/file-upload.css +134 -36
  52. package/css/flex.css +20 -1
  53. package/css/float-button.css +238 -57
  54. package/css/floating-panel.css +173 -92
  55. package/css/focus.css +46 -0
  56. package/css/forced-colors.css +93 -0
  57. package/css/form.css +254 -77
  58. package/css/gradient-text.css +34 -10
  59. package/css/grid.css +516 -5
  60. package/css/heatmap.css +87 -29
  61. package/css/highlight.css +20 -5
  62. package/css/hover-card.css +97 -47
  63. package/css/icon-wrapper.css +15 -3
  64. package/css/icon.css +74 -8
  65. package/css/image-cropper.css +143 -29
  66. package/css/image-viewer.css +150 -89
  67. package/css/image.css +30 -1
  68. package/css/infinite-scroll.css +51 -1
  69. package/css/input-group.css +277 -0
  70. package/css/json-viewer.css +102 -45
  71. package/css/kbd.css +72 -0
  72. package/css/label.css +78 -0
  73. package/css/layers.css +5 -0
  74. package/css/layout.css +175 -31
  75. package/css/list.css +24 -3
  76. package/css/listbox.css +208 -67
  77. package/css/loading-bar.css +52 -3
  78. package/css/log.css +144 -9
  79. package/css/markdown-stream.css +149 -0
  80. package/css/marquee.css +27 -1
  81. package/css/masonry.css +7 -0
  82. package/css/matrix-code.css +67 -0
  83. package/css/mention.css +234 -152
  84. package/css/menu.css +180 -83
  85. package/css/menubar.css +204 -136
  86. package/css/message-feed.css +197 -0
  87. package/css/navigation-menu.css +144 -90
  88. package/css/notification.css +178 -100
  89. package/css/number-animation.css +7 -0
  90. package/css/number-field.css +120 -277
  91. package/css/overlay-arrow.css +12 -7
  92. package/css/page-header.css +99 -14
  93. package/css/pagination.css +175 -135
  94. package/css/password-input.css +222 -219
  95. package/css/pin-input.css +101 -67
  96. package/css/pointer.css +36 -0
  97. package/css/popconfirm.css +185 -94
  98. package/css/popover.css +121 -77
  99. package/css/progress.css +60 -12
  100. package/css/prompt-input.css +224 -0
  101. package/css/question-flow.css +513 -0
  102. package/css/radio-group.css +147 -30
  103. package/css/rating.css +193 -43
  104. package/css/reasoning.css +336 -0
  105. package/css/reset.css +84 -6
  106. package/css/resizable.css +237 -0
  107. package/css/scroll-area.css +68 -7
  108. package/css/scrollbar.css +47 -13
  109. package/css/segmented.css +94 -29
  110. package/css/select.css +288 -274
  111. package/css/separator.css +101 -1
  112. package/css/side-nav.css +133 -75
  113. package/css/signature-pad.css +74 -54
  114. package/css/skeleton.css +84 -11
  115. package/css/slider.css +132 -39
  116. package/css/sortable.css +198 -0
  117. package/css/spinner.css +126 -12
  118. package/css/splitter.css +29 -8
  119. package/css/statistic.css +55 -0
  120. package/css/steps.css +206 -72
  121. package/css/switch.css +268 -36
  122. package/css/table.css +695 -118
  123. package/css/tabs.css +604 -42
  124. package/css/tag-group.css +219 -0
  125. package/css/tag.css +118 -28
  126. package/css/tags-input.css +156 -274
  127. package/css/text-field.css +101 -249
  128. package/css/time-field.css +100 -128
  129. package/css/time-picker.css +323 -252
  130. package/css/time-range-picker.css +719 -0
  131. package/css/timeline.css +202 -68
  132. package/css/timer.css +56 -52
  133. package/css/timestamp.css +34 -0
  134. package/css/toast.css +373 -144
  135. package/css/toggle-group.css +309 -76
  136. package/css/toggle.css +116 -85
  137. package/css/tone.css +47 -0
  138. package/css/tool-call.css +421 -0
  139. package/css/toolbar.css +181 -33
  140. package/css/tooltip.css +107 -36
  141. package/css/tour.css +183 -152
  142. package/css/transfer.css +351 -138
  143. package/css/tree-select.css +323 -216
  144. package/css/tree.css +294 -62
  145. package/css/truncate.css +65 -0
  146. package/css/typography.css +228 -5
  147. package/css/undefined.css +10 -1
  148. package/css/virtualizer.css +9 -10
  149. package/css/visually-hidden.css +7 -0
  150. package/css/watermark.css +7 -0
  151. package/family/action-control.css +487 -0
  152. package/family/collection-item.css +721 -0
  153. package/family/field-chrome.css +298 -0
  154. package/family/motion.css +166 -0
  155. package/family/swatch.css +59 -0
  156. package/index.css +41705 -126
  157. package/index.unlayered.css +33692 -17339
  158. package/package.json +51 -17
  159. package/css/calendar.css +0 -367
  160. package/css/code-block.css +0 -91
  161. package/css/composer.css +0 -134
  162. package/css/countdown.css +0 -26
  163. package/css/dynamic-input.css +0 -220
  164. package/css/ellipsis.css +0 -52
  165. package/css/hotkeys.css +0 -90
  166. package/css/popselect.css +0 -391
  167. package/css/qr-code.css +0 -55
  168. package/css/result.css +0 -119
  169. package/css/space.css +0 -117
  170. package/css/thread.css +0 -120
  171. package/css/time.css +0 -25
@@ -1,3 +1,10 @@
1
+ /*
2
+ * Copyright (c) 2021-Present XiHanFun and contributors.
3
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
4
+ */
5
+
6
+ /* 定义 button group 样式。 */
7
+
1
8
  @layer xihan.components {
2
9
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
10
  [data-scope='button-group'] {
@@ -6,13 +13,31 @@
6
13
 
7
14
  /* 一组按钮连成一条:段与段之间不留缝,靠共边接上,所以不设 gap。
8
15
  排布只能读 data-orientation——role=group 不收 aria-orientation,连接层根本不输出它。
9
- 圆角槽位在容器上先收成 0,组内每段都取得到,两端的圆角再逐段补回来 */
16
+ 圆角在容器上先收成 0,组内每段都取得到,两端的圆角再逐段补回来。
17
+
18
+ 排不下时这里不折行、也不自己翻成竖排,两条都是有意的:
19
+ 折行会把两端的圆角切在行末与行首——圆角只补在 :first-child 与 :last-child 上,
20
+ 中间那两个角是直角,一条焊死的按钮条断在半路。
21
+ 皮肤自己翻成竖排则会与 data-orientation 脱钩:合边的负外边距收哪个轴、
22
+ 两端圆角补哪一对角、连接层给分隔线派的朝向,三处全部由 data-orientation 决定,
23
+ 只有排布偷偷换了轴,线会横竖不对、角会补在错的两端。
24
+ 朝向由使用者显式给 orientation="vertical",三处才一起换过去。 */
10
25
  [data-scope='button-group'][data-part='root'] {
11
26
  /* 组件内那几档层号只在自己人之间排序:建一个层叠上下文把它们关起来,
12
27
  既不让页面上的层号插进来,也不让它们漏出去压住别人 */
13
28
  isolation: isolate;
14
29
 
15
- --xh-button-radius: 0;
30
+ /* 与下面几档同理,写私有槽而非 --xh-button-radius
31
+ 后者是使用者的入口,写死它,进了组的胶囊按钮就再也圆不起来 */
32
+ --xh-_button-group-radius: 0;
33
+
34
+ /* 段与段共边焊成一条,solid 段静息不落贴地软影、悬停那一档不抬:抬起其中一段,投影会压到
35
+ 左右两段的面上。两档都只留实心面顶边那条高光;其余形态的影本来就是 none,这两支只被
36
+ button.css 的 solid 块消费。颜色不在这里灌:段自带 data-xh-action-variant,底 / 前景 / 描边由
37
+ 家族形态矩阵按段自己的形态给出 */
38
+ --xh-_button-group-shadow-rest: var(--xh-_highlight-tone);
39
+ --xh-_button-group-shadow-hover: var(--xh-_highlight-tone);
40
+ --xh-_group-separator-size: calc(var(--xh-_button-group-h, var(--xh-control-h-md)) / 2);
16
41
 
17
42
  display: inline-flex;
18
43
  align-items: stretch;
@@ -26,12 +51,82 @@
26
51
  flex-direction: column;
27
52
  }
28
53
 
54
+ /* 撑满行宽:整组占满可用宽度,每段等分剩余空间。inline-flex 改成 flex:行内盒撑不满一行 */
55
+ [data-scope='button-group'][data-part='root'][data-full-width] {
56
+ display: flex;
57
+ inline-size: 100%;
58
+ }
59
+
60
+ /* 分隔线不参与等分:它有自己的厚度,跟着拉伸就成了一条粗带 */
61
+ [data-scope='button-group'][data-part='root'][data-full-width] > *:not([data-xh-button-group-separator]) {
62
+ flex: 1;
63
+ }
64
+
29
65
  /* 给了 display 的角色节点必须补这条:display 声明会盖掉 UA 的 [hidden]{display:none},
30
66
  不补的话作者给节点加 hidden 后它照样显示 */
31
67
  [data-scope='button-group'][data-part='root'][hidden] {
32
68
  display: none;
33
69
  }
34
70
 
71
+ /* —— 段间的线 —— */
72
+ /* 装饰性的一条线,读屏不念。段本来靠共边连成一体,插了它就是把一排按钮分成几组。
73
+ 朝向是这条线自己的(横排里是竖线),与 root 的排布相反 */
74
+ [data-scope='button-group'][data-part='root'] > [data-xh-button-group-separator] {
75
+ flex: none;
76
+ align-self: center;
77
+ z-index: 1;
78
+ border-radius: var(--xh-button-group-separator-radius, var(--xh-shape-pill));
79
+ background: var(--xh-button-group-separator-color, var(--xh-fg-default));
80
+ opacity: var(--xh-button-group-separator-opacity, var(--xh-control-separator-opacity));
81
+ pointer-events: none;
82
+ transition:
83
+ background-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
84
+ opacity var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
85
+ }
86
+
87
+ [data-scope='button-group'][data-part='root'] > [data-xh-button-group-separator][data-orientation='vertical'] {
88
+ inline-size: var(--xh-button-group-separator-thickness, var(--xh-stroke-thin));
89
+ block-size: var(
90
+ --xh-button-group-separator-size,
91
+ var(--xh-_group-separator-size)
92
+ );
93
+ margin-inline-start: calc(-1 * var(--xh-button-group-separator-thickness, var(--xh-stroke-thin)));
94
+ }
95
+
96
+ [data-scope='button-group'][data-part='root'] > [data-xh-button-group-separator][data-orientation='horizontal'] {
97
+ block-size: var(--xh-button-group-separator-thickness, var(--xh-stroke-thin));
98
+ inline-size: var(
99
+ --xh-button-group-separator-size,
100
+ var(--xh-_group-separator-size)
101
+ );
102
+ margin-block-start: calc(-1 * var(--xh-button-group-separator-thickness, var(--xh-stroke-thin)));
103
+ }
104
+
105
+ /* 整组禁用:段真正的禁用由适配器落到每一段的原生 disabled 上,
106
+ 置灰的原生按钮不收指针事件,指针形状只能由这一层给 */
107
+ [data-scope='button-group'][data-part='root'][data-disabled] {
108
+ cursor: not-allowed;
109
+ }
110
+
111
+ /* 段都按不动了,段间那条线跟着退到弱边一档;换色按 micro 走一遍,
112
+ 与两侧那几段自己的禁用过渡同拍 */
113
+ [data-scope='button-group'][data-part='root'] > [data-xh-button-group-separator][data-disabled] {
114
+ background: var(--xh-button-group-separator-color-disabled, var(--xh-border-subtle));
115
+ opacity: var(--xh-button-group-separator-opacity-disabled, var(--xh-control-separator-disabled-opacity));
116
+ }
117
+
118
+ @media (forced-colors: active) {
119
+ /* 段间那条线画的是一块纯底色,这一档里底色被统一换成 Canvas,线整条消失,
120
+ 几组按钮就连成了一长串。换成前景色画回来 */
121
+ [data-scope='button-group'][data-part='root'] > [data-xh-button-group-separator] {
122
+ background: CanvasText;
123
+ }
124
+
125
+ [data-scope='button-group'][data-part='root'][data-variant='outline']::after {
126
+ border-color: ButtonText;
127
+ }
128
+ }
129
+
35
130
  /* —— 尺寸 —— */
36
131
  /* 高度、内边距、间距、字号在容器上写一次,沿继承流给组内每一段。
37
132
  三档都写出来:自定义属性会一路继承,组里再套一组时内层不写就会沿用外层的档。
@@ -58,34 +153,48 @@
58
153
  }
59
154
 
60
155
  /* —— 形态 —— */
61
- /* 只写按钮暴露出来的颜色槽位:段自己写了形态时走的是它自己那条规则,这里的槽位不参与。
62
- 用哪族颜色由根上的 data-tone 决定,语气槽沿继承流下来,没写语气就沿用品牌/中性配色 */
156
+ /* 组的形态由适配器下发到每一段(段自己写了的优先),段的颜色由家族形态矩阵给出;
157
+ 这里只定段间那条线在各档面上的颜色与浓淡,用哪族颜色由根上的 data-tone 决定 */
63
158
  [data-scope='button-group'][data-part='root'][data-variant='solid'] {
64
- --xh-button-bg: var(--xh-_tone, var(--xh-bg-brand));
65
- --xh-button-bg-hover: var(--xh-_tone-hover, var(--xh-bg-brand-hover));
66
- --xh-button-bg-active: var(--xh-_tone-active, var(--xh-bg-brand-active));
67
- --xh-button-fg: var(--xh-_tone-on, var(--xh-fg-on-brand));
159
+ --xh-button-group-separator-color: var(--xh-_tone-on, var(--xh-fg-on-brand));
160
+ --xh-button-group-separator-opacity: 0.24;
68
161
  }
69
162
 
70
163
  [data-scope='button-group'][data-part='root'][data-variant='subtle'] {
71
- --xh-button-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
72
- --xh-button-bg-hover: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover));
73
- --xh-button-bg-active: var(--xh-_tone-subtle-active, var(--xh-bg-subtle-active));
74
- --xh-button-fg: var(--xh-_tone-fg, var(--xh-fg-default));
164
+ --xh-button-group-separator-color: var(--xh-_tone-fg, var(--xh-fg-default));
165
+ --xh-button-group-separator-opacity: 0.16;
166
+ }
167
+
168
+ [data-scope='button-group'][data-part='root'][data-variant='outline'] {
169
+ --xh-button-group-separator-color: var(--xh-_tone-border-control, var(--xh-border-control));
170
+ --xh-button-group-separator-opacity: 1;
171
+
172
+ position: relative;
173
+ border-radius: var(--xh-button-group-radius, var(--xh-shape-control));
174
+ }
175
+
176
+ [data-scope='button-group'][data-part='root'][data-variant='outline']::after {
177
+ content: '';
178
+ position: absolute;
179
+ z-index: 2;
180
+ inset: 0;
181
+ border: var(--xh-stroke-thin) solid
182
+ var(--xh-button-group-outline-color, var(--xh-_tone-border-control, var(--xh-border-control)));
183
+ border-radius: inherit;
184
+ pointer-events: none;
75
185
  }
76
186
 
77
- [data-scope='button-group'][data-part='root'][data-variant='outline'],
78
187
  [data-scope='button-group'][data-part='root'][data-variant='ghost'] {
79
- --xh-button-bg: transparent;
80
- --xh-button-bg-hover: var(--xh-_tone-subtle, var(--xh-bg-subtle));
81
- --xh-button-bg-active: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-active));
82
- --xh-button-fg: var(--xh-_tone-fg, var(--xh-fg-default));
188
+ --xh-button-group-separator-color: var(--xh-_tone-fg, var(--xh-fg-default));
189
+ --xh-button-group-separator-opacity: 0.16;
83
190
  }
84
191
 
85
- /* 段的描边归组画(两个适配器各一条选择器:Vue 的段就是直接子节点,WC 的段在行为宿主里面):合边本来就是组的事,按钮那边没有描边色的槽位可以流 */
86
- [data-scope='button-group'][data-part='root'][data-variant='outline'] > *,
87
- [data-scope='button-group'][data-part='root'][data-variant='outline'] > * [data-scope='button'][data-part='root'] {
88
- border-color: var(--xh-_tone-border-control, var(--xh-border-control));
192
+ /* outline 的外框由组根统一绘制,组内每一段都不再各画一圈边框(段的形态矩阵描边被这一条压平)。 */
193
+ [data-scope='button-group'][data-part='root'][data-variant='outline']
194
+ > [data-scope='button'][data-part='root'],
195
+ [data-scope='button-group'][data-part='root'][data-variant='outline']
196
+ > * > [data-scope='button'][data-part='root'] {
197
+ border-color: transparent;
89
198
  }
90
199
 
91
200
  /* —— 合边 —— */
@@ -96,12 +205,15 @@
96
205
  }
97
206
 
98
207
  /* 后一段往回挪一个描边宽度,相邻的两条边重合成一条。
99
- 方向跟着 data-orientation 走,横排收 inline 轴、竖排收 block 轴 */
100
- [data-scope='button-group'][data-part='root'][data-orientation='horizontal'] > * + * {
208
+ 方向跟着 data-orientation 走,横排收 inline 轴、竖排收 block 轴。
209
+ 分隔线两侧不合边:那两段本来就是被这条线分开的,再压一次会把线盖掉一半 */
210
+ [data-scope='button-group'][data-part='root'][data-orientation='horizontal']
211
+ > *:not([data-xh-button-group-separator]) + *:not([data-xh-button-group-separator]) {
101
212
  margin-inline-start: calc(-1 * var(--xh-stroke-thin));
102
213
  }
103
214
 
104
- [data-scope='button-group'][data-part='root'][data-orientation='vertical'] > * + * {
215
+ [data-scope='button-group'][data-part='root'][data-orientation='vertical']
216
+ > *:not([data-xh-button-group-separator]) + *:not([data-xh-button-group-separator]) {
105
217
  margin-block-start: calc(-1 * var(--xh-stroke-thin));
106
218
  }
107
219
 
@@ -112,6 +224,16 @@
112
224
  z-index: 1;
113
225
  }
114
226
 
227
+ /* 独立 Button 按下时缩一档;组内段却靠共边重叠一像素,单段缩放会同时撕开两侧接缝。
228
+ 组内仍保留 active 换底与撤高光,只把会改变外轮廓几何的 scale 收掉(§9.2 允许的只换面);
229
+ 键盘与触屏的按压由 Headless 投影 data-pressed,与指针 :active 同一档。 */
230
+ [data-scope='button-group'][data-part='root']
231
+ > [data-scope='button'][data-part='root']:not([data-disabled]):not([data-loading]):is(:active, [data-pressed]),
232
+ [data-scope='button-group'][data-part='root']
233
+ > * > [data-scope='button'][data-part='root']:not([data-disabled]):not([data-loading]):is(:active, [data-pressed]) {
234
+ scale: none;
235
+ }
236
+
115
237
  /* —— 两端圆角 —— */
116
238
  /* 逻辑角属性写,rtl 与竖排都跟着换边,不必另写一套。
117
239
  只认直接子节点:段之间插了别的节点时宁可退化成直角,也不要圆错位置 */
package/css/button.css CHANGED
@@ -1,6 +1,13 @@
1
+ /*
2
+ * Copyright (c) 2021-Present XiHanFun and contributors.
3
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
4
+ */
5
+
6
+ /* 独立 Button 皮肤与 full bundle 都从同一份 Family Recipe 取得布局、状态和命中区。 */
7
+ @import '../family/action-control.css';
8
+
1
9
  @layer xihan.motion {
2
- /* 动画名的查找只认「文档里有没有这个名字」:引用别处文件里的名字,那份文件不一定在场,
3
- 动画会静默地整个不跑。每份皮肤都能单独引入,所以它用到的关键帧都写在自己身上。 */
10
+ /* 独立皮肤可单独引入,spinner 动画名不能依赖别的组件文件。 */
4
11
  @keyframes xh-spin {
5
12
  to {
6
13
  transform: rotate(360deg);
@@ -9,142 +16,152 @@
9
16
  }
10
17
 
11
18
  @layer xihan.components {
12
- /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
13
19
  [data-scope='button'] {
14
20
  --xh-button-skin: 1;
15
21
  }
16
22
 
23
+ /* 四种形态的底 / 前景 / 描边由家族形态矩阵按 data-xh-action-variant 给出(缺省 solid 由连接层显式投影,
24
+ 只有 Button 缺省品牌实心);这里只把公开颜色槽桥接到矩阵之前:使用者槽 → 形态矩阵 → 家族缺省,
25
+ 再映射几何、海拔与顶光。状态选择器、命中区与尺寸 profile 不在这里复制。 */
17
26
  [data-scope='button'][data-part='root'] {
18
- /* 私有槽位:组件令牌 → 语义令牌两级回退,变体只改这几个槽位 */
19
- --xh-_bg: var(--xh-button-bg, var(--xh-bg-subtle));
20
- --xh-_bg-hover: var(--xh-button-bg-hover, var(--xh-bg-subtle-hover));
21
- --xh-_bg-active: var(--xh-button-bg-active, var(--xh-bg-subtle-active));
22
- --xh-_fg: var(--xh-button-fg, var(--xh-fg-default));
23
-
24
- /* 作者塞进来的图标跟着按钮的尺寸档走:xh-icon 的直径读的就是这个继承槽,
25
- 它自己显式写了 size 时特异性更高,仍以作者写的为准 */
26
- --xh-icon-size: var(--xh-button-icon-size, var(--xh-glyph-size-md));
27
+ --xh-action-bg-rest: var(--xh-button-bg, var(--xh-_action-variant-bg-rest));
28
+ --xh-action-bg-hover: var(--xh-button-bg-hover, var(--xh-_action-variant-bg-hover));
29
+ --xh-action-bg-pressed: var(--xh-button-bg-active, var(--xh-_action-variant-bg-pressed));
30
+ --xh-action-bg-focus-visible: var(--xh-button-bg, var(--xh-_action-variant-bg-focus-visible));
31
+ --xh-action-bg-loading: var(--xh-button-bg, var(--xh-_action-variant-bg-loading));
32
+ --xh-action-fg-rest: var(--xh-button-fg, var(--xh-_action-variant-fg-rest));
33
+ --xh-action-fg-hover: var(--xh-button-fg, var(--xh-_action-variant-fg-hover));
34
+ --xh-action-fg-pressed: var(--xh-button-fg, var(--xh-_action-variant-fg-pressed));
35
+ --xh-action-fg-focus-visible: var(--xh-button-fg, var(--xh-_action-variant-fg-focus-visible));
36
+ --xh-action-fg-loading: var(--xh-button-fg, var(--xh-_action-variant-fg-loading));
37
+ --xh-action-highlight-rest: transparent;
38
+ --xh-action-highlight-pressed: transparent;
39
+ /* 海拔只有 solid 一档有:其余形态按 §8.3 无影,私有槽在下面的 solid 块里才被赋值 */
40
+ --xh-_button-shadow-rest: var(--xh-button-shadow, none);
41
+ --xh-_button-shadow-hover: var(--xh-button-shadow-hover, none);
42
+ --xh-_button-shadow-pressed: none;
43
+ --xh-action-shadow-rest: var(--xh-_button-shadow-rest);
44
+ --xh-action-shadow-hover: var(--xh-_button-shadow-hover);
45
+ --xh-action-shadow-pressed: var(--xh-_button-shadow-pressed);
46
+ --xh-action-radius: var(--xh-button-radius, var(--xh-_button-radius));
47
+ /* 成组时几何从 ButtonGroup 的私有槽取:高度、内距、间距与字号沿继承流下来 */
48
+ --xh-action-visual-size: var(--xh-button-h, var(--xh-_button-group-h, var(--xh-_action-profile-visual-size)));
49
+ --xh-action-padding-inline: var(--xh-button-px, var(--xh-_button-group-px, var(--xh-_action-profile-padding-inline)));
50
+ --xh-action-gap: var(--xh-button-gap, var(--xh-_button-group-gap, var(--xh-_action-profile-gap)));
51
+ --xh-action-font-size: var(--xh-button-font-size, var(--xh-_button-group-font-size, var(--xh-_action-profile-font-size)));
52
+ /* 圆角只有一个身份:普通 control 的 4px,与 toggle / button-group 一致,按钮没有 shape 轴;
53
+ 成组时由按钮组接管相接侧,要换档写 --xh-button-radius */
54
+ --xh-_button-radius: var(--xh-_button-group-radius, var(--xh-shape-control));
55
+ --xh-icon-size: var(--xh-button-icon-size, var(--xh-_action-profile-glyph-size));
27
56
 
28
- display: inline-flex;
29
- align-items: center;
30
- justify-content: center;
31
- gap: var(--xh-button-gap, var(--xh-_button-group-gap, var(--xh-control-gap-md)));
32
- block-size: var(--xh-button-h, var(--xh-_button-group-h, var(--xh-control-h-md)));
33
- padding-inline: var(--xh-button-px, var(--xh-_button-group-px, var(--xh-control-px-md)));
34
- border: var(--xh-stroke-thin) solid transparent;
35
- border-radius: var(--xh-button-radius, var(--xh-shape-control));
36
- background: var(--xh-_bg);
37
- color: var(--xh-_fg);
38
- font-size: var(--xh-button-font-size, var(--xh-_button-group-font-size, var(--xh-text-label-size)));
39
57
  font-weight: var(--xh-button-font-weight, var(--xh-text-label-weight));
40
- line-height: var(--xh-leading-none);
41
- white-space: nowrap;
42
- cursor: pointer;
43
- user-select: none;
44
- transition:
45
- background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
46
- border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
47
- }
48
-
49
- [data-scope='button'][data-part='root']:hover {
50
- background: var(--xh-_bg-hover);
51
- }
52
-
53
- [data-scope='button'][data-part='root']:active {
54
- background: var(--xh-_bg-active);
55
58
  }
56
59
 
57
- [data-scope='button'][data-part='root']:focus-visible {
58
- outline: var(--xh-ring-width) solid var(--xh-ring-focus);
59
- outline-offset: var(--xh-ring-offset);
60
+ /* Button soft 是 §5.3 登记的 raised 消费者:solid 一档静息带顶光与贴地软影,悬停抬到 raised,按下只留顶光。
61
+ 成组时段与段共边焊成一条,静息不落软影、悬停不抬起——投影会压到左右两段的面上,
62
+ ButtonGroup --xh-_button-group-shadow-rest / -hover 只留顶光 */
63
+ [data-scope='button'][data-part='root'][data-variant='solid'] {
64
+ --xh-action-highlight-pressed: var(--xh-_highlight-tone);
65
+ --xh-_button-shadow-rest: var(--xh-_button-group-shadow-rest, var(--xh-_highlight-tone), var(--xh-material-soft-shadow));
66
+ --xh-_button-shadow-hover: var(--xh-_button-group-shadow-hover, var(--xh-_highlight-tone), var(--xh-elevation-raised));
67
+ --xh-_button-shadow-pressed: var(--xh-_highlight-tone);
60
68
  }
61
69
 
62
- [data-scope='button'][data-part='root'][data-disabled],
63
- [data-scope='button'][data-part='root'][data-loading] {
64
- cursor: not-allowed;
65
- opacity: var(--xh-state-disabled-opacity);
70
+ /* 实心面用对比前景作内环,再加品牌外环;不覆盖表面 box-shadow。 */
71
+ [data-scope='button'][data-part='root'][data-xh-action-variant='solid']:focus-visible {
72
+ --xh-action-ring-color-focus-visible: currentColor;
66
73
  }
67
74
 
68
- /* —— 变体 —— */
69
- /* 形态只决定颜色怎么用;用哪族颜色由 data-tone 决定,没写 tone 就沿用原来的品牌/中性配色 */
70
- [data-scope='button'][data-part='root'][data-variant='solid'] {
71
- --xh-_bg: var(--xh-_tone, var(--xh-bg-brand));
72
- --xh-_bg-hover: var(--xh-_tone-hover, var(--xh-bg-brand-hover));
73
- --xh-_bg-active: var(--xh-_tone-active, var(--xh-bg-brand-active));
74
- --xh-_fg: var(--xh-_tone-on, var(--xh-fg-on-brand));
75
+ [data-scope='button'][data-part='root'][data-xh-action-variant='solid']:focus-visible::before {
76
+ content: '';
77
+ position: absolute;
78
+ inset: calc(-1 * (var(--xh-ring-width) + var(--xh-stroke-thin)));
79
+ border: var(--xh-ring-width) solid var(--xh-ring-focus);
80
+ border-radius: inherit;
81
+ pointer-events: none;
75
82
  }
76
83
 
77
- [data-scope='button'][data-part='root'][data-variant='subtle'] {
78
- --xh-_bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
79
- --xh-_bg-hover: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover));
80
- --xh-_bg-active: var(--xh-_tone-subtle-active, var(--xh-bg-subtle-active));
81
- --xh-_fg: var(--xh-_tone-fg, var(--xh-fg-default));
84
+ /* icon profile 已经给出正方视觉盒;Button 的公开 px 槽不能把它重新撑开。 */
85
+ [data-scope='button'][data-part='root'][data-icon-only] {
86
+ --xh-action-padding-inline: 0;
82
87
  }
83
88
 
84
- [data-scope='button'][data-part='root'][data-variant='outline'] {
85
- --xh-_bg: transparent;
86
- --xh-_bg-hover: var(--xh-_tone-subtle, var(--xh-bg-subtle));
87
- --xh-_bg-active: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-active));
88
- --xh-_fg: var(--xh-_tone-fg, var(--xh-fg-default));
89
-
90
- border-color: var(--xh-_tone-border-control, var(--xh-border-control));
89
+ [data-scope='button'][data-part='root'][data-full-width] {
90
+ inline-size: 100%;
91
91
  }
92
92
 
93
- [data-scope='button'][data-part='root'][data-variant='ghost'] {
94
- --xh-_bg: transparent;
95
- --xh-_bg-hover: var(--xh-_tone-subtle, var(--xh-bg-subtle));
96
- --xh-_bg-active: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-active));
97
- --xh-_fg: var(--xh-_tone-fg, var(--xh-fg-default));
93
+ [data-scope='button'][data-part='label'],
94
+ [data-scope='button'][data-part='prefix'],
95
+ [data-scope='button'][data-part='suffix'],
96
+ [data-scope='button'][data-part='indicator'] {
97
+ display: inline-flex;
98
+ align-items: center;
99
+ justify-content: center;
100
+ line-height: inherit;
98
101
  }
99
102
 
100
- /* —— 尺寸 —— */
101
- [data-scope='button'][data-part='root'][data-size='sm'] {
102
- --xh-icon-size: var(--xh-button-icon-size, var(--xh-glyph-size-sm));
103
-
104
- block-size: var(--xh-button-h, var(--xh-control-h-sm));
105
- padding-inline: var(--xh-button-px, var(--xh-control-px-sm));
106
- font-size: var(--xh-button-font-size, var(--xh-control-font-sm));
103
+ [data-scope='button'][data-part='label'] {
104
+ min-inline-size: 0;
107
105
  }
108
106
 
109
- [data-scope='button'][data-part='root'][data-size='lg'] {
110
- --xh-icon-size: var(--xh-button-icon-size, var(--xh-glyph-size-lg));
111
-
112
- block-size: var(--xh-button-h, var(--xh-control-h-lg));
113
- padding-inline: var(--xh-button-px, var(--xh-control-px-lg));
114
- font-size: var(--xh-button-font-size, var(--xh-control-font-lg));
107
+ [data-scope='button'][data-part='prefix'],
108
+ [data-scope='button'][data-part='suffix'],
109
+ [data-scope='button'][data-part='indicator'] {
110
+ flex: none;
111
+ line-height: 0;
115
112
  }
116
113
 
117
- /* 只有图标:左右内距清零,宽度靠比例跟住当前档的高度——换尺寸档时仍是正方形,
118
- 不必把档位抄进行内样式。写在尺寸档之后,压得住三档各自的 padding-inline */
119
- [data-scope='button'][data-part='root'][data-icon-only] {
120
- padding-inline: 0;
121
- aspect-ratio: 1;
114
+ [data-scope='button'][data-part='prefix'] > svg,
115
+ [data-scope='button'][data-part='suffix'] > svg,
116
+ [data-scope='button'][data-part='indicator'],
117
+ [data-scope='button'][data-part='indicator'] > svg {
118
+ inline-size: var(--xh-icon-size);
119
+ block-size: var(--xh-icon-size);
122
120
  }
123
121
 
124
- [data-scope='button'][data-part='root'][data-full-width] {
125
- inline-size: 100%;
122
+ /* indicator 常驻占位;loading 只切换绘制,不靠删节点改变按钮宽度。 */
123
+ [data-scope='button'][data-part='indicator'] {
124
+ visibility: hidden;
126
125
  }
127
126
 
128
- [data-scope='button'][data-part='indicator'] {
129
- display: inline-flex;
130
- animation: xh-spin var(--xh-button-spin-duration, var(--xh-spin-duration)) linear infinite;
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;
131
130
  }
132
131
 
133
- /* 减少动效:指示器停转,"还在忙"由按钮自身的挂起样式与指示器图标继续表达。
134
- 两条规则是同一件事的两个触发条件:系统偏好与作者打的 data-motion */
135
132
  @media (prefers-reduced-motion: reduce) {
136
- [data-scope='button'][data-part='indicator'] {
133
+ [data-scope='button'][data-part='root'][data-loading] [data-scope='button'][data-part='indicator'] {
137
134
  animation: none;
138
135
  }
139
136
  }
140
137
 
141
- :where([data-motion='reduce']) [data-scope='button'][data-part='indicator'] {
138
+ :where([data-motion='reduce'])
139
+ [data-scope='button'][data-part='root'][data-loading]
140
+ [data-scope='button'][data-part='indicator'] {
142
141
  animation: none;
143
142
  }
144
143
 
145
- /* 声明过任何 display 就盖掉了 UA 的 [hidden]{display:none},收起态得自己还回去。
146
- 写在文件末尾:兜底必须排在该部件所有 display 声明之后 */
147
- [data-scope='button'][data-part='root'][hidden] {
144
+ [data-scope='button'][data-part='root'][hidden],
145
+ [data-scope='button'][data-part='label'][hidden],
146
+ [data-scope='button'][data-part='prefix'][hidden],
147
+ [data-scope='button'][data-part='suffix'][hidden],
148
+ [data-scope='button'][data-part='indicator'][hidden] {
148
149
  display: none;
149
150
  }
151
+
152
+ @media (forced-colors: active) {
153
+ [data-scope='button'][data-part='root'][data-xh-action-variant='solid']:focus-visible::before {
154
+ border-color: Highlight;
155
+ }
156
+ }
157
+
158
+ @media print {
159
+ [data-scope='button'][data-part='root'] {
160
+ box-shadow: none;
161
+ }
162
+
163
+ [data-scope='button'][data-part='root'][data-loading] [data-scope='button'][data-part='indicator'] {
164
+ animation: none;
165
+ }
166
+ }
150
167
  }