@xihan-ui/styles 1.0.0-alpha.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 (114) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/LICENSE +21 -0
  3. package/README.md +27 -0
  4. package/css/accordion.css +108 -0
  5. package/css/affix.css +27 -0
  6. package/css/alert.css +108 -0
  7. package/css/anchor.css +137 -0
  8. package/css/avatar-group.css +82 -0
  9. package/css/avatar.css +55 -0
  10. package/css/back-top.css +61 -0
  11. package/css/badge.css +50 -0
  12. package/css/breadcrumb.css +127 -0
  13. package/css/button-group.css +131 -0
  14. package/css/button.css +98 -0
  15. package/css/calendar.css +219 -0
  16. package/css/card.css +116 -0
  17. package/css/carousel.css +193 -0
  18. package/css/cascader.css +397 -0
  19. package/css/checkbox-group.css +185 -0
  20. package/css/checkbox.css +91 -0
  21. package/css/clipboard.css +133 -0
  22. package/css/code-block.css +81 -0
  23. package/css/collapsible.css +63 -0
  24. package/css/color-picker.css +424 -0
  25. package/css/combobox.css +433 -0
  26. package/css/composer.css +127 -0
  27. package/css/context-menu.css +263 -0
  28. package/css/countdown.css +21 -0
  29. package/css/date-field.css +200 -0
  30. package/css/date-picker.css +231 -0
  31. package/css/descriptions.css +200 -0
  32. package/css/dialog.css +127 -0
  33. package/css/drawer.css +372 -0
  34. package/css/dynamic-input.css +157 -0
  35. package/css/editable.css +249 -0
  36. package/css/ellipsis.css +41 -0
  37. package/css/empty-state.css +78 -0
  38. package/css/field.css +65 -0
  39. package/css/file-upload.css +298 -0
  40. package/css/flex.css +95 -0
  41. package/css/float-button.css +121 -0
  42. package/css/form.css +185 -0
  43. package/css/gradient-text.css +62 -0
  44. package/css/grid.css +428 -0
  45. package/css/highlight.css +21 -0
  46. package/css/hover-card.css +146 -0
  47. package/css/icon-wrapper.css +64 -0
  48. package/css/icon.css +54 -0
  49. package/css/image.css +45 -0
  50. package/css/infinite-scroll.css +22 -0
  51. package/css/layers.css +2 -0
  52. package/css/layout.css +169 -0
  53. package/css/list.css +117 -0
  54. package/css/listbox.css +174 -0
  55. package/css/loading-bar.css +66 -0
  56. package/css/log.css +66 -0
  57. package/css/marquee.css +182 -0
  58. package/css/mention.css +222 -0
  59. package/css/menu.css +144 -0
  60. package/css/menubar.css +271 -0
  61. package/css/motion.css +58 -0
  62. package/css/navigation-menu.css +270 -0
  63. package/css/number-animation.css +32 -0
  64. package/css/number-field.css +219 -0
  65. package/css/page-header.css +85 -0
  66. package/css/pagination.css +148 -0
  67. package/css/pin-input.css +157 -0
  68. package/css/popconfirm.css +150 -0
  69. package/css/popover.css +150 -0
  70. package/css/popselect.css +207 -0
  71. package/css/progress.css +37 -0
  72. package/css/qr-code.css +50 -0
  73. package/css/radio-group.css +101 -0
  74. package/css/rating.css +141 -0
  75. package/css/reset.css +22 -0
  76. package/css/result.css +104 -0
  77. package/css/scroll-area.css +176 -0
  78. package/css/select.css +264 -0
  79. package/css/separator.css +17 -0
  80. package/css/skeleton.css +78 -0
  81. package/css/slider.css +197 -0
  82. package/css/spinner.css +74 -0
  83. package/css/splitter.css +127 -0
  84. package/css/statistic.css +67 -0
  85. package/css/steps.css +252 -0
  86. package/css/switch.css +61 -0
  87. package/css/table.css +404 -0
  88. package/css/tabs.css +157 -0
  89. package/css/tags-input.css +360 -0
  90. package/css/text-field.css +195 -0
  91. package/css/thread.css +107 -0
  92. package/css/time-field.css +200 -0
  93. package/css/time-picker.css +347 -0
  94. package/css/time.css +20 -0
  95. package/css/timeline.css +284 -0
  96. package/css/toast.css +195 -0
  97. package/css/toaster.css +69 -0
  98. package/css/toggle-group.css +160 -0
  99. package/css/toggle.css +103 -0
  100. package/css/tone.css +85 -0
  101. package/css/toolbar.css +114 -0
  102. package/css/tooltip.css +109 -0
  103. package/css/tour.css +305 -0
  104. package/css/transfer.css +318 -0
  105. package/css/tree-select.css +441 -0
  106. package/css/tree.css +204 -0
  107. package/css/typography.css +151 -0
  108. package/css/undefined.css +38 -0
  109. package/css/virtualizer.css +104 -0
  110. package/css/visually-hidden.css +13 -0
  111. package/css/watermark.css +48 -0
  112. package/index.css +113 -0
  113. package/index.unlayered.css +16164 -0
  114. package/package.json +163 -0
