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

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 (110) hide show
  1. package/CHANGELOG.md +341 -0
  2. package/css/accordion.css +5 -0
  3. package/css/affix.css +5 -0
  4. package/css/alert.css +5 -0
  5. package/css/anchor.css +5 -0
  6. package/css/avatar-group.css +5 -0
  7. package/css/avatar.css +5 -0
  8. package/css/back-top.css +7 -2
  9. package/css/badge.css +5 -0
  10. package/css/breadcrumb.css +5 -0
  11. package/css/button-group.css +6 -1
  12. package/css/button.css +16 -1
  13. package/css/calendar.css +6 -1
  14. package/css/card.css +5 -0
  15. package/css/carousel.css +7 -2
  16. package/css/cascader.css +204 -24
  17. package/css/checkbox-group.css +25 -20
  18. package/css/checkbox.css +6 -1
  19. package/css/clipboard.css +8 -3
  20. package/css/code-block.css +5 -0
  21. package/css/collapsible.css +5 -0
  22. package/css/color-picker.css +27 -6
  23. package/css/combobox.css +42 -20
  24. package/css/composer.css +6 -1
  25. package/css/context-menu.css +62 -34
  26. package/css/countdown.css +5 -0
  27. package/css/date-field.css +10 -5
  28. package/css/date-picker.css +72 -3
  29. package/css/descriptions.css +5 -0
  30. package/css/dialog.css +69 -9
  31. package/css/drawer.css +33 -12
  32. package/css/dynamic-input.css +7 -2
  33. package/css/editable.css +11 -6
  34. package/css/ellipsis.css +5 -0
  35. package/css/empty-state.css +5 -0
  36. package/css/field.css +37 -1
  37. package/css/file-upload.css +8 -3
  38. package/css/flex.css +5 -0
  39. package/css/float-button.css +7 -2
  40. package/css/form.css +23 -6
  41. package/css/gradient-text.css +5 -0
  42. package/css/grid.css +5 -0
  43. package/css/highlight.css +5 -0
  44. package/css/hover-card.css +34 -17
  45. package/css/icon-wrapper.css +5 -0
  46. package/css/icon.css +5 -0
  47. package/css/image-viewer.css +196 -0
  48. package/css/image.css +5 -0
  49. package/css/infinite-scroll.css +5 -0
  50. package/css/layout.css +5 -0
  51. package/css/list.css +5 -0
  52. package/css/listbox.css +14 -6
  53. package/css/loading-bar.css +5 -0
  54. package/css/log.css +5 -0
  55. package/css/marquee.css +5 -0
  56. package/css/mention.css +33 -22
  57. package/css/menu.css +57 -23
  58. package/css/menubar.css +34 -25
  59. package/css/navigation-menu.css +6 -1
  60. package/css/number-animation.css +5 -0
  61. package/css/number-field.css +21 -16
  62. package/css/page-header.css +5 -0
  63. package/css/pagination.css +5 -0
  64. package/css/pin-input.css +11 -6
  65. package/css/popconfirm.css +44 -0
  66. package/css/popover.css +34 -11
  67. package/css/popselect.css +50 -16
  68. package/css/progress.css +73 -1
  69. package/css/qr-code.css +5 -0
  70. package/css/radio-group.css +6 -1
  71. package/css/rating.css +5 -0
  72. package/css/result.css +5 -0
  73. package/css/scroll-area.css +5 -6
  74. package/css/select.css +167 -17
  75. package/css/separator.css +5 -0
  76. package/css/side-nav.css +168 -0
  77. package/css/skeleton.css +5 -0
  78. package/css/slider.css +60 -0
  79. package/css/spinner.css +9 -1
  80. package/css/splitter.css +5 -0
  81. package/css/statistic.css +5 -0
  82. package/css/steps.css +7 -2
  83. package/css/switch.css +34 -0
  84. package/css/table.css +11 -6
  85. package/css/tabs.css +5 -0
  86. package/css/tags-input.css +12 -7
  87. package/css/text-field.css +28 -10
  88. package/css/thread.css +6 -1
  89. package/css/time-field.css +10 -5
  90. package/css/time-picker.css +42 -27
  91. package/css/time.css +5 -0
  92. package/css/timeline.css +5 -0
  93. package/css/toast.css +13 -12
  94. package/css/toaster.css +5 -0
  95. package/css/toggle-group.css +6 -1
  96. package/css/toggle.css +6 -1
  97. package/css/tone.css +22 -0
  98. package/css/toolbar.css +5 -0
  99. package/css/tooltip.css +39 -21
  100. package/css/tour.css +45 -21
  101. package/css/transfer.css +16 -9
  102. package/css/tree-select.css +45 -18
  103. package/css/tree.css +13 -5
  104. package/css/typography.css +5 -0
  105. package/css/virtualizer.css +5 -0
  106. package/css/watermark.css +5 -0
  107. package/index.css +6 -3
  108. package/index.unlayered.css +2325 -588
  109. package/package.json +4 -3
  110. package/css/motion.css +0 -58
