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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/CHANGELOG.md +527 -0
  2. package/README.md +1 -1
  3. package/css/accordion.css +61 -5
  4. package/css/affix.css +5 -0
  5. package/css/alert.css +35 -5
  6. package/css/anchor.css +12 -7
  7. package/css/avatar-group.css +8 -3
  8. package/css/avatar.css +8 -3
  9. package/css/back-top.css +21 -1
  10. package/css/badge.css +21 -2
  11. package/css/breadcrumb.css +29 -4
  12. package/css/button-group.css +19 -13
  13. package/css/button.css +57 -11
  14. package/css/calendar.css +124 -2
  15. package/css/card.css +10 -5
  16. package/css/carousel.css +41 -5
  17. package/css/cascader.css +188 -102
  18. package/css/checkbox-group.css +46 -19
  19. package/css/checkbox.css +59 -8
  20. package/css/clipboard.css +7 -1
  21. package/css/code-block.css +17 -7
  22. package/css/collapsible.css +47 -2
  23. package/css/color-picker.css +117 -46
  24. package/css/combobox.css +132 -52
  25. package/css/composer.css +10 -3
  26. package/css/context-menu.css +101 -75
  27. package/css/countdown.css +5 -0
  28. package/css/date-field.css +96 -14
  29. package/css/date-picker.css +281 -65
  30. package/css/descriptions.css +7 -2
  31. package/css/dialog.css +109 -22
  32. package/css/download-trigger.css +76 -0
  33. package/css/drawer.css +75 -23
  34. package/css/dynamic-input.css +73 -17
  35. package/css/editable.css +17 -10
  36. package/css/ellipsis.css +6 -1
  37. package/css/empty-state.css +12 -7
  38. package/css/field.css +14 -6
  39. package/css/fieldset.css +69 -0
  40. package/css/file-upload.css +50 -7
  41. package/css/flex.css +5 -0
  42. package/css/float-button.css +21 -1
  43. package/css/floating-panel.css +310 -0
  44. package/css/form.css +10 -5
  45. package/css/gradient-text.css +5 -0
  46. package/css/grid.css +5 -0
  47. package/css/heatmap.css +410 -0
  48. package/css/highlight.css +6 -1
  49. package/css/hotkeys.css +90 -0
  50. package/css/hover-card.css +58 -50
  51. package/css/icon-wrapper.css +5 -0
  52. package/css/icon.css +5 -0
  53. package/css/image-cropper.css +231 -0
  54. package/css/image-viewer.css +167 -22
  55. package/css/image.css +7 -2
  56. package/css/infinite-scroll.css +6 -1
  57. package/css/json-viewer.css +241 -0
  58. package/css/layout.css +11 -10
  59. package/css/list.css +13 -8
  60. package/css/listbox.css +25 -4
  61. package/css/loading-bar.css +12 -1
  62. package/css/log.css +6 -1
  63. package/css/marquee.css +14 -0
  64. package/css/masonry.css +63 -0
  65. package/css/mention.css +105 -32
  66. package/css/menu.css +131 -46
  67. package/css/menubar.css +150 -66
  68. package/css/navigation-menu.css +59 -29
  69. package/css/number-animation.css +8 -3
  70. package/css/number-field.css +178 -16
  71. package/css/overlay-arrow.css +51 -0
  72. package/css/page-header.css +10 -5
  73. package/css/pagination.css +18 -13
  74. package/css/password-input.css +356 -0
  75. package/css/pin-input.css +18 -10
  76. package/css/popconfirm.css +73 -16
  77. package/css/popover.css +88 -44
  78. package/css/popselect.css +201 -52
  79. package/css/progress.css +52 -7
  80. package/css/qr-code.css +6 -1
  81. package/css/radio-group.css +19 -8
  82. package/css/rating.css +7 -2
  83. package/css/reset.css +17 -0
  84. package/css/result.css +11 -6
  85. package/css/scroll-area.css +65 -94
  86. package/css/scrollbar.css +167 -0
  87. package/css/segmented.css +215 -0
  88. package/css/select.css +292 -124
  89. package/css/separator.css +7 -2
  90. package/css/side-nav.css +116 -19
  91. package/css/signature-pad.css +144 -0
  92. package/css/skeleton.css +12 -1
  93. package/css/slider.css +15 -10
  94. package/css/space.css +117 -0
  95. package/css/spinner.css +31 -11
  96. package/css/splitter.css +5 -0
  97. package/css/statistic.css +14 -9
  98. package/css/steps.css +30 -8
  99. package/css/switch.css +62 -13
  100. package/css/table.css +136 -37
  101. package/css/tabs.css +9 -4
  102. package/css/tag.css +177 -0
  103. package/css/tags-input.css +83 -40
  104. package/css/text-field.css +154 -36
  105. package/css/thread.css +6 -1
  106. package/css/time-field.css +102 -16
  107. package/css/time-picker.css +264 -69
  108. package/css/time.css +5 -0
  109. package/css/timeline.css +15 -10
  110. package/css/timer.css +119 -0
  111. package/css/toast.css +46 -21
  112. package/css/toaster.css +6 -1
  113. package/css/toggle-group.css +5 -0
  114. package/css/toggle.css +11 -6
  115. package/css/tone.css +10 -5
  116. package/css/toolbar.css +6 -1
  117. package/css/tooltip.css +54 -38
  118. package/css/tour.css +128 -63
  119. package/css/transfer.css +73 -17
  120. package/css/tree-select.css +203 -83
  121. package/css/tree.css +90 -1
  122. package/css/typography.css +9 -4
  123. package/css/undefined.css +11 -1
  124. package/css/virtualizer.css +8 -1
  125. package/css/watermark.css +6 -2
  126. package/index.css +17 -1
  127. package/index.unlayered.css +9394 -2883
  128. package/package.json +19 -4
  129. package/css/motion.css +0 -58
