@xihan-ui/styles 1.0.0-alpha.2 → 1.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 (122) hide show
  1. package/CHANGELOG.md +2177 -0
  2. package/README.md +1 -1
  3. package/css/accordion.css +80 -6
  4. package/css/alert.css +43 -14
  5. package/css/anchor.css +8 -8
  6. package/css/avatar-group.css +7 -3
  7. package/css/avatar.css +9 -3
  8. package/css/back-top.css +22 -1
  9. package/css/badge.css +104 -32
  10. package/css/breadcrumb.css +25 -5
  11. package/css/button-group.css +18 -13
  12. package/css/button.css +48 -11
  13. package/css/calendar.css +150 -7
  14. package/css/card.css +20 -11
  15. package/css/carousel.css +36 -5
  16. package/css/cascader.css +211 -124
  17. package/css/checkbox-group.css +72 -22
  18. package/css/checkbox.css +70 -13
  19. package/css/clipboard.css +2 -1
  20. package/css/code-block.css +12 -7
  21. package/css/collapsible.css +49 -3
  22. package/css/color-picker.css +132 -46
  23. package/css/combobox.css +132 -60
  24. package/css/composer.css +5 -3
  25. package/css/context-menu.css +113 -74
  26. package/css/date-field.css +93 -16
  27. package/css/date-picker.css +293 -72
  28. package/css/descriptions.css +11 -5
  29. package/css/dialog.css +47 -14
  30. package/css/download-trigger.css +76 -0
  31. package/css/drawer.css +57 -16
  32. package/css/dynamic-input.css +75 -17
  33. package/css/editable.css +12 -10
  34. package/css/ellipsis.css +7 -1
  35. package/css/empty-state.css +22 -12
  36. package/css/field.css +16 -6
  37. package/css/fieldset.css +69 -0
  38. package/css/file-upload.css +46 -8
  39. package/css/flex.css +13 -7
  40. package/css/float-button.css +23 -1
  41. package/css/floating-panel.css +316 -0
  42. package/css/form.css +4 -4
  43. package/css/grid.css +11 -5
  44. package/css/heatmap.css +411 -0
  45. package/css/highlight.css +2 -2
  46. package/css/hotkeys.css +90 -0
  47. package/css/hover-card.css +43 -45
  48. package/css/image-cropper.css +233 -0
  49. package/css/image-viewer.css +155 -17
  50. package/css/image.css +2 -2
  51. package/css/infinite-scroll.css +1 -1
  52. package/css/json-viewer.css +274 -0
  53. package/css/layout.css +19 -15
  54. package/css/list.css +22 -13
  55. package/css/listbox.css +24 -6
  56. package/css/loading-bar.css +12 -5
  57. package/css/log.css +7 -3
  58. package/css/marquee.css +9 -0
  59. package/css/masonry.css +63 -0
  60. package/css/mention.css +100 -36
  61. package/css/menu.css +127 -50
  62. package/css/menubar.css +155 -61
  63. package/css/navigation-menu.css +56 -31
  64. package/css/notification.css +377 -0
  65. package/css/number-animation.css +3 -3
  66. package/css/number-field.css +182 -19
  67. package/css/overlay-arrow.css +51 -0
  68. package/css/page-header.css +12 -5
  69. package/css/pagination.css +171 -14
  70. package/css/password-input.css +356 -0
  71. package/css/pin-input.css +15 -12
  72. package/css/popconfirm.css +55 -12
  73. package/css/popover.css +73 -45
  74. package/css/popselect.css +207 -57
  75. package/css/progress.css +54 -8
  76. package/css/qr-code.css +1 -1
  77. package/css/radio-group.css +27 -10
  78. package/css/rating.css +4 -4
  79. package/css/reset.css +17 -0
  80. package/css/result.css +21 -11
  81. package/css/scroll-area.css +61 -89
  82. package/css/scrollbar.css +170 -0
  83. package/css/segmented.css +215 -0
  84. package/css/select.css +298 -131
  85. package/css/separator.css +2 -2
  86. package/css/side-nav.css +127 -24
  87. package/css/signature-pad.css +144 -0
  88. package/css/skeleton.css +7 -1
  89. package/css/slider.css +14 -12
  90. package/css/space.css +117 -0
  91. package/css/spinner.css +22 -10
  92. package/css/statistic.css +9 -9
  93. package/css/steps.css +29 -12
  94. package/css/switch.css +57 -8
  95. package/css/table.css +146 -44
  96. package/css/tabs.css +13 -5
  97. package/css/tag.css +179 -0
  98. package/css/tags-input.css +80 -42
  99. package/css/text-field.css +151 -38
  100. package/css/thread.css +11 -3
  101. package/css/time-field.css +99 -18
  102. package/css/time-picker.css +261 -71
  103. package/css/timeline.css +15 -15
  104. package/css/timer.css +119 -0
  105. package/css/toast.css +129 -55
  106. package/css/toggle-group.css +30 -4
  107. package/css/toggle.css +12 -6
  108. package/css/tone.css +57 -22
  109. package/css/toolbar.css +1 -1
  110. package/css/tooltip.css +40 -31
  111. package/css/tour.css +112 -61
  112. package/css/transfer.css +98 -21
  113. package/css/tree-select.css +215 -89
  114. package/css/tree.css +131 -5
  115. package/css/typography.css +5 -5
  116. package/css/undefined.css +12 -1
  117. package/css/virtualizer.css +3 -1
  118. package/css/watermark.css +1 -2
  119. package/index.css +18 -1
  120. package/index.unlayered.css +10356 -3255
  121. package/package.json +20 -4
  122. package/css/toaster.css +0 -74
