@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/menubar.css CHANGED
@@ -1,18 +1,23 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='menubar'] {
4
+ --xh-menubar-skin: 1;
5
+ }
6
+
2
7
  /* 一排 trigger。主轴跟着 data-orientation 走:菜单栏本身可以横排也可以竖排 */
3
8
  [data-scope='menubar'][data-part='root'] {
4
9
  /* 尺寸档只换这五个私有槽,trigger 与菜单里的条目都从 root 继承它们,
5
10
  换档不必给每个部件各写一条选择器。菜单浮层在 positioner 里、也是 root 的后代,一并继承 */
6
11
  --xh-_menubar-trigger-py: var(--xh-space-1);
7
12
  --xh-_menubar-trigger-px: var(--xh-control-px-sm);
8
- --xh-_menubar-item-py: var(--xh-space-2);
9
- --xh-_menubar-item-px: var(--xh-control-px-sm);
13
+ --xh-_menubar-item-py: var(--xh-space-1_5);
14
+ --xh-_menubar-item-px: var(--xh-control-px-md);
10
15
  --xh-_menubar-font-size: var(--xh-text-body-size);
11
16
 
12
- /* 高亮底随语气走:展开着的那张菜单的 trigger 与菜单里的高亮条目共用这一档。
13
- 没写 data-tone 时 --xh-_tone-subtle-hover 没被声明,退回原来的中性底。
17
+ /* 展开着的那张菜单的 trigger 走品牌淡底强档,随语气换色相;没写 data-tone 时退回品牌淡底。
18
+ 悬停与键盘焦点恒中性灰轻档、不接语气槽——两档同色相的话哪张开着就认不出来了。
14
19
  聚焦环不在其列,它恒是 --xh-ring-focus */
15
- --xh-_menubar-highlight-bg: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover));
20
+ --xh-_menubar-active-bg: var(--xh-_tone-subtle, var(--xh-bg-brand-subtle));
16
21
 
17
22
  display: flex;
18
23
  align-items: center;
@@ -34,21 +39,21 @@
34
39
  }
35
40
 
36
41
  /* —— 尺寸 —— */
37
- /* 缺省档写在上面的基础规则里。内边距的缺省档已经落在控件内边距尺的 sm 那一格,
38
- 往下没有更小的一格,sm 档改取空间尺;lg 档回到控件尺的下一格 */
42
+ /* 缺省档写在上面的基础规则里。trigger 的行内内缩缺省落在控件尺 sm 档、再往下取空间尺;
43
+ 条目纵向走 space 档、行内内缩走 control-px 档(随密度轴),换档各挪一级 */
39
44
  [data-scope='menubar'][data-part='root'][data-size='sm'] {
40
45
  --xh-_menubar-trigger-py: var(--xh-space-0_5);
41
46
  --xh-_menubar-trigger-px: var(--xh-space-1_5);
42
- --xh-_menubar-item-py: var(--xh-space-1_5);
43
- --xh-_menubar-item-px: var(--xh-space-1_5);
47
+ --xh-_menubar-item-py: var(--xh-space-1);
48
+ --xh-_menubar-item-px: var(--xh-control-px-sm);
44
49
  --xh-_menubar-font-size: var(--xh-font-size-sm);
45
50
  }
46
51
 
47
52
  [data-scope='menubar'][data-part='root'][data-size='lg'] {
48
53
  --xh-_menubar-trigger-py: var(--xh-space-2);
49
54
  --xh-_menubar-trigger-px: var(--xh-control-px-md);
50
- --xh-_menubar-item-py: var(--xh-space-3);
51
- --xh-_menubar-item-px: var(--xh-control-px-md);
55
+ --xh-_menubar-item-py: var(--xh-space-2);
56
+ --xh-_menubar-item-px: var(--xh-control-px-lg);
52
57
  --xh-_menubar-font-size: var(--xh-font-size-lg);
53
58
  }
54
59
 
@@ -78,15 +83,23 @@
78
83
  white-space: nowrap;
79
84
  cursor: pointer;
80
85
  user-select: none;