package/css/image.css CHANGED
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='image'] {
4
+ --xh-image-skin: 1;
5
+ }
6
+
2
7
  /* 尺寸交给三个变量而不是写死:这个组件既要能当封面图,也要能当头图与缩略图。
3
8
  给了 --xh-image-ratio 才有确定高度,回退内容与图片在同一个盒子里换人时不会跳。 */
4
9
  [data-scope='image'][data-part='root'] {
@@ -8,7 +13,7 @@
8
13
  inline-size: var(--xh-image-w, 100%);
9
14
  block-size: var(--xh-image-h, auto);
10
15
  aspect-ratio: var(--xh-image-ratio, auto);
11
- border-radius: var(--xh-image-radius, var(--xh-radius-md));
16
+ border-radius: var(--xh-image-radius, var(--xh-shape-control));
12
17
  background: var(--xh-image-bg, var(--xh-bg-subtle));
13
18
  }
14
19
 
@@ -33,7 +38,7 @@
33
38
  /* 没给 ratio 时根盒子高度是 auto,回退内容自己撑出一个不塌的高度 */
34
39
  min-block-size: var(--xh-image-fallback-min-h, var(--xh-control-h-lg));
35
40
  color: var(--xh-image-fallback-fg, var(--xh-fg-muted));
36
- font-size: var(--xh-image-fallback-font-size, var(--xh-font-size-sm));
41
+ font-size: var(--xh-image-fallback-font-size, var(--xh-text-secondary-size));
37
42
  line-height: var(--xh-leading-none);
38
43
  user-select: none;
39
44
  }
