@xihan-ui/styles 1.0.0-alpha.0 → 1.0.0-alpha.1

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 (58) hide show
  1. package/CHANGELOG.md +261 -0
  2. package/css/back-top.css +2 -2
  3. package/css/button-group.css +1 -1
  4. package/css/button.css +1 -1
  5. package/css/calendar.css +1 -1
  6. package/css/carousel.css +2 -2
  7. package/css/cascader.css +199 -24
  8. package/css/checkbox-group.css +20 -20
  9. package/css/checkbox.css +1 -1
  10. package/css/clipboard.css +3 -3
  11. package/css/color-picker.css +21 -5
  12. package/css/combobox.css +23 -20
  13. package/css/composer.css +1 -1
  14. package/css/context-menu.css +56 -26
  15. package/css/date-field.css +5 -5
  16. package/css/date-picker.css +66 -2
  17. package/css/drawer.css +3 -3
  18. package/css/dynamic-input.css +2 -2
  19. package/css/editable.css +6 -6
  20. package/css/field.css +32 -1
  21. package/css/file-upload.css +3 -3
  22. package/css/float-button.css +2 -2
  23. package/css/form.css +17 -5
  24. package/css/hover-card.css +15 -11
  25. package/css/image-viewer.css +176 -0
  26. package/css/listbox.css +9 -6
  27. package/css/mention.css +14 -22
  28. package/css/menu.css +38 -23
  29. package/css/menubar.css +28 -17
  30. package/css/number-field.css +16 -16
  31. package/css/pin-input.css +6 -6
  32. package/css/popconfirm.css +23 -0
  33. package/css/popover.css +15 -11
  34. package/css/popselect.css +31 -16
  35. package/css/progress.css +68 -1
  36. package/css/radio-group.css +1 -1
  37. package/css/select.css +148 -17
  38. package/css/side-nav.css +163 -0
  39. package/css/slider.css +55 -0
  40. package/css/steps.css +2 -2
  41. package/css/switch.css +27 -0
  42. package/css/table.css +6 -6
  43. package/css/tags-input.css +7 -7
  44. package/css/text-field.css +23 -10
  45. package/css/thread.css +1 -1
  46. package/css/time-field.css +5 -5
  47. package/css/time-picker.css +36 -26
  48. package/css/toggle-group.css +1 -1
  49. package/css/toggle.css +1 -1
  50. package/css/tone.css +22 -0
  51. package/css/tooltip.css +20 -15
  52. package/css/tour.css +16 -12
  53. package/css/transfer.css +11 -9
  54. package/css/tree-select.css +26 -18
  55. package/css/tree.css +8 -5
  56. package/index.css +6 -2
  57. package/index.unlayered.css +1499 -413
  58. package/package.json +4 -2
package/css/composer.css CHANGED
@@ -8,7 +8,7 @@
8
8
  align-items: flex-end;
9
9
  gap: var(--xh-composer-gap, var(--xh-control-gap-md));
10
10
  padding: var(--xh-composer-p, var(--xh-space-2));
11
- border: var(--xh-stroke-thin) solid var(--xh-composer-border, var(--xh-border-default));
11
+ border: var(--xh-stroke-thin) solid var(--xh-composer-border, var(--xh-border-control));
12
12
  border-radius: var(--xh-composer-radius, var(--xh-shape-surface));
13
13
  background: var(--xh-composer-bg, var(--xh-bg-surface));
14
14
  }
@@ -4,13 +4,16 @@
4
4
  /* 尺寸档只换这四个私有槽,条目与分组标题都从 root 继承它们,
5
5
  换档不必给每个部件各写一条选择器。浮层在 positioner 里、也是 root 的后代,一并继承
6
6
  (display:contents 不影响继承:自定义属性沿 DOM 树传) */
7
- --xh-_context-menu-item-py: var(--xh-space-2);
8
- --xh-_context-menu-item-px: var(--xh-control-px-sm);
7
+ --xh-_context-menu-item-py: var(--xh-space-1_5);
8
+ --xh-_context-menu-item-px: var(--xh-control-px-md);
9
9
  --xh-_context-menu-item-gap: var(--xh-control-gap-md);
10
10
  --xh-_context-menu-font-size: var(--xh-text-body-size);
11
11
 
12
- /* 条目高亮底与标记位强调色随语气走。没写 data-tone 时这两个语气槽没被声明,各自退回原来的取值 */
13
- --xh-_context-menu-item-highlight-bg: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover));
12
+ /* 打开子菜单的触发条目走品牌淡底强档,随语气换色相;没写 data-tone 时退回品牌淡底。
13
+ 悬停与键盘焦点恒中性灰轻档、不接语气槽——两档同色相的话打开路径就认不出来了 */
14
+ --xh-_context-menu-item-active-bg: var(--xh-_tone-subtle, var(--xh-bg-brand-subtle));
15
+
16
+ /* 标记位强调色随语气走,没写 data-tone 时退回原来的取值 */
14
17
  --xh-_context-menu-accent: var(--xh-_tone, var(--xh-fg-subtle));
15
18
 
16
19
  display: contents;
@@ -21,18 +24,18 @@
21
24
  }
22
25
 
23
26
  /* —— 尺寸 —— */
