@xihan-ui/styles 1.0.0-alpha.2 → 1.0.0-alpha.3

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 (118) hide show
  1. package/CHANGELOG.md +447 -0
  2. package/README.md +1 -1
  3. package/css/accordion.css +56 -5
  4. package/css/alert.css +30 -5
  5. package/css/anchor.css +7 -7
  6. package/css/avatar-group.css +3 -3
  7. package/css/avatar.css +3 -3
  8. package/css/back-top.css +16 -1
  9. package/css/badge.css +16 -2
  10. package/css/breadcrumb.css +24 -4
  11. package/css/button-group.css +14 -13
  12. package/css/button.css +42 -11
  13. package/css/calendar.css +119 -2
  14. package/css/card.css +5 -5
  15. package/css/carousel.css +36 -5
  16. package/css/cascader.css +183 -102
  17. package/css/checkbox-group.css +41 -19
  18. package/css/checkbox.css +54 -8
  19. package/css/clipboard.css +2 -1
  20. package/css/code-block.css +12 -7
  21. package/css/collapsible.css +42 -2
  22. package/css/color-picker.css +111 -45
  23. package/css/combobox.css +114 -53
  24. package/css/composer.css +5 -3
  25. package/css/context-menu.css +97 -69
  26. package/css/date-field.css +91 -14
  27. package/css/date-picker.css +276 -65
  28. package/css/descriptions.css +2 -2
  29. package/css/dialog.css +40 -13
  30. package/css/download-trigger.css +76 -0
  31. package/css/drawer.css +45 -14
  32. package/css/dynamic-input.css +68 -17
  33. package/css/editable.css +12 -10
  34. package/css/ellipsis.css +1 -1
  35. package/css/empty-state.css +7 -7
  36. package/css/field.css +9 -6
  37. package/css/fieldset.css +69 -0
  38. package/css/file-upload.css +45 -7
  39. package/css/float-button.css +16 -1
  40. package/css/floating-panel.css +310 -0
  41. package/css/form.css +4 -4
  42. package/css/heatmap.css +410 -0
  43. package/css/highlight.css +1 -1
  44. package/css/hotkeys.css +90 -0
  45. package/css/hover-card.css +40 -45
  46. package/css/image-cropper.css +231 -0
  47. package/css/image-viewer.css +137 -12
  48. package/css/image.css +2 -2
  49. package/css/infinite-scroll.css +1 -1
  50. package/css/json-viewer.css +241 -0
  51. package/css/layout.css +6 -10
  52. package/css/list.css +8 -8
  53. package/css/listbox.css +20 -4
  54. package/css/loading-bar.css +7 -1
  55. package/css/log.css +1 -1
  56. package/css/marquee.css +9 -0
  57. package/css/masonry.css +63 -0
  58. package/css/mention.css +87 -33
  59. package/css/menu.css +113 -47
  60. package/css/menubar.css +146 -60
  61. package/css/navigation-menu.css +54 -29
  62. package/css/number-animation.css +3 -3
  63. package/css/number-field.css +173 -16
  64. package/css/overlay-arrow.css +51 -0
  65. package/css/page-header.css +5 -5
  66. package/css/pagination.css +13 -13
  67. package/css/password-input.css +356 -0
  68. package/css/pin-input.css +13 -10
  69. package/css/popconfirm.css +46 -10
  70. package/css/popover.css +70 -45
  71. package/css/popselect.css +183 -53
  72. package/css/progress.css +47 -7
  73. package/css/qr-code.css +1 -1
  74. package/css/radio-group.css +14 -8
  75. package/css/rating.css +2 -2
  76. package/css/reset.css +17 -0
  77. package/css/result.css +6 -6
  78. package/css/scroll-area.css +61 -89
  79. package/css/scrollbar.css +167 -0
  80. package/css/segmented.css +215 -0
  81. package/css/select.css +274 -125
  82. package/css/separator.css +2 -2
  83. package/css/side-nav.css +111 -19
  84. package/css/signature-pad.css +144 -0
  85. package/css/skeleton.css +7 -1
  86. package/css/slider.css +10 -10
  87. package/css/space.css +117 -0
  88. package/css/spinner.css +22 -10
  89. package/css/statistic.css +9 -9
  90. package/css/steps.css +25 -8
  91. package/css/switch.css +48 -6
  92. package/css/table.css +131 -37
  93. package/css/tabs.css +4 -4
  94. package/css/tag.css +177 -0
  95. package/css/tags-input.css +78 -40
  96. package/css/text-field.css +149 -36
  97. package/css/thread.css +1 -1
  98. package/css/time-field.css +97 -16
  99. package/css/time-picker.css +259 -69
  100. package/css/timeline.css +10 -10
  101. package/css/timer.css +119 -0
  102. package/css/toast.css +34 -10
  103. package/css/toaster.css +1 -1
  104. package/css/toggle.css +6 -6
  105. package/css/tone.css +10 -5
  106. package/css/toolbar.css +1 -1
  107. package/css/tooltip.css +36 -33
  108. package/css/tour.css +103 -58
  109. package/css/transfer.css +68 -17
  110. package/css/tree-select.css +185 -84
  111. package/css/tree.css +85 -1
  112. package/css/typography.css +4 -4
  113. package/css/undefined.css +11 -1
  114. package/css/virtualizer.css +3 -1
  115. package/css/watermark.css +1 -2
  116. package/index.css +17 -0
  117. package/index.unlayered.css +8659 -2799
  118. package/package.json +19 -3