86
+ transition: background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
81
87
  }
82
88
 
83
89
  [data-scope='menubar'][data-part='trigger'][hidden] {
84
90
  display: none;
85
91
  }
86
92
 
87
- /* 展开着的那一项与指针悬停共用一套底色:菜单栏里"哪一张开着"必须一眼看得出来 */
88
- [data-scope='menubar'][data-part='trigger']:not([data-disabled]):is(:hover, [data-state='open']) {
89
- background: var(--xh-menubar-trigger-bg-active, var(--xh-_menubar-highlight-bg));
93
+ /* 指针悬停走中性灰轻档 */
94
+ [data-scope='menubar'][data-part='trigger']:not([data-disabled]):hover {
95
+ background: var(--xh-menubar-trigger-bg-hover, var(--xh-bg-subtle));
96
+ }
97
+
98
+ /* 展开着的那一张是打开路径,走品牌淡底强档:菜单栏里"哪一张开着"必须一眼看得出来。
99
+ 不加粗:横排的栏加粗会推挤相邻触发器。
100
+ 必须排在轻档之后:指针回到它身上时同特指度靠书写顺序压过轻档 */
101
+ [data-scope='menubar'][data-part='trigger']:not([data-disabled])[data-state='open'] {
102
+ background: var(--xh-menubar-trigger-bg-active, var(--xh-_menubar-active-bg));
90
103
  }
91
104
 
92
105
  /* trigger 自带 Tab 位(整条菜单栏唯一的那个),必须画得出焦点 */
@@ -147,13 +160,6 @@
147
160
  animation: xh-menubar-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
148
161
  }
149
162
 
150
- /* 关掉动效的用户不该被涨开动画晃一下;这里只管本组件,公共那份不动 */
151
- @media (prefers-reduced-motion: reduce) {
152
- [data-scope='menubar'][data-part='content'][data-state='open'] {
153
- animation-duration: 0.01ms;
154
- }
155
- }
156
-
157
163
  [data-scope='menubar'][data-part='group'] {
158
164
  display: flex;
159
165
  flex-direction: column;
@@ -189,10 +195,13 @@
189
195
  border-radius: var(--xh-menubar-item-radius, var(--xh-shape-control));
190
196
  color: var(--xh-menubar-item-fg, var(--xh-fg-default));
191
197
  font-size: var(--xh-menubar-item-font-size, var(--xh-_menubar-font-size));
192
- line-height: var(--xh-leading-none);
198
+ line-height: var(--xh-menubar-item-leading, var(--xh-leading-normal));
193
199
  white-space: nowrap;
194
200
  cursor: pointer;
195
201
  user-select: none;
202
+ transition:
203
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
204
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
196
205
  }
197
206
 
198
207
  /* 条目可以按需收起(按上下文过滤掉的命令),有 display 就得补这条 */
@@ -200,11 +209,11 @@
200
209
  display: none;
201
210
  }
202
211
 
203
- /* 键盘高亮与指针高亮共用一套底色:条目本身承载焦点,不另画描边。
212
+ /* 悬停与键盘焦点共用中性灰轻档:条目本身承载焦点,不另画描边。
204
213
  data-highlighted 跟的是连接层记的焦点条目,与 :focus 说的是同一件事,
205
214
  子部件够不着那个伪类,只能读这个标记 */
206
215
  [data-scope='menubar'][data-part='item']:not([data-disabled]):is(:hover, :focus, [data-highlighted]) {
207
- background: var(--xh-menubar-item-bg-highlight, var(--xh-_menubar-highlight-bg));
216
+ background: var(--xh-menubar-item-bg-hover, var(--xh-bg-subtle));
208
217
  }
209
218
 
