@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/carousel.css CHANGED
@@ -1,35 +1,51 @@
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
+ /* 定义 carousel 样式。 */
7
+
8
+ @import '../family/action-control.css';
9
+
1
10
  @layer xihan.components {
2
11
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
12
  [data-scope='carousel'] {
4
13
  --xh-carousel-skin: 1;
5
14
  }
6
15
 
7
- /* 根节点是 region 地标:把上一张 / 视口 / 下一张排成一行,指示点另起一行。
8
- 两端按钮浮在视口上还是排在两侧由作者定,这里只给最朴素的一版排布 */
16
+ /* 根节点建立自己的覆盖层:视口占满容器,导航与分页压在内容之上,
17
+ isolation 把内部层号关在本组件内,不参与页面级层序。 */
9
18
  [data-scope='carousel'][data-part='root'] {
10
- /* 作者塞进指示符槽的图标跟着字号走,与皮肤画的兜底字形同一把尺 */
11
- --xh-icon-size: var(--xh-carousel-icon-size, var(--xh-glyph-size-text));
19
+ /* 三颗控制钮浮在媒体之上,缺省 M2 磨砂面:背景、边缘、顶光、投影与磨砂从同一份配方取,
20
+ 高对比、减少透明度、打印和强制色便能一起降级。三支写在根上:check-elevation-role 按赋值点认海拔角色 */
21
+ --xh-_carousel-trigger-bg: var(--xh-material-frosted-bg);
22
+ --xh-_carousel-trigger-border: var(--xh-material-frosted-border);
23
+ --xh-_carousel-trigger-shadow: var(--xh-material-frosted-shadow);
24
+ /* 自动播放开启时由 Headless 写入真实间隔;0ms 只用于未接入运行时的静态标记。 */
25
+ --xh-_carousel-autoplay-duration: 0ms;
26
+ --xh-_carousel-progress-from: 0 1;
27
+ --xh-_carousel-progress-to: 1 1;
12
28
 
13
- display: flex;
14
- flex-wrap: wrap;
15
- align-items: center;
16
- gap: var(--xh-carousel-gap, var(--xh-space-2));
29
+ position: relative;
30
+ display: block;
31
+ isolation: isolate;
32
+ inline-size: 100%;
17
33
  }
18
34
 
19
35
  [data-scope='carousel'][data-part='root'][hidden] {
20
36
  display: none;
21
37
  }
22
38
 
23
- /* 纵轨时按钮在上下两端,整条竖着排 */
24
39
  [data-scope='carousel'][data-part='root'][data-orientation='vertical'] {
25
- flex-direction: column;
40
+ --xh-_carousel-progress-from: 1 0;
41
+ --xh-_carousel-progress-to: 1 1;
26
42
  }
27
43
 
28
44
  /* 视口是裁切窗口:轨道比它宽(高),露在外面的那些张全靠它切掉。
29
45
  min-inline-size: 0 是关键——弹性子项的默认最小尺寸是内容尺寸,
30
46
  不压成 0 的话视口会被整条轨道撑开,什么都切不掉 */
31
47
  [data-scope='carousel'][data-part='viewport'] {
32
- flex: 1 1 auto;
48
+ inline-size: 100%;
33
49
  min-inline-size: 0;
34
50
  overflow: hidden;
35
51
  border-radius: var(--xh-carousel-viewport-radius, var(--xh-shape-surface));
@@ -43,25 +59,27 @@
43
59
  transform 归连接层:整页位移与拖拽中的像素偏移都写在它的内联 style 上,
44
60
  样式层再碰这条轴就会把两个位移互相覆盖,画面当场错位。
45
61
  这里只负责排布与过渡 */
46
- [data-scope='carousel'][data-part='item-group'] {
62
+ [data-scope='carousel'][data-part='list'] {
47
63
  display: flex;
48
64
  flex-direction: row;
49
65
  block-size: 100%;
50
- transition: transform var(--xh-carousel-duration, var(--xh-motion-duration-enter))
51
- var(--xh-carousel-ease, var(--xh-motion-ease-enter));
66
+ transition: transform var(--xh-carousel-duration, var(--xh-motion-duration-slide))
67
+ var(--xh-carousel-ease, var(--xh-motion-ease-slide));
52
68
  }
53
69
 
54
- [data-scope='carousel'][data-part='item-group'][hidden] {
70
+ [data-scope='carousel'][data-part='list'][hidden] {
55
71
  display: none;
56
72
  }
57
73
 
58
- [data-scope='carousel'][data-part='item-group'][data-orientation='vertical'] {
74
+ [data-scope='carousel'][data-part='list'][data-orientation='vertical'] {
59
75
  flex-direction: column;
60
76
  }
61
77
 
62
- /* 拖拽期间关掉过渡:留着的话画面会拖着手指慢半拍,像在拉一根皮筋 */
63
- [data-scope='carousel'][data-part='item-group'][data-dragging] {
78
+ /* 拖拽期间关掉过渡:留着的话画面会拖着手指慢半拍,像在拉一根皮筋。
79
+ 整条轨道的位移这一档每帧都在改,合成层只在这一档备好,松手随 data-dragging 一起撤掉 */
80
+ [data-scope='carousel'][data-part='list'][data-dragging] {
64
81
  transition: none;
82
+ will-change: transform;
65
83
  }
66
84
 
67
85
  /* 一张幻灯片。
@@ -81,66 +99,96 @@
81
99
  display: none;
82
100
  }
83
101
 
84
- /* 两端按钮:圆形、与控件同高,禁用时靠原生 :disabled 变灰 */
102
+ /* 两端按钮与播放开关:浮在媒体之上的单图标圆钮,接 Action Control floating 档(§4.1):
103
+ 盒型(md = box-lg 48px,圆形)、四态面、0.97 按压与 44px 命中区由家族配方给出,组件没有 size 轴、固定 md。
104
+ 连接层不投影 variant:面由这里桥接到磨砂缺省(使用者槽 → 磨砂面),悬停 / 按下的底走画布承载的阶梯
105
+ (100 → 200,§7.2 第 3 条),边缘与投影不动;这里只留定位、桥接槽与图标尺 */
106
+ [data-scope='carousel'][data-part='prev-trigger'],
107
+ [data-scope='carousel'][data-part='next-trigger'],
108
+ [data-scope='carousel'][data-part='autoplay-trigger'] {
109
+ --xh-action-bg-rest: var(--xh-carousel-trigger-bg, var(--xh-_carousel-trigger-bg));
110
+ --xh-action-bg-hover: var(--xh-carousel-trigger-bg-hover, var(--xh-bg-subtle));
111
+ --xh-action-bg-pressed: var(--xh-carousel-trigger-bg-active, var(--xh-bg-subtle-hover));
112
+ --xh-action-bg-focus-visible: var(--xh-carousel-trigger-bg, var(--xh-material-frosted-focus-surface));
113
+ --xh-action-bg-disabled: var(--xh-carousel-trigger-bg, var(--xh-_carousel-trigger-bg));
114
+ --xh-action-fg-rest: var(--xh-carousel-trigger-fg, var(--xh-material-frosted-fg));
115
+ --xh-action-fg-hover: var(--xh-carousel-trigger-fg, var(--xh-material-frosted-fg));
116
+ --xh-action-fg-pressed: var(--xh-carousel-trigger-fg, var(--xh-material-frosted-fg));
117
+ --xh-action-fg-focus-visible: var(--xh-carousel-trigger-fg, var(--xh-material-frosted-fg));
118
+ --xh-action-border-rest: var(--xh-carousel-trigger-border, var(--xh-_carousel-trigger-border));
119
+ --xh-action-border-hover: var(--xh-carousel-trigger-border, var(--xh-_carousel-trigger-border));
120
+ --xh-action-border-pressed: var(--xh-carousel-trigger-border, var(--xh-_carousel-trigger-border));
121
+ --xh-action-border-focus-visible: var(--xh-carousel-trigger-border, var(--xh-_carousel-trigger-border));
122
+ --xh-action-border-disabled: var(--xh-carousel-trigger-border, var(--xh-_carousel-trigger-border));
123
+ --xh-action-shadow-rest: var(--xh-carousel-trigger-shadow, var(--xh-_carousel-trigger-shadow));
124
+ --xh-action-shadow-hover: var(--xh-carousel-trigger-shadow-hover, var(--xh-_carousel-trigger-shadow));
125
+ --xh-action-shadow-pressed: var(--xh-carousel-trigger-shadow, var(--xh-_carousel-trigger-shadow));
126
+ --xh-action-shadow-focus-visible: var(--xh-carousel-trigger-shadow, var(--xh-_carousel-trigger-shadow));
127
+ --xh-action-shadow-disabled: var(--xh-carousel-trigger-shadow, var(--xh-_carousel-trigger-shadow));
128
+ /* M2 顶光走配方的高光通道 */
129
+ --xh-action-highlight-rest: var(--xh-material-frosted-highlight);
130
+ --xh-action-highlight-hover: var(--xh-material-frosted-highlight);
131
+ --xh-action-highlight-pressed: var(--xh-material-frosted-highlight);
132
+ --xh-action-highlight-focus-visible: var(--xh-material-frosted-highlight);
133
+ --xh-action-highlight-disabled: var(--xh-material-frosted-highlight);
134
+ --xh-action-radius: var(--xh-carousel-trigger-radius, var(--xh-_action-profile-radius));
135
+ --xh-action-visual-size: var(--xh-carousel-trigger-size, var(--xh-_action-profile-visual-size));
136
+ /* 作者塞进来的图标跟着 floating 档走(md 24px),与皮肤画的兜底字形同一把尺 */
137
+ --xh-icon-size: var(--xh-carousel-icon-size, var(--xh-_action-profile-glyph-size));
138
+
139
+ position: absolute;
140
+ z-index: 1;
141
+ -webkit-backdrop-filter: var(--xh-material-frosted-backdrop);
142
+ backdrop-filter: var(--xh-material-frosted-backdrop);
143
+ }
144
+
85
145
  [data-scope='carousel'][data-part='prev-trigger'],
86
146
  [data-scope='carousel'][data-part='next-trigger'] {
87
- display: inline-flex;
88
- flex: none;
89
- align-items: center;
90
- justify-content: center;
91
- inline-size: var(--xh-carousel-trigger-size, var(--xh-control-h-md));
92
- block-size: var(--xh-carousel-trigger-size, var(--xh-control-h-md));
93
- border: var(--xh-stroke-thin) solid var(--xh-carousel-trigger-border, var(--xh-border-control));
94
- border-radius: var(--xh-carousel-trigger-radius, var(--xh-shape-pill));
95
- background: var(--xh-carousel-trigger-bg, var(--xh-bg-surface));
96
- color: var(--xh-carousel-trigger-fg, var(--xh-fg-default));
97
- line-height: var(--xh-leading-none);
98
- cursor: pointer;
99
- transition:
100
- background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
101
- border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
147
+ inset-block-start: 50%;
148
+ translate: 0 -50%;
102
149
  }
103
150
 
104
- [data-scope='carousel'][data-part='prev-trigger'][hidden],
105
- [data-scope='carousel'][data-part='next-trigger'][hidden] {
106
- display: none;
151
+ [data-scope='carousel'][data-part='prev-trigger'] {
152
+ inset-inline-start: var(--xh-carousel-control-inset, var(--xh-space-3));
107
153
  }
108
154
 
109
- [data-scope='carousel'][data-part='prev-trigger']:hover:not(:disabled),
110
- [data-scope='carousel'][data-part='next-trigger']:hover:not(:disabled) {
111
- background: var(--xh-carousel-trigger-bg-hover, var(--xh-bg-subtle-hover));
155
+ [data-scope='carousel'][data-part='next-trigger'] {
156
+ inset-inline-end: var(--xh-carousel-control-inset, var(--xh-space-3));
112
157
  }
113
158
 
114
- [data-scope='carousel'][data-part='prev-trigger']:active:not(:disabled),
115
- [data-scope='carousel'][data-part='next-trigger']:active:not(:disabled) {
116
- background: var(--xh-carousel-trigger-bg-active, var(--xh-bg-subtle-active));
159
+ [data-scope='carousel'][data-part='autoplay-trigger'] {
160
+ inset-block-start: auto;
161
+ inset-block-end: var(--xh-carousel-control-inset, var(--xh-space-3));
162
+ inset-inline-end: var(--xh-carousel-control-inset, var(--xh-space-3));
117
163
  }
118
164
 
119
- [data-scope='carousel'][data-part='prev-trigger']:focus-visible,
120
- [data-scope='carousel'][data-part='next-trigger']:focus-visible,
121
- [data-scope='carousel'][data-part='indicator']:focus-visible {
122
- outline: var(--xh-ring-width) solid var(--xh-ring-focus);
123
- outline-offset: var(--xh-ring-offset);
165
+ [data-scope='carousel'][data-part='prev-trigger'][hidden],
166
+ [data-scope='carousel'][data-part='next-trigger'][hidden],
167
+ [data-scope='carousel'][data-part='autoplay-trigger'][hidden] {
168
+ display: none;
124
169
  }
125
170
 
126
- /* 两端到头时按钮是原生 disabled(不可聚焦、进不了 Tab 序列),所以这里必须用 :disabled
127
- 连接层同时给的 data-disabled 只是留给作者的钩子 */
128
- [data-scope='carousel'][data-part='prev-trigger']:disabled,
129
- [data-scope='carousel'][data-part='next-trigger']:disabled {
130
- color: var(--xh-fg-disabled);
131
- cursor: not-allowed;
171
+ /* 两端到头 / 没配自动播放时按钮是原生 disabled(不可聚焦、进不了 Tab 序列),连接层同时给 data-disabled
172
+ 到头即隐藏是刻意行为,配方 disabled 列给的面被 opacity 0 盖住,两者不冲突 */
173
+ [data-scope='carousel'][data-part='prev-trigger'][data-disabled],
174
+ [data-scope='carousel'][data-part='next-trigger'][data-disabled],
175
+ [data-scope='carousel'][data-part='autoplay-trigger'][data-disabled] {
176
+ opacity: 0;
177
+ pointer-events: none;
132
178
  }
133
179
 
134
180
  /* 视口与指示点都不是可聚焦节点,落焦只会是浏览器兜底行为,别画焦点环 */
135
181
  [data-scope='carousel'][data-part='viewport']:focus-visible,
136
- [data-scope='carousel'][data-part='item-group']:focus-visible,
182
+ [data-scope='carousel'][data-part='list']:focus-visible,
137
183
  [data-scope='carousel'][data-part='indicator-group']:focus-visible {
138
184
  outline: none;
139
185
  }
140
186
 
141
187
  [data-scope='carousel'][data-part='indicator-group'] {
188
+ position: absolute;
189
+ z-index: 1;
142
190
  display: flex;
143
- flex-wrap: wrap;
191
+ flex-wrap: nowrap;
144
192
  align-items: center;
145
193
  justify-content: center;
146
194
  gap: var(--xh-carousel-indicator-gap, var(--xh-space-1));
@@ -152,58 +200,224 @@
152
200
 
153
201
  [data-scope='carousel'][data-part='indicator-group'][data-orientation='vertical'] {
154
202
  flex-direction: column;
203
+ inset-block-start: 50%;
204
+ inset-inline-end: var(--xh-carousel-control-inset, var(--xh-space-3));
205
+ translate: 0 -50%;
155
206
  }
156
207
 
157
- /* 横轨时指示点另起一行占满整宽(根节点开了 flex-wrap),不跟两端按钮挤在同一行 */
208
+ /* 横轨分页覆盖在内容下沿居中。 */
158
209
  [data-scope='carousel'][data-part='indicator-group'][data-orientation='horizontal'] {
159
- flex-basis: 100%;
210
+ inset-block-end: var(--xh-carousel-indicator-inset, var(--xh-space-3));
211
+ inset-inline-start: 50%;
212
+ translate: -50% 0;
160
213
  }
161
214
 
162
- /* 指示点:一颗小圆点。点得动的东西必须够大才点得中,
163
- 所以圆点本身小、可点区域靠内边距撑到最小触达尺寸 */
215
+ /* 分页点与 Tour 的步骤点同一种语言(§6.3):8px 圆点,当前页拉长成 20px 胶囊并换品牌色。
216
+ 本体就是那颗点:没有文字(页码交给作者,默认不显示),底色即标记。
217
+ 悬停 / 按下的回执落在点的颜色上:中性标记按前景阶梯加深一档、再加深一档;当前页按品牌阶梯压一档 */
164
218
  [data-scope='carousel'][data-part='indicator'] {
219
+ position: relative;
165
220
  display: inline-flex;
166
221
  flex: none;
167
222
  align-items: center;
168
223
  justify-content: center;
169
224
  inline-size: var(--xh-carousel-indicator-size, var(--xh-space-2));
170
225
  block-size: var(--xh-carousel-indicator-size, var(--xh-space-2));
171
- /* 圆点本身没有文字,按钮里的文案(页码)交给作者,默认不显示 */
172
226
  padding: 0;
173
227
  border: 0;
174
- border-radius: var(--xh-carousel-indicator-radius, var(--xh-shape-pill));
175
- background: var(--xh-carousel-indicator-bg, var(--xh-border-control));
228
+ border-radius: var(--xh-carousel-indicator-radius, var(--xh-shape-circle));
229
+ background: var(--xh-carousel-indicator-bg, var(--xh-border-default));
176
230
  font-size: 0;
231
+ /* 自动播放的进度条画在点里,超出胶囊的那截裁掉 */
232
+ overflow: hidden;
177
233
  cursor: pointer;
178
- transition: background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
234
+ transition:
235
+ inline-size var(--xh-motion-duration-micro) var(--xh-motion-ease-continuous),
236
+ block-size var(--xh-motion-duration-micro) var(--xh-motion-ease-continuous),
237
+ background-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
238
+ scale var(--xh-motion-duration-release) var(--xh-motion-ease-release);
179
239
  }
180
240
 
181
241
  [data-scope='carousel'][data-part='indicator'][hidden] {
182
242
  display: none;
183
243
  }
184
244
 
245
+ /* 细指针的命中区外扩到 24×24(8px 点四面各扩 8px);粗指针下这颗伪元素改画点,盒子自己承担命中 */
246
+ [data-scope='carousel'][data-part='indicator']::after {
247
+ content: '';
248
+ position: absolute;
249
+ inset: calc(-1 * var(--xh-space-2));
250
+ }
251
+
185
252
  [data-scope='carousel'][data-part='indicator']:hover:not([data-current]) {
186
- background: var(--xh-carousel-indicator-bg-hover, var(--xh-fg-subtle));
253
+ background: var(--xh-carousel-indicator-bg-hover, var(--xh-fg-muted));
254
+ }
255
+
256
+ /* 按下:点的颜色再压一档,同时缩一下(§9.1 同时换底);指针按住由 :active 表出,
257
+ Space / Enter 与触屏按住由 Headless 投影 data-pressed,三种输入同一副面 */
258
+ [data-scope='carousel'][data-part='indicator']:is(:active, [data-pressed]):not([data-current]) {
259
+ background: var(--xh-carousel-indicator-bg-active, var(--xh-fg-default));
260
+ scale: var(--xh-motion-scale-press);
261
+ transition-duration: var(--xh-motion-duration-press);
262
+ transition-timing-function: var(--xh-motion-ease-press);
263
+ }
264
+
265
+ /* 悬停 / 按下的点是前景色阶梯上的两档(fg-muted → fg-default),落着焦点时默认环压在这块前景色的点上
266
+ 没有对比(浅色档悬停面 neutral-500 与环 brand-600 同一明度,深色档按下面就是白点);
267
+ 环取这块面配对的那支——前景色的配对面是画布色,与实心面取配对前景同一口径 */
268
+ [data-scope='carousel'][data-part='indicator']:not([data-current]):is(:hover, :active, [data-pressed]):focus-visible {
269
+ --xh-_ring-color: var(--xh-bg-canvas);
187
270
  }
188
271
 
189
272
  /* 当前页:data-current 与 aria-current='true' 由连接层成对给出,
190
- 样式挂在 data 钩子上,无障碍语义留给 aria,两边各司其职 */
273
+ 样式挂在 data 钩子上,无障碍语义留给 aria,两边各司其职。
274
+ 拉长成胶囊并换品牌色(格状当前 = 实心品牌,§7.3) */
191
275
  [data-scope='carousel'][data-part='indicator'][data-current] {
276
+ inline-size: var(--xh-carousel-indicator-size-current, var(--xh-space-5));
277
+ border-radius: var(--xh-carousel-indicator-radius-current, var(--xh-shape-pill));
278
+ }
279
+
280
+ /* 没开自动播放:整颗品牌胶囊。面是品牌实心,环色与面同族画在面里看不出来:
281
+ 环取面配对的前景色(聚焦规则灌 currentColor)。两条都带「没开自动播放」的祖先条件,
282
+ 与下面自动播放那一档互斥,环色只灌给实心那一档 */
283
+ [data-scope='carousel'][data-part='root']:not([data-autoplay], [data-paused])
284
+ [data-scope='carousel'][data-part='indicator'][data-current] {
285
+ background: var(--xh-carousel-indicator-bg-selected, var(--xh-bg-brand));
286
+ color: var(--xh-carousel-indicator-fg-selected, var(--xh-fg-on-brand));
287
+ }
288
+
289
+ [data-scope='carousel'][data-part='root']:not([data-autoplay], [data-paused])
290
+ [data-scope='carousel'][data-part='indicator'][data-current]:focus-visible {
291
+ --xh-_ring-color: currentColor;
292
+ }
293
+
294
+ [data-scope='carousel'][data-part='indicator'][data-current]:is(:active, [data-pressed]) {
295
+ background: var(--xh-carousel-indicator-bg-selected-active, var(--xh-bg-brand-active));
296
+ scale: var(--xh-motion-scale-press);
297
+ transition-duration: var(--xh-motion-duration-press);
298
+ transition-timing-function: var(--xh-motion-ease-press);
299
+ }
300
+
301
+ /* 自动播放的进度:胶囊是品牌淡底当轨道(仍是「当前」语义,§7.1;淡底上公共环够对比,不灌环色),
302
+ 品牌填充条从起点沿轨道方向长满这一页的时长;暂停时定住 */
303
+ [data-scope='carousel'][data-part='root'][data-autoplay]
304
+ [data-scope='carousel'][data-part='indicator'][data-current],
305
+ [data-scope='carousel'][data-part='root'][data-paused]
306
+ [data-scope='carousel'][data-part='indicator'][data-current] {
307
+ background: var(--xh-carousel-indicator-bg-track, var(--xh-bg-brand-subtle));
308
+ }
309
+
310
+ [data-scope='carousel'][data-part='root'][data-autoplay]
311
+ [data-scope='carousel'][data-part='indicator'][data-current]::before,
312
+ [data-scope='carousel'][data-part='root'][data-paused]
313
+ [data-scope='carousel'][data-part='indicator'][data-current]::before {
314
+ content: '';
315
+ position: absolute;
316
+ inset: 0;
317
+ border-radius: inherit;
192
318
  background: var(--xh-carousel-indicator-bg-selected, var(--xh-bg-brand));
319
+ transform-origin: left center;
320
+ animation: xh-carousel-indicator-progress var(--xh-_carousel-autoplay-duration) var(--xh-motion-ease-loop) both;
321
+ }
322
+
323
+ [data-scope='carousel'][data-part='root'][data-paused]
324
+ [data-scope='carousel'][data-part='indicator'][data-current]::before {
325
+ animation-play-state: paused;
326
+ }
327
+
328
+ /* 纵轨:胶囊沿块轴拉长,进度条从上往下长 */
329
+ [data-scope='carousel'][data-part='indicator-group'][data-orientation='vertical']
330
+ > [data-scope='carousel'][data-part='indicator'][data-current] {
331
+ inline-size: var(--xh-carousel-indicator-size, var(--xh-space-2));
332
+ block-size: var(--xh-carousel-indicator-size-current, var(--xh-space-5));
333
+ }
334
+
335
+ [data-scope='carousel'][data-part='indicator-group'][data-orientation='vertical']
336
+ > [data-scope='carousel'][data-part='indicator'][data-current]::before {
337
+ transform-origin: center top;
338
+ }
339
+
340
+ [dir='rtl'] [data-scope='carousel'][data-part='indicator-group'][data-orientation='horizontal']
341
+ > [data-scope='carousel'][data-part='indicator']::before {
342
+ transform-origin: right center;
343
+ }
344
+
345
+ [data-scope='carousel'][data-part='root'][data-orientation='vertical']
346
+ > :is([data-part='prev-trigger'], [data-part='next-trigger']) {
347
+ inset-inline-start: 50%;
348
+ inset-inline-end: auto;
349
+ translate: -50% 0;
350
+ rotate: 90deg;
351
+ }
352
+
353
+ [data-scope='carousel'][data-part='root'][data-orientation='vertical']
354
+ > [data-part='prev-trigger'] {
355
+ inset-block-start: var(--xh-carousel-control-inset, var(--xh-space-3));
356
+ }
357
+
358
+ [data-scope='carousel'][data-part='root'][data-orientation='vertical']
359
+ > [data-part='next-trigger'] {
360
+ inset-block-start: auto;
361
+ inset-block-end: var(--xh-carousel-control-inset, var(--xh-space-3));
193
362
  }
194
363
 
195
364
  /* 减少动效:位移立刻到位。翻页本身照常发生,只是不再有滑动的过程。
196
365
  两条规则是同一件事的两个触发条件:系统偏好与作者打的 data-motion */
197
366
  @media (prefers-reduced-motion: reduce) {
198
- [data-scope='carousel'][data-part='item-group'] {
367
+ [data-scope='carousel'][data-part='list'] {
199
368
  transition: none;
200
369
  }
370
+
371
+ [data-scope='carousel'][data-part='indicator'][data-current]::before {
372
+ animation: none;
373
+ scale: var(--xh-_carousel-progress-to);
374
+ }
201
375
  }
202
376
 
203
- :where([data-motion='reduce']) [data-scope='carousel'][data-part='item-group'] {
377
+ :where([data-motion='reduce']) [data-scope='carousel'][data-part='list'] {
204
378
  transition: none;
205
379
  }
206
380
 
381
+ :where([data-motion='reduce']) [data-scope='carousel'][data-part='indicator'][data-current]::before {
382
+ animation: none;
383
+ scale: var(--xh-_carousel-progress-to);
384
+ }
385
+
386
+ @media (forced-colors: active) {
387
+ [data-scope='carousel'][data-part='indicator'],
388
+ [data-scope='carousel'][data-part='indicator']::after {
389
+ background: GrayText;
390
+ forced-color-adjust: none;
391
+ }
392
+
393
+ [data-scope='carousel'][data-part='indicator'][data-current],
394
+ [data-scope='carousel'][data-part='indicator'][data-current]::after,
395
+ [data-scope='carousel'][data-part='indicator'][data-current]::before {
396
+ background: Highlight;
397
+ forced-color-adjust: none;
398
+ }
399
+
400
+ /* 自动播放时轨道退回画布色、描一圈 Highlight 边,进度条仍是 Highlight:走没走在动由描边与填充的差表出 */
401
+ [data-scope='carousel'][data-part='root'][data-autoplay]
402
+ [data-scope='carousel'][data-part='indicator'][data-current],
403
+ [data-scope='carousel'][data-part='root'][data-paused]
404
+ [data-scope='carousel'][data-part='indicator'][data-current],
405
+ [data-scope='carousel'][data-part='root'][data-autoplay]
406
+ [data-scope='carousel'][data-part='indicator'][data-current]::after,
407
+ [data-scope='carousel'][data-part='root'][data-paused]
408
+ [data-scope='carousel'][data-part='indicator'][data-current]::after {
409
+ border: var(--xh-stroke-thin) solid Highlight;
410
+ background: Canvas;
411
+ }
412
+
413
+ /* 按下:常态靠底色再压一档并缩一下表出,这一档里底色被钉成系统色、缩放又看不出来;
414
+ 描一圈系统前景色把按住那一下画回来,粗指针下点画在 ::after 上,同一圈描到它身上 */
415
+ [data-scope='carousel'][data-part='indicator']:is(:active, [data-pressed]),
416
+ [data-scope='carousel'][data-part='indicator']:is(:active, [data-pressed])::after {
417
+ border: var(--xh-stroke-thin) solid CanvasText;
418
+ }
419
+ }
420
+
207
421
  /* 没写内容就由皮肤画兜底字形;作者往部件里塞了自己的图形,这几条即不命中 */
208
422
  [data-scope='carousel'][data-part='prev-trigger']:empty::before {
209
423
  content: '';
@@ -226,4 +440,134 @@
226
440
  -webkit-mask: var(--xh-glyph-mark-chevron-right) center / contain no-repeat;
227
441
  mask: var(--xh-glyph-mark-chevron-right) center / contain no-repeat;
228
442
  }
443
+
444
+ /* 播放开关的字形随状态换:正在播画暂停、停着画播放,与按钮名字指的是同一个动作 */
445
+ [data-scope='carousel'][data-part='autoplay-trigger'][data-state='running']:empty::before {
446
+ content: '';
447
+ display: inline-block;
448
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
449
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
450
+ vertical-align: var(--xh-glyph-baseline-shift);
451
+ background-color: currentColor;
452
+ -webkit-mask: var(--xh-glyph-mark-pause) center / contain no-repeat;
453
+ mask: var(--xh-glyph-mark-pause) center / contain no-repeat;
454
+ }
455
+
456
+ [data-scope='carousel'][data-part='autoplay-trigger'][data-state='paused']:empty::before {
457
+ content: '';
458
+ display: inline-block;
459
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
460
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
461
+ vertical-align: var(--xh-glyph-baseline-shift);
462
+ background-color: currentColor;
463
+ -webkit-mask: var(--xh-glyph-mark-play) center / contain no-repeat;
464
+ mask: var(--xh-glyph-mark-play) center / contain no-repeat;
465
+ }
466
+
467
+ /* 手指落点:三颗钮的 44px 命中区由配方的 ::after 给(floating 档)。
468
+ 分页点的视觉仍是 8 / 20px,按钮盒改成首尾相接的 44px 分区:直接用负 inset 外扩会让相邻页的命中区重叠,
469
+ 落点归属取决于绘制顺序;这里让真实按钮盒承担命中,点与进度条改由伪元素画在盒子中央 */
470
+ @media (pointer: coarse) {
471
+ [data-scope='carousel'][data-part='indicator-group'] {
472
+ gap: 0;
473
+ }
474
+
475
+ /* 盒子只管命中:四个状态的底色都从盒子上撤下来,落到 ::after 画的点上 */
476
+ [data-scope='carousel'][data-part='indicator'],
477
+ [data-scope='carousel'][data-part='indicator']:hover:not([data-current]),
478
+ [data-scope='carousel'][data-part='indicator']:is(:active, [data-pressed]):not([data-current]),
479
+ [data-scope='carousel'][data-part='indicator'][data-current],
480
+ [data-scope='carousel'][data-part='indicator'][data-current]:is(:active, [data-pressed]),
481
+ [data-scope='carousel'][data-part='root']:not([data-autoplay], [data-paused])
482
+ [data-scope='carousel'][data-part='indicator'][data-current],
483
+ [data-scope='carousel'][data-part='root'][data-autoplay]
484
+ [data-scope='carousel'][data-part='indicator'][data-current],
485
+ [data-scope='carousel'][data-part='root'][data-paused]
486
+ [data-scope='carousel'][data-part='indicator'][data-current] {
487
+ min-inline-size: var(--xh-carousel-indicator-target-size, 44px);
488
+ min-block-size: var(--xh-carousel-indicator-target-size, 44px);
489
+ border-radius: 0;
490
+ background: transparent;
491
+ overflow: visible;
492
+ }
493
+
494
+ [data-scope='carousel'][data-part='indicator']::after {
495
+ inset: auto;
496
+ inset-block-start: 50%;
497
+ inset-inline-start: 50%;
498
+ inline-size: var(--xh-carousel-indicator-size, var(--xh-space-2));
499
+ block-size: var(--xh-carousel-indicator-size, var(--xh-space-2));
500
+ border-radius: var(--xh-carousel-indicator-radius, var(--xh-shape-circle));
501
+ background: var(--xh-carousel-indicator-bg, var(--xh-border-default));
502
+ translate: -50% -50%;
503
+ transition:
504
+ inline-size var(--xh-motion-duration-micro) var(--xh-motion-ease-continuous),
505
+ block-size var(--xh-motion-duration-micro) var(--xh-motion-ease-continuous),
506
+ background-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
507
+ }
508
+
509
+ [data-scope='carousel'][data-part='indicator']:is(:active, [data-pressed]):not([data-current])::after {
510
+ background: var(--xh-carousel-indicator-bg-active, var(--xh-fg-default));
511
+ }
512
+
513
+ [data-scope='carousel'][data-part='indicator'][data-current]::after {
514
+ inline-size: var(--xh-carousel-indicator-size-current, var(--xh-space-5));
515
+ border-radius: var(--xh-carousel-indicator-radius-current, var(--xh-shape-pill));
516
+ }
517
+
518
+ [data-scope='carousel'][data-part='root']:not([data-autoplay], [data-paused])
519
+ [data-scope='carousel'][data-part='indicator'][data-current]::after {
520
+ background: var(--xh-carousel-indicator-bg-selected, var(--xh-bg-brand));
521
+ }
522
+
523
+ [data-scope='carousel'][data-part='indicator'][data-current]:is(:active, [data-pressed])::after {
524
+ background: var(--xh-carousel-indicator-bg-selected-active, var(--xh-bg-brand-active));
525
+ }
526
+
527
+ [data-scope='carousel'][data-part='root'][data-autoplay]
528
+ [data-scope='carousel'][data-part='indicator'][data-current]::after,
529
+ [data-scope='carousel'][data-part='root'][data-paused]
530
+ [data-scope='carousel'][data-part='indicator'][data-current]::after {
531
+ background: var(--xh-carousel-indicator-bg-track, var(--xh-bg-brand-subtle));
532
+ }
533
+
534
+ /* 进度条落到点的那块盒上,跟 ::after 画的胶囊同位同形 */
535
+ [data-scope='carousel'][data-part='root'][data-autoplay]
536
+ [data-scope='carousel'][data-part='indicator'][data-current]::before,
537
+ [data-scope='carousel'][data-part='root'][data-paused]
538
+ [data-scope='carousel'][data-part='indicator'][data-current]::before {
539
+ inset: auto;
540
+ inset-block-start: 50%;
541
+ inset-inline-start: 50%;
542
+ z-index: 1;
543
+ inline-size: var(--xh-carousel-indicator-size-current, var(--xh-space-5));
544
+ block-size: var(--xh-carousel-indicator-size, var(--xh-space-2));
545
+ border-radius: var(--xh-carousel-indicator-radius-current, var(--xh-shape-pill));
546
+ translate: -50% -50%;
547
+ }
548
+
549
+ [data-scope='carousel'][data-part='indicator-group'][data-orientation='vertical']
550
+ > [data-scope='carousel'][data-part='indicator'][data-current]::after,
551
+ [data-scope='carousel'][data-part='root'][data-autoplay]
552
+ [data-scope='carousel'][data-part='indicator-group'][data-orientation='vertical']
553
+ > [data-scope='carousel'][data-part='indicator'][data-current]::before,
554
+ [data-scope='carousel'][data-part='root'][data-paused]
555
+ [data-scope='carousel'][data-part='indicator-group'][data-orientation='vertical']
556
+ > [data-scope='carousel'][data-part='indicator'][data-current]::before {
557
+ inline-size: var(--xh-carousel-indicator-size, var(--xh-space-2));
558
+ block-size: var(--xh-carousel-indicator-size-current, var(--xh-space-5));
559
+ }
560
+ }
561
+ }
562
+
563
+ @layer xihan.motion {
564
+ @keyframes xh-carousel-indicator-progress {
565
+ from {
566
+ scale: var(--xh-_carousel-progress-from);
567
+ }
568
+
569
+ to {
570
+ scale: var(--xh-_carousel-progress-to);
571
+ }
572
+ }
229
573
  }