@@ -5,16 +5,20 @@
5
5
  }
6
6
 
7
7
  /* root 只负责纵向堆叠:标签一行、输入行一行。
8
- 输入框与清空按钮中间那层包裹是作者自己写的、不在角色表里,
9
- 所以尺寸与间距一律长在各自的角色节点上,不去假设那层存在 */
8
+ 输入框与清空按钮收进 control 部件即得一体式:视觉盒画在 control 上,框内两件都退成透明分段。
9
+ 不写 control 时退回两件并排的观感(下方 input 与清空按钮的基础规则原样生效) */
10
10
  [data-scope='text-field'][data-part='root'] {
11
+ /* 作者塞进来的图标跟着控件的尺寸档走;它自己写了 size 时仍以作者的为准 */
12
+ --xh-icon-size: var(--xh-text-field-icon-size, var(--xh-glyph-size-md));
13
+
11
14
  /* 尺寸档只换这几个私有槽,标题、输入框与清空按钮都从 root 继承它们,
12
15
  换档不必给每个部件各写一条选择器 */
13
16
  --xh-_text-field-h: var(--xh-control-h-md);
14
17
  --xh-_text-field-px: var(--xh-control-px-md);
15
- --xh-_text-field-font-size: var(--xh-text-body-size);
16
- --xh-_text-field-label-font-size: var(--xh-text-label-size);
17
- --xh-_text-field-clear-font-size: var(--xh-font-size-sm);
18
+ --xh-_text-field-gap: var(--xh-control-gap-md);
19
+ --xh-_text-field-font-size: var(--xh-control-font-md);
20
+ --xh-_text-field-label-font-size: var(--xh-control-font-md);
21
+ --xh-_text-field-action-font-size: var(--xh-control-caption-md);
18
22
 
19
23
  /* 形态档只换这三个私有槽:底、常态描边、悬停描边 */
20
24
  --xh-_text-field-bg: var(--xh-bg-canvas);
@@ -24,7 +28,7 @@
24
28
  /* 语气只落在聚焦这一处:环与聚焦态描边。正文颜色不归它管。
25
29
  没写 data-tone 时 --xh-_tone 没被声明,退回原来的聚焦环色与常态描边 */
26
30
  --xh-_text-field-ring: var(--xh-ring-focus);
27
- --xh-_text-field-border-focus: var(--xh-text-field-input-border, var(--xh-_tone, var(--xh-border-control)));
31
+ --xh-_text-field-border-focus: var(--xh-text-field-input-border, var(--xh-_tone, var(--xh-border-control-focus)));
28
32
 
29
33
  display: inline-flex;
30
34
  flex-direction: column;
@@ -39,19 +43,23 @@
39
43
  /* —— 尺寸 —— */
40
44
  /* 缺省档写在上面的基础规则里 */
41
45
  [data-scope='text-field'][data-part='root'][data-size='sm'] {
46
+ --xh-icon-size: var(--xh-text-field-icon-size, var(--xh-glyph-size-sm));
42
47
  --xh-_text-field-h: var(--xh-control-h-sm);
43
48
  --xh-_text-field-px: var(--xh-control-px-sm);
44
- --xh-_text-field-font-size: var(--xh-font-size-sm);
45
- --xh-_text-field-label-font-size: var(--xh-font-size-sm);
46
- --xh-_text-field-clear-font-size: var(--xh-font-size-xs);
49
+ --xh-_text-field-gap: var(--xh-control-gap-sm);
50
+ --xh-_text-field-font-size: var(--xh-control-font-sm);
51
+ --xh-_text-field-label-font-size: var(--xh-control-font-sm);
52
+ --xh-_text-field-action-font-size: var(--xh-control-caption-sm);
47
53
  }
48
54
 
49
55
  [data-scope='text-field'][data-part='root'][data-size='lg'] {
56
+ --xh-icon-size: var(--xh-text-field-icon-size, var(--xh-glyph-size-lg));
50
57
  --xh-_text-field-h: var(--xh-control-h-lg);
51
58
  --xh-_text-field-px: var(--xh-control-px-lg);
52
- --xh-_text-field-font-size: var(--xh-font-size-lg);
53
- --xh-_text-field-label-font-size: var(--xh-font-size-lg);
54
- --xh-_text-field-clear-font-size: var(--xh-font-size-md);
59
+ --xh-_text-field-gap: var(--xh-control-gap-lg);
60
+ --xh-_text-field-font-size: var(--xh-control-font-lg);
61
+ --xh-_text-field-label-font-size: var(--xh-control-font-lg);
62
+ --xh-_text-field-action-font-size: var(--xh-control-caption-lg);
55
63
  }
56
64
 
57
65
  /* —— 形态 —— */
@@ -61,7 +69,7 @@
61
69
  --xh-_text-field-bg: var(--xh-bg-canvas);
62
70
  --xh-_text-field-border: var(--xh-border-control);
63
71
  --xh-_text-field-border-hover: var(--xh-border-control-hover);
64
- --xh-_text-field-border-focus: var(--xh-text-field-input-border, var(--xh-_tone, var(--xh-border-control)));
72
+ --xh-_text-field-border-focus: var(--xh-text-field-input-border, var(--xh-_tone, var(--xh-border-control-focus)));
65
73
  }
