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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/CHANGELOG.md +447 -0
  2. package/README.md +1 -1
  3. package/css/accordion.css +56 -5
  4. package/css/alert.css +30 -5
  5. package/css/anchor.css +7 -7
  6. package/css/avatar-group.css +3 -3
  7. package/css/avatar.css +3 -3
  8. package/css/back-top.css +16 -1
  9. package/css/badge.css +16 -2
  10. package/css/breadcrumb.css +24 -4
  11. package/css/button-group.css +14 -13
  12. package/css/button.css +42 -11
  13. package/css/calendar.css +119 -2
  14. package/css/card.css +5 -5
  15. package/css/carousel.css +36 -5
  16. package/css/cascader.css +183 -102
  17. package/css/checkbox-group.css +41 -19
  18. package/css/checkbox.css +54 -8
  19. package/css/clipboard.css +2 -1
  20. package/css/code-block.css +12 -7
  21. package/css/collapsible.css +42 -2
  22. package/css/color-picker.css +111 -45
  23. package/css/combobox.css +114 -53
  24. package/css/composer.css +5 -3
  25. package/css/context-menu.css +97 -69
  26. package/css/date-field.css +91 -14
  27. package/css/date-picker.css +276 -65
  28. package/css/descriptions.css +2 -2
  29. package/css/dialog.css +40 -13
  30. package/css/download-trigger.css +76 -0
  31. package/css/drawer.css +45 -14
  32. package/css/dynamic-input.css +68 -17
  33. package/css/editable.css +12 -10
  34. package/css/ellipsis.css +1 -1
  35. package/css/empty-state.css +7 -7
  36. package/css/field.css +9 -6
  37. package/css/fieldset.css +69 -0
  38. package/css/file-upload.css +45 -7
  39. package/css/float-button.css +16 -1
  40. package/css/floating-panel.css +310 -0
  41. package/css/form.css +4 -4
  42. package/css/heatmap.css +410 -0
  43. package/css/highlight.css +1 -1
  44. package/css/hotkeys.css +90 -0
  45. package/css/hover-card.css +40 -45
  46. package/css/image-cropper.css +231 -0
  47. package/css/image-viewer.css +137 -12
  48. package/css/image.css +2 -2
  49. package/css/infinite-scroll.css +1 -1
  50. package/css/json-viewer.css +241 -0
  51. package/css/layout.css +6 -10
  52. package/css/list.css +8 -8
  53. package/css/listbox.css +20 -4
  54. package/css/loading-bar.css +7 -1
  55. package/css/log.css +1 -1
  56. package/css/marquee.css +9 -0
  57. package/css/masonry.css +63 -0
  58. package/css/mention.css +87 -33
  59. package/css/menu.css +113 -47
  60. package/css/menubar.css +146 -60
  61. package/css/navigation-menu.css +54 -29
  62. package/css/number-animation.css +3 -3
  63. package/css/number-field.css +173 -16
  64. package/css/overlay-arrow.css +51 -0
  65. package/css/page-header.css +5 -5
  66. package/css/pagination.css +13 -13
  67. package/css/password-input.css +356 -0
  68. package/css/pin-input.css +13 -10
  69. package/css/popconfirm.css +46 -10
  70. package/css/popover.css +70 -45
  71. package/css/popselect.css +183 -53
  72. package/css/progress.css +47 -7
  73. package/css/qr-code.css +1 -1
  74. package/css/radio-group.css +14 -8
  75. package/css/rating.css +2 -2
  76. package/css/reset.css +17 -0
  77. package/css/result.css +6 -6
  78. package/css/scroll-area.css +61 -89
  79. package/css/scrollbar.css +167 -0
  80. package/css/segmented.css +215 -0
  81. package/css/select.css +274 -125
  82. package/css/separator.css +2 -2
  83. package/css/side-nav.css +111 -19
  84. package/css/signature-pad.css +144 -0
  85. package/css/skeleton.css +7 -1
  86. package/css/slider.css +10 -10
  87. package/css/space.css +117 -0
  88. package/css/spinner.css +22 -10
  89. package/css/statistic.css +9 -9
  90. package/css/steps.css +25 -8
  91. package/css/switch.css +48 -6
  92. package/css/table.css +131 -37
  93. package/css/tabs.css +4 -4
  94. package/css/tag.css +177 -0
  95. package/css/tags-input.css +78 -40
  96. package/css/text-field.css +149 -36
  97. package/css/thread.css +1 -1
  98. package/css/time-field.css +97 -16
  99. package/css/time-picker.css +259 -69
  100. package/css/timeline.css +10 -10
  101. package/css/timer.css +119 -0
  102. package/css/toast.css +34 -10
  103. package/css/toaster.css +1 -1
  104. package/css/toggle.css +6 -6
  105. package/css/tone.css +10 -5
  106. package/css/toolbar.css +1 -1
  107. package/css/tooltip.css +36 -33
  108. package/css/tour.css +103 -58
  109. package/css/transfer.css +68 -17
  110. package/css/tree-select.css +185 -84
  111. package/css/tree.css +85 -1
  112. package/css/typography.css +4 -4
  113. package/css/undefined.css +11 -1
  114. package/css/virtualizer.css +3 -1
  115. package/css/watermark.css +1 -2
  116. package/index.css +17 -0
  117. package/index.unlayered.css +8659 -2799
  118. package/package.json +19 -3
