@xihan-ui/styles 2.1.0 → 3.1.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 (152) hide show
  1. package/CHANGELOG.md +1261 -0
  2. package/README.md +6 -1
  3. package/component-tokens.d.ts +533 -96
  4. package/components.tokens.json +35461 -26045
  5. package/css/accordion.css +34 -8
  6. package/css/alert.css +59 -1
  7. package/css/anchor.css +46 -8
  8. package/css/approval.css +116 -76
  9. package/css/avatar-group.css +1 -1
  10. package/css/avatar.css +13 -5
  11. package/css/back-top.css +61 -42
  12. package/css/badge.css +62 -12
  13. package/css/bar-code.css +1 -1
  14. package/css/breadcrumb.css +62 -3
  15. package/css/button-group.css +4 -4
  16. package/css/button.css +67 -19
  17. package/css/calendar-picker.css +19 -0
  18. package/css/calendar-range-picker.css +39 -2
  19. package/css/card.css +115 -1
  20. package/css/carousel.css +148 -43
  21. package/css/cartesian-chart.css +830 -0
  22. package/css/cascader.css +181 -65
  23. package/css/checkbox-group.css +59 -89
  24. package/css/checkbox.css +16 -49
  25. package/css/citation.css +470 -0
  26. package/css/clipboard.css +20 -63
  27. package/css/code-view.css +101 -6
  28. package/css/collapsible.css +34 -8
  29. package/css/color-field.css +18 -0
  30. package/css/color-picker.css +128 -50
  31. package/css/color-slider.css +22 -15
  32. package/css/color-swatch-picker.css +3 -1
  33. package/css/combobox.css +80 -33
  34. package/css/command.css +45 -43
  35. package/css/context-menu.css +39 -22
  36. package/css/date-field.css +56 -0
  37. package/css/date-picker.css +132 -26
  38. package/css/date-range-picker.css +337 -14
  39. package/css/description.css +17 -1
  40. package/css/descriptions.css +42 -0
  41. package/css/dialog.css +94 -36
  42. package/css/diff-view.css +97 -12
  43. package/css/download-trigger.css +44 -78
  44. package/css/drawer.css +160 -119
  45. package/css/editable.css +19 -0
  46. package/css/empty-state.css +7 -6
  47. package/css/field-array.css +36 -0
  48. package/css/field.css +20 -18
  49. package/css/file-upload.css +125 -135
  50. package/css/flex.css +324 -8
  51. package/css/float-button.css +263 -38
  52. package/css/floating-panel.css +53 -19
  53. package/css/form.css +31 -17
  54. package/css/funnel-chart.css +106 -0
  55. package/css/graph-chart.css +205 -0
  56. package/css/grid-list.css +235 -0
  57. package/css/grid.css +252 -1
  58. package/css/heatmap.css +119 -36
  59. package/css/hierarchy-chart.css +298 -0
  60. package/css/hover-card.css +31 -14
  61. package/css/icon.css +100 -0
  62. package/css/image-cropper.css +33 -7
  63. package/css/image-viewer.css +167 -22
  64. package/css/image.css +23 -4
  65. package/css/input-group.css +11 -13
  66. package/css/json-viewer.css +50 -2
  67. package/css/layout.css +68 -18
  68. package/css/liquid.css +106 -0
  69. package/css/list.css +1 -1
  70. package/css/listbox.css +46 -6
  71. package/css/loading-bar.css +27 -13
  72. package/css/log.css +129 -33
  73. package/css/markdown-stream.css +1 -1
  74. package/css/marquee.css +163 -7
  75. package/css/matrix-code.css +1 -1
  76. package/css/mention.css +35 -19
  77. package/css/menu.css +35 -21
  78. package/css/menubar.css +36 -20
  79. package/css/message-feed.css +172 -51
  80. package/css/navigation-menu.css +172 -11
  81. package/css/notification.css +435 -108
  82. package/css/number-field.css +22 -0
  83. package/css/page-header.css +3 -1
  84. package/css/pagination.css +126 -24
  85. package/css/password-input.css +40 -8
  86. package/css/pie-chart.css +247 -0
  87. package/css/popconfirm.css +44 -50
  88. package/css/popover.css +50 -14
  89. package/css/progress.css +308 -10
  90. package/css/prompt-input.css +36 -0
  91. package/css/question-flow.css +97 -57
  92. package/css/radar-chart.css +233 -0
  93. package/css/radio-group.css +363 -5
  94. package/css/rating.css +21 -23
  95. package/css/reasoning.css +61 -54
  96. package/css/resizable.css +6 -1
  97. package/css/sankey-chart.css +174 -0
  98. package/css/scroll-area.css +32 -0
  99. package/css/scrollbar.css +1 -1
  100. package/css/select.css +74 -55
  101. package/css/side-nav.css +173 -24
  102. package/css/signature-pad.css +16 -1
  103. package/css/skeleton.css +13 -14
  104. package/css/slider.css +35 -13
  105. package/css/sortable.css +37 -7
  106. package/css/sparkline.css +164 -0
  107. package/css/spinner.css +50 -22
  108. package/css/splitter.css +7 -1
  109. package/css/statistic.css +10 -0
  110. package/css/steps.css +248 -29
  111. package/css/switch.css +23 -48
  112. package/css/table.css +175 -118
  113. package/css/tabs.css +231 -19
  114. package/css/tag-group.css +27 -3
  115. package/css/tag.css +53 -12
  116. package/css/tags-input.css +40 -3
  117. package/css/text-field.css +19 -0
  118. package/css/time-field.css +20 -1
  119. package/css/time-picker.css +103 -17
  120. package/css/time-range-picker.css +45 -16
  121. package/css/timeline.css +1 -1
  122. package/css/toggle-group.css +4 -4
  123. package/css/toggle.css +4 -3
  124. package/css/tool-call.css +59 -51
  125. package/css/toolbar.css +101 -3
  126. package/css/tooltip.css +15 -2
  127. package/css/tour.css +42 -12
  128. package/css/transfer.css +98 -75
  129. package/css/tree-select.css +232 -53
  130. package/css/tree.css +271 -55
  131. package/css/truncate.css +55 -12
  132. package/css/typography.css +93 -0
  133. package/css/virtualizer.css +56 -0
  134. package/css/watermark.css +23 -0
  135. package/family/action-control.css +14 -5
  136. package/family/chart.css +480 -0
  137. package/family/check-mark.css +94 -0
  138. package/family/choice-card.css +97 -0
  139. package/family/collection-item.css +9 -15
  140. package/family/field-chrome.css +1 -12
  141. package/family/loading-ring.css +95 -0
  142. package/family/material.css +72 -0
  143. package/family/menu-choice.css +56 -0
  144. package/family/motion.css +207 -10
  145. package/family/tag-list.css +59 -0
  146. package/index.css +13824 -4030
  147. package/index.unlayered.css +13403 -3605
  148. package/package.json +20 -8
  149. package/css/gradient-text.css +0 -91
  150. package/css/icon-wrapper.css +0 -81
  151. package/css/segmented.css +0 -280
  152. package/css/toast.css +0 -499