24
- /* 缺省档写在上面的基础规则里。条目内外边距缺省档本就不在 control-h/px 那把尺上
25
- (高度由 padding 撑出来、行内内缩取的是 sm 档),故按 space 档上下各挪一级 */
27
+ /* 缺省档写在上面的基础规则里。条目高度由 padding 撑出来:
28
+ 纵向走 space 档、行内内缩走 control-px 档(随密度轴),换档各挪一级 */
26
29
  [data-scope='context-menu'][data-part='root'][data-size='sm'] {
27
30
  --xh-_context-menu-item-py: var(--xh-space-1);
28
- --xh-_context-menu-item-px: var(--xh-space-1);
31
+ --xh-_context-menu-item-px: var(--xh-control-px-sm);
29
32
  --xh-_context-menu-item-gap: var(--xh-control-gap-sm);
30
33
  --xh-_context-menu-font-size: var(--xh-font-size-sm);
31
34
  }
32
35
 
33
36
  [data-scope='context-menu'][data-part='root'][data-size='lg'] {
34
- --xh-_context-menu-item-py: var(--xh-space-3);
35
- --xh-_context-menu-item-px: var(--xh-space-3);
37
+ --xh-_context-menu-item-py: var(--xh-space-2);
38
+ --xh-_context-menu-item-px: var(--xh-control-px-lg);
36
39
  --xh-_context-menu-item-gap: var(--xh-control-gap-lg);
37
40
  --xh-_context-menu-font-size: var(--xh-font-size-lg);
38
41
  }
@@ -82,8 +85,14 @@
82
85
  display: none;
83
86
  }
84
87
 
85
- /* 容器只是焦点域兜底落点,落焦不画环:高亮永远长在条目上 */
86
- [data-scope='context-menu'][data-part='content']:focus-visible {
88
+ /* 焦点落在容器上只有一种情形:指针打开、还没有任何条目当锚点。那一刻它认领着 Tab 位,
89
+ 环画在浮层内沿,用户才看得出键盘此刻在哪儿;有锚点时环长在条目上,容器不再画 */
90
+ [data-scope='context-menu'][data-part='content'][tabindex='0']:focus-visible {
91
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
92
+ outline-offset: calc(-1 * var(--xh-ring-width));
93
+ }
94
+
95
+ [data-scope='context-menu'][data-part='content']:not([tabindex='0']):focus-visible {
87
96
  outline: none;
88
97
  }
89
98
 
@@ -135,10 +144,13 @@
135
144
  border-radius: var(--xh-context-menu-item-radius, var(--xh-shape-control));
136
145
  color: var(--xh-context-menu-item-fg, var(--xh-fg-default));
137
146
  font-size: var(--xh-context-menu-item-font-size, var(--xh-_context-menu-font-size));
138
- line-height: var(--xh-leading-none);
147
+ line-height: var(--xh-context-menu-item-leading, var(--xh-leading-normal));
139
148
  white-space: nowrap;
140
149
  cursor: pointer;
141
150
  user-select: none;
151
+ transition:
152
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
153
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
142
154
  }
143
155
 
144
156
  /* 条目可以按需收起(按上下文过滤掉的命令),有 display 就得补这条 */
@@ -146,14 +158,28 @@
146
158
  display: none;
147
159
  }
148
160
 
149
- /* 键盘高亮与指针高亮共用一套底色:条目本身承载焦点,不另画描边。
161
+ /* 悬停与键盘焦点共用中性灰轻档:条目本身承载焦点,不另画描边。
150
162
  data-highlighted 跟的是连接层记的焦点条目,与 :focus 说的是同一件事,
151
163
  子部件够不着那个伪类,只能读这个标记 */
152
164
  [data-scope='context-menu'][data-part='item']:not([data-disabled]):is(:hover, :focus, [data-highlighted]) {
153
- background: var(--xh-context-menu-item-bg-highlight, var(--xh-_context-menu-item-highlight-bg));
165
+ background: var(--xh-context-menu-item-bg-hover, var(--xh-bg-subtle));
166
+ }
167
+
168
+ /* 打开子菜单的触发条目是展开路径,走品牌淡底加粗强档。
169
+ 必须排在轻档之后:悬停或焦点回到它身上时同特指度靠书写顺序压过轻档 */
170
+ [data-scope='context-menu'][data-part='item']:not([data-disabled])[data-state='open'] {
171
+ background: var(--xh-context-menu-item-bg-active, var(--xh-_context-menu-item-active-bg));
172
+ font-weight: var(--xh-context-menu-item-active-font-weight, var(--xh-font-weight-semibold));
154
173
  }
155
174
 
156
- [data-scope='context-menu'][data-part='item']:focus {
175
+ /* 键盘走到条目上要画环:轻档底色只有 1.1:1,够不上焦点指示器的对比度要求。
176
+ 指针悬停走的是程序化聚焦,鼠标模态下 :focus-visible 不匹配,不会误画 */
177
+ [data-scope='context-menu'][data-part='item']:focus-visible {
178
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
179
+ outline-offset: calc(-1 * var(--xh-ring-width));
180
+ }
181
+
182
+ [data-scope='context-menu'][data-part='item']:focus:not(:focus-visible) {
157
183
  outline: none;
158
184
  }
159
185
 
@@ -204,11 +230,15 @@
204
230
  }
205
231
 