package/css/card.css CHANGED
@@ -27,7 +27,7 @@
27
27
  }
28
28
 
29
29
  [data-scope='card'][data-part='root'][data-variant='elevated'] {
30
- box-shadow: var(--xh-elevation-2);
30
+ box-shadow: var(--xh-card-shadow, var(--xh-elevation-raised));
31
31
  }
32
32
 
33
33
  [data-scope='card'][data-part='root'][data-variant='ghost'] {
@@ -47,7 +47,7 @@
47
47
  @media (hover: hover) {
48
48
  [data-scope='card'][data-part='root'][data-hoverable]:hover {
49
49
  border-color: var(--xh-card-border, var(--xh-border-strong));
50
- box-shadow: var(--xh-elevation-2);
50
+ box-shadow: var(--xh-card-shadow, var(--xh-elevation-raised));
51
51
  }
52
52
  }
53
53
 
@@ -74,18 +74,18 @@
74
74
  }
75
75
 
76
76
  [data-scope='card'][data-part='title'] {
77
- font-size: var(--xh-card-title-font-size, var(--xh-font-size-md));
77
+ font-size: var(--xh-card-title-font-size, var(--xh-text-label-size));
78
78
  font-weight: var(--xh-font-weight-medium);
79
79
  line-height: var(--xh-leading-tight);
80
80
  }
81
81
 
82
82
  [data-scope='card'][data-part='root'][data-size='lg'] [data-part='title'] {
83
- font-size: var(--xh-card-title-font-size, var(--xh-font-size-lg));
83
+ font-size: var(--xh-card-title-font-size, var(--xh-control-font-lg));
84
84
  }
85
85
 
86
86
  [data-scope='card'][data-part='description'] {
87
87
  color: var(--xh-fg-muted);
88
- font-size: var(--xh-font-size-sm);
88
+ font-size: var(--xh-text-secondary-size);
89
89
  line-height: var(--xh-leading-normal);
90
90
  }
91
91
 
package/css/carousel.css CHANGED
@@ -7,6 +7,9 @@
7
7
  /* 根节点是 region 地标:把上一张 / 视口 / 下一张排成一行,指示点另起一行。
8
8
  两端按钮浮在视口上还是排在两侧由作者定,这里只给最朴素的一版排布 */
9
9
  [data-scope='carousel'][data-part='root'] {
10
+ /* 作者塞进指示符槽的图标跟着字号走,与皮肤画的兜底字形同一把尺 */
11
+ --xh-icon-size: var(--xh-carousel-icon-size, var(--xh-glyph-size-text));
12
+
10
13
  display: flex;
11
14
  flex-wrap: wrap;
12
15
  align-items: center;
@@ -168,7 +171,7 @@
168
171
  /* 圆点本身没有文字,按钮里的文案(页码)交给作者,默认不显示 */
169
172
  padding: 0;
170
173
  border: 0;
171
- border-radius: var(--xh-carousel-indicator-radius, var(--xh-radius-full));
174
+ border-radius: var(--xh-carousel-indicator-radius, var(--xh-shape-pill));
172
175
  background: var(--xh-carousel-indicator-bg, var(--xh-border-control));
173
176
  font-size: 0;
174
177
  cursor: pointer;
@@ -179,20 +182,48 @@
179
182
  display: none;
180
183
  }
181
184
 
182
- [data-scope='carousel'][data-part='indicator']:hover:not([data-selected]) {
185
+ [data-scope='carousel'][data-part='indicator']:hover:not([data-current]) {
183
186
  background: var(--xh-carousel-indicator-bg-hover, var(--xh-fg-subtle));
184
187
  }
185
188
 
186
- /* 当前页:data-selected 与 aria-current='true' 由连接层成对给出,
189
+ /* 当前页:data-current 与 aria-current='true' 由连接层成对给出,
187
190
  样式挂在 data 钩子上,无障碍语义留给 aria,两边各司其职 */
188
- [data-scope='carousel'][data-part='indicator'][data-selected] {
191
+ [data-scope='carousel'][data-part='indicator'][data-current] {
189
192
  background: var(--xh-carousel-indicator-bg-selected, var(--xh-bg-brand));
190
193
  }
191
194
 
192
- /* 减少动效:位移立刻到位。翻页本身照常发生,只是不再有滑动的过程 */
195
+ /* 减少动效:位移立刻到位。翻页本身照常发生,只是不再有滑动的过程。
196
+ 两条规则是同一件事的两个触发条件:系统偏好与作者打的 data-motion */
193
197
  @media (prefers-reduced-motion: reduce) {
194
198
  [data-scope='carousel'][data-part='item-group'] {
195
199
  transition: none;
196
200
  }
197
201
  }
202
+
203
+ :where([data-motion='reduce']) [data-scope='carousel'][data-part='item-group'] {
204
+ transition: none;
205
+ }
206
+
207
+ /* 没写内容就由皮肤画兜底字形;作者往部件里塞了自己的图形,这几条即不命中 */
208
+ [data-scope='carousel'][data-part='prev-trigger']:empty::before {
209
+ content: '';
210
+ display: inline-block;
211
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
212
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
213
+ vertical-align: var(--xh-glyph-baseline-shift);
214
+ background-color: currentColor;
215
+ -webkit-mask: var(--xh-glyph-mark-chevron-left) center / contain no-repeat;
216
+ mask: var(--xh-glyph-mark-chevron-left) center / contain no-repeat;
217
+ }
218
+
219
+ [data-scope='carousel'][data-part='next-trigger']:empty::before {
220
+ content: '';
221
+ display: inline-block;
222
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
223
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
224
+ vertical-align: var(--xh-glyph-baseline-shift);
225
+ background-color: currentColor;
226
+ -webkit-mask: var(--xh-glyph-mark-chevron-right) center / contain no-repeat;
227
+ mask: var(--xh-glyph-mark-chevron-right) center / contain no-repeat;
228
+ }
198
229
  }
package/css/cascader.css CHANGED
@@ -4,11 +4,25 @@
4
4
  --xh-cascader-skin: 1;
5
5
  }
