@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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xihan-ui/styles",
3
3
  "type": "module",
4
- "version": "2.1.0",
4
+ "version": "3.1.0",
5
5
  "description": "XiHan.UI styled layer — pure CSS skins driven by data-attributes and design tokens.",
6
6
  "author": {
7
7
  "name": "XiHanFun",
@@ -34,10 +34,15 @@
34
34
  "import": "./component-tokens.js"
35
35
  },
36
36
  "./action-control.css": "./family/action-control.css",
37
+ "./chart.css": "./family/chart.css",
38
+ "./choice-card.css": "./family/choice-card.css",
37
39
  "./collection-item.css": "./family/collection-item.css",
38
40
  "./field-chrome.css": "./family/field-chrome.css",
41
+ "./loading-ring.css": "./family/loading-ring.css",
42
+ "./material.css": "./family/material.css",
39
43
  "./motion.css": "./family/motion.css",
40
44
  "./swatch.css": "./family/swatch.css",
45
+ "./tag-list.css": "./family/tag-list.css",
41
46
  "./components.tokens.json": "./components.tokens.json",
42
47
  "./accordion.css": "./css/accordion.css",
43
48
  "./affix.css": "./css/affix.css",
@@ -56,9 +61,11 @@
56
61
  "./calendar-range-picker.css": "./css/calendar-range-picker.css",
57
62
  "./card.css": "./css/card.css",
58
63
  "./carousel.css": "./css/carousel.css",
64
+ "./cartesian-chart.css": "./css/cartesian-chart.css",
59
65
  "./cascader.css": "./css/cascader.css",
60
66
  "./checkbox-group.css": "./css/checkbox-group.css",
61
67
  "./checkbox.css": "./css/checkbox.css",
68
+ "./citation.css": "./css/citation.css",
62
69
  "./clipboard.css": "./css/clipboard.css",
63
70
  "./code-view.css": "./css/code-view.css",
64
71
  "./collapsible.css": "./css/collapsible.css",
@@ -91,12 +98,14 @@
91
98
  "./floating-panel.css": "./css/floating-panel.css",
92
99
  "./forced-colors.css": "./css/forced-colors.css",
93
100
  "./form.css": "./css/form.css",
94
- "./gradient-text.css": "./css/gradient-text.css",
101
+ "./funnel-chart.css": "./css/funnel-chart.css",
102
+ "./graph-chart.css": "./css/graph-chart.css",
95
103
  "./grid.css": "./css/grid.css",
104
+ "./grid-list.css": "./css/grid-list.css",
96
105
  "./heatmap.css": "./css/heatmap.css",
106
+ "./hierarchy-chart.css": "./css/hierarchy-chart.css",
97
107
  "./highlight.css": "./css/highlight.css",
98
108
  "./hover-card.css": "./css/hover-card.css",
99
- "./icon-wrapper.css": "./css/icon-wrapper.css",
100
109
  "./icon.css": "./css/icon.css",
101
110
  "./kbd.css": "./css/kbd.css",
102
111
  "./image-cropper.css": "./css/image-cropper.css",
@@ -130,6 +139,7 @@
130
139
  "./page-header.css": "./css/page-header.css",
131
140
  "./pagination.css": "./css/pagination.css",
132
141
  "./password-input.css": "./css/password-input.css",
142
+ "./pie-chart.css": "./css/pie-chart.css",
133
143
  "./pin-input.css": "./css/pin-input.css",
134
144
  "./pointer.css": "./css/pointer.css",
135
145
  "./popconfirm.css": "./css/popconfirm.css",
@@ -137,20 +147,22 @@
137
147
  "./progress.css": "./css/progress.css",
138
148
  "./prompt-input.css": "./css/prompt-input.css",
139
149
  "./question-flow.css": "./css/question-flow.css",
150
+ "./radar-chart.css": "./css/radar-chart.css",
140
151
  "./radio-group.css": "./css/radio-group.css",
141
152
  "./rating.css": "./css/rating.css",
142
153
  "./reset.css": "./css/reset.css",
143
154
  "./reasoning.css": "./css/reasoning.css",
144
155
  "./resizable.css": "./css/resizable.css",