@@ -9,6 +9,7 @@
9
9
 
10
10
  /* 共享关键帧由 family/motion.css 提供,本皮肤 @import 它,单独引入仍成立。 */
11
11
  @import '../family/motion.css';
12
+ @import '../family/material.css';
12
13
 
13
14
  @layer xihan.components {
14
15
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
@@ -65,6 +66,104 @@
65
66
  inset-inline-end: max(var(--xh-_float-button-offset), env(safe-area-inset-left), env(safe-area-inset-right));
66
67
  }
67
68
 
69
+ /* —— 位置 ——
70
+ 拖到别处、或作者给了位置时,角落那两条贴边里有一条换掉:data-placement 由连接层按位置推出
71
+ (离触发器最近的恒是视口的那条边、展开组朝页面中间长),这里只换沿边的那一条。
72
+ 百分比按固定定位的包含块(视口去掉滚动条)量,与连接层量的视口同一块;
73
+ 夹取的上下界与四角同样和安全区取大的一头,比例再怎么写,钮也不会压进刘海与横条。
74
+ 触发器边长取它自己那一档(使用者槽在前):沿边的比例量的是触发器中心 */
75
+ [data-scope='float-button'][data-part='root'] {
76
+ --xh-_float-button-span: var(--xh-float-button-size, var(--xh-_float-button-size));
77
+ }
78
+
79
+ /* 贴左右边:上下按比例,锚在离触发器近的那一头 */
80
+ [data-scope='float-button'][data-part='root'][data-edge^='inline'][data-placement^='top'] {
81
+ inset-block-start: clamp(
82
+ max(var(--xh-_float-button-offset), env(safe-area-inset-top)),
83
+ calc(var(--xh-_float-button-ratio) * 100% - var(--xh-_float-button-span) / 2),
84
+ calc(100% - var(--xh-_float-button-span) - max(var(--xh-_float-button-offset), env(safe-area-inset-bottom)))
85
+ );
86
+ }
87
+
88
+ [data-scope='float-button'][data-part='root'][data-edge^='inline'][data-placement^='bottom'] {
89
+ inset-block-end: clamp(
90
+ max(var(--xh-_float-button-offset), env(safe-area-inset-bottom)),
91
+ calc((1 - var(--xh-_float-button-ratio)) * 100% - var(--xh-_float-button-span) / 2),
92
+ calc(100% - var(--xh-_float-button-span) - max(var(--xh-_float-button-offset), env(safe-area-inset-top)))
93
+ );
94
+ }
95
+
96
+ /* 贴上下边:行内方向按比例,从行首量起 */
97
+ [data-scope='float-button'][data-part='root'][data-edge^='block'][data-placement$='-start'] {
98
+ inset-inline-start: clamp(
99
+ max(var(--xh-_float-button-offset), env(safe-area-inset-left), env(safe-area-inset-right)),
100
+ calc(var(--xh-_float-button-ratio) * 100% - var(--xh-_float-button-span) / 2),
101
+ calc(100% - var(--xh-_float-button-span) - max(var(--xh-_float-button-offset), env(safe-area-inset-left), env(safe-area-inset-right)))
102
+ );
103
+ }
104
+
105
+ [data-scope='float-button'][data-part='root'][data-edge^='block'][data-placement$='-end'] {
106
+ inset-inline-end: clamp(
107
+ max(var(--xh-_float-button-offset), env(safe-area-inset-left), env(safe-area-inset-right)),
108
+ calc((1 - var(--xh-_float-button-ratio)) * 100% - var(--xh-_float-button-span) / 2),
109
+ calc(100% - var(--xh-_float-button-span) - max(var(--xh-_float-button-offset), env(safe-area-inset-left), env(safe-area-inset-right)))
110
+ );
111
+ }
112
+
113
+ /* 停在一点、或正跟着指针走:坐标是触发器左上角的物理像素,横向一律从左边量,不随书写方向翻。
114
+ 竖向锚在离触发器近的那一头:朝下长的锚上沿,朝上长的锚下沿,展开组冒出来时触发器不挪窝。
115
+ 停着的点仍夹在视口里(视口变小时不跑出去);跟手的坐标由连接层夹好,这里原样用 */
116
+ [data-scope='float-button'][data-part='root'][data-point][data-placement] {
117
+ inset-inline: auto;
118
+ /* stylelint-disable-next-line property-disallowed-list -- 停点由连接层按指针的物理坐标写成 x,横向从左边量、不随书写方向翻 */
119
+ left: clamp(
120
+ max(var(--xh-_float-button-offset), env(safe-area-inset-left)),
121
+ var(--xh-_float-button-x),
122
+ calc(100% - var(--xh-_float-button-span) - max(var(--xh-_float-button-offset), env(safe-area-inset-right)))
123
+ );
124
+ }
125
+
126
+ [data-scope='float-button'][data-part='root'][data-point][data-placement^='top'] {
127
+ top: clamp(
128
+ max(var(--xh-_float-button-offset), env(safe-area-inset-top)),
129
+ var(--xh-_float-button-y),
130
+ calc(100% - var(--xh-_float-button-span) - max(var(--xh-_float-button-offset), env(safe-area-inset-bottom)))
131
+ );
132
+ }
133
+
134
+ [data-scope='float-button'][data-part='root'][data-point][data-placement^='bottom'] {
135
+ bottom: clamp(
136
+ max(var(--xh-_float-button-offset), env(safe-area-inset-bottom)),
137
+ calc(100% - var(--xh-_float-button-y) - var(--xh-_float-button-span)),
138
+ calc(100% - var(--xh-_float-button-span) - max(var(--xh-_float-button-offset), env(safe-area-inset-top)))
139
+ );
140
+ }
141
+
142
+ /* 跟手排在贴边与停点之后:拖动与落定途中压过提交了的位置 */
143
+ [data-scope='float-button'][data-part='root'][data-moving][data-placement] {
144
+ inset-inline: auto;
145
+ /* stylelint-disable-next-line property-disallowed-list -- 同上:跟手坐标是指针的物理 x */
146
+ left: var(--xh-_float-button-x);
147
+ }
148
+
149
+ [data-scope='float-button'][data-part='root'][data-moving][data-placement^='top'] {
150
+ top: var(--xh-_float-button-y);
151
+ }
152
+
153
+ [data-scope='float-button'][data-part='root'][data-moving][data-placement^='bottom'] {
154
+ bottom: calc(100% - var(--xh-_float-button-y) - var(--xh-_float-button-span));
155
+ }
156
+
157
+ /* 能拖时触屏手指在触发器上的移动归拖动,不让浏览器拿去滚页面;光标提示能抓、正抓着 */
158
+ [data-scope='float-button'][data-part='root'][data-draggable] [data-scope='float-button'][data-part='trigger'] {
159
+ touch-action: none;
160
+ cursor: grab;
161
+ }
162
+
163
+ [data-scope='float-button'][data-part='root'][data-dragging] [data-scope='float-button'][data-part='trigger'] {
164
+ cursor: grabbing;
165
+ }
166
+
68
167
  /* —— 尺寸:原生动作项与触发器的 floating 档逐档同表 —— */