package/css/combobox.css CHANGED
@@ -1,4 +1,23 @@
1
+ @layer xihan.motion {
2
+ /* 动画名的查找只认「文档里有没有这个名字」:引用别处文件里的名字,那份文件不一定在场,
3
+ 动画会静默地整个不跑。每份皮肤都能单独引入,所以它用到的关键帧都写在自己身上。 */
4
+ @keyframes xh-fade-in {
5
+ from {
6
+ opacity: 0;
7
+ }
8
+
9
+ to {
10
+ opacity: 1;
11
+ }
12
+ }
13
+ }
14
+
1
15
  @layer xihan.components {
16
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
17
+ [data-scope='combobox'] {
18
+ --xh-combobox-skin: 1;
19
+ }
20
+
2
21
  [data-scope='combobox'][data-part='root'] {
3
22
  /* 尺寸档只换这五个私有槽,输入行、标题与候选都从 root 继承它们,
4
23
  换档不必给每个部件各写一条选择器。浮层在 positioner 里、也是 root 的后代,一并继承 */
@@ -10,14 +29,13 @@
10
29
 
11
30
  /* 形态档只换这三个槽:缺省即 outline(描边 + 画布底) */
12
31
  --xh-_combobox-control-bg: var(--xh-bg-canvas);
13
- --xh-_combobox-control-border: var(--xh-border-default);
14
- --xh-_combobox-control-border-hover: var(--xh-border-strong);
32
+ --xh-_combobox-control-border: var(--xh-border-control);
33
+ --xh-_combobox-control-border-hover: var(--xh-border-control-hover);
15
34
 
16
- /* 强调色随语气走,只落在聚焦与选中上,正文文字色不动。
17
- 没写 data-tone 时 --xh-_tone 没被声明,三处都退回原来的取值 */
35
+ /* 聚焦环恒用固定环色;强调色随语气走,只落在勾选标记与选中文字上,正文文字色不动。
36
+ 没写 data-tone 时 --xh-_tone 没被声明,两处都退回原来的取值 */
18
37
  --xh-_combobox-ring: var(--xh-ring-focus);
19
38
  --xh-_combobox-accent: var(--xh-_tone, var(--xh-bg-brand));
20
- --xh-_combobox-item-highlight-bg: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover));
21
39
  --xh-_combobox-item-selected-fg: var(--xh-_tone-fg, var(--xh-fg-brand-strong));
22
40
 
23
41
  display: inline-flex;
@@ -53,21 +71,21 @@
53
71
  形态只决定描边与底色怎么用,用哪族颜色由 data-tone 决定 */
54
72
  [data-scope='combobox'][data-part='root'][data-variant='outline'] {
55
73
  --xh-_combobox-control-bg: var(--xh-bg-canvas);
56
- --xh-_combobox-control-border: var(--xh-border-default);
57
- --xh-_combobox-control-border-hover: var(--xh-border-strong);
74
+ --xh-_combobox-control-border: var(--xh-border-control);
75
+ --xh-_combobox-control-border-hover: var(--xh-border-control-hover);
58
76
  }
59
77
 
60
78
  /* 填充式:底色顶替描边,悬停时才浮出一道淡边 */
61
79
  [data-scope='combobox'][data-part='root'][data-variant='subtle'] {
62
80
  --xh-_combobox-control-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
63
81
  --xh-_combobox-control-border: transparent;
64
- --xh-_combobox-control-border-hover: var(--xh-_tone-border, var(--xh-border-default));
82
+ --xh-_combobox-control-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
65
83
  }
66
84
 
67
85
  [data-scope='combobox'][data-part='root'][data-variant='ghost'] {
68
86
  --xh-_combobox-control-bg: transparent;
69
87
  --xh-_combobox-control-border: transparent;
70
- --xh-_combobox-control-border-hover: var(--xh-_tone-border, var(--xh-border-default));
88
+ --xh-_combobox-control-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
71
89
  }
72
90
 
73
91
  [data-scope='combobox'][data-part='label'] {
@@ -205,11 +223,11 @@
205
223
  display: none;
206
224
  }
207
225
 