@@ -0,0 +1,76 @@
1
+ @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='download-trigger'] {
4
+ --xh-download-trigger-skin: 1;
5
+ }
6
+
7
+ /*
8
+ * 触发器就是这一个原生按钮,两个适配器都由组件自己渲染它,
9
+ * 所以外观得由设计系统给出一份完整的中性按钮:底色、描边、高度、字号都上令牌,
10
+ * 要换成品牌样式的作者改 --xh-download-trigger-* 这一族槽位,或直接写自己的类名。
11
+ */
12
+ [data-scope='download-trigger'][data-part='root'] {
13
+ display: inline-flex;
14
+ align-items: center;
15
+ justify-content: center;
16
+ gap: var(--xh-download-trigger-gap, var(--xh-control-gap-md));
17
+ block-size: var(--xh-download-trigger-h, var(--xh-control-h-md));
18
+ padding-inline: var(--xh-download-trigger-px, var(--xh-control-px-md));
19
+ padding-block: 0;
20
+ border: var(--xh-stroke-thin) solid var(--xh-download-trigger-border, var(--xh-border-control));
21
+ border-radius: var(--xh-download-trigger-radius, var(--xh-shape-control));
22
+ background: var(--xh-download-trigger-bg, var(--xh-bg-canvas));
23
+ color: var(--xh-download-trigger-fg, var(--xh-fg-default));
24
+ /* 表单控件不继承页面字体,只给字号会让按钮用 UA 默认字族 */
25
+ font-family: inherit;
26
+ font-size: var(--xh-download-trigger-font-size, var(--xh-text-label-size));
27
+ font-weight: var(--xh-download-trigger-font-weight, var(--xh-text-label-weight));
28
+ line-height: var(--xh-leading-none);
29
+ white-space: nowrap;
30
+ cursor: pointer;
31
+ /* 导出键常被连点,触屏上默认会当成双击缩放 */
32
+ user-select: none;
33
+ touch-action: manipulation;
34
+ transition:
35
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
36
+ border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
37
+ }
38
+
39
+ /* 上面声明了 display,会盖掉 UA 的 [hidden] 规则,收起态要在这里接回来 */
40
+ [data-scope='download-trigger'][data-part='root'][hidden] {
41
+ display: none;
42
+ }
43
+
44
+ [data-scope='download-trigger'][data-part='root']:hover {
45
+ border-color: var(--xh-download-trigger-border-hover, var(--xh-border-control-hover));
46
+ background: var(--xh-download-trigger-bg-hover, var(--xh-bg-subtle-hover));
47
+ }
48
+
49
+ [data-scope='download-trigger'][data-part='root']:active {
50
+ background: var(--xh-download-trigger-bg-active, var(--xh-bg-subtle-active));
51
+ }
52
+
53
+ [data-scope='download-trigger'][data-part='root']:focus-visible {
54
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
55
+ outline-offset: var(--xh-ring-offset);
56
+ }
57
+
58
+ /* 取数在途:按钮留在原地不动,只换指针形状——位置一变,正要再点的人就点空了 */
59
+ [data-scope='download-trigger'][data-part='root'][data-state='preparing'] {
60
+ cursor: progress;
61
+ }
62
+
63
+ /*
64
+ * 单体原生控件的禁用走 :disabled。前景与底色一起换,禁用前后两侧都由令牌管着;
65
+ * 用了 fg-disabled 就不再叠 opacity——两样一起用会压到读不出来。
66
+ * 这条排在悬停与按下之后,特异度相同、后写的赢。
67
+ * 描边仍按可操作区那一档:这是一颗独立按钮,四周没有别的结构标出它的位置,
68
+ * 描边一压下去整颗按钮在页面上就没影了。
69
+ */
70
+ [data-scope='download-trigger'][data-part='root']:disabled {
71
+ border-color: var(--xh-download-trigger-border-disabled, var(--xh-border-control));
72
+ background: var(--xh-download-trigger-bg-disabled, var(--xh-bg-muted));
73
+ color: var(--xh-fg-disabled);
74
+ cursor: not-allowed;
75
+ }
76
+ }
package/css/drawer.css CHANGED
@@ -118,6 +118,9 @@
118
118
  /* root 留在页面原地,只是 data-side / data-state 的落点,不该凭空多出一层盒子把
119
119
  trigger 从作者的 flex / grid 行里挤出去,故 display: contents。 */
120
120
  [data-scope='drawer'][data-part='root'] {
121
+ /* 作者塞进指示符槽的图标跟着字号走,与皮肤画的兜底字形同一把尺 */
122
+ --xh-icon-size: var(--xh-drawer-icon-size, var(--xh-glyph-size-text));
123
+
121
124
  display: contents;
122
125
  }
123
126
 