69
168
  [data-scope='float-button'][data-part='root'][data-size='sm'] {
70
169
  --xh-_float-button-size: var(--xh-control-box-md);
@@ -97,10 +196,11 @@
97
196
  --xh-_float-button-shadow: none;
98
197
  }
99
198
 
199
+ /* 浮动钮钉在内容之上,面要盖得住底下的内容:没写语气时淡底取不透明档(ghost 静息就透景,不在此列) */
100
200
  [data-scope='float-button'][data-part='root'][data-variant='subtle'] {
101
- --xh-_float-button-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
102
- --xh-_float-button-bg-hover: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover));
103
- --xh-_float-button-bg-active: var(--xh-_tone-subtle-active, var(--xh-bg-subtle-active));
201
+ --xh-_float-button-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle-opaque));
202
+ --xh-_float-button-bg-hover: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover-opaque));
203
+ --xh-_float-button-bg-active: var(--xh-_tone-subtle-active, var(--xh-bg-subtle-active-opaque));
104
204
  --xh-_float-button-fg: var(--xh-_tone-fg, var(--xh-fg-default));
105
205
  --xh-_float-button-border: transparent;
106
206
  --xh-_float-button-border-hover: transparent;
@@ -109,8 +209,8 @@
109
209
 
110
210
  [data-scope='float-button'][data-part='root'][data-variant='outline'] {
111
211
  --xh-_float-button-bg: var(--xh-material-frosted-bg);
112
- --xh-_float-button-bg-hover: var(--xh-_tone-subtle, var(--xh-bg-subtle));
113
- --xh-_float-button-bg-active: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover));
212
+ --xh-_float-button-bg-hover: var(--xh-_tone-subtle, var(--xh-bg-subtle-opaque));
213
+ --xh-_float-button-bg-active: var(--xh-_tone-subtle-hover, var(--xh-bg-subtle-hover-opaque));
114
214
  --xh-_float-button-fg: var(--xh-material-frosted-fg);
115
215
  --xh-_float-button-border: var(--xh-material-frosted-border);
116
216
  --xh-_float-button-border-hover: var(--xh-material-frosted-border);
@@ -158,38 +258,114 @@
158
258
  --xh-icon-size: var(--xh-float-button-icon-size, var(--xh-_action-profile-glyph-size));
159
259
  }
160
260
 
161
- /* 缺省 outline 是 M2 磨砂面:背景、边缘、顶光、投影与磨砂从同一份配方取。
162
- 悬停 / 按下的底走矩阵 outline 列的画布阶梯(100 → 200),边缘与投影不动;
163
- 键盘聚焦时铺配方的实体 focus surface——透明面上的内收环没有稳定的底可量对比;
164
- 置灰只换字色(矩阵 disabled 列的前景),面仍留着,透空的面在任何内容上都读不出边界 */
261
+ /* 缺省 outline 是 frosted 材质面:连接层只在这一档投影 data-xh-material,面由材质家族配方给——
262
+ 底、描边、投影、1px 顶光与背景滤镜一份取值,高对比、减少透明度、打印和强制色沿同一令牌通道降级。
263
+ 悬停 / 按下换不透明的淡底一档、二档,边缘与投影不动;键盘聚焦时铺实体 focus surface——透明面上的
264
+ 内收环不能依赖页面底色;置灰只换字色,面仍留着,透空的面在任何内容上都读不出边界 */
165
265
  [data-scope='float-button'][data-part='root'][data-variant='outline']