@@ -1,7 +1,12 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='infinite-scroll'] {
4
+ --xh-infinite-scroll-skin: 1;
5
+ }
6
+
2
7
  /*
3
8
  * 列表外壳这一层刻意不给盒模型:滚的是页面还是作者自己的 overflow 容器、列表长什么样,
4
- * 都归作者的布局管,组件只往它身上挂 data-state / data-loading / data-disabled 供作者取用。
9
+ * 都归作者的布局管,组件只往它身上挂 data-loading / data-disabled 供作者取用。
5
10
  * 一旦声明 display,UA 的 [hidden]{display:none} 就压不住了,故连 display 也不写。
6
11
  *
7
12
  * 哨兵是纯机制,不是可见元素:给它一条最细的高度让它有个能被观察到的盒子,
@@ -0,0 +1,241 @@
1
+ @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='json-viewer'] {
4
+ --xh-json-viewer-skin: 1;
5
+ }
6
+
7
+ /* 缺省态(md 档)。等宽字体让层级缩进与值的长度对得齐。
8
+ 逐类型配色走 --xh-syntax-* 语法令牌,与代码块的记号色同源、随主题明暗切换。 */
9
+ [data-scope='json-viewer'][data-part='root'] {
10
+ /* 作者塞进指示符槽的图标跟着字号走,与皮肤画的兜底字形同一把尺 */
11
+ --xh-icon-size: var(--xh-json-viewer-icon-size, var(--xh-glyph-size-text));
12
+
13
+ --xh-_json-viewer-font-size: var(--xh-text-secondary-size);
14
+ --xh-_json-viewer-row-py: var(--xh-space-0_5);
15
+ --xh-_json-viewer-indent: var(--xh-space-4);
16
+ --xh-json-viewer-key-fg: var(--xh-fg-brand-strong);
17
+ --xh-json-viewer-punctuation-fg: var(--xh-fg-subtle);
18
+ --xh-json-viewer-null-fg: var(--xh-fg-subtle);
19
+ --xh-json-viewer-string-fg: var(--xh-syntax-string);
20
+ --xh-json-viewer-number-fg: var(--xh-syntax-number);
21
+ --xh-json-viewer-boolean-fg: var(--xh-syntax-keyword);
22
+
23
+ display: block;
24
+ color: var(--xh-json-viewer-fg, var(--xh-fg-default));
25
+ font-family: var(--xh-json-viewer-font, var(--xh-font-family-mono));
26
+ font-size: var(--xh-json-viewer-font-size, var(--xh-_json-viewer-font-size));
27
+ line-height: var(--xh-leading-normal);
28
+ }
29
+
30
+ /* 写了 display 就会盖掉 UA 的 [hidden]{display:none},这里补回 */
31
+ [data-scope='json-viewer'][data-part='root'][hidden] {
32
+ display: none;
33
+ }
34
+
35
+ /* 尺寸轴只换私有槽,部件从 root 继承 */
36
+ [data-scope='json-viewer'][data-part='root'][data-size='sm'] {
37
+ --xh-_json-viewer-font-size: var(--xh-control-caption-sm);
38
+ --xh-_json-viewer-row-py: var(--xh-space-0);
39
+ --xh-_json-viewer-indent: var(--xh-space-3);
40
+ }
41
+
42
+ [data-scope='json-viewer'][data-part='root'][data-size='lg'] {
43
+ --xh-_json-viewer-font-size: var(--xh-control-caption-lg);
44
+ --xh-_json-viewer-row-py: var(--xh-space-1);
45
+ --xh-_json-viewer-indent: var(--xh-space-5);
46
+ }
47
+
48
+ /* 常驻容器不是浮层:两轴都可能溢出——深层缩进往行首方向推、长字符串往行尾伸 */
49
+ [data-scope='json-viewer'][data-part='tree'] {
50
+ display: flex;
51
+ flex-direction: column;
52
+ max-block-size: var(--xh-json-viewer-max-h, var(--xh-viewport-max-h));
53
+ padding-block: var(--xh-json-viewer-py, var(--xh-space-2));
54
+ padding-inline: var(--xh-json-viewer-px, var(--xh-space-2));
55
+ border: var(--xh-stroke-thin) solid var(--xh-json-viewer-border, var(--xh-border-default));
56
+ border-radius: var(--xh-json-viewer-radius, var(--xh-shape-surface));
57
+ background: var(--xh-json-viewer-bg, var(--xh-bg-surface));
58
+ overflow: auto;
59
+ }
60
+
61
+ [data-scope='json-viewer'][data-part='tree'][hidden] {
62
+ display: none;
63
+ }
64
+
65
+ /* 焦点在树外时容器自己进 Tab 序列兜底,落焦不画环:环永远长在行上 */
66
+ [data-scope='json-viewer'][data-part='tree']:focus-visible {
67
+ outline: none;
68
+ }
69
+
70
+ /* 焦点落在 branch 上(它才是 treeitem),但环画在它自己那一行上:
71
+ 整棵子层套一圈描边会把展开的层级全框进去 */
72
+ [data-scope='json-viewer'][data-part='branch']:focus-visible {
73
+ outline: none;
74
+ }
75
+
76
+ [data-scope='json-viewer'][data-part='branch']:focus-visible > [data-scope='json-viewer'][data-part='branch-control'] {
77
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
78
+ /* 往内收:行是撑满宽度的,环画在外沿会被容器裁掉 */
79
+ outline-offset: calc(-1 * var(--xh-ring-width));
80
+ }
81
+
82
+ /* 行:标量与分支共用同一套盒子,两种行在视觉上必须齐平 */
83
+ [data-scope='json-viewer'][data-part='item'],
84
+ [data-scope='json-viewer'][data-part='branch-control'] {
85
+ display: flex;
86
+ align-items: baseline;
87
+ gap: var(--xh-json-viewer-row-gap, var(--xh-space-1));
88
+ padding-block: var(--xh-json-viewer-row-py, var(--xh-_json-viewer-row-py));
89
+ padding-inline: var(--xh-json-viewer-row-px, var(--xh-space-1));
90
+ border-radius: var(--xh-json-viewer-row-radius, var(--xh-shape-inset));
91
+ white-space: nowrap;
92
+ transition: background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
93
+ }
94
+
95
+ [data-scope='json-viewer'][data-part='item'][hidden],
96
+ [data-scope='json-viewer'][data-part='branch-control'][hidden] {
97
+ display: none;
98
+ }
99
+
100
+ /* 悬停与键盘锚点共用中性灰轻档:data-highlighted 跟的是连接层记的焦点行,
101
+ 键盘落焦另有聚焦环标记位置 */
102
+ [data-scope='json-viewer'][data-part='item']:is(:hover, [data-highlighted]),
103
+ [data-scope='json-viewer'][data-part='branch-control']:is(:hover, [data-highlighted]) {
104
+ background: var(--xh-json-viewer-row-bg-hover, var(--xh-bg-subtle));
105
+ }
106
+
107
+ /* 分支那一行是唯一的点击目标:点它切换展开态 */
108
+ [data-scope='json-viewer'][data-part='branch-control'] {
109
+ cursor: pointer;
110
+ user-select: none;
111
+ }
112
+
113
+ /* 标量行的焦点环直接画在行上:它自己就是 treeitem */
114
+ [data-scope='json-viewer'][data-part='item']:focus-visible {
115
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
116
+ outline-offset: calc(-1 * var(--xh-ring-width));
117
+ }
118
+
119
+ /* 键名不参与压缩:它短且必须完整可读,被挤掉的应该是值 */
120
+ [data-scope='json-viewer'][data-part='item-key'],
121
+ [data-scope='json-viewer'][data-part='branch-text'] {
122
+ flex: none;
123
+ color: var(--xh-json-viewer-key-fg);
124
+ font-weight: var(--xh-json-viewer-key-font-weight, var(--xh-font-weight-medium));
125
+ }
126
+
127
+ /* 键名与值之间的冒号由皮肤画:它是排版符号,进不了可及名字反而更清爽 */
128
+ [data-scope='json-viewer'][data-part='item-key']::after,
129
+ [data-scope='json-viewer'][data-part='branch-text']::after {
130
+ content: ':';
131
+ color: var(--xh-json-viewer-punctuation-fg);
132
+ }
133
+
134
+ /* 弹性子项默认最小尺寸是内容尺寸,不归零则长字符串把行撑破、省略号轮不上 */
135
+ [data-scope='json-viewer'][data-part='item-value'] {
136
+ flex: 1 1 auto;
137
+ min-inline-size: 0;
138
+ overflow: hidden;
139
+ white-space: nowrap;
140
+ text-overflow: ellipsis;
141
+ }
142
+
143
+ [data-scope='json-viewer'][data-part='item-value'][data-type='string'] {
144
+ color: var(--xh-json-viewer-string-fg);
145
+ }
146
+
147
+ [data-scope='json-viewer'][data-part='item-value'][data-type='number'] {
148
+ color: var(--xh-json-viewer-number-fg);
149
+ }
150
+
151
+ [data-scope='json-viewer'][data-part='item-value'][data-type='boolean'] {
152
+ color: var(--xh-json-viewer-boolean-fg);
153
+ }
154
+
155
+ [data-scope='json-viewer'][data-part='item-value'][data-type='null'] {
156
+ color: var(--xh-json-viewer-null-fg);
157
+ }
158
+
159
+ /* 空对象与空数组只剩一对括号,跟标点同色 */
160
+ [data-scope='json-viewer'][data-part='item-value'][data-type='array'],
161
+ [data-scope='json-viewer'][data-part='item-value'][data-type='object'] {
162
+ color: var(--xh-json-viewer-punctuation-fg);
163
+ }
164
+
165
+ /* 循环引用与截断占位不是真数据,排在语气之外用弱化字重表达 */
166
+ [data-scope='json-viewer'][data-part='item-value'][data-circular],
167
+ [data-scope='json-viewer'][data-part='item-value'][data-truncated] {
168
+ color: var(--xh-fg-muted);
169
+ font-style: italic;
170
+ }
171
+
172
+ /* 展开箭头与勾选标记同宽,两种行的文字起点才对得齐 */
173
+ [data-scope='json-viewer'][data-part='branch-trigger'] {
174
+ display: inline-flex;
175
+ flex: none;
176
+ align-items: center;
177
+ justify-content: center;
178
+ inline-size: var(--xh-json-viewer-indicator-size, var(--xh-control-indicator-size));
179
+ color: var(--xh-json-viewer-indicator-fg, var(--xh-fg-subtle));
180
+ }
181
+
182
+ [data-scope='json-viewer'][data-part='branch-trigger'][hidden] {
183
+ display: none;
184
+ }
185
+
186
+ [data-scope='json-viewer'][data-part='branch-indicator'] {
187
+ display: inline-block;
188
+ line-height: var(--xh-leading-none);
189
+ transition: rotate var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
190
+ }
191
+
192
+ [data-scope='json-viewer'][data-part='branch-indicator'][hidden] {
193
+ display: none;
194
+ }
195
+
196
+ /* 没写内容就由皮肤画;作者往部件里塞了自己的图形,这条规则即不命中 */
197
+ [data-scope='json-viewer'][data-part='branch-indicator']:empty::before {
198
+ content: '';
199
+ display: inline-block;
200
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
201
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
202
+ vertical-align: var(--xh-glyph-baseline-shift);
203
+ background-color: currentColor;
204
+ -webkit-mask: var(--xh-glyph-mark-chevron-right) center / contain no-repeat;
205
+ mask: var(--xh-glyph-mark-chevron-right) center / contain no-repeat;
206
+ }
207
+
208
+ /* 收起时朝向行首、展开时转向下方,与逻辑书写方向一致:
209
+ rtl 下 inline-start 在右侧,箭头因此要反着转 */
210
+ [data-scope='json-viewer'][data-part='branch-indicator'][data-state='open'] {
211
+ rotate: 90deg;
212
+ }
213
+
214
+ [dir='rtl'] [data-scope='json-viewer'][data-part='branch-indicator'] {
215
+ rotate: 180deg;
216
+ }
217
+
218
+ [dir='rtl'] [data-scope='json-viewer'][data-part='branch-indicator'][data-state='open'] {
219
+ rotate: 90deg;
220
+ }
221
+
222
+ /* 收起摘要不参与选中:框选整块 JSON 时不该把它一起复制走 */
223
+ [data-scope='json-viewer'][data-part='preview'] {
224
+ flex: none;
225
+ color: var(--xh-json-viewer-preview-fg, var(--xh-fg-muted));
226
+ font-size: var(--xh-json-viewer-preview-font-size, var(--xh-text-caption-size));
227
+ user-select: none;
228
+ }
229
+
230
+ /* 层级缩进靠子层容器自己顶:分支物理嵌套着子层,每深一层就多缩一格,
231
+ 无需把层级号写进内联样式,也就不必在两个适配器里各写一遍 */
232
+ [data-scope='json-viewer'][data-part='branch-content'] {
233
+ display: flex;
234
+ flex-direction: column;
235
+ padding-inline-start: var(--xh-json-viewer-indent, var(--xh-_json-viewer-indent));
236
+ }
237
+
238
+ [data-scope='json-viewer'][data-part='branch-content'][hidden] {
239
+ display: none;
240
+ }
241
+ }
package/css/layout.css CHANGED
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='layout'] {
4
+ --xh-layout-skin: 1;
5
+ }
6
+
2
7
  /* 页面骨架排成三行两列:头与脚各横贯一整行,侧栏与内容并排占中间那一行。
3
8
  缺哪一段,对应的行/列就没有条目,宽高自动收成 0,不必为"少写一段"另立规则。
4
9
 
@@ -52,7 +57,7 @@
52
57
  }
53
58
 
54
59
  /* 折起来时不换行:窄到放不下的那几个字直接被 overflow 裁掉,不会挤成一列竖排 */