66
74
 
67
75
  /* 填充式:底色顶替描边表达边界。描边只退成透明而不去掉,1px 的位置留着,
@@ -70,15 +78,15 @@
70
78
  --xh-_text-field-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
71
79
  --xh-_text-field-border: transparent;
72
80
  --xh-_text-field-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
73
- --xh-_text-field-border-focus: var(--xh-_tone, var(--xh-border-control));
81
+ --xh-_text-field-border-focus: var(--xh-_tone, var(--xh-border-control-focus));
74
82
  }
75
83
 
76
- /* 无框:底与描边都透明,平时只看得见字,悬停与聚焦才浮出边界 */
84
+ /* 无框:底与常态描边透明,悬停与聚焦浮出描边 */
77
85
  [data-scope='text-field'][data-part='root'][data-variant='ghost'] {
78
86
  --xh-_text-field-bg: transparent;
79
87
  --xh-_text-field-border: transparent;
80
88
  --xh-_text-field-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
81
- --xh-_text-field-border-focus: var(--xh-_tone, var(--xh-border-control));
89
+ --xh-_text-field-border-focus: var(--xh-_tone, var(--xh-border-control-focus));
82
90
  }
83
91
 
84
92
  [data-scope='text-field'][data-part='label'] {
@@ -92,7 +100,103 @@
92
100
  color: var(--xh-text-field-label-fg-disabled, var(--xh-fg-subtle));
93
101
  }
94
102
 