208
- /* 展开时箭头翻个面,指向与列表的去向一致;transition 要重写全份,只补一条会覆盖掉上面那条 */
209
- [data-scope='combobox'][data-part='trigger'] {
210
- transition:
211
- background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
212
- rotate var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
226
+ /* 展开时箭头翻个面,指向与列表的去向一致。转的是字形或作者塞的图形,不转按钮本体:
227
+ 按钮自带悬停底色,整个按钮转会带着底色一起转 */
228
+ [data-scope='combobox'][data-part='trigger']:empty::before,
229
+ [data-scope='combobox'][data-part='trigger'] > * {
230
+ transition: rotate var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
213
231
  }
214
232
 
215
233
  /* 没写内容就由皮肤画箭头;作者往部件里塞了自己的图形,这条规则即不命中 */
@@ -222,7 +240,8 @@
222
240
  content: '✕';
223
241
  }
224
242
 
225
- [data-scope='combobox'][data-part='trigger'][data-state='open'] {
243
+ [data-scope='combobox'][data-part='trigger'][data-state='open']:empty::before,
244
+ [data-scope='combobox'][data-part='trigger'][data-state='open'] > * {
226
245
  rotate: 180deg;
227
246
  }
228
247
 
@@ -315,7 +334,7 @@
315
334
  [data-scope='combobox'][data-part='item-group-label'] {
316
335
  display: block;
317
336
  padding-block: var(--xh-combobox-group-label-py, var(--xh-space-1));
318
- padding-inline: var(--xh-combobox-group-label-px, var(--xh-control-px-sm));
337
+ padding-inline: var(--xh-combobox-group-label-px, var(--xh-control-px-md));
319
338
  color: var(--xh-combobox-group-label-fg, var(--xh-fg-subtle));
320
339
  font-size: var(--xh-combobox-group-label-font-size, var(--xh-font-size-xs));
321
340
  font-weight: var(--xh-combobox-group-label-font-weight, var(--xh-font-weight-medium));
@@ -331,8 +350,8 @@
331
350
  display: flex;
332
351
  align-items: center;
333
352
  gap: var(--xh-combobox-item-gap, var(--xh-_combobox-gap));
334
- padding-block: var(--xh-combobox-item-py, var(--xh-space-1));
335
- padding-inline: var(--xh-combobox-item-px, var(--xh-control-px-sm));
353
+ padding-block: var(--xh-combobox-item-py, var(--xh-space-1_5));
354
+ padding-inline: var(--xh-combobox-item-px, var(--xh-control-px-md));
336
355
  border-radius: var(--xh-combobox-item-radius, var(--xh-shape-control));
337
356
  color: var(--xh-combobox-item-fg, var(--xh-fg-default));
338
357
  font-size: var(--xh-combobox-item-font-size, var(--xh-_combobox-font-size));
@@ -341,6 +360,9 @@
341
360
  white-space: nowrap;
342
361
  cursor: pointer;
343
362
  user-select: none;
363
+ transition:
364
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
365
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
344
366
  }
345
367
 
346
368
  /* 候选按需收起(作者自己藏掉某几条)时要让位,有 display 就得补这条 */
@@ -348,10 +370,10 @@
348
370
  display: none;
349
371
  }
350
372
 
351
- /* 只认 data-highlighted,不写 :hover:指针划过时连接层已经把高亮搬过来了,
373
+ /* 键盘锚点走中性灰轻档。只认 data-highlighted,不写 :hover:指针划过时连接层已经把高亮搬过来了,
352
374
  两套底色各画各的会出现"鼠标停在 A 上、回车提交的却是 B" */
353
375
  [data-scope='combobox'][data-part='item'][data-highlighted]:not([data-disabled]) {
354
- background: var(--xh-combobox-item-bg-highlight, var(--xh-_combobox-item-highlight-bg));
376
+ background: var(--xh-combobox-item-bg-hover, var(--xh-bg-subtle));
355
377
  }
356
378
 
357
379
  /* 选中只换文字色与字重,配合行尾的 ✓ 标记;底色整条通道留给高亮,
package/css/composer.css CHANGED
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='composer'] {
4
+ --xh-composer-skin: 1;
5
+ }
6
+
2
7
  /* 边框画在 root 上,内部 textarea 无边框;align-items: flex-end 让按钮随输入框长高贴着底边 */
3
8
  [data-scope='composer'][data-part='root'] {
4
9
  /* 输入与发送按钮共用的控件高度 */
@@ -8,7 +13,7 @@
8
13
  align-items: flex-end;
9
14
  gap: var(--xh-composer-gap, var(--xh-control-gap-md));
10
15
  padding: var(--xh-composer-p, var(--xh-space-2));
11
- border: var(--xh-stroke-thin) solid var(--xh-composer-border, var(--xh-border-default));
16
+ border: var(--xh-stroke-thin) solid var(--xh-composer-border, var(--xh-border-control));
12
17
  border-radius: var(--xh-composer-radius, var(--xh-shape-surface));
13
18
  background: var(--xh-composer-bg, var(--xh-bg-surface));
14
19
  }