156
+ "./sankey-chart.css": "./css/sankey-chart.css",
145
157
  "./scroll-area.css": "./css/scroll-area.css",
146
158
  "./scrollbar.css": "./css/scrollbar.css",
147
- "./segmented.css": "./css/segmented.css",
148
159
  "./select.css": "./css/select.css",
149
160
  "./separator.css": "./css/separator.css",
150
161
  "./side-nav.css": "./css/side-nav.css",
151
162
  "./signature-pad.css": "./css/signature-pad.css",
152
163
  "./skeleton.css": "./css/skeleton.css",
153
164
  "./slider.css": "./css/slider.css",
165
+ "./sparkline.css": "./css/sparkline.css",
154
166
  "./spinner.css": "./css/spinner.css",
155
167
  "./sortable.css": "./css/sortable.css",
156
168
  "./splitter.css": "./css/splitter.css",
@@ -169,10 +181,10 @@
169
181
  "./timeline.css": "./css/timeline.css",
170
182
  "./timer.css": "./css/timer.css",
171
183
  "./timestamp.css": "./css/timestamp.css",
172
- "./toast.css": "./css/toast.css",
173
184
  "./toggle-group.css": "./css/toggle-group.css",
174
185
  "./toggle.css": "./css/toggle.css",
175
186
  "./tone.css": "./css/tone.css",
187
+ "./liquid.css": "./css/liquid.css",
176
188
  "./tool-call.css": "./css/tool-call.css",
177
189
  "./toolbar.css": "./css/toolbar.css",
178
190
  "./tooltip.css": "./css/tooltip.css",
@@ -205,10 +217,10 @@
205
217
  "registry": "https://registry.npmjs.org/"
206
218
  },
207
219
  "dependencies": {
208
- "@xihan-ui/tokens": "^2.1.0"
220
+ "@xihan-ui/tokens": "^3.1.0"
209
221
  },
210
222
  "scripts": {
211
- "gen": "node build/action-control-recipe.mjs && node build/field-chrome-recipe.mjs && node build/collection-item-recipe.mjs && node build/swatch-recipe.mjs && node build/emit-component-tokens.mjs && node build/emit-entries.mjs",
212
- "build": "node build/action-control-recipe.mjs && node build/field-chrome-recipe.mjs && node build/collection-item-recipe.mjs && node build/swatch-recipe.mjs && node build/emit-component-tokens.mjs && node build/emit-entries.mjs"
223
+ "gen": "node build/action-control-recipe.mjs && node build/field-chrome-recipe.mjs && node build/collection-item-recipe.mjs && node build/swatch-recipe.mjs && node build/chart-recipe.mjs && node build/emit-component-tokens.mjs && node build/emit-entries.mjs",
224
+ "build": "node build/action-control-recipe.mjs && node build/field-chrome-recipe.mjs && node build/collection-item-recipe.mjs && node build/swatch-recipe.mjs && node build/chart-recipe.mjs && node build/emit-component-tokens.mjs && node build/emit-entries.mjs"
213
225
  }
214
226
  }
