@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
@@ -1,15 +1,24 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='text-field'] {
4
+ --xh-text-field-skin: 1;
5
+ }
6
+
2
7
  /* root 只负责纵向堆叠:标签一行、输入行一行。
3
- 输入框与清空按钮中间那层包裹是作者自己写的、不在角色表里,
4
- 所以尺寸与间距一律长在各自的角色节点上,不去假设那层存在 */
8
+ 输入框与清空按钮收进 control 部件即得一体式:视觉盒画在 control 上,框内两件都退成透明分段。
9
+ 不写 control 时退回两件并排的观感(下方 input 与清空按钮的基础规则原样生效) */
5
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
+
6
14
  /* 尺寸档只换这几个私有槽,标题、输入框与清空按钮都从 root 继承它们,
7
15
  换档不必给每个部件各写一条选择器 */
8
16
  --xh-_text-field-h: var(--xh-control-h-md);
9
17
  --xh-_text-field-px: var(--xh-control-px-md);
18
+ --xh-_text-field-gap: var(--xh-control-gap-md);
10
19
  --xh-_text-field-font-size: var(--xh-text-body-size);
11
20
  --xh-_text-field-label-font-size: var(--xh-text-label-size);
12
- --xh-_text-field-clear-font-size: var(--xh-font-size-sm);
21
+ --xh-_text-field-action-font-size: var(--xh-text-secondary-size);
13
22
 
14
23
  /* 形态档只换这三个私有槽:底、常态描边、悬停描边 */
15
24
  --xh-_text-field-bg: var(--xh-bg-canvas);
@@ -19,7 +28,7 @@
19
28
  /* 语气只落在聚焦这一处:环与聚焦态描边。正文颜色不归它管。
20
29
  没写 data-tone 时 --xh-_tone 没被声明,退回原来的聚焦环色与常态描边 */
21
30
  --xh-_text-field-ring: var(--xh-ring-focus);
22
- --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)));
23
32
 
24
33
  display: inline-flex;
25
34
  flex-direction: column;
@@ -34,19 +43,23 @@
34
43
  /* —— 尺寸 —— */
35
44
  /* 缺省档写在上面的基础规则里 */
36
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));
37
47
  --xh-_text-field-h: var(--xh-control-h-sm);
38
48
  --xh-_text-field-px: var(--xh-control-px-sm);
39
- --xh-_text-field-font-size: var(--xh-font-size-sm);
40
- --xh-_text-field-label-font-size: var(--xh-font-size-sm);
41
- --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);
42
53
  }
43
54
 
44
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));
45
57
  --xh-_text-field-h: var(--xh-control-h-lg);
46
58
  --xh-_text-field-px: var(--xh-control-px-lg);
47
- --xh-_text-field-font-size: var(--xh-font-size-lg);
48
- --xh-_text-field-label-font-size: var(--xh-font-size-lg);
49
- --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);
50
63
  }
51
64
 
52
65
  /* —— 形态 —— */
@@ -56,7 +69,7 @@
56
69
  --xh-_text-field-bg: var(--xh-bg-canvas);
57
70
  --xh-_text-field-border: var(--xh-border-control);
58
71
  --xh-_text-field-border-hover: var(--xh-border-control-hover);
59
- --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)));
60
73
  }
61
74
 
62
75
  /* 填充式:底色顶替描边表达边界。描边只退成透明而不去掉,1px 的位置留着,
@@ -65,15 +78,15 @@
65
78
  --xh-_text-field-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
66
79
  --xh-_text-field-border: transparent;
67
80
  --xh-_text-field-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
68
- --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));
69
82
  }
70
83
 
71
- /* 无框:底与描边都透明,平时只看得见字,悬停与聚焦才浮出边界 */
84
+ /* 无框:底与常态描边透明,悬停与聚焦浮出描边 */
72
85
  [data-scope='text-field'][data-part='root'][data-variant='ghost'] {
73
86
  --xh-_text-field-bg: transparent;
74
87
  --xh-_text-field-border: transparent;
75
88
  --xh-_text-field-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
76
- --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));
77
90
  }
78
91
 
79
92
  [data-scope='text-field'][data-part='label'] {
@@ -87,7 +100,103 @@
87
100
  color: var(--xh-text-field-label-fg-disabled, var(--xh-fg-subtle));
88
101
  }
89
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
+
90
198
  [data-scope='text-field'][data-part='input'] {
199
+ min-inline-size: var(--xh-text-field-input-min-w, var(--xh-control-min-w));
91
200
  block-size: var(--xh-text-field-input-h, var(--xh-_text-field-h));
92
201
  padding-inline: var(--xh-text-field-input-px, var(--xh-_text-field-px));
93
202
  padding-block: 0;
@@ -136,11 +245,14 @@
136
245
  /* 顶到字数上限:再敲进不去,边框先给个提示。放在 invalid 之前,
137
246
  两者同时成立时按源序让 invalid 赢——真出错比"写满了"要紧 */
138
247
  [data-scope='text-field'][data-part='input'][data-at-limit] {
139
- 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));
140
249
  }
141
250
 