166
266
  [data-scope='float-button'][data-part='trigger'] {
167
- --xh-action-bg-rest: var(--xh-float-button-bg, var(--xh-_float-button-bg));
168
- --xh-action-bg-focus-visible: var(--xh-float-button-bg, var(--xh-material-frosted-focus-surface));
169
- --xh-action-bg-disabled: var(--xh-float-button-bg, var(--xh-_float-button-bg));
170
- --xh-action-fg-rest: var(--xh-float-button-fg, var(--xh-_float-button-fg));
171
- --xh-action-fg-hover: var(--xh-float-button-fg, var(--xh-_float-button-fg));
172
- --xh-action-fg-pressed: var(--xh-float-button-fg, var(--xh-_float-button-fg));
173
- --xh-action-fg-focus-visible: var(--xh-float-button-fg, var(--xh-_float-button-fg));
174
- --xh-action-border-rest: var(--xh-float-button-border, var(--xh-_float-button-border));
175
- --xh-action-border-hover: var(--xh-float-button-border-hover, var(--xh-_float-button-border-hover));
176
- --xh-action-border-pressed: var(--xh-float-button-border-hover, var(--xh-_float-button-border-hover));
177
- --xh-action-border-focus-visible: var(--xh-float-button-border, var(--xh-_float-button-border));
178
- --xh-action-border-disabled: var(--xh-float-button-border, var(--xh-_float-button-border));
179
- --xh-action-shadow-rest: var(--xh-float-button-shadow, var(--xh-_float-button-shadow));
180
- --xh-action-shadow-hover: var(--xh-float-button-shadow, var(--xh-_float-button-shadow));
181
- --xh-action-shadow-pressed: var(--xh-float-button-shadow, var(--xh-_float-button-shadow));
182
- --xh-action-shadow-focus-visible: var(--xh-float-button-shadow, var(--xh-_float-button-shadow));
183
- --xh-action-shadow-disabled: var(--xh-float-button-shadow, var(--xh-_float-button-shadow));
267
+ /* 面走材质家族配方(family/material.css):frosted 档的底、交互阶梯、描边、投影、1px 顶光与背景滤镜,
268
+ liquid 档由配方换成液态面;这里把使用者槽接到 Action Control 的桥接槽 */
269
+ --xh-action-bg-rest: var(--xh-float-button-bg, var(--xh-_material-bg));
270
+ --xh-action-bg-hover: var(--xh-float-button-bg-hover, var(--xh-_material-bg-hover));
271
+ --xh-action-bg-pressed: var(--xh-float-button-bg-active, var(--xh-_material-bg-pressed));
272
+ --xh-action-bg-focus-visible: var(--xh-float-button-bg, var(--xh-_material-bg-focus));
273
+ --xh-action-bg-disabled: var(--xh-float-button-bg, var(--xh-_material-bg));
274
+ --xh-action-fg-rest: var(--xh-float-button-fg, var(--xh-_material-fg));
275
+ --xh-action-fg-hover: var(--xh-float-button-fg, var(--xh-_material-fg));
276
+ --xh-action-fg-pressed: var(--xh-float-button-fg, var(--xh-_material-fg));
277
+ --xh-action-fg-focus-visible: var(--xh-float-button-fg, var(--xh-_material-fg));
278
+ --xh-action-border-rest: var(--xh-float-button-border, var(--xh-_material-border));
279
+ --xh-action-border-hover: var(--xh-float-button-border-hover, var(--xh-_material-border));
280
+ --xh-action-border-pressed: var(--xh-float-button-border-hover, var(--xh-_material-border));
281
+ --xh-action-border-focus-visible: var(--xh-float-button-border, var(--xh-_material-border));
282
+ --xh-action-border-disabled: var(--xh-float-button-border, var(--xh-_material-border));
283
+ --xh-action-shadow-rest: var(--xh-float-button-shadow, var(--xh-_material-shadow));
284
+ --xh-action-shadow-hover: var(--xh-float-button-shadow, var(--xh-_material-shadow));
285
+ --xh-action-shadow-pressed: var(--xh-float-button-shadow, var(--xh-_material-shadow));
286
+ --xh-action-shadow-focus-visible: var(--xh-float-button-shadow, var(--xh-_material-shadow));
287
+ --xh-action-shadow-disabled: var(--xh-float-button-shadow, var(--xh-_material-shadow));
288
+ --xh-action-highlight-rest: var(--xh-_material-highlight);
289
+ --xh-action-highlight-hover: var(--xh-_material-highlight);
290
+ --xh-action-highlight-pressed: var(--xh-_material-highlight);
291
+ --xh-action-highlight-focus-visible: var(--xh-_material-highlight);
292
+ --xh-action-highlight-disabled: var(--xh-_material-highlight);
184
293
  /* M2 顶光走配方的高光通道:实心、淡色与透空形态各有自身表面,不能把磨砂高光误铺上去 */
185
- --xh-action-highlight-rest: var(--xh-material-frosted-highlight);
186
- --xh-action-highlight-hover: var(--xh-material-frosted-highlight);
187
- --xh-action-highlight-pressed: var(--xh-material-frosted-highlight);
188
- --xh-action-highlight-focus-visible: var(--xh-material-frosted-highlight);
189
- --xh-action-highlight-disabled: var(--xh-material-frosted-highlight);
294
+ }
190
295
 