206
232
  /* 箭头是旋转 45° 的方块,只保留朝向锚点那两条边的描边;
207
- 引擎不产出箭头偏移,因此沿交叉轴固定居中 */
233
+ 交叉轴落点由定位引擎经内联自定义属性写进来,没给时退回居中 */
208
234
  [data-scope='context-menu'][data-part='arrow'] {
235
+ --xh-_context-menu-arrow-size: var(--xh-context-menu-arrow-size, var(--xh-overlay-arrow-size));
236
+ --xh-_context-menu-arrow-x: 50%;
237
+ --xh-_context-menu-arrow-y: 50%;
238
+
209
239
  position: absolute;
210
- inline-size: var(--xh-context-menu-arrow-size, var(--xh-overlay-arrow-size));
211
- block-size: var(--xh-context-menu-arrow-size, var(--xh-overlay-arrow-size));
240
+ inline-size: var(--xh-_context-menu-arrow-size);
241
+ block-size: var(--xh-_context-menu-arrow-size);
212
242
  background: var(--xh-context-menu-bg, var(--xh-bg-surface));
213
243
  border: var(--xh-stroke-thin) solid var(--xh-context-menu-border, var(--xh-border-default));
214
244
  rotate: 45deg;
@@ -219,29 +249,29 @@
219
249
  }
220
250
 
221
251
  [data-scope='context-menu'][data-part='arrow'][data-placement^='bottom'] {
222
- inset-block-start: calc(var(--xh-context-menu-arrow-size, var(--xh-overlay-arrow-size)) / -2);
223
- inset-inline-start: calc(50% - var(--xh-context-menu-arrow-size, var(--xh-overlay-arrow-size)) / 2);
252
+ inset-block-start: calc(var(--xh-_context-menu-arrow-size) / -2);
253
+ inset-inline-start: calc(var(--xh-_context-menu-arrow-x) - var(--xh-_context-menu-arrow-size) / 2);
224
254
  border-block-end: 0;
225
255
  border-inline-end: 0;
226
256
  }
227
257
 
228
258
  [data-scope='context-menu'][data-part='arrow'][data-placement^='top'] {
229
- inset-block-end: calc(var(--xh-context-menu-arrow-size, var(--xh-overlay-arrow-size)) / -2);
230
- inset-inline-start: calc(50% - var(--xh-context-menu-arrow-size, var(--xh-overlay-arrow-size)) / 2);
259
+ inset-block-end: calc(var(--xh-_context-menu-arrow-size) / -2);
260
+ inset-inline-start: calc(var(--xh-_context-menu-arrow-x) - var(--xh-_context-menu-arrow-size) / 2);
231
261
  border-block-start: 0;
232
262
  border-inline-start: 0;
233
263
  }
234
264
 
235
265
  [data-scope='context-menu'][data-part='arrow'][data-placement^='right'] {
236
- inset-inline-start: calc(var(--xh-context-menu-arrow-size, var(--xh-overlay-arrow-size)) / -2);
237
- inset-block-start: calc(50% - var(--xh-context-menu-arrow-size, var(--xh-overlay-arrow-size)) / 2);
266
+ inset-inline-start: calc(var(--xh-_context-menu-arrow-size) / -2);
267
+ inset-block-start: calc(var(--xh-_context-menu-arrow-y) - var(--xh-_context-menu-arrow-size) / 2);
238
268
  border-block-start: 0;
239
269
  border-inline-end: 0;
240
270
  }
241
271
 
242
272
  [data-scope='context-menu'][data-part='arrow'][data-placement^='left'] {
243
- inset-inline-end: calc(var(--xh-context-menu-arrow-size, var(--xh-overlay-arrow-size)) / -2);
244
- inset-block-start: calc(50% - var(--xh-context-menu-arrow-size, var(--xh-overlay-arrow-size)) / 2);
273
+ inset-inline-end: calc(var(--xh-_context-menu-arrow-size) / -2);
274
+ inset-block-start: calc(var(--xh-_context-menu-arrow-y) - var(--xh-_context-menu-arrow-size) / 2);
245
275
  border-block-end: 0;
246
276
  border-inline-start: 0;
247
277
  }
@@ -8,8 +8,8 @@
8
8
  --xh-_date-field-label-font-size: var(--xh-text-label-size);
9
9
  /* 形态档只换这三个私有槽:底色与两档描边 */
10
10
  --xh-_date-field-control-bg: var(--xh-bg-canvas);
11
- --xh-_date-field-control-border: var(--xh-border-default);
12
- --xh-_date-field-control-border-hover: var(--xh-border-strong);
11
+ --xh-_date-field-control-border: var(--xh-border-control);
12
+ --xh-_date-field-control-border-hover: var(--xh-border-control-hover);
13
13
  /* 聚焦环与段位反白随语气走。没写 data-tone 时这几个 --xh-_tone* 没被声明,退回原来的焦点色与品牌色 */
14
14
  --xh-_date-field-ring: var(--xh-ring-focus);
15
15
  --xh-_date-field-segment-bg: var(--xh-_tone, var(--xh-bg-brand));
@@ -39,15 +39,15 @@
39
39
  /* 描边式:与缺省等价,写出来是为了三档形态在皮肤里都看得见 */