@@ -177,10 +180,18 @@
177
180
  [data-scope='drawer'][data-part='backdrop'] {
178
181
  position: fixed;
179
182
  inset: 0;
180
- z-index: var(--xh-layer-drawer);
183
+ z-index: var(--xh-drawer-backdrop-layer, var(--xh-layer-drawer));
181
184
  background: var(--xh-drawer-backdrop-bg, var(--xh-bg-overlay));
182
185
  }
183
186
 
187
+ /* 挂在局部容器里:遮罩与定位层从视口坐标换成容器坐标,只罩住那个容器。
188
+ 作者要给那个容器加 position(relative 之类),否则 absolute 会往上找到别的定位祖先——
189
+ 这一条写进了示例与 props 说明 */
190
+ [data-scope='drawer'][data-part='backdrop'][data-contained],
191
+ [data-scope='drawer'][data-part='positioner'][data-contained] {
192
+ position: absolute;
193
+ }
194
+
184
195
  [data-scope='drawer'][data-part='backdrop'][data-state='open'] {
185
196
  animation: xh-fade-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
186
197
  }
@@ -196,7 +207,7 @@
196
207
  [data-scope='drawer'][data-part='positioner'] {
197
208
  position: fixed;
198
209
  inset: 0;
199
- z-index: var(--xh-layer-drawer);
210
+ z-index: var(--xh-drawer-layer, var(--xh-layer-drawer));
200
211
  pointer-events: none;
201
212
  }
202
213
 
@@ -212,18 +223,18 @@
212
223
  /* 尺寸档只换这一个私有槽:左右放置时它是面板宽度、上下放置时是面板高度,
213
224
  两种取法都在下面「四条边」那两条规则里。声明在 content 而不是 root:
214
225
  content 会被 portal 到 body,root 上的槽位继承不过来(data-side 也是同一个道理) */
215
- --xh-_drawer-size: 20rem;
226
+ --xh-_drawer-size: var(--xh-overlay-drawer-w-md);
216
227
 
217
228
  position: fixed;
218
- z-index: var(--xh-layer-drawer);
229
+ z-index: var(--xh-drawer-layer, var(--xh-layer-drawer));
219
230
  display: flex;
220
231
  flex-direction: column;
221
232
  gap: var(--xh-drawer-gap, var(--xh-stack-gap-md));
222
- padding-block: var(--xh-drawer-py, var(--xh-section-py));
223
- padding-inline: var(--xh-drawer-px, var(--xh-control-px-lg));
233
+ padding-block: var(--xh-drawer-py, var(--xh-surface-py-md));
234
+ padding-inline: var(--xh-drawer-px, var(--xh-surface-px-md));
224
235
  background: var(--xh-drawer-bg, var(--xh-bg-surface));
225
236
  color: var(--xh-drawer-fg, var(--xh-fg-default));
226
- box-shadow: var(--xh-drawer-shadow, var(--xh-elevation-3));
237
+ box-shadow: var(--xh-drawer-shadow, var(--xh-elevation-sheet));
227
238
  overflow: auto;
228
239
  /* 尺寸是照视口那条边算出来的,padding 再往外顶就会把面板挤出屏幕、
229
240
  逼出一条横向滚动条。reset 层已有同款兜底,这里显式写一遍表明尺寸算法依赖它 */
@@ -254,11 +265,11 @@
254
265
  /* 只有面板厚度分档;中间档不写规则,缺省即中档。
255
266
  data-size 与 data-side 一样同时打在 root 与 content 上,这里只认 content 自己那一份。 */
256
267
  [data-scope='drawer'][data-part='content'][data-size='sm'] {
257
- --xh-_drawer-size: 16rem;
268
+ --xh-_drawer-size: var(--xh-overlay-drawer-w-sm);
258
269
  }
259
270
 
260
271
  [data-scope='drawer'][data-part='content'][data-size='lg'] {
261
- --xh-_drawer-size: 28rem;
272
+ --xh-_drawer-size: var(--xh-overlay-drawer-w-lg);
262
273
  }
263
274
 
264
275
  /* —— 四条边 ——
@@ -358,7 +369,7 @@
358
369
  否则长标题会直接钻到按钮底下。只在真有关闭按钮时让位。 */
359
370
  [data-scope='drawer'][data-part='content']:has([data-scope='drawer'][data-part='close-trigger'])
360
371
  [data-scope='drawer'][data-part='title'] {
361
- padding-inline-end: calc(var(--xh-control-h-sm) + var(--xh-space-3));
372
+ padding-inline-end: calc(var(--xh-drawer-close-size, var(--xh-control-h-sm)) + var(--xh-space-3));
362
373
  }
363
374
 
364
375
  [data-scope='drawer'][data-part='close-trigger'] {
@@ -368,13 +379,16 @@
368
379
  display: inline-flex;
369
380
  align-items: center;
370
381
  justify-content: center;
371
- block-size: var(--xh-control-h-sm);
372
- inline-size: var(--xh-control-h-sm);
382
+ block-size: var(--xh-drawer-close-size, var(--xh-control-h-sm));
383
+ inline-size: var(--xh-drawer-close-size, var(--xh-control-h-sm));
373
384
  border: 0;
374
- border-radius: var(--xh-shape-control);
385
+ border-radius: var(--xh-drawer-close-radius, var(--xh-shape-control));
375
386
  background: transparent;
376
387
  color: var(--xh-fg-muted);
377
388
  cursor: pointer;
389
+ transition:
390
+ background-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
391
+ scale var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
378
392
  }