210
219
  [data-scope='menubar'][data-part='item']:focus {
@@ -260,7 +269,7 @@
260
269
  @keyframes xh-menubar-in {
261
270
  from {
262
271
  opacity: 0;
263
- scale: 0.96;
272
+ scale: var(--xh-motion-scale-enter);
264
273
  }
265
274
 
266
275
  to {
@@ -3,7 +3,7 @@
3
3
  @keyframes xh-navigation-menu-in {
4
4
  from {
5
5
  opacity: 0;
6
- translate: 0 -4px;
6
+ translate: 0 calc(-1 * var(--xh-motion-distance-sm));
7
7
  }
8
8
 
9
9
  to {
@@ -14,6 +14,11 @@
14
14
  }
15
15
 
16
16
  @layer xihan.components {
17
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
18
+ [data-scope='navigation-menu'] {
19
+ --xh-navigation-menu-skin: 1;
20
+ }
21
+
17
22
  /* 根节点是 nav 地标,不参与排版;给 relative 是为了让共享外壳(viewport)有个定位参照系。
18
23
  这里不给 display,UA 的 [hidden]{display:none} 就仍然压得住 */
19
24
  [data-scope='navigation-menu'][data-part='root'] {
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='number-animation'] {
4
+ --xh-number-animation-skin: 1;
5
+ }
6
+
2
7
  [data-scope='number-animation'][data-part='root'] {
3
8
  /* 不写尺寸就跟着上下文的字号走:这个数常常摆在别人的标题、卡片标题或统计数值槽里,
4
9
  自带一个字号只会跟摆放它的地方打架。三个档位各自换掉这个私有槽 */
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='number-field'] {
4
+ --xh-number-field-skin: 1;
5
+ }
6
+
2
7
  /* 与文本框同一套堆叠:标签一行、输入行一行。
3
8
  加减钮与输入框中间那层包裹归作者,这里不碰,三者各自把高度对齐即可成一体 */
4
9
  [data-scope='number-field'][data-part='root'] {
@@ -12,18 +17,18 @@
12
17
 
13
18
  /* 形态档只换这几个私有槽:输入框的底与描边,加减钮的底与描边 */
14
19
  --xh-_number-field-bg: var(--xh-bg-canvas);
15
- --xh-_number-field-border: var(--xh-border-default);
16
- --xh-_number-field-border-hover: var(--xh-border-strong);
20
+ --xh-_number-field-border: var(--xh-border-control);
21
+ --xh-_number-field-border-hover: var(--xh-border-control-hover);
17
22
  --xh-_number-field-trigger-bg: var(--xh-bg-subtle);
18
23
  --xh-_number-field-trigger-bg-hover: var(--xh-bg-subtle-hover);
19
24
  --xh-_number-field-trigger-bg-active: var(--xh-bg-subtle-active);
20
- --xh-_number-field-trigger-border: var(--xh-border-default);
21
- --xh-_number-field-trigger-border-hover: var(--xh-border-strong);
25
+ --xh-_number-field-trigger-border: var(--xh-border-control);
26
+ --xh-_number-field-trigger-border-hover: var(--xh-border-control-hover);
22
27
 
23
28
  /* 语气只落在聚焦这一处:环与聚焦态描边。框里的数字不归它管。
24
29
  没写 data-tone 时 --xh-_tone 没被声明,退回原来的聚焦环色与常态描边 */
25
30
  --xh-_number-field-ring: var(--xh-ring-focus);
26
- --xh-_number-field-border-focus: var(--xh-number-field-input-border, var(--xh-_tone, var(--xh-border-default)));
31
+ --xh-_number-field-border-focus: var(--xh-number-field-input-border, var(--xh-_tone, var(--xh-border-control)));
27
32
 
28
33
  display: inline-flex;
29
34
  flex-direction: column;
@@ -58,14 +63,14 @@
58
63
  输入框没有实心档:填满一个要往里打字的框,字与底就没法同时读 */
59
64
  [data-scope='number-field'][data-part='root'][data-variant='outline'] {
60
65
  --xh-_number-field-bg: var(--xh-bg-canvas);
61
- --xh-_number-field-border: var(--xh-border-default);
62
- --xh-_number-field-border-hover: var(--xh-border-strong);
66
+ --xh-_number-field-border: var(--xh-border-control);
67
+ --xh-_number-field-border-hover: var(--xh-border-control-hover);
63
68
  --xh-_number-field-trigger-bg: var(--xh-bg-subtle);
64
69
  --xh-_number-field-trigger-bg-hover: var(--xh-bg-subtle-hover);
65
70
  --xh-_number-field-trigger-bg-active: var(--xh-bg-subtle-active);
66
- --xh-_number-field-trigger-border: var(--xh-border-default);
67
- --xh-_number-field-trigger-border-hover: var(--xh-border-strong);
68
- --xh-_number-field-border-focus: var(--xh-number-field-input-border, var(--xh-_tone, var(--xh-border-default)));
71
+ --xh-_number-field-trigger-border: var(--xh-border-control);
72
+ --xh-_number-field-trigger-border-hover: var(--xh-border-control-hover);
73
+ --xh-_number-field-border-focus: var(--xh-number-field-input-border, var(--xh-_tone, var(--xh-border-control)));
69
74
  }
70
75
 
71
76
  /* 填充式:底色顶替描边表达边界。描边只退成透明而不去掉,1px 的位置留着,
@@ -73,26 +78,26 @@
73
78
  [data-scope='number-field'][data-part='root'][data-variant='subtle'] {
74
79
  --xh-_number-field-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
75
80
  --xh-_number-field-border: transparent;
76
- --xh-_number-field-border-hover: var(--xh-_tone-border, var(--xh-border-default));
81
+ --xh-_number-field-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
77
82
  --xh-_number-field-trigger-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
78
83
  --xh-_number-field-trigger-bg-hover: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover));
79
84
  --xh-_number-field-trigger-bg-active: var(--xh-_tone-subtle-active, var(--xh-bg-subtle-active));
80
85
  --xh-_number-field-trigger-border: transparent;
81
- --xh-_number-field-trigger-border-hover: var(--xh-_tone-border, var(--xh-border-default));
82
- --xh-_number-field-border-focus: var(--xh-_tone, var(--xh-border-default));
86
+ --xh-_number-field-trigger-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
87
+ --xh-_number-field-border-focus: var(--xh-_tone, var(--xh-border-control));
83
88
  }
84
89
 
85
90
  /* 无框:底与描边都透明,平时只看得见数字,悬停与聚焦才浮出边界 */
86
91
  [data-scope='number-field'][data-part='root'][data-variant='ghost'] {
87
92
  --xh-_number-field-bg: transparent;
88
93
  --xh-_number-field-border: transparent;
89
- --xh-_number-field-border-hover: var(--xh-_tone-border, var(--xh-border-default));
94
+ --xh-_number-field-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
90
95
  --xh-_number-field-trigger-bg: transparent;
91
96
  --xh-_number-field-trigger-bg-hover: var(--xh-_tone-subtle, var(--xh-bg-subtle-hover));
92
97
  --xh-_number-field-trigger-bg-active: var(--xh-_tone-subtle-active, var(--xh-bg-subtle-active));
93
98
  --xh-_number-field-trigger-border: transparent;
94
- --xh-_number-field-trigger-border-hover: var(--xh-_tone-border, var(--xh-border-default));
95
- --xh-_number-field-border-focus: var(--xh-_tone, var(--xh-border-default));
99
+ --xh-_number-field-trigger-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
100
+ --xh-_number-field-border-focus: var(--xh-_tone, var(--xh-border-control));
96
101
  }
97
102
 
98
103
  [data-scope='number-field'][data-part='label'] {
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='page-header'] {
4
+ --xh-page-header-skin: 1;
5
+ }
6
+
2
7
  [data-scope='page-header'][data-part='root'] {
3
8
  /* 尺寸档只换这三个私有槽,各段从根上继承它们,换档不必给每个部件各写一条选择器 */
4
9
  --xh-_page-header-py: var(--xh-space-4);
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='pagination'] {
4
+ --xh-pagination-skin: 1;
5
+ }
6
+
2
7
  /* 根节点是 nav 地标,只负责把上一页 / 页码 / 下一页排成一行。
3
8
  页数为 0 时连接层会打 data-empty,这里不拿它做隐藏:作者常把「第 x-y 条」这类
4
9
  区间回显也写在 nav 里,整块藏掉会连那行字一起吞了,是否隐藏交给作者决定 */
package/css/pin-input.css CHANGED
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='pin-input'] {
4
+ --xh-pin-input-skin: 1;
5
+ }
6
+
2
7
  /* 与文本框、数字框同一套堆叠:标签一行、格子一行 */
3
8
  [data-scope='pin-input'][data-part='root'] {
4
9
  /* 尺寸档只换这四个私有槽,标题与格子都从 root 继承它们,换档不必给每个部件各写一条选择器 */
@@ -8,8 +13,8 @@
8
13
  --xh-_pin-input-label-font-size: var(--xh-text-label-size);
9
14
  /* 形态档只换这三个私有槽,缺省这一份就是描边式(描边 + 画布底) */
10
15
  --xh-_pin-input-box-bg: var(--xh-bg-canvas);
11
- --xh-_pin-input-box-border: var(--xh-border-default);
12
- --xh-_pin-input-box-border-hover: var(--xh-border-strong);
16
+ --xh-_pin-input-box-border: var(--xh-border-control);
17
+ --xh-_pin-input-box-border-hover: var(--xh-border-control-hover);
13
18
  /* 聚焦环与填满态的强调色随语气走。没写 data-tone 时 --xh-_tone 没被声明,退回原来的取值 */
14
19
  --xh-_pin-input-ring: var(--xh-ring-focus);
15
20
  --xh-_pin-input-accent: var(--xh-_tone, var(--xh-bg-brand));
@@ -29,22 +34,22 @@
29
34
  不做 solid:实心填充盖住了"这里能打字"这件事,输入框用不上它 */
30
35
  [data-scope='pin-input'][data-part='root'][data-variant='outline'] {
31
36
  --xh-_pin-input-box-bg: var(--xh-bg-canvas);
32
- --xh-_pin-input-box-border: var(--xh-border-default);
33
- --xh-_pin-input-box-border-hover: var(--xh-border-strong);
37
+ --xh-_pin-input-box-border: var(--xh-border-control);
38
+ --xh-_pin-input-box-border-hover: var(--xh-border-control-hover);
34
39
  }
35
40
 
36
41
  /* 填充式:底色说明这里是一格输入位,描边让位给底色 */
37
42
  [data-scope='pin-input'][data-part='root'][data-variant='subtle'] {
38
43
  --xh-_pin-input-box-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
39
44
  --xh-_pin-input-box-border: transparent;
40
- --xh-_pin-input-box-border-hover: var(--xh-_tone-border, var(--xh-border-default));
45
+ --xh-_pin-input-box-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
41
46
  }
42
47
 
43
48
  /* 无边框、透明底:静息时格子完全隐入背景,可见反馈全靠悬停描边与聚焦环 */
44
49
  [data-scope='pin-input'][data-part='root'][data-variant='ghost'] {
45
50
  --xh-_pin-input-box-bg: transparent;
46
51
  --xh-_pin-input-box-border: transparent;
47
- --xh-_pin-input-box-border-hover: var(--xh-_tone-border, var(--xh-border-default));
52
+ --xh-_pin-input-box-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
48
53
  }
49
54
 
50
55
  /* —— 尺寸 —— */
@@ -1,4 +1,32 @@
1
+ @layer xihan.motion {
2
+ /* 转圈的关键帧定义在本文件里:引用别处文件的动画名时那份文件不一定在场,动画会静默不跑 */
3
+ @keyframes xh-popconfirm-rotate {
4
+ to {
5
+ transform: rotate(1turn);
6
+ }
7
+ }
8
+ }
9
+
10
+ @layer xihan.motion {
11
+ /* 动画名的查找只认「文档里有没有这个名字」:引用别处文件里的名字,那份文件不一定在场,
12
+ 动画会静默地整个不跑。每份皮肤都能单独引入,所以它用到的关键帧都写在自己身上。 */
13
+ @keyframes xh-fade-in {
14
+ from {
15
+ opacity: 0;
16
+ }
17
+
18
+ to {
19
+ opacity: 1;
20
+ }
21
+ }
22
+ }
23
+
1
24
  @layer xihan.components {
25
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
26
+ [data-scope='popconfirm'] {
27
+ --xh-popconfirm-skin: 1;
28
+ }
29
+
2
30
  /* 根只框住触发器:浮层树靠 positioner 的固定定位飞出去,不占根的盒子 */
3
31
  [data-scope='popconfirm'][data-part='root'] {
4
32
  display: inline-block;
@@ -120,6 +148,22 @@
120
148
  color: var(--xh-popconfirm-confirm-fg, var(--xh-_tone-on, var(--xh-fg-on-brand)));
121
149
  }
122
150
 
151
+ /* 异步确认挂起:按钮转圈、指针挂起;不是禁用,焦点与对比度都保持 */
152
+ [data-scope='popconfirm'][data-part='confirm-trigger'][data-loading] {
153
+ cursor: wait;
154
+ }
155
+
156
+ [data-scope='popconfirm'][data-part='confirm-trigger'][data-loading]::before {
157
+ content: '';
158
+ inline-size: 1em;
159
+ block-size: 1em;
160
+ margin-inline-end: var(--xh-space-1);
161
+ border: var(--xh-stroke-thin) solid transparent;
162
+ border-block-start-color: currentcolor;
163
+ border-radius: var(--xh-shape-pill);
164
+ animation: xh-popconfirm-rotate var(--xh-popconfirm-loading-duration, calc(var(--xh-duration-slow) * 2)) linear infinite;
165
+ }
166
+
123
167
  [data-scope='popconfirm'][data-part='confirm-trigger']:hover {
124
168
  background: var(--xh-_tone-hover, var(--xh-bg-brand-hover));
125
169
  }
package/css/popover.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='popover'] {
18
+ --xh-popover-skin: 1;
19
+ }
20
+
2
21
  /* 坐标由定位引擎写进 style(inset-inline-start / inset-block-start),这里只定位模式与层级 */
3
22
  [data-scope='popover'][data-part='positioner'] {
4
23
  position: fixed;
@@ -110,40 +129,44 @@
110
129
  }
111
130
 
112
131
  /* 箭头是旋转 45° 的方块,只保留朝向锚点那两条边的描边;
113
- 引擎不产出箭头偏移,因此沿交叉轴固定居中 */
132
+ 交叉轴落点由定位引擎经内联自定义属性写进来,没给时退回居中 */
114
133
  [data-scope='popover'][data-part='arrow'] {
134
+ --xh-_popover-arrow-size: var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size));
135
+ --xh-_popover-arrow-x: 50%;
136
+ --xh-_popover-arrow-y: 50%;
137
+
115
138
  position: absolute;
116
- inline-size: var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size));
117
- block-size: var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size));
139
+ inline-size: var(--xh-_popover-arrow-size);
140
+ block-size: var(--xh-_popover-arrow-size);
118
141
  background: var(--xh-popover-bg, var(--xh-bg-surface));
119
142
  border: var(--xh-stroke-thin) solid var(--xh-popover-border, var(--xh-border-default));
120
143
  rotate: 45deg;
121
144
  }