103
+ /* —— 一体式 control:输入框与清空按钮同框 —— */
104
+ /* control 是唯一的视觉盒:描边、底色、圆角、聚焦环全画在它身上,框内两件都退成透明分段。
105
+ :focus-within 而不是 :focus——持有焦点的是框里的输入框,被聚焦的"控件"在眼里是整个框 */
106
+ [data-scope='text-field'][data-part='control'] {
107
+ display: inline-flex;
108
+ align-items: center;
109
+ gap: var(--xh-text-field-control-gap, var(--xh-_text-field-gap));
110
+ min-inline-size: var(--xh-text-field-control-min-w, var(--xh-control-min-w));
111
+ block-size: var(--xh-text-field-control-h, var(--xh-_text-field-h));
112
+ padding-inline: var(--xh-text-field-control-px, var(--xh-_text-field-px));
113
+ border: var(--xh-stroke-thin) solid var(--xh-text-field-control-border, var(--xh-_text-field-border));
114
+ border-radius: var(--xh-text-field-control-radius, var(--xh-shape-control));
115
+ background: var(--xh-text-field-control-bg, var(--xh-_text-field-bg));
116
+ color: var(--xh-text-field-control-fg, var(--xh-fg-default));
117
+ transition: border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
118
+ }
119
+
120
+ [data-scope='text-field'][data-part='control'][hidden] {
121
+ display: none;
122
+ }
123
+
124
+ /* hover 只在中性态换描边:invalid 与 at-limit 正拿边框颜色说事,
125
+ 被 hover 盖掉那条信息就没了 */
126
+ [data-scope='text-field'][data-part='control']:hover:not([data-disabled], [data-invalid], [data-at-limit]) {
127
+ border-color: var(--xh-text-field-control-border-hover, var(--xh-_text-field-border-hover));
128
+ }
129
+
130
+ [data-scope='text-field'][data-part='control']:focus-within:not([data-disabled]) {
131
+ border-color: var(--xh-text-field-control-border-focus, var(--xh-_text-field-border-focus));
132
+ outline: var(--xh-ring-width) solid var(--xh-_text-field-ring);
133
+ outline-offset: var(--xh-ring-offset);
134
+ }
135
+
136
+ /* 顶到字数上限:再敲进不去,边框先给个提示。放在 invalid 之前,
137
+ 两者同时成立时按源序让 invalid 赢 */
138
+ [data-scope='text-field'][data-part='control'][data-at-limit] {
139
+ border-color: var(--xh-text-field-control-border-at-limit, var(--xh-border-at-limit));
140
+ }
141
+
142
+ [data-scope='text-field'][data-part='control'][data-invalid] {
143
+ border-color: var(--xh-text-field-control-border-invalid, var(--xh-border-invalid));
144
+
145
+ /* 校验失败时环也跟着变红:两圈等宽相邻却一红一蓝,读的人不知道哪个才是当前状态 */
146
+ --xh-_text-field-ring: var(--xh-ring-invalid);
147
+ }
148
+
149
+ /* 只读仍可聚焦、可复制,所以不置灰,只把底色压下去表示"改不动" */
150
+ [data-scope='text-field'][data-part='control'][data-readonly] {
151
+ background: var(--xh-text-field-control-bg-readonly, var(--xh-bg-subtle));
152
+ }
153
+
154
+ [data-scope='text-field'][data-part='control'][data-disabled] {
155
+ background: var(--xh-text-field-control-bg-disabled, var(--xh-bg-subtle));
156
+ color: var(--xh-fg-disabled);
157
+ cursor: not-allowed;
158
+ }
159
+
160
+ /* 盒内 input:无边框无底无圆角,观感全归 control;行内内衬也归 control,
161
+ 这里再留一份就是框内套了一圈空白。它占满剩余宽度,把清空按钮顶到最右 */
162
+ [data-scope='text-field'][data-part='control'] [data-part='input'] {
163
+ flex: 1 1 auto;
164
+ min-inline-size: 0;
165
+ block-size: 100%;
166
+ padding-inline: 0;
167
+ border: 0;
168
+ border-radius: 0;
169
+ background: transparent;
170
+ }
171
+
172
+ /* 盒内 input 不再自己画各态,全部交还 control。hover 这条带上与基础规则同样的
173
+ :not 条件:基础 hover 的特异度里有它们,不带就打不过 */
174
+ [data-scope='text-field'][data-part='control'] [data-part='input']:hover:not(:disabled, [data-invalid], [data-at-limit]) {
175
+ border-color: transparent;
176
+ }
177
+
178
+ [data-scope='text-field'][data-part='control'] [data-part='input']:focus-visible {
179
+ border-color: transparent;
180
+ outline: none;
181
+ }
182
+
183
+ [data-scope='text-field'][data-part='control'] [data-part='input'][data-at-limit],
184
+ [data-scope='text-field'][data-part='control'] [data-part='input'][data-invalid] {
185
+ border-color: transparent;
186
+ }
187
+
188
+ [data-scope='text-field'][data-part='control'] [data-part='input']:disabled,
189
+ [data-scope='text-field'][data-part='control'] [data-part='input'][readonly] {
190
+ background: transparent;
191
+ }
192
+
193
+ /* 尾部动作钮不参与分宽,宽度由它自己的方钮尺寸决定 */
194
+ [data-scope='text-field'][data-part='control'] [data-part='clear-trigger'] {
195
+ flex: none;
196
+ }
197
+
95
198
  [data-scope='text-field'][data-part='input'] {
199
+ min-inline-size: var(--xh-text-field-input-min-w, var(--xh-control-min-w));
96
200
  block-size: var(--xh-text-field-input-h, var(--xh-_text-field-h));
97
201
  padding-inline: var(--xh-text-field-input-px, var(--xh-_text-field-px));
98
202
  padding-block: 0;
@@ -141,11 +245,14 @@
141
245
  /* 顶到字数上限:再敲进不去,边框先给个提示。放在 invalid 之前,
142
246
  两者同时成立时按源序让 invalid 赢——真出错比"写满了"要紧 */
143
247
  [data-scope='text-field'][data-part='input'][data-at-limit] {
144
- border-color: var(--xh-text-field-input-border-at-limit, var(--xh-color-warning-600));
248
+ border-color: var(--xh-text-field-input-border-at-limit, var(--xh-border-at-limit));
145
249
  }
146
250
 
147
251
  [data-scope='text-field'][data-part='input'][data-invalid] {
148
- border-color: var(--xh-text-field-input-border-invalid, var(--xh-color-danger-500));
252
+ border-color: var(--xh-text-field-input-border-invalid, var(--xh-border-invalid));
253
+
254
+ /* 校验失败时环也跟着变红:两圈等宽相邻却一红一蓝,读的人不知道哪个才是当前状态 */
255
+ --xh-_text-field-ring: var(--xh-ring-invalid);
149
256
  }
150
257
 
151
258
  /* 只读仍可聚焦、可复制,所以不置灰,只把底色压下去表示"改不动" */
@@ -160,41 +267,42 @@
160
267
  cursor: not-allowed;
161
268
  }