379
393
 
380
394
  [data-scope='drawer'][data-part='close-trigger'][hidden] {
@@ -382,12 +396,29 @@
382
396
  }
383
397
 
384
398
  [data-scope='drawer'][data-part='close-trigger']:hover {
385
- background: var(--xh-bg-subtle);
399
+ background: var(--xh-drawer-close-bg-hover, var(--xh-bg-subtle-hover));
386
400
  color: var(--xh-fg-default);
387
401
  }
388
402
 
403
+ [data-scope='drawer'][data-part='close-trigger']:active {
404
+ background: var(--xh-drawer-close-bg-active, var(--xh-bg-subtle-active));
405
+ scale: var(--xh-motion-scale-press);
406
+ }
407
+
389
408
  [data-scope='drawer'][data-part='close-trigger']:focus-visible {
390
409
  outline: var(--xh-ring-width) solid var(--xh-ring-focus);
391
410
  outline-offset: var(--xh-ring-offset);
392
411
  }
412
+
413
+ /* 没写内容就由皮肤画兜底字形;作者往部件里塞了自己的图形,这几条即不命中 */
414
+ [data-scope='drawer'][data-part='close-trigger']:empty::before {
415
+ content: '';
416
+ display: inline-block;
417
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
418
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
419
+ vertical-align: var(--xh-glyph-baseline-shift);
420
+ background-color: currentColor;
421
+ -webkit-mask: var(--xh-glyph-mark-close) center / contain no-repeat;
422
+ mask: var(--xh-glyph-mark-close) center / contain no-repeat;
423
+ }
393
424
  }
@@ -6,6 +6,9 @@
6
6
 
7
7
  /* 一行一条,纵向堆叠;新增把手跟在最后一行下面 */
8
8
  [data-scope='dynamic-input'][data-part='root'] {
9
+ /* 作者塞进指示符槽的图标跟着字号走,与皮肤画的兜底字形同一把尺 */
10
+ --xh-icon-size: var(--xh-dynamic-input-icon-size, var(--xh-glyph-size-text));
11
+
9
12
  display: flex;
10
13
  flex-direction: column;
11
14
  gap: var(--xh-dynamic-input-gap, var(--xh-space-2));
@@ -47,55 +50,69 @@
47
50
  }
48
51
 
49
52
  /* —— 行内三个把手 —— */
50
- [data-scope='dynamic-input'][data-part='remove-trigger'],
53
+ [data-scope='dynamic-input'][data-part='item-delete-trigger'],
51
54
  [data-scope='dynamic-input'][data-part='move-up-trigger'],
52
55
  [data-scope='dynamic-input'][data-part='move-down-trigger'] {
53
56
  display: inline-flex;
54
57
  flex: none;
55
58
  align-items: center;
56
59
  justify-content: center;
57
- inline-size: var(--xh-dynamic-input-trigger-size, var(--xh-control-action-size));
58
- block-size: var(--xh-dynamic-input-trigger-size, var(--xh-control-action-size));
59
60
  padding: 0;
60
61
  border: 0;
61
- border-radius: var(--xh-dynamic-input-trigger-radius, var(--xh-shape-control));
62
62
  background: var(--xh-dynamic-input-trigger-bg, transparent);
63
63
  color: var(--xh-dynamic-input-trigger-fg, var(--xh-fg-muted));
64
64
  font-family: inherit;
65
- font-size: var(--xh-dynamic-input-trigger-font-size, var(--xh-font-size-sm));
65
+ font-size: var(--xh-dynamic-input-trigger-font-size, var(--xh-text-secondary-size));
66
66
  line-height: var(--xh-leading-none);
67
67
  cursor: pointer;
68
- transition: background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
68
+ transition:
69
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
70
+ scale var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
71
+ }
72
+
73
+ /* 行级删除把手:命中区取行内动作按钮尺寸,圆角取控件圆角 */
74
+ [data-scope='dynamic-input'][data-part='item-delete-trigger'] {
75
+ inline-size: var(--xh-dynamic-input-trigger-size, var(--xh-control-action-size));
76
+ block-size: var(--xh-dynamic-input-trigger-size, var(--xh-control-action-size));
77
+ border-radius: var(--xh-dynamic-input-trigger-radius, var(--xh-shape-control));
78
+ }
79
+
80
+ [data-scope='dynamic-input'][data-part='move-up-trigger'],
81
+ [data-scope='dynamic-input'][data-part='move-down-trigger'] {
82
+ inline-size: var(--xh-dynamic-input-trigger-size, var(--xh-control-action-size));
83
+ block-size: var(--xh-dynamic-input-trigger-size, var(--xh-control-action-size));
84
+ border-radius: var(--xh-dynamic-input-trigger-radius, var(--xh-shape-control));
69
85
  }
70
86
 
71
87
  /* 不换序时连接层给换序把手写 hidden;上面那条 display 会把 UA 的 [hidden]{display:none}
72
88
  盖掉,少了这条把手还留在原地。删除把手同样声明了 display,作者自己收起它时要的是同一条 */