122
145
 
123
146
  [data-scope='popover'][data-part='arrow'][data-placement^='bottom'] {
124
- inset-block-start: calc(var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size)) / -2);
125
- inset-inline-start: calc(50% - var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size)) / 2);
147
+ inset-block-start: calc(var(--xh-_popover-arrow-size) / -2);
148
+ inset-inline-start: calc(var(--xh-_popover-arrow-x) - var(--xh-_popover-arrow-size) / 2);
126
149
  border-block-end: 0;
127
150
  border-inline-end: 0;
128
151
  }
129
152
 
130
153
  [data-scope='popover'][data-part='arrow'][data-placement^='top'] {
131
- inset-block-end: calc(var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size)) / -2);
132
- inset-inline-start: calc(50% - var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size)) / 2);
154
+ inset-block-end: calc(var(--xh-_popover-arrow-size) / -2);
155
+ inset-inline-start: calc(var(--xh-_popover-arrow-x) - var(--xh-_popover-arrow-size) / 2);
133
156
  border-block-start: 0;
134
157
  border-inline-start: 0;
135
158
  }
136
159
 
137
160
  [data-scope='popover'][data-part='arrow'][data-placement^='right'] {
138
- inset-inline-start: calc(var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size)) / -2);
139
- inset-block-start: calc(50% - var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size)) / 2);
161
+ inset-inline-start: calc(var(--xh-_popover-arrow-size) / -2);
162
+ inset-block-start: calc(var(--xh-_popover-arrow-y) - var(--xh-_popover-arrow-size) / 2);
140
163
  border-block-start: 0;