191
- -webkit-backdrop-filter: var(--xh-material-frosted-backdrop);
192
- backdrop-filter: var(--xh-material-frosted-backdrop);
296
+ /* 液态组:液态档下触发器与展开的动作结成一组(状态机挂进 core),共用宿主最前面那层色块,
297
+ 靠近时边缘像液体一样连起来。底色、墨色细线、亮边与投影都由色块层沿整组的外形画,块自己的面与细线
298
+ 换成透明、亮边与投影撤掉,只留磨砂;悬停 / 按下改成往前景色兑的一层薄色叠在色块上。
299
+ 只有缺省 outline 的液态面才结组,其余形态的面是实心的,色块层藏起来。强制色下色块层撤掉,块保留原样 */
300
+ @media (forced-colors: none) {
301
+ :where([data-material='liquid']) [data-scope='float-button'][data-part='root'][data-variant='outline'][data-xh-liquid-goo]
302
+ [data-scope='float-button'][data-part='trigger'][data-xh-liquid] {
303
+ --xh-action-bg-rest: var(--xh-float-button-bg, transparent);
304
+ --xh-action-bg-hover: var(--xh-float-button-bg-hover, color-mix(in oklab, var(--xh-material-liquid-fg) 8%, transparent));
305
+ --xh-action-bg-pressed: var(--xh-float-button-bg-active, color-mix(in oklab, var(--xh-material-liquid-fg) 14%, transparent));
306
+ --xh-action-bg-disabled: var(--xh-float-button-bg, transparent);
307
+ --xh-action-border-rest: var(--xh-float-button-border, transparent);
308
+ --xh-action-border-hover: var(--xh-float-button-border-hover, transparent);
309
+ --xh-action-border-pressed: var(--xh-float-button-border-hover, transparent);
310
+ --xh-action-border-focus-visible: var(--xh-float-button-border, transparent);
311
+ --xh-action-border-disabled: var(--xh-float-button-border, transparent);
312
+ --xh-action-shadow-rest: none;
313
+ --xh-action-shadow-hover: none;
314
+ --xh-action-shadow-pressed: none;
315
+ --xh-action-shadow-focus-visible: none;
316
+ --xh-action-shadow-disabled: none;
317
+ }
318
+ }
319
+
320
+ [data-scope='float-button'][data-part='root']:not([data-variant='outline']) > [data-xh-liquid-goo-layer] {
321
+ display: none;
322
+ }
323
+
324
+ /* 整组的投影由色块层沿外形画,使用者的投影槽接到那里。色块层不是部件,只按 scope 选 */
325
+ [data-scope='float-button'] > [data-xh-liquid-goo-layer] {
326
+ --xh-_liquid-goo-shadow: var(--xh-float-button-shadow, var(--xh-material-liquid-shadow));
327
+ }
328
+
329
+ /* 色块层不设层号、靠排在最前压在各块之下:展开组也得是定位元素才盖得住它(触发器本就是) */
330
+ [data-scope='float-button'][data-part='root'][data-xh-liquid-goo] [data-scope='float-button'][data-part='list'] {
331
+ position: relative;
332
+ }
333
+
334
+ /* 展开组里的原生动作项在组里同样是液态面:底色、细线、亮边与投影交给色块层,块上留前景与磨砂 */
335
+ @media (forced-colors: none) {
336
+ :where([data-material='liquid']) [data-scope='float-button'][data-part='root'][data-variant='outline'][data-xh-liquid-goo]
337
+ [data-scope='float-button'][data-part='list']
338
+ > button:not([data-scope]) {
339
+ border-color: var(--xh-float-button-border, transparent);
340
+ background-color: var(--xh-float-button-bg, transparent);
341
+ background-image: none;
342
+ color: var(--xh-float-button-fg, var(--xh-material-liquid-fg));
343
+ box-shadow: none;
344
+ -webkit-backdrop-filter: var(--xh-material-liquid-backdrop);
345
+ backdrop-filter: var(--xh-material-liquid-backdrop);
346
+ }
347
+ }
348
+
349
+ @media (forced-colors: none) and (hover: hover) {
350
+ :where([data-material='liquid']) [data-scope='float-button'][data-part='root'][data-variant='outline'][data-xh-liquid-goo]
351
+ [data-scope='float-button'][data-part='list']
352
+ > button:not([data-scope]):not(:disabled):hover {
353
+ border-color: var(--xh-float-button-border-hover, transparent);
354
+ background-color: var(--xh-float-button-bg-hover, color-mix(in oklab, var(--xh-material-liquid-fg) 8%, transparent));
355
+ }
356
+ }
357
+
358
+ @media (forced-colors: none) {
359
+ :where([data-material='liquid']) [data-scope='float-button'][data-part='root'][data-variant='outline'][data-xh-liquid-goo]
360
+ [data-scope='float-button'][data-part='list']
361
+ > button:not([data-scope]):not(:disabled):active {
362
+ background-color: var(--xh-float-button-bg-active, color-mix(in oklab, var(--xh-material-liquid-fg) 14%, transparent));
363
+ }
364
+ }
365
+
366
+ /* 从触发器里分离、融回的那几块由液态组的弹簧逐帧写姿态,CSS 的逐条冒出不再叠上去 */
367
+ [data-scope='float-button'][data-part='list'] > [data-xh-liquid-goo-split] {
368
+ animation: none;
193
369
  }
194
370
 
