@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
package/css/switch.css CHANGED
@@ -1,3 +1,12 @@
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
+ /* 定义 switch 样式。 */
7
+
8
+ @import '../family/action-control.css';
9
+
1
10
  @layer xihan.motion {
2
11
  /* 转圈的关键帧定义在本文件里:引用别处文件的动画名时那份文件不一定在场,动画会静默不跑 */
3
12
  @keyframes xh-switch-rotate {
@@ -19,40 +28,110 @@
19
28
  --xh-_switch-track-h: var(--xh-switch-track-h-md);
20
29
  --xh-_switch-pad: var(--xh-space-0_5);
21
30
  --xh-_switch-thumb-size: calc(var(--xh-_switch-track-h) - var(--xh-_switch-pad) * 2);
31
+ --xh-_switch-thumb-press-stretch: var(--xh-switch-thumb-press-stretch, var(--xh-motion-distance-sm));
22
32
 
23
33
  /* 选中态轨道色随语气走。没写 data-tone 时 --xh-_tone 没被声明,退回原来的品牌色 */
24
34
  --xh-_switch-accent: var(--xh-_tone, var(--xh-bg-brand));
35
+ /* 轨道边界以内描边表达,不吃掉滑块行程;checked / readonly / invalid / disabled 只换这一支色。 */
36
+ --xh-_switch-track-border: var(--xh-switch-border, var(--xh-border-control));
37
+ /* 轨道的面:未选中是 --xh-bg-subtle-active(300)的实体字段控件面;checked / readonly / disabled 换这一支。
38
+ 静息已是 300,中性阶梯再无更深一档可换:按下的底缺省仍是轨道静息面(使用者可经 --xh-switch-bg-pressed
39
+ 另给),换面的通道由下面滑块的橡皮带拉伸与压平投影承担;选中轨道按下换到语气的 active 档 */
40
+ --xh-_switch-track-bg: var(--xh-switch-bg, var(--xh-bg-subtle-active));
41
+ --xh-_switch-track-bg-pressed: var(--xh-switch-bg-pressed, var(--xh-_switch-track-bg));
42
+ --xh-_switch-fg: var(--xh-switch-fg, var(--xh-fg-default));
25
43
 
26
- position: relative;
27
- display: inline-flex;
28
- align-items: center;
44
+ /* 轨道接 Action Control text 档(§9.1 定尺轨道):悬停 / 按下 / 禁用面、0.97 缩放与换底、粗指针热区、
45
+ 焦点环与过渡由家族配方给,这里只把轨道的私有槽映射到桥接槽。轨道没有实描边(边界以内描边画,不吃行程),
46
+ 边位映射 transparent 并把 border 宽度归零;内描边经 shadow 通道各态同一支。悬停不换面(§7.2 阶梯只给按下) */
47
+ --xh-action-visual-size: var(--xh-_switch-track-h);
48
+ --xh-action-min-inline-size: calc(var(--xh-_switch-track-h) * 2 - var(--xh-_switch-pad) * 2);
49
+ --xh-action-padding-inline: var(--xh-_switch-pad);
50
+ --xh-action-gap: 0;
51
+ --xh-action-radius: var(--xh-switch-radius, var(--xh-shape-pill));
52
+ --xh-action-bg-rest: var(--xh-_switch-track-bg);
53
+ --xh-action-bg-hover: var(--xh-_switch-track-bg);
54
+ --xh-action-bg-pressed: var(--xh-_switch-track-bg-pressed);
55
+ --xh-action-bg-focus-visible: var(--xh-_switch-track-bg);
56
+ --xh-action-bg-disabled: var(--xh-_switch-track-bg);
57
+ --xh-action-border-rest: transparent;
58
+ --xh-action-border-hover: transparent;
59
+ --xh-action-border-pressed: transparent;
60
+ --xh-action-border-focus-visible: transparent;
61
+ --xh-action-border-disabled: transparent;
62
+ --xh-action-fg-rest: var(--xh-_switch-fg);
63
+ --xh-action-fg-hover: var(--xh-_switch-fg);
64
+ --xh-action-fg-pressed: var(--xh-_switch-fg);
65
+ --xh-action-fg-focus-visible: var(--xh-_switch-fg);
66
+ --xh-action-fg-disabled: var(--xh-_switch-fg);
67
+ --xh-action-shadow-rest: inset 0 0 0 var(--xh-stroke-thin) var(--xh-_switch-track-border);
68
+ --xh-action-shadow-hover: inset 0 0 0 var(--xh-stroke-thin) var(--xh-_switch-track-border);
69
+ --xh-action-shadow-pressed: inset 0 0 0 var(--xh-stroke-thin) var(--xh-_switch-track-border);
70
+ --xh-action-shadow-focus-visible: inset 0 0 0 var(--xh-stroke-thin) var(--xh-_switch-track-border);
71
+ --xh-action-shadow-disabled: inset 0 0 0 var(--xh-stroke-thin) var(--xh-_switch-track-border);
72
+ /* 内收环压在实体轨道上,直接取轨道的配对前景:未选中与只读 checked 的中性轨道配 fg-default,
73
+ 选中的语气轨道配它的 on 色 */
74
+ --xh-action-ring-color-focus-visible: currentColor;
75
+
76
+ /* 滑块贴 inline-start 起步,checked 才位移到另一端;家族的居中排布在轨道上不成立 */
77
+ justify-content: start;
29
78
  inline-size: calc(var(--xh-_switch-track-h) * 2 - var(--xh-_switch-pad) * 2);
30
- block-size: var(--xh-_switch-track-h);
31
79
  padding: var(--xh-_switch-pad);
32
- border: 0;
33
- border-radius: var(--xh-switch-radius, var(--xh-shape-pill));
34
- background: var(--xh-switch-bg, var(--xh-bg-subtle-active));
35
- cursor: pointer;
36
- transition: background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
80
+ border-width: 0;
37
81
  }
38
82
 
39
83
  [data-scope='switch'][data-part='root'][data-state='checked'] {
40
- background: var(--xh-switch-bg-checked, var(--xh-_switch-accent));
84
+ --xh-_switch-track-border: var(--xh-switch-border-checked, var(--xh-_switch-accent));
85
+ --xh-_switch-track-bg: var(--xh-switch-bg-checked, var(--xh-_switch-accent));
86
+ --xh-_switch-track-bg-pressed: var(--xh-switch-bg-checked-pressed, var(--xh-_tone-active, var(--xh-bg-brand-active)));
87
+ --xh-_switch-fg: var(--xh-switch-fg-checked, var(--xh-_tone-on, var(--xh-fg-on-brand)));
88
+ }
89
+
90
+ /* 只读:值仍要读得清、也仍会随表单提交,所以不压对比度、也不借禁用那档灰。
91
+ 变的是"动不动得了":指针不摆手型、按下不缩放不换底(家族的按压块由这里的映射钉回静息面),
92
+ 选中档的轨道退回未选中那一档的中性底(§7.2 交互态的底只从语义面派生),
93
+ 滑块收掉那层抬起的投影——投影是"这颗按得动"的信号。与文本框只读那一档同口径(压低底色而非置灰)。
94
+ 滑块停在哪一端不受影响,值本身仍由位置这条非颜色通道读出。 */
95
+ [data-scope='switch'][data-part='root'][data-readonly]:not([data-disabled]) {
96
+ --xh-_switch-track-bg-pressed: var(--xh-_switch-track-bg);
97
+ --xh-action-scale-pressed: none;
98
+ --xh-action-cursor-rest: default;
99
+ --xh-action-cursor-hover: default;
100
+ --xh-action-cursor-pressed: default;
101
+ --xh-action-cursor-focus-visible: default;
102
+ }
103
+
104
+ [data-scope='switch'][data-part='root'][data-readonly][data-state='checked'] {
105
+ --xh-_switch-track-border: var(--xh-switch-border-checked-readonly, var(--xh-border-control));
106
+ --xh-_switch-track-bg: var(--xh-switch-bg-checked-readonly, var(--xh-bg-subtle-active));
107
+ --xh-_switch-fg: var(--xh-switch-fg-checked-readonly, var(--xh-fg-default));
108
+ }
109
+
110
+ /* 高对比轴把中性状态的边界提到 strong;实心语气色 checked 仍由自己的色相边界表达。 */
111
+ :where([data-contrast='more']) [data-scope='switch'][data-part='root'][data-state='unchecked'] {
112
+ --xh-_switch-track-border: var(--xh-switch-border, var(--xh-border-strong));
41
113
  }
42
114
 
43
- /* 校验失败:轨道描一圈警示色。轨道本身没有边框,这里补出来 */
115
+ :where([data-contrast='more'])
116
+ [data-scope='switch'][data-part='root'][data-readonly][data-state='checked'] {
117
+ --xh-_switch-track-border: var(--xh-switch-border-checked-readonly, var(--xh-border-strong));
118
+ }
119
+
120
+ /* 校验失败排在状态与对比轴之后:只把轨道内描边换成警示色,不改轨道几何。 */
44
121
  [data-scope='switch'][data-part='root'][data-invalid] {
45
- box-shadow: inset 0 0 0 var(--xh-stroke-thin) var(--xh-switch-border-invalid, var(--xh-border-invalid));
122
+ --xh-_switch-track-border: var(--xh-switch-border-invalid, var(--xh-border-invalid));
46
123
  }
47
124
 
48
- [data-scope='switch'][data-part='root'][data-disabled] {
49
- cursor: not-allowed;
50
- opacity: var(--xh-state-disabled-opacity);
125
+ [data-scope='switch'][data-part='root'][data-readonly] [data-scope='switch'][data-part='thumb'] {
126
+ box-shadow: var(--xh-switch-thumb-shadow-readonly, none);
51
127
  }
52
128
 
53
- [data-scope='switch'][data-part='root']:focus-visible {
54
- outline: var(--xh-ring-width) solid var(--xh-ring-focus);
55
- outline-offset: var(--xh-ring-offset);
129
+ /* 禁用(§7.2):不靠 opacity——轨道退回 --xh-bg-subtle 底 + --xh-border-default 内描边,滑块前景置灰;
130
+ 选中的禁用轨道同样退回中性面,值由滑块位置读出。面与手型由家族的禁用块按映射给 */
131
+ [data-scope='switch'][data-part='root'][data-disabled] {
132
+ --xh-_switch-track-border: var(--xh-switch-border-disabled, var(--xh-border-default));
133
+ --xh-_switch-track-bg: var(--xh-switch-bg-disabled, var(--xh-bg-subtle));
134
+ --xh-_switch-fg: var(--xh-switch-fg-disabled, var(--xh-fg-disabled));
56
135
  }
57
136
 
58
137
  /* —— 尺寸 —— */
@@ -65,27 +144,87 @@
65
144
  --xh-_switch-track-h: var(--xh-switch-track-h-lg);
66
145
  }
67
146
 
147
+ /* 鼠标落点:轨道只有二十来个像素高,命中区靠一枚绝对定位的伪元素往外扩,
148
+ 轨道本身一动不动——把轨道加高会改掉整条开关的比例。常态外扩 4px,最矮的 sm 档由 18px 到 26px。 */
149
+ [data-scope='switch'][data-part='root']::after {
150
+ content: '';
151
+ position: absolute;
152
+ inset: calc(-1 * var(--xh-space-1));
153
+ }
154
+
155
+ /* 粗指针:家族 text 档的 ::after 把块轴补到 44px、铺满轨道宽并以 -50% 平移居中,行内轴(md 40px)
156
+ 这里再补到 44px。起点必须交还家族的 50%:上面那条 inset 负值特指度更高,留着的话盒的左上角
157
+ 被钉在 -4px、再被家族的平移拉走半个盒,热区的中心落到轨道左上角外面,右半条开关摸不着 */
158
+ @media (pointer: coarse) {
159
+ [data-scope='switch'][data-part='root']::after {
160
+ inset-block: 50% auto;
161
+ inset-inline: 50% auto;
162
+ min-inline-size: 44px;
163
+ }
164
+ }
165
+
166
+ /* 滑块是 raised 抬起面(§5.3 可拖起部件,逐部件登记):--xh-bg-surface-raised 底 + --xh-border-default 描边 +
167
+ --xh-elevation-raised 影,无顶光;描边必须在,影只是加成 */
68
168
  [data-scope='switch'][data-part='thumb'] {
169
+ position: relative;
170
+ display: grid;
171
+ flex: none;
172
+ place-items: center;
69
173
  inline-size: var(--xh-_switch-thumb-size);
70
174
  block-size: var(--xh-_switch-thumb-size);
71
- border-radius: var(--xh-switch-thumb-radius, var(--xh-shape-pill));
72
- background: var(--xh-switch-thumb, var(--xh-bg-canvas));
175
+ border: var(--xh-stroke-thin) solid var(--xh-switch-thumb-border, var(--xh-border-default));
176
+ border-radius: var(--xh-switch-thumb-radius, var(--xh-shape-circle));
177
+ background: var(--xh-switch-thumb, var(--xh-bg-surface-raised));
178
+ color: var(--xh-switch-thumb-fg, var(--xh-fg-default));
73
179
  box-shadow: var(--xh-switch-thumb-shadow, var(--xh-elevation-raised));
74
- transition: translate var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
180
+ line-height: 0;
181
+ /* 状态切换只动位移与表面,按压拉伸另走 inline-size;不使用 transition-all。 */
182
+ transition:
183
+ translate var(--xh-motion-duration-micro) var(--xh-motion-ease-continuous),
184
+ inline-size var(--xh-motion-duration-micro) var(--xh-motion-ease-continuous),
185
+ box-shadow var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
75
186
  }
76
187
 
77
188
  [data-scope='switch'][data-part='thumb'][data-state='checked'] {
78
189
  translate: var(--xh-_switch-thumb-size) 0;
79
190
  }
80
191
 
81
- /* 提交中:指针挂起、滑块里转圈;不是禁用,焦点与对比度都保持 */
82
- [data-scope='switch'][data-part='root'][data-loading] {
83
- cursor: wait;
192
+ /* flex 的 inline-start 会随 dir 翻转,物理 translate 也要反向,checked 才始终走到 inline-end。 */
193
+ [dir='rtl'] [data-scope='switch'][data-part='thumb'][data-state='checked'] {
194
+ translate: calc(-1 * var(--xh-_switch-thumb-size)) 0;
84
195
  }
85
196
 
86
- [data-scope='switch'][data-part='thumb'][data-loading] {
87
- display: grid;
88
- place-items: center;
197
+ /* Naive 同类的橡皮带回执:按住时向行进方向拉长并压平投影,已选中端同步回退同一段,外缘不越出轨道。 */
198
+ [data-scope='switch'][data-part='root']:not([data-disabled]):not([data-readonly]):not([data-loading]):is(:active, [data-pressed])
199
+ [data-scope='switch'][data-part='thumb'] {
200
+ inline-size: calc(var(--xh-_switch-thumb-size) + var(--xh-_switch-thumb-press-stretch));
201
+ box-shadow: var(--xh-switch-thumb-shadow-pressed, none);
202
+ }
203
+
204
+ [data-scope='switch'][data-part='root'][data-state='checked']:not([data-disabled]):not([data-readonly]):not([data-loading]):is(:active, [data-pressed])
205
+ [data-scope='switch'][data-part='thumb'] {
206
+ translate: calc(var(--xh-_switch-thumb-size) - var(--xh-_switch-thumb-press-stretch)) 0;
207
+ }
208
+
209
+ [dir='rtl']
210
+ [data-scope='switch'][data-part='root'][data-state='checked']:not([data-disabled]):not([data-readonly]):not([data-loading]):is(:active, [data-pressed])
211
+ [data-scope='switch'][data-part='thumb'] {
212
+ translate: calc(-1 * (var(--xh-_switch-thumb-size) - var(--xh-_switch-thumb-press-stretch))) 0;
213
+ }
214
+
215
+ /* 禁用滑块收掉抬起影、前景置灰;描边与底保持,滑块仍是一块读得出位置的实体 */
216
+ [data-scope='switch'][data-part='root'][data-disabled] [data-scope='switch'][data-part='thumb'] {
217
+ color: var(--xh-switch-thumb-fg-disabled, var(--xh-fg-disabled));
218
+ box-shadow: var(--xh-switch-thumb-shadow-disabled, none);
219
+ }
220
+
221
+ /* 提交中:指针摆「在途」、滑块里转圈;不是禁用,焦点与对比度都保持。
222
+ 家族按压块已排除 data-loading,这里只换各态手型 */
223
+ [data-scope='switch'][data-part='root'][data-loading]:not([data-disabled]) {
224
+ --xh-action-cursor-rest: progress;
225
+ --xh-action-cursor-hover: progress;
226
+ --xh-action-cursor-pressed: progress;
227
+ --xh-action-cursor-focus-visible: progress;
89
228
  }
90
229
 
91
230
  [data-scope='switch'][data-part='thumb'][data-loading]::after {
@@ -93,27 +232,54 @@
93
232
  inline-size: 60%;
94
233
  block-size: 60%;
95
234
  border: var(--xh-stroke-thin) solid transparent;
96
- border-block-start-color: var(--xh-switch-loading-fg, var(--xh-bg-brand));
97
- border-radius: var(--xh-shape-pill);
98
- animation: xh-switch-rotate var(--xh-switch-loading-duration, var(--xh-spin-duration)) linear infinite;
235
+ border-block-start-color: var(--xh-switch-loading-fg, var(--xh-_switch-accent));
236
+ /* 加载环是等宽高的圆(§6.3 全部加载环取 circle) */
237
+ border-radius: var(--xh-shape-circle);
238
+ animation: xh-switch-rotate var(--xh-switch-loading-duration, var(--xh-spin-duration)) var(--xh-motion-ease-loop) infinite;
99
239
  }
100
240
 
101
- /* —— 带文字的整行:<label> 包住控件与文字,与 checkbox-group 的条目同一副样子 —— */
241
+ /* —— 带文字的整行:<label> 包住控件与文字,与 checkbox 的标签、checkbox-group 的条目同一副样子 ——
242
+ 文字是控件自己的文字,字号随 size 档取 --xh-control-font-*(§6.4 控件字号随档);
243
+ 行高取正文档,长文字可以自然换行 */
102
244
  [data-scope='switch'][data-part='label'] {
245
+ --xh-_switch-label-font-size: var(--xh-control-font-md);
246
+
103
247
  display: inline-flex;
104
248
  align-items: center;
105
249
  gap: var(--xh-switch-label-gap, var(--xh-control-gap-md));
106
250
  color: var(--xh-switch-label-fg, var(--xh-fg-default));
107
- font-size: var(--xh-switch-label-font-size, var(--xh-text-label-size));
108
- line-height: var(--xh-leading-none);
251
+ font-size: var(--xh-switch-label-font-size, var(--xh-_switch-label-font-size));
252
+ line-height: var(--xh-switch-label-leading, var(--xh-leading-normal));
109
253
  cursor: pointer;
110
254
  }
111
255
 
256
+ [data-scope='switch'][data-part='label'][data-size='sm'] {
257
+ --xh-_switch-label-font-size: var(--xh-control-font-sm);
258
+ }
259
+
260
+ [data-scope='switch'][data-part='label'][data-size='lg'] {
261
+ --xh-_switch-label-font-size: var(--xh-control-font-lg);
262
+ }
263
+
264
+ /* 禁用标签色统一 --xh-fg-subtle(§6.4) */
112
265
  [data-scope='switch'][data-part='label'][data-disabled] {
113
- color: var(--xh-switch-label-fg-disabled, var(--xh-fg-disabled));
266
+ color: var(--xh-switch-label-fg-disabled, var(--xh-fg-subtle));
114
267
  cursor: not-allowed;
115
268
  }
116
269
 
270
+ /* label 没有独立的 readonly/loading 状态属性,现读里面真实的轨道状态,不复制一套业务状态。 */
271
+ [data-scope='switch'][data-part='label']:has(
272
+ [data-scope='switch'][data-part='root'][data-readonly]:not([data-disabled])
273
+ ) {
274
+ cursor: default;
275
+ }
276
+
277
+ [data-scope='switch'][data-part='label']:has(
278
+ [data-scope='switch'][data-part='root'][data-loading]:not([data-disabled])
279
+ ) {
280
+ cursor: progress;
281
+ }
282
+
117
283
  [data-scope='switch'][data-part='text'] {
118
284
  color: inherit;
119
285
  user-select: none;
@@ -122,23 +288,89 @@
122
288
  /* 减少动效:圆环停下并整圈换成虚线,静止的形状仍然读得出"还没好"。
123
289
  两条规则是同一件事的两个触发条件:系统偏好与作者打的 data-motion */
124
290
  @media (prefers-reduced-motion: reduce) {
291
+ [data-scope='switch'][data-part='root'] {
292
+ --xh-_switch-thumb-press-stretch: var(--xh-motion-distance-sm);
293
+ }
294
+
125
295
  [data-scope='switch'][data-part='thumb'][data-loading]::after {
126
296
  border-style: dotted;
127
- border-color: var(--xh-switch-loading-fg, var(--xh-bg-brand));
297
+ border-color: var(--xh-switch-loading-fg, var(--xh-_switch-accent));
128
298
  animation: none;
129
299
  }
130
300
  }
131
301
 
302
+ :where([data-motion='reduce']) [data-scope='switch'][data-part='root'] {
303
+ --xh-_switch-thumb-press-stretch: var(--xh-motion-distance-sm);
304
+ }
305
+
132
306
  :where([data-motion='reduce']) [data-scope='switch'][data-part='thumb'][data-loading]::after {
133
307
  border-style: dotted;
134
- border-color: var(--xh-switch-loading-fg, var(--xh-bg-brand));
308
+ border-color: var(--xh-switch-loading-fg, var(--xh-_switch-accent));
135
309
  animation: none;
136
310
  }
137
311
 
138
312
  /* 声明过任何 display 就盖掉了 UA 的 [hidden]{display:none},收起态得自己还回去。
139
313
  写在文件末尾:兜底必须排在该部件所有 display 声明之后 */
140
314
  [data-scope='switch'][data-part='root'][hidden],
315
+ [data-scope='switch'][data-part='thumb'][hidden],
141
316
  [data-scope='switch'][data-part='label'][hidden] {
142
317
  display: none;
143
318
  }
319
+
320
+ @media (forced-colors: active) {
321
+ /* 系统会丢弃 box-shadow:轨道改用内收 outline,滑块用真实边框保住两层几何。 */
322
+ [data-scope='switch'][data-part='root'] {
323
+ outline: var(--xh-stroke-thin) solid CanvasText;
324
+ outline-offset: calc(-1 * var(--xh-stroke-thin));
325
+ }
326
+
327
+ [data-scope='switch'][data-part='root'][data-disabled] {
328
+ outline-color: GrayText;
329
+ }
330
+
331
+ /* 按下:家族的 forced-colors 按压块把轨道底换成 Highlight,这里不再另画 */
332
+ [data-scope='switch'][data-part='root'][data-readonly] {
333
+ outline-style: dashed;
334
+ }
335
+
336
+ [data-scope='switch'][data-part='root'][data-readonly]:focus-visible {
337
+ outline-width: var(--xh-ring-width);
338
+ outline-style: solid;
339
+ outline-color: Highlight;
340
+ outline-offset: var(--xh-ring-offset);
341
+ }
342
+
343
+ [data-scope='switch'][data-part='thumb'] {
344
+ border-color: CanvasText;
345
+ box-shadow: none;
346
+ }
347
+ }
348
+
349
+ @media print {
350
+ /* 打印默认丢底色与投影:轨道与滑块都只有一块底,落到纸上整条开关消失,开与关无从读起。
351
+ 轨道补一圈实边把形状画出来,滑块补一圈实边把它停在哪一端画出来——
352
+ 开与关本来就由滑块的位置读出,位置这条通道在纸上仍然成立。
353
+ 轨道走 outline 而不是 border:这条盒子的内衬与滑块行程都从轨道高算出来,
354
+ 加一条实边会把行程改掉。 */
355
+ [data-scope='switch'][data-part='root'] {
356
+ outline: var(--xh-stroke-thin) solid currentColor;
357
+ outline-offset: calc(-1 * var(--xh-stroke-thin));
358
+ box-shadow: none;
359
+ }
360
+
361
+ [data-scope='switch'][data-part='thumb'] {
362
+ border: var(--xh-stroke-thin) solid currentColor;
363
+ box-shadow: none;
364
+ }
365
+
366
+ /* 打印:动画会被定格在任意一帧——转了一半的圈、扫到一半的微光。这里换成静止的表达,
367
+ 取值与减弱动效那一档相同。两档各写一块、不并进一条媒体查询列表:减弱动效另有一份
368
+ :where([data-motion='reduce']) 的钩子版本与它成对,打印没有对应的钩子可配,
369
+ 并成一条之后这层配对关系在文本上就断了。 */
370
+ [data-scope='switch'][data-part='thumb'][data-loading]::after {
371
+ border-style: dotted;
372
+ border-color: var(--xh-switch-loading-fg, var(--xh-_switch-accent));
373
+ animation: none;
374
+ }
375
+ }
144
376
  }