73
- [data-scope='dynamic-input'][data-part='remove-trigger'][hidden],
89
+ [data-scope='dynamic-input'][data-part='item-delete-trigger'][hidden],
74
90
  [data-scope='dynamic-input'][data-part='move-up-trigger'][hidden],
75
91
  [data-scope='dynamic-input'][data-part='move-down-trigger'][hidden] {
76
92
  display: none;
77
93
  }
78
94
 
79
95
  /* 按不动的把手不给悬停与按下反馈:它仍然可聚焦、可点,反馈会让人以为点得动 */
80
- [data-scope='dynamic-input'][data-part='remove-trigger']:hover:not([aria-disabled='true']),
96
+ [data-scope='dynamic-input'][data-part='item-delete-trigger']:hover:not([aria-disabled='true']),
81
97
  [data-scope='dynamic-input'][data-part='move-up-trigger']:hover:not([aria-disabled='true']),
82
98
  [data-scope='dynamic-input'][data-part='move-down-trigger']:hover:not([aria-disabled='true']) {
83
99
  background: var(--xh-dynamic-input-trigger-bg-hover, var(--xh-bg-subtle-hover));
84
100
  color: var(--xh-dynamic-input-trigger-fg-hover, var(--xh-fg-default));
85
101
  }
86
102
 
87
- [data-scope='dynamic-input'][data-part='remove-trigger']:active:not([aria-disabled='true']),
88
- [data-scope='dynamic-input'][data-part='move-up-trigger']:active:not([aria-disabled='true']),
89
- [data-scope='dynamic-input'][data-part='move-down-trigger']:active:not([aria-disabled='true']) {
103
+ [data-scope='dynamic-input'][data-part='item-delete-trigger']:not([aria-disabled='true']):active,
104
+ [data-scope='dynamic-input'][data-part='move-up-trigger']:not([aria-disabled='true']):active,
105
+ [data-scope='dynamic-input'][data-part='move-down-trigger']:not([aria-disabled='true']):active {
90
106
  background: var(--xh-dynamic-input-trigger-bg-active, var(--xh-bg-subtle-active));
107
+ scale: var(--xh-motion-scale-press);
91
108
  }
92
109
 
93
110
  /* 删除把手悬停转危险色:它是这一行里唯一不可撤销的动作 */
94
- [data-scope='dynamic-input'][data-part='remove-trigger']:hover:not([aria-disabled='true']) {
95
- color: var(--xh-dynamic-input-remove-fg-hover, var(--xh-fg-danger-hover));
111
+ [data-scope='dynamic-input'][data-part='item-delete-trigger']:hover:not([aria-disabled='true']) {
112
+ color: var(--xh-dynamic-input-item-delete-fg-hover, var(--xh-fg-danger-hover));
96
113
  }
97
114
 
98
- [data-scope='dynamic-input'][data-part='remove-trigger']:focus-visible,
115
+ [data-scope='dynamic-input'][data-part='item-delete-trigger']:focus-visible,
99
116
  [data-scope='dynamic-input'][data-part='move-up-trigger']:focus-visible,
100
117
  [data-scope='dynamic-input'][data-part='move-down-trigger']:focus-visible {
101
118
  outline: var(--xh-ring-width) solid var(--xh-ring-focus);
@@ -104,7 +121,7 @@
104
121
 
105
122
  /* 到上下限、或整份禁用时用 aria-disabled 表达(原生 disabled 不派 click,禁用守卫走不到),
106
123
  所以样式也得认这个属性,写 :disabled 永远不命中 */
107
- [data-scope='dynamic-input'][data-part='remove-trigger'][aria-disabled='true'],
124
+ [data-scope='dynamic-input'][data-part='item-delete-trigger'][aria-disabled='true'],
108
125
  [data-scope='dynamic-input'][data-part='move-up-trigger'][aria-disabled='true'],
109
126
  [data-scope='dynamic-input'][data-part='move-down-trigger'][aria-disabled='true'] {
110
127
  background: transparent;
@@ -144,7 +161,7 @@
144
161
  background: var(--xh-dynamic-input-add-bg-hover, var(--xh-bg-subtle-hover));
145
162
  }
146
163
 
147
- [data-scope='dynamic-input'][data-part='add-trigger']:active:not([aria-disabled='true']) {
164
+ [data-scope='dynamic-input'][data-part='add-trigger']:not([aria-disabled='true']):active {
148
165
  background: var(--xh-dynamic-input-add-bg-active, var(--xh-bg-subtle-active));
149
166
  }
150
167
 
@@ -154,9 +171,43 @@
154
171
  }
155
172
 
156
173
  [data-scope='dynamic-input'][data-part='add-trigger'][aria-disabled='true'] {
157
- border-color: var(--xh-border-subtle);
174
+ border-color: var(--xh-dynamic-input-add-border-disabled, var(--xh-border-subtle));
158
175
  background: transparent;
159
176
  color: var(--xh-fg-disabled);
160
177
  cursor: not-allowed;
161
178
  }
179
+
180
+ /* 没写内容就由皮肤画兜底字形;作者往部件里塞了自己的图形,这几条即不命中 */
181
+ [data-scope='dynamic-input'][data-part='move-up-trigger']:empty::before {
182
+ content: '';
183
+ display: inline-block;
184
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
185
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
186
+ vertical-align: var(--xh-glyph-baseline-shift);
187
+ background-color: currentColor;
188
+ -webkit-mask: var(--xh-glyph-mark-chevron-up) center / contain no-repeat;
189
+ mask: var(--xh-glyph-mark-chevron-up) center / contain no-repeat;
190
+ }
191
+
192
+ [data-scope='dynamic-input'][data-part='move-down-trigger']:empty::before {
193
+ content: '';
194
+ display: inline-block;
195
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
196
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
197
+ vertical-align: var(--xh-glyph-baseline-shift);
198
+ background-color: currentColor;
199
+ -webkit-mask: var(--xh-glyph-mark-chevron-down) center / contain no-repeat;
200
+ mask: var(--xh-glyph-mark-chevron-down) center / contain no-repeat;
201
+ }
202
+
203
+ [data-scope='dynamic-input'][data-part='item-delete-trigger']:empty::before {
204
+ content: '';
205
+ display: inline-block;
206
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
207
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
208
+ vertical-align: var(--xh-glyph-baseline-shift);
209
+ background-color: currentColor;
210
+ -webkit-mask: var(--xh-glyph-mark-close) center / contain no-repeat;
211
+ mask: var(--xh-glyph-mark-close) center / contain no-repeat;
212
+ }
162
213
  }
package/css/editable.css CHANGED
@@ -34,8 +34,7 @@
34
34
  cursor: not-allowed;
35
35
  }
36
36
 
37
- /* 预览区与输入框轮流上场,两者在同一处落脚:
38
- area 定死最小宽高,切换形态时这一块不会忽大忽小地跳 */
37
+ /* 预览区与输入框轮流上场,两者在同一处落脚:area 给这一处定最小宽高 */
39
38
  [data-scope='editable'][data-part='area'] {
40
39
  display: inline-flex;
41
40
  align-items: center;
@@ -47,21 +46,23 @@
47
46
  display: none;
48
47
  }
49
48
 
50
- /* 预览区的内边距与边框要和输入框对齐:只有描边是透明的,
51
- 否则从预览切到编辑时文字会左右挪一下 */
49
+ /* 预览区的高度、内边距与边框都和输入框取同一档,只有描边是透明的:
50
+ 单行值定在控件档上,多行值才往下长 */
52
51
  [data-scope='editable'][data-part='preview'] {
53
- display: inline-block;
52
+ display: inline-flex;
53
+ align-items: center;
54
54
  inline-size: 100%;
55
+ min-block-size: var(--xh-editable-preview-min-h, var(--xh-control-h-md));
55
56
  padding-inline: var(--xh-editable-preview-px, var(--xh-control-px-md));
56
57
  padding-block: 0;
57
58
  border: var(--xh-stroke-thin) solid transparent;
58
59
  border-radius: var(--xh-editable-preview-radius, var(--xh-shape-control));
59
60
  color: var(--xh-editable-preview-fg, var(--xh-fg-default));
60
61
  font-size: var(--xh-editable-preview-font-size, var(--xh-text-body-size));
61
- line-height: var(--xh-editable-preview-line-height, var(--xh-control-h-md));
62
- /* 值里的换行与连续空格照原样显示,但过长的行仍要能断开 */
62
+ line-height: var(--xh-leading-normal);
63
+ /* 值里的换行与连续空格照原样显示,过长的行断开而不撑破弹性行 */
63
64
  white-space: pre-wrap;
64
- overflow-wrap: break-word;
65
+ overflow-wrap: anywhere;
65
66
  }
66
67
 
67
68
  /* 编辑态由连接层给 hidden;上面那条 display 会盖掉 UA 规则,少了这条就收不起来 */
@@ -140,12 +141,13 @@
140
141
  }
