@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
@@ -1,3 +1,14 @@
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
+ */
5
+
6
+ /* 定义 floating panel 样式。 */
7
+
8
+ /* 共享关键帧由 family/motion.css 提供,本皮肤 @import 它,单独引入仍成立。 */
9
+ @import '../family/motion.css';
10
+ @import '../family/action-control.css';
11
+
1
12
  @layer xihan.components {
2
13
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
14
  [data-scope='floating-panel'] {
@@ -10,8 +21,8 @@
10
21
  皮肤只管层级、配色、圆角、阴影、把手的摆放与状态表现。 */
11
22
 
12
23
  [data-scope='floating-panel'][data-part='root'] {
13
- /* 作者塞进指示符槽的图标跟着字号走,与皮肤画的兜底字形同一把尺 */
14
- --xh-icon-size: var(--xh-floating-panel-icon-size, var(--xh-glyph-size-text));
24
+ /* 作者塞进面板里的图标按控件档取(§6.5);标题栏上的按钮另由家族配方按各自的档下发字形尺 */
25
+ --xh-icon-size: var(--xh-floating-panel-icon-size, var(--xh-glyph-size-md));
15
26
 
16
27
  /* 面板本身是 fixed 定位,root 只是作者那棵树里的容器,不参与排布 */
17
28
  display: contents;
@@ -20,34 +31,41 @@
20
31
  /* 浮层被搬到 portal 落点,继承不到 root 上的槽:这把尺在被搬走的那一侧再声明一次,
21
32
  关闭钮与条目里作者塞的图标才和皮肤画的兜底字形一样大 */
22
33
  [data-scope='floating-panel'][data-part='content'] {
23
- --xh-icon-size: var(--xh-floating-panel-icon-size, var(--xh-glyph-size-text));
34
+ --xh-icon-size: var(--xh-floating-panel-icon-size, var(--xh-glyph-size-md));
24
35
  }
25
36
 
26
37
  [data-scope='floating-panel'][data-part='root'][hidden] {
27
38
  display: none;
28
39
  }
29
40
 
41
+ /*
42
+ * 开合触发器是页面上的独立文字按钮(§9.1):盒型、md 档几何、outline 四态面、0.97 按压并换底与粗指针命中区
43
+ * 由家族配方按连接层投影的 data-xh-action-variant / -size 给出(此前只缩放不换底)。这里只把公开槽桥接到
44
+ * 矩阵之前(使用者槽 → 形态矩阵 → 家族缺省)与几何槽映射。
45
+ */
30
46
  [data-scope='floating-panel'][data-part='trigger'] {
31
- display: inline-flex;
32
- align-items: center;
33
- justify-content: center;
34
- block-size: var(--xh-floating-panel-trigger-h, var(--xh-control-h-md));
35
- padding-inline: var(--xh-floating-panel-trigger-px, var(--xh-control-px-md));
36
- border: var(--xh-stroke-thin) solid var(--xh-floating-panel-trigger-border, var(--xh-border-control));
37
- border-radius: var(--xh-floating-panel-trigger-radius, var(--xh-shape-control));
38
- background: var(--xh-floating-panel-trigger-bg, var(--xh-bg-surface));
39
- color: var(--xh-floating-panel-trigger-fg, var(--xh-fg-default));
40
- font-size: var(--xh-text-label-size);
41
- cursor: pointer;
47
+ --xh-action-bg-rest: var(--xh-floating-panel-trigger-bg, var(--xh-_action-variant-bg-rest));
48
+ --xh-action-bg-focus-visible: var(--xh-floating-panel-trigger-bg, var(--xh-_action-variant-bg-focus-visible));
49
+ --xh-action-fg-rest: var(--xh-floating-panel-trigger-fg, var(--xh-_action-variant-fg-rest));
50
+ --xh-action-fg-hover: var(--xh-floating-panel-trigger-fg, var(--xh-_action-variant-fg-hover));
51
+ --xh-action-fg-pressed: var(--xh-floating-panel-trigger-fg, var(--xh-_action-variant-fg-pressed));
52
+ --xh-action-fg-focus-visible: var(--xh-floating-panel-trigger-fg, var(--xh-_action-variant-fg-focus-visible));
53
+ --xh-action-border-rest: var(--xh-floating-panel-trigger-border, var(--xh-_action-variant-border-rest));
54
+ --xh-action-border-focus-visible: var(--xh-floating-panel-trigger-border, var(--xh-_action-variant-border-focus-visible));
55
+ --xh-action-radius: var(--xh-floating-panel-trigger-radius, var(--xh-shape-control));
56
+ --xh-action-visual-size: var(--xh-floating-panel-trigger-h, var(--xh-_action-profile-visual-size));
57
+ --xh-action-padding-inline: var(--xh-floating-panel-trigger-px, var(--xh-_action-profile-padding-inline));
58
+ /* 作者塞进按钮的图标随档取字形尺(md = 20px) */
59
+ --xh-icon-size: var(--xh-floating-panel-icon-size, var(--xh-_action-profile-glyph-size));
42
60
  }
43
61
 
44
62
  [data-scope='floating-panel'][data-part='trigger'][hidden] {
45
63
  display: none;
46
64
  }
47
65
 
48
- [data-scope='floating-panel'][data-part='trigger']:focus-visible {
49
- outline: var(--xh-ring-width) solid var(--xh-ring-focus);
50
- outline-offset: var(--xh-ring-offset);
66
+ /* 面是实心的,环色与面同族,画在面里就看不出来;环改取面自己的前景色 */
67
+ [data-scope='floating-panel'][data-part='trigger'][data-xh-action-variant='solid']:focus-visible {
68
+ --xh-action-ring-color-focus-visible: currentColor;
51
69
  }
52
70
 
53
71
  /* 把手的粗细:八个把手共用这两档,声明在这里往下继承。 */
@@ -61,8 +79,21 @@
61
79
  display: block;
62
80
  }
63
81
 
64
- [data-scope='floating-panel'][data-part='positioner'][hidden] {
65
- display: none;
82
+ /* 这里刻意没有 [data-part='positioner'][hidden] { display: none }:收起态连接层会给
83
+ positioner 打 hidden,而上面那条 display 正是要盖掉 UA 的 [hidden]{display:none}——
84
+ 面板整棵子树都在 positioner 底下,它一收就不生成盒子,退场动画一帧都播不出来。
85
+ 真正的收起由宿主在退场结束后落成内联 display:none。 */
86
+
87
+ /* 在场那一档才提前备好合成层:属性随 data-state 一起消失,收起后不长期占着 */
88
+ [data-scope='floating-panel'][data-part='positioner'][data-state='open'] {
89
+ will-change: opacity, scale;
90
+ animation: xh-pop-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong);
91
+ }
92
+
93
+ /* 退场:收起那一帧由退场闸门押后,这里才播得出来。forwards 把末态钉住,
94
+ 免得动画结束到真收起之间闪一下原样 */
95
+ [data-scope='floating-panel'][data-part='positioner'][data-state='closed'] {
96
+ animation: xh-pop-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
66
97
  }
67
98
 
68
99
  [data-scope='floating-panel'][data-part='content'] {
@@ -71,13 +102,32 @@
71
102
  display: flex;
72
103
  flex-direction: column;
73
104
  overflow: hidden;
74
- /* 面板底下没有遮罩,压着的是任意页面内容:这条描边是它与背景之间唯一的边界,
75
- border-default 与同样无遮罩的气泡卡片一档,别退到更淡的 border-subtle */
76
- border: var(--xh-stroke-thin) solid var(--xh-floating-panel-border, var(--xh-border-default));
77
- border-radius: var(--xh-floating-panel-radius, var(--xh-shape-surface));
78
- background: var(--xh-floating-panel-bg, var(--xh-bg-surface));
79
- color: var(--xh-floating-panel-fg, var(--xh-fg-default));
80
- box-shadow: var(--xh-floating-panel-shadow, var(--xh-elevation-sheet));
105
+ /* 默认态是 M2 磨砂面:背景、边缘、高光、投影与光学采样同出一张配方。
106
+ 作者既有的面板槽仍排在外层;没有覆写时才落进这套磨砂令牌。 */
107
+ border: var(--xh-stroke-thin) solid var(--xh-floating-panel-border, var(--xh-material-frosted-border));
108
+ border-radius: var(--xh-floating-panel-radius, var(--xh-shape-overlay));
109
+ background:
110
+ linear-gradient(
111
+ to bottom,
112
+ var(--xh-material-frosted-highlight) 0 var(--xh-stroke-thin),
113
+ transparent var(--xh-stroke-thin)
114
+ ),
115
+ var(--xh-floating-panel-bg, var(--xh-material-frosted-bg));
116
+ color: var(--xh-floating-panel-fg, var(--xh-material-frosted-fg));
117
+ box-shadow: var(--xh-floating-panel-shadow, var(--xh-material-frosted-shadow));
118
+ -webkit-backdrop-filter: var(--xh-material-frosted-backdrop);
119
+ backdrop-filter: var(--xh-material-frosted-backdrop);
120
+ /* 铺满那一档要把投影整个撤掉,海拔的这一跳跟着过渡走 */
121
+ transition: box-shadow var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
122
+ }
123
+
124
+ /* 最大化时几何层把面板写成铺满视口(left/top 归零、宽高 100%),四条边全贴着视口:
125
+ 圆角会在四角把底下的页面露出来,描边缩成一圈贴边的多余框线,投影整块落在视口之外。
126
+ 三样一起撤,与抽屉贴住视口的那条边不圆角是同一回事。 */
127
+ [data-scope='floating-panel'][data-part='content'][data-window-state='maximized'] {
128
+ border: 0;
129
+ border-radius: 0;
130
+ box-shadow: none;
81
131
  }
82
132
 
83
133
  [data-scope='floating-panel'][data-part='content'][hidden] {
@@ -101,24 +151,25 @@
101
151
  gap: var(--xh-floating-panel-header-gap, var(--xh-control-gap-sm));
102
152
  padding-block: var(--xh-floating-panel-header-py, var(--xh-space-2));
103
153
  padding-inline: var(--xh-floating-panel-header-px, var(--xh-space-3));
104
- border-block-end: var(--xh-stroke-thin) solid var(--xh-floating-panel-header-border, var(--xh-border-subtle));
105
- background: var(--xh-floating-panel-header-bg, var(--xh-bg-subtle));
154
+ border-block-end: var(--xh-stroke-thin) solid var(--xh-floating-panel-header-border, var(--xh-material-frosted-separator));
155
+ background: var(--xh-floating-panel-header-bg, var(--xh-material-frosted-bg));
106
156
  }
107
157
 
108
158
  [data-scope='floating-panel'][data-part='header'][hidden] {
109
159
  display: none;
110
160
  }
111
161
 
162
+ /* 浮层内标题(§6.4):14 / 600 */
112
163
  [data-scope='floating-panel'][data-part='title'] {
113
164
  margin: 0;
114
165
  flex: 1 1 auto;
115
166
  overflow: hidden;
116
167
  font-size: var(--xh-floating-panel-title-font-size, var(--xh-text-label-size));
117
- font-weight: var(--xh-floating-panel-title-font-weight, var(--xh-text-label-weight));
168
+ font-weight: var(--xh-floating-panel-title-font-weight, var(--xh-font-weight-semibold));
118
169
  line-height: var(--xh-leading-tight);
119
170
  white-space: nowrap;
120
171
  text-overflow: ellipsis;
121
- color: var(--xh-floating-panel-title-fg, var(--xh-fg-default));
172
+ color: var(--xh-floating-panel-title-fg, var(--xh-material-frosted-fg));
122
173
  }
123
174
 
124
175
  /* 拖拽把手铺满标题栏剩下的横向空间:只给一小块纹理的话很难抓住 */
@@ -140,11 +191,6 @@
140
191
  cursor: default;
141
192
  }
142
193
 
143
- [data-scope='floating-panel'][data-part='drag-trigger']:focus-visible {
144
- outline: var(--xh-ring-width) solid var(--xh-ring-focus);
145
- outline-offset: calc(-1 * var(--xh-ring-offset));
146
- }
147
-
148
194
  /* —— 改尺把手 ——
149
195
  这一段刻意用物理的 inset / width / height:面板的坐标是屏幕坐标(连接层写的是
150
196
  left/top),指针位移与方向键推动的也是屏幕方向。改成逻辑属性后,RTL 下 inset-inline-start
@@ -163,31 +209,30 @@
163
209
  cursor: default;
164
210
  }
165
211
 
166
- [data-scope='floating-panel'][data-part='resize-trigger']:focus-visible {
167
- outline: var(--xh-ring-width) solid var(--xh-ring-focus);
168
- outline-offset: calc(-1 * var(--xh-ring-offset));
169
- }
170
-
171
212
  [data-scope='floating-panel'][data-part='resize-trigger'][data-edge='n'] {
172
213
  inset: 0 0 auto 0;
214
+ /* stylelint-disable-next-line property-disallowed-list -- 面板的落位与尺寸由连接层写成物理 left / top / width / height,data-edge 的 n/s/e/w 是屏幕方向,把手的厚度沿同一根物理轴 */
173
215
  height: var(--xh-_floating-panel-handle);
174
216
  cursor: ns-resize;
175
217
  }
176
218
 
177
219
  [data-scope='floating-panel'][data-part='resize-trigger'][data-edge='s'] {
178
220
  inset: auto 0 0 0;
221
+ /* stylelint-disable-next-line property-disallowed-list -- 同上 */
179
222
  height: var(--xh-_floating-panel-handle);
180
223
  cursor: ns-resize;
181
224
  }
182
225
 
183
226
  [data-scope='floating-panel'][data-part='resize-trigger'][data-edge='w'] {
184
227
  inset: 0 auto 0 0;
228
+ /* stylelint-disable-next-line property-disallowed-list -- 同上 */
185
229
  width: var(--xh-_floating-panel-handle);
186
230
  cursor: ew-resize;
187
231
  }
188
232
 
189
233
  [data-scope='floating-panel'][data-part='resize-trigger'][data-edge='e'] {
190
234
  inset: 0 0 0 auto;
235
+ /* stylelint-disable-next-line property-disallowed-list -- 同上 */
191
236
  width: var(--xh-_floating-panel-handle);
192
237
  cursor: ew-resize;
193
238
  }
@@ -195,104 +240,103 @@
195
240
  /* 四个角排在四条边之后,命中区大一圈:角上要同时抓住两条边 */
196
241
  [data-scope='floating-panel'][data-part='resize-trigger'][data-edge='nw'] {
197
242
  inset: 0 auto auto 0;
243
+ /* stylelint-disable-next-line property-disallowed-list -- 同上 */
198
244
  width: var(--xh-_floating-panel-corner);
245
+ /* stylelint-disable-next-line property-disallowed-list -- 同上 */
199
246
  height: var(--xh-_floating-panel-corner);
200
247
  cursor: nwse-resize;
201
248
  }
202
249
 
203
250
  [data-scope='floating-panel'][data-part='resize-trigger'][data-edge='ne'] {
204
251
  inset: 0 0 auto auto;
252
+ /* stylelint-disable-next-line property-disallowed-list -- 同上 */
205
253
  width: var(--xh-_floating-panel-corner);
254
+ /* stylelint-disable-next-line property-disallowed-list -- 同上 */
206
255
  height: var(--xh-_floating-panel-corner);
207
256
  cursor: nesw-resize;
208
257
  }
209
258
 
210
259
  [data-scope='floating-panel'][data-part='resize-trigger'][data-edge='se'] {
211
260
  inset: auto 0 0 auto;
261
+ /* stylelint-disable-next-line property-disallowed-list -- 同上 */
212
262
  width: var(--xh-_floating-panel-corner);
263
+ /* stylelint-disable-next-line property-disallowed-list -- 同上 */
213
264
  height: var(--xh-_floating-panel-corner);
214
265
  cursor: nwse-resize;
215
266
  }
216
267
 
217
268
  [data-scope='floating-panel'][data-part='resize-trigger'][data-edge='sw'] {
218
269
  inset: auto auto 0 0;
270
+ /* stylelint-disable-next-line property-disallowed-list -- 同上 */
219
271
  width: var(--xh-_floating-panel-corner);
272
+ /* stylelint-disable-next-line property-disallowed-list -- 同上 */
220
273
  height: var(--xh-_floating-panel-corner);
221
274
  cursor: nesw-resize;
222
275
  }
223
276
 
224
- /* —— 标题栏上的按钮 —— */
225
- [data-scope='floating-panel'][data-part='stage-trigger'],
277
+ /*
278
+ * —— 标题栏上的按钮 ——
279
+ * 形态钮与关闭钮是标题栏上的单图标钮(§9.1):正方盒、sm 档几何、ghost 四态面、0.97 按压并换底与粗指针
280
+ * 命中区由家族配方给出——磨砂白面上走画布承载阶梯悬停 --xh-bg-subtle(100)→ 按下 --xh-bg-subtle-hover(200),
281
+ * 焦点面透明吃库环。这里只把公开槽桥接到矩阵之前(使用者槽 → 形态矩阵 → 家族缺省);
282
+ * 静息前景取磨砂面的 muted 字色,悬停 / 按下回到 fg-default。
283
+ */
284
+ [data-scope='floating-panel'][data-part='window-state-trigger'],
226
285
  [data-scope='floating-panel'][data-part='close-trigger'] {
227
- flex: 0 0 auto;
228
- display: inline-flex;
229
- align-items: center;
230
- justify-content: center;
231
- border: 0;
232
- background: transparent;
233
- color: var(--xh-floating-panel-action-fg, var(--xh-fg-muted));
234
- cursor: pointer;
235
- transition:
236
- background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
237
- color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
238
- scale var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
286
+ --xh-action-bg-hover: var(--xh-floating-panel-action-bg-hover, var(--xh-_action-variant-bg-hover));
287
+ --xh-action-bg-pressed: var(--xh-floating-panel-action-bg-active, var(--xh-_action-variant-bg-pressed));
288
+ --xh-action-fg-rest: var(--xh-floating-panel-action-fg, var(--xh-material-frosted-fg-muted));
289
+ --xh-action-fg-hover: var(--xh-floating-panel-action-fg-hover, var(--xh-_action-variant-fg-hover));
290
+ --xh-action-fg-pressed: var(--xh-floating-panel-action-fg-active, var(--xh-_action-variant-fg-pressed));
291
+ --xh-action-fg-focus-visible: var(--xh-floating-panel-action-fg-hover, var(--xh-_action-variant-fg-focus-visible));
292
+ /* 作者塞进按钮的图标随档取字形尺(sm = 16px),与皮肤画的兜底字形同一把尺 */
293
+ --xh-icon-size: var(--xh-floating-panel-icon-size, var(--xh-_action-profile-glyph-size));
239
294
  }
240
295
 
241
- /* 两颗钮同高同圆角,尺寸与圆角槽按各自角色命名 */
242
- [data-scope='floating-panel'][data-part='stage-trigger'] {
243
- inline-size: var(--xh-floating-panel-action-size, var(--xh-control-h-sm));
244
- block-size: var(--xh-floating-panel-action-size, var(--xh-control-h-sm));
245
- border-radius: var(--xh-floating-panel-action-radius, var(--xh-shape-control));
296
+ /* 两颗钮同高同圆角,尺寸与圆角槽按各自角色命名,映到家族正方盒 */
297
+ [data-scope='floating-panel'][data-part='window-state-trigger'] {
298
+ --xh-action-visual-size: var(--xh-floating-panel-action-size, var(--xh-_action-profile-visual-size));
299
+ --xh-action-radius: var(--xh-floating-panel-action-radius, var(--xh-shape-control));
246
300
  }
247
301
 
248
302
  [data-scope='floating-panel'][data-part='close-trigger'] {
249
- inline-size: var(--xh-floating-panel-close-size, var(--xh-control-h-sm));
250
- block-size: var(--xh-floating-panel-close-size, var(--xh-control-h-sm));
251
- border-radius: var(--xh-floating-panel-close-radius, var(--xh-shape-control));
303
+ --xh-action-visual-size: var(--xh-floating-panel-close-size, var(--xh-_action-profile-visual-size));
304
+ --xh-action-radius: var(--xh-floating-panel-close-radius, var(--xh-shape-control));
252
305
  }
253
306
 
254
- [data-scope='floating-panel'][data-part='stage-trigger'][hidden],
307
+ [data-scope='floating-panel'][data-part='window-state-trigger'][hidden],
255
308
  [data-scope='floating-panel'][data-part='close-trigger'][hidden] {
256
309
  display: none;
257
310
  }
258
311
 
259
- [data-scope='floating-panel'][data-part='stage-trigger']:hover,
260
- [data-scope='floating-panel'][data-part='close-trigger']:hover {
261
- background: var(--xh-floating-panel-action-bg-hover, var(--xh-bg-subtle-hover));
262
- color: var(--xh-floating-panel-action-fg-hover, var(--xh-fg-default));
263
- }
264
-
265
- [data-scope='floating-panel'][data-part='stage-trigger']:active,
266
- [data-scope='floating-panel'][data-part='close-trigger']:active {
267
- background: var(--xh-floating-panel-action-bg-active, var(--xh-bg-subtle-active));
268
- scale: var(--xh-motion-scale-press);
312
+ /* 当前形态的那颗钮是按下的开关(aria-pressed),无滑块开关的 on 态取品牌淡底(§7.3):
313
+ rest 12% → hover 20% → pressed 28%,前景一律 --xh-fg-on-brand-subtle(对比度由该令牌承担);
314
+ 淡底不是实心面,焦点吃库环 */
315
+ [data-scope='floating-panel'][data-part='window-state-trigger'][data-state='on'] {
316
+ --xh-action-bg-rest: var(--xh-floating-panel-action-bg-on, var(--xh-bg-brand-subtle));
317
+ --xh-action-bg-hover: var(--xh-floating-panel-action-bg-on-hover, var(--xh-bg-brand-subtle-hover));
318
+ --xh-action-bg-pressed: var(--xh-floating-panel-action-bg-on-active, var(--xh-bg-brand-subtle-active));
319
+ --xh-action-bg-focus-visible: var(--xh-floating-panel-action-bg-on, var(--xh-bg-brand-subtle));
320
+ --xh-action-fg-rest: var(--xh-floating-panel-action-fg-on, var(--xh-fg-on-brand-subtle));
321
+ --xh-action-fg-hover: var(--xh-floating-panel-action-fg-on, var(--xh-fg-on-brand-subtle));
322
+ --xh-action-fg-pressed: var(--xh-floating-panel-action-fg-on, var(--xh-fg-on-brand-subtle));
323
+ --xh-action-fg-focus-visible: var(--xh-floating-panel-action-fg-on, var(--xh-fg-on-brand-subtle));
269
324
  }
270
325
 
271
- /* 当前形态的那个按钮压着不放,用户一眼看得出面板正处于哪一档。
272
- 按下态的前景取 fg-default 而不是品牌色:品牌色压在这块底上只有 3.4:1(深色皮肤 2.9:1),
273
- 按钮里通常就一个字形,够不着文字该有的对比度。 */
274
- [data-scope='floating-panel'][data-part='stage-trigger'][data-state='on'] {
275
- background: var(--xh-floating-panel-action-bg-active, var(--xh-bg-subtle-active));
276
- color: var(--xh-floating-panel-action-fg-active, var(--xh-fg-default));
277
- }
278
-
279
- /* 只换前景不叠 opacity:叠了之后禁用文字的对比度会掉到读不出来 */
280
- [data-scope='floating-panel'][data-part='stage-trigger'][data-disabled] {
281
- background: transparent;
282
- color: var(--xh-fg-disabled);
283
- cursor: not-allowed;
284
- }
285
-
286
- [data-scope='floating-panel'][data-part='stage-trigger']:focus-visible,
287
- [data-scope='floating-panel'][data-part='close-trigger']:focus-visible {
288
- outline: var(--xh-ring-width) solid var(--xh-ring-focus);
289
- outline-offset: var(--xh-ring-offset);
326
+ @media (forced-colors: active) {
327
+ /* 强制色不会绘制背景图层;直接撤掉顶边装饰,边界与文字改由 frosted 的系统色令牌接手。 */
328
+ [data-scope='floating-panel'][data-part='content'] {
329
+ background-image: none;
330
+ }
290
331
  }
291
332
 
333
+ /* 正文是浮在页面之上的 Overlay 滚动面(§6.6):原生细条由 reset 层统一给,滚到头不把页面一起带走;
334
+ 内容高度不动态变化,不写 scrollbar-gutter */
292
335
  [data-scope='floating-panel'][data-part='body'] {
293
336
  flex: 1 1 auto;
294
337
  display: block;
295
338
  overflow: auto;
339
+ overscroll-behavior: contain;
296
340
  padding-block: var(--xh-floating-panel-body-py, var(--xh-surface-py-sm));
297
341
  padding-inline: var(--xh-floating-panel-body-px, var(--xh-surface-px-sm));
298
342
  }
@@ -313,4 +357,41 @@
313
357
  -webkit-mask: var(--xh-glyph-mark-close) center / contain no-repeat;
314
358
  mask: var(--xh-glyph-mark-close) center / contain no-repeat;
315
359
  }
360
+
361
+ /* 形态钮是同一个部件的三副面孔,字形按它「按下去切到哪一档」分:
362
+ 还原是四角朝内、铺满是四角朝外、收拢是一条横线。不分档的话标题栏上排着的
363
+ 两三颗钮长得一模一样,按哪颗全靠猜。
364
+ 这条打底画「还原」,另两档在下面换掉 mask-image。
365
+ 选的是 data-target-window-state 而不是面板身上的 data-window-state:后者说的是面板此刻在哪一档,
366
+ 用它会让排在一起的几颗钮同时换成同一枚字形。
367
+ 字形也不随 data-state='on' 翻面:可访问名由连接层按 target 定死(按下的仍念「铺满面板」),
368
+ 字形跟着按下态改就与念出来的名字对不上了 */
369
+ [data-scope='floating-panel'][data-part='window-state-trigger']:empty::before {
370
+ content: '';
371
+ display: inline-block;
372
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
373
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
374
+ vertical-align: var(--xh-glyph-baseline-shift);
375
+ background-color: currentColor;
376
+ -webkit-mask: var(--xh-glyph-mark-restore) center / contain no-repeat;
377
+ mask: var(--xh-glyph-mark-restore) center / contain no-repeat;
378
+ }
379
+
380
+ [data-scope='floating-panel'][data-part='window-state-trigger'][data-target-window-state='maximized']:empty::before {
381
+ -webkit-mask-image: var(--xh-glyph-mark-maximize);
382
+ mask-image: var(--xh-glyph-mark-maximize);
383
+ }
384
+
385
+ [data-scope='floating-panel'][data-part='window-state-trigger'][data-target-window-state='minimized']:empty::before {
386
+ -webkit-mask-image: var(--xh-glyph-mark-minus);
387
+ mask-image: var(--xh-glyph-mark-minus);
388
+ }
389
+
390
+ @media print {
391
+ /* 打印:定位层与遮罩是交互期才存在的东西,落到纸上只会盖住底下的正文。
392
+ 收起写在这份皮肤里而不是 reset:reset 层排在组件层之前,组件层里任何一条 display 都压得住它。 */
393
+ [data-scope='floating-panel'][data-part='positioner'] {
394
+ display: none;
395
+ }
396
+ }
316
397
  }
package/css/focus.css ADDED
@@ -0,0 +1,46 @@
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
+ */
5
+
6
+ /*
7
+ * 聚焦环的公共层:全库共用一份。
8
+ *
9
+ * 解剖给每个部件打上 data-scope 与 data-part,键盘焦点落在哪个部件上,环就由这里画:
10
+ * 粗细取 --xh-ring-width,颜色取 --xh-_ring-color(默认 --xh-ring-focus),
11
+ * 偏移取 --xh-ring-offset——这个令牌是负的一个环宽,环的外沿与元素边框外沿重合,
12
+ * 元素聚焦前后占的地方一样大。全库只有这一种偏移,要另画的组件把整条环重写。
13
+ *
14
+ * 环往内收之后,内侧紧挨着的是环压着的那块面。面是实心的那几档,环色与面同族、贴上去就
15
+ * 看不出来;那几档在各自皮肤的 :focus-visible 规则里把 --xh-_ring-color 灌成 currentColor,
16
+ * 环随之取这块面配对的前景色——那一支色本来就要在这块面上把字读清楚,与面的对比是按可读性定的。
17
+ * 取的是「面配对的前景色」,不是语气色:环仍然不随 data-tone 走,没有实心面的那些档
18
+ * 一律是 --xh-ring-focus 这一支。
19
+ *
20
+ * 灌 currentColor 的档要同时成立三件事:
21
+ * 一、面画在谁身上——画在祖先上(标签里那颗透空的关闭钮坐在标签那块实心底上)时
22
+ * 规则写成后代选择器,槽仍然灌在拿焦点的那个节点上;
23
+ * 二、部件得有 color——圆点、滑块这类没有字的部件先补一支与面配对的墨色,
24
+ * 不补则 currentColor 取到继承下来的正文色或原生控件的 UA 前景色;
25
+ * 只有一档要换的(开关选中的轨道)把那支色直接写进槽,不绕 currentColor;
26
+ * 三、面被换走的档要退出——失效档把面换成置灰底、前景换成置灰色或透明,
27
+ * 中性轻档把实心底换成灰底,currentColor 在这些档上会把环跟着糊掉甚至整条画成透明。
28
+ * 靠收窄选择器退回本份的默认值,或把槽显式写回 var(--xh-ring-focus)。
29
+ *
30
+ * 槽在这里只留一个默认值,名单不收在本份里——谁的面是实心的由那份皮肤自己知道,
31
+ * 也只有引了那份皮肤的人该为它付字节。
32
+ * :focus-visible 只匹配拿着焦点的那一个节点,祖先不匹配,所以这个槽不会被后代读到。
33
+ *
34
+ * 选择器停在两个属性加一个伪类(0,3,0):与组件皮肤里
35
+ * [data-scope='x'][data-part='y']:focus-visible 同级,组件皮肤排在本份之后,
36
+ * 要另画的组件照写即可压过;同时压过 forced-colors.css 里 (0,2,0) 的状态环,
37
+ * 高对比档里键盘焦点仍然压在选中、展开这类状态之上。
38
+ *
39
+ * 排在全部组件皮肤之前:组件不必为了换个环色把整条环重写一遍。
40
+ */
41
+ @layer xihan.components {
42
+ [data-scope][data-part]:focus-visible {
43
+ outline: var(--xh-ring-width) solid var(--xh-_ring-color, var(--xh-ring-focus));
44
+ outline-offset: var(--xh-ring-offset);
45
+ }
46
+ }
@@ -0,0 +1,93 @@
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
+ */
5
+
6
+ /*
7
+ * 高对比档(forced-colors: active)的公共补救层。
8
+ *
9
+ * 这一档里系统接管配色:作者写的每一个颜色取值都被换成系统调色板里的对应角色,
10
+ * box-shadow 与 background-image 直接丢弃。于是「只靠底色差表达的状态」全部塌成同一个样子——
11
+ * 列表的悬停档与展开路径档、勾选与未勾选、当前页与别的页,看上去一模一样。
12
+ *
13
+ * 这份文件按状态词汇表逐个把通道补回来:outline 在这一档里不被丢弃,用它画两档可分的环。
14
+ * 逐组件写不动的东西收在这里,组件自己特有的(画的就是颜色、或者靠背景图承载信息)
15
+ * 在各自皮肤里另有 forced-colors 块。
16
+ *
17
+ * 取值只用 system color keyword:这一档里 --xh-* 令牌已经不生效,写了是误导。
18
+ * 排在全部组件皮肤之后:同特指度靠源序取胜,组件皮肤里任何与这里同特指度的 outline 声明
19
+ * 都压不掉这里的状态环。选择器一律停在两个属性(0,2,0),组件的 :focus-visible 规则
20
+ * 特指度更高,聚焦环恒压在状态环之上。
21
+ */
22
+ @layer xihan.components {
23
+ @media (forced-colors: active) {
24
+ /* 轻档:方向键锚定的那一条(焦点仍在宿主上)。虚线环,与强档一眼分得开。 */
25
+ [data-scope][data-highlighted] {
26
+ outline: var(--xh-stroke-thin) dashed CanvasText;
27
+ outline-offset: calc(-1 * var(--xh-stroke-thin));
28
+ }
29
+
30
+ /*
31
+ * 强档:选中、当前项、展开路径、勾选、激活、按下、走到的步骤、区间内的日期。
32
+ * 实线环加系统高亮色,比轻档粗一档。
33
+ *
34
+ * data-state=open 那条要摘掉三个部件:定位层与遮罩都铺满视口,画上环就是绕着整块屏幕
35
+ * 描一圈;content 面的边由下面那条浮层规则统一给,不走状态环。
36
+ * 摘除写在 :where() 里,它不计特指度,整条选择器仍停在 (0,2,0)。
37
+ */
38
+ [data-scope][data-selected],
39
+ [data-scope][data-current],
40
+ [data-scope][data-in-path],
41
+ [data-scope][data-in-range],
42
+ [data-scope][data-indeterminate],
43
+ [data-scope][data-passed],
44
+ [data-scope][data-state='checked'],
45
+ [data-scope][data-state='indeterminate'],
46
+ [data-scope][data-state='active'],
47
+ [data-scope][data-state='on'],
48
+ [data-scope][data-state='current'],
49
+ [data-scope][data-state='completed'],
50
+ [data-scope][data-state='open']:where(:not([data-part='positioner'], [data-part='content'], [data-part='backdrop'])) {
51
+ outline: var(--xh-stroke-thick) solid Highlight;
52
+ outline-offset: calc(-1 * var(--xh-stroke-thick));
53
+ }
54
+
55
+ /*
56
+ * 禁用:常态靠淡化的前景色表达,这一档里前景被统一换成 CanvasText,淡化那一层没了。
57
+ * GrayText 是系统给「不可用」留的那个角色。
58
+ */
59
+ [data-scope][data-disabled] {
60
+ color: GrayText;
61
+ border-color: GrayText;
62
+ }
63
+
64
+ /*
65
+ * 只读:常态是压低一档底色,这一档里底色被统一换掉,与能改的控件一模一样。
66
+ * 改画虚线边——只读仍要照读,字色一动不动,改不动这件事写在框上。
67
+ */
68
+ [data-scope][data-readonly] {
69
+ border-style: dashed;
70
+ }
71
+
72
+ /*
73
+ * 浮层面:三档海拔角色都写在 box-shadow 上,这一档里整层丢弃,浮层与页面之间零分界。
74
+ * 定位层里的那张面补一圈实边把它框出来。
75
+ */
76
+ [data-scope][data-part='positioner'] [data-scope][data-part='content'] {
77
+ border: var(--xh-stroke-thin) solid CanvasText;
78
+ }
79
+
80
+ /*
81
+ * 正在编辑的那一段:日期与时间的分段框里,当前段常态靠反白表达,这一档里底色被统一换掉,
82
+ * 整行数字看上去一模一样,而这类框本来就没有光标那道线。改画一圈实边把它框出来。
83
+ *
84
+ * 这一条走三个属性而不是上面那些的两个:分段框的皮肤在同一档上写了 outline: none
85
+ * (常态下环画在外层的 control 上),那条规则本身就是三个属性,两个属性压不过它。
86
+ * data-focus 只长在日期段与时间段上,这条选到的正是它们。
87
+ */
88
+ [data-scope][data-part='segment'][data-focus] {
89
+ outline: var(--xh-stroke-thick) solid Highlight;
90
+ outline-offset: calc(-1 * var(--xh-stroke-thick));
91
+ }
92
+ }
93
+ }