40
40
  [data-scope='date-field'][data-part='root'][data-variant='outline'] {
41
41
  --xh-_date-field-control-bg: var(--xh-bg-canvas);
42
- --xh-_date-field-control-border: var(--xh-border-default);
43
- --xh-_date-field-control-border-hover: var(--xh-border-strong);
42
+ --xh-_date-field-control-border: var(--xh-border-control);
43
+ --xh-_date-field-control-border-hover: var(--xh-border-control-hover);
44
44
  }
45
45
 
46
46
  /* 填充式:底色顶替描边,悬停时才浮出一道淡边 */
47
47
  [data-scope='date-field'][data-part='root'][data-variant='subtle'] {
48
48
  --xh-_date-field-control-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
49
49
  --xh-_date-field-control-border: transparent;
50
- --xh-_date-field-control-border-hover: var(--xh-_tone-border, var(--xh-border-default));
50
+ --xh-_date-field-control-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
51
51
  }
52
52
 
53
53
  /* 无框式:底与边都让开,可见反馈只剩聚焦那一档 */
@@ -39,7 +39,7 @@
39
39
  block-size: var(--xh-date-picker-control-h, var(--xh-control-h-md));
40
40
  padding-inline: var(--xh-date-picker-control-px, var(--xh-control-px-md));
41
41
  padding-block: 0;
42
- border: var(--xh-stroke-thin) solid var(--xh-date-picker-control-border, var(--xh-border-default));
42
+ border: var(--xh-stroke-thin) solid var(--xh-date-picker-control-border, var(--xh-border-control));
43
43
  border-radius: var(--xh-date-picker-control-radius, var(--xh-shape-control));
44
44
  background: var(--xh-date-picker-control-bg, var(--xh-bg-canvas));
45
45
  color: var(--xh-date-picker-control-fg, var(--xh-fg-default));
@@ -52,7 +52,7 @@
52
52
 
53
53
  /* hover 只在中性态换描边:invalid 正拿边框颜色说事,被 hover 盖掉那条信息就没了 */
54
54
  [data-scope='date-picker'][data-part='control']:hover:not([data-disabled], [data-invalid]) {
55
- border-color: var(--xh-date-picker-control-border-hover, var(--xh-border-strong));
55
+ border-color: var(--xh-date-picker-control-border-hover, var(--xh-border-control-hover));
56
56
  }
57
57
 
58
58
  /* 焦点落在某一段或某个按钮上,而边框长在 control 上:焦点环得由内向外借一层。
@@ -213,6 +213,70 @@
213
213
  [data-scope='date-picker'][data-part='calendar'][hidden] {
214
214
  display: none;
215
215
  }
216
+
217
+ /* —— showTime:时间列贴在日历右侧、逐列滚动,确认按钮收口 ——
218
+ content 是 block,作者把日历与时间列包进一行排布;列自身只管滚与选项画法 */
219
+ [data-scope='date-picker'][data-part='time-column'] {
220
+ display: flex;
221
+ flex-direction: column;
222
+ gap: 2px;
223
+ max-block-size: var(--xh-date-picker-time-column-h, 14rem);
224
+ padding: var(--xh-space-1);
225
+ overflow-y: auto;
226
+ border-inline-start: var(--xh-stroke-thin) solid var(--xh-date-picker-content-border, var(--xh-border-default));
227
+ }
228
+
229
+ [data-scope='date-picker'][data-part='time-column'][hidden] {
230
+ display: none;
231
+ }
232
+
233
+ [data-scope='date-picker'][data-part='time-item'] {
234
+ padding-block: var(--xh-space-1);
235
+ padding-inline: var(--xh-space-3);
236
+ border-radius: var(--xh-shape-control);
237
+ font-size: var(--xh-font-size-sm);
238
+ line-height: var(--xh-leading-normal);
239
+ text-align: center;
240
+ cursor: pointer;
241
+ user-select: none;
242
+ }
243
+
244
+ [data-scope='date-picker'][data-part='time-item']:hover {
245
+ background: var(--xh-date-picker-time-item-bg-hover, var(--xh-bg-subtle));
246
+ }
247
+
248
+ [data-scope='date-picker'][data-part='time-item'][data-selected] {
249
+ background: var(--xh-date-picker-time-item-bg-selected, var(--xh-bg-brand-subtle));
250
+ color: var(--xh-date-picker-time-item-fg-selected, var(--xh-fg-brand));
251
+ font-weight: var(--xh-font-weight-medium);
252
+ }
253
+
254
+ [data-scope='date-picker'][data-part='confirm-trigger'] {
255
+ display: inline-flex;
256
+ align-items: center;
257
+ justify-content: center;
258
+ block-size: var(--xh-control-h-sm);
259
+ padding-inline: var(--xh-control-px-sm);
260
+ border: none;
261
+ border-radius: var(--xh-shape-control);
262
+ background: var(--xh-date-picker-confirm-bg, var(--xh-bg-brand));
263
+ color: var(--xh-date-picker-confirm-fg, var(--xh-fg-on-brand));
264
+ font-size: var(--xh-font-size-sm);
265
+ cursor: pointer;
266
+ }
267
+
268
+ [data-scope='date-picker'][data-part='confirm-trigger']:hover {
269
+ background: var(--xh-date-picker-confirm-bg-hover, var(--xh-bg-brand-hover));
270
+ }
271
+
272
+ [data-scope='date-picker'][data-part='confirm-trigger']:focus-visible {
273
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
274
+ outline-offset: var(--xh-ring-offset);
275
+ }
276
+
277
+ [data-scope='date-picker'][data-part='confirm-trigger'][hidden] {
278
+ display: none;
279
+ }
216
280
  }