55
- [data-scope='layout'][data-part='sider'][data-state='collapsed'] {
60
+ [data-scope='layout'][data-part='sider'][data-collapsed] {
56
61
  inline-size: var(--xh-_layout-sider-collapsed-w);
57
62
  white-space: nowrap;
58
63
  }
@@ -69,7 +74,7 @@
69
74
  padding: var(--xh-space-3) var(--xh-space-4);
70
75
  background: var(--xh-layout-footer-bg, var(--xh-bg-surface));
71
76
  color: var(--xh-fg-muted);
72
- font-size: var(--xh-font-size-sm);
77
+ font-size: var(--xh-text-secondary-size);
73
78
  }
74
79
 
75
80
  /* —— 折叠把手 —— */
@@ -84,7 +89,7 @@
84
89
  border-radius: var(--xh-layout-sider-trigger-radius, var(--xh-shape-control));
85
90
  background: var(--xh-layout-sider-trigger-bg, transparent);
86
91
  color: var(--xh-layout-sider-trigger-fg, var(--xh-fg-default));
87
- font-size: var(--xh-font-size-sm);
92
+ font-size: var(--xh-text-secondary-size);
88
93
  line-height: var(--xh-leading-none);
89
94
  cursor: pointer;
90
95
  }
@@ -151,19 +156,15 @@
151
156
  /* 侧栏保持默认的拉伸对齐,只加一道高度上限:内容短时它照旧填满整列,内容长过一屏时它收成
152
157
  可视区那么高,短出来的那一段就是 sticky 的行程。
153
158
  上限量的是滚动容器可视区的高度,缺省按视口算;滚动容器不是视口时由作者把那个高度写进
154
- --xh-layout-scrollport-h。视口取 dvh:移动端地址栏收放会改变可视高度,dvh 跟着变。
159
+ --xh-layout-scrollport-h。视口取 dvh:移动端地址栏收放会改变可视高度,dvh 跟着变;
160
+ 不认 dvh 的引擎由级联回落到前一条的 vh。
155
161
  侧栏自己的条目多到装不下时在内部滚,横轴仍归上面那条 overflow: hidden 裁,
156
162
  折叠态靠它把放不下的字裁掉;逻辑属性 overflow-block 尚无可用实现,写物理轴。 */
