@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/progress.css CHANGED
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='progress'] {
4
+ --xh-progress-skin: 1;
5
+ }
6
+
2
7
  /* 私有槽位:组件令牌 → 中档字面量两级回退。厚度写在 root 上而不是 track 上,
3
8
  尺寸档才改得动一处就够,track 靠继承拿到值。 */
4
9
  [data-scope='progress'][data-part='root'] {
@@ -26,7 +31,7 @@
26
31
  }
27
32
 
28
33
  /* —— 尺寸 —— */
29
- /* 只有厚度分档;中间档不写规则,缺省即中档 */
34
+ /* 线形只有厚度分档;中间档不写规则,缺省即中档 */
30
35
  [data-scope='progress'][data-part='root'][data-size='sm'] {
31
36
  --xh-_thickness: var(--xh-progress-thickness, var(--xh-space-1));
32
37
  }
@@ -34,4 +39,71 @@
34
39
  [data-scope='progress'][data-part='root'][data-size='lg'] {
35
40
  --xh-_thickness: var(--xh-progress-thickness, var(--xh-space-2));
36
41
  }
42
+
43
+ /* —— 环形 —— */
44
+ /* 环画在固定的 100×100 viewBox 里,直径由这里的尺寸决定;
45
+ 画面与环心文字叠在同一格上,文字因此恒在圆心 */
46
+ [data-scope='progress'][data-part='root'][data-variant='circle'],
47
+ [data-scope='progress'][data-part='root'][data-variant='dashboard'] {
48
+ --xh-_diameter: var(--xh-progress-size, 7.5rem);
49
+
50
+ display: inline-grid;
51
+ place-items: center;
52
+ inline-size: var(--xh-_diameter);
53
+ block-size: var(--xh-_diameter);
54
+ }
55
+
56
+ [data-scope='progress'][data-part='root'][data-variant='circle'][data-size='sm'],
57
+ [data-scope='progress'][data-part='root'][data-variant='dashboard'][data-size='sm'] {
58
+ --xh-_diameter: var(--xh-progress-size, 5rem);
59
+ }
60
+
61
+ [data-scope='progress'][data-part='root'][data-variant='circle'][data-size='lg'],
62
+ [data-scope='progress'][data-part='root'][data-variant='dashboard'][data-size='lg'] {
63
+ --xh-_diameter: var(--xh-progress-size, 10rem);
64
+ }
65
+
66
+ [data-scope='progress'][data-part='canvas'],
67
+ [data-scope='progress'][data-part='label'] {
68
+ grid-area: 1 / 1;
69
+ }
70
+
71
+ [data-scope='progress'][data-part='canvas'] {
72
+ inline-size: 100%;
73
+ block-size: 100%;
74
+ }
75
+
76
+ /* 环心文字:占位不撑格子,长内容自己换行由作者定 */
77
+ [data-scope='progress'][data-part='label'] {
78
+ color: var(--xh-progress-label-fg, var(--xh-fg-default));
79
+ font-size: var(--xh-progress-label-font-size, var(--xh-text-body-size));
80
+ line-height: var(--xh-leading-none);
81
+ text-align: center;
82
+ }
83
+
84
+ /* 圆心、半径与那两段虚线都由连接层算好写进内联样式,这里只上色。
85
+ 底槽不跟着语气换族:只有已完成的那段换色,才读得出"完成了多少" */
86
+ [data-scope='progress'][data-part='track'][data-variant='circle'],
87
+ [data-scope='progress'][data-part='track'][data-variant='dashboard'] {
88
+ stroke: var(--xh-progress-track, var(--xh-bg-subtle-active));
89
+ }
90
+
91
+ [data-scope='progress'][data-part='range'][data-variant='circle'],
92
+ [data-scope='progress'][data-part='range'][data-variant='dashboard'] {
93
+ stroke: var(--xh-progress-range, var(--xh-_tone, var(--xh-bg-brand)));
94
+ stroke-linecap: var(--xh-progress-linecap, round);
95
+ transition: stroke-dashoffset var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
96
+ }
97
+
98
+ /* 零进度:圆角端点会在长度为 0 处画出一个圆点,收掉它 */
99
+ [data-scope='progress'][data-part='range'][data-empty] {
100
+ visibility: hidden;
101
+ }
102
+
103
+ /* 关掉动效的用户不该被进度条的补间晃一下;这里只管本组件 */
104
+ @media (prefers-reduced-motion: reduce) {
105
+ [data-scope='progress'][data-part='range'] {
106
+ transition-duration: 0.01ms;
107
+ }
108
+ }
37
109
  }