@@ -1,16 +1,24 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='context-menu'] {
4
+ --xh-context-menu-skin: 1;
5
+ }
6
+
2
7
  /* 根只是把触发区与浮层圈在一起做作用域,不该在作者布局里多插一层盒子 */
3
8
  [data-scope='context-menu'][data-part='root'] {
4
9
  /* 尺寸档只换这四个私有槽,条目与分组标题都从 root 继承它们,
5
10
  换档不必给每个部件各写一条选择器。浮层在 positioner 里、也是 root 的后代,一并继承
6
11
  (display:contents 不影响继承:自定义属性沿 DOM 树传) */
7
- --xh-_context-menu-item-py: var(--xh-space-2);
8
- --xh-_context-menu-item-px: var(--xh-control-px-sm);
12
+ --xh-_context-menu-item-py: var(--xh-space-1_5);
13
+ --xh-_context-menu-item-px: var(--xh-control-px-md);
9
14
  --xh-_context-menu-item-gap: var(--xh-control-gap-md);
10
15
  --xh-_context-menu-font-size: var(--xh-text-body-size);
11
16
 
12
- /* 条目高亮底与标记位强调色随语气走。没写 data-tone 时这两个语气槽没被声明,各自退回原来的取值 */
13
- --xh-_context-menu-item-highlight-bg: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover));
17
+ /* 打开子菜单的触发条目走品牌淡底强档,随语气换色相;没写 data-tone 时退回品牌淡底。
18
+ 悬停与键盘焦点恒中性灰轻档、不接语气槽——两档同色相的话打开路径就认不出来了 */
19
+ --xh-_context-menu-item-active-bg: var(--xh-_tone-subtle, var(--xh-bg-brand-subtle));
20
+
21
+ /* 标记位强调色随语气走,没写 data-tone 时退回原来的取值 */
14
22
  --xh-_context-menu-accent: var(--xh-_tone, var(--xh-fg-subtle));
15
23
 
16
24
  display: contents;
@@ -21,18 +29,18 @@
21
29
  }
22
30
 
23
31
  /* —— 尺寸 —— */
24
- /* 缺省档写在上面的基础规则里。条目内外边距缺省档本就不在 control-h/px 那把尺上
25
- (高度由 padding 撑出来、行内内缩取的是 sm 档),故按 space 档上下各挪一级 */
32
+ /* 缺省档写在上面的基础规则里。条目高度由 padding 撑出来:
33
+ 纵向走 space 档、行内内缩走 control-px 档(随密度轴),换档各挪一级 */
26
34
  [data-scope='context-menu'][data-part='root'][data-size='sm'] {
27
35
  --xh-_context-menu-item-py: var(--xh-space-1);
28
- --xh-_context-menu-item-px: var(--xh-space-1);
36
+ --xh-_context-menu-item-px: var(--xh-control-px-sm);
29
37
  --xh-_context-menu-item-gap: var(--xh-control-gap-sm);
30
38
  --xh-_context-menu-font-size: var(--xh-font-size-sm);
31
39
  }
32
40
 
33
41
  [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);
42
+ --xh-_context-menu-item-py: var(--xh-space-2);
43
+ --xh-_context-menu-item-px: var(--xh-control-px-lg);
36
44
  --xh-_context-menu-item-gap: var(--xh-control-gap-lg);
37
45
  --xh-_context-menu-font-size: var(--xh-font-size-lg);
38
46
  }
@@ -82,8 +90,14 @@
82
90
  display: none;
83
91
  }
84
92
 
85
- /* 容器只是焦点域兜底落点,落焦不画环:高亮永远长在条目上 */
86
- [data-scope='context-menu'][data-part='content']:focus-visible {
93
+ /* 焦点落在容器上只有一种情形:指针打开、还没有任何条目当锚点。那一刻它认领着 Tab 位,
94
+ 环画在浮层内沿,用户才看得出键盘此刻在哪儿;有锚点时环长在条目上,容器不再画 */
95
+ [data-scope='context-menu'][data-part='content'][tabindex='0']:focus-visible {
96
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
97
+ outline-offset: calc(-1 * var(--xh-ring-width));
98
+ }
99
+
100
+ [data-scope='context-menu'][data-part='content']:not([tabindex='0']):focus-visible {
87
101
  outline: none;
88
102
  }
89
103
 
@@ -93,13 +107,6 @@
93
107
  animation: xh-context-menu-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
94
108
  }