162
269
 
163
- /* 清空按钮画成输入框右侧的小方钮:它是 input 的兄弟节点而不是子节点,
164
- 没法叠进框内,只能与框等高、贴着排,靠同一套边角与高度看起来是一体的 */
270
+ /* 清空按钮是 input 的兄弟节点而不是子节点,由作者与 input 排成一行;
271
+ 它按行内动作钮的尺寸画,在作者那一行里居中即与框对齐 */
165
272
  [data-scope='text-field'][data-part='clear-trigger'] {
166
273
  display: inline-flex;
167
274
  flex: none;
168
275
  align-items: center;
169
276
  justify-content: center;
170
- inline-size: var(--xh-text-field-clear-size, var(--xh-_text-field-h));
171
- block-size: var(--xh-text-field-clear-size, var(--xh-_text-field-h));
277
+ inline-size: var(--xh-text-field-action-size, var(--xh-control-action-size));
278
+ block-size: var(--xh-text-field-action-size, var(--xh-control-action-size));
172
279
  padding: 0;
173
280
  border: 0;
174
- border-radius: var(--xh-text-field-clear-radius, var(--xh-shape-control));
175
- background: var(--xh-text-field-clear-bg, transparent);
176
- color: var(--xh-text-field-clear-fg, var(--xh-fg-muted));
281
+ border-radius: var(--xh-text-field-action-radius, var(--xh-shape-control));
282
+ background: var(--xh-text-field-action-bg, transparent);
283
+ color: var(--xh-text-field-action-fg, var(--xh-fg-muted));
177
284
  font-family: inherit;
178
- font-size: var(--xh-text-field-clear-font-size, var(--xh-_text-field-clear-font-size));
285
+ font-size: var(--xh-text-field-action-font-size, var(--xh-_text-field-action-font-size));
179
286
  line-height: var(--xh-leading-none);
180
287
  cursor: pointer;
181
288
  user-select: none;
182
- transition: background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
289
+ transition: background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter), scale var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
183
290
  }
184
291
 
185
- /* 没开 clearable 时连接层给的是 hidden 属性;上面那条 display 会把 UA 规则盖掉,
292
+ /* 没开 clearable 或清不了时连接层给的是 hidden 属性;上面那条 display 会把 UA 规则盖掉,
186
293
  少了这条按钮就一直杵在那儿 */
187
294
  [data-scope='text-field'][data-part='clear-trigger'][hidden] {
188
295
  display: none;
189
296
  }
190
297
 