package/css/qr-code.css CHANGED
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='qr-code'] {
4
+ --xh-qr-code-skin: 1;
5
+ }
6
+
2
7
  /*
3
8
  * 根 <svg>。深浅两色取固定档而不是语义前景/背景:语义色跟着主题走,深色主题下整张码
4
9
  * 会翻成浅底深模块的反相图,一部分读码器不认反相码。要换配色改下面这三个覆盖槽。
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='radio-group'] {
4
+ --xh-radio-group-skin: 1;
5
+ }
6
+
2
7
  [data-scope='radio-group'][data-part='root'] {
3
8
  /* 尺寸档只换这两个私有槽,标题与条目都从 root 继承它们,换档不必给每个部件各写一条选择器 */
4
9
  --xh-_radio-group-item-gap: var(--xh-control-gap-md);
@@ -69,7 +74,7 @@
69
74
  justify-content: center;
70
75
  inline-size: 16px;
71
76
  block-size: 16px;
72
- border: var(--xh-stroke-thin) solid var(--xh-radio-group-indicator-border, var(--xh-border-default));
77
+ border: var(--xh-stroke-thin) solid var(--xh-radio-group-indicator-border, var(--xh-border-control));
73
78
  border-radius: var(--xh-shape-pill);
74
79
  background: var(--xh-radio-group-indicator-bg, var(--xh-bg-canvas));
75
80
  transition: border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
package/css/rating.css CHANGED
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='rating'] {
4
+ --xh-rating-skin: 1;
5
+ }
6
+
2
7
  /* position 是给表单影子做定位基准的:那份输入被连接层内联写成绝对定位,
3
8
  根上不留基准它就会贴到更外层的定位祖先上,原生校验提示跟着飘到页面别处。 */
4
9
  [data-scope='rating'][data-part='root'] {
package/css/result.css CHANGED
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='result'] {
4
+ --xh-result-skin: 1;
5
+ }
6
+
2
7
  [data-scope='result'][data-part='root'] {
3
8
  /* 三个尺寸档只换这几个私有槽,图标与标题都从 root 继承它们,
4
9
  换档不必给每个部件各写一条选择器 */
@@ -11,15 +11,14 @@
11
11
  opacity: 1;
12
12
  }
13
13
  }
14
-
15
- @media (prefers-reduced-motion: reduce) {
16
- [data-scope='scroll-area'] [data-part] {
17
- animation-duration: 0.01ms !important;
18
- }
19
- }
20
14
  }
21
15
 
22
16
  @layer xihan.components {
17
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
18
+ [data-scope='scroll-area'] {
19
+ --xh-scroll-area-skin: 1;
20
+ }
21
+
23
22
  /* 定位分工:滑块在**主轴**上的起点与长度由连接层每帧写进内联样式——
24
23
  竖向是 inset-block-start / block-size,横向是 inset-inline-start / inline-size。
25
24
  这份皮肤一个都不碰:内联优先级高于样式表,写了不但是死声明,
package/css/select.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='select'] {
18
+ --xh-select-skin: 1;
19
+ }
20
+
2
21
  [data-scope='select'][data-part='root'] {
3
22
  /* 尺寸档只换这五个私有槽,触发器、标题与条目都从 root 继承它们,
4
23
  换档不必给每个部件各写一条选择器。浮层在 positioner 里、也是 root 的后代,一并继承 */
@@ -10,14 +29,13 @@
10
29
 
11
30
  /* 形态档只换这三个槽:缺省即 outline(描边 + 画布底) */
12
31
  --xh-_select-trigger-bg: var(--xh-bg-canvas);
13
- --xh-_select-trigger-border: var(--xh-border-default);
14
- --xh-_select-trigger-border-hover: var(--xh-border-strong);
32
+ --xh-_select-trigger-border: var(--xh-border-control);
33
+ --xh-_select-trigger-border-hover: var(--xh-border-control-hover);
15
34
 
16
- /* 强调色随语气走,只落在聚焦与选中上,正文文字色不动。
17
- 没写 data-tone 时 --xh-_tone 没被声明,三处都退回原来的取值 */
35
+ /* 聚焦环恒用固定环色;强调色随语气走,只落在勾选标记与选中文字上,正文文字色不动。
36
+ 没写 data-tone 时 --xh-_tone 没被声明,两处都退回原来的取值 */
18
37
  --xh-_select-ring: var(--xh-ring-focus);
19
38
  --xh-_select-accent: var(--xh-_tone, var(--xh-bg-brand));
20
- --xh-_select-item-highlight-bg: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover));
21
39
  --xh-_select-item-selected-fg: var(--xh-_tone-fg, var(--xh-fg-brand-strong));