95
109
 
96
- /* 关掉动效的用户不该被涨开动画晃一下;这里只管本组件,公共那份不动 */
97
- @media (prefers-reduced-motion: reduce) {
98
- [data-scope='context-menu'][data-part='content'][data-state='open'] {
99
- animation-duration: 0.01ms;
100
- }
101
- }
102
-
103
110
  [data-scope='context-menu'][data-part='group'] {
104
111
  display: flex;
105
112
  flex-direction: column;
@@ -135,10 +142,13 @@
135
142
  border-radius: var(--xh-context-menu-item-radius, var(--xh-shape-control));
136
143
  color: var(--xh-context-menu-item-fg, var(--xh-fg-default));
137
144
  font-size: var(--xh-context-menu-item-font-size, var(--xh-_context-menu-font-size));
138
- line-height: var(--xh-leading-none);
145
+ line-height: var(--xh-context-menu-item-leading, var(--xh-leading-normal));
139
146
  white-space: nowrap;
140
147
  cursor: pointer;
141
148
  user-select: none;
149
+ transition:
150
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
151
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
142
152
  }
143
153
 
144
154
  /* 条目可以按需收起(按上下文过滤掉的命令),有 display 就得补这条 */
@@ -146,14 +156,28 @@
146
156
  display: none;
147
157
  }
148
158
 
149
- /* 键盘高亮与指针高亮共用一套底色:条目本身承载焦点,不另画描边。
159
+ /* 悬停与键盘焦点共用中性灰轻档:条目本身承载焦点,不另画描边。
150
160
  data-highlighted 跟的是连接层记的焦点条目,与 :focus 说的是同一件事,
151
161
  子部件够不着那个伪类,只能读这个标记 */
152
162
  [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));
163
+ background: var(--xh-context-menu-item-bg-hover, var(--xh-bg-subtle));
164
+ }
165
+
166
+ /* 打开子菜单的触发条目是展开路径,走品牌淡底加粗强档。
167
+ 必须排在轻档之后:悬停或焦点回到它身上时同特指度靠书写顺序压过轻档 */
168
+ [data-scope='context-menu'][data-part='item']:not([data-disabled])[data-state='open'] {
169
+ background: var(--xh-context-menu-item-bg-active, var(--xh-_context-menu-item-active-bg));
170
+ font-weight: var(--xh-context-menu-item-active-font-weight, var(--xh-font-weight-semibold));
154
171
  }
155
172
 
156
- [data-scope='context-menu'][data-part='item']:focus {
173
+ /* 键盘走到条目上要画环:轻档底色只有 1.1:1,够不上焦点指示器的对比度要求。
174
+ 指针悬停走的是程序化聚焦,鼠标模态下 :focus-visible 不匹配,不会误画 */
175
+ [data-scope='context-menu'][data-part='item']:focus-visible {
176
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
177
+ outline-offset: calc(-1 * var(--xh-ring-width));
178
+ }
179
+
180
+ [data-scope='context-menu'][data-part='item']:focus:not(:focus-visible) {
157
181
  outline: none;
158
182
  }
159
183
 
@@ -204,11 +228,15 @@
204
228
  }
205
229
 
206
230
  /* 箭头是旋转 45° 的方块,只保留朝向锚点那两条边的描边;
207
- 引擎不产出箭头偏移,因此沿交叉轴固定居中 */
231
+ 交叉轴落点由定位引擎经内联自定义属性写进来,没给时退回居中 */
208
232
  [data-scope='context-menu'][data-part='arrow'] {
233
+ --xh-_context-menu-arrow-size: var(--xh-context-menu-arrow-size, var(--xh-overlay-arrow-size));
234
+ --xh-_context-menu-arrow-x: 50%;
235
+ --xh-_context-menu-arrow-y: 50%;
236
+
209
237
  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));
238
+ inline-size: var(--xh-_context-menu-arrow-size);
239
+ block-size: var(--xh-_context-menu-arrow-size);
212
240
  background: var(--xh-context-menu-bg, var(--xh-bg-surface));
213
241
  border: var(--xh-stroke-thin) solid var(--xh-context-menu-border, var(--xh-border-default));
214
242
  rotate: 45deg;
@@ -219,29 +247,29 @@
219
247
  }
220
248
 
221
249
  [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);
250
+ inset-block-start: calc(var(--xh-_context-menu-arrow-size) / -2);
251
+ inset-inline-start: calc(var(--xh-_context-menu-arrow-x) - var(--xh-_context-menu-arrow-size) / 2);
224
252
  border-block-end: 0;
225
253
  border-inline-end: 0;
226
254
  }
227
255
 