package/css/drawer.css ADDED
@@ -0,0 +1,372 @@
1
+ @layer xihan.motion {
2
+ /* 四条边各一对滑入/滑出。位移量用 translate 的百分比,参照的是面板自己的边框盒,
3
+ 所以不管 --xh-drawer-size 被改成多少,"正好推到视口外"这件事都成立。
4
+
5
+ 位移写成两个私有槽位而不是写死 ±100%:贴边用的是逻辑 inset(inset-inline-start 等),
6
+ RTL 下 inline-end 落在物理左边;transform 没有逻辑等价物,若这里写死物理方向,
7
+ 就会出现"面板贴在左边、却从右边滑进来"。槽位在 RTL 下翻符号,两者才始终同向。
8
+ 竖排书写模式下 block 轴不再是 Y 轴,位移会退化为纵向,但贴边本身仍由逻辑 inset 保证。 */
9
+ @keyframes xh-drawer-in-right {
10
+ from {
11
+ translate: var(--xh-_drawer-shift-inline, 100%) 0;
12
+ }
13
+
14
+ to {
15
+ translate: 0 0;
16
+ }
17
+ }
18
+
19
+ @keyframes xh-drawer-out-right {
20
+ from {
21
+ translate: 0 0;
22
+ }
23
+
24
+ to {
25
+ translate: var(--xh-_drawer-shift-inline, 100%) 0;
26
+ }
27
+ }
28
+
29
+ @keyframes xh-drawer-in-left {
30
+ from {
31
+ translate: calc(-1 * var(--xh-_drawer-shift-inline, 100%)) 0;
32
+ }
33
+
34
+ to {
35
+ translate: 0 0;
36
+ }
37
+ }
38
+
39
+ @keyframes xh-drawer-out-left {
40
+ from {
41
+ translate: 0 0;
42
+ }
43
+
44
+ to {
45
+ translate: calc(-1 * var(--xh-_drawer-shift-inline, 100%)) 0;
46
+ }
47
+ }
48
+
49
+ @keyframes xh-drawer-in-top {
50
+ from {
51
+ translate: 0 calc(-1 * var(--xh-_drawer-shift-block, 100%));
52
+ }
53
+
54
+ to {
55
+ translate: 0 0;
56
+ }
57
+ }
58
+
59
+ @keyframes xh-drawer-out-top {
60
+ from {
61
+ translate: 0 0;
62
+ }
63
+
64
+ to {
65
+ translate: 0 calc(-1 * var(--xh-_drawer-shift-block, 100%));
66
+ }
67
+ }
68
+
69
+ @keyframes xh-drawer-in-bottom {
70
+ from {
71
+ translate: 0 var(--xh-_drawer-shift-block, 100%);
72
+ }
73
+
74
+ to {
75
+ translate: 0 0;
76
+ }
77
+ }
78
+
79
+ @keyframes xh-drawer-out-bottom {
80
+ from {
81
+ translate: 0 0;
82
+ }
83
+
84
+ to {
85
+ translate: 0 var(--xh-_drawer-shift-block, 100%);
86
+ }
87
+ }
88
+
89
+ @media (prefers-reduced-motion: reduce) {
90
+ [data-scope='drawer'][data-part] {
91
+ animation-duration: 0.01ms !important;
92
+ }
93
+ }
94
+ }
95
+
96
+ @layer xihan.components {
97
+ /* root 留在页面原地,只是 data-side / data-state 的落点,不该凭空多出一层盒子把
98
+ trigger 从作者的 flex / grid 行里挤出去,故 display: contents。 */
99
+ [data-scope='drawer'][data-part='root'] {
100
+ display: contents;
101
+ }
102
+
103
+ /* 声明了 display 就盖掉了 UA 的 [hidden]{display:none},作者手写 hidden 时得还回去 */
104
+ [data-scope='drawer'][data-part='root'][hidden] {
105
+ display: none;
106
+ }
107
+
108
+ [data-scope='drawer'][data-part='trigger'] {
109
+ display: inline-flex;
110
+ align-items: center;
111
+ justify-content: center;
112
+ gap: var(--xh-drawer-trigger-gap, var(--xh-control-gap-md));
113
+ block-size: var(--xh-drawer-trigger-h, var(--xh-control-h-md));
114
+ padding-inline: var(--xh-drawer-trigger-px, var(--xh-control-px-md));
115
+ border: var(--xh-stroke-thin) solid var(--xh-drawer-trigger-border, var(--xh-border-default));
116
+ border-radius: var(--xh-drawer-trigger-radius, var(--xh-shape-control));
117
+ background: var(--xh-drawer-trigger-bg, var(--xh-bg-canvas));
118
+ color: var(--xh-drawer-trigger-fg, var(--xh-fg-default));
119
+ font-size: var(--xh-drawer-trigger-font-size, var(--xh-text-label-size));
120
+ font-weight: var(--xh-drawer-trigger-font-weight, var(--xh-text-label-weight));
121
+ line-height: var(--xh-leading-none);
122
+ white-space: nowrap;
123
+ cursor: pointer;
124
+ transition:
125
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
126
+ border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
127
+ }
128
+
129
+ [data-scope='drawer'][data-part='trigger'][hidden] {
130
+ display: none;
131
+ }
132
+
133
+ [data-scope='drawer'][data-part='trigger']:hover:not(:disabled) {
134
+ background: var(--xh-drawer-trigger-bg-hover, var(--xh-bg-subtle-hover));
135
+ border-color: var(--xh-drawer-trigger-border-hover, var(--xh-border-strong));
136
+ }
137
+
138
+ /* 抽屉展开期间触发按钮压住不弹起:面板贴在视口边上,它和按钮往往离得很远,
139
+ 按钮自己得说明"这一扇是我开的"。 */
140
+ [data-scope='drawer'][data-part='trigger'][data-state='open'] {
141
+ background: var(--xh-drawer-trigger-bg-open, var(--xh-bg-subtle-active));
142
+ border-color: var(--xh-drawer-trigger-border-open, var(--xh-border-strong));
143
+ }
144
+
145
+ [data-scope='drawer'][data-part='trigger']:focus-visible {
146
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
147
+ outline-offset: var(--xh-ring-offset);
148
+ }
149
+
150
+ /* trigger 是单体控件,禁用走原生 disabled */
151
+ [data-scope='drawer'][data-part='trigger']:disabled {
152
+ color: var(--xh-fg-disabled);
153
+ cursor: not-allowed;
154
+ }
155
+
156
+ [data-scope='drawer'][data-part='backdrop'] {
157
+ position: fixed;
158
+ inset: 0;
159
+ z-index: var(--xh-layer-drawer);
160
+ background: var(--xh-drawer-backdrop-bg, var(--xh-bg-overlay));
161
+ }
162
+
163
+ [data-scope='drawer'][data-part='backdrop'][data-state='open'] {
164
+ animation: xh-fade-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
165
+ }
166
+
167
+ /* 退场补 forwards:动画播完到宿主真正卸载之间还有一帧,不定住终态遮罩会闪回全黑 */
168
+ [data-scope='drawer'][data-part='backdrop'][data-state='closed'] {
169
+ animation: xh-fade-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
170
+ }
171
+
172
+ /* positioner 只负责把浮层抬到抽屉层,贴边一概不管(原因见 content)。
173
+ 铺满视口是为了在自己这层里罩住 content,但不吃指针:非模态抽屉展开时
174
+ 页面其余部分还要能点,模态的拦截由 backdrop 负责。 */
175
+ [data-scope='drawer'][data-part='positioner'] {
176
+ position: fixed;
177
+ inset: 0;
178
+ z-index: var(--xh-layer-drawer);
179
+ pointer-events: none;
180
+ }
181
+
182
+ /* 贴边写在 content 自己身上,而不是交给 positioner:
183
+ positioner 在抽屉里不是必需部件,最小合规结构只有 root + content,
184
+ 贴边若挂在 positioner 上,那种结构下面板会直接塌回文档流。
185
+ content 是 fixed,包住它的 positioner 没有 transform / filter,
186
+ 不构成固定定位的包含块,两种结构下参照的都是视口,位置一致。 */
187
+ [data-scope='drawer'][data-part='content'] {
188
+ /* 位移方向的私有槽位:默认按 LTR,RTL 在下面整体翻符号 */
189
+ --xh-_drawer-shift-inline: 100%;
190
+ --xh-_drawer-shift-block: 100%;
191
+ /* 尺寸档只换这一个私有槽:左右放置时它是面板宽度、上下放置时是面板高度,
192
+ 两种取法都在下面「四条边」那两条规则里。声明在 content 而不是 root:
193
+ content 会被 portal 到 body,root 上的槽位继承不过来(data-side 也是同一个道理) */
194
+ --xh-_drawer-size: 20rem;
195
+
196
+ position: fixed;
197
+ z-index: var(--xh-layer-drawer);
198
+ display: flex;
199
+ flex-direction: column;
200
+ gap: var(--xh-drawer-gap, var(--xh-stack-gap-md));
201
+ padding-block: var(--xh-drawer-py, var(--xh-section-py));
202
+ padding-inline: var(--xh-drawer-px, var(--xh-control-px-lg));
203
+ background: var(--xh-drawer-bg, var(--xh-bg-surface));
204
+ color: var(--xh-drawer-fg, var(--xh-fg-default));
205
+ box-shadow: var(--xh-drawer-shadow, var(--xh-elevation-3));
206
+ overflow: auto;
207
+ /* 尺寸是照视口那条边算出来的,padding 再往外顶就会把面板挤出屏幕、
208
+ 逼出一条横向滚动条。reset 层已有同款兜底,这里显式写一遍表明尺寸算法依赖它 */
209
+ box-sizing: border-box;
210
+ /* positioner 整层不吃指针,面板得自己收回来 */
211
+ pointer-events: auto;
212
+ }
213
+
214
+ [data-scope='drawer'][data-part='content']:dir(rtl) {
215
+ --xh-_drawer-shift-inline: -100%;
216
+ }
217
+
218
+ /* 这里刻意没有 [data-part='content'][hidden] { display: none },与 dialog 一致。
219
+ 收起态连接层会给 content 打 hidden,而上面这条 display: flex 正是要盖掉
220
+ UA 的 [hidden]{display:none}——退场动画得先能播:
221
+ Vue 侧 presence 读的是 content 的 animationName,读到就申领租约、等 animationend
222
+ 才卸载;一旦 display: none,动画根本不会启动,animationend 永不到达,
223
+ 租约挂死,面板就永久留在 DOM 里(每开合一次多一份)。
224
+ 真正的收起两个适配器各自兜住了:Vue 卸载整棵,WC 写内联 style.display。
225
+ 其余声明了 display 的角色节点仍补了 [hidden],那几处没有退场动画,作者手写 hidden 要生效。 */
226
+
227
+ /* 面板是焦点陷阱的兜底落点(tabindex="-1"),落焦不画环:环长在里面的可聚焦控件上 */
228
+ [data-scope='drawer'][data-part='content']:focus-visible {
229
+ outline: none;
230
+ }
231
+
232
+ /* —— 尺寸 —— */
233
+ /* 只有面板厚度分档;中间档不写规则,缺省即中档。
234
+ data-size 与 data-side 一样同时打在 root 与 content 上,这里只认 content 自己那一份。 */
235
+ [data-scope='drawer'][data-part='content'][data-size='sm'] {
236
+ --xh-_drawer-size: 16rem;
237
+ }
238
+
239
+ [data-scope='drawer'][data-part='content'][data-size='lg'] {
240
+ --xh-_drawer-size: 28rem;
241
+ }
242
+
243
+ /* —— 四条边 ——
244
+ data-side 同时打在 root 与 content 上;content 会被 portal 到 body,
245
+ 选择器只认得 content 自己身上这一份。 */
246
+
247
+ /* 左右:满高、定宽 */
248
+ [data-scope='drawer'][data-part='content'][data-side='left'],
249
+ [data-scope='drawer'][data-part='content'][data-side='right'] {
250
+ inset-block: 0;
251
+ inline-size: var(--xh-drawer-size, var(--xh-_drawer-size));
252
+ /* 视口比抽屉还窄时让位,否则面板会顶出一条横向滚动条 */
253
+ max-inline-size: 100%;
254
+ }
255
+
256
+ /* 只圆朝向页面那两个角:贴住视口的那条边圆角是看不见的,圆了反而露出底下的页面 */
257
+ [data-scope='drawer'][data-part='content'][data-side='left'] {
258
+ inset-inline-start: 0;
259
+ border-start-end-radius: var(--xh-drawer-radius, var(--xh-shape-surface));
260
+ border-end-end-radius: var(--xh-drawer-radius, var(--xh-shape-surface));
261
+ }
262
+
263
+ [data-scope='drawer'][data-part='content'][data-side='right'] {
264
+ inset-inline-end: 0;
265
+ border-start-start-radius: var(--xh-drawer-radius, var(--xh-shape-surface));
266
+ border-end-start-radius: var(--xh-drawer-radius, var(--xh-shape-surface));
267
+ }
268
+
269
+ /* 上下:满宽、定高 */
270
+ [data-scope='drawer'][data-part='content'][data-side='top'],
271
+ [data-scope='drawer'][data-part='content'][data-side='bottom'] {
272
+ inset-inline: 0;
273
+ block-size: var(--xh-drawer-size, var(--xh-_drawer-size));
274
+ max-block-size: 100%;
275
+ }
276
+
277
+ [data-scope='drawer'][data-part='content'][data-side='top'] {
278
+ inset-block-start: 0;
279
+ border-end-start-radius: var(--xh-drawer-radius, var(--xh-shape-surface));
280
+ border-end-end-radius: var(--xh-drawer-radius, var(--xh-shape-surface));
281
+ }
282
+
283
+ [data-scope='drawer'][data-part='content'][data-side='bottom'] {
284
+ inset-block-end: 0;
285
+ border-start-start-radius: var(--xh-drawer-radius, var(--xh-shape-surface));
286
+ border-start-end-radius: var(--xh-drawer-radius, var(--xh-shape-surface));
287
+ }
288
+
289
+ /* 滑入/滑出按边配对。退场一律 forwards:presence 要等 animationend 才卸载,
290
+ 不定住终态,面板会在最后一帧弹回原位再消失。 */
291
+ [data-scope='drawer'][data-part='content'][data-side='right'][data-state='open'] {
292
+ animation: xh-drawer-in-right var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
293
+ }
294
+
295
+ [data-scope='drawer'][data-part='content'][data-side='right'][data-state='closed'] {
296
+ animation: xh-drawer-out-right var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
297
+ }
298
+
299
+ [data-scope='drawer'][data-part='content'][data-side='left'][data-state='open'] {
300
+ animation: xh-drawer-in-left var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
301
+ }
302
+
303
+ [data-scope='drawer'][data-part='content'][data-side='left'][data-state='closed'] {
304
+ animation: xh-drawer-out-left var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
305
+ }
306
+
307
+ [data-scope='drawer'][data-part='content'][data-side='top'][data-state='open'] {
308
+ animation: xh-drawer-in-top var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
309
+ }
310
+
311
+ [data-scope='drawer'][data-part='content'][data-side='top'][data-state='closed'] {
312
+ animation: xh-drawer-out-top var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
313
+ }
314
+
315
+ [data-scope='drawer'][data-part='content'][data-side='bottom'][data-state='open'] {
316
+ animation: xh-drawer-in-bottom var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
317
+ }
318
+
319
+ [data-scope='drawer'][data-part='content'][data-side='bottom'][data-state='closed'] {
320
+ animation: xh-drawer-out-bottom var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
321
+ }
322
+
323
+ [data-scope='drawer'][data-part='title'] {
324
+ margin: 0;
325
+ font-size: var(--xh-drawer-title-font-size, var(--xh-text-heading-3-size));
326
+ font-weight: var(--xh-drawer-title-font-weight, var(--xh-text-heading-3-weight));
327
+ color: var(--xh-drawer-title-fg, var(--xh-fg-default));
328
+ }
329
+
330
+ [data-scope='drawer'][data-part='description'] {
331
+ margin: 0;
332
+ font-size: var(--xh-text-body-size);
333
+ color: var(--xh-drawer-description-fg, var(--xh-fg-muted));
334
+ }
335
+
336
+ /* 关闭按钮是绝对定位的,不占流内宽度:标题得自己让出那块角,
337
+ 否则长标题会直接钻到按钮底下。只在真有关闭按钮时让位。 */
338
+ [data-scope='drawer'][data-part='content']:has([data-scope='drawer'][data-part='close-trigger'])
339
+ [data-scope='drawer'][data-part='title'] {
340
+ padding-inline-end: calc(var(--xh-control-h-sm) + var(--xh-space-3));
341
+ }
342
+
343
+ [data-scope='drawer'][data-part='close-trigger'] {
344
+ position: absolute;
345
+ inset-block-start: var(--xh-space-3);
346
+ inset-inline-end: var(--xh-space-3);
347
+ display: inline-flex;
348
+ align-items: center;
349
+ justify-content: center;
350
+ block-size: var(--xh-control-h-sm);
351
+ inline-size: var(--xh-control-h-sm);
352
+ border: 0;
353
+ border-radius: var(--xh-shape-control);
354
+ background: transparent;
355
+ color: var(--xh-fg-muted);
356
+ cursor: pointer;
357
+ }
358
+
359
+ [data-scope='drawer'][data-part='close-trigger'][hidden] {
360
+ display: none;
361
+ }
362
+
363
+ [data-scope='drawer'][data-part='close-trigger']:hover {
364
+ background: var(--xh-bg-subtle);
365
+ color: var(--xh-fg-default);
366
+ }
367
+
368
+ [data-scope='drawer'][data-part='close-trigger']:focus-visible {
369
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
370
+ outline-offset: var(--xh-ring-offset);
371
+ }
372
+ }
@@ -0,0 +1,157 @@
1
+ @layer xihan.components {
2
+ /* 一行一条,纵向堆叠;新增把手跟在最后一行下面 */
3
+ [data-scope='dynamic-input'][data-part='root'] {
4
+ display: flex;
5
+ flex-direction: column;
6
+ gap: var(--xh-dynamic-input-gap, var(--xh-space-2));
7
+ }
8
+
9
+ /* 声明了 display 就盖掉了 UA 的 [hidden]{display:none},收起态得自己还回去 */
10
+ [data-scope='dynamic-input'][data-part='root'][hidden] {
11
+ display: none;
12
+ }
13
+
14
+ /* 内容在左、把手在右,把手那一列不随内容伸缩 */
15
+ [data-scope='dynamic-input'][data-part='item'] {
16
+ display: flex;
17
+ align-items: center;
18
+ gap: var(--xh-dynamic-input-item-gap, var(--xh-space-2));
19
+ padding: var(--xh-dynamic-input-item-padding, var(--xh-space-0));
20
+ border-radius: var(--xh-dynamic-input-item-radius, var(--xh-shape-surface));
21
+ }
22
+
23
+ [data-scope='dynamic-input'][data-part='item'][hidden] {
24
+ display: none;
25
+ }
26
+
27
+ /* 行里放什么控件归作者,这里只给它一条能撑开也能收窄的轨道。
28
+ min-inline-size 归零:不写的话里面的输入框按 UA 默认宽度顶住,长内容会把整行撑破 */
29
+ [data-scope='dynamic-input'][data-part='item-content'] {
30
+ display: flex;
31
+ flex: 1 1 auto;
32
+ align-items: center;
33
+ gap: var(--xh-dynamic-input-content-gap, var(--xh-space-2));
34
+ min-inline-size: 0;
35
+ }
36
+
37
+ [data-scope='dynamic-input'][data-part='item-action'] {
38
+ display: inline-flex;
39
+ flex: none;
40
+ align-items: center;
41
+ gap: var(--xh-dynamic-input-action-gap, var(--xh-space-1));
42
+ }
43
+
44
+ /* —— 行内三个把手 —— */
45
+ [data-scope='dynamic-input'][data-part='remove-trigger'],
46
+ [data-scope='dynamic-input'][data-part='move-up-trigger'],
47
+ [data-scope='dynamic-input'][data-part='move-down-trigger'] {
48
+ display: inline-flex;
49
+ flex: none;
50
+ align-items: center;
51
+ justify-content: center;
52
+ inline-size: var(--xh-dynamic-input-trigger-size, var(--xh-control-action-size));
53
+ block-size: var(--xh-dynamic-input-trigger-size, var(--xh-control-action-size));
54
+ padding: 0;
55
+ border: 0;
56
+ border-radius: var(--xh-dynamic-input-trigger-radius, var(--xh-shape-control));
57
+ background: var(--xh-dynamic-input-trigger-bg, transparent);
58
+ color: var(--xh-dynamic-input-trigger-fg, var(--xh-fg-muted));
59
+ font-family: inherit;
60
+ font-size: var(--xh-dynamic-input-trigger-font-size, var(--xh-font-size-sm));
61
+ line-height: var(--xh-leading-none);
62
+ cursor: pointer;
63
+ transition: background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
64
+ }
65
+
66
+ /* 不换序时连接层给换序把手写 hidden;上面那条 display 会把 UA 的 [hidden]{display:none}
67
+ 盖掉,少了这条把手还留在原地。删除把手同样声明了 display,作者自己收起它时要的是同一条 */
68
+ [data-scope='dynamic-input'][data-part='remove-trigger'][hidden],
69
+ [data-scope='dynamic-input'][data-part='move-up-trigger'][hidden],
70
+ [data-scope='dynamic-input'][data-part='move-down-trigger'][hidden] {
71
+ display: none;
72
+ }
73
+
74
+ /* 按不动的把手不给悬停与按下反馈:它仍然可聚焦、可点,反馈会让人以为点得动 */
75
+ [data-scope='dynamic-input'][data-part='remove-trigger']:hover:not([aria-disabled='true']),
76
+ [data-scope='dynamic-input'][data-part='move-up-trigger']:hover:not([aria-disabled='true']),
77
+ [data-scope='dynamic-input'][data-part='move-down-trigger']:hover:not([aria-disabled='true']) {
78
+ background: var(--xh-dynamic-input-trigger-bg-hover, var(--xh-bg-subtle-hover));
79
+ color: var(--xh-dynamic-input-trigger-fg-hover, var(--xh-fg-default));
80
+ }
81
+
82
+ [data-scope='dynamic-input'][data-part='remove-trigger']:active:not([aria-disabled='true']),
83
+ [data-scope='dynamic-input'][data-part='move-up-trigger']:active:not([aria-disabled='true']),
84
+ [data-scope='dynamic-input'][data-part='move-down-trigger']:active:not([aria-disabled='true']) {
85
+ background: var(--xh-dynamic-input-trigger-bg-active, var(--xh-bg-subtle-active));
86
+ }
87
+
88
+ /* 删除把手悬停转危险色:它是这一行里唯一不可撤销的动作 */
89
+ [data-scope='dynamic-input'][data-part='remove-trigger']:hover:not([aria-disabled='true']) {
90
+ color: var(--xh-dynamic-input-remove-fg-hover, var(--xh-fg-danger-hover));
91
+ }
92
+
93
+ [data-scope='dynamic-input'][data-part='remove-trigger']:focus-visible,
94
+ [data-scope='dynamic-input'][data-part='move-up-trigger']:focus-visible,
95
+ [data-scope='dynamic-input'][data-part='move-down-trigger']:focus-visible {
96
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
97
+ outline-offset: var(--xh-ring-offset);
98
+ }
99
+
100
+ /* 到上下限、或整份禁用时用 aria-disabled 表达(原生 disabled 不派 click,禁用守卫走不到),
101
+ 所以样式也得认这个属性,写 :disabled 永远不命中 */
102
+ [data-scope='dynamic-input'][data-part='remove-trigger'][aria-disabled='true'],
103
+ [data-scope='dynamic-input'][data-part='move-up-trigger'][aria-disabled='true'],
104
+ [data-scope='dynamic-input'][data-part='move-down-trigger'][aria-disabled='true'] {
105
+ background: transparent;
106
+ color: var(--xh-fg-disabled);
107
+ cursor: not-allowed;
108
+ }
109
+
110
+ /* —— 新增把手 —— */
111
+ /* 虚线描边把它和行里的实心控件区分开:它加的是行,不是行里的值 */
112
+ [data-scope='dynamic-input'][data-part='add-trigger'] {
113
+ display: inline-flex;
114
+ align-self: flex-start;
115
+ align-items: center;
116
+ justify-content: center;
117
+ gap: var(--xh-dynamic-input-action-gap, var(--xh-space-1));
118
+ block-size: var(--xh-dynamic-input-add-height, var(--xh-control-h-md));
119
+ padding-inline: var(--xh-dynamic-input-add-px, var(--xh-control-px-md));
120
+ border: var(--xh-stroke-thin) dashed var(--xh-dynamic-input-add-border, var(--xh-border-default));
121
+ border-radius: var(--xh-dynamic-input-add-radius, var(--xh-shape-control));
122
+ background: var(--xh-dynamic-input-add-bg, transparent);
123
+ color: var(--xh-dynamic-input-add-fg, var(--xh-fg-brand));
124
+ font-family: inherit;
125
+ font-size: var(--xh-dynamic-input-add-font-size, var(--xh-text-label-size));
126
+ line-height: var(--xh-leading-none);
127
+ cursor: pointer;
128
+ transition:
129
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
130
+ border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
131
+ }
132
+
133
+ [data-scope='dynamic-input'][data-part='add-trigger'][hidden] {
134
+ display: none;
135
+ }
136
+
137
+ [data-scope='dynamic-input'][data-part='add-trigger']:hover:not([aria-disabled='true']) {
138
+ border-color: var(--xh-dynamic-input-add-border-hover, var(--xh-border-strong));
139
+ background: var(--xh-dynamic-input-add-bg-hover, var(--xh-bg-subtle-hover));
140
+ }
141
+
142
+ [data-scope='dynamic-input'][data-part='add-trigger']:active:not([aria-disabled='true']) {
143
+ background: var(--xh-dynamic-input-add-bg-active, var(--xh-bg-subtle-active));
144
+ }
145
+
146
+ [data-scope='dynamic-input'][data-part='add-trigger']:focus-visible {
147
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
148
+ outline-offset: var(--xh-ring-offset);
149
+ }
150
+
151
+ [data-scope='dynamic-input'][data-part='add-trigger'][aria-disabled='true'] {
152
+ border-color: var(--xh-border-subtle);
153
+ background: transparent;
154
+ color: var(--xh-fg-disabled);
155
+ cursor: not-allowed;
156
+ }
157
+ }