22
40
 
23
41
  display: inline-flex;
@@ -50,21 +68,21 @@
50
68
  形态只决定描边与底色怎么用,用哪族颜色由 data-tone 决定 */
51
69
  [data-scope='select'][data-part='root'][data-variant='outline'] {
52
70
  --xh-_select-trigger-bg: var(--xh-bg-canvas);
53
- --xh-_select-trigger-border: var(--xh-border-default);
54
- --xh-_select-trigger-border-hover: var(--xh-border-strong);
71
+ --xh-_select-trigger-border: var(--xh-border-control);
72
+ --xh-_select-trigger-border-hover: var(--xh-border-control-hover);
55
73
  }
56
74
 
57
75
  /* 填充式:底色顶替描边,悬停时才浮出一道淡边 */
58
76
  [data-scope='select'][data-part='root'][data-variant='subtle'] {
59
77
  --xh-_select-trigger-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
60
78
  --xh-_select-trigger-border: transparent;
61
- --xh-_select-trigger-border-hover: var(--xh-_tone-border, var(--xh-border-default));
79
+ --xh-_select-trigger-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
62
80
  }
63
81
 
64
82
  [data-scope='select'][data-part='root'][data-variant='ghost'] {
65
83
  --xh-_select-trigger-bg: transparent;
66
84
  --xh-_select-trigger-border: transparent;
67
- --xh-_select-trigger-border-hover: var(--xh-_tone-border, var(--xh-border-default));
85
+ --xh-_select-trigger-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
68
86
  }
69
87
 
70
88
  [data-scope='select'][data-part='label'] {
@@ -98,10 +116,126 @@
98
116
  cursor: pointer;
99
117
  }
100
118
 