228
256
  [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);
257
+ inset-block-end: calc(var(--xh-_context-menu-arrow-size) / -2);
258
+ inset-inline-start: calc(var(--xh-_context-menu-arrow-x) - var(--xh-_context-menu-arrow-size) / 2);
231
259
  border-block-start: 0;
232
260
  border-inline-start: 0;
233
261
  }
234
262
 
235
263
  [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);
264
+ inset-inline-start: calc(var(--xh-_context-menu-arrow-size) / -2);
265
+ inset-block-start: calc(var(--xh-_context-menu-arrow-y) - var(--xh-_context-menu-arrow-size) / 2);
238
266
  border-block-start: 0;
239
267
  border-inline-end: 0;
240
268
  }
241
269
 
242
270
  [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);
271
+ inset-inline-end: calc(var(--xh-_context-menu-arrow-size) / -2);
272
+ inset-block-start: calc(var(--xh-_context-menu-arrow-y) - var(--xh-_context-menu-arrow-size) / 2);
245
273
  border-block-end: 0;
246
274
  border-inline-start: 0;
247
275
  }
@@ -252,7 +280,7 @@
252
280
  @keyframes xh-context-menu-in {
253
281
  from {
254
282
  opacity: 0;
255
- scale: 0.96;
283
+ scale: var(--xh-motion-scale-enter);
256
284
  }
257
285
 
258
286
  to {
package/css/countdown.css CHANGED
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='countdown'] {
4
+ --xh-countdown-skin: 1;
5
+ }
6
+
2
7
  [data-scope='countdown'][data-part='root'] {
3
8
  display: inline-block;
4
9
  color: var(--xh-countdown-fg, var(--xh-fg-default));
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='date-field'] {
4
+ --xh-date-field-skin: 1;
5
+ }
6
+
2
7
  /* 与文本框、数字框同一套堆叠:标签一行、分段框一行 */
3
8
  [data-scope='date-field'][data-part='root'] {
4
9
  /* 尺寸档只换这四个私有槽,标题与分段框都从 root 继承它们,换档不必给每个部件各写一条选择器 */
@@ -8,8 +13,8 @@
8
13
  --xh-_date-field-label-font-size: var(--xh-text-label-size);
9
14
  /* 形态档只换这三个私有槽:底色与两档描边 */
10
15
  --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);
16
+ --xh-_date-field-control-border: var(--xh-border-control);
17
+ --xh-_date-field-control-border-hover: var(--xh-border-control-hover);
13
18
  /* 聚焦环与段位反白随语气走。没写 data-tone 时这几个 --xh-_tone* 没被声明,退回原来的焦点色与品牌色 */
14
19
  --xh-_date-field-ring: var(--xh-ring-focus);
15
20
  --xh-_date-field-segment-bg: var(--xh-_tone, var(--xh-bg-brand));
@@ -39,15 +44,15 @@
39
44
  /* 描边式:与缺省等价,写出来是为了三档形态在皮肤里都看得见 */
40
45
  [data-scope='date-field'][data-part='root'][data-variant='outline'] {
41
46
  --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);
47
+ --xh-_date-field-control-border: var(--xh-border-control);
48
+ --xh-_date-field-control-border-hover: var(--xh-border-control-hover);
44
49
  }
45
50
 
46
51
  /* 填充式:底色顶替描边,悬停时才浮出一道淡边 */
47
52
  [data-scope='date-field'][data-part='root'][data-variant='subtle'] {
48
53
  --xh-_date-field-control-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
49
54
  --xh-_date-field-control-border: transparent;
50
- --xh-_date-field-control-border-hover: var(--xh-_tone-border, var(--xh-border-default));
55
+ --xh-_date-field-control-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
51
56
  }
52
57
 
53
58
  /* 无框式:底与边都让开,可见反馈只剩聚焦那一档 */
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='date-picker'] {
4
+ --xh-date-picker-skin: 1;
5
+ }
6
+
2
7
  /* 与文本框、分段日期同一套堆叠:标签一行、输入行一行 */
3
8
  [data-scope='date-picker'][data-part='root'] {
4
9
  display: inline-flex;
@@ -39,7 +44,7 @@
39
44
  block-size: var(--xh-date-picker-control-h, var(--xh-control-h-md));
40
45
  padding-inline: var(--xh-date-picker-control-px, var(--xh-control-px-md));
41
46
  padding-block: 0;
42
- border: var(--xh-stroke-thin) solid var(--xh-date-picker-control-border, var(--xh-border-default));
47
+ border: var(--xh-stroke-thin) solid var(--xh-date-picker-control-border, var(--xh-border-control));
43
48
  border-radius: var(--xh-date-picker-control-radius, var(--xh-shape-control));
44
49
  background: var(--xh-date-picker-control-bg, var(--xh-bg-canvas));
45
50
  color: var(--xh-date-picker-control-fg, var(--xh-fg-default));
@@ -52,7 +57,7 @@
52
57
 
53
58
  /* hover 只在中性态换描边:invalid 正拿边框颜色说事,被 hover 盖掉那条信息就没了 */
54
59
  [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));