157
163
  [data-scope='layout'][data-part='sider'][data-fixed] {
158
164
  position: sticky;
159
165
  inset-block-start: var(--xh-_layout-sider-offset);
166
+ max-block-size: calc(var(--xh-layout-scrollport-h, 100vh) - var(--xh-_layout-sider-offset));
160
167
  max-block-size: calc(var(--xh-layout-scrollport-h, 100dvh) - var(--xh-_layout-sider-offset));
161
168
  overflow-y: auto;
162
169
  }
163
-
164
- @media (prefers-reduced-motion: reduce) {
165
- [data-scope='layout'][data-part='sider'] {
166
- transition: none;
167
- }
168
- }
169
170
  }
package/css/list.css CHANGED
@@ -1,12 +1,17 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='list'] {
4
+ --xh-list-skin: 1;
5
+ }
6
+
2
7
  [data-scope='list'][data-part='root'] {
3
8
  /* 一个尺寸档只换这几个私有槽,条目与两行文字都从根上继承它们,
4
9
  换档不必给每个部件各写一条选择器 */
5
- --xh-_list-item-py: var(--xh-space-3);
10
+ --xh-_list-item-py: var(--xh-list-item-py-md);
6
11
  --xh-_list-item-px: var(--xh-space-4);
7
12
  --xh-_list-item-gap: var(--xh-space-3);
8
- --xh-_list-title-size: var(--xh-font-size-md);
9
- --xh-_list-description-size: var(--xh-font-size-sm);
13
+ --xh-_list-title-size: var(--xh-control-font-md);
14
+ --xh-_list-description-size: var(--xh-control-caption-md);
10
15
 
11
16
  display: flex;
12
17
  flex-direction: column;
@@ -24,18 +29,18 @@
24
29
 
25
30
  /* —— 尺寸 —— */
26
31
  [data-scope='list'][data-part='root'][data-size='sm'] {
27
- --xh-_list-item-py: var(--xh-space-2);
32
+ --xh-_list-item-py: var(--xh-list-item-py-sm);
28
33
  --xh-_list-item-px: var(--xh-space-3);
29
34
  --xh-_list-item-gap: var(--xh-space-2);
30
- --xh-_list-title-size: var(--xh-font-size-sm);
31
- --xh-_list-description-size: var(--xh-font-size-xs);
35
+ --xh-_list-title-size: var(--xh-control-font-sm);
36
+ --xh-_list-description-size: var(--xh-control-caption-sm);
32
37
  }
33
38
 
34
39
  [data-scope='list'][data-part='root'][data-size='lg'] {
35
- --xh-_list-item-py: var(--xh-space-4);
40
+ --xh-_list-item-py: var(--xh-list-item-py-lg);
36
41
  --xh-_list-item-px: var(--xh-space-5);
37
42
  --xh-_list-item-gap: var(--xh-space-4);
38
- --xh-_list-title-size: var(--xh-font-size-lg);
43
+ --xh-_list-title-size: var(--xh-control-font-lg);
39
44
  }
40
45
 
41
46
  /* —— 外框 —— */
package/css/listbox.css CHANGED
@@ -1,5 +1,13 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='listbox'] {
4
+ --xh-listbox-skin: 1;
5
+ }
6
+
2
7
  [data-scope='listbox'][data-part='root'] {
8
+ /* 作者塞进指示符槽的图标跟着字号走,与皮肤画的兜底字形同一把尺 */
9
+ --xh-icon-size: var(--xh-listbox-icon-size, var(--xh-glyph-size-text));
10
+
3
11
  display: flex;
4
12
  flex-direction: column;
5
13
  gap: var(--xh-listbox-gap, var(--xh-space-2));
@@ -26,7 +34,7 @@
26
34
  [data-scope='listbox'][data-part='content'] {
27
35
  display: flex;
28
36
  flex-direction: column;
29
- max-block-size: var(--xh-listbox-content-max-h, var(--xh-overlay-max-h));
37
+ max-block-size: var(--xh-listbox-content-max-h, var(--xh-viewport-h-md));
30
38
  padding-block: var(--xh-listbox-content-py, var(--xh-space-1));
31
39
  padding-inline: var(--xh-listbox-content-px, var(--xh-space-1));
32
40
  border: var(--xh-stroke-thin) solid var(--xh-listbox-content-border, var(--xh-border-default));
@@ -76,7 +84,7 @@
76
84
  padding-block: var(--xh-listbox-group-label-py, var(--xh-space-1));
77
85
  padding-inline: var(--xh-listbox-group-label-px, var(--xh-control-px-md));
78
86
  color: var(--xh-listbox-group-label-fg, var(--xh-fg-subtle));
79
- font-size: var(--xh-listbox-group-label-font-size, var(--xh-font-size-xs));
87
+ font-size: var(--xh-listbox-group-label-font-size, var(--xh-text-caption-size));
80
88
  font-weight: var(--xh-listbox-group-label-font-weight, var(--xh-font-weight-medium));
81
89
  line-height: var(--xh-leading-none);
82
90
  user-select: none;
@@ -127,7 +135,13 @@
127
135
  font-weight: var(--xh-listbox-item-font-weight-selected, var(--xh-font-weight-medium));
128
136
  }
129
137
 
130
- [data-scope='listbox'][data-part='item']:focus {
138
+ /* 键盘落焦的条目画内收环;指针触发的聚焦不画 */
139
+ [data-scope='listbox'][data-part='item']:focus-visible {
140
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
141
+ outline-offset: calc(-1 * var(--xh-ring-width));
142
+ }
143
+
144
+ [data-scope='listbox'][data-part='item']:focus:not(:focus-visible) {
131
145
  outline: none;
132
146
  }
133
147
 
@@ -164,7 +178,14 @@
164
178
 
165
179
  /* 没写内容就由皮肤画勾;作者往部件里塞了自己的图形,这条规则即不命中 */
166
180
  [data-scope='listbox'][data-part='item-indicator']:empty::before {
167
- content: '✓';
181
+ content: '';
182
+ display: inline-block;
183
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
184
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
185
+ vertical-align: var(--xh-glyph-baseline-shift);
186
+ background-color: currentColor;
187
+ -webkit-mask: var(--xh-glyph-mark-check) center / contain no-repeat;
188
+ mask: var(--xh-glyph-mark-check) center / contain no-repeat;
168
189
  }
169
190
 
170
191
  [data-scope='listbox'][data-part='item-indicator'][data-state='checked'] {
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='loading-bar'] {
4
+ --xh-loading-bar-skin: 1;
5
+ }
6
+
2
7
  /*
3
8
  * 贴在视口顶边的一条细带。厚度那条轴(block-size)由连接层写进内联样式——
4
9
  * height 这个 prop 的缺省值在 connect 里收口,这里再声明一次只会被内联盖掉,
@@ -56,11 +61,17 @@
56
61
  }
57
62
 
58
63
  /* 减少动效:宽度与淡出都立刻到位。进度本身照常推进,只是不再有过渡的过程——
59
- 一条按 200ms 一拍持续变宽的带子正是最容易引发不适的那种动效 */
64
+ 一条按 200ms 一拍持续变宽的带子正是最容易引发不适的那种动效。
65
+ 两条规则是同一件事的两个触发条件:系统偏好与作者打的 data-motion */
60
66
  @media (prefers-reduced-motion: reduce) {
61
67
  [data-scope='loading-bar'][data-part='root'],
62
68
  [data-scope='loading-bar'][data-part='range'] {
63
69
  transition: none;
64
70
  }
65
71
  }
72
+
73
+ :where([data-motion='reduce']) [data-scope='loading-bar'][data-part='root'],
74
+ :where([data-motion='reduce']) [data-scope='loading-bar'][data-part='range'] {
75
+ transition: none;
76
+ }
66
77
  }
package/css/log.css CHANGED
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='log'] {
4
+ --xh-log-skin: 1;
5
+ }
6
+
2
7
  /* 行高是本组件的定尺基准:视口按行数定高、每行排版,都从这一个槽位来。
3
8
  它的兜底值须与连接层内联高度里用的那份逐字一致,否则 rows 算出来的高度对不上行。 */