142
251
  [data-scope='text-field'][data-part='input'][data-invalid] {
143
- 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);
144
256
  }
145
257
 
146
258
  /* 只读仍可聚焦、可复制,所以不置灰,只把底色压下去表示"改不动" */
@@ -155,41 +267,42 @@
155
267
  cursor: not-allowed;
156
268
  }
157
269
 
158
- /* 清空按钮画成输入框右侧的小方钮:它是 input 的兄弟节点而不是子节点,
159
- 没法叠进框内,只能与框等高、贴着排,靠同一套边角与高度看起来是一体的 */
270
+ /* 清空按钮是 input 的兄弟节点而不是子节点,由作者与 input 排成一行;
271
+ 它按行内动作钮的尺寸画,在作者那一行里居中即与框对齐 */
160
272
  [data-scope='text-field'][data-part='clear-trigger'] {
161
273
  display: inline-flex;
162
274
  flex: none;
163
275
  align-items: center;
164
276
  justify-content: center;
165
- inline-size: var(--xh-text-field-clear-size, var(--xh-_text-field-h));
166
- 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));
167
279
  padding: 0;
168
280
  border: 0;
169
- border-radius: var(--xh-text-field-clear-radius, var(--xh-shape-control));
170
- background: var(--xh-text-field-clear-bg, transparent);
171
- 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));
172
284
  font-family: inherit;
173
- 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));
174
286
  line-height: var(--xh-leading-none);
175
287
  cursor: pointer;
176
288
  user-select: none;
177
- 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);
178
290
  }
179
291
 
180
- /* 没开 clearable 时连接层给的是 hidden 属性;上面那条 display 会把 UA 规则盖掉,
292
+ /* 没开 clearable 或清不了时连接层给的是 hidden 属性;上面那条 display 会把 UA 规则盖掉,
181
293
  少了这条按钮就一直杵在那儿 */
182
294
  [data-scope='text-field'][data-part='clear-trigger'][hidden] {
183
295
  display: none;
184
296
  }
185
297
 
186
- [data-scope='text-field'][data-part='clear-trigger']:hover:not(:disabled) {
187
- background: var(--xh-text-field-clear-bg-hover, var(--xh-bg-subtle-hover));
188
- 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));
189
301
  }
190
302
 
191
- [data-scope='text-field'][data-part='clear-trigger']:active:not(:disabled) {
192
- 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);
193
306
  }
194
307
 
195
308
  /* 按钮不占 Tab 位(tabindex=-1)也不抢焦点,正常操作走不到这里;
@@ -199,10 +312,15 @@
199
312
  outline-offset: var(--xh-ring-offset);
200
313
  }
201
314
 
202
- /* 框里没字、只读或禁用时按钮转灰但不收起:位置留着,敲进第一个字就亮 */
203
- [data-scope='text-field'][data-part='clear-trigger']:disabled {
204
- background: var(--xh-text-field-clear-bg, transparent);
205
- color: var(--xh-fg-disabled);
206
- 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;
207
325
  }
208
326
  }
package/css/thread.css CHANGED
@@ -1,4 +1,9 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='thread'] {
4
+ --xh-thread-skin: 1;
5
+ }
6
+
2
7
  /* 自建定位上下文,作为绝对定位的回到底部按钮的参照系;
3
8
  block-size: 100% 让高度由外部决定,使滚动发生在内部。 */
4
9
  [data-scope='thread'][data-part='root'] {
@@ -61,7 +66,7 @@
61
66
  border-radius: var(--xh-thread-scroll-button-radius, var(--xh-shape-pill));
62
67
  background: var(--xh-thread-scroll-button-bg, var(--xh-bg-surface));
63
68
  color: var(--xh-thread-scroll-button-fg, var(--xh-fg-default));
64
- box-shadow: var(--xh-thread-scroll-button-shadow, var(--xh-elevation-2));
69
+ box-shadow: var(--xh-thread-scroll-button-shadow, var(--xh-elevation-sheet));
65
70
  /* 按钮默认不继承页面字族,显式继承 */
66
71
  font-family: inherit;
67
72
  font-size: var(--xh-thread-scroll-button-font-size, var(--xh-text-label-size));
@@ -1,9 +1,18 @@
1
1
  @layer xihan.components {
2
+ /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
3
+ [data-scope='time-field'] {
4
+ --xh-time-field-skin: 1;
5
+ }
6
+
2
7
  /* 与文本框、数字框同一套堆叠:标签一行、控件一行 */
3
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
+
4
12
  /* 尺寸档只换这四个私有槽,标题与控件都从 root 继承它们,换档不必给每个部件各写一条选择器 */
5
13
  --xh-_time-field-control-h: var(--xh-control-h-md);
6
14
  --xh-_time-field-control-px: var(--xh-control-px-md);
15
+ --xh-_time-field-gap: var(--xh-control-gap-md);
7
16
  --xh-_time-field-font-size: var(--xh-text-body-size);
8
17
  --xh-_time-field-label-font-size: var(--xh-text-label-size);
9
18
  /* 形态档只换这三个私有槽:底色与两档描边 */
@@ -22,17 +31,21 @@
22
31
 
23
32
  /* —— 尺寸 —— */
24
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));
25
35
  --xh-_time-field-control-h: var(--xh-control-h-sm);