60
+ border-color: var(--xh-date-picker-control-border-hover, var(--xh-border-control-hover));
56
61
  }
57
62
 
58
63
  /* 焦点落在某一段或某个按钮上,而边框长在 control 上:焦点环得由内向外借一层。
@@ -213,6 +218,70 @@
213
218
  [data-scope='date-picker'][data-part='calendar'][hidden] {
214
219
  display: none;
215
220
  }
221
+
222
+ /* —— showTime:时间列贴在日历右侧、逐列滚动,确认按钮收口 ——
223
+ content 是 block,作者把日历与时间列包进一行排布;列自身只管滚与选项画法 */
224
+ [data-scope='date-picker'][data-part='time-column'] {
225
+ display: flex;
226
+ flex-direction: column;
227
+ gap: 2px;
228
+ max-block-size: var(--xh-date-picker-time-column-h, 14rem);
229
+ padding: var(--xh-space-1);
230
+ overflow-y: auto;
231
+ border-inline-start: var(--xh-stroke-thin) solid var(--xh-date-picker-content-border, var(--xh-border-default));
232
+ }
233
+
234
+ [data-scope='date-picker'][data-part='time-column'][hidden] {
235
+ display: none;
236
+ }
237
+
238
+ [data-scope='date-picker'][data-part='time-item'] {
239
+ padding-block: var(--xh-space-1);
240
+ padding-inline: var(--xh-space-3);
241
+ border-radius: var(--xh-shape-control);
242
+ font-size: var(--xh-font-size-sm);
243
+ line-height: var(--xh-leading-normal);
244
+ text-align: center;
245
+ cursor: pointer;
246
+ user-select: none;
247
+ }
248
+
249
+ [data-scope='date-picker'][data-part='time-item']:hover {
250
+ background: var(--xh-date-picker-time-item-bg-hover, var(--xh-bg-subtle));
251
+ }
252
+
253
+ [data-scope='date-picker'][data-part='time-item'][data-selected] {
254
+ background: var(--xh-date-picker-time-item-bg-selected, var(--xh-bg-brand-subtle));
255
+ color: var(--xh-date-picker-time-item-fg-selected, var(--xh-fg-brand));
256
+ font-weight: var(--xh-font-weight-medium);
257
+ }
258
+
259
+ [data-scope='date-picker'][data-part='confirm-trigger'] {
260
+ display: inline-flex;
261
+ align-items: center;
262
+ justify-content: center;
263
+ block-size: var(--xh-control-h-sm);
264
+ padding-inline: var(--xh-control-px-sm);
265
+ border: none;
266
+ border-radius: var(--xh-shape-control);
267
+ background: var(--xh-date-picker-confirm-bg, var(--xh-bg-brand));
268
+ color: var(--xh-date-picker-confirm-fg, var(--xh-fg-on-brand));
269
+ font-size: var(--xh-font-size-sm);
270
+ cursor: pointer;
271
+ }
272
+
273
+ [data-scope='date-picker'][data-part='confirm-trigger']:hover {
274
+ background: var(--xh-date-picker-confirm-bg-hover, var(--xh-bg-brand-hover));
275
+ }
276
+
277
+ [data-scope='date-picker'][data-part='confirm-trigger']:focus-visible {
278
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
279
+ outline-offset: var(--xh-ring-offset);
280
+ }
281
+
282
+ [data-scope='date-picker'][data-part='confirm-trigger'][hidden] {
283
+ display: none;
284
+ }
216
285
  }
217
286
 