217
281
 
218
282
  @layer xihan.motion {
package/css/drawer.css CHANGED
@@ -112,7 +112,7 @@
112
112
  gap: var(--xh-drawer-trigger-gap, var(--xh-control-gap-md));
113
113
  block-size: var(--xh-drawer-trigger-h, var(--xh-control-h-md));
114
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));
115
+ border: var(--xh-stroke-thin) solid var(--xh-drawer-trigger-border, var(--xh-border-control));
116
116
  border-radius: var(--xh-drawer-trigger-radius, var(--xh-shape-control));
117
117
  background: var(--xh-drawer-trigger-bg, var(--xh-bg-canvas));
118
118
  color: var(--xh-drawer-trigger-fg, var(--xh-fg-default));
@@ -132,14 +132,14 @@
132
132
 
133
133
  [data-scope='drawer'][data-part='trigger']:hover:not(:disabled) {
134
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));
135
+ border-color: var(--xh-drawer-trigger-border-hover, var(--xh-border-control-hover));
136
136
  }
137
137
 
138
138
  /* 抽屉展开期间触发按钮压住不弹起:面板贴在视口边上,它和按钮往往离得很远,
139
139
  按钮自己得说明"这一扇是我开的"。 */
140
140
  [data-scope='drawer'][data-part='trigger'][data-state='open'] {
141
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));
142
+ border-color: var(--xh-drawer-trigger-border-open, var(--xh-border-control-hover));
143
143
  }
144
144
 
145
145
  [data-scope='drawer'][data-part='trigger']:focus-visible {
@@ -117,7 +117,7 @@
117
117
  gap: var(--xh-dynamic-input-action-gap, var(--xh-space-1));
118
118
  block-size: var(--xh-dynamic-input-add-height, var(--xh-control-h-md));
119
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));
120
+ border: var(--xh-stroke-thin) dashed var(--xh-dynamic-input-add-border, var(--xh-border-control));
121
121
  border-radius: var(--xh-dynamic-input-add-radius, var(--xh-shape-control));
122
122
  background: var(--xh-dynamic-input-add-bg, transparent);
123
123
  color: var(--xh-dynamic-input-add-fg, var(--xh-fg-brand));
@@ -135,7 +135,7 @@
135
135
  }
136
136
 
137
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));
138
+ border-color: var(--xh-dynamic-input-add-border-hover, var(--xh-border-control-hover));
139
139
  background: var(--xh-dynamic-input-add-bg-hover, var(--xh-bg-subtle-hover));
140
140
  }
141
141
 
package/css/editable.css CHANGED
@@ -103,7 +103,7 @@
103
103
  block-size: var(--xh-editable-input-h, var(--xh-control-h-md));
104
104
  padding-inline: var(--xh-editable-input-px, var(--xh-control-px-md));
105
105
  padding-block: 0;
106
- border: var(--xh-stroke-thin) solid var(--xh-editable-input-border, var(--xh-border-default));
106
+ border: var(--xh-stroke-thin) solid var(--xh-editable-input-border, var(--xh-border-control));
107
107
  border-radius: var(--xh-editable-input-radius, var(--xh-shape-control));
108
108
  background: var(--xh-editable-input-bg, var(--xh-bg-canvas));
109
109
  color: var(--xh-editable-input-fg, var(--xh-fg-default));
@@ -131,7 +131,7 @@
131
131
 
132
132
  /* hover 只在中性态换描边:invalid 正拿边框颜色说事,被 hover 盖掉那条信息就没了 */
133
133
  [data-scope='editable'][data-part='input']:hover:not(:disabled, [data-invalid]) {
134
- border-color: var(--xh-editable-input-border-hover, var(--xh-border-strong));
134
+ border-color: var(--xh-editable-input-border-hover, var(--xh-border-control-hover));
135
135
  }
136
136
 
137
137
  [data-scope='editable'][data-part='input']:focus-visible {
@@ -178,7 +178,7 @@
178
178
  block-size: var(--xh-editable-trigger-h, var(--xh-control-h-sm));
179
179
  padding-inline: var(--xh-editable-trigger-px, var(--xh-control-px-sm));
180
180
  padding-block: 0;
181
- border: var(--xh-stroke-thin) solid var(--xh-editable-trigger-border, var(--xh-border-default));
181
+ border: var(--xh-stroke-thin) solid var(--xh-editable-trigger-border, var(--xh-border-control));
182
182
  border-radius: var(--xh-editable-trigger-radius, var(--xh-shape-control));
183
183
  background: var(--xh-editable-trigger-bg, var(--xh-bg-subtle));
184
184
  color: var(--xh-editable-trigger-fg, var(--xh-fg-default));
@@ -202,7 +202,7 @@
202
202
  [data-scope='editable'][data-part='edit-trigger']:hover:not(:disabled),
203
203
  [data-scope='editable'][data-part='submit-trigger']:hover:not(:disabled),
204
204
  [data-scope='editable'][data-part='cancel-trigger']:hover:not(:disabled) {
205
- border-color: var(--xh-editable-trigger-border-hover, var(--xh-border-strong));
205
+ border-color: var(--xh-editable-trigger-border-hover, var(--xh-border-control-hover));
206
206
  background: var(--xh-editable-trigger-bg-hover, var(--xh-bg-subtle-hover));
207
207
  }
208
208
 
@@ -238,12 +238,12 @@
238
238
  }