101
- [data-scope='select'][data-part='trigger']:hover:not(:disabled) {
119
+ /* hover 只在中性态换描边:invalid 正拿边框颜色说事,被 hover 盖掉那条信息就没了 */
120
+ [data-scope='select'][data-part='trigger']:hover:not(:disabled, [data-invalid]) {
102
121
  border-color: var(--xh-select-trigger-border-hover, var(--xh-_select-trigger-border-hover));
103
122
  }
104
123
 
124
+ [data-scope='select'][data-part='trigger'][data-invalid] {
125
+ border-color: var(--xh-select-trigger-border-invalid, var(--xh-color-danger-500));
126
+ }
127
+
128
+ /* 收纳容器:触发器与清空按钮的定位基准,触发器在里面占满一行 */
129
+ [data-scope='select'][data-part='control'] {
130
+ position: relative;
131
+ display: flex;
132
+ align-items: center;
133
+ }
134
+
135
+ [data-scope='select'][data-part='control'] > [data-part='trigger'] {
136
+ flex: 1;
137
+ }
138
+
139
+ /* 清空按钮:trigger 的兄弟节点,幽灵小圆钮;没选中或禁用时连接层给 hidden */
140
+ [data-scope='select'][data-part='clear-trigger'] {
141
+ display: inline-grid;
142
+ place-items: center;
143
+ inline-size: var(--xh-select-clear-size, var(--xh-control-indicator-size));
144
+ block-size: var(--xh-select-clear-size, var(--xh-control-indicator-size));
145
+ padding: 0;
146
+ border: none;
147
+ border-radius: var(--xh-shape-pill);
148
+ background: none;
149
+ color: var(--xh-select-clear-fg, var(--xh-fg-subtle));
150
+
151
+ /* 字号行高钉死:继承页面字号会让行框高过按钮,字形被基线顶离正中 */
152
+ font-size: var(--xh-select-clear-font-size, var(--xh-font-size-xs));
153
+ line-height: var(--xh-leading-none);
154
+ cursor: pointer;
155
+ }
156
+
157
+ [data-scope='select'][data-part='clear-trigger']:hover {
158
+ background: var(--xh-select-clear-bg-hover, var(--xh-bg-subtle-hover));
159
+ color: var(--xh-select-clear-fg-hover, var(--xh-fg-default));
160
+ }
161
+
162
+ [data-scope='select'][data-part='clear-trigger']:focus-visible {
163
+ outline: var(--xh-ring-width) solid var(--xh-_select-ring);
164
+ outline-offset: var(--xh-ring-offset);
165
+ }
166
+
167
+ [data-scope='select'][data-part='clear-trigger'][hidden] {
168
+ display: none;
169
+ }
170
+
171
+ /* 进了 control 就嵌进触发器右端(与展开指示符同位),纵向钉在正中;平时不占位也不接指针 */
172
+ [data-scope='select'][data-part='control'] [data-part='clear-trigger'] {
173
+ position: absolute;
174
+ inset-block-start: 50%;
175
+ inset-inline-end: var(--xh-select-trigger-px, var(--xh-_select-px));
176
+ translate: 0 -50%;
177
+ opacity: 0;
178
+ pointer-events: none;
179
+ }
180
+
181
+ /* 悬停或键盘聚到清空钮上时显形 */
182
+ [data-scope='select'][data-part='control']:hover [data-part='clear-trigger']:not([hidden]),
183
+ [data-scope='select'][data-part='control'] [data-part='clear-trigger']:focus-visible {
184
+ opacity: 1;
185
+ pointer-events: auto;
186
+ }
187
+
188
+ /* 清空钮显形期间展开指示符让位,两个图标不叠在一起 */
189
+ [data-scope='select'][data-part='control']:hover:has([data-part='clear-trigger']:not([hidden])) [data-part='indicator'],
190
+ [data-scope='select'][data-part='control']:has([data-part='clear-trigger']:focus-visible) [data-part='indicator'] {
191
+ opacity: 0;
192
+ }
193
+
194
+ /* 多选标签:淡底小药丸;放触发器里是纯展示,放外面配 tag-remove 可删 */
195
+ [data-scope='select'][data-part='tag'] {
196
+ display: inline-flex;
197
+ align-items: center;
198
+ gap: var(--xh-select-tag-gap, var(--xh-space-1));
199
+ padding-block: 0;
200
+ padding-inline: var(--xh-select-tag-px, var(--xh-space-2));
201
+ border-radius: var(--xh-select-tag-radius, var(--xh-shape-pill));
202
+ background: var(--xh-select-tag-bg, var(--xh-bg-subtle));
203
+ color: var(--xh-select-tag-fg, var(--xh-fg-default));
204
+ font-size: var(--xh-select-tag-font-size, var(--xh-font-size-xs));
205
+ line-height: var(--xh-leading-normal);
206
+ white-space: nowrap;
207
+ }
208
+
209
+ [data-scope='select'][data-part='tag'][data-disabled] {
210
+ color: var(--xh-fg-disabled);
211
+ }
212
+
213
+ [data-scope='select'][data-part='tag-remove'] {
214
+ display: inline-grid;
215
+ place-items: center;
216
+ padding: 0;
217
+ border: none;
218
+ border-radius: var(--xh-shape-pill);
219
+ background: none;
220
+ color: var(--xh-select-tag-remove-fg, var(--xh-fg-subtle));
221
+ font-size: inherit;
222
+ cursor: pointer;
223
+ }
224
+
225
+ [data-scope='select'][data-part='tag-remove']:hover {
226
+ color: var(--xh-select-tag-remove-fg-hover, var(--xh-fg-default));
227
+ }
228
+
229
+ [data-scope='select'][data-part='tag-remove']:focus-visible {
230
+ outline: var(--xh-ring-width) solid var(--xh-_select-ring);
231
+ outline-offset: var(--xh-ring-offset);
232
+ }
233
+
234
+ [data-scope='select'][data-part='tag-remove'][data-disabled] {
235
+ color: var(--xh-fg-disabled);
236
+ cursor: not-allowed;
237
+ }
238
+
105
239
  [data-scope='select'][data-part='trigger']:focus-visible {
106
240
  outline: var(--xh-ring-width) solid var(--xh-_select-ring);
107
241
  outline-offset: var(--xh-ring-offset);
@@ -181,8 +315,14 @@
181
315
  display: none;
182
316
  }
183
317
 
184
- /* 容器只是焦点域兜底落点,落焦不画环:高亮永远长在条目上 */
185
- [data-scope='select'][data-part='content']:focus-visible {
318
+ /* 焦点落在容器上只有一种情形:指针打开、无选中值、还没有任何条目当锚点。
319
+ 那一刻它认领着 Tab 位,环画在浮层内沿;有锚点时环长在条目上,容器不再画 */
320
+ [data-scope='select'][data-part='content'][tabindex='0']:focus-visible {
321
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
322
+ outline-offset: calc(-1 * var(--xh-ring-width));
323
+ }
324
+
325
+ [data-scope='select'][data-part='content']:not([tabindex='0']):focus-visible {
186
326
  outline: none;
187
327
  }
188
328
 
@@ -194,8 +334,8 @@
194
334
  display: flex;
195
335
  align-items: center;
196
336
  gap: var(--xh-select-item-gap, var(--xh-_select-gap));
197
- padding-block: var(--xh-select-item-py, var(--xh-space-1));
198
- padding-inline: var(--xh-select-item-px, var(--xh-control-px-sm));
337
+ padding-block: var(--xh-select-item-py, var(--xh-space-1_5));
338
+ padding-inline: var(--xh-select-item-px, var(--xh-control-px-md));
199
339
  border-radius: var(--xh-select-item-radius, var(--xh-shape-control));
200
340
  color: var(--xh-select-item-fg, var(--xh-fg-default));
201
341
  font-size: var(--xh-select-item-font-size, var(--xh-_select-font-size));
@@ -204,11 +344,14 @@
204
344
  white-space: nowrap;
205
345
  cursor: pointer;
206
346
  user-select: none;
347
+ transition:
348
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
349
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
207
350
  }
208
351
 
209
- /* 键盘高亮与指针高亮共用一套底色:条目本身承载焦点,不另画描边 */
352
+ /* 悬停与键盘锚点共用中性灰轻档:条目本身承载焦点,不另画描边 */
210
353
  [data-scope='select'][data-part='item']:not([data-disabled]):is(:hover, [data-highlighted]) {
211
- background: var(--xh-select-item-bg-highlight, var(--xh-_select-item-highlight-bg));
354
+ background: var(--xh-select-item-bg-hover, var(--xh-bg-subtle));
212
355
  }
213
356
 
214
357
  /* 选中只换文字色与字重,配合行尾的 ✓ 标记;底色整条通道留给高亮,
@@ -218,7 +361,14 @@
218
361
  font-weight: var(--xh-select-item-font-weight-selected, var(--xh-font-weight-medium));
219
362
  }
220
363
 
221
- [data-scope='select'][data-part='item']:focus {
364
+ /* 键盘走到条目上要画环:轻档底色对白底只有 1.1:1,够不上焦点指示器的对比度要求。
365
+ 指针悬停走的是程序化聚焦,鼠标模态下 :focus-visible 不匹配,不会误画 */
366
+ [data-scope='select'][data-part='item']:focus-visible {
367
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
368
+ outline-offset: calc(-1 * var(--xh-ring-width));
369
+ }
370
+
371
+ [data-scope='select'][data-part='item']:focus:not(:focus-visible) {
222
372
  outline: none;
223
373
  }
224
374
 
package/css/separator.css CHANGED
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='separator'] {
4
+ --xh-separator-skin: 1;
5
+ }
6
+
2
7
  [data-scope='separator'][data-part='root'] {
3
8
  border: 0;
4
9
  background: var(--xh-separator-color, var(--xh-border-default));
@@ -0,0 +1,168 @@
1
+ @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='side-nav'] {
4
+ --xh-side-nav-skin: 1;
5
+ }
6
+
7
+ [data-scope='side-nav'][data-part='root'] {
8
+ inline-size: var(--xh-side-nav-w, 240px);
9
+ display: flex;
10
+ flex-direction: column;
11
+ gap: var(--xh-side-nav-gap, var(--xh-space-1));
12
+ padding: var(--xh-side-nav-p, var(--xh-space-2));
13
+ color: var(--xh-side-nav-fg, var(--xh-fg-default));
14
+ }
15
+
16
+ /* 折叠成图标栏:整栏收窄,文字随行宽被裁掉 */
17
+ [data-scope='side-nav'][data-part='root'][data-collapsed] {
18
+ inline-size: var(--xh-side-nav-collapsed-w, 56px);
19
+ }
20
+
21
+ [data-scope='side-nav'][data-part='list'],
22
+ [data-scope='side-nav'][data-part='branch-content'] {
23
+ display: flex;
24
+ flex-direction: column;
25
+ gap: var(--xh-side-nav-gap, var(--xh-space-1));
26
+ margin: 0;
27
+ padding: 0;
28
+ list-style: none;
29
+ }
30
+
31
+ /* 子层靠嵌套缩进,层级越深自然越靠里 */
32
+ [data-scope='side-nav'][data-part='branch-content'] {
33
+ padding-inline-start: var(--xh-side-nav-indent, var(--xh-space-4));
34
+ }
35
+
36
+ [data-scope='side-nav'][data-part='branch-content'][hidden] {
37
+ display: none;
38
+ }
39
+
40
+ [data-scope='side-nav'][data-part='group'] {
41
+ display: flex;
42
+ flex-direction: column;
43
+ gap: var(--xh-side-nav-gap, var(--xh-space-1));
44
+ list-style: none;
45
+ }
46
+
47
+ [data-scope='side-nav'][data-part='group-label'] {
48
+ padding-block: var(--xh-space-1);
49
+ padding-inline: var(--xh-control-px-md);
50
+ font-size: var(--xh-font-size-xs);
51
+ color: var(--xh-fg-subtle);
52
+ user-select: none;
53
+ }
54
+
55
+ /* 折叠时分组标题没有落点,藏掉 */
56
+ [data-scope='side-nav'][data-part='group-label'][data-collapsed] {
57
+ display: none;
58
+ }
59
+
60
+ [data-scope='side-nav'][data-part='branch'] {
61
+ display: flex;
62
+ flex-direction: column;
63
+ gap: var(--xh-side-nav-gap, var(--xh-space-1));
64
+ list-style: none;
65
+ }
66
+
67
+ /* —— 行:分支按钮与链接同一副长相 —— */
68
+ [data-scope='side-nav'][data-part='branch-trigger'],
69
+ [data-scope='side-nav'][data-part='link'] {
70
+ display: flex;
71
+ align-items: center;
72
+ gap: var(--xh-control-gap-md);
73
+ inline-size: 100%;
74
+ min-block-size: var(--xh-control-h-md);
75
+ padding-inline: var(--xh-control-px-md);
76
+ border: none;
77
+ border-radius: var(--xh-shape-control);
78
+ background: none;
79
+ color: inherit;
80
+ font-size: var(--xh-text-body-size);
81
+ text-align: start;
82
+ text-decoration: none;
83
+ cursor: pointer;
84
+ }
85
+
86
+ [data-scope='side-nav'][data-part='branch-trigger']:hover,
87
+ [data-scope='side-nav'][data-part='link']:hover {
88
+ background: var(--xh-bg-subtle-hover);
89
+ }
90
+
91
+ [data-scope='side-nav'][data-part='branch-trigger']:focus-visible,
92
+ [data-scope='side-nav'][data-part='link']:focus-visible {
93
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
94
+ outline-offset: var(--xh-ring-offset);
95
+ }
96
+
97
+ /* 选中的那条:品牌淡底 + 强调字 */
98
+ [data-scope='side-nav'][data-part='link'][data-selected] {
99
+ background: var(--xh-bg-brand-subtle);
100
+ color: var(--xh-fg-brand);
101
+ font-weight: var(--xh-font-weight-medium);
102
+ }
103
+
104
+ /* 选中项的祖先枝:只染字色,一路指到当前页 */
105
+ [data-scope='side-nav'][data-part='branch-trigger'][data-active] {
106
+ color: var(--xh-fg-brand);
107
+ }
108
+
109
+ [data-scope='side-nav'][data-part='branch-trigger'][data-disabled],
110
+ [data-scope='side-nav'][data-part='link'][data-disabled] {
111
+ color: var(--xh-fg-disabled);
112
+ cursor: not-allowed;
113
+ }
114
+
115
+ [data-scope='side-nav'][data-part='branch-text'],
116
+ [data-scope='side-nav'][data-part='link-text'] {
117
+ flex: 1;
118
+ min-inline-size: 0;
119
+ overflow: hidden;
120
+ text-overflow: ellipsis;
121
+ white-space: nowrap;
122
+ }
123
+
124
+ /* 折叠成图标栏:文字与展开指示整个藏掉、行内容居中,不裁半个字。
125
+ 弹出面板里的行不在此列——面板是正常宽度的浮层,文字照常显示 */
126
+ [data-scope='side-nav'][data-part='root'][data-collapsed] [data-part='branch-text']:not([data-popout] *),
127
+ [data-scope='side-nav'][data-part='root'][data-collapsed] [data-part='link-text']:not([data-popout] *),
128
+ [data-scope='side-nav'][data-part='root'][data-collapsed] [data-part='branch-indicator']:not([data-popout] *) {
129
+ display: none;
130
+ }
131
+
132
+ [data-scope='side-nav'][data-part='root'][data-collapsed] [data-part='branch-trigger']:not([data-popout] *),
133
+ [data-scope='side-nav'][data-part='root'][data-collapsed] [data-part='link']:not([data-popout] *) {
134
+ justify-content: center;
135
+ padding-inline: 0;
136
+ }
137
+
138
+ /* 折叠态的弹出面板:贴着触发按钮旁侧的浮层卡片,坐标由连接层写在行内样式上。
139
+ 面板自己不吃子层缩进(padding 整圈盖掉),面板内的嵌套子层照常缩进 */
140
+ [data-scope='side-nav'][data-part='branch-content'][data-popout] {
141
+ z-index: var(--xh-side-nav-popout-layer, var(--xh-layer-popover));
142
+ min-inline-size: var(--xh-side-nav-popout-min-w, var(--xh-overlay-menu-min-w));
143
+ max-inline-size: var(--xh-side-nav-popout-max-w, var(--xh-overlay-max-w));
144
+ padding: var(--xh-side-nav-popout-p, var(--xh-space-1));
145
+ border: var(--xh-stroke-thin) solid var(--xh-side-nav-popout-border, var(--xh-border-default));
146
+ border-radius: var(--xh-side-nav-popout-radius, var(--xh-shape-surface));
147
+ background: var(--xh-side-nav-popout-bg, var(--xh-bg-surface));
148
+ box-shadow: var(--xh-side-nav-popout-shadow, var(--xh-elevation-2));
149
+ pointer-events: auto;
150
+ }
151
+
152
+ [data-scope='side-nav'][data-part='branch-indicator'] {
153
+ margin-inline-start: auto;
154
+ display: inline-flex;
155
+ flex: none;
156
+ transition: rotate var(--xh-duration-fast) var(--xh-ease-standard);
157
+ }
158
+
159
+ [data-scope='side-nav'][data-part='branch-indicator'][data-state='open'] {
160
+ rotate: 90deg;
161
+ }
162
+
163
+ @media (prefers-reduced-motion: reduce) {
164
+ [data-scope='side-nav'][data-part='branch-indicator'] {
165
+ transition: none;
166
+ }
167
+ }
168
+ }
package/css/skeleton.css CHANGED
@@ -13,6 +13,11 @@
13
13
  }
14
14
 
15
15
  @layer xihan.components {
16
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
17
+ [data-scope='skeleton'] {
18
+ --xh-skeleton-skin: 1;
19
+ }
20
+
16
21
  /* 骨架容器:竖着码放各根骨架条 */
17
22
  [data-scope='skeleton'][data-part='root'] {
18
23
  display: flex;