218
287
  @layer xihan.motion {
@@ -220,7 +289,7 @@
220
289
  @keyframes xh-date-picker-in {
221
290
  from {
222
291
  opacity: 0;
223
- translate: 0 -4px;
292
+ translate: 0 calc(-1 * var(--xh-motion-distance-sm));
224
293
  }
225
294
 
226
295
  to {
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='descriptions'] {
4
+ --xh-descriptions-skin: 1;
5
+ }
6
+
2
7
  [data-scope='descriptions'][data-part='root'] {
3
8
  /* 一个尺寸档只换这几个私有槽,每一格与格内两段都从根上继承它们,
4
9
  换档不必给每个部件各写一条选择器 */
package/css/dialog.css CHANGED
@@ -1,4 +1,63 @@
1
+ @layer xihan.motion {
2
+ /* 动画名的查找只认「文档里有没有这个名字」:引用别处文件里的名字,那份文件不一定在场,
3
+ 动画会静默地整个不跑。每份皮肤都能单独引入,所以它用到的关键帧都写在自己身上。 */
4
+ @keyframes xh-fade-in {
5
+ from {
6
+ opacity: 0;
7
+ }
8
+
9
+ to {
10
+ opacity: 1;
11
+ }
12
+ }
13
+
14
+ @keyframes xh-fade-out {
15
+ from {
16
+ opacity: 1;
17
+ }
18
+
19
+ to {
20
+ opacity: 0;
21
+ }
22
+ }
23
+
24
+ /* 幅度走令牌,减弱动效时由令牌层归零;位移与缩放用独立属性而非 transform 简写,
25
+ 两者互不覆盖,手势期间写 translate 也不会把 scale 抹掉 */
26
+ @keyframes xh-dialog-in {
27
+ from {
28
+ opacity: 0;
29
+ translate: 0 var(--xh-motion-distance-md);
30
+ scale: var(--xh-motion-scale-enter);
31
+ }
32
+
33
+ to {
34
+ opacity: 1;
35
+ translate: 0 0;
36
+ scale: 1;
37
+ }
38
+ }
39
+
40
+ @keyframes xh-dialog-out {
41
+ from {
42
+ opacity: 1;
43
+ translate: 0 0;
44
+ scale: 1;
45
+ }
46
+
47
+ to {
48
+ opacity: 0;
49
+ translate: 0 var(--xh-motion-distance-md);
50
+ scale: var(--xh-motion-scale-enter);
51
+ }
52
+ }
53
+ }
54
+
1
55
  @layer xihan.components {
56
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
57
+ [data-scope='dialog'] {
58
+ --xh-dialog-skin: 1;
59
+ }
60
+
2
61
  [data-scope='dialog'][data-part='backdrop'] {
3
62
  position: fixed;
4
63
  inset: 0;
@@ -10,8 +69,9 @@
10
69
  animation: xh-fade-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
11
70
  }
12
71
 
72
+ /* 退场补 forwards:动画播完到宿主真正卸载之间还有一帧,不定住终态遮罩会闪回全黑 */
13
73
  [data-scope='dialog'][data-part='backdrop'][data-state='closed'] {
14
- animation: xh-fade-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit);
74
+ animation: xh-fade-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
15
75
  }
16
76
 
17
77
  [data-scope='dialog'][data-part='positioner'] {
@@ -48,12 +108,13 @@
48
108
  box-shadow: var(--xh-dialog-shadow, var(--xh-elevation-3));
49
109
  }
50
110
 
51
- /* 收起态:connect 会给 content hidden,但上面那条 display 会盖掉 UA
52
- [hidden]{display:none}。meta positioner 并非必需部件,按最小合规结构
53
- (只有 trigger + content)写时,没有这一条收起的对话框就一直摊在页面上。 */
54
- [data-scope='dialog'][data-part='content'][hidden] {
55
- display: none;
56
- }
111
+ /* 这里刻意没有 [data-part='content'][hidden] { display: none },与 drawer 一致:
112
+ 收起态连接层会给 content 打 hidden,而上面那条 display: flex 正是要盖掉 UA 的
113
+ [hidden]{display:none}——退场动画得先能播。presence 读的是 content 的 animationName,
114
+ 读到才申领租约、等 animationend 才卸载;一旦 display: none,元素不生成盒子、
115
+ 动画根本不启动,探测器直接放弃租约,退场动画一帧都播不出来。
116
+ 真正的收起由适配器兜住:Vue 卸载整棵,WC 写内联 style.display。
117
+ 其余声明了 display 的角色节点仍补 [hidden],那几处没有退场动画。 */
57
118
 
58
119
  /* —— 尺寸 —— */
59
120
  /* 只有最大宽度分档;中间档不写规则,缺省即中档。
@@ -70,13 +131,12 @@
70
131
  outline: none;
71
132
  }
72
133
 
73
- /* 入场用 @starting-style 只驱动动画名切换;退场由 presence 保留 DOM 后播放 */
74
134
  [data-scope='dialog'][data-part='content'][data-state='open'] {
75
135
  animation: xh-dialog-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
76
136
  }
77
137
 
78
138
  [data-scope='dialog'][data-part='content'][data-state='closed'] {
79
- animation: xh-dialog-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit);
139
+ animation: xh-dialog-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
80
140
  }
81
141
 
82
142
  [data-scope='dialog'][data-part='title'] {