239
239
 
240
240
  [data-scope='editable'][data-part='submit-trigger']:hover:not(:disabled) {
241
- border-color: var(--xh-editable-submit-bg-hover, var(--xh-bg-brand-hover));
241
+ border-color: var(--xh-editable-submit-border-hover, var(--xh-editable-submit-bg-hover, var(--xh-bg-brand-hover)));
242
242
  background: var(--xh-editable-submit-bg-hover, var(--xh-bg-brand-hover));
243
243
  }
244
244
 
245
245
  [data-scope='editable'][data-part='submit-trigger']:active:not(:disabled) {
246
- border-color: var(--xh-editable-submit-bg-active, var(--xh-bg-brand-active));
246
+ border-color: var(--xh-editable-submit-border-active, var(--xh-editable-submit-bg-active, var(--xh-bg-brand-active)));
247
247
  background: var(--xh-editable-submit-bg-active, var(--xh-bg-brand-active));
248
248
  }
249
249
  }
package/css/field.css CHANGED
@@ -5,6 +5,30 @@
5
5
  gap: var(--xh-field-gap, var(--xh-space-1));
6
6
  }
7
7
 
8
+ /* 表单横排(标签左置):字段转两列网格,标签列宽由表单的 --xh-form-label-w 统一给,
9
+ 整表据此对齐;描述与错误文案跟着控件走第二列 */
10
+ [data-scope='form'][data-part='root'][data-layout='horizontal'] [data-scope='field'][data-part='root'] {
11
+ display: grid;
12
+ grid-template-columns: var(--xh-form-label-w) minmax(0, 1fr);
13
+ column-gap: var(--xh-field-label-gap, var(--xh-space-3));
14
+ align-items: start;
15
+ }
16
+
17
+ [data-scope='form'][data-part='root'][data-layout='horizontal'] [data-scope='field'][data-part='label'] {
18
+ grid-column: 1;
19
+ justify-self: end;
20
+ /* 与控件的首行文字对齐:按控件高度撑一行再居中 */
21
+ line-height: var(--xh-control-h-md);
22
+ }
23
+
24
+ [data-scope='form'][data-part='root'][data-layout='horizontal'][data-label-align='start'] [data-scope='field'][data-part='label'] {
25
+ justify-self: start;
26
+ }
27
+
28
+ [data-scope='form'][data-part='root'][data-layout='horizontal'] [data-scope='field'][data-part='root'] > :not([data-part='label']) {
29
+ grid-column: 2;
30
+ }
31
+
8
32
  [data-scope='field'][data-part='label'] {
9
33
  color: var(--xh-field-label-fg, var(--xh-fg-default));
10
34
  font-size: var(--xh-field-label-font-size, var(--xh-text-label-size));
@@ -16,12 +40,19 @@
16
40
  color: var(--xh-field-label-fg-disabled, var(--xh-fg-subtle));
17
41
  }
18
42
 
43
+ /* 必填星号:跟着根上的 data-required 亮,选填不标 */
44
+ [data-scope='field'][data-part='root'][data-required] [data-part='label']::after {
45
+ content: '*';
46
+ margin-inline-start: var(--xh-space-1);
47
+ color: var(--xh-field-label-star, var(--xh-fg-danger));
48
+ }
49
+
19
50
  /* control 是作者自己的输入控件,这里只给一套可被覆盖的基础皮肤 */