141
164
  border-inline-end: 0;
142
165
  }
143
166
 
144
167
  [data-scope='popover'][data-part='arrow'][data-placement^='left'] {
145
- inset-inline-end: calc(var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size)) / -2);
146
- inset-block-start: calc(50% - var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size)) / 2);
168
+ inset-inline-end: calc(var(--xh-_popover-arrow-size) / -2);
169
+ inset-block-start: calc(var(--xh-_popover-arrow-y) - var(--xh-_popover-arrow-size) / 2);
147
170
  border-block-end: 0;
148
171
  border-inline-start: 0;
149
172
  }
package/css/popselect.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='popselect'] {
18
+ --xh-popselect-skin: 1;
19
+ }
20
+
2
21
  [data-scope='popselect'][data-part='root'] {
3
22
  /* 尺寸档只换这四个私有槽,触发器与条目都从 root 继承它们,换档不必给每个部件各写一条选择器。
4
23
  浮层在 positioner 里、也是 root 的后代,一并继承 */
@@ -9,14 +28,13 @@
9
28
 
10
29
  /* 形态档只换这三个槽:缺省即 outline(描边 + 画布底) */
11
30
  --xh-_popselect-trigger-bg: var(--xh-bg-canvas);
12
- --xh-_popselect-trigger-border: var(--xh-border-default);
13
- --xh-_popselect-trigger-border-hover: var(--xh-border-strong);
31
+ --xh-_popselect-trigger-border: var(--xh-border-control);
32
+ --xh-_popselect-trigger-border-hover: var(--xh-border-control-hover);
14
33
 
15
- /* 强调色随语气走,只落在聚焦与选中上,正文文字色不动。
16
- 没写 data-tone 时 --xh-_tone 没被声明,三处都退回原来的取值 */
34
+ /* 聚焦环恒用固定环色;强调色随语气走,只落在勾选标记与选中文字上,正文文字色不动。
35
+ 没写 data-tone 时 --xh-_tone 没被声明,两处都退回原来的取值 */
17
36
  --xh-_popselect-ring: var(--xh-ring-focus);
18
37
  --xh-_popselect-accent: var(--xh-_tone, var(--xh-bg-brand));
19
- --xh-_popselect-item-highlight-bg: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover));
20
38
  --xh-_popselect-item-selected-fg: var(--xh-_tone-fg, var(--xh-fg-brand-strong));