6
6
 
7
- /* 解剖里没有把「触发器 + 清空按钮」裹在一起的 control 部件,一行是靠 flex-wrap 分出来的:
8
- 标签占满整行把后面的挤到下一行,触发器与清空按钮就并排落在同一行上。
9
- 这样作者少写标签、少写清空按钮,剩下的部件也不会塌 */
10
- [data-scope='cascader'][data-part='root'] {
11
- /* 尺寸档只换这几个私有槽,触发框与列里的条目都从 root 继承它们,换档不必给每个部件各写一条选择器 */
7
+ /* 坐标由定位引擎写进 style(inset-inline-start / inset-block-start),这里只定位模式与层级 */
8
+ [data-scope='cascader'][data-part='positioner'] {
9
+ position: fixed;
10
+ z-index: var(--xh-cascader-layer, var(--xh-layer-popover));
11
+ /* 空壳不吃指针,交互只发生在 content */
12
+ pointer-events: none;
13
+ }
14
+
15
+ /* 锚点被滚出可视区:整个浮层随之收起 */
16
+ [data-scope='cascader'][data-part='positioner'][data-hidden] {
17
+ visibility: hidden;
18
+ }
19
+
20
+ /* 私有槽同时声明在根与 positioner 上:浮层被搬到 portal 落点,不是根的后代,继承不过去 */
21
+ [data-scope='cascader']:is([data-part='root'], [data-part='positioner']) {
22
+ /* 作者塞进指示符槽的图标跟着尺寸档走,与皮肤画的兜底字形同一把尺 */
23
+ --xh-icon-size: var(--xh-cascader-icon-size, var(--xh-glyph-size-md));
24
+
25
+ /* 尺寸档只换这几个私有槽,盒与列里的条目都从这两处继承它们,换档不必给每个部件各写一条选择器 */
12
26
  --xh-_cascader-h: var(--xh-control-h-md);
13
27
  --xh-_cascader-px: var(--xh-control-px-md);
14
28
  --xh-_cascader-gap: var(--xh-control-gap-md);
@@ -27,9 +41,14 @@
27
41
  /* 展开路径的底色:随语气走淡底,没写 data-tone 时退回品牌淡底 */
28
42
  --xh-_cascader-active-bg: var(--xh-_tone-subtle, var(--xh-bg-brand-subtle));
29
43
 
44
+ /* 聚焦环恒用固定环色;校验失败时换成告警环色 */
45
+ --xh-_cascader-ring: var(--xh-ring-focus);
46
+ }
47
+
48
+ /* 标签一行、盒一行的两行堆叠 */
49
+ [data-scope='cascader'][data-part='root'] {
30
50
  display: inline-flex;
31
- flex-wrap: wrap;
32
- align-items: center;
51
+ flex-direction: column;
33
52
  gap: var(--xh-cascader-gap, var(--xh-space-1));
34
53
  }
35
54
 
@@ -41,48 +60,48 @@
41
60
  /* —— 形态 —— */
42
61
  /* 形态只决定描边与底色怎么用;用哪族颜色由 data-tone 决定,没写 tone 就沿用原来的中性配色 */
43
62
  /* outline 就是缺省那一身:描边加画布底,写出来只为让三档并列可读 */
44
- [data-scope='cascader'][data-part='root'][data-variant='outline'] {
63
+ [data-scope='cascader']:is([data-part='root'], [data-part='positioner'])[data-variant='outline'] {
45
64
  --xh-_cascader-border: var(--xh-border-control);
46
65
  --xh-_cascader-border-hover: var(--xh-border-control-hover);
47
66
  --xh-_cascader-bg: var(--xh-bg-canvas);
48
67
  }
49
68
 
50
69
  /* subtle 是填充式:底色接过「这里能点」的表达,描边随之让位 */
51
- [data-scope='cascader'][data-part='root'][data-variant='subtle'] {
70
+ [data-scope='cascader']:is([data-part='root'], [data-part='positioner'])[data-variant='subtle'] {
52
71
  --xh-_cascader-border: transparent;
53
72
  --xh-_cascader-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
54
73
  --xh-_cascader-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
55
74
  }
56
75
 
57
- /* ghost 无框无底,可见反馈只在聚焦时给;边框宽度仍占位,切换形态不引起布局抖动 */
58
- [data-scope='cascader'][data-part='root'][data-variant='ghost'] {
76
+ /* 无框:底与常态描边透明,悬停浮出描边,聚焦由焦点环表达 */
77
+ [data-scope='cascader']:is([data-part='root'], [data-part='positioner'])[data-variant='ghost'] {
59
78
  --xh-_cascader-border: transparent;
60
- --xh-_cascader-border-hover: transparent;
79
+ --xh-_cascader-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
61
80
  --xh-_cascader-bg: transparent;
62
81
  }
63
82
 
64
83
  /* —— 尺寸 —— */
65
84
  /* 缺省档写在上面的基础规则里 */
66
- [data-scope='cascader'][data-part='root'][data-size='sm'] {
85
+ [data-scope='cascader']:is([data-part='root'], [data-part='positioner'])[data-size='sm'] {
86
+ --xh-icon-size: var(--xh-cascader-icon-size, var(--xh-glyph-size-sm));
67
87
  --xh-_cascader-h: var(--xh-control-h-sm);
68
88
  --xh-_cascader-px: var(--xh-control-px-sm);
69
89
  --xh-_cascader-gap: var(--xh-control-gap-sm);
70
- --xh-_cascader-font-size: var(--xh-font-size-sm);
71
- --xh-_cascader-label-font-size: var(--xh-font-size-sm);
90
+ --xh-_cascader-font-size: var(--xh-control-font-sm);
91
+ --xh-_cascader-label-font-size: var(--xh-control-font-sm);
72
92
  }
73
93
 
74
- [data-scope='cascader'][data-part='root'][data-size='lg'] {
94
+ [data-scope='cascader']:is([data-part='root'], [data-part='positioner'])[data-size='lg'] {
95
+ --xh-icon-size: var(--xh-cascader-icon-size, var(--xh-glyph-size-lg));
75
96
  --xh-_cascader-h: var(--xh-control-h-lg);
76
97
  --xh-_cascader-px: var(--xh-control-px-lg);
77
98
  --xh-_cascader-gap: var(--xh-control-gap-lg);
78
- --xh-_cascader-font-size: var(--xh-font-size-lg);
79
- --xh-_cascader-label-font-size: var(--xh-font-size-lg);
99
+ --xh-_cascader-font-size: var(--xh-control-font-lg);
100
+ --xh-_cascader-label-font-size: var(--xh-control-font-lg);
80
101
  }
81
102
 
82
103
  [data-scope='cascader'][data-part='label'] {
83
- /* 占满整行,把触发器那一行挤下去 */
84
- flex-basis: 100%;
85
- color: var(--xh-cascader-label-fg, var(--xh-fg-muted));
104
+ color: var(--xh-cascader-label-fg, var(--xh-fg-default));
86
105
  font-size: var(--xh-cascader-label-font-size, var(--xh-_cascader-label-font-size));
87
106
  font-weight: var(--xh-cascader-label-font-weight, var(--xh-text-label-weight));
88
107
  line-height: var(--xh-leading-none);
@@ -93,64 +112,88 @@
93
112
  color: var(--xh-fg-subtle);
94
113
  }
95
114
 
96
- /* 画成一个框的就是 trigger 自己:整条路径的回显与展开箭头都长在它里面,
97
- 清空按钮贴在框外与它并排 */
98
- [data-scope='cascader'][data-part='trigger'] {
115
+ /* 盒:描边、底色、控件高度与行内内衬都落在这一层,触发按钮与清空按钮在里面并排 */
116
+ [data-scope='cascader'][data-part='control'] {
99
117
  display: inline-flex;
100
- flex: 1;
101
118
  align-items: center;
102
- justify-content: space-between;
103
- gap: var(--xh-cascader-trigger-gap, var(--xh-_cascader-gap));
104
- min-inline-size: var(--xh-cascader-trigger-min-w, 14rem);
105
- block-size: var(--xh-cascader-trigger-h, var(--xh-_cascader-h));
106
- padding-inline: var(--xh-cascader-trigger-px, var(--xh-_cascader-px));
107
- border: var(--xh-stroke-thin) solid var(--xh-cascader-trigger-border, var(--xh-_cascader-border));
108
- border-radius: var(--xh-cascader-trigger-radius, var(--xh-shape-control));
109
- background: var(--xh-cascader-trigger-bg, var(--xh-_cascader-bg));
110
- color: var(--xh-cascader-trigger-fg, var(--xh-fg-default));
111
- font-family: inherit;
112
- font-size: var(--xh-cascader-trigger-font-size, var(--xh-_cascader-font-size));
113
- line-height: var(--xh-leading-none);
114
- text-align: start;
119
+ gap: var(--xh-cascader-control-gap, var(--xh-_cascader-gap));
120
+ min-inline-size: var(--xh-cascader-control-min-w, var(--xh-control-min-w));
121
+ block-size: var(--xh-cascader-control-h, var(--xh-_cascader-h));
122
+ padding-inline: var(--xh-cascader-control-px, var(--xh-_cascader-px));
123
+ border: var(--xh-stroke-thin) solid var(--xh-cascader-control-border, var(--xh-_cascader-border));
124
+ border-radius: var(--xh-cascader-control-radius, var(--xh-shape-control));
125
+ background: var(--xh-cascader-control-bg, var(--xh-_cascader-bg));
126
+ color: var(--xh-cascader-control-fg, var(--xh-fg-default));
115
127
  cursor: pointer;
116
128
  transition: border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
117
129
  }
118
130
 
119
- [data-scope='cascader'][data-part='trigger'][hidden] {
131
+ [data-scope='cascader'][data-part='control'][hidden] {
120
132
  display: none;
121
133
  }
122
134
 
123
135
  /* hover 只在中性态换描边:invalid 正拿边框颜色说事,被 hover 盖掉那条信息就没了 */
124
- [data-scope='cascader'][data-part='trigger']:hover:not(:disabled, [data-invalid]) {
125
- border-color: var(--xh-cascader-trigger-border-hover, var(--xh-_cascader-border-hover));
136
+ [data-scope='cascader'][data-part='control']:hover:not([data-disabled], [data-invalid]) {
137
+ border-color: var(--xh-cascader-control-border-hover, var(--xh-_cascader-border-hover));
126
138
  }
127
139
 
128
- /* trigger 是整个控件唯一的 Tab 停靠点(展开后停靠点才移进列里),落焦必须看得见。
129
- 聚焦环恒用固定环色,不随语气走 */
130
- [data-scope='cascader'][data-part='trigger']:focus-visible {
131
- outline: var(--xh-ring-width) solid var(--xh-ring-focus);
140
+ /* Tab 停靠点是盒里的 trigger(展开后才移进列里),环画在盒上 */
141
+ [data-scope='cascader'][data-part='control']:focus-within:not([data-disabled]) {
142
+ border-color: var(--xh-cascader-control-border-focus, var(--xh-_tone, var(--xh-border-control-focus)));
143
+ outline: var(--xh-ring-width) solid var(--xh-_cascader-ring);
132
144
  outline-offset: var(--xh-ring-offset);
133
145
  }
134
146
 
135
- [data-scope='cascader'][data-part='trigger'][data-invalid] {
136
- border-color: var(--xh-cascader-trigger-border-invalid, var(--xh-color-danger-500));
147
+ [data-scope='cascader'][data-part='control'][data-invalid] {
148
+ border-color: var(--xh-cascader-control-border-invalid, var(--xh-border-invalid));
149
+
150
+ /* 校验失败时环也跟着变红:两圈等宽相邻却一红一蓝,读的人不知道哪个才是当前状态 */
151
+ --xh-_cascader-ring: var(--xh-ring-invalid);
137
152
  }
138
153
 
139
154
  /* 只读仍可聚焦、浮层仍展得开、列仍能浏览,所以不置灰,只把底色压下去表示「改不动」 */
140
- [data-scope='cascader'][data-part='trigger'][data-readonly] {
141
- background: var(--xh-cascader-trigger-bg-readonly, var(--xh-bg-subtle));
155
+ [data-scope='cascader'][data-part='control'][data-readonly] {
156
+ background: var(--xh-cascader-control-bg-readonly, var(--xh-bg-subtle));
157
+ }
158
+
159
+ [data-scope='cascader'][data-part='control'][data-disabled] {
160
+ background: var(--xh-cascader-control-bg-disabled, var(--xh-bg-subtle));
161
+ color: var(--xh-fg-disabled);
162
+ cursor: not-allowed;
163
+ }
164
+
165
+ /* 盒内那颗可聚焦的按钮:不画描边不铺底也不自带高度,只占满剩余宽度、排布回显与箭头 */
166
+ [data-scope='cascader'][data-part='trigger'] {
167
+ display: inline-flex;
168
+ flex: 1 1 auto;
169
+ align-items: center;
170
+ justify-content: space-between;
171
+ gap: var(--xh-cascader-trigger-gap, var(--xh-_cascader-gap));
172
+ min-inline-size: 0;
173
+ padding: 0;
174
+ border: 0;
175
+ background: transparent;
176
+ color: var(--xh-cascader-trigger-fg, var(--xh-fg-default));
177
+ font-family: inherit;
178
+ font-size: var(--xh-cascader-trigger-font-size, var(--xh-_cascader-font-size));
179
+ line-height: var(--xh-leading-none);
180
+ text-align: start;
181
+ cursor: pointer;
182
+ }
183
+
184
+ [data-scope='cascader'][data-part='trigger'][hidden] {
185
+ display: none;
142
186
  }
143
187
 
144
188
  /* trigger 是单体控件,用原生 disabled(列里的集合条目一律 aria-disabled,写 :disabled 永不命中) */
145
189
  [data-scope='cascader'][data-part='trigger']:disabled {
146
- background: var(--xh-cascader-trigger-bg-disabled, var(--xh-bg-subtle));
147
190
  color: var(--xh-fg-disabled);
148
191
  cursor: not-allowed;
149
192
  }
150
193
 
151
- /* 路径连起来会很长,必须能截断;框宽由 trigger 定,这里得允许自己被压缩 */
194
+ /* 路径连起来会很长,必须能截断;框宽由盒定,这里得允许自己被压缩 */
152
195
  [data-scope='cascader'][data-part='value-text'] {
153
- flex: 1;
196
+ flex: 1 1 auto;
154
197
  min-inline-size: 0;
155
198
  overflow: hidden;
156
199
  white-space: nowrap;
@@ -166,6 +209,12 @@
166
209
  color: var(--xh-fg-disabled);
167
210
  }
168
211
 
212
+ /* 有值时清空钮顶上来,箭头让位:两个图标并排堆着,用户分不清点哪个。
213
+ 让位而不是禁用——禁用的图标还占着位置,看着像坏了 */
214
+ [data-scope='cascader'][data-part='indicator'][data-clearable] {
215
+ display: none;
216
+ }
217
+
169
218
  [data-scope='cascader'][data-part='indicator'] {
170
219
  display: inline-flex;
171
220
  flex: none;
@@ -181,7 +230,14 @@
181
230
 
182
231
  /* 没写内容就由皮肤画箭头;作者往部件里塞了自己的图形,这条规则即不命中 */
183
232
  [data-scope='cascader'][data-part='indicator']:empty::before {
184
- content: '';
233
+ content: '';
234
+ display: inline-block;
235
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
236
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
237
+ vertical-align: var(--xh-glyph-baseline-shift);
238
+ background-color: currentColor;
239
+ -webkit-mask: var(--xh-glyph-mark-chevron-down) center / contain no-repeat;
240
+ mask: var(--xh-glyph-mark-chevron-down) center / contain no-repeat;
185
241
  }
186
242
 
187
243
  [data-scope='cascader'][data-part='indicator'][data-state='open'] {
@@ -192,7 +248,7 @@
192
248
  color: var(--xh-fg-disabled);
193
249
  }
194
250
 
195
- /* 贴在触发框旁边的小方钮,不撑高那一行 */
251
+ /* 盒内靠右的小方钮,不撑高那一行 */
196
252
  [data-scope='cascader'][data-part='clear-trigger'] {
197
253
  display: inline-flex;
198
254
  flex: none;
@@ -206,24 +262,25 @@
206
262
  background: var(--xh-cascader-action-bg, transparent);
207
263
  color: var(--xh-cascader-action-fg, var(--xh-fg-muted));
208
264
  font-family: inherit;
209
- font-size: var(--xh-cascader-action-font-size, var(--xh-font-size-sm));
265
+ font-size: var(--xh-cascader-action-font-size, var(--xh-text-secondary-size));
210
266
  line-height: var(--xh-leading-none);
211
267
  cursor: pointer;
212
268
  user-select: none;
213
- transition: background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
269
+ transition: background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter), scale var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
214
270
  }
215
271
 
216
272
  [data-scope='cascader'][data-part='clear-trigger'][hidden] {
217
273
  display: none;
218
274
  }
219
275
 
220
- [data-scope='cascader'][data-part='clear-trigger']:hover:not(:disabled) {
276
+ [data-scope='cascader'][data-part='clear-trigger']:hover {
221
277
  background: var(--xh-cascader-action-bg-hover, var(--xh-bg-subtle-hover));
222
278
  color: var(--xh-cascader-action-fg-hover, var(--xh-fg-default));
223
279
  }
224
280
 
225
- [data-scope='cascader'][data-part='clear-trigger']:active:not(:disabled) {
281
+ [data-scope='cascader'][data-part='clear-trigger']:active {
226
282
  background: var(--xh-cascader-action-bg-active, var(--xh-bg-subtle-active));
283
+ scale: var(--xh-motion-scale-press);
227
284
  }
228
285
 
229
286
  /* 清空按钮退出了 Tab 序列(tabindex=-1),正常操作走不到这里;
@@ -233,47 +290,28 @@
233
290
  outline-offset: var(--xh-ring-offset);
234
291
  }
235
292
 
236
- /* 无选中、只读、整控件禁用三种情况下都按不动,用的是原生 disabled */
237
- [data-scope='cascader'][data-part='clear-trigger']:disabled {
238
- background: var(--xh-cascader-action-bg, transparent);
239
- color: var(--xh-fg-disabled);
240
- cursor: not-allowed;
241
- }
242
-
243
- /* 坐标由定位引擎写进 style(inset-inline-start / inset-block-start),这里只定位模式与层级 */
244
- [data-scope='cascader'][data-part='positioner'] {
245
- position: fixed;
246
- z-index: var(--xh-layer-popover);
247
- /* 空壳不吃指针,交互只发生在 content 里 */
248
- pointer-events: none;
249
- }
250
-
251
- /* 锚点被滚出可视区:整个浮层随之收起 */
252
- [data-scope='cascader'][data-part='positioner'][data-hidden] {
253
- visibility: hidden;
254
- }
255
-
256
293
  /* 级联的骨架就在这一条上:列沿书写方向并排,一层一列。
257
294
  横向随列数增减,因此不设固定宽度,只给每列自己的宽度 */
258
295
  [data-scope='cascader'][data-part='content'] {
259
296
  display: flex;
260
297
  flex-direction: row;
261
298
  align-items: stretch;
262
- max-inline-size: var(--xh-cascader-content-max-w, 48rem);
299
+ max-inline-size: var(--xh-cascader-content-max-w, var(--xh-overlay-max-w-xl));
263
300
  border: var(--xh-stroke-thin) solid var(--xh-cascader-content-border, var(--xh-border-default));
264
301
  border-radius: var(--xh-cascader-content-radius, var(--xh-shape-surface));
265
302
  background: var(--xh-cascader-content-bg, var(--xh-bg-surface));
266
303
  color: var(--xh-cascader-content-fg, var(--xh-fg-default));
267
- box-shadow: var(--xh-cascader-content-shadow, var(--xh-elevation-2));
304
+ box-shadow: var(--xh-cascader-content-shadow, var(--xh-elevation-floating));
268
305
  /* 列多到放不下时整体横向滚动;纵向溢出归每一列自己 */
269
306
  overflow-x: auto;
270
307
  pointer-events: auto;
271
308
  }
272
309
 
273
- /* 收起态的唯一开关:display 盖掉了 UA 的 [hidden]{display:none},漏了这条浮层就收不起来 */
274
- [data-scope='cascader'][data-part='content'][hidden] {
275
- display: none;
276
- }
310
+ /* 这里刻意没有 [data-part='content'][hidden] { display: none },与 dialog 一致:
311
+ 收起态连接层会给 contenthidden,而上面那条 display 正是要盖掉 UA 的
312
+ [hidden]{display:none}——退场动画得先能播。presence 读的是 content 的 animationName,
313
+ 真正的收起由宿主在退场结束后落成内联 display:none(Vue 那边直接不渲染)。
314
+ 其余声明了 display 的角色节点仍补 [hidden],那几处没有退场动画。 */
277
315
 
278
316
  /* 容器只是焦点域的兜底落点,落焦不画环:高亮永远长在条目上 */
279
317
  [data-scope='cascader'][data-part='content']:focus-visible {
@@ -281,14 +319,13 @@
281
319
  }
282
320
 
283
321
  [data-scope='cascader'][data-part='content'][data-state='open'] {
284
- /* 入场横移沿书写方向起始缘:位移量经私有槽供给关键帧,rtl 只翻符号 */
285
- --xh-_cascader-slide: calc(-1 * var(--xh-motion-distance-sm));
286
-
287
- animation: xh-cascader-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
322
+ animation: xh-pop-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
288
323
  }
289
324
 
290
- [dir='rtl'] [data-scope='cascader'][data-part='content'][data-state='open'] {
291
- --xh-_cascader-slide: var(--xh-motion-distance-sm);
325
+ /* 退场:收起那一帧 hidden 由退场闸门押后,这里才播得出来。forwards 把末态钉住,
326
+ 免得动画结束到真收起之间闪一下原样 */
327
+ [data-scope='cascader'][data-part='content'][data-state='closed'] {
328
+ animation: xh-pop-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
292
329
  }
293
330
 
294
331
  /* 一列:宽度由内容撑出、设下限防塌,高度定死——切换展开路径时浮层不上下跳动,
@@ -298,7 +335,7 @@
298
335
  flex: none;
299
336
  flex-direction: column;
300
337
  min-inline-size: var(--xh-cascader-column-min-w, 7rem);
301
- block-size: var(--xh-cascader-column-h, 11.25rem);
338
+ block-size: var(--xh-cascader-column-h, var(--xh-viewport-h-sm));
302
339
  padding-block: var(--xh-cascader-column-py, var(--xh-space-1));
303
340
  padding-inline: var(--xh-cascader-column-px, var(--xh-space-1));
304
341
  overflow-y: auto;
@@ -331,7 +368,7 @@
331
368
  display: flex;
332
369
  align-items: center;
333
370
  gap: var(--xh-cascader-row-gap, var(--xh-_cascader-gap));
334
- max-inline-size: var(--xh-cascader-row-max-w, 25rem);
371
+ max-inline-size: var(--xh-cascader-row-max-w, var(--xh-overlay-max-w));
335
372
  padding-block: var(--xh-cascader-row-py, var(--xh-space-1_5));
336
373
  padding-inline: var(--xh-cascader-row-px, var(--xh-control-px-md));
337
374
  border-radius: var(--xh-cascader-row-radius, var(--xh-shape-control));
@@ -361,14 +398,14 @@
361
398
  }
362
399
 
363
400
  /* 必须排在轻档之后:锚点走到路径上时同特指度靠书写顺序压过轻档 */
364
- [data-scope='cascader'][data-part='item']:not([data-disabled])[data-active] {
401
+ [data-scope='cascader'][data-part='item']:not([data-disabled])[data-in-path] {
365
402
  background: var(--xh-cascader-row-bg-active, var(--xh-_cascader-active-bg));
366
403
  font-weight: var(--xh-cascader-row-active-font-weight, var(--xh-font-weight-semibold));
367
404
  }
368
405
 
369
406
  /* 选中只换文字色与字重,配合行尾的 ✓ 标记;底色强档归展开路径、轻档归悬停,
370
407
  三者因此能同时看见 */
371
- [data-scope='cascader'][data-part='item'][data-selected] {
408
+ [data-scope='cascader']:is([data-part='item'], [data-part='search-item'])[data-state='checked'] {
372
409
  color: var(--xh-cascader-row-fg-selected, var(--xh-_cascader-selected-fg));
373
410
  font-weight: var(--xh-cascader-row-selected-font-weight, var(--xh-font-weight-medium));
374
411
  }
@@ -433,10 +470,17 @@
433
470
 
434
471
  /* 没写内容就由皮肤画勾;作者往部件里塞了自己的图形,这条规则即不命中 */
435
472
  [data-scope='cascader'][data-part='item-indicator']:empty::before {
436
- content: '';
473
+ content: '';
474
+ display: inline-block;
475
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
476
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
477
+ vertical-align: var(--xh-glyph-baseline-shift);
478
+ background-color: currentColor;
479
+ -webkit-mask: var(--xh-glyph-mark-check) center / contain no-repeat;
480
+ mask: var(--xh-glyph-mark-check) center / contain no-repeat;
437
481
  }
438
482
 
439
- [data-scope='cascader'][data-part='item-indicator'][data-selected] {
483
+ [data-scope='cascader'][data-part='item-indicator'][data-state='checked'] {
440
484
  visibility: visible;
441
485
  }
442
486
 
@@ -446,8 +490,15 @@
446
490
  visibility: visible;
447
491
  }
448
492
 
449
- [data-scope='cascader'][data-part='item-indicator'][data-indeterminate]:not([data-selected]):empty::before {
450
- content: '';
493
+ [data-scope='cascader'][data-part='item-indicator'][data-indeterminate]:not([data-state='checked']):empty::before {
494
+ content: '';
495
+ display: inline-block;
496
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
497
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
498
+ vertical-align: var(--xh-glyph-baseline-shift);
499
+ background-color: currentColor;
500
+ -webkit-mask: var(--xh-glyph-mark-minus) center / contain no-repeat;
501
+ mask: var(--xh-glyph-mark-minus) center / contain no-repeat;
451
502
  font-weight: var(--xh-font-weight-semibold);
452
503
  }
453
504
 
@@ -484,11 +535,12 @@
484
535
  display: none;
485
536
  }
486
537
 
538
+ /* 与列同高:搜索视图与浏览视图互换时浮层不改高度,候选多了在列表内自己滚 */
487
539
  [data-scope='cascader'][data-part='search-list'] {
488
540
  display: flex;
489
541
  flex-basis: 100%;
490
542
  flex-direction: column;
491
- max-block-size: var(--xh-cascader-search-max-h, 11.25rem);
543
+ block-size: var(--xh-cascader-column-h, var(--xh-viewport-h-sm));
492
544
  padding: var(--xh-cascader-search-p, var(--xh-space-1));
493
545
  overflow-y: auto;
494
546
  }
@@ -527,6 +579,12 @@
527
579
  background: var(--xh-cascader-row-bg-hover, var(--xh-bg-subtle));
528
580
  }
529
581
 
582
+ /* 焦点恒在输入框,环只跟虚拟高亮走,悬停不画;往内收,行是撑满列宽的,环画在外沿会被裁掉 */
583
+ [data-scope='cascader'][data-part='search-item']:not([data-disabled])[data-highlighted] {
584
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
585
+ outline-offset: calc(-1 * var(--xh-ring-width));
586
+ }
587
+
530
588
  [data-scope='cascader'][data-part='search-item'][data-disabled] {
531
589
  color: var(--xh-fg-disabled);
532
590
  cursor: not-allowed;
@@ -557,21 +615,44 @@
557
615
  [data-scope='cascader'][data-part='empty'][hidden] {
558
616
  display: none;
559
617
  }
618
+
619
+ /* 没写内容就由皮肤画兜底字形;作者往部件里塞了自己的图形,这几条即不命中 */
620
+ [data-scope='cascader'][data-part='clear-trigger']:empty::before {
621
+ content: '';
622
+ display: inline-block;
623
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
624
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
625
+ vertical-align: var(--xh-glyph-baseline-shift);
626
+ background-color: currentColor;
627
+ -webkit-mask: var(--xh-glyph-mark-close) center / contain no-repeat;
628
+ mask: var(--xh-glyph-mark-close) center / contain no-repeat;
629
+ }
560
630
  }
561
631
 
562
632
  @layer xihan.motion {
563
633
  /* 浮层是横向长出来的:淡入之外再沿书写方向起始缘推一小段,观感上像列被「拉开」。
564
634
  位移量从 content 上的私有槽取,rtl 侧翻符号后同一份关键帧两个方向都对。
565
635
  关键帧写在本文件里、名字带组件前缀,不动公共的那一份 */
566
- @keyframes xh-cascader-in {
636
+ @keyframes xh-pop-in {
567
637
  from {
568
638
  opacity: 0;
569
- translate: var(--xh-_cascader-slide, calc(-1 * var(--xh-motion-distance-sm))) 0;
639
+ scale: var(--xh-motion-scale-enter);
570
640
  }
571
641
 
572
642
  to {
573
643
  opacity: 1;
574
- translate: 0 0;
644
+ scale: 1;
645
+ }
646
+ }
647
+ @keyframes xh-pop-out {
648
+ from {
649
+ opacity: 1;
650
+ scale: 1;
651
+ }
652
+
653
+ to {
654
+ opacity: 0;
655
+ scale: var(--xh-motion-scale-enter);
575
656
  }
576
657
  }
577
658
  }