195
371
  /* 面是实心的,环色与面同族,画在面里就看不出来;环改取面自己的前景色——
@@ -223,7 +399,12 @@
223
399
  inline-size: var(--xh-float-button-size, var(--xh-_float-button-size));
224
400
  block-size: var(--xh-float-button-size, var(--xh-_float-button-size));
225
401
  border-radius: var(--xh-float-button-radius, var(--xh-shape-circle));
226
- /* 逐条依次冒出来,离触发器最近的那条先到。backwards 让每条在自己那份延迟里保持起始帧 */
402
+ }
403
+
404
+ /* 逐条依次冒出来,离触发器最近的那条先到。backwards 让每条在自己那份延迟里保持起始帧。
405
+ 挂载时就展开着的那一段(data-instant)属于首帧,直接呈现。守卫包在 :where 里不加特异性:
406
+ 后面的交错延迟、液态分离块与收起那几条要照旧压得过这一条 */
407
+ [data-scope='float-button'][data-part='list']:where(:not([data-instant])) > * {
227
408
  animation: xh-pop-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong) backwards;
228
409
  }
229
410
 
@@ -278,7 +459,7 @@
278
459
  cursor: not-allowed;
279
460
  }
280
461
 
281
- /* 交错的间隔:第二条起逐级往后推,第六条起统一钉在第五级,
462
+ /* 交错的间隔:第二条起逐级往后推,第五条起统一钉在第四级——与收起、与列表动效同一个封顶,
282
463
  动作再多也不会拖出一条越来越慢的尾巴 */
283
464
  [data-scope='float-button'][data-part='list'] > *:nth-child(2) {
284
465
  animation-delay: var(--xh-motion-stagger-step);
@@ -292,12 +473,31 @@
292
473
  animation-delay: calc(3 * var(--xh-motion-stagger-step));
293
474
  }
294
475
 
295
- [data-scope='float-button'][data-part='list'] > *:nth-child(5) {
476
+ [data-scope='float-button'][data-part='list'] > *:nth-child(n + 5) {
296
477
  animation-delay: calc(4 * var(--xh-motion-stagger-step));
297
478
  }
298
479
 
299
- [data-scope='float-button'][data-part='list'] > *:nth-child(n + 6) {
300
- animation-delay: calc(5 * var(--xh-motion-stagger-step));
480
+ /* 收起:逆着冒出的次序逐条缩回,离触发器最远的先走、最近的最后收,连接层等它们播完才藏起展开组。
481
+ 液态档里从触发器拆出来的那几块由液态组融回,不挂这段 */
482
+ [data-scope='float-button'][data-part='list'][data-state='closed'] > :not([data-xh-liquid-goo-split]) {
483
+ pointer-events: none;
484
+ animation: xh-pop-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
485
+ }
486
+
487
+ [data-scope='float-button'][data-part='list'][data-state='closed'] > :not([data-xh-liquid-goo-split]):nth-last-child(2) {
488
+ animation-delay: var(--xh-motion-stagger-step);
489
+ }
490
+
491
+ [data-scope='float-button'][data-part='list'][data-state='closed'] > :not([data-xh-liquid-goo-split]):nth-last-child(3) {
492
+ animation-delay: calc(2 * var(--xh-motion-stagger-step));
493
+ }
494
+
495
+ [data-scope='float-button'][data-part='list'][data-state='closed'] > :not([data-xh-liquid-goo-split]):nth-last-child(4) {
496
+ animation-delay: calc(3 * var(--xh-motion-stagger-step));
497
+ }
498
+
499
+ [data-scope='float-button'][data-part='list'][data-state='closed'] > :not([data-xh-liquid-goo-split]):nth-last-child(n + 5) {
500
+ animation-delay: calc(4 * var(--xh-motion-stagger-step));
301
501
  }
302
502
 
303
503
  /* 没写内容就由皮肤画兜底字形;作者往部件里塞了自己的图形,这几条即不命中。
@@ -311,6 +511,12 @@
311
511
  background-color: currentColor;
312
512
  -webkit-mask: var(--xh-glyph-mark-plus) center / contain no-repeat;
313
513
  mask: var(--xh-glyph-mark-plus) center / contain no-repeat;
514
+ /* 原地的小幅转动:展开后「+」转 45° 成「×」,读得出再按一下就收起;减弱动效下即刻到位 */
515
+ transition: rotate var(--xh-motion-duration-nudge) var(--xh-motion-ease-enter-strong);
516
+ }
517
+
518
+ [data-scope='float-button'][data-part='trigger'][data-state='open']:empty::before {
519
+ rotate: 45deg;
314
520
  }
315
521
 
316
522
  /* 声明过任何 display 就盖掉了 UA 的 [hidden]{display:none},收起态得自己还回去。
@@ -326,4 +532,23 @@
326
532
  display: none;
327
533
  }
328
534
  }
535
+
536
+ @media (forced-colors: active) {
537
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
538
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
539
+ 按钮面上取 ButtonText,悬停与按下时家族把按钮涂成 Highlight、字形换 HighlightText;
540
+ 禁用取 GrayText */
541
+ [data-scope='float-button'][data-part='trigger']:empty::before {
542
+ forced-color-adjust: none;
543
+ background-color: ButtonText;
544
+ }
545
+
546
+ [data-scope='float-button'][data-part='trigger']:not([data-disabled]):not([data-loading]):is(:hover, :active, [data-pressed]):empty::before {
547
+ background-color: HighlightText;
548
+ }
549
+
550
+ [data-scope='float-button'][data-part='trigger'][data-disabled]:empty::before {
551
+ background-color: GrayText;
552
+ }
553
+ }
329
554
  }
@@ -8,6 +8,7 @@
8
8
  /* 共享关键帧由 family/motion.css 提供,本皮肤 @import 它,单独引入仍成立。 */
9
9
  @import '../family/motion.css';
10
10
  @import '../family/action-control.css';
11
+ @import '../family/material.css';
11
12
 