21
39
 
22
40
  display: inline-flex;
@@ -42,21 +60,21 @@
42
60
  /* 形态只决定描边与底色怎么用,用哪族颜色由 data-tone 决定 */
43
61
  [data-scope='popselect'][data-part='root'][data-variant='outline'] {
44
62
  --xh-_popselect-trigger-bg: var(--xh-bg-canvas);
45
- --xh-_popselect-trigger-border: var(--xh-border-default);
46
- --xh-_popselect-trigger-border-hover: var(--xh-border-strong);
63
+ --xh-_popselect-trigger-border: var(--xh-border-control);
64
+ --xh-_popselect-trigger-border-hover: var(--xh-border-control-hover);
47
65
  }
48
66
 
49
67
  /* 填充式:底色顶替描边,悬停时才浮出一道淡边 */
50
68
  [data-scope='popselect'][data-part='root'][data-variant='subtle'] {
51
69
  --xh-_popselect-trigger-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
52
70
  --xh-_popselect-trigger-border: transparent;
53
- --xh-_popselect-trigger-border-hover: var(--xh-_tone-border, var(--xh-border-default));
71
+ --xh-_popselect-trigger-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
54
72
  }
55
73
 
56
74
  [data-scope='popselect'][data-part='root'][data-variant='ghost'] {
57
75
  --xh-_popselect-trigger-bg: transparent;
58
76
  --xh-_popselect-trigger-border: transparent;
59
- --xh-_popselect-trigger-border-hover: var(--xh-_tone-border, var(--xh-border-default));
77
+ --xh-_popselect-trigger-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
60
78
  }