4
9
  [data-scope='log'][data-part='root'] {
@@ -50,7 +55,7 @@
50
55
  display: block;
51
56
  padding-inline: var(--xh-log-content-px, var(--xh-space-3));
52
57
  font-family: var(--xh-log-font, var(--xh-font-family-mono));
53
- font-size: var(--xh-log-font-size, var(--xh-font-size-sm));
58
+ font-size: var(--xh-log-font-size, var(--xh-text-secondary-size));
54
59
  }
55
60
 
56
61
  [data-scope='log'][data-part='content'][hidden] {
package/css/marquee.css CHANGED
@@ -25,6 +25,11 @@
25
25
  }
26
26
 
27
27
  @layer xihan.components {
28
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
29
+ [data-scope='marquee'] {
30
+ --xh-marquee-skin: 1;
31
+ }
32
+
28
33
  /*
29
34
  * 窗口:只露出轨道的一段。
30
35
  *
@@ -169,6 +174,7 @@
169
174
  * 减少动效:轨道停在起点不再走。一段自己动起来、不停到底的横移正是最容易引发不适的那一类,
170
175
  * 这里不能只把速度压到很慢——慢速横移仍然是横移。
171
176
  * 停下后窗口外那段就够不着了,故窗口同时改成可滚,内容仍然读得全。
177
+ * 两组规则是同一件事的两个触发条件:系统偏好与作者打的 data-motion。
172
178
  */
173
179
  @media (prefers-reduced-motion: reduce) {
174
180
  [data-scope='marquee'][data-part='content'] {
@@ -179,4 +185,12 @@
179
185
  overflow: auto;
180
186
  }
181
187
  }
188
+
189
+ :where([data-motion='reduce']) [data-scope='marquee'][data-part='content'] {
190
+ animation: none;
191
+ }
192
+
193
+ :where([data-motion='reduce']) [data-scope='marquee'][data-part='root'] {
194
+ overflow: auto;
195
+ }
182
196
  }