12
13
  @layer xihan.components {
13
14
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
@@ -84,7 +85,8 @@
84
85
  面板整棵子树都在 positioner 底下,它一收就不生成盒子,退场动画一帧都播不出来。
85
86
  真正的收起由宿主在退场结束后落成内联 display:none。 */
86
87
 
87
- [data-scope='floating-panel'][data-part='positioner'][data-state='open'] {
88
+ /* 挂载时就开着的那一段(data-instant)属于首帧,直接呈现 */
89
+ [data-scope='floating-panel'][data-part='positioner'][data-state='open']:not([data-instant]) {
88
90
  animation: xh-pop-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong);
89
91
  }
90
92
 
@@ -95,39 +97,52 @@
95
97
  }
96
98
 
97
99
  [data-scope='floating-panel'][data-part='content'] {
100
+ /* 材质由家族配方画(family/material.css 的 frosted 面:描边、底、1px 顶光、前景、投影与背景滤镜),
101
+ 这里只把使用者槽接进来 */
102
+ --xh-frosted-bg: var(--xh-floating-panel-bg, var(--xh-_material-bg));
103
+ --xh-frosted-border: var(--xh-floating-panel-border, var(--xh-_material-border));
104
+ --xh-frosted-fg: var(--xh-floating-panel-fg, var(--xh-_material-fg));
105
+ --xh-frosted-shadow: var(--xh-floating-panel-shadow, var(--xh-_material-shadow));
106
+
98
107
  position: relative;
99
108
  block-size: 100%;
100
109
  display: flex;
101
110
  flex-direction: column;
102
111
  overflow: hidden;
103
- /* 默认态是 M2 磨砂面:背景、边缘、高光、投影与光学采样同出一张配方。
104
- 作者既有的面板槽仍排在外层;没有覆写时才落进这套磨砂令牌。 */
105
- border: var(--xh-stroke-thin) solid var(--xh-floating-panel-border, var(--xh-material-frosted-border));
106
112
  border-radius: var(--xh-floating-panel-radius, var(--xh-shape-overlay));
107
- background:
108
- linear-gradient(
109
- to bottom,
110
- var(--xh-material-frosted-highlight) 0 var(--xh-stroke-thin),
111
- transparent var(--xh-stroke-thin)
112
- ),
113
- var(--xh-floating-panel-bg, var(--xh-material-frosted-bg));
114
- color: var(--xh-floating-panel-fg, var(--xh-material-frosted-fg));
115
- box-shadow: var(--xh-floating-panel-shadow, var(--xh-material-frosted-shadow));
116
- -webkit-backdrop-filter: var(--xh-material-frosted-backdrop);
117
- backdrop-filter: var(--xh-material-frosted-backdrop);
118
- /* 铺满那一档要把投影整个撤掉,海拔的这一跳跟着过渡走 */
119
- transition: box-shadow var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
113
+ /* 铺满那一档要把投影与描边色整个撤掉,海拔的这一跳跟着过渡走 */
114
+ transition:
115
+ box-shadow var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
116
+ border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
120
117
  }
121
118
 
122
119
  /* 最大化时几何层把面板写成铺满视口(left/top 归零、宽高 100%),四条边全贴着视口:
123
120
  圆角会在四角把底下的页面露出来,描边缩成一圈贴边的多余框线,投影整块落在视口之外。
124
- 三样一起撤,与抽屉贴住视口的那条边不圆角是同一回事。 */
121
+ 三样一起撤,与抽屉贴住视口的那条边不圆角是同一回事。描边只撤颜色、不撤宽度,
122
+ 进出最大化时盒子不因描边一收一放跳一像素 */
125
123
  [data-scope='floating-panel'][data-part='content'][data-window-state='maximized'] {
126
- border: 0;
124
+ border-color: transparent;
127
125
  border-radius: 0;
128
126
  box-shadow: none;
129
127
  }
130
128
 
129
+ /* 进出最大化:位置与尺寸按指示档补过去,圆角同步收放,不再瞬切。只在这一段挂——
130
+ 拖动与改尺寸要跟手,平时的定位层不带过渡;减弱动效下即刻到位 */
131
+ [data-scope='floating-panel'][data-part='positioner'][data-animating] {
132
+ transition:
133
+ left var(--xh-motion-duration-move) var(--xh-motion-ease-continuous),
134
+ top var(--xh-motion-duration-move) var(--xh-motion-ease-continuous),
135
+ width var(--xh-motion-duration-move) var(--xh-motion-ease-continuous),
136
+ height var(--xh-motion-duration-move) var(--xh-motion-ease-continuous);
137
+ }
138
+
139
+ [data-scope='floating-panel'][data-part='positioner'][data-animating] [data-scope='floating-panel'][data-part='content'] {
140
+ transition:
141
+ box-shadow var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
142
+ border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
143
+ border-radius var(--xh-motion-duration-move) var(--xh-motion-ease-continuous);
144
+ }
145
+
131
146
  [data-scope='floating-panel'][data-part='content'][hidden] {
132
147
  display: none;
133
148
  }
@@ -392,4 +407,23 @@
392
407
  display: none;
393
408
  }
394
409
  }
410
+
411
+ @media (forced-colors: active) {
412
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
413
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
414
+ 按钮面上取 ButtonText,悬停与按下时家族把按钮涂成 Highlight、字形换 HighlightText;
415
+ 禁用取 GrayText */
416
+ [data-scope='floating-panel']:is([data-part='close-trigger'], [data-part='window-state-trigger']):empty::before {
417
+ forced-color-adjust: none;
418
+ background-color: ButtonText;
419
+ }
420
+
421
+ [data-scope='floating-panel']:is([data-part='close-trigger'], [data-part='window-state-trigger']):not([data-disabled]):not([data-loading]):is(:hover, :active, [data-pressed]):empty::before {
422
+ background-color: HighlightText;
423
+ }
424
+
425
+ [data-scope='floating-panel']:is([data-part='close-trigger'], [data-part='window-state-trigger'])[data-disabled]:empty::before {
426
+ background-color: GrayText;
427
+ }
428
+ }
395
429
  }