61
79
 
62
80
  [data-scope='popselect'][data-part='trigger'] {
@@ -130,8 +148,14 @@
130
148
  display: none;
131
149
  }
132
150
 
133
- /* 容器只是焦点域兜底落点,落焦不画环:高亮永远长在条目上 */
134
- [data-scope='popselect'][data-part='content']:focus-visible {
151
+ /* 焦点落在容器上只有一种情形:指针打开、无选中值、还没有任何条目当锚点。
152
+ 那一刻它认领着 Tab 位,环画在浮层内沿;有锚点时环长在条目上,容器不再画 */
153
+ [data-scope='popselect'][data-part='content'][tabindex='0']:focus-visible {
154
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
155
+ outline-offset: calc(-1 * var(--xh-ring-width));
156
+ }
157
+
158
+ [data-scope='popselect'][data-part='content']:not([tabindex='0']):focus-visible {
135
159
  outline: none;
136
160
  }
137
161
 
@@ -143,8 +167,8 @@
143
167
  display: flex;
144
168
  align-items: center;
145
169
  gap: var(--xh-popselect-item-gap, var(--xh-_popselect-gap));
146
- padding-block: var(--xh-popselect-item-py, var(--xh-space-1));
147
- padding-inline: var(--xh-popselect-item-px, var(--xh-control-px-sm));
170
+ padding-block: var(--xh-popselect-item-py, var(--xh-space-1_5));
171
+ padding-inline: var(--xh-popselect-item-px, var(--xh-control-px-md));
148
172
  border-radius: var(--xh-popselect-item-radius, var(--xh-shape-control));
149
173
  color: var(--xh-popselect-item-fg, var(--xh-fg-default));
150
174
  font-size: var(--xh-popselect-item-font-size, var(--xh-_popselect-font-size));
@@ -153,11 +177,14 @@
153
177
  white-space: nowrap;
154
178
  cursor: pointer;
155
179
  user-select: none;
180
+ transition:
181
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
182
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
156
183
  }