26
36
  --xh-_time-field-control-px: var(--xh-control-px-sm);
27
- --xh-_time-field-font-size: var(--xh-font-size-sm);
28
- --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);
29
40
  }
30
41
 
31
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));
32
44
  --xh-_time-field-control-h: var(--xh-control-h-lg);
33
45
  --xh-_time-field-control-px: var(--xh-control-px-lg);
34
- --xh-_time-field-font-size: var(--xh-font-size-lg);
35
- --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);
36
49
  }
37
50
 
38
51
  /* —— 形态 —— */
@@ -50,11 +63,11 @@
50
63
  --xh-_time-field-control-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
51
64
  }
52
65
 
53
- /* 无框式:底与边都让开,可见反馈只剩聚焦那一档 */
66
+ /* 无框:底与常态描边透明,悬停与聚焦浮出描边 */
54
67
  [data-scope='time-field'][data-part='root'][data-variant='ghost'] {
55
68
  --xh-_time-field-control-bg: transparent;
56
69
  --xh-_time-field-control-border: transparent;
57
- --xh-_time-field-control-border-hover: transparent;
70
+ --xh-_time-field-control-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
58
71
  }
59
72
 
60
73
  /* 声明了 display 就盖掉了 UA 的 [hidden]{display:none},收起态得自己还回去 */
@@ -67,9 +80,6 @@
67
80
  font-size: var(--xh-time-field-label-font-size, var(--xh-_time-field-label-font-size));
68
81
  font-weight: var(--xh-time-field-label-font-weight, var(--xh-text-label-weight));
69
82
  line-height: var(--xh-leading-none);
70
- /* 点标题会把焦点送到第一段(段不是能被 for 指向的原生控件,这条由连接层接管),
71
- 指针得如实反映它可点 */
72
- cursor: pointer;
73
83
  }
74
84
 
75
85
  [data-scope='time-field'][data-part='label'][data-disabled] {
@@ -81,7 +91,9 @@
81
91
  段自己只管一小块可聚焦的高亮区 */
82
92
  [data-scope='time-field'][data-part='control'] {
83
93
  display: inline-flex;
94
+ min-inline-size: var(--xh-time-field-control-min-w, var(--xh-control-min-w));
84
95
  align-items: center;
96
+ gap: var(--xh-time-field-control-gap, var(--xh-_time-field-gap));
85
97
  block-size: var(--xh-time-field-control-h, var(--xh-_time-field-control-h));
86
98
  padding-inline: var(--xh-time-field-control-px, var(--xh-_time-field-control-px));
87
99
  padding-block: 0;
@@ -110,11 +122,16 @@
110
122
  /* 焦点真正落在段上,但整块看起来才是一个控件:段内有焦点时整框亮起来。
111
123
  control 自己不占 Tab 位,所以这里写的是 focus-within 而不是 focus-visible */
112
124
  [data-scope='time-field'][data-part='control']:focus-within:not([data-disabled]) {
113
- 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);
114
128
  }
115
129
 
116
130
  [data-scope='time-field'][data-part='control'][data-invalid] {
117
- 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);
118
135
  }
119
136
 
120
137
  /* 只读仍可聚焦、可换段,所以不置灰,只把底色压下去表示"改不动" */
@@ -129,13 +146,25 @@
129
146
  cursor: not-allowed;
130
147
  }
131
148
 
132
- /* 段是一小块可聚焦区,紧挨着排;分隔符由作者写在段与段之间的普通节点里,
133
- 不在角色表内,所以间距只能长在段自己身上 */
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
+ /* 段是一小块可聚焦区,紧挨着排;间距长在段自己身上 */
134
163
  [data-scope='time-field'][data-part='segment'] {
135
164
  display: inline-block;
136
- 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));
137
166
  padding-block: 0;
138
- 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));
139
168
  color: inherit;
140
169
  font-variant-numeric: inherit;
141
170
  text-align: center;
@@ -154,7 +183,7 @@
154
183
 
155
184
  /* 还没填的段压成弱色:一行里哪几段是占位符,一眼要看得出来 */
156
185
  [data-scope='time-field'][data-part='segment'][data-placeholder] {
157
- color: var(--xh-time-field-segment-fg-placeholder, var(--xh-fg-subtle));
186
+ color: var(--xh-time-field-placeholder-fg, var(--xh-fg-subtle));
158
187
  }
159
188
 
160
189
  /* 一行段长得差不多,光有边框亮起来看不出正在改哪一段:当前段必须自己有底色。
@@ -182,6 +211,63 @@
182
211
  cursor: not-allowed;
183
212
  }
184
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
+
185
271
  /* 连接层把它写成 type=hidden,UA 自己就不渲染;这里的视觉隐藏是给元素升级前那一瞬兜底
186
272
  (作者手写的是一个普通 input,升级完成前它是可见的文本框)。
187
273
  刻意不写 display:写了反而会盖掉 UA 对 type=hidden 的收起,把它显出来 */