141
142
 
142
143
  [data-scope='editable'][data-part='input']:focus-visible {
144
+ border-color: var(--xh-editable-input-border-focus, var(--xh-_tone, var(--xh-border-control-focus)));
143
145
  outline: var(--xh-ring-width) solid var(--xh-ring-focus);
144
146
  outline-offset: var(--xh-ring-offset);
145
147
  }
146
148
 
147
149
  [data-scope='editable'][data-part='input'][data-invalid] {
148
- border-color: var(--xh-editable-input-border-invalid, var(--xh-color-danger-500));
150
+ border-color: var(--xh-editable-input-border-invalid, var(--xh-border-invalid));
149
151
  }
150
152
 
151
153
  /* 只读仍可聚焦、可复制,不置灰,只把底色压下去表示"改不动" */
@@ -188,7 +190,7 @@
188
190
  background: var(--xh-editable-trigger-bg, var(--xh-bg-subtle));
189
191
  color: var(--xh-editable-trigger-fg, var(--xh-fg-default));
190
192
  font-family: inherit;
191
- font-size: var(--xh-editable-trigger-font-size, var(--xh-font-size-sm));
193
+ font-size: var(--xh-editable-trigger-font-size, var(--xh-text-secondary-size));
192
194
  line-height: var(--xh-leading-none);
193
195
  white-space: nowrap;