package/css/form.css CHANGED
@@ -5,8 +5,11 @@
5
5
 
6
6
  /* 定义 form 样式。 */
7
7
 
8
+ /* 共享关键帧由 family/motion.css 提供,本皮肤 @import 它,单独引入仍成立。 */
9
+ @import '../family/motion.css';
8
10
  /* Form 独立皮肤同时传递引入提交 / 重置钮的动作控件配方;full bundle 递归去重。 */
9
11
  @import '../family/action-control.css';
12
+ @import '../family/loading-ring.css';
10
13
 
11
14
  @layer xihan.components {
12
15
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
@@ -41,6 +44,14 @@
41
44
  flex-flow: row wrap;
42
45
  align-items: start;
43
46
  column-gap: var(--xh-form-inline-gap, var(--xh-space-4));
47
+ /* 一行流里字段按内容收,没有列宽可铺:在表单根上取下此处的字段缺省宽,交给下面的字段还原 */
48
+ --xh-_form-inline-control-w: var(--xh-control-w);
49
+ }
50
+
51
+ /* 字段会把里面的控件铺满字段宽,而一行流的字段宽正是由控件撑出来的,两者互相指望就塌成内容宽;
52
+ 这一档的字段还原成表单根上的缺省宽 */
53
+ [data-scope='form'][data-part='root'][data-layout='inline'] [data-scope='field'][data-part='root'] {
54
+ --xh-control-w: var(--xh-_form-inline-control-w);
44
55
  }
45
56
 
46
57
  /* 一列是网格档的缺省,这里从两列起逐档写 */
@@ -204,11 +215,16 @@
204
215
  /* 从收起翻成显出的那一下给一小段入场:读屏靠 role=alert 播报,
205
216
  视觉这边也得有个"它是刚冒出来的"的交代,否则像一直杵在那儿 */
206
217
  [data-scope='form'][data-part='error-summary'][data-state='invalid'] {
207
- animation: xh-form-summary-enter var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong);
218
+ animation: xh-drop-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong);
219
+ }
220
+
221
+ /* 错误全改完或重置时淡出,播完连接层才写 hidden。收起态常挂 hidden,这条只在退场途中命中 */
222
+ [data-scope='form'][data-part='error-summary'][data-state='idle'] {
223
+ animation: xh-fade-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
208
224
  }
209
225
 
210
226
  /* 摘要里的每一条是一个真链接,指向对应字段容器。铺满一行的集合行:悬停 / 按下只换面不缩放,
211
- 底面坐在摘要的 surface 白底上,走白底承载阶梯 100 → 200;换面走按压时间线,释放回 release 档 */
227
+ 底面坐在摘要的 surface 白底上,走白底承载阶梯 100 → 200;换面按下走 press,其余走 micro */
212
228
  [data-scope='form'][data-part='error-summary-item'] {
213
229
  display: block;
214
230
  padding-inline: var(--xh-form-summary-item-px, var(--xh-space-1));
@@ -220,7 +236,7 @@
220
236
  text-underline-offset: var(--xh-form-summary-item-underline-offset, var(--xh-space-0_5));
221
237
  cursor: pointer;
222
238
  transition:
223
- background-color var(--xh-motion-duration-release) var(--xh-motion-ease-release),
239
+ background-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
224
240
  color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
225
241
  }
226
242
 
@@ -232,7 +248,7 @@
232
248
  /* 摘要显出时条目逐条入场,越靠后起步越晚。
233
249
  backwards 让还没轮到的条目停在首帧,不是先显出再重播 */
234
250
  [data-scope='form'][data-part='error-summary'][data-state='invalid'] [data-part='error-summary-item'] {
235
- animation: xh-form-summary-enter var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong) backwards;
251
+ animation: xh-drop-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong) backwards;
236
252
  }
237
253
 
238
254
  /* 排序按"前面有几条露面的"数,不按 nth-child:作者一次把所有字段的条目都写上,
@@ -360,20 +376,18 @@
360
376
  --xh-action-shadow-pressed: var(--xh-form-submit-shadow, var(--xh-_highlight-brand));
361
377
  --xh-action-ring-color-focus-visible: currentColor;
362
378
  }
363
- }
364
379
 
365
- @layer xihan.motion {
366
- /* 关键帧写在自己文件里、名字带组件前缀:motion.css 是公共的,
367
- 往那儿加一条只服务本组件的动画会让所有组件都得跟着重下载 */
368
- @keyframes xh-form-summary-enter {
369
- from {
370
- opacity: 0;
371
- translate: 0 calc(-1 * var(--xh-motion-distance-sm));
372
- }
380
+ /* 提交回调在途:环由加载环配方压在提交钮正中(连接层投影 data-xh-loading-ring="overlay"),直径取钮的图标档,
381
+ 起始边取钮的真实前景、转速认使用者槽。不是禁用,焦点与对比度都保持,在途指针由家族 loading 面给。
382
+ 提交文案随前景淡到透明留位:进入在途等一个 micro 才起淡(快速落定什么都不闪),落定时不等、按 micro 淡回 */
383
+ [data-scope='form'][data-part='submit-trigger'] {
384
+ --xh-_loading-ring-fg: var(--xh-form-submit-fg, var(--xh-_action-variant-fg-loading));
385
+ --xh-_loading-ring-duration: var(--xh-form-loading-duration, var(--xh-motion-loop-spin));
386
+ }
373
387
 
374
- to {
375
- opacity: 1;
376
- translate: 0 0;
377
- }
388
+ [data-scope='form'][data-part='submit-trigger'][data-loading] {
389
+ --xh-action-fg-loading: transparent;
390
+
391
+ transition-delay: var(--xh-motion-duration-micro);
378
392
  }
379
393
  }