@@ -0,0 +1,63 @@
1
+ @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='masonry'] {
4
+ --xh-masonry-skin: 1;
5
+ }
6
+
7
+ [data-scope='masonry'][data-part='root'] {
8
+ display: flex;
9
+ /* 各列按自己的内容收高,不被最高的那列拉平——列不等高正是瀑布流的样子 */
10
+ align-items: flex-start;
11
+ gap: var(--xh-_masonry-gap);
12
+
13
+ /* 列间距与列内的项间距共用这一份取值,声明在根上、由列继承下去;
14
+ 不写 gap 档位就不留间距。使用者槽位排在档位之前,写死一个值即盖过所有档 */
15
+ --xh-_masonry-gap: var(--xh-masonry-gap, var(--xh-space-0));
16
+ }
17
+
18
+ /* 声明了 display 就盖掉了 UA 的 [hidden] 规则,这里还回去 */
19
+ [data-scope='masonry'][data-part='root'][hidden] {
20
+ display: none;
21
+ }
22
+
23
+ [data-scope='masonry'][data-part='column'] {
24
+ display: flex;
25
+ flex-direction: column;
26
+ /* 各列等宽;下限写 0 而不是 auto,一段长内容才不会把自己那列撑宽 */
27
+ flex: 1 1 0;
28
+ min-inline-size: 0;
29
+ gap: var(--xh-_masonry-gap);
30
+ }
31
+
32
+ /* 当前档位用不上的那几列由适配器收起;这条兜住作者自己写 hidden 的情形 */
33
+ [data-scope='masonry'][data-part='column'][hidden] {
34
+ display: none;
35
+ }
36
+
37
+ /* 项不发话,高度由内容定;只兜住一件事——不被列的弹性压缩,压缩了量到的高度就不是它真实的高度 */
38
+ [data-scope='masonry'][data-part='item'] {
39
+ flex: none;
40
+ }
41
+
42
+ /* —— 间距档位 —— */
43
+ /* 每一档指向一个间距令牌,列之间与列内项之间吃同一份间距 */
44
+ [data-scope='masonry'][data-part='root'][data-gap='xs'] {
45
+ --xh-_masonry-gap: var(--xh-masonry-gap, var(--xh-space-1));
46
+ }
47
+
48
+ [data-scope='masonry'][data-part='root'][data-gap='sm'] {
49
+ --xh-_masonry-gap: var(--xh-masonry-gap, var(--xh-space-2));
50
+ }
51
+
52
+ [data-scope='masonry'][data-part='root'][data-gap='md'] {
53
+ --xh-_masonry-gap: var(--xh-masonry-gap, var(--xh-space-3));
54
+ }
55
+
56
+ [data-scope='masonry'][data-part='root'][data-gap='lg'] {
57
+ --xh-_masonry-gap: var(--xh-masonry-gap, var(--xh-space-4));
58
+ }
59
+
60
+ [data-scope='masonry'][data-part='root'][data-gap='xl'] {
61
+ --xh-_masonry-gap: var(--xh-masonry-gap, var(--xh-space-6));
62
+ }
63
+ }