@xihan-ui/styles 1.0.0 → 2.0.0

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 (171) hide show
  1. package/CHANGELOG.md +5146 -6
  2. package/README.md +22 -2
  3. package/component-tokens.d.ts +4212 -0
  4. package/component-tokens.js +2 -0
  5. package/components.tokens.json +80116 -0
  6. package/css/accordion.css +131 -75
  7. package/css/affix.css +16 -1
  8. package/css/alert.css +114 -67
  9. package/css/anchor.css +99 -27
  10. package/css/approval.css +461 -0
  11. package/css/avatar-group.css +14 -6
  12. package/css/avatar.css +33 -9
  13. package/css/back-top.css +103 -48
  14. package/css/badge.css +42 -14
  15. package/css/bar-code.css +62 -0
  16. package/css/breadcrumb.css +40 -21
  17. package/css/button-group.css +146 -24
  18. package/css/button.css +116 -99
  19. package/css/calendar-picker.css +448 -0
  20. package/css/calendar-range-picker.css +517 -0
  21. package/css/card.css +54 -77
  22. package/css/carousel.css +412 -68
  23. package/css/cascader.css +429 -246
  24. package/css/checkbox-group.css +225 -77
  25. package/css/checkbox.css +174 -37
  26. package/css/clipboard.css +266 -47
  27. package/css/code-view.css +279 -0
  28. package/css/collapsible.css +108 -63
  29. package/css/color-field.css +158 -0
  30. package/css/color-picker.css +172 -184
  31. package/css/color-slider.css +274 -0
  32. package/css/color-swatch-picker.css +243 -0
  33. package/css/color-swatch.css +35 -0
  34. package/css/combobox.css +267 -273
  35. package/css/command.css +399 -0
  36. package/css/context-menu.css +183 -108
  37. package/css/date-field.css +94 -121
  38. package/css/date-picker.css +486 -249
  39. package/css/date-range-picker.css +559 -0
  40. package/css/description.css +51 -0
  41. package/css/descriptions.css +133 -87
  42. package/css/dialog.css +247 -68
  43. package/css/diff-view.css +428 -0
  44. package/css/download-trigger.css +155 -46
  45. package/css/drawer.css +194 -111
  46. package/css/editable.css +172 -152
  47. package/css/empty-state.css +80 -11
  48. package/css/field-array.css +190 -0
  49. package/css/field.css +84 -31
  50. package/css/fieldset.css +51 -18
  51. package/css/file-upload.css +134 -36
  52. package/css/flex.css +20 -1
  53. package/css/float-button.css +238 -57
  54. package/css/floating-panel.css +173 -92
  55. package/css/focus.css +46 -0
  56. package/css/forced-colors.css +93 -0
  57. package/css/form.css +254 -77
  58. package/css/gradient-text.css +34 -10
  59. package/css/grid.css +516 -5
  60. package/css/heatmap.css +87 -29
  61. package/css/highlight.css +20 -5
  62. package/css/hover-card.css +97 -47
  63. package/css/icon-wrapper.css +15 -3
  64. package/css/icon.css +74 -8
  65. package/css/image-cropper.css +143 -29
  66. package/css/image-viewer.css +150 -89
  67. package/css/image.css +30 -1
  68. package/css/infinite-scroll.css +51 -1
  69. package/css/input-group.css +277 -0
  70. package/css/json-viewer.css +102 -45
  71. package/css/kbd.css +72 -0
  72. package/css/label.css +78 -0
  73. package/css/layers.css +5 -0
  74. package/css/layout.css +175 -31
  75. package/css/list.css +24 -3
  76. package/css/listbox.css +208 -67
  77. package/css/loading-bar.css +52 -3
  78. package/css/log.css +144 -9
  79. package/css/markdown-stream.css +149 -0
  80. package/css/marquee.css +27 -1
  81. package/css/masonry.css +7 -0
  82. package/css/matrix-code.css +67 -0
  83. package/css/mention.css +234 -152
  84. package/css/menu.css +180 -83
  85. package/css/menubar.css +204 -136
  86. package/css/message-feed.css +197 -0
  87. package/css/navigation-menu.css +144 -90
  88. package/css/notification.css +178 -100
  89. package/css/number-animation.css +7 -0
  90. package/css/number-field.css +120 -277
  91. package/css/overlay-arrow.css +12 -7
  92. package/css/page-header.css +99 -14
  93. package/css/pagination.css +175 -135
  94. package/css/password-input.css +222 -219
  95. package/css/pin-input.css +101 -67
  96. package/css/pointer.css +36 -0
  97. package/css/popconfirm.css +185 -94
  98. package/css/popover.css +121 -77
  99. package/css/progress.css +60 -12
  100. package/css/prompt-input.css +224 -0
  101. package/css/question-flow.css +513 -0
  102. package/css/radio-group.css +147 -30
  103. package/css/rating.css +193 -43
  104. package/css/reasoning.css +336 -0
  105. package/css/reset.css +84 -6
  106. package/css/resizable.css +237 -0
  107. package/css/scroll-area.css +68 -7
  108. package/css/scrollbar.css +47 -13
  109. package/css/segmented.css +94 -29
  110. package/css/select.css +288 -274
  111. package/css/separator.css +101 -1
  112. package/css/side-nav.css +133 -75
  113. package/css/signature-pad.css +74 -54
  114. package/css/skeleton.css +84 -11
  115. package/css/slider.css +132 -39
  116. package/css/sortable.css +198 -0
  117. package/css/spinner.css +126 -12
  118. package/css/splitter.css +29 -8
  119. package/css/statistic.css +55 -0
  120. package/css/steps.css +206 -72
  121. package/css/switch.css +268 -36
  122. package/css/table.css +695 -118
  123. package/css/tabs.css +604 -42
  124. package/css/tag-group.css +219 -0
  125. package/css/tag.css +118 -28
  126. package/css/tags-input.css +156 -274
  127. package/css/text-field.css +101 -249
  128. package/css/time-field.css +100 -128
  129. package/css/time-picker.css +323 -252
  130. package/css/time-range-picker.css +719 -0
  131. package/css/timeline.css +202 -68
  132. package/css/timer.css +56 -52
  133. package/css/timestamp.css +34 -0
  134. package/css/toast.css +373 -144
  135. package/css/toggle-group.css +309 -76
  136. package/css/toggle.css +116 -85
  137. package/css/tone.css +47 -0
  138. package/css/tool-call.css +421 -0
  139. package/css/toolbar.css +181 -33
  140. package/css/tooltip.css +107 -36
  141. package/css/tour.css +183 -152
  142. package/css/transfer.css +351 -138
  143. package/css/tree-select.css +323 -216
  144. package/css/tree.css +294 -62
  145. package/css/truncate.css +65 -0
  146. package/css/typography.css +228 -5
  147. package/css/undefined.css +10 -1
  148. package/css/virtualizer.css +9 -10
  149. package/css/visually-hidden.css +7 -0
  150. package/css/watermark.css +7 -0
  151. package/family/action-control.css +487 -0
  152. package/family/collection-item.css +721 -0
  153. package/family/field-chrome.css +298 -0
  154. package/family/motion.css +166 -0
  155. package/family/swatch.css +59 -0
  156. package/index.css +41705 -126
  157. package/index.unlayered.css +33692 -17339
  158. package/package.json +51 -17
  159. package/css/calendar.css +0 -367
  160. package/css/code-block.css +0 -91
  161. package/css/composer.css +0 -134
  162. package/css/countdown.css +0 -26
  163. package/css/dynamic-input.css +0 -220
  164. package/css/ellipsis.css +0 -52
  165. package/css/hotkeys.css +0 -90
  166. package/css/popselect.css +0 -391
  167. package/css/qr-code.css +0 -55
  168. package/css/result.css +0 -119
  169. package/css/space.css +0 -117
  170. package/css/thread.css +0 -120
  171. package/css/time.css +0 -25