191
- [data-scope='text-field'][data-part='clear-trigger']:hover:not(:disabled) {
192
- background: var(--xh-text-field-clear-bg-hover, var(--xh-bg-subtle-hover));
193
- color: var(--xh-text-field-clear-fg-hover, var(--xh-fg-default));
298
+ [data-scope='text-field'][data-part='clear-trigger']:hover {
299
+ background: var(--xh-text-field-action-bg-hover, var(--xh-bg-subtle-hover));
300
+ color: var(--xh-text-field-action-fg-hover, var(--xh-fg-default));
194
301
  }
195
302
 
196
- [data-scope='text-field'][data-part='clear-trigger']:active:not(:disabled) {
197
- background: var(--xh-text-field-clear-bg-active, var(--xh-bg-subtle-active));
303
+ [data-scope='text-field'][data-part='clear-trigger']:active {
304
+ background: var(--xh-text-field-action-bg-active, var(--xh-bg-subtle-active));
305
+ scale: var(--xh-motion-scale-press);
198
306
  }
199
307
 
200
308
  /* 按钮不占 Tab 位(tabindex=-1)也不抢焦点,正常操作走不到这里;
@@ -204,10 +312,15 @@
204
312
  outline-offset: var(--xh-ring-offset);
205
313
  }
206
314
 
207
- /* 框里没字、只读或禁用时按钮转灰但不收起:位置留着,敲进第一个字就亮 */
208
- [data-scope='text-field'][data-part='clear-trigger']:disabled {
209
- background: var(--xh-text-field-clear-bg, transparent);
210
- color: var(--xh-fg-disabled);
211
- cursor: not-allowed;
315
+ /* 没写内容就由皮肤画兜底字形;作者往部件里塞了自己的图形,这几条即不命中 */
316
+ [data-scope='text-field'][data-part='clear-trigger']:empty::before {
317
+ content: '';
318
+ display: inline-block;
319
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
320
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
321
+ vertical-align: var(--xh-glyph-baseline-shift);
322
+ background-color: currentColor;
323
+ -webkit-mask: var(--xh-glyph-mark-close) center / contain no-repeat;
324
+ mask: var(--xh-glyph-mark-close) center / contain no-repeat;
212
325
  }
213
326
  }
package/css/thread.css CHANGED
@@ -7,6 +7,10 @@
7
7
  /* 自建定位上下文,作为绝对定位的回到底部按钮的参照系;
8
8
  block-size: 100% 让高度由外部决定,使滚动发生在内部。 */
9
9
  [data-scope='thread'][data-part='root'] {
10
+ /* 组件内那几档层号只在自己人之间排序:建一个层叠上下文把它们关起来,
11
+ 既不让页面上的层号插进来,也不让它们漏出去压住别人 */
12
+ isolation: isolate;
13
+
10
14
  position: relative;
11
15
  display: flex;
12
16
  flex-direction: column;
@@ -20,14 +24,18 @@
20
24
 
21
25
  /* 滚动容器,粘底读的是它的 scrollTop / clientHeight / scrollHeight。
22
26
  min-block-size: 0 让弹性子项能被压缩从而触发溢出;
23
- overscroll-behavior: contain 阻止滚动量传给页面;
24
- scrollbar-gutter: stable 避免滚动条出现与消失时推动文字。 */
27
+ overscroll-behavior: contain 阻止滚动量传给页面。 */
25
28
  [data-scope='thread'][data-part='viewport'] {
26
29
  flex: 1;
27
30
  min-block-size: 0;
28
31
  /* 逻辑属性 overflow-block 尚无可用实现,写物理轴 */
29
32
  overflow-y: auto;
30
33
  overscroll-behavior: contain;
34
+ }
35
+
36
+ /* 还在用原生滚动条的视口留一条空道,滚动条出现与消失时不推动文字。
37
+ 带 data-xh-scrollbar 的容器已经把原生条藏成零宽,这条空道对它没有布局作用,排除掉 */
38
+ [data-scope='thread'][data-part='viewport']:not([data-xh-scrollbar]) {
31
39
  scrollbar-gutter: stable;
32
40
  }
33
41
 
@@ -66,7 +74,7 @@
66
74
  border-radius: var(--xh-thread-scroll-button-radius, var(--xh-shape-pill));
67
75
  background: var(--xh-thread-scroll-button-bg, var(--xh-bg-surface));
68
76
  color: var(--xh-thread-scroll-button-fg, var(--xh-fg-default));
69
- box-shadow: var(--xh-thread-scroll-button-shadow, var(--xh-elevation-2));
77
+ box-shadow: var(--xh-thread-scroll-button-shadow, var(--xh-elevation-sheet));
70
78
  /* 按钮默认不继承页面字族,显式继承 */
71
79
  font-family: inherit;
72
80
  font-size: var(--xh-thread-scroll-button-font-size, var(--xh-text-label-size));
@@ -6,11 +6,15 @@
6
6
 
7
7
  /* 与文本框、数字框同一套堆叠:标签一行、控件一行 */
8
8
  [data-scope='time-field'][data-part='root'] {
9
+ /* 作者塞进来的图标跟着控件的尺寸档走;它自己写了 size 时仍以作者的为准 */
10
+ --xh-icon-size: var(--xh-time-field-icon-size, var(--xh-glyph-size-md));
11
+
9
12
  /* 尺寸档只换这四个私有槽,标题与控件都从 root 继承它们,换档不必给每个部件各写一条选择器 */
10
13
  --xh-_time-field-control-h: var(--xh-control-h-md);
11
14
  --xh-_time-field-control-px: var(--xh-control-px-md);
12
- --xh-_time-field-font-size: var(--xh-text-body-size);
13
- --xh-_time-field-label-font-size: var(--xh-text-label-size);
15
+ --xh-_time-field-gap: var(--xh-control-gap-md);
16
+ --xh-_time-field-font-size: var(--xh-control-font-md);
17
+ --xh-_time-field-label-font-size: var(--xh-control-font-md);
14
18
  /* 形态档只换这三个私有槽:底色与两档描边 */
15
19
  --xh-_time-field-control-bg: var(--xh-bg-canvas);
16
20
  --xh-_time-field-control-border: var(--xh-border-control);
@@ -27,17 +31,21 @@
27
31
 
28
32
  /* —— 尺寸 —— */
29
33
  [data-scope='time-field'][data-part='root'][data-size='sm'] {
34
+ --xh-icon-size: var(--xh-time-field-icon-size, var(--xh-glyph-size-sm));
30
35
  --xh-_time-field-control-h: var(--xh-control-h-sm);
31
36
  --xh-_time-field-control-px: var(--xh-control-px-sm);
32
- --xh-_time-field-font-size: var(--xh-font-size-sm);
33
- --xh-_time-field-label-font-size: var(--xh-font-size-sm);
37
+ --xh-_time-field-gap: var(--xh-control-gap-sm);
38
+ --xh-_time-field-font-size: var(--xh-control-font-sm);
39
+ --xh-_time-field-label-font-size: var(--xh-control-font-sm);
34
40
  }
35
41
 
36
42
  [data-scope='time-field'][data-part='root'][data-size='lg'] {
43
+ --xh-icon-size: var(--xh-time-field-icon-size, var(--xh-glyph-size-lg));
37
44
  --xh-_time-field-control-h: var(--xh-control-h-lg);
38
45
  --xh-_time-field-control-px: var(--xh-control-px-lg);
39
- --xh-_time-field-font-size: var(--xh-font-size-lg);
40
- --xh-_time-field-label-font-size: var(--xh-font-size-lg);
46
+ --xh-_time-field-gap: var(--xh-control-gap-lg);
47
+ --xh-_time-field-font-size: var(--xh-control-font-lg);
48
+ --xh-_time-field-label-font-size: var(--xh-control-font-lg);
41
49
  }
42
50
 
43
51
  /* —— 形态 —— */
@@ -55,11 +63,11 @@
55
63
  --xh-_time-field-control-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
56
64
  }
57
65
 
58
- /* 无框式:底与边都让开,可见反馈只剩聚焦那一档 */
66
+ /* 无框:底与常态描边透明,悬停与聚焦浮出描边 */
59
67
  [data-scope='time-field'][data-part='root'][data-variant='ghost'] {
60
68
  --xh-_time-field-control-bg: transparent;
61
69
  --xh-_time-field-control-border: transparent;
62
- --xh-_time-field-control-border-hover: transparent;
70
+ --xh-_time-field-control-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
63
71
  }
64
72
 
65
73
  /* 声明了 display 就盖掉了 UA 的 [hidden]{display:none},收起态得自己还回去 */
@@ -72,9 +80,6 @@
72
80
  font-size: var(--xh-time-field-label-font-size, var(--xh-_time-field-label-font-size));
73
81
  font-weight: var(--xh-time-field-label-font-weight, var(--xh-text-label-weight));
74
82
  line-height: var(--xh-leading-none);
75
- /* 点标题会把焦点送到第一段(段不是能被 for 指向的原生控件,这条由连接层接管),
76
- 指针得如实反映它可点 */
77
- cursor: pointer;
78
83
  }
79
84
 
80
85
  [data-scope='time-field'][data-part='label'][data-disabled] {
@@ -86,7 +91,9 @@
86
91
  段自己只管一小块可聚焦的高亮区 */
87
92
  [data-scope='time-field'][data-part='control'] {
88
93
  display: inline-flex;
94
+ min-inline-size: var(--xh-time-field-control-min-w, var(--xh-control-min-w));
89
95
  align-items: center;
96
+ gap: var(--xh-time-field-control-gap, var(--xh-_time-field-gap));
90
97
  block-size: var(--xh-time-field-control-h, var(--xh-_time-field-control-h));
91
98
  padding-inline: var(--xh-time-field-control-px, var(--xh-_time-field-control-px));
92
99
  padding-block: 0;
@@ -115,11 +122,16 @@
115
122
  /* 焦点真正落在段上,但整块看起来才是一个控件:段内有焦点时整框亮起来。
116
123
  control 自己不占 Tab 位,所以这里写的是 focus-within 而不是 focus-visible */
117
124
  [data-scope='time-field'][data-part='control']:focus-within:not([data-disabled]) {
118
- border-color: var(--xh-time-field-control-border-focus, var(--xh-_time-field-ring));
125
+ border-color: var(--xh-time-field-control-border-focus, var(--xh-_tone, var(--xh-border-control-focus)));
126
+ outline: var(--xh-ring-width) solid var(--xh-_time-field-ring);
127
+ outline-offset: var(--xh-ring-offset);
119
128
  }
120
129
 
121
130
  [data-scope='time-field'][data-part='control'][data-invalid] {
122
- border-color: var(--xh-time-field-control-border-invalid, var(--xh-color-danger-500));
131
+ border-color: var(--xh-time-field-control-border-invalid, var(--xh-border-invalid));
132
+
133
+ /* 校验失败时环也跟着变红:两圈等宽相邻却一红一蓝,读的人不知道哪个才是当前状态 */
134
+ --xh-_time-field-ring: var(--xh-ring-invalid);
123
135
  }
124
136
 
125
137
  /* 只读仍可聚焦、可换段,所以不置灰,只把底色压下去表示"改不动" */
@@ -134,13 +146,25 @@
134
146
  cursor: not-allowed;
135
147
  }
136
148
 
137
- /* 段是一小块可聚焦区,紧挨着排;分隔符由作者写在段与段之间的普通节点里,
138
- 不在角色表内,所以间距只能长在段自己身上 */
149
+ /* 段位与作者写在段间的分隔符都在这一层里:它占满盒里剩下的宽度,
150
+ 清空钮因此靠在框内末端 */
151
+ [data-scope='time-field'][data-part='segment-group'] {
152
+ display: inline-flex;
153
+ flex: 1 1 auto;
154
+ align-items: center;
155
+ min-inline-size: 0;
156
+ }
157
+
158
+ [data-scope='time-field'][data-part='segment-group'][hidden] {
159
+ display: none;
160
+ }
161
+
162
+ /* 段是一小块可聚焦区,紧挨着排;间距长在段自己身上 */
139
163
  [data-scope='time-field'][data-part='segment'] {
140
164
  display: inline-block;
141
- padding-inline: var(--xh-time-field-segment-px, var(--xh-space-0_5));
165
+ padding-inline: var(--xh-time-field-segment-px, var(--xh-space-1));
142
166
  padding-block: 0;
143
- border-radius: var(--xh-time-field-segment-radius, var(--xh-radius-sm));
167
+ border-radius: var(--xh-time-field-segment-radius, var(--xh-shape-inset));
144
168
  color: inherit;
145
169
  font-variant-numeric: inherit;
146
170
  text-align: center;
@@ -159,7 +183,7 @@
159
183
 
160
184
  /* 还没填的段压成弱色:一行里哪几段是占位符,一眼要看得出来 */
161
185
  [data-scope='time-field'][data-part='segment'][data-placeholder] {
162
- color: var(--xh-time-field-segment-fg-placeholder, var(--xh-fg-subtle));
186
+ color: var(--xh-time-field-placeholder-fg, var(--xh-fg-subtle));
163
187
  }
164
188
 
165
189
  /* 一行段长得差不多,光有边框亮起来看不出正在改哪一段:当前段必须自己有底色。
@@ -187,6 +211,63 @@
187
211
  cursor: not-allowed;
188
212
  }
189
213
 
214
+ /* 清空按钮贴在框内末端:不占宽度也不撑高 control,有值时才显形。
215
+ 靠段位外壳的 flex:1 顶到末端,自己不写 margin */
216
+ [data-scope='time-field'][data-part='clear-trigger'] {
217
+ display: inline-flex;
218
+ flex: none;
219
+ align-items: center;
220
+ justify-content: center;
221
+ inline-size: var(--xh-time-field-action-size, var(--xh-control-action-size));
222
+ block-size: var(--xh-time-field-action-size, var(--xh-control-action-size));
223
+ padding: 0;
224
+ border: 0;
225
+ border-radius: var(--xh-time-field-action-radius, var(--xh-shape-control));
226
+ background: var(--xh-time-field-action-bg, transparent);
227
+ color: var(--xh-time-field-action-fg, var(--xh-fg-muted));
228
+ font-family: inherit;
229
+ font-size: var(--xh-time-field-action-font-size, var(--xh-text-secondary-size));
230
+ line-height: var(--xh-leading-none);
231
+ cursor: pointer;
232
+ user-select: none;
233
+ transition:
234
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
235
+ scale var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
236
+ }
237
+
238
+ /* 没值时连接层给的是 hidden 属性;上面那条 display 会把 UA 规则盖掉 */
239
+ [data-scope='time-field'][data-part='clear-trigger'][hidden] {
240
+ display: none;
241
+ }
242
+
243
+ [data-scope='time-field'][data-part='clear-trigger']:hover {
244
+ background: var(--xh-time-field-action-bg-hover, var(--xh-bg-subtle-hover));
245
+ color: var(--xh-time-field-action-fg-hover, var(--xh-fg-default));
246
+ }
247
+
248
+ [data-scope='time-field'][data-part='clear-trigger']:active {
249
+ background: var(--xh-time-field-action-bg-active, var(--xh-bg-subtle-active));
250
+ scale: var(--xh-motion-scale-press);
251
+ }
252
+
253
+ /* 按钮不占 Tab 位(tabindex=-1),这条是给程序化聚焦兜底:落焦时得看得见 */
254
+ [data-scope='time-field'][data-part='clear-trigger']:focus-visible {
255
+ outline: var(--xh-ring-width) solid var(--xh-_time-field-ring);
256
+ outline-offset: var(--xh-ring-offset);
257
+ }
258
+
259
+ /* 没写内容就由皮肤画兜底字形;作者往部件里塞了自己的图形,这条即不命中 */
260
+ [data-scope='time-field'][data-part='clear-trigger']:empty::before {
261
+ content: '';
262
+ display: inline-block;
263
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
264
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
265
+ vertical-align: var(--xh-glyph-baseline-shift);
266
+ background-color: currentColor;
267
+ -webkit-mask: var(--xh-glyph-mark-close) center / contain no-repeat;
268
+ mask: var(--xh-glyph-mark-close) center / contain no-repeat;
269
+ }
270
+
190
271
  /* 连接层把它写成 type=hidden,UA 自己就不渲染;这里的视觉隐藏是给元素升级前那一瞬兜底
191
272
  (作者手写的是一个普通 input,升级完成前它是可见的文本框)。
192
273
  刻意不写 display:写了反而会盖掉 UA 对 type=hidden 的收起,把它显出来 */