@xihan-ui/styles 1.0.0-alpha.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 (114) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/LICENSE +21 -0
  3. package/README.md +27 -0
  4. package/css/accordion.css +108 -0
  5. package/css/affix.css +27 -0
  6. package/css/alert.css +108 -0
  7. package/css/anchor.css +137 -0
  8. package/css/avatar-group.css +82 -0
  9. package/css/avatar.css +55 -0
  10. package/css/back-top.css +61 -0
  11. package/css/badge.css +50 -0
  12. package/css/breadcrumb.css +127 -0
  13. package/css/button-group.css +131 -0
  14. package/css/button.css +98 -0
  15. package/css/calendar.css +219 -0
  16. package/css/card.css +116 -0
  17. package/css/carousel.css +193 -0
  18. package/css/cascader.css +397 -0
  19. package/css/checkbox-group.css +185 -0
  20. package/css/checkbox.css +91 -0
  21. package/css/clipboard.css +133 -0
  22. package/css/code-block.css +81 -0
  23. package/css/collapsible.css +63 -0
  24. package/css/color-picker.css +424 -0
  25. package/css/combobox.css +433 -0
  26. package/css/composer.css +127 -0
  27. package/css/context-menu.css +263 -0
  28. package/css/countdown.css +21 -0
  29. package/css/date-field.css +200 -0
  30. package/css/date-picker.css +231 -0
  31. package/css/descriptions.css +200 -0
  32. package/css/dialog.css +127 -0
  33. package/css/drawer.css +372 -0
  34. package/css/dynamic-input.css +157 -0
  35. package/css/editable.css +249 -0
  36. package/css/ellipsis.css +41 -0
  37. package/css/empty-state.css +78 -0
  38. package/css/field.css +65 -0
  39. package/css/file-upload.css +298 -0
  40. package/css/flex.css +95 -0
  41. package/css/float-button.css +121 -0
  42. package/css/form.css +185 -0
  43. package/css/gradient-text.css +62 -0
  44. package/css/grid.css +428 -0
  45. package/css/highlight.css +21 -0
  46. package/css/hover-card.css +146 -0
  47. package/css/icon-wrapper.css +64 -0
  48. package/css/icon.css +54 -0
  49. package/css/image.css +45 -0
  50. package/css/infinite-scroll.css +22 -0
  51. package/css/layers.css +2 -0
  52. package/css/layout.css +169 -0
  53. package/css/list.css +117 -0
  54. package/css/listbox.css +174 -0
  55. package/css/loading-bar.css +66 -0
  56. package/css/log.css +66 -0
  57. package/css/marquee.css +182 -0
  58. package/css/mention.css +222 -0
  59. package/css/menu.css +144 -0
  60. package/css/menubar.css +271 -0
  61. package/css/motion.css +58 -0
  62. package/css/navigation-menu.css +270 -0
  63. package/css/number-animation.css +32 -0
  64. package/css/number-field.css +219 -0
  65. package/css/page-header.css +85 -0
  66. package/css/pagination.css +148 -0
  67. package/css/pin-input.css +157 -0
  68. package/css/popconfirm.css +150 -0
  69. package/css/popover.css +150 -0
  70. package/css/popselect.css +207 -0
  71. package/css/progress.css +37 -0
  72. package/css/qr-code.css +50 -0
  73. package/css/radio-group.css +101 -0
  74. package/css/rating.css +141 -0
  75. package/css/reset.css +22 -0
  76. package/css/result.css +104 -0
  77. package/css/scroll-area.css +176 -0
  78. package/css/select.css +264 -0
  79. package/css/separator.css +17 -0
  80. package/css/skeleton.css +78 -0
  81. package/css/slider.css +197 -0
  82. package/css/spinner.css +74 -0
  83. package/css/splitter.css +127 -0
  84. package/css/statistic.css +67 -0
  85. package/css/steps.css +252 -0
  86. package/css/switch.css +61 -0
  87. package/css/table.css +404 -0
  88. package/css/tabs.css +157 -0
  89. package/css/tags-input.css +360 -0
  90. package/css/text-field.css +195 -0
  91. package/css/thread.css +107 -0
  92. package/css/time-field.css +200 -0
  93. package/css/time-picker.css +347 -0
  94. package/css/time.css +20 -0
  95. package/css/timeline.css +284 -0
  96. package/css/toast.css +195 -0
  97. package/css/toaster.css +69 -0
  98. package/css/toggle-group.css +160 -0
  99. package/css/toggle.css +103 -0
  100. package/css/tone.css +85 -0
  101. package/css/toolbar.css +114 -0
  102. package/css/tooltip.css +109 -0
  103. package/css/tour.css +305 -0
  104. package/css/transfer.css +318 -0
  105. package/css/tree-select.css +441 -0
  106. package/css/tree.css +204 -0
  107. package/css/typography.css +151 -0
  108. package/css/undefined.css +38 -0
  109. package/css/virtualizer.css +104 -0
  110. package/css/visually-hidden.css +13 -0
  111. package/css/watermark.css +48 -0
  112. package/index.css +113 -0
  113. package/index.unlayered.css +16164 -0
  114. package/package.json +163 -0