@@ -1,91 +0,0 @@
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
- /* 定义 gradient text 样式。 */
7
-
8
- @layer xihan.components {
9
- /* 轴边界只路由简单前景值,渐变本身仍在组件根用本地 direction/from/to 计算。
10
- 这样内层 default/more 能双向重置,也不会把未解析的组件变量冻结在祖先上。 */
11
- :where(:root, [data-contrast='default']) {
12
- --xh-_gradient-text-fg: transparent;
13
- }
14
-
15
- :where([data-contrast='more']) {
16
- --xh-_gradient-text-fg: var(--xh-fg-default);
17
- }
18
-
19
- /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
20
- [data-scope='gradient-text'] {
21
- --xh-gradient-text-skin: 1;
22
- }
23
-
24
- /* 渐变画在背景上,再用 background-clip 把背景裁进字形里;前景必须透明才透得出底下的渐变。
25
- 两条 background-clip 都写:WebKit 只认带前缀那条。
26
- -webkit-text-fill-color 在 WebKit 里决定字形填充色并盖过 color,也要一并透明。 */
27
- [data-scope='gradient-text'][data-part='root'] {
28
- /* 走向由 data-direction 换掉这一个槽,背景那条声明只写一遍 */
29
- --xh-_gradient-text-dir: to right;
30
- /* 两端颜色各收一个私有槽:语气档换的是这两支,使用者槽仍排在它们前面 */
31
- --xh-_gradient-text-from: var(--xh-gradient-brand-from);
32
- --xh-_gradient-text-to: var(--xh-gradient-brand-to);
33
-
34
- background-image: linear-gradient(
35
- var(--xh-_gradient-text-dir),
36
- var(--xh-gradient-text-from, var(--xh-_gradient-text-from)),
37
- var(--xh-gradient-text-to, var(--xh-_gradient-text-to))
38
- );
39
- -webkit-background-clip: text;
40
- background-clip: text;
41
- color: var(--xh-_gradient-text-fg);
42
- -webkit-text-fill-color: currentColor;
43
- }
44
-
45
- /* —— 走向 —— */
46
- /* 逐档换算成 CSS 的 to <边或角>,四条边加四个角 */
47
- [data-scope='gradient-text'][data-part='root'][data-direction='to-left'] {
48
- --xh-_gradient-text-dir: to left;
49
- }
50
-
51
- [data-scope='gradient-text'][data-part='root'][data-direction='to-bottom'] {
52
- --xh-_gradient-text-dir: to bottom;
53
- }
54
-
55
- [data-scope='gradient-text'][data-part='root'][data-direction='to-top'] {
56
- --xh-_gradient-text-dir: to top;
57
- }
58
-
59
- [data-scope='gradient-text'][data-part='root'][data-direction='to-bottom-right'] {
60
- --xh-_gradient-text-dir: to bottom right;
61
- }
62
-
63
- [data-scope='gradient-text'][data-part='root'][data-direction='to-bottom-left'] {
64
- --xh-_gradient-text-dir: to bottom left;
65
- }
66
-
67
- [data-scope='gradient-text'][data-part='root'][data-direction='to-top-right'] {
68
- --xh-_gradient-text-dir: to top right;
69
- }
70
-
71
- [data-scope='gradient-text'][data-part='root'][data-direction='to-top-left'] {
72
- --xh-_gradient-text-dir: to top left;
73
- }
74
-
75
- /* —— 语气 —— */
76
- /* 两端取该族主色与它压深一档的取值,深浅两态自动跟随。
77
- 排在使用者槽之后:作者写了 from / to,六族语气一并让位 */
78
- [data-scope='gradient-text'][data-part='root'][data-tone] {
79
- --xh-_gradient-text-from: var(--xh-_tone);
80
- --xh-_gradient-text-to: var(--xh-_tone-active);
81
- }
82
-
83
- /* 无 JS 时的系统高对比、强制色与打印统一退回单色;显式局部轴由上面的最近作用域标记负责。 */
84
- @media (prefers-contrast: more), (forced-colors: active), print {
85
- [data-scope='gradient-text'][data-part='root'] {
86
- background-image: none;
87
- color: inherit;
88
- -webkit-text-fill-color: currentColor;
89
- }
90
- }
91
- }
@@ -1,81 +0,0 @@
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
- /* 定义 icon wrapper 样式。 */
7
-
8
- @layer xihan.components {
9
- /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
10
- [data-scope='icon-wrapper'] {
11
- --xh-icon-wrapper-skin: 1;
12
- }
13
-
14
- /* 定直径的图标底座:图元在正中,底色、描边与形状由三轴决定。
15
- 直径与图元直径分两个槽位,底座换档时里面的图元跟着一起换 */
16
- [data-scope='icon-wrapper'][data-part='root'] {
17
- /* 私有槽位:底座直径与图元直径,换档只改这两个 */
18
- --xh-_icon-wrapper-size: var(--xh-icon-wrapper-size, var(--xh-control-h-md));
19
-
20
- /* 流给里面的图元——包括自己写了档位的那一枚,它的档位取值也读这个槽,
21
- 图元直径因此始终跟着底座换档 */
22
- --xh-icon-size: var(--xh-icon-wrapper-glyph-size, var(--xh-glyph-size-md));
23
-
24
- display: inline-flex;
25
- flex: none;
26
- align-items: center;
27
- justify-content: center;
28
- inline-size: var(--xh-_icon-wrapper-size);
29
- block-size: var(--xh-_icon-wrapper-size);
30
- border: var(--xh-stroke-thin) solid transparent;
31
- border-radius: var(--xh-icon-wrapper-radius, var(--xh-shape-circle));
32
- background: var(--xh-icon-wrapper-bg, var(--xh-bg-subtle));
33
- color: var(--xh-icon-wrapper-fg, var(--xh-fg-default));
34
- line-height: var(--xh-leading-none);
35
- }
36
-
37
- /* 给了 display 的角色节点必须补这条:display 声明会盖掉 UA 的 [hidden]{display:none},
38
- 不补的话作者给节点加 hidden 后它照样显示 */
39
- [data-scope='icon-wrapper'][data-part='root'][hidden] {
40
- display: none;
41
- }
42
-
43
- /* —— 形态 —— */
44
- /* 形态只决定颜色怎么用;用哪族颜色由 data-tone 决定,没写 tone 就沿用原来的品牌/中性配色。
45
- 实心档还在顶边画一条极浅的内高光,把这块底座衬出厚度;另外三档面上没有实心底,不画 */
46
- [data-scope='icon-wrapper'][data-part='root'][data-variant='solid'] {
47
- --xh-_icon-wrapper-highlight: var(--xh-_highlight-tone);
48
-
49
- background: var(--xh-_tone, var(--xh-bg-brand));
50
- color: var(--xh-_tone-on, var(--xh-fg-on-brand));
51
- box-shadow: var(--xh-icon-wrapper-shadow, var(--xh-_icon-wrapper-highlight));
52
- }
53
-
54
- [data-scope='icon-wrapper'][data-part='root'][data-variant='subtle'] {
55
- background: var(--xh-_tone-subtle, var(--xh-bg-subtle));
56
- color: var(--xh-_tone-fg, var(--xh-fg-default));
57
- }
58
-
59
- [data-scope='icon-wrapper'][data-part='root'][data-variant='outline'] {
60
- border-color: var(--xh-_tone-border, var(--xh-border-default));
61
- background: transparent;
62
- color: var(--xh-_tone-fg, var(--xh-fg-default));
63
- }
64
-
65
- [data-scope='icon-wrapper'][data-part='root'][data-variant='ghost'] {
66
- background: transparent;
67
- color: var(--xh-_tone-fg, var(--xh-fg-default));
68
- }
69
-
70
- /* —— 尺寸 —— */
71
- /* 缺省档写在上面的基础规则里 */
72
- [data-scope='icon-wrapper'][data-part='root'][data-size='sm'] {
73
- --xh-_icon-wrapper-size: var(--xh-icon-wrapper-size, var(--xh-control-h-sm));
74
- --xh-icon-size: var(--xh-icon-wrapper-glyph-size, var(--xh-glyph-size-sm));
75
- }
76
-
77
- [data-scope='icon-wrapper'][data-part='root'][data-size='lg'] {
78
- --xh-_icon-wrapper-size: var(--xh-icon-wrapper-size, var(--xh-control-h-lg));
79
- --xh-icon-size: var(--xh-icon-wrapper-glyph-size, var(--xh-glyph-size-lg));
80
- }
81
- }
package/css/segmented.css DELETED
@@ -1,280 +0,0 @@
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
- /* 定义 segmented 样式。 */
7
-
8
- @layer xihan.components {
9
- /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
10
- [data-scope='segmented'] {
11
- --xh-segmented-skin: 1;
12
- }
13
-
14
- /* 一条轨道装着若干段,指示器在轨道里滑。指示器是绝对定位的,
15
- root 必须是它的定位参照系;段与段之间不留缝,所以不设 gap */
16
- [data-scope='segmented'][data-part='root'] {
17
- /* 尺寸档只换这四个私有槽,段与文字从 root 继承它们,换档不必给每个部件各写一条选择器 */
18
- --xh-_segmented-h: var(--xh-control-h-md);
19
- --xh-_segmented-px: var(--xh-control-px-md);
20
- --xh-_segmented-gap: var(--xh-control-gap-md);
21
- --xh-_segmented-font-size: var(--xh-control-font-md);
22
- /* 指示器(有滑块开关的白色抬起面)的底、描边与选中段的文字色;没写 data-tone 时就是这里的取值 */
23
- --xh-_segmented-indicator-bg: var(--xh-bg-surface-raised);
24
- --xh-_segmented-fg-selected: var(--xh-fg-default);
25
- /* 轨道是淡底承载面:段坐在它上面,悬停 200 → 按下 300,host 槽向内下发同一副阶梯 */
26
- --xh-action-host-bg-hover: var(--xh-bg-subtle-hover);
27
- --xh-action-host-bg-pressed: var(--xh-bg-subtle-active);
28
-
29
- /* 一行排不下就折行:每个选项都必须看得见、点得到,硬撑一行会把末尾几段顶出容器。
30
- 段各自带圆角,折行不会切角;不改成横向滚动是因为段离轨道内沿只有一格衬距,
31
- 轨道一旦成为滚动容器,段的焦点环(画在段外侧 2px 处)会被裁掉。
32
- 轨道是一块淡底面:--xh-bg-subtle 底 + 同宽透明占位边 + 无影,边界由滑块的描边与轨道底色的落差承担 */
33
- position: relative;
34
- display: inline-flex;
35
- flex-wrap: wrap;
36
- align-items: stretch;
37
- padding: var(--xh-segmented-track-padding, var(--xh-space-0_5));
38
- border: var(--xh-stroke-thin) solid var(--xh-segmented-border, transparent);
39
- border-radius: var(--xh-segmented-radius, var(--xh-shape-surface));
40
- background: var(--xh-segmented-bg, var(--xh-bg-subtle));
41
- font-size: var(--xh-segmented-font-size, var(--xh-_segmented-font-size));
42
- }
43
-
44
- /* 横排时外盒整体就是一档控件高:段被 align-items: stretch 撑满轨道内侧,与同行控件齐平。
45
- 写下限而非定值:排得下时两者逐值相同,排不下折了行才让轨道往下长 */
46
- [data-scope='segmented'][data-part='root'][data-orientation='horizontal'] {
47
- flex-direction: row;
48
- min-block-size: var(--xh-segmented-h, var(--xh-_segmented-h));
49
- }
50
-
51
- /* 段的高度交给轨道:一行时被 stretch 撑满轨道内侧。
52
- 下限按「一档控件高减掉轨道上下的衬距与描边」算,折了行每一行仍是这个高度,
53
- 不写的话多行会被 align-content 均分成一叠扁条 */
54
- [data-scope='segmented'][data-part='root'][data-orientation='horizontal'] [data-scope='segmented'][data-part='item'] {
55
- block-size: auto;
56
- min-block-size: calc(
57
- var(--xh-segmented-h, var(--xh-_segmented-h)) - 2 * var(--xh-segmented-track-padding, var(--xh-space-0_5)) - 2 *
58
- var(--xh-stroke-thin)
59
- );
60
- }
61
-
62
- [data-scope='segmented'][data-part='root'][data-orientation='vertical'] {
63
- flex-direction: column;
64
- flex-wrap: nowrap;
65
- }
66
-
67
- /* 撑满行宽:各段等分剩余空间,长短不一的文字也排得齐 */
68
- [data-scope='segmented'][data-part='root'][data-block] {
69
- display: flex;
70
- inline-size: 100%;
71
- }
72
-
73
- /* 等分只在横排成立:竖排的主轴是块轴,flex-basis: 0 会顶掉段自己的高度档,
74
- 段高改由「等分容器高度」决定,三档控件高度从此对它没有作用。
75
- 竖排下段本来就靠上面那条 inline-size: 100% 与 align-items: stretch 撑满宽度 */
76
- [data-scope='segmented'][data-part='root'][data-block][data-orientation='horizontal']
77
- > [data-scope='segmented'][data-part='item'] {
78
- flex: 1 1 0;
79
- }
80
-
81
- /* root 会短暂拿到 tabindex=0,但它的 onFocus 立刻把焦点转投给段,
82
- 在它身上画环只会闪一下就跳走,高亮要长在段上 */
83
- [data-scope='segmented'][data-part='root']:focus-visible {
84
- outline: none;
85
- }
86
-
87
- [data-scope='segmented'][data-part='root'][data-invalid] {
88
- border-color: var(--xh-segmented-border-invalid, var(--xh-border-invalid));
89
- }
90
-
91
- [data-scope='segmented'][data-part='root'][data-disabled] {
92
- background: var(--xh-segmented-bg-disabled, var(--xh-bg-muted));
93
- }
94
-
95
- /* 整组禁用时指示器落回轨道平面(不再浮起),但底色一点不兑淡:
96
- 它上面压着选中段的文字,往 muted 兑哪怕一成,brand 的对比度就从 5.08 掉到 4.24 */
97
- [data-scope='segmented'][data-part='root'][data-disabled]
98
- [data-scope='segmented'][data-part='indicator'] {
99
- box-shadow: var(--xh-segmented-indicator-shadow-disabled, none);
100
- }
101
-
102
- /* —— 语气 —— */
103
- /* 选择器带 [data-tone] 限定:不限定的话,放在语气容器里的分段控件会跟着改色 */
104
- [data-scope='segmented'][data-part='root'][data-tone] {
105
- --xh-_segmented-indicator-bg: var(--xh-_tone, var(--xh-bg-surface-raised));
106
- --xh-_segmented-fg-selected: var(--xh-_tone-on, var(--xh-fg-default));
107
- }
108
-
109
- /* 语气档的滑块是实心语气面,描边与底同色:raised 面必带描边,这里的描边只是把边位补齐,不另画一圈 */
110
- [data-scope='segmented'][data-part='root'][data-tone] [data-scope='segmented'][data-part='indicator'] {
111
- border-color: var(--xh-segmented-indicator-border, var(--xh-_tone, var(--xh-border-default)));
112
- }
113
-
114
- /* —— 尺寸 —— */
115
- /* 缺省的 md 档写在上面的基础规则里 */
116
- [data-scope='segmented'][data-part='root'][data-size='sm'] {
117
- --xh-_segmented-h: var(--xh-control-h-sm);
118
- --xh-_segmented-px: var(--xh-control-px-sm);
119
- --xh-_segmented-gap: var(--xh-control-gap-sm);
120
- --xh-_segmented-font-size: var(--xh-control-font-sm);
121
- }
122
-
123
- [data-scope='segmented'][data-part='root'][data-size='lg'] {
124
- --xh-_segmented-h: var(--xh-control-h-lg);
125
- --xh-_segmented-px: var(--xh-control-px-lg);
126
- --xh-_segmented-gap: var(--xh-control-gap-lg);
127
- --xh-_segmented-font-size: var(--xh-control-font-lg);
128
- }
129
-
130
- /* 段自己也定位,于是与绝对定位的指示器按文档序比高低:
131
- 指示器写在段之前,段与它的焦点环便压在指示器上面,不必动层号 */
132
- [data-scope='segmented'][data-part='item'] {
133
- position: relative;
134
- display: inline-flex;
135
- align-items: center;
136
- justify-content: center;
137
- gap: var(--xh-segmented-item-gap, var(--xh-_segmented-gap));
138
- block-size: var(--xh-segmented-item-h, var(--xh-_segmented-h));
139
- padding-inline: var(--xh-segmented-item-px, var(--xh-_segmented-px));
140
- border: 0;
141
- border-radius: var(--xh-segmented-item-radius, var(--xh-shape-inset));
142
- background: transparent;
143
- color: var(--xh-segmented-item-fg, var(--xh-fg-muted));
144
- /* 按钮不继承宿主字体,尺寸档在 root 上,两者都要显式接过来 */
145
- font-family: inherit;
146
- font-size: inherit;
147
- font-weight: var(--xh-segmented-item-font-weight, var(--xh-text-label-weight));
148
- line-height: var(--xh-leading-none);
149
- white-space: nowrap;
150
- cursor: pointer;
151
- user-select: none;
152
- /* 段是轨道里铺开的一段(Segmented item 归行级),按下只换面不缩放:transition 里没有 scale */
153
- transition:
154
- color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
155
- background-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
156
- }
157
-
158
- /* 选中态:单选是 role=radio + aria-checked,样式一律读 data-state,不读 ARIA。
159
- 文字色也要跟着变——指示器是可选部件,作者不渲染它时"当前是哪一段"仍然要看得出来 */
160
- [data-scope='segmented'][data-part='item'][data-state='checked'] {
161
- color: var(--xh-segmented-item-fg-checked, var(--xh-_segmented-fg-selected));
162
- }
163
-
164
- /* 指示器在场时选中段自己透空,环紧挨着的是压在它底下、与它同一块矩形的指示器实心底;
165
- 环色与那块底同族就看不出来,改取选中段自己的前景色——那一支本来就配着这块底定的。
166
- 指示器是可选部件,作者没渲染它时选中段直接坐在轨道底上,那一档的环仍取默认那一支 */
167
- [data-scope='segmented'][data-part='root']:has([data-scope='segmented'][data-part='indicator']:not([hidden]))
168
- [data-scope='segmented'][data-part='item'][data-state='checked']:focus-visible {
169
- --xh-_ring-color: currentColor;
170
- }
171
-
172
- /* 悬停只提亮未选中的那几段:选中段的底下压着指示器,再变色就成了两套强调。
173
- 底色用中性的悬停档——它是「指哪一段」的轻反馈,不表达选中;段坐在淡底轨道里,悬停取 200 */
174
- [data-scope='segmented'][data-part='item']:not([data-disabled]):not([data-state='checked']):hover {
175
- background-color: var(--xh-segmented-item-bg-hover, var(--xh-bg-subtle-hover));
176
- color: var(--xh-segmented-item-fg-hover, var(--xh-fg-default));
177
- }
178
-
179
- /* 按下去要有回应:未选中段换到按下面(淡底承载 300),几何不动;只读段不压。
180
- 选中段不叠按下面:它压着的白色滑块本身就是选中身份(有滑块开关无叠加态) */
181
- [data-scope='segmented'][data-part='item']:not([data-disabled]):not([data-readonly]):not([data-state='checked']):is(:active, [data-pressed]) {
182
- background-color: var(--xh-segmented-item-bg-pressed, var(--xh-bg-subtle-active));
183
- transition-duration: var(--xh-motion-duration-press);
184
- transition-timing-function: var(--xh-motion-ease-press);
185
- }
186
-
187
- /* 只读:值改不动,但对比度不降,光标也不摆出"禁止"的样子 */
188
- [data-scope='segmented'][data-part='item'][data-readonly]:not([data-disabled]) {
189
- cursor: default;
190
- }
191
-
192
- /* 集合条目一律 aria-disabled 而非原生 disabled(禁用段仍要能当方向键的起点),
193
- 所以这里读 data-disabled,写 :disabled 永远不命中。
194
- 用了 fg-disabled 就不再叠 opacity:半透明会把底下的指示器透出来 */
195
- [data-scope='segmented'][data-part='item'][data-disabled] {
196
- color: var(--xh-fg-disabled);
197
- }
198
-
199
- /* 禁用且选中的那一段不换文字色:它压着的是指示器的实心底,换成 fg-disabled 后
200
- 六族语气实测只剩 1.04–3.01 的对比度,「当前选的是哪一段」当场看不见。
201
- 禁用由轨道底色、未选中段的文字色与光标表达,选中段保持可读 */
202
- [data-scope='segmented'][data-part='item'][data-disabled][data-state='checked'] {
203
- color: var(--xh-segmented-item-fg-checked-disabled, var(--xh-_segmented-fg-selected));
204
- }
205
-
206
- [data-scope='segmented'][data-part='item-text'] {
207
- color: inherit;
208
- user-select: none;
209
- }
210
-
211
- [data-scope='segmented'][data-part='indicator'] {
212
- /* 位置与尺寸由连接层量好写成内联样式(它量得到,样式表量不到);
213
- 这四个是还没量到时的落点,量到即被内联样式盖过 */
214
- --xh-_segmented-indicator-x: 0;
215
- --xh-_segmented-indicator-y: 0;
216
- --xh-_segmented-indicator-w: 0;
217
- --xh-_segmented-indicator-h: 0;
218
-
219
- position: absolute;
220
- display: block;
221
- inset-inline-start: var(--xh-_segmented-indicator-x);
222
- inset-block-start: var(--xh-_segmented-indicator-y);
223
- inline-size: var(--xh-_segmented-indicator-w);
224
- block-size: var(--xh-_segmented-indicator-h);
225
- /* 白色抬起面三件(有滑块开关):surface-raised 底 + border-default 描边 + raised 影,描边必须在,影只是加成。
226
- 尺寸由连接层按段的盒量出,描边吃进盒里(border-box),滑块与段仍是同一块矩形 */
227
- border: var(--xh-stroke-thin) solid var(--xh-segmented-indicator-border, var(--xh-border-default));
228
- border-radius: var(--xh-segmented-indicator-radius, var(--xh-shape-inset));
229
- background: var(--xh-segmented-indicator-bg, var(--xh-_segmented-indicator-bg));
230
- box-shadow: var(--xh-segmented-indicator-shadow, var(--xh-elevation-raised));
231
- /* 段压在它上面,点击一律交给段;作者把它写在段之后时也不至于吞掉点击 */
232
- pointer-events: none;
233
- /* 用连续档而不是进场那条:滑块是被推过去的,起步就该有速度,
234
- 纯减速曲线看着像自己飘过去。四个几何量同时走,段宽不等时才不会先变形再挪位。
235
- 投影单列一项:整组禁用时滑块落回轨道平面,那层投影跟着收 */
236
- transition:
237
- inset-inline-start var(--xh-motion-duration-enter) var(--xh-motion-ease-continuous),
238
- inset-block-start var(--xh-motion-duration-enter) var(--xh-motion-ease-continuous),
239
- inline-size var(--xh-motion-duration-enter) var(--xh-motion-ease-continuous),
240
- block-size var(--xh-motion-duration-enter) var(--xh-motion-ease-continuous),
241
- box-shadow var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
242
- }
243
-
244
- /* 给了 display 的角色节点必须补这条:display 声明会盖掉 UA 的 [hidden]{display:none},
245
- 不补的话作者给节点加 hidden 后它照样显示 */
246
- [data-scope='segmented'][data-part='root'][hidden],
247
- [data-scope='segmented'][data-part='item'][hidden],
248
- [data-scope='segmented'][data-part='indicator'][hidden] {
249
- display: none;
250
- }
251
-
252
- @media print {
253
- /* 打印默认丢背景色:滑块那块底与它的落影都不画,「当前选的是哪一段」在纸上没了。
254
- 选中段改画一圈实边、字加粗,两条都不吃底色 */
255
- [data-scope='segmented'][data-part='item'][data-state='checked'] {
256
- border: var(--xh-stroke-thin) solid currentColor;
257
- font-weight: var(--xh-font-weight-bold);
258
- }
259
- }
260
-
261
- @media (forced-colors: active) {
262
- /* 按下:常态靠底色深一档表出,这一档里底色被统一换成 Canvas;按住那一下用系统高亮反色画回来 */
263
- [data-scope='segmented'][data-part='item']:not([data-disabled]):not([data-readonly]):not([data-state='checked']):is(:active, [data-pressed]) {
264
- background-color: Highlight;
265
- color: HighlightText;
266
- }
267
- }
268
-
269
- @media (pointer: coarse) {
270
- /* 手指的落点比一段高:命中区由伪元素向块轴两侧外扩,段的盒与轨道内的分格一点不动。
271
- 只扩块轴——段与段在行内轴上首尾相接,往两侧扩会与相邻段的命中区叠上。
272
- 取 8px 是按最矮的 sm 档算的(28 + 8 × 2 = 44),md 与 lg 档更宽裕 */
273
- [data-scope='segmented'][data-part='item']::before {
274
- content: '';
275
- position: absolute;
276
- inset-block: -8px;
277
- inset-inline: 0;
278
- }
279
- }
280
- }