194
196
  cursor: pointer;
package/css/ellipsis.css CHANGED
@@ -27,7 +27,7 @@
27
27
  }
28
28
 
29
29
  /* 铺开:裁剪整个撤掉。写在两档之后,压得住上面同权重的那两条 */
30
- [data-scope='ellipsis'][data-part='root'][data-expanded] {
30
+ [data-scope='ellipsis'][data-part='root'][data-state='open'] {
31
31
  display: block;
32
32
  overflow: visible;
33
33
  white-space: normal;
@@ -9,8 +9,8 @@
9
9
  换档不必给每个部件各写一条选择器 */
10
10
  --xh-_empty-state-gap: var(--xh-stack-gap-md);
11
11
  --xh-_empty-state-py: var(--xh-section-py);
12
- --xh-_empty-state-icon-size: 2.5rem;
13
- --xh-_empty-state-title-size: var(--xh-font-size-lg);
12
+ --xh-_empty-state-icon-size: var(--xh-glyph-size-2xl);
13
+ --xh-_empty-state-title-size: var(--xh-control-font-lg);
14
14
  /* 说明的读行宽度:整句话不收窄就会拉成一条难读的长行 */
15
15
  --xh-_empty-state-measure: 32rem;
16
16
 
@@ -33,15 +33,15 @@
33
33
  [data-scope='empty-state'][data-part='root'][data-size='sm'] {
34
34
  --xh-_empty-state-gap: var(--xh-space-2);
35
35
  --xh-_empty-state-py: var(--xh-space-4);
36
- --xh-_empty-state-icon-size: 2rem;
37
- --xh-_empty-state-title-size: var(--xh-font-size-md);
36
+ --xh-_empty-state-icon-size: var(--xh-glyph-size-xl);
37
+ --xh-_empty-state-title-size: var(--xh-control-font-md);
38
38
  }
39
39
 
40
40
  [data-scope='empty-state'][data-part='root'][data-size='lg'] {
41
41
  --xh-_empty-state-gap: var(--xh-space-6);
42
42
  --xh-_empty-state-py: var(--xh-space-8);
43
- --xh-_empty-state-icon-size: 3.5rem;
44
- --xh-_empty-state-title-size: var(--xh-font-size-xl);
43
+ --xh-_empty-state-icon-size: var(--xh-glyph-size-3xl);
44
+ --xh-_empty-state-title-size: var(--xh-text-heading-3-size);
45
45
  }
46
46
 
47
47
  [data-scope='empty-state'][data-part='icon'] {
@@ -53,7 +53,7 @@
53
53
  inline-size: var(--xh-empty-state-icon-size, var(--xh-_empty-state-icon-size));
54
54
  color: var(--xh-empty-state-icon-fg, var(--xh-fg-subtle));
55
55
  /* 图标常是一个字形,字号跟着图标框走,line-height 不再另占高度 */
56
- font-size: var(--xh-empty-state-icon-font-size, var(--xh-font-size-3xl));
56
+ font-size: var(--xh-empty-state-icon-font-size, var(--xh-text-heading-1-size));
57
57
  line-height: var(--xh-leading-none);
58
58
  }
59
59
 
package/css/field.css CHANGED
@@ -22,8 +22,10 @@
22
22
  [data-scope='form'][data-part='root'][data-layout='horizontal'] [data-scope='field'][data-part='label'] {
23
23
  grid-column: 1;
24
24
  justify-self: end;
25
- /* 与控件的首行文字对齐:按控件高度撑一行再居中 */
26
- line-height: var(--xh-control-h-md);
25
+ /* 与控件的首行文字对齐:行高保持正文行距(标签折行时每行不会撑成一个控件高),
26
+ 上下留白把单行文本盒撑到控件高度;配的是哪一档控件由 --xh-field-control-h 告诉它 */
27
+ line-height: var(--xh-field-label-leading, var(--xh-leading-normal));
28
+ padding-block: calc((var(--xh-field-control-h, var(--xh-control-h-md)) - 1lh) / 2);
27
29
  }
28
30
 
29
31
  [data-scope='form'][data-part='root'][data-layout='horizontal'][data-label-align='start'] [data-scope='field'][data-part='label'] {
@@ -47,7 +49,7 @@
47
49
 
48
50
  /* 必填星号:跟着根上的 data-required 亮,选填不标 */
49
51
  [data-scope='field'][data-part='root'][data-required] [data-part='label']::after {
50
- content: '*';
52
+ content: var(--xh-glyph-mark-required);
51
53
  margin-inline-start: var(--xh-space-1);
52
54
  color: var(--xh-field-label-star, var(--xh-fg-danger));
53
55
  }
@@ -65,12 +67,13 @@
65
67
  }
66
68
 
67
69
  [data-scope='field'][data-part='control']:focus-visible {
70
+ border-color: var(--xh-field-control-border-focus, var(--xh-_tone, var(--xh-border-control-focus)));
68
71
  outline: var(--xh-ring-width) solid var(--xh-ring-focus);
69
72
  outline-offset: var(--xh-ring-offset);
70
73
  }
71
74
 
72
75
  [data-scope='field'][data-part='control'][data-invalid] {
73
- border-color: var(--xh-field-control-border-invalid, var(--xh-color-danger-500));
76
+ border-color: var(--xh-field-control-border-invalid, var(--xh-border-invalid));
74
77
  }
75
78
 
76
79
  /* 禁用由作者在控件上落原生 disabled,样式统一走 data-disabled */
@@ -83,7 +86,7 @@
83
86
  [data-scope='field'][data-part='description'] {
84
87
  margin: 0;
85
88
  color: var(--xh-field-description-fg, var(--xh-fg-muted));
86
- font-size: var(--xh-field-description-font-size, var(--xh-font-size-sm));
89
+ font-size: var(--xh-field-description-font-size, var(--xh-text-secondary-size));
87
90
  line-height: var(--xh-leading-normal);
88
91
  }
89
92
 
@@ -95,7 +98,7 @@
95
98
  [data-scope='field'][data-part='error-text'] {
96
99
  margin: 0;
97
100
  color: var(--xh-field-error-fg, var(--xh-fg-danger));
98
- font-size: var(--xh-field-error-font-size, var(--xh-font-size-sm));
101
+ font-size: var(--xh-field-error-font-size, var(--xh-text-secondary-size));
99
102
  line-height: var(--xh-leading-normal);
100
103
  }
101
104
  }
@@ -0,0 +1,69 @@
1
+ @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='fieldset'] {
4
+ --xh-fieldset-skin: 1;
5
+ }
6
+
7
+ /* 抹掉 UA 给 <fieldset> 的边框、内外边距与 min-content 下限,间距全部改由令牌给。
8
+ display 落在 fieldset 的匿名内容盒上:首个 <legend> 不在这个盒子里,
9
+ 所以 gap 只作用于组内各块,legend 与内容之间的留白由 legend 自己的下外边距给。 */
10
+ [data-scope='fieldset'][data-part='root'] {
11
+ display: flex;
12
+ flex-direction: column;
13
+ gap: var(--xh-fieldset-gap, var(--xh-space-4));
14
+ margin: 0;
15
+ border: 0;
16
+ padding: 0;
17
+ min-inline-size: 0;
18
+ }
19
+
20
+ [data-scope='fieldset'][data-part='root'][hidden] {
21
+ display: none;
22
+ }
23
+
24
+ [data-scope='fieldset'][data-part='legend'] {
25
+ padding: 0;
26
+ margin-block-end: var(--xh-fieldset-legend-gap, var(--xh-space-2));
27
+ color: var(--xh-fieldset-legend-fg, var(--xh-fg-default));
28
+ font-size: var(--xh-fieldset-legend-font-size, var(--xh-text-label-size));
29
+ font-weight: var(--xh-fieldset-legend-font-weight, var(--xh-text-label-weight));
30
+ line-height: var(--xh-leading-tight);
31
+ }
32
+
33
+ /* 整组无效时组标题转警示色:没渲染错误文案的字段集上,这是屏幕上唯一变化的东西 */
34
+ [data-scope='fieldset'][data-part='root'][data-invalid] > [data-scope='fieldset'][data-part='legend'] {
35
+ color: var(--xh-fieldset-legend-fg-invalid, var(--xh-fg-danger));
36
+ }
37
+
38
+ /* 整组禁用时组标题跟着变淡;组内控件由原生 fieldset[disabled] 各自呈现禁用态。
39
+ 选择器与上一条同权重且排在后面,禁用与无效同时为真时以禁用为准。 */
40
+ [data-scope='fieldset'][data-part='root'] > [data-scope='fieldset'][data-part='legend'][data-disabled] {
41
+ color: var(--xh-fieldset-legend-fg-disabled, var(--xh-fg-subtle));
42
+ }
43
+
44
+ /* 必填星号:跟着根上的 data-required 亮,选填不标 */
45
+ [data-scope='fieldset'][data-part='root'][data-required] > [data-scope='fieldset'][data-part='legend']::after {
46
+ content: var(--xh-glyph-mark-required);
47
+ margin-inline-start: var(--xh-space-1);
48
+ color: var(--xh-fieldset-legend-star, var(--xh-fg-danger));
49
+ }
50
+
51
+ [data-scope='fieldset'][data-part='helper-text'] {
52
+ margin: 0;
53
+ color: var(--xh-fieldset-helper-fg, var(--xh-fg-muted));
54
+ font-size: var(--xh-fieldset-helper-font-size, var(--xh-text-secondary-size));
55
+ line-height: var(--xh-leading-normal);
56
+ }
57
+
58
+ [data-scope='fieldset'][data-part='helper-text'][data-disabled] {
59
+ color: var(--xh-fieldset-helper-fg-disabled, var(--xh-fg-subtle));
60
+ }
61
+
62
+ /* 不 invalid 时节点带 hidden:此处不声明 display,交给浏览器的 [hidden] 规则收起 */
63
+ [data-scope='fieldset'][data-part='error-text'] {
64
+ margin: 0;
65
+ color: var(--xh-fieldset-error-fg, var(--xh-fg-danger));
66
+ font-size: var(--xh-fieldset-error-font-size, var(--xh-text-secondary-size));
67
+ line-height: var(--xh-leading-normal);
68
+ }
69
+ }