20
51
  [data-scope='field'][data-part='control'] {
21
52
  inline-size: 100%;
22
53
  block-size: var(--xh-field-control-h, var(--xh-control-h-md));
23
54
  padding-inline: var(--xh-field-control-px, var(--xh-control-px-md));
24
- border: var(--xh-stroke-thin) solid var(--xh-field-control-border, var(--xh-border-default));
55
+ border: var(--xh-stroke-thin) solid var(--xh-field-control-border, var(--xh-border-control));
25
56
  border-radius: var(--xh-field-control-radius, var(--xh-shape-control));
26
57
  background: var(--xh-field-control-bg, var(--xh-bg-canvas));
27
58
  color: var(--xh-field-control-fg, var(--xh-fg-default));
@@ -32,7 +32,7 @@
32
32
  min-block-size: var(--xh-file-upload-dropzone-min-h, 8rem);
33
33
  padding-block: var(--xh-file-upload-dropzone-py, var(--xh-space-5));
34
34
  padding-inline: var(--xh-file-upload-dropzone-px, var(--xh-space-4));
35
- border: var(--xh-stroke-thick) dashed var(--xh-file-upload-dropzone-border, var(--xh-border-default));
35
+ border: var(--xh-stroke-thick) dashed var(--xh-file-upload-dropzone-border, var(--xh-border-control));
36
36
  border-radius: var(--xh-file-upload-dropzone-radius, var(--xh-shape-surface));
37
37
  background: var(--xh-file-upload-dropzone-bg, var(--xh-bg-canvas));
38
38
  color: var(--xh-file-upload-dropzone-fg, var(--xh-fg-muted));
@@ -51,7 +51,7 @@
51
51
 
52
52
  /* hover 只在中性态换描边:invalid 与 dragging 正拿边框颜色说事,被 hover 盖掉那条信息就没了 */
53
53
  [data-scope='file-upload'][data-part='dropzone']:hover:not([data-disabled], [data-invalid], [data-dragging]) {
54
- border-color: var(--xh-file-upload-dropzone-border-hover, var(--xh-border-strong));
54
+ border-color: var(--xh-file-upload-dropzone-border-hover, var(--xh-border-control-hover));
55
55
  }
56
56
 
57
57
  /* 投放区自己承载焦点(它就是那个大按钮),所以画环而不是 outline:none */
@@ -86,7 +86,7 @@
86
86
  gap: var(--xh-file-upload-trigger-gap, var(--xh-control-gap-md));
87
87
  block-size: var(--xh-file-upload-trigger-h, var(--xh-control-h-md));
88
88
  padding-inline: var(--xh-file-upload-trigger-px, var(--xh-control-px-md));
89
- border: var(--xh-stroke-thin) solid var(--xh-file-upload-trigger-border, var(--xh-border-default));
89
+ border: var(--xh-stroke-thin) solid var(--xh-file-upload-trigger-border, var(--xh-border-control));
90
90
  border-radius: var(--xh-file-upload-trigger-radius, var(--xh-shape-control));
91
91
  background: var(--xh-file-upload-trigger-bg, var(--xh-bg-surface));
92
92
  color: var(--xh-file-upload-trigger-fg, var(--xh-fg-default));
@@ -56,7 +56,7 @@
56
56
  inline-size: var(--xh-_float-button-size);
57
57
  block-size: var(--xh-_float-button-size);
58
58
  padding: 0;
59
- border: var(--xh-stroke-thin) solid var(--xh-float-button-border, var(--xh-border-default));
59
+ border: var(--xh-stroke-thin) solid var(--xh-float-button-border, var(--xh-border-control));
60
60
  border-radius: var(--xh-_float-button-radius);
61
61
  /* 使用者令牌排在缺省之前:写了覆盖钩子就以它为准,没写才退到中性缺省 */
62
62
  background: var(--xh-float-button-bg, var(--xh-bg-surface-raised));
@@ -72,7 +72,7 @@
72
72
  /* 悬停只在能用指针的设备上生效,触摸端不留一个按不掉的悬停态 */
73
73
  @media (hover: hover) {
74
74
  [data-scope='float-button'][data-part='trigger']:not([data-disabled]):hover {
75
- border-color: var(--xh-float-button-border-hover, var(--xh-border-strong));
75
+ border-color: var(--xh-float-button-border-hover, var(--xh-border-control-hover));
76
76
  background: var(--xh-float-button-bg-hover, var(--xh-bg-subtle-hover));
77
77
  }
78
78
  }
package/css/form.css CHANGED
@@ -12,6 +12,18 @@
12
12
  display: none;
13
13
  }
14
14
 
15
+ /* 标签左置:标签列宽的缺省档在这儿,labelWidth 会以行内样式盖过它 */
16
+ [data-scope='form'][data-part='root'][data-layout='horizontal'] {
17
+ --xh-form-label-w: 30%;
18
+ }
19
+
20
+ /* 横排一行流:字段并肩排,放不下自动折行 */
21
+ [data-scope='form'][data-part='root'][data-layout='inline'] {
22
+ flex-flow: row wrap;
23
+ align-items: start;
24
+ column-gap: var(--xh-form-inline-gap, var(--xh-space-4));
25
+ }
26
+
15
27
  /* 字段容器只管排版与状态标记:标题、控件、描述、错误文案都是里面那个 Field 的活儿 */
16
28
  [data-scope='form'][data-part='field-group'] {
17
29
  display: flex;
@@ -104,7 +116,7 @@
104
116
  block-size: var(--xh-form-trigger-h, var(--xh-control-h-md));
105
117
  padding-block: 0;
106
118
  padding-inline: var(--xh-form-trigger-px, var(--xh-control-px-md));
107
- border: var(--xh-stroke-thin) solid var(--xh-form-trigger-border, var(--xh-border-default));
119
+ border: var(--xh-stroke-thin) solid var(--xh-form-trigger-border, var(--xh-border-control));
108
120
  border-radius: var(--xh-form-trigger-radius, var(--xh-shape-control));
109
121
  background: var(--xh-form-trigger-bg, var(--xh-bg-subtle));
110
122
  color: var(--xh-form-trigger-fg, var(--xh-fg-default));
@@ -125,7 +137,7 @@
125
137
 
126
138
  [data-scope='form'][data-part='submit-trigger']:hover:not(:disabled),
127
139
  [data-scope='form'][data-part='reset-trigger']:hover:not(:disabled) {
128
- border-color: var(--xh-form-trigger-border-hover, var(--xh-border-strong));
140
+ border-color: var(--xh-form-trigger-border-hover, var(--xh-border-control-hover));
129
141
  background: var(--xh-form-trigger-bg-hover, var(--xh-bg-subtle-hover));
130
142
  }
131
143
 
@@ -152,18 +164,18 @@
152
164
 
153
165
  /* 提交是这一组里的主操作,给它一层实心底色区分开 */
154
166
  [data-scope='form'][data-part='submit-trigger']:not(:disabled) {
155
- border-color: var(--xh-form-submit-bg, var(--xh-bg-brand));
167
+ border-color: var(--xh-form-submit-border, var(--xh-form-submit-bg, var(--xh-bg-brand)));
156
168
  background: var(--xh-form-submit-bg, var(--xh-bg-brand));
157
169
  color: var(--xh-form-submit-fg, var(--xh-fg-on-brand));
158
170
  }
159
171
 
160
172
  [data-scope='form'][data-part='submit-trigger']:hover:not(:disabled) {
161
- border-color: var(--xh-form-submit-bg-hover, var(--xh-bg-brand-hover));
173
+ border-color: var(--xh-form-submit-border-hover, var(--xh-form-submit-bg-hover, var(--xh-bg-brand-hover)));
162
174
  background: var(--xh-form-submit-bg-hover, var(--xh-bg-brand-hover));
163
175
  }
164
176
 
165
177
  [data-scope='form'][data-part='submit-trigger']:active:not(:disabled) {
166
- border-color: var(--xh-form-submit-bg-active, var(--xh-bg-brand-active));
178
+ border-color: var(--xh-form-submit-border-active, var(--xh-form-submit-bg-active, var(--xh-bg-brand-active)));
167
179
  background: var(--xh-form-submit-bg-active, var(--xh-bg-brand-active));
168
180
  }
169
181
  }