@@ -0,0 +1,200 @@
1
+ @layer xihan.components {
2
+ /* 与文本框、数字框同一套堆叠:标签一行、控件一行 */
3
+ [data-scope='time-field'][data-part='root'] {
4
+ /* 尺寸档只换这四个私有槽,标题与控件都从 root 继承它们,换档不必给每个部件各写一条选择器 */
5
+ --xh-_time-field-control-h: var(--xh-control-h-md);
6
+ --xh-_time-field-control-px: var(--xh-control-px-md);
7
+ --xh-_time-field-font-size: var(--xh-text-body-size);
8
+ --xh-_time-field-label-font-size: var(--xh-text-label-size);
9
+ /* 形态档只换这三个私有槽:底色与两档描边 */
10
+ --xh-_time-field-control-bg: var(--xh-bg-canvas);
11
+ --xh-_time-field-control-border: var(--xh-border-default);
12
+ --xh-_time-field-control-border-hover: var(--xh-border-strong);
13
+ /* 聚焦描边与段位反白随语气走。没写 data-tone 时这几个 --xh-_tone* 没被声明,退回原来的焦点色与品牌色 */
14
+ --xh-_time-field-ring: var(--xh-ring-focus);
15
+ --xh-_time-field-segment-bg: var(--xh-_tone, var(--xh-bg-brand));
16
+ --xh-_time-field-segment-fg: var(--xh-_tone-on, var(--xh-fg-on-brand));
17
+
18
+ display: inline-flex;
19
+ flex-direction: column;
20
+ gap: var(--xh-time-field-gap, var(--xh-space-1));
21
+ }
22
+
23
+ /* —— 尺寸 —— */
24
+ [data-scope='time-field'][data-part='root'][data-size='sm'] {
25
+ --xh-_time-field-control-h: var(--xh-control-h-sm);
26
+ --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);
29
+ }
30
+
31
+ [data-scope='time-field'][data-part='root'][data-size='lg'] {
32
+ --xh-_time-field-control-h: var(--xh-control-h-lg);
33
+ --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);
36
+ }
37
+
38
+ /* —— 形态 —— */
39
+ /* 描边式:与缺省等价,写出来是为了三档形态在皮肤里都看得见 */
40
+ [data-scope='time-field'][data-part='root'][data-variant='outline'] {
41
+ --xh-_time-field-control-bg: var(--xh-bg-canvas);
42
+ --xh-_time-field-control-border: var(--xh-border-default);
43
+ --xh-_time-field-control-border-hover: var(--xh-border-strong);
44
+ }
45
+
46
+ /* 填充式:底色顶替描边,悬停时才浮出一道淡边 */
47
+ [data-scope='time-field'][data-part='root'][data-variant='subtle'] {
48
+ --xh-_time-field-control-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
49
+ --xh-_time-field-control-border: transparent;
50
+ --xh-_time-field-control-border-hover: var(--xh-_tone-border, var(--xh-border-default));
51
+ }
52
+
53
+ /* 无框式:底与边都让开,可见反馈只剩聚焦那一档 */
54
+ [data-scope='time-field'][data-part='root'][data-variant='ghost'] {
55
+ --xh-_time-field-control-bg: transparent;
56
+ --xh-_time-field-control-border: transparent;
57
+ --xh-_time-field-control-border-hover: transparent;
58
+ }
59
+
60
+ /* 声明了 display 就盖掉了 UA 的 [hidden]{display:none},收起态得自己还回去 */
61
+ [data-scope='time-field'][data-part='root'][hidden] {
62
+ display: none;
63
+ }
64
+
65
+ [data-scope='time-field'][data-part='label'] {
66
+ color: var(--xh-time-field-label-fg, var(--xh-fg-default));
67
+ font-size: var(--xh-time-field-label-font-size, var(--xh-_time-field-label-font-size));
68
+ font-weight: var(--xh-time-field-label-font-weight, var(--xh-text-label-weight));
69
+ line-height: var(--xh-leading-none);
70
+ /* 点标题会把焦点送到第一段(段不是能被 for 指向的原生控件,这条由连接层接管),
71
+ 指针得如实反映它可点 */
72
+ cursor: pointer;
73
+ }
74
+
75
+ [data-scope='time-field'][data-part='label'][data-disabled] {
76
+ color: var(--xh-time-field-label-fg-disabled, var(--xh-fg-subtle));
77
+ cursor: not-allowed;
78
+ }
79
+
80
+ /* 几段合起来看着像一个输入框:边框、高度、内边距全长在 control 上,
81
+ 段自己只管一小块可聚焦的高亮区 */
82
+ [data-scope='time-field'][data-part='control'] {
83
+ display: inline-flex;
84
+ align-items: center;
85
+ block-size: var(--xh-time-field-control-h, var(--xh-_time-field-control-h));
86
+ padding-inline: var(--xh-time-field-control-px, var(--xh-_time-field-control-px));
87
+ padding-block: 0;
88
+ border: var(--xh-stroke-thin) solid var(--xh-time-field-control-border, var(--xh-_time-field-control-border));
89
+ border-radius: var(--xh-time-field-control-radius, var(--xh-shape-control));
90
+ background: var(--xh-time-field-control-bg, var(--xh-_time-field-control-bg));
91
+ color: var(--xh-time-field-control-fg, var(--xh-fg-default));
92
+ /* 表单控件不继承页面字体,只给字号会让它用 UA 默认字族,与周围文本对不上 */
93
+ font-family: inherit;
94
+ font-size: var(--xh-time-field-font-size, var(--xh-_time-field-font-size));
95
+ /* 等宽数字:各位字宽不一时逐段填入会让整行左右跳 */
96
+ font-variant-numeric: tabular-nums;
97
+ line-height: var(--xh-leading-normal);
98
+ transition: border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
99
+ }
100
+
101
+ [data-scope='time-field'][data-part='control'][hidden] {
102
+ display: none;
103
+ }
104
+
105
+ /* hover 只在中性态换描边:invalid 正拿边框颜色说事,被 hover 盖掉那条信息就没了 */
106
+ [data-scope='time-field'][data-part='control']:hover:not([data-disabled], [data-invalid]) {
107
+ border-color: var(--xh-time-field-control-border-hover, var(--xh-_time-field-control-border-hover));
108
+ }
109
+
110
+ /* 焦点真正落在段上,但整块看起来才是一个控件:段内有焦点时整框亮起来。
111
+ control 自己不占 Tab 位,所以这里写的是 focus-within 而不是 focus-visible */
112
+ [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));
114
+ }
115
+
116
+ [data-scope='time-field'][data-part='control'][data-invalid] {
117
+ border-color: var(--xh-time-field-control-border-invalid, var(--xh-color-danger-500));
118
+ }
119
+
120
+ /* 只读仍可聚焦、可换段,所以不置灰,只把底色压下去表示"改不动" */
121
+ [data-scope='time-field'][data-part='control'][data-readonly] {
122
+ background: var(--xh-time-field-control-bg-readonly, var(--xh-bg-subtle));
123
+ }
124
+
125
+ /* control 是个 div,没有 :disabled 可用;禁用态由连接层的 data-disabled 表达 */
126
+ [data-scope='time-field'][data-part='control'][data-disabled] {
127
+ background: var(--xh-time-field-control-bg-disabled, var(--xh-bg-subtle));
128
+ color: var(--xh-fg-disabled);
129
+ cursor: not-allowed;
130
+ }
131
+
132
+ /* 段是一小块可聚焦区,紧挨着排;分隔符由作者写在段与段之间的普通节点里,
133
+ 不在角色表内,所以间距只能长在段自己身上 */
134
+ [data-scope='time-field'][data-part='segment'] {
135
+ display: inline-block;
136
+ padding-inline: var(--xh-time-field-segment-px, var(--xh-space-0_5));
137
+ padding-block: 0;
138
+ border-radius: var(--xh-time-field-segment-radius, var(--xh-radius-sm));
139
+ color: inherit;
140
+ font-variant-numeric: inherit;
141
+ text-align: center;
142
+ /* 段是 role=spinbutton 的展示节点,双击选中一段文字只会碍事 */
143
+ user-select: none;
144
+ transition:
145
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
146
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
147
+ }
148
+
149
+ /* granularity / hourCycle 关掉的段由连接层打 hidden;上面那条 display 会把 UA 规则盖掉,
150
+ 少了这条,收起的段会一直杵在那儿 */
151
+ [data-scope='time-field'][data-part='segment'][hidden] {
152
+ display: none;
153
+ }
154
+
155
+ /* 还没填的段压成弱色:一行里哪几段是占位符,一眼要看得出来 */
156
+ [data-scope='time-field'][data-part='segment'][data-placeholder] {
157
+ color: var(--xh-time-field-segment-fg-placeholder, var(--xh-fg-subtle));
158
+ }
159
+
160
+ /* 一行段长得差不多,光有边框亮起来看不出正在改哪一段:当前段必须自己有底色。
161
+ data-focus 由机器给(它知道谁是当前段),:focus-visible 兜住机器状态还没回流的那一帧,
162
+ 两条同时写不冲突 */
163
+ [data-scope='time-field'][data-part='segment'][data-focus]:not([data-disabled]),
164
+ [data-scope='time-field'][data-part='segment']:focus-visible {
165
+ background: var(--xh-time-field-segment-bg-focus, var(--xh-_time-field-segment-bg));
166
+ color: var(--xh-time-field-segment-fg-focus, var(--xh-_time-field-segment-fg));
167
+ outline: none;
168
+ }
169
+
170
+ /* 选中态用了品牌底色之后,占位符那条弱色会被它盖住,这里明确让位给焦点态 */
171
+ [data-scope='time-field'][data-part='segment'][data-focus][data-placeholder]:not([data-disabled]) {
172
+ color: var(--xh-time-field-segment-fg-focus, var(--xh-_time-field-segment-fg));
173
+ }
174
+
175
+ [data-scope='time-field'][data-part='segment']:hover:not([data-focus], [data-disabled]) {
176
+ background: var(--xh-time-field-segment-bg-hover, var(--xh-bg-subtle-hover));
177
+ }
178
+
179
+ /* 段是集合条目形态(role=spinbutton 的 span),禁用走 data-disabled 而不是 :disabled */
180
+ [data-scope='time-field'][data-part='segment'][data-disabled] {
181
+ color: var(--xh-fg-disabled);
182
+ cursor: not-allowed;
183
+ }
184
+
185
+ /* 连接层把它写成 type=hidden,UA 自己就不渲染;这里的视觉隐藏是给元素升级前那一瞬兜底
186
+ (作者手写的是一个普通 input,升级完成前它是可见的文本框)。
187
+ 刻意不写 display:写了反而会盖掉 UA 对 type=hidden 的收起,把它显出来 */
188
+ [data-scope='time-field'][data-part='hidden-input'] {
189
+ position: absolute;
190
+ inline-size: 1px;
191
+ block-size: 1px;
192
+ padding: 0;
193
+ margin: -1px;
194
+ border: 0;
195
+ overflow: hidden;
196
+ clip-path: inset(50%);
197
+ white-space: nowrap;
198
+ pointer-events: none;
199
+ }
200
+ }
@@ -0,0 +1,347 @@
1
+ @layer xihan.components {
2
+ /* 与文本框、时间输入同一套堆叠:标签一行、控件一行 */
3
+ [data-scope='time-picker'][data-part='root'] {
4
+ display: inline-flex;
5
+ flex-direction: column;
6
+ gap: var(--xh-time-picker-gap, var(--xh-space-1));
7
+ }
8
+
9
+ /* 声明了 display 就盖掉了 UA 的 [hidden]{display:none},收起态得自己还回去 */
10
+ [data-scope='time-picker'][data-part='root'][hidden] {
11
+ display: none;
12
+ }
13
+
14
+ [data-scope='time-picker'][data-part='label'] {
15
+ color: var(--xh-time-picker-label-fg, var(--xh-fg-default));
16
+ font-size: var(--xh-time-picker-label-font-size, var(--xh-text-label-size));
17
+ font-weight: var(--xh-time-picker-label-font-weight, var(--xh-text-label-weight));
18
+ line-height: var(--xh-leading-none);
19
+ /* 点标题会把焦点送到第一段(段不是能被 for 指向的原生控件,这条由连接层接管),
20
+ 指针得如实反映它可点 */
21
+ cursor: pointer;
22
+ }
23
+
24
+ [data-scope='time-picker'][data-part='label'][data-disabled] {
25
+ color: var(--xh-time-picker-label-fg-disabled, var(--xh-fg-subtle));
26
+ cursor: not-allowed;
27
+ }
28
+
29
+ /* 画成一个框的是 control:几段文字、展开按钮、清空按钮要看起来是一体的,
30
+ 边框长在其中任何一个身上都会把另外几个割在框外 */
31
+ [data-scope='time-picker'][data-part='control'] {
32
+ display: inline-flex;
33
+ align-items: center;
34
+ gap: var(--xh-time-picker-control-gap, var(--xh-control-gap-md));
35
+ block-size: var(--xh-time-picker-control-h, var(--xh-control-h-md));
36
+ padding-inline: var(--xh-time-picker-control-px, var(--xh-control-px-md));
37
+ padding-block: 0;
38
+ border: var(--xh-stroke-thin) solid var(--xh-time-picker-control-border, var(--xh-border-default));
39
+ border-radius: var(--xh-time-picker-control-radius, var(--xh-shape-control));
40
+ background: var(--xh-time-picker-control-bg, var(--xh-bg-canvas));
41
+ color: var(--xh-time-picker-control-fg, var(--xh-fg-default));
42
+ /* 表单控件不继承页面字体,只给字号会让它用 UA 默认字族,与周围文本对不上 */
43
+ font-family: inherit;
44
+ font-size: var(--xh-time-picker-font-size, var(--xh-text-body-size));
45
+ /* 等宽数字:各位字宽不一时逐段填入会让整行左右跳 */
46
+ font-variant-numeric: tabular-nums;
47
+ line-height: var(--xh-leading-normal);
48
+ transition: border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
49
+ }
50
+
51
+ [data-scope='time-picker'][data-part='control'][hidden] {
52
+ display: none;
53
+ }
54
+
55
+ /* hover 只在中性态换描边:invalid 正拿边框颜色说事,被 hover 盖掉那条信息就没了 */
56
+ [data-scope='time-picker'][data-part='control']:hover:not([data-disabled], [data-invalid]) {
57
+ border-color: var(--xh-time-picker-control-border-hover, var(--xh-border-strong));
58
+ }
59
+
60
+ /* 焦点真正落在段或按钮上,但整块看起来才是一个控件:内部有焦点时整框亮起来。
61
+ control 自己不占 Tab 位,所以这里写的是 focus-within 而不是 focus-visible */
62
+ [data-scope='time-picker'][data-part='control']:focus-within:not([data-disabled]) {
63
+ border-color: var(--xh-time-picker-control-border-focus, var(--xh-ring-focus));
64
+ }
65
+
66
+ [data-scope='time-picker'][data-part='control'][data-invalid] {
67
+ border-color: var(--xh-time-picker-control-border-invalid, var(--xh-color-danger-500));
68
+ }
69
+
70
+ /* 只读仍可聚焦、可换段,所以不置灰,只把底色压下去表示"改不动" */
71
+ [data-scope='time-picker'][data-part='control'][data-readonly] {
72
+ background: var(--xh-time-picker-control-bg-readonly, var(--xh-bg-subtle));
73
+ }
74
+
75
+ /* control 是个 div,没有 :disabled 可用;禁用态由连接层的 data-disabled 表达 */
76
+ [data-scope='time-picker'][data-part='control'][data-disabled] {
77
+ background: var(--xh-time-picker-control-bg-disabled, var(--xh-bg-subtle));
78
+ color: var(--xh-fg-disabled);
79
+ cursor: not-allowed;
80
+ }
81
+
82
+ /* 每一段是一小块可聚焦区,紧挨着排;分隔符由作者写在段与段之间的普通节点里,
83
+ 不在角色表内,所以间距只能长在段自己身上 */
84
+ [data-scope='time-picker'][data-part='input'] {
85
+ display: inline-block;
86
+ padding-inline: var(--xh-time-picker-segment-px, var(--xh-space-0_5));
87
+ padding-block: 0;
88
+ border-radius: var(--xh-time-picker-segment-radius, var(--xh-radius-sm));
89
+ color: inherit;
90
+ font-variant-numeric: inherit;
91
+ text-align: center;
92
+ /* 段是 role=spinbutton 的展示节点,双击选中一段文字只会碍事 */
93
+ user-select: none;
94
+ transition:
95
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
96
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
97
+ }
98
+
99
+ /* granularity / hourCycle 关掉的段由连接层打 hidden;上面那条 display 会把 UA 规则盖掉,
100
+ 少了这条,收起的段会一直杵在那儿 */
101
+ [data-scope='time-picker'][data-part='input'][hidden] {
102
+ display: none;
103
+ }
104
+
105
+ /* 还没填的段压成弱色:一行里哪几段是占位符,一眼要看得出来 */
106
+ [data-scope='time-picker'][data-part='input'][data-placeholder] {
107
+ color: var(--xh-time-picker-segment-fg-placeholder, var(--xh-fg-subtle));
108
+ }
109
+
110
+ /* 一行段长得差不多,光有边框亮起来看不出正在改哪一段:当前段必须自己有底色。
111
+ data-focus 由机器给(它知道谁是当前段),:focus-visible 兜住机器状态还没回流的那一帧 */
112
+ [data-scope='time-picker'][data-part='input'][data-focus]:not([data-disabled]),
113
+ [data-scope='time-picker'][data-part='input']:focus-visible {
114
+ background: var(--xh-time-picker-segment-bg-focus, var(--xh-bg-brand));
115
+ color: var(--xh-time-picker-segment-fg-focus, var(--xh-fg-on-brand));
116
+ outline: none;
117
+ }
118
+
119
+ /* 焦点态用了品牌底色之后,占位符那条弱色会被它盖住,这里明确让位给焦点态 */
120
+ [data-scope='time-picker'][data-part='input'][data-focus][data-placeholder]:not([data-disabled]) {
121
+ color: var(--xh-time-picker-segment-fg-focus, var(--xh-fg-on-brand));
122
+ }
123
+
124
+ [data-scope='time-picker'][data-part='input']:hover:not([data-focus], [data-disabled]) {
125
+ background: var(--xh-time-picker-segment-bg-hover, var(--xh-bg-subtle-hover));
126
+ }
127
+
128
+ /* 段是集合条目形态(role=spinbutton 的 span),禁用走 data-disabled 而不是 :disabled */
129
+ [data-scope='time-picker'][data-part='input'][data-disabled] {
130
+ color: var(--xh-fg-disabled);
131
+ cursor: not-allowed;
132
+ }
133
+
134
+ /* 两个按钮同一副长相:贴在框内的小方钮,不占宽度也不撑高 control */
135
+ [data-scope='time-picker'][data-part='trigger'],
136
+ [data-scope='time-picker'][data-part='clear-trigger'] {
137
+ display: inline-flex;
138
+ flex: none;
139
+ align-items: center;
140
+ justify-content: center;
141
+ inline-size: var(--xh-time-picker-action-size, var(--xh-control-action-size));
142
+ block-size: var(--xh-time-picker-action-size, var(--xh-control-action-size));
143
+ padding: 0;
144
+ border: 0;
145
+ border-radius: var(--xh-time-picker-action-radius, var(--xh-shape-control));
146
+ background: var(--xh-time-picker-action-bg, transparent);
147
+ color: var(--xh-time-picker-action-fg, var(--xh-fg-muted));
148
+ font-family: inherit;
149
+ font-size: var(--xh-time-picker-action-font-size, var(--xh-font-size-sm));
150
+ line-height: var(--xh-leading-none);
151
+ cursor: pointer;
152
+ user-select: none;
153
+ transition: background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
154
+ }
155
+
156
+ [data-scope='time-picker'][data-part='trigger'][hidden],
157
+ [data-scope='time-picker'][data-part='clear-trigger'][hidden] {
158
+ display: none;
159
+ }
160
+
161
+ /* 触发器把整行推到最右:它与清空按钮之间不留缝,两者一起靠框内末端 */
162
+ [data-scope='time-picker'][data-part='trigger'] {
163
+ margin-inline-start: auto;
164
+ }
165
+
166
+ [data-scope='time-picker'][data-part='trigger']:hover:not(:disabled),
167
+ [data-scope='time-picker'][data-part='clear-trigger']:hover:not(:disabled) {
168
+ background: var(--xh-time-picker-action-bg-hover, var(--xh-bg-subtle-hover));
169
+ color: var(--xh-time-picker-action-fg-hover, var(--xh-fg-default));
170
+ }
171
+
172
+ [data-scope='time-picker'][data-part='trigger']:active:not(:disabled),
173
+ [data-scope='time-picker'][data-part='clear-trigger']:active:not(:disabled) {
174
+ background: var(--xh-time-picker-action-bg-active, var(--xh-bg-subtle-active));
175
+ }
176
+
177
+ /* 触发器是常规 Tab 停靠点,落焦必须看得见;清空按钮退出了 Tab 序列,
178
+ 这条是给程序化聚焦兜底 */
179
+ [data-scope='time-picker'][data-part='trigger']:focus-visible,
180
+ [data-scope='time-picker'][data-part='clear-trigger']:focus-visible {
181
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
182
+ outline-offset: var(--xh-ring-offset);
183
+ }
184
+
185
+ /* 两个按钮都是单体控件,禁用走原生 :disabled(列里的格一律 data-disabled) */
186
+ [data-scope='time-picker'][data-part='trigger']:disabled,
187
+ [data-scope='time-picker'][data-part='clear-trigger']:disabled {
188
+ background: var(--xh-time-picker-action-bg, transparent);
189
+ color: var(--xh-fg-disabled);
190
+ cursor: not-allowed;
191
+ }
192
+
193
+ /* 坐标由定位引擎写进 style(inset-inline-start / inset-block-start),这里只定位模式与层级 */
194
+ [data-scope='time-picker'][data-part='positioner'] {
195
+ position: fixed;
196
+ z-index: var(--xh-layer-popover);
197
+ /* 空壳不吃指针,交互只发生在 content 里 */
198
+ pointer-events: none;
199
+ }
200
+
201
+ /* 锚点被滚出可视区:整个浮层随之收起 */
202
+ [data-scope='time-picker'][data-part='positioner'][data-hidden] {
203
+ visibility: hidden;
204
+ }
205
+
206
+ /* 几列并排:整体高度由列自己撑,滚动发生在列里而不是整个面板上——
207
+ 整面板滚会让时列与分列一起走,挑分的时候时就看不见了 */
208
+ [data-scope='time-picker'][data-part='content'] {
209
+ display: flex;
210
+ align-items: stretch;
211
+ padding: var(--xh-time-picker-content-p, var(--xh-space-1));
212
+ border: var(--xh-stroke-thin) solid var(--xh-time-picker-content-border, var(--xh-border-default));
213
+ border-radius: var(--xh-time-picker-content-radius, var(--xh-shape-surface));
214
+ background: var(--xh-time-picker-content-bg, var(--xh-bg-surface));
215
+ color: var(--xh-time-picker-content-fg, var(--xh-fg-default));
216
+ font-variant-numeric: tabular-nums;
217
+ box-shadow: var(--xh-time-picker-content-shadow, var(--xh-elevation-2));
218
+ pointer-events: auto;
219
+ }
220
+
221
+ /* display 会盖掉 hidden 属性的 UA 规则,收起态显式让位 */
222
+ [data-scope='time-picker'][data-part='content'][hidden] {
223
+ display: none;
224
+ }
225
+
226
+ /* 面板本身不承载焦点(焦点在某一列的某一格上),落焦时不画环 */
227
+ [data-scope='time-picker'][data-part='content']:focus-visible {
228
+ outline: none;
229
+ }
230
+
231
+ [data-scope='time-picker'][data-part='content'][data-state='open'] {
232
+ animation: xh-time-picker-panel-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
233
+ }
234
+
235
+ /* 一列一个滚动区:列与列之间用细分隔线,视觉上"时 | 分 | 秒"三栏分明 */
236
+ [data-scope='time-picker'][data-part='column'] {
237
+ display: flex;
238
+ flex: none;
239
+ flex-direction: column;
240
+ gap: var(--xh-time-picker-column-gap, var(--xh-space-0_5));
241
+ min-inline-size: var(--xh-time-picker-column-min-w, 3.5rem);
242
+ max-block-size: var(--xh-time-picker-column-max-h, 12rem);
243
+ padding-inline: var(--xh-time-picker-column-px, var(--xh-space-1));
244
+ overflow-y: auto;
245
+ /* 滚动容器在某些浏览器里会被自动塞进 Tab 序列,落焦时不画环——
246
+ 焦点该落在格上,列只在整列被界卡空时才兜底占一个 Tab 位 */
247
+ scrollbar-width: thin;
248
+ }
249
+
250
+ /* granularity 关掉的列(精度不到秒时的秒列)由连接层打 hidden */
251
+ [data-scope='time-picker'][data-part='column'][hidden] {
252
+ display: none;
253
+ }
254
+
255
+ [data-scope='time-picker'][data-part='column'] + [data-scope='time-picker'][data-part='column'] {
256
+ border-inline-start: var(--xh-stroke-thin) solid var(--xh-time-picker-column-divider, var(--xh-border-subtle));
257
+ }
258
+
259
+ [data-scope='time-picker'][data-part='column']:focus-visible {
260
+ outline: none;
261
+ }
262
+
263
+ [data-scope='time-picker'][data-part='option'] {
264
+ display: flex;
265
+ flex: none;
266
+ align-items: center;
267
+ justify-content: center;
268
+ padding-block: var(--xh-time-picker-option-py, var(--xh-space-1));
269
+ padding-inline: var(--xh-time-picker-option-px, var(--xh-space-2));
270
+ border-radius: var(--xh-time-picker-option-radius, var(--xh-shape-control));
271
+ color: var(--xh-time-picker-option-fg, var(--xh-fg-default));
272
+ font-size: var(--xh-time-picker-option-font-size, var(--xh-text-body-size));
273
+ line-height: var(--xh-leading-none);
274
+ white-space: nowrap;
275
+ cursor: pointer;
276
+ user-select: none;
277
+ }
278
+
279
+ /* 作者自己藏掉某几格时要让位,有 display 就得补这条 */
280
+ [data-scope='time-picker'][data-part='option'][hidden] {
281
+ display: none;
282
+ }
283
+
284
+ /* 焦点所在与选中互相独立:可以停在一个没被选中的格上 */
285
+ [data-scope='time-picker'][data-part='option'][data-highlighted]:not([data-disabled]),
286
+ [data-scope='time-picker'][data-part='option']:hover:not([data-disabled]) {
287
+ background: var(--xh-time-picker-option-bg-highlight, var(--xh-bg-subtle-hover));
288
+ }
289
+
290
+ [data-scope='time-picker'][data-part='option'][data-state='checked'] {
291
+ background: var(--xh-time-picker-option-bg-checked, var(--xh-bg-brand));
292
+ color: var(--xh-time-picker-option-fg-checked, var(--xh-fg-on-brand));
293
+ font-weight: var(--xh-time-picker-option-weight-checked, var(--xh-font-weight-medium));
294
+ }
295
+
296
+ /* 选中格上再落焦点或悬停时,底色留在品牌色一侧:上面那条中性高亮的选择器更具体,
297
+ 不写这条就只有底色被换走、文字仍是品牌底上的反白色 */
298
+ [data-scope='time-picker'][data-part='option'][data-state='checked'][data-highlighted]:not([data-disabled]),
299
+ [data-scope='time-picker'][data-part='option'][data-state='checked']:hover:not([data-disabled]) {
300
+ background: var(--xh-time-picker-option-bg-checked-highlight, var(--xh-bg-brand-hover));
301
+ }
302
+
303
+ [data-scope='time-picker'][data-part='option']:focus-visible {
304
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
305
+ /* 往内收:选项在滚动列里挨着排,环画在外沿会被列的溢出裁掉 */
306
+ outline-offset: calc(-1 * var(--xh-ring-offset));
307
+ }
308
+
309
+ /* 禁用走 aria-disabled 而非原生 disabled(原生 disabled 不派 click,禁用守卫就走不到),
310
+ 所以只能靠 data-disabled 表达,写 :disabled 永远不命中 */
311
+ [data-scope='time-picker'][data-part='option'][data-disabled] {
312
+ color: var(--xh-fg-disabled);
313
+ cursor: not-allowed;
314
+ }
315
+
316
+ /* 连接层把它写成 type=hidden,UA 自己就不渲染;这里的视觉隐藏是给元素升级前那一瞬兜底
317
+ (作者手写的是一个普通 input,升级完成前它是可见的文本框)。
318
+ 刻意不写 display:写了反而会盖掉 UA 对 type=hidden 的收起,把它显出来 */
319
+ [data-scope='time-picker'][data-part='hidden-input'] {
320
+ position: absolute;
321
+ inline-size: 1px;
322
+ block-size: 1px;
323
+ padding: 0;
324
+ margin: -1px;
325
+ border: 0;
326
+ overflow: hidden;
327
+ clip-path: inset(50%);
328
+ white-space: nowrap;
329
+ pointer-events: none;
330
+ }
331
+ }
332
+
333
+ @layer xihan.motion {
334
+ /* 浮层进场:淡入 + 轻微上浮,方向与它从输入行下方展开一致。
335
+ 关键帧写在自己文件里并带组件前缀,不去动共享的 motion.css */
336
+ @keyframes xh-time-picker-panel-in {
337
+ from {
338
+ opacity: 0;
339
+ translate: 0 calc(-1 * var(--xh-space-1));
340
+ }
341
+
342
+ to {
343
+ opacity: 1;
344
+ translate: 0 0;
345
+ }
346
+ }
347
+ }
package/css/time.css ADDED
@@ -0,0 +1,20 @@
1
+ @layer xihan.components {
2
+ [data-scope='time'][data-part='root'] {
3
+ color: var(--xh-time-fg, var(--xh-fg-default));
4
+ /* 等宽数字:秒数跳动时后面的字符不会左右挪 */
5
+ font-variant-numeric: tabular-nums;
6
+ /* 一个时间戳不该被拆到两行去 */
7
+ white-space: nowrap;
8
+ }
9
+
10
+ /* 相对说法是一句话不是一串数字,等宽数字反而把字距拉开 */
11
+ [data-scope='time'][data-part='root'][data-relative] {
12
+ font-variant-numeric: normal;
13
+ }
14
+
15
+ /* 没给时刻、或给了认不出的时刻:这时显示的是作者写在节点里的兜底文案,退成弱前景 */
16
+ [data-scope='time'][data-part='root'][data-state='empty'],
17
+ [data-scope='time'][data-part='root'][data-state='invalid'] {
18
+ color: var(--xh-time-placeholder-fg, var(--xh-fg-muted));
19
+ }
20
+ }