157
184
 
158
- /* 键盘高亮与指针高亮共用一套底色:条目本身承载焦点,不另画描边 */
185
+ /* 悬停与键盘锚点共用中性灰轻档:条目本身承载焦点,不另画描边 */
159
186
  [data-scope='popselect'][data-part='item']:not([data-disabled]):is(:hover, [data-highlighted]) {
160
- background: var(--xh-popselect-item-bg-highlight, var(--xh-_popselect-item-highlight-bg));
187
+ background: var(--xh-popselect-item-bg-hover, var(--xh-bg-subtle));
161
188
  }
162
189
 
163
190
  /* 选中只换文字色与字重,配合行尾的 ✓ 标记;底色整条通道留给高亮,两者因此能同时看见 */
@@ -166,7 +193,14 @@
166
193
  font-weight: var(--xh-popselect-item-font-weight-selected, var(--xh-font-weight-medium));
167
194
  }
168
195
 
169
- [data-scope='popselect'][data-part='item']:focus {
196
+ /* 键盘走到条目上要画环:轻档底色对白底只有 1.1:1,够不上焦点指示器的对比度要求。
197
+ 指针悬停走的是程序化聚焦,鼠标模态下 :focus-visible 不匹配,不会误画 */
198
+ [data-scope='popselect'][data-part='item']:focus-visible {
199
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
200
+ outline-offset: calc(-1 * var(--xh-ring-width));
201
+ }
202
+
203
+ [data-scope='popselect'][data-part='item']:focus:not(:focus-visible) {
170
204
  outline: none;
171
205
  }
172
206