@@ -100,40 +100,44 @@
100
100
  }
101
101
 
102
102
  /* 箭头是旋转 45° 的方块,只保留朝向锚点那两条边的描边;
103
- 引擎不产出箭头偏移,因此沿交叉轴固定居中 */
103
+ 交叉轴落点由定位引擎经内联自定义属性写进来,没给时退回居中 */
104
104
  [data-scope='hover-card'][data-part='arrow'] {
105
+ --xh-_hover-card-arrow-size: var(--xh-hover-card-arrow-size, var(--xh-overlay-arrow-size));
106
+ --xh-_hover-card-arrow-x: 50%;
107
+ --xh-_hover-card-arrow-y: 50%;
108
+
105
109
  position: absolute;
106
- inline-size: var(--xh-hover-card-arrow-size, var(--xh-overlay-arrow-size));
107
- block-size: var(--xh-hover-card-arrow-size, var(--xh-overlay-arrow-size));
110
+ inline-size: var(--xh-_hover-card-arrow-size);
111
+ block-size: var(--xh-_hover-card-arrow-size);
108
112
  border: var(--xh-stroke-thin) solid var(--xh-hover-card-border, var(--xh-border-default));
109
113
  background: var(--xh-hover-card-bg, var(--xh-bg-surface));
110
114
  rotate: 45deg;
111
115
  }
112
116
 
113
117
  [data-scope='hover-card'][data-part='arrow'][data-placement^='bottom'] {
114
- inset-block-start: calc(var(--xh-hover-card-arrow-size, var(--xh-overlay-arrow-size)) / -2);
115
- inset-inline-start: calc(50% - var(--xh-hover-card-arrow-size, var(--xh-overlay-arrow-size)) / 2);
118
+ inset-block-start: calc(var(--xh-_hover-card-arrow-size) / -2);
119
+ inset-inline-start: calc(var(--xh-_hover-card-arrow-x) - var(--xh-_hover-card-arrow-size) / 2);
116
120
  border-block-end: 0;
117
121
  border-inline-end: 0;
118
122
  }
119
123
 
120
124
  [data-scope='hover-card'][data-part='arrow'][data-placement^='top'] {
121
- inset-block-end: calc(var(--xh-hover-card-arrow-size, var(--xh-overlay-arrow-size)) / -2);
122
- inset-inline-start: calc(50% - var(--xh-hover-card-arrow-size, var(--xh-overlay-arrow-size)) / 2);
125
+ inset-block-end: calc(var(--xh-_hover-card-arrow-size) / -2);
126
+ inset-inline-start: calc(var(--xh-_hover-card-arrow-x) - var(--xh-_hover-card-arrow-size) / 2);
123
127
  border-block-start: 0;
124
128
  border-inline-start: 0;
125
129
  }
126
130
 
127
131
  [data-scope='hover-card'][data-part='arrow'][data-placement^='right'] {
128
- inset-inline-start: calc(var(--xh-hover-card-arrow-size, var(--xh-overlay-arrow-size)) / -2);
129
- inset-block-start: calc(50% - var(--xh-hover-card-arrow-size, var(--xh-overlay-arrow-size)) / 2);
132
+ inset-inline-start: calc(var(--xh-_hover-card-arrow-size) / -2);
133
+ inset-block-start: calc(var(--xh-_hover-card-arrow-y) - var(--xh-_hover-card-arrow-size) / 2);
130
134
  border-block-start: 0;
131
135
  border-inline-end: 0;
132
136
  }
133
137
 
134
138
  [data-scope='hover-card'][data-part='arrow'][data-placement^='left'] {
135
- inset-inline-end: calc(var(--xh-hover-card-arrow-size, var(--xh-overlay-arrow-size)) / -2);
136
- inset-block-start: calc(50% - var(--xh-hover-card-arrow-size, var(--xh-overlay-arrow-size)) / 2);
139
+ inset-inline-end: calc(var(--xh-_hover-card-arrow-size) / -2);
140
+ inset-block-start: calc(var(--xh-_hover-card-arrow-y) - var(--xh-_hover-card-arrow-size) / 2);
137
141
  border-block-end: 0;
138
142
  border-inline-start: 0;
139
143
  }