package/css/drawer.css CHANGED
@@ -1,26 +1,15 @@
1
- @layer xihan.motion {
2
- /* 动画名的查找只认「文档里有没有这个名字」:引用别处文件里的名字,那份文件不一定在场,
3
- 动画会静默地整个不跑。每份皮肤都能单独引入,所以它用到的关键帧都写在自己身上。 */
4
- @keyframes xh-fade-in {
5
- from {
6
- opacity: 0;
7
- }
1
+ /*
2
+ * Copyright (c) 2021-Present XiHanFun and contributors.
3
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
4
+ */
8
5
 
9
- to {
10
- opacity: 1;
11
- }
12
- }
6
+ /* 定义 drawer 样式。 */
13
7
 
14
- @keyframes xh-fade-out {
15
- from {
16
- opacity: 1;
17
- }
18
-
19
- to {
20
- opacity: 0;
21
- }
22
- }
8
+ /* 共享关键帧由 family/motion.css 提供,本皮肤 @import 它,单独引入仍成立。 */
9
+ @import '../family/motion.css';
10
+ @import '../family/action-control.css';
23
11
 
12
+ @layer xihan.motion {
24
13
  /* 四条边各一对滑入/滑出。位移量用 translate 的百分比,参照的是面板自己的边框盒,
25
14
  所以不管 --xh-drawer-size 被改成多少,"正好推到视口外"这件事都成立。
26
15
 
@@ -118,16 +107,16 @@
118
107
  /* root 留在页面原地,只是 data-side / data-state 的落点,不该凭空多出一层盒子把
119
108
  trigger 从作者的 flex / grid 行里挤出去,故 display: contents。 */
120
109
  [data-scope='drawer'][data-part='root'] {
121
- /* 作者塞进指示符槽的图标跟着字号走,与皮肤画的兜底字形同一把尺 */
122
- --xh-icon-size: var(--xh-drawer-icon-size, var(--xh-glyph-size-text));
110
+ /* 作者塞进面板里的图标按控件档取(§6.5);触发器与关闭钮另由家族配方按各自的档下发字形尺 */
111
+ --xh-icon-size: var(--xh-drawer-icon-size, var(--xh-glyph-size-md));
123
112
 
124
113
  display: contents;
125
114
  }
126
115
 
127
116
  /* 浮层被搬到 portal 落点,继承不到 root 上的槽:这把尺在被搬走的那一侧再声明一次,
128
- 关闭钮与条目里作者塞的图标才和皮肤画的兜底字形一样大 */
117
+ 作者塞进面板里的图标才和皮肤画的兜底字形一样大 */
129
118
  [data-scope='drawer'][data-part='content'] {
130
- --xh-icon-size: var(--xh-drawer-icon-size, var(--xh-glyph-size-text));
119
+ --xh-icon-size: var(--xh-drawer-icon-size, var(--xh-glyph-size-md));
131
120
  }
132
121
 
133
122
  /* 声明了 display 就盖掉了 UA 的 [hidden]{display:none},作者手写 hidden 时得还回去 */
@@ -135,58 +124,59 @@
135
124
  display: none;
136
125
  }
137
126
 
127
+ /*
128
+ * 触发器是页面上的独立文字按钮(§9.1):盒型、md 档几何、outline 四态面、0.97 按压并换底与粗指针命中区
129
+ * 由家族配方按连接层投影的 data-xh-action-variant / -size 给出(此前自写描边盒型,hover 落 200 / active 落 300)。
130
+ * 这里只把公开槽桥接到矩阵之前(使用者槽 → 形态矩阵 → 家族缺省)与几何槽映射。
131
+ */
138
132
  [data-scope='drawer'][data-part='trigger'] {
139
- display: inline-flex;
140
- align-items: center;
141
- justify-content: center;
142
- gap: var(--xh-drawer-trigger-gap, var(--xh-control-gap-md));
143
- block-size: var(--xh-drawer-trigger-h, var(--xh-control-h-md));
144
- padding-inline: var(--xh-drawer-trigger-px, var(--xh-control-px-md));
145
- border: var(--xh-stroke-thin) solid var(--xh-drawer-trigger-border, var(--xh-border-control));
146
- border-radius: var(--xh-drawer-trigger-radius, var(--xh-shape-control));
147
- background: var(--xh-drawer-trigger-bg, var(--xh-bg-canvas));
148
- color: var(--xh-drawer-trigger-fg, var(--xh-fg-default));
149
- font-size: var(--xh-drawer-trigger-font-size, var(--xh-text-label-size));
133
+ --xh-action-gap: var(--xh-drawer-trigger-gap, var(--xh-_action-profile-gap));
134
+ --xh-action-visual-size: var(--xh-drawer-trigger-h, var(--xh-_action-profile-visual-size));
135
+ --xh-action-padding-inline: var(--xh-drawer-trigger-px, var(--xh-_action-profile-padding-inline));
136
+ --xh-action-radius: var(--xh-drawer-trigger-radius, var(--xh-shape-control));
137
+ --xh-action-font-size: var(--xh-drawer-trigger-font-size, var(--xh-_action-profile-font-size));
138
+ --xh-action-bg-rest: var(--xh-drawer-trigger-bg, var(--xh-_action-variant-bg-rest));
139
+ --xh-action-bg-hover: var(--xh-drawer-trigger-bg-hover, var(--xh-_action-variant-bg-hover));
140
+ --xh-action-bg-pressed: var(--xh-drawer-trigger-bg-active, var(--xh-_action-variant-bg-pressed));
141
+ --xh-action-bg-focus-visible: var(--xh-drawer-trigger-bg, var(--xh-_action-variant-bg-focus-visible));
142
+ --xh-action-fg-rest: var(--xh-drawer-trigger-fg, var(--xh-_action-variant-fg-rest));
143
+ --xh-action-fg-hover: var(--xh-drawer-trigger-fg, var(--xh-_action-variant-fg-hover));
144
+ --xh-action-fg-pressed: var(--xh-drawer-trigger-fg, var(--xh-_action-variant-fg-pressed));
145
+ --xh-action-fg-focus-visible: var(--xh-drawer-trigger-fg, var(--xh-_action-variant-fg-focus-visible));
146
+ --xh-action-border-rest: var(--xh-drawer-trigger-border, var(--xh-_action-variant-border-rest));
147
+ --xh-action-border-hover: var(--xh-drawer-trigger-border-hover, var(--xh-_action-variant-border-hover));
148
+ --xh-action-border-pressed: var(--xh-drawer-trigger-border-hover, var(--xh-_action-variant-border-pressed));
149
+ --xh-action-border-focus-visible: var(--xh-drawer-trigger-border, var(--xh-_action-variant-border-focus-visible));
150
+ /* 作者塞进按钮的图标随档取字形尺(md = 20px) */
151
+ --xh-icon-size: var(--xh-drawer-icon-size, var(--xh-_action-profile-glyph-size));
152
+
153
+ /* 触发器的文字与 Button 同一字重(家族配方不管字重) */
150
154
  font-weight: var(--xh-drawer-trigger-font-weight, var(--xh-text-label-weight));
151
- line-height: var(--xh-leading-none);
152
- white-space: nowrap;
153
- cursor: pointer;
154
- transition:
155
- background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
156
- border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
157
155
  }
158
156
 
159
157
  [data-scope='drawer'][data-part='trigger'][hidden] {
160
158
  display: none;
161
159
  }
162
160
 
163
- [data-scope='drawer'][data-part='trigger']:hover:not(:disabled) {
164
- background: var(--xh-drawer-trigger-bg-hover, var(--xh-bg-subtle-hover));
165
- border-color: var(--xh-drawer-trigger-border-hover, var(--xh-border-control-hover));
166
- }
167
-
168
161
  /* 抽屉展开期间触发按钮压住不弹起:面板贴在视口边上,它和按钮往往离得很远,
169
- 按钮自己得说明"这一扇是我开的"。 */
162
+ 按钮自己得说明"这一扇是我开的"。open 与家族 hover 同档中性(§7.2 第 8 条):
163
+ 白底承载的 --xh-bg-subtle(100)+ 悬停描边,聚焦时这块面留着 */
170
164
  [data-scope='drawer'][data-part='trigger'][data-state='open'] {
171
- background: var(--xh-drawer-trigger-bg-open, var(--xh-bg-subtle-active));
172
- border-color: var(--xh-drawer-trigger-border-open, var(--xh-border-control-hover));
173
- }
174
-
175
- [data-scope='drawer'][data-part='trigger']:focus-visible {
176
- outline: var(--xh-ring-width) solid var(--xh-ring-focus);
177
- outline-offset: var(--xh-ring-offset);
165
+ --xh-action-bg-rest: var(--xh-drawer-trigger-bg-open, var(--xh-bg-subtle));
166
+ --xh-action-bg-focus-visible: var(--xh-drawer-trigger-bg-open, var(--xh-bg-subtle));
167
+ --xh-action-border-rest: var(--xh-drawer-trigger-border-open, var(--xh-border-control-hover));
168
+ --xh-action-border-focus-visible: var(--xh-drawer-trigger-border-open, var(--xh-border-control-hover));
178
169
  }
179
170
 
180
- /* trigger 是单体控件,禁用走原生 disabled */
181
- [data-scope='drawer'][data-part='trigger']:disabled {
182
- color: var(--xh-fg-disabled);
183
- cursor: not-allowed;
171
+ /* 面是实心的,环色与面同族,画在面里就看不出来;环改取面自己的前景色 */
172
+ [data-scope='drawer'][data-part='trigger'][data-xh-action-variant='solid']:focus-visible {
173
+ --xh-action-ring-color-focus-visible: currentColor;
184
174
  }
185
175
 
186
176
  [data-scope='drawer'][data-part='backdrop'] {
187
177
  position: fixed;
188
178
  inset: 0;
189
- z-index: var(--xh-drawer-backdrop-layer, var(--xh-layer-drawer));
179
+ z-index: var(--xh-drawer-backdrop-layer, var(--xh-_layer, var(--xh-layer-drawer)));
190
180
  background: var(--xh-drawer-backdrop-bg, var(--xh-bg-overlay));
191
181
  }
192
182
 
@@ -201,6 +191,24 @@
201
191
  position: absolute;
202
192
  }
203
193
 
194
+ /* 挂在局部容器里的面板碰不到屏幕边,安全区那一段让位收回去:留着会在容器内白让出一条。 */
195
+ [data-scope='drawer'][data-part='content'][data-contained] {
196
+ padding-block-start: var(--xh-drawer-py, var(--xh-surface-py-md));
197
+ padding-block-end: var(--xh-drawer-py, var(--xh-surface-py-md));
198
+ padding-inline: var(--xh-drawer-px, var(--xh-surface-px-md));
199
+ }
200
+
201
+ /* 同一层底色之上再糊背后的页面;与 dialog 同写法,两个前缀都写 */
202
+ [data-scope='drawer'][data-part='backdrop'][data-variant='blur'] {
203
+ -webkit-backdrop-filter: blur(var(--xh-drawer-backdrop-blur, var(--xh-overlay-backdrop-blur)));
204
+ backdrop-filter: blur(var(--xh-drawer-backdrop-blur, var(--xh-overlay-backdrop-blur)));
205
+ }
206
+
207
+ /* 去掉底色,只留下这一层吃指针:交互外关闭与滚动锁定照旧 */
208
+ [data-scope='drawer'][data-part='backdrop'][data-variant='transparent'] {
209
+ background: transparent;
210
+ }
211
+
204
212
  [data-scope='drawer'][data-part='backdrop'][data-state='open'] {
205
213
  animation: xh-fade-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
206
214
  }
@@ -216,7 +224,7 @@
216
224
  [data-scope='drawer'][data-part='positioner'] {
217
225
  position: fixed;
218
226
  inset: 0;
219
- z-index: var(--xh-drawer-layer, var(--xh-layer-drawer));
227
+ z-index: var(--xh-drawer-layer, var(--xh-_layer, var(--xh-layer-drawer)));
220
228
  pointer-events: none;
221
229
  }
222
230
 
@@ -236,16 +244,30 @@
236
244
  --xh-_drawer-size: var(--xh-overlay-drawer-w-md);
237
245
 
238
246
  position: fixed;
239
- z-index: var(--xh-drawer-layer, var(--xh-layer-drawer));
247
+ z-index: var(--xh-drawer-layer, var(--xh-_layer, var(--xh-layer-drawer)));
240
248
  display: flex;
241
249
  flex-direction: column;
242
250
  gap: var(--xh-drawer-gap, var(--xh-stack-gap-md));
243
- padding-block: var(--xh-drawer-py, var(--xh-surface-py-md));
244
- padding-inline: var(--xh-drawer-px, var(--xh-surface-px-md));
245
- background: var(--xh-drawer-bg, var(--xh-bg-surface));
246
- color: var(--xh-drawer-fg, var(--xh-fg-default));
247
- box-shadow: var(--xh-drawer-shadow, var(--xh-elevation-sheet));
251
+ /* 面板贴死视口的某一条边、另一向满铺,内衬与安全区取大的一头:
252
+ 刘海、圆角与底部横条压住的那一段由系统给出,内衬比它窄时首尾两行会被切掉。
253
+ 桌面上 env() 恒为 0,取值与内衬槽完全一致。安全区只有物理方向的四个名字,
254
+ 行内轴上两侧同取较宽的那一段,RTL 下不会翻错边。 */
255
+ padding-block-start: max(var(--xh-drawer-py, var(--xh-surface-py-md)), env(safe-area-inset-top));
256
+ padding-block-end: max(var(--xh-drawer-py, var(--xh-surface-py-md)), env(safe-area-inset-bottom));
257
+ padding-inline: max(
258
+ var(--xh-drawer-px, var(--xh-surface-px-md)),
259
+ env(safe-area-inset-left),
260
+ env(safe-area-inset-right)
261
+ );
262
+ /* M4 sheet 三件套(§8.4):任何浮层不得只靠 box-shadow 分层,亮色下面板与页面同色,
263
+ 1px 描边才是它的边界;影只是加成 */
264
+ border: var(--xh-stroke-thin) solid var(--xh-drawer-border, var(--xh-material-elevated-border));
265
+ background: var(--xh-drawer-bg, var(--xh-material-elevated-bg));
266
+ color: var(--xh-drawer-fg, var(--xh-material-elevated-fg));
267
+ box-shadow: var(--xh-drawer-shadow, var(--xh-material-elevated-shadow));
268
+ /* 作者不用 header / body / footer 三段时它是唯一的滚动层:模态面滚到头不把页面一起带走(§6.6) */
248
269
  overflow: auto;
270
+ overscroll-behavior: contain;
249
271
  /* 尺寸是照视口那条边算出来的,padding 再往外顶就会把面板挤出屏幕、
250
272
  逼出一条横向滚动条。reset 层已有同款兜底,这里显式写一遍表明尺寸算法依赖它 */
251
273
  box-sizing: border-box;
@@ -298,14 +320,14 @@
298
320
  /* 只圆朝向页面那两个角:贴住视口的那条边圆角是看不见的,圆了反而露出底下的页面 */
299
321
  [data-scope='drawer'][data-part='content'][data-side='left'] {
300
322
  inset-inline-start: 0;
301
- border-start-end-radius: var(--xh-drawer-radius, var(--xh-shape-surface));
302
- border-end-end-radius: var(--xh-drawer-radius, var(--xh-shape-surface));
323
+ border-start-end-radius: var(--xh-drawer-radius, var(--xh-shape-overlay));
324
+ border-end-end-radius: var(--xh-drawer-radius, var(--xh-shape-overlay));
303
325
  }
304
326
 
305
327
  [data-scope='drawer'][data-part='content'][data-side='right'] {
306
328
  inset-inline-end: 0;
307
- border-start-start-radius: var(--xh-drawer-radius, var(--xh-shape-surface));
308
- border-end-start-radius: var(--xh-drawer-radius, var(--xh-shape-surface));
329
+ border-start-start-radius: var(--xh-drawer-radius, var(--xh-shape-overlay));
330
+ border-end-start-radius: var(--xh-drawer-radius, var(--xh-shape-overlay));
309
331
  }
310
332
 
311
333
  /* 上下:满宽、定高 */
@@ -318,20 +340,27 @@
318
340
 
319
341
  [data-scope='drawer'][data-part='content'][data-side='top'] {
320
342
  inset-block-start: 0;
321
- border-end-start-radius: var(--xh-drawer-radius, var(--xh-shape-surface));
322
- border-end-end-radius: var(--xh-drawer-radius, var(--xh-shape-surface));
343
+ border-end-start-radius: var(--xh-drawer-radius, var(--xh-shape-overlay));
344
+ border-end-end-radius: var(--xh-drawer-radius, var(--xh-shape-overlay));
323
345
  }
324
346
 
325
347
  [data-scope='drawer'][data-part='content'][data-side='bottom'] {
326
348
  inset-block-end: 0;
327
- border-start-start-radius: var(--xh-drawer-radius, var(--xh-shape-surface));
328
- border-start-end-radius: var(--xh-drawer-radius, var(--xh-shape-surface));
349
+ border-start-start-radius: var(--xh-drawer-radius, var(--xh-shape-overlay));
350
+ border-start-end-radius: var(--xh-drawer-radius, var(--xh-shape-overlay));
351
+ }
352
+
353
+ /* 展开期间面板整块推到一屏之外再推回来:只在开着的这一档挂,
354
+ 合成层随 data-state 一起收走,收起后不再占着 */
355
+ [data-scope='drawer'][data-part='content'][data-state='open'] {
356
+ will-change: translate;
329
357
  }
330
358
 
331
- /* 滑入/滑出按边配对。退场一律 forwards:presence 要等 animationend 才卸载,
359
+ /* 滑入/滑出按边配对。入场是整面板从画外推入的大尺度位移(§9.5):走 --xh-motion-duration-slide
360
+ 与 --xh-motion-ease-slide;退场仍走 exit 档。退场一律 forwards:presence 要等 animationend 才卸载,
332
361
  不定住终态,面板会在最后一帧弹回原位再消失。 */
333
362
  [data-scope='drawer'][data-part='content'][data-side='right'][data-state='open'] {
334
- animation: xh-drawer-in-right var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
363
+ animation: xh-drawer-in-right var(--xh-motion-duration-slide) var(--xh-motion-ease-slide);
335
364
  }
336
365
 
337
366
  [data-scope='drawer'][data-part='content'][data-side='right'][data-state='closed'] {
@@ -339,7 +368,7 @@
339
368
  }
340
369
 
341
370
  [data-scope='drawer'][data-part='content'][data-side='left'][data-state='open'] {
342
- animation: xh-drawer-in-left var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
371
+ animation: xh-drawer-in-left var(--xh-motion-duration-slide) var(--xh-motion-ease-slide);
343
372
  }
344
373
 
345
374
  [data-scope='drawer'][data-part='content'][data-side='left'][data-state='closed'] {
@@ -347,7 +376,7 @@
347
376
  }
348
377
 
349
378
  [data-scope='drawer'][data-part='content'][data-side='top'][data-state='open'] {
350
- animation: xh-drawer-in-top var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
379
+ animation: xh-drawer-in-top var(--xh-motion-duration-slide) var(--xh-motion-ease-slide);
351
380
  }
352
381
 
353
382
  [data-scope='drawer'][data-part='content'][data-side='top'][data-state='closed'] {
@@ -355,13 +384,66 @@
355
384
  }
356
385
 
357
386
  [data-scope='drawer'][data-part='content'][data-side='bottom'][data-state='open'] {
358
- animation: xh-drawer-in-bottom var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
387
+ animation: xh-drawer-in-bottom var(--xh-motion-duration-slide) var(--xh-motion-ease-slide);
359
388
  }
360
389
 
361
390
  [data-scope='drawer'][data-part='content'][data-side='bottom'][data-state='closed'] {
362
391
  animation: xh-drawer-out-bottom var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
363
392
  }
364
393
 
394
+ /* —— 面板三段 —— */
395
+ /* header / body / footer 把面板切成「头尾定在原处、正文自己滚」三段,
396
+ 与弹窗、浮动面板同一套角色划分:头尾不参与压缩,正文吃掉剩下的高度并自己滚。
397
+ 面板的内衬与安全区让位仍由 content 一层给出,三段只接手段与段之间那道缝;
398
+ 整块的滚动交还给正文段——两层都能滚时滚轮落在哪一层要看指针停在哪,
399
+ 贴边的头尾会跟着一起走。 */
400
+ [data-scope='drawer'][data-part='content']:has([data-scope='drawer'][data-part='body']) {
401
+ gap: 0;
402
+ overflow: hidden;
403
+ }
404
+
405
+ /* 头段:标题与说明纵向堆叠;不参与压缩,正文再长也压不扁它 */
406
+ [data-scope='drawer'][data-part='header'] {
407
+ flex: none;
408
+ display: flex;
409
+ flex-direction: column;
410
+ gap: var(--xh-drawer-header-gap, var(--xh-stack-gap-sm));
411
+ padding-block-end: var(--xh-drawer-header-pb, var(--xh-space-2));
412
+ }
413
+
414
+ /* 正文段:面板里唯一会滚的一段。最小高度归零,
415
+ 否则弹性项按内容撑高,容器再矮也不产生溢出,滚动条永远出不来。
416
+ 模态 body 是原生细条滚动面(§6.6):细条由 reset 层统一给,滚到头不把页面一起带走 */
417
+ [data-scope='drawer'][data-part='body'] {
418
+ flex: 1 1 auto;
419
+ min-block-size: 0;
420
+ overflow: auto;
421
+ overscroll-behavior: contain;
422
+ }
423
+
424
+ /* 正文高度随交互变化(表单校验、展开段落):还在用原生滚动条的正文留一条稳定空道,
425
+ 滚动条出现与消失时不推动文字。带 data-xh-scrollbar 的容器已经把原生条藏成零宽,排除掉 */
426
+ [data-scope='drawer'][data-part='body']:not([data-xh-scrollbar]) {
427
+ scrollbar-gutter: stable;
428
+ }
429
+
430
+ /* 尾段:动作按钮排一行靠尾;同样不参与压缩 */
431
+ [data-scope='drawer'][data-part='footer'] {
432
+ flex: none;
433
+ display: flex;
434
+ align-items: center;
435
+ justify-content: flex-end;
436
+ gap: var(--xh-drawer-footer-gap, var(--xh-control-gap-md));
437
+ padding-block-start: var(--xh-drawer-footer-pt, var(--xh-space-2));
438
+ }
439
+
440
+ /* 声明过 display 就盖掉了 UA 的 [hidden]{display:none},收起态得自己还回去 */
441
+ [data-scope='drawer'][data-part='header'][hidden],
442
+ [data-scope='drawer'][data-part='body'][hidden],
443
+ [data-scope='drawer'][data-part='footer'][hidden] {
444
+ display: none;
445
+ }
446
+
365
447
  [data-scope='drawer'][data-part='title'] {
366
448
  margin: 0;
367
449
  font-size: var(--xh-drawer-title-font-size, var(--xh-text-heading-3-size));
@@ -369,9 +451,10 @@
369
451
  color: var(--xh-drawer-title-fg, var(--xh-fg-default));
370
452
  }
371
453
 
454
+ /* 说明文字走说明档(§6.4):13px + fg-muted */
372
455
  [data-scope='drawer'][data-part='description'] {
373
456
  margin: 0;
374
- font-size: var(--xh-text-body-size);
457
+ font-size: var(--xh-drawer-description-font-size, var(--xh-text-secondary-size));
375
458
  color: var(--xh-drawer-description-fg, var(--xh-fg-muted));
376
459
  }
377
460
 
@@ -379,47 +462,38 @@
379
462
  否则长标题会直接钻到按钮底下。只在真有关闭按钮时让位。 */
380
463
  [data-scope='drawer'][data-part='content']:has([data-scope='drawer'][data-part='close-trigger'])
381
464
  [data-scope='drawer'][data-part='title'] {
382
- padding-inline-end: calc(var(--xh-drawer-close-size, var(--xh-control-h-sm)) + var(--xh-space-3));
465
+ padding-inline-end: calc(var(--xh-drawer-close-size, var(--xh-control-h-sm)) + var(--xh-surface-action-inset));
383
466
  }
384
467
 
468
+ /*
469
+ * 关闭钮是面板角落的单图标钮(§9.1):正方盒、sm 档几何、ghost 四态面、0.97 按压并换底与粗指针命中区
470
+ * 由家族配方给出——白面上走画布承载阶梯悬停 --xh-bg-subtle(100)→ 按下 --xh-bg-subtle-hover(200),
471
+ * 焦点面透明吃库环。这里只把公开槽桥接到矩阵之前(使用者槽 → 形态矩阵 → 家族缺省)与摆放,
472
+ * 聚焦档的 -bg-focus / -fg-focus 两槽同样桥到配方的 focus-visible 面(与 dialog 一致),缺省仍是透明底与悬停字色;
473
+ * 静息前景取 muted 字色,悬停 / 按下回到 fg-default。
474
+ */
385
475
  [data-scope='drawer'][data-part='close-trigger'] {
476
+ --xh-action-visual-size: var(--xh-drawer-close-size, var(--xh-_action-profile-visual-size));
477
+ --xh-action-radius: var(--xh-drawer-close-radius, var(--xh-shape-control));
478
+ --xh-action-bg-hover: var(--xh-drawer-close-bg-hover, var(--xh-_action-variant-bg-hover));
479
+ --xh-action-bg-pressed: var(--xh-drawer-close-bg-active, var(--xh-_action-variant-bg-pressed));
480
+ --xh-action-bg-focus-visible: var(--xh-drawer-close-bg-focus, var(--xh-_action-variant-bg-focus-visible));
481
+ --xh-action-fg-rest: var(--xh-drawer-close-fg, var(--xh-fg-muted));
482
+ --xh-action-fg-hover: var(--xh-drawer-close-fg-hover, var(--xh-_action-variant-fg-hover));
483
+ --xh-action-fg-pressed: var(--xh-drawer-close-fg-hover, var(--xh-_action-variant-fg-pressed));
484
+ --xh-action-fg-focus-visible: var(--xh-drawer-close-fg-focus, var(--xh-drawer-close-fg-hover, var(--xh-_action-variant-fg-focus-visible)));
485
+ /* 作者塞进按钮的图标随档取字形尺(sm = 16px),与皮肤画的兜底字形同一把尺 */
486
+ --xh-icon-size: var(--xh-drawer-icon-size, var(--xh-_action-profile-glyph-size));
487
+
386
488
  position: absolute;
387
- inset-block-start: var(--xh-space-3);
388
- inset-inline-end: var(--xh-space-3);
389
- display: inline-flex;
390
- align-items: center;
391
- justify-content: center;
392
- block-size: var(--xh-drawer-close-size, var(--xh-control-h-sm));
393
- inline-size: var(--xh-drawer-close-size, var(--xh-control-h-sm));
394
- border: 0;
395
- border-radius: var(--xh-drawer-close-radius, var(--xh-shape-control));
396
- background: transparent;
397
- color: var(--xh-fg-muted);
398
- cursor: pointer;
399
- transition:
400
- background-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
401
- scale var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
489
+ inset-block-start: var(--xh-surface-action-inset);
490
+ inset-inline-end: var(--xh-surface-action-inset);
402
491
  }
403
492
 
404
493
  [data-scope='drawer'][data-part='close-trigger'][hidden] {
405
494
  display: none;
406
495
  }
407
496
 
408
- [data-scope='drawer'][data-part='close-trigger']:hover {
409
- background: var(--xh-drawer-close-bg-hover, var(--xh-bg-subtle-hover));
410
- color: var(--xh-fg-default);
411
- }
412
-
413
- [data-scope='drawer'][data-part='close-trigger']:active {
414
- background: var(--xh-drawer-close-bg-active, var(--xh-bg-subtle-active));
415
- scale: var(--xh-motion-scale-press);
416
- }
417
-
418
- [data-scope='drawer'][data-part='close-trigger']:focus-visible {
419
- outline: var(--xh-ring-width) solid var(--xh-ring-focus);
420
- outline-offset: var(--xh-ring-offset);
421
- }
422
-
423
497
  /* 没写内容就由皮肤画兜底字形;作者往部件里塞了自己的图形,这几条即不命中 */
424
498
  [data-scope='drawer'][data-part='close-trigger']:empty::before {
425
499
  content: '';
@@ -431,4 +505,13 @@
431
505
  -webkit-mask: var(--xh-glyph-mark-close) center / contain no-repeat;
432
506
  mask: var(--xh-glyph-mark-close) center / contain no-repeat;
433
507
  }
508
+
509
+ @media print {
510
+ /* 打印:定位层与遮罩是交互期才存在的东西,落到纸上只会盖住底下的正文。
511
+ 收起写在这份皮肤里而不是 reset:reset 层排在组件层之前,组件层里任何一条 display 都压得住它。 */
512
+ [data-scope='drawer'][data-part='backdrop'],
513
+ [data-scope='drawer'][data-part='positioner'] {
514
+ display: none;
515
+ }
516
+ }
434
517
  }