@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.
- package/CHANGELOG.md +44 -0
- package/LICENSE +21 -0
- package/README.md +27 -0
- package/css/accordion.css +108 -0
- package/css/affix.css +27 -0
- package/css/alert.css +108 -0
- package/css/anchor.css +137 -0
- package/css/avatar-group.css +82 -0
- package/css/avatar.css +55 -0
- package/css/back-top.css +61 -0
- package/css/badge.css +50 -0
- package/css/breadcrumb.css +127 -0
- package/css/button-group.css +131 -0
- package/css/button.css +98 -0
- package/css/calendar.css +219 -0
- package/css/card.css +116 -0
- package/css/carousel.css +193 -0
- package/css/cascader.css +397 -0
- package/css/checkbox-group.css +185 -0
- package/css/checkbox.css +91 -0
- package/css/clipboard.css +133 -0
- package/css/code-block.css +81 -0
- package/css/collapsible.css +63 -0
- package/css/color-picker.css +424 -0
- package/css/combobox.css +433 -0
- package/css/composer.css +127 -0
- package/css/context-menu.css +263 -0
- package/css/countdown.css +21 -0
- package/css/date-field.css +200 -0
- package/css/date-picker.css +231 -0
- package/css/descriptions.css +200 -0
- package/css/dialog.css +127 -0
- package/css/drawer.css +372 -0
- package/css/dynamic-input.css +157 -0
- package/css/editable.css +249 -0
- package/css/ellipsis.css +41 -0
- package/css/empty-state.css +78 -0
- package/css/field.css +65 -0
- package/css/file-upload.css +298 -0
- package/css/flex.css +95 -0
- package/css/float-button.css +121 -0
- package/css/form.css +185 -0
- package/css/gradient-text.css +62 -0
- package/css/grid.css +428 -0
- package/css/highlight.css +21 -0
- package/css/hover-card.css +146 -0
- package/css/icon-wrapper.css +64 -0
- package/css/icon.css +54 -0
- package/css/image.css +45 -0
- package/css/infinite-scroll.css +22 -0
- package/css/layers.css +2 -0
- package/css/layout.css +169 -0
- package/css/list.css +117 -0
- package/css/listbox.css +174 -0
- package/css/loading-bar.css +66 -0
- package/css/log.css +66 -0
- package/css/marquee.css +182 -0
- package/css/mention.css +222 -0
- package/css/menu.css +144 -0
- package/css/menubar.css +271 -0
- package/css/motion.css +58 -0
- package/css/navigation-menu.css +270 -0
- package/css/number-animation.css +32 -0
- package/css/number-field.css +219 -0
- package/css/page-header.css +85 -0
- package/css/pagination.css +148 -0
- package/css/pin-input.css +157 -0
- package/css/popconfirm.css +150 -0
- package/css/popover.css +150 -0
- package/css/popselect.css +207 -0
- package/css/progress.css +37 -0
- package/css/qr-code.css +50 -0
- package/css/radio-group.css +101 -0
- package/css/rating.css +141 -0
- package/css/reset.css +22 -0
- package/css/result.css +104 -0
- package/css/scroll-area.css +176 -0
- package/css/select.css +264 -0
- package/css/separator.css +17 -0
- package/css/skeleton.css +78 -0
- package/css/slider.css +197 -0
- package/css/spinner.css +74 -0
- package/css/splitter.css +127 -0
- package/css/statistic.css +67 -0
- package/css/steps.css +252 -0
- package/css/switch.css +61 -0
- package/css/table.css +404 -0
- package/css/tabs.css +157 -0
- package/css/tags-input.css +360 -0
- package/css/text-field.css +195 -0
- package/css/thread.css +107 -0
- package/css/time-field.css +200 -0
- package/css/time-picker.css +347 -0
- package/css/time.css +20 -0
- package/css/timeline.css +284 -0
- package/css/toast.css +195 -0
- package/css/toaster.css +69 -0
- package/css/toggle-group.css +160 -0
- package/css/toggle.css +103 -0
- package/css/tone.css +85 -0
- package/css/toolbar.css +114 -0
- package/css/tooltip.css +109 -0
- package/css/tour.css +305 -0
- package/css/transfer.css +318 -0
- package/css/tree-select.css +441 -0
- package/css/tree.css +204 -0
- package/css/typography.css +151 -0
- package/css/undefined.css +38 -0
- package/css/virtualizer.css +104 -0
- package/css/visually-hidden.css +13 -0
- package/css/watermark.css +48 -0
- package/index.css +113 -0
- package/index.unlayered.css +16164 -0
- package/package.json +163 -0
package/css/timeline.css
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
@layer xihan.components {
|
|
2
|
+
[data-scope='timeline'][data-part='root'] {
|
|
3
|
+
/* 尺寸档只换这几个私有槽,条目、圆点与文字都从 root 继承它们,
|
|
4
|
+
换档不必给每个部件各写一条选择器 */
|
|
5
|
+
--xh-_timeline-dot-size: 10px;
|
|
6
|
+
--xh-_timeline-gutter: var(--xh-space-3);
|
|
7
|
+
--xh-_timeline-item-gap: var(--xh-space-5);
|
|
8
|
+
--xh-_timeline-title-font-size: var(--xh-text-label-size);
|
|
9
|
+
--xh-_timeline-caption-font-size: var(--xh-font-size-xs);
|
|
10
|
+
/* 标题首行占多高。圆点按它居中下移,圆心才落在标题这一行的中线上 */
|
|
11
|
+
--xh-_timeline-line-h: calc(
|
|
12
|
+
var(--xh-timeline-title-font-size, var(--xh-_timeline-title-font-size)) * var(--xh-leading-tight)
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
/* 条目之间不设 gap:留白由 content 自己的边距撑出来,连线才连得到下一条的圆点。
|
|
16
|
+
ol 的项目符号与内外边距是 UA 给的,库里没有全局 preflight,只能在这儿自己抹平 */
|
|
17
|
+
display: flex;
|
|
18
|
+
margin: 0;
|
|
19
|
+
padding: 0;
|
|
20
|
+
color: var(--xh-timeline-fg, var(--xh-fg-default));
|
|
21
|
+
list-style: none;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
[data-scope='timeline'][data-part='root'][hidden] {
|
|
25
|
+
display: none;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
[data-scope='timeline'][data-part='root'][data-orientation='vertical'] {
|
|
29
|
+
flex-direction: column;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
[data-scope='timeline'][data-part='root'][data-orientation='horizontal'] {
|
|
33
|
+
flex-direction: row;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* —— 尺寸 —— */
|
|
37
|
+
/* 缺省档写在上面的基础规则里。圆点直径不在控件高度那把尺上,两侧直接给像素 */
|
|
38
|
+
[data-scope='timeline'][data-part='root'][data-size='sm'] {
|
|
39
|
+
--xh-_timeline-dot-size: 8px;
|
|
40
|
+
--xh-_timeline-gutter: var(--xh-space-2);
|
|
41
|
+
--xh-_timeline-item-gap: var(--xh-space-4);
|
|
42
|
+
--xh-_timeline-title-font-size: var(--xh-font-size-sm);
|
|
43
|
+
/* 字号档最小就是 xs,时刻文本不再往下走,故不重声明 caption */
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
[data-scope='timeline'][data-part='root'][data-size='lg'] {
|
|
47
|
+
--xh-_timeline-dot-size: 12px;
|
|
48
|
+
--xh-_timeline-gutter: var(--xh-space-4);
|
|
49
|
+
--xh-_timeline-item-gap: var(--xh-space-6);
|
|
50
|
+
--xh-_timeline-title-font-size: var(--xh-font-size-lg);
|
|
51
|
+
--xh-_timeline-caption-font-size: var(--xh-font-size-sm);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* —— 条目 —— */
|
|
55
|
+
/* 圆点与连线占一列(横排时占一行),内容占另一列,用 grid 而不是再包一层节点:
|
|
56
|
+
解剖里没有那个包裹层,作者也不该被逼着多写一个 div */
|
|
57
|
+
[data-scope='timeline'][data-part='item'] {
|
|
58
|
+
display: grid;
|
|
59
|
+
column-gap: var(--xh-timeline-gutter, var(--xh-_timeline-gutter));
|
|
60
|
+
/* 库里没有全局 preflight,列表项的外边距在这里自己抹平 */
|
|
61
|
+
margin: 0;
|
|
62
|
+
/* 允许被压缩,内容里的省略号才有生效的余地 */
|
|
63
|
+
min-inline-size: 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
[data-scope='timeline'][data-part='item'][hidden] {
|
|
67
|
+
display: none;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
[data-scope='timeline'][data-part='item'][data-orientation='vertical'] {
|
|
71
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
72
|
+
grid-template-rows: auto minmax(0, 1fr);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* 横排时每条等分整行 */
|
|
76
|
+
[data-scope='timeline'][data-part='item'][data-orientation='horizontal'] {
|
|
77
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
78
|
+
grid-template-rows: auto minmax(0, 1fr);
|
|
79
|
+
flex: 1;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* —— 圆点 —— */
|
|
83
|
+
[data-scope='timeline'][data-part='indicator'] {
|
|
84
|
+
display: flex;
|
|
85
|
+
flex: none;
|
|
86
|
+
align-items: center;
|
|
87
|
+
justify-content: center;
|
|
88
|
+
inline-size: var(--xh-timeline-indicator-size, var(--xh-_timeline-dot-size));
|
|
89
|
+
block-size: var(--xh-timeline-indicator-size, var(--xh-_timeline-dot-size));
|
|
90
|
+
border-radius: var(--xh-radius-full);
|
|
91
|
+
/* 使用者令牌排在语气之前;两者都没有时退回一个中性的实心点 */
|
|
92
|
+
background: var(--xh-timeline-indicator-bg, var(--xh-_tone-soft, var(--xh-fg-muted)));
|
|
93
|
+
color: var(--xh-timeline-indicator-fg, var(--xh-_tone-on, var(--xh-fg-on-brand)));
|
|
94
|
+
font-size: var(--xh-timeline-indicator-font-size, var(--xh-_timeline-caption-font-size));
|
|
95
|
+
line-height: var(--xh-leading-none);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
[data-scope='timeline'][data-part='indicator'][hidden] {
|
|
99
|
+
display: none;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
[data-scope='timeline'][data-part='item'][data-orientation='vertical'] [data-scope='timeline'][data-part='indicator'] {
|
|
103
|
+
grid-column: 1;
|
|
104
|
+
grid-row: 1;
|
|
105
|
+
/* 行高减去直径的一半:calc 里的除法要写成乘 0.5,除号在这儿是无效语法 */
|
|
106
|
+
margin-block-start: calc(
|
|
107
|
+
(var(--xh-_timeline-line-h) - var(--xh-timeline-indicator-size, var(--xh-_timeline-dot-size))) * 0.5
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
[data-scope='timeline'][data-part='item'][data-orientation='horizontal'] [data-scope='timeline'][data-part='indicator'] {
|
|
112
|
+
grid-column: 1;
|
|
113
|
+
grid-row: 1;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* —— 连线 —— */
|
|
117
|
+
[data-scope='timeline'][data-part='connector'] {
|
|
118
|
+
border-radius: var(--xh-radius-full);
|
|
119
|
+
background: var(--xh-timeline-connector-bg, var(--xh-border-default));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
[data-scope='timeline'][data-part='connector'][hidden] {
|
|
123
|
+
display: none;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* 竖排:从圆点下沿一直伸到条目底边,接上下一条的圆点。
|
|
127
|
+
min-block-size 兜住内容极短的条目,那时也还看得见一截线 */
|
|
128
|
+
[data-scope='timeline'][data-part='item'][data-orientation='vertical'] [data-scope='timeline'][data-part='connector'] {
|
|
129
|
+
grid-column: 1;
|
|
130
|
+
grid-row: 2;
|
|
131
|
+
justify-self: center;
|
|
132
|
+
inline-size: var(--xh-timeline-connector-thickness, var(--xh-stroke-thick));
|
|
133
|
+
min-block-size: var(--xh-timeline-connector-min-length, var(--xh-space-4));
|
|
134
|
+
margin-block-start: var(--xh-space-1);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/* 横排:线转成横的一条,与圆点同一行 */
|
|
138
|
+
[data-scope='timeline'][data-part='item'][data-orientation='horizontal'] [data-scope='timeline'][data-part='connector'] {
|
|
139
|
+
grid-column: 2;
|
|
140
|
+
grid-row: 1;
|
|
141
|
+
align-self: center;
|
|
142
|
+
min-inline-size: var(--xh-timeline-connector-min-length, var(--xh-space-4));
|
|
143
|
+
block-size: var(--xh-timeline-connector-thickness, var(--xh-stroke-thick));
|
|
144
|
+
margin-inline-start: var(--xh-space-1);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* 末条后面没有下一件事,那截线不该占地方;作者不写它也一样,写了就替他收掉 */
|
|
148
|
+
[data-scope='timeline'][data-part='item']:last-child [data-scope='timeline'][data-part='connector'] {
|
|
149
|
+
display: none;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* —— 内容 —— */
|
|
153
|
+
[data-scope='timeline'][data-part='content'] {
|
|
154
|
+
display: flex;
|
|
155
|
+
flex-direction: column;
|
|
156
|
+
gap: var(--xh-space-1);
|
|
157
|
+
min-inline-size: 0;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
[data-scope='timeline'][data-part='content'][hidden] {
|
|
161
|
+
display: none;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/* 条目之间的留白由内容自己的边距撑出来,连线的那一列因此能一路连到下一条 */
|
|
165
|
+
[data-scope='timeline'][data-part='item'][data-orientation='vertical'] [data-scope='timeline'][data-part='content'] {
|
|
166
|
+
grid-column: 2;
|
|
167
|
+
grid-row: 1 / -1;
|
|
168
|
+
padding-block-end: var(--xh-timeline-item-gap, var(--xh-_timeline-item-gap));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
[data-scope='timeline'][data-part='item'][data-orientation='horizontal'] [data-scope='timeline'][data-part='content'] {
|
|
172
|
+
grid-column: 1 / -1;
|
|
173
|
+
grid-row: 2;
|
|
174
|
+
padding-block-start: var(--xh-space-2);
|
|
175
|
+
padding-inline-end: var(--xh-timeline-item-gap, var(--xh-_timeline-item-gap));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* 末条的留白同样收掉:它后面已经没有要隔开的东西 */
|
|
179
|
+
[data-scope='timeline'][data-part='item'][data-orientation='vertical']:last-child [data-scope='timeline'][data-part='content'] {
|
|
180
|
+
padding-block-end: 0;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
[data-scope='timeline'][data-part='item'][data-orientation='horizontal']:last-child [data-scope='timeline'][data-part='content'] {
|
|
184
|
+
padding-inline-end: 0;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* —— 侧别 —— */
|
|
188
|
+
/* 不写 placement 时内容落在结束侧,即上面那几条基础规则。
|
|
189
|
+
start 把线挪到结束侧、内容挪到起始侧,文字一并靠向线 */
|
|
190
|
+
[data-scope='timeline'][data-part='item'][data-orientation='vertical'][data-placement='start'] {
|
|
191
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
[data-scope='timeline'][data-part='item'][data-orientation='vertical'][data-placement='start'] [data-scope='timeline'][data-part='indicator'],
|
|
195
|
+
[data-scope='timeline'][data-part='item'][data-orientation='vertical'][data-placement='start'] [data-scope='timeline'][data-part='connector'] {
|
|
196
|
+
grid-column: 2;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
[data-scope='timeline'][data-part='item'][data-orientation='vertical'][data-placement='start'] [data-scope='timeline'][data-part='content'] {
|
|
200
|
+
grid-column: 1;
|
|
201
|
+
align-items: flex-end;
|
|
202
|
+
text-align: end;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/* alternate:线走中间,奇数条在结束侧、偶数条在起始侧 */
|
|
206
|
+
[data-scope='timeline'][data-part='item'][data-orientation='vertical'][data-placement='alternate'] {
|
|
207
|
+
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
[data-scope='timeline'][data-part='item'][data-orientation='vertical'][data-placement='alternate'] [data-scope='timeline'][data-part='indicator'],
|
|
211
|
+
[data-scope='timeline'][data-part='item'][data-orientation='vertical'][data-placement='alternate'] [data-scope='timeline'][data-part='connector'] {
|
|
212
|
+
grid-column: 2;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
[data-scope='timeline'][data-part='item'][data-orientation='vertical'][data-placement='alternate'] [data-scope='timeline'][data-part='content'] {
|
|
216
|
+
grid-column: 3;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
[data-scope='timeline'][data-part='item'][data-orientation='vertical'][data-placement='alternate']:nth-child(even) [data-scope='timeline'][data-part='content'] {
|
|
220
|
+
grid-column: 1;
|
|
221
|
+
align-items: flex-end;
|
|
222
|
+
text-align: end;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/* 横排的 start 是把内容挪到线的上方,内容贴着线的那一边对齐 */
|
|
226
|
+
[data-scope='timeline'][data-part='item'][data-orientation='horizontal'][data-placement='start'] {
|
|
227
|
+
grid-template-rows: minmax(0, 1fr) auto;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
[data-scope='timeline'][data-part='item'][data-orientation='horizontal'][data-placement='start'] [data-scope='timeline'][data-part='indicator'],
|
|
231
|
+
[data-scope='timeline'][data-part='item'][data-orientation='horizontal'][data-placement='start'] [data-scope='timeline'][data-part='connector'] {
|
|
232
|
+
grid-row: 2;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
[data-scope='timeline'][data-part='item'][data-orientation='horizontal'][data-placement='start'] [data-scope='timeline'][data-part='content'] {
|
|
236
|
+
grid-row: 1;
|
|
237
|
+
align-self: end;
|
|
238
|
+
padding-block-start: 0;
|
|
239
|
+
padding-block-end: var(--xh-space-2);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
[data-scope='timeline'][data-part='item'][data-orientation='horizontal'][data-placement='alternate'] {
|
|
243
|
+
grid-template-rows: minmax(0, 1fr) auto minmax(0, 1fr);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
[data-scope='timeline'][data-part='item'][data-orientation='horizontal'][data-placement='alternate'] [data-scope='timeline'][data-part='indicator'],
|
|
247
|
+
[data-scope='timeline'][data-part='item'][data-orientation='horizontal'][data-placement='alternate'] [data-scope='timeline'][data-part='connector'] {
|
|
248
|
+
grid-row: 2;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
[data-scope='timeline'][data-part='item'][data-orientation='horizontal'][data-placement='alternate'] [data-scope='timeline'][data-part='content'] {
|
|
252
|
+
grid-row: 3;
|
|
253
|
+
align-self: start;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
[data-scope='timeline'][data-part='item'][data-orientation='horizontal'][data-placement='alternate']:nth-child(even) [data-scope='timeline'][data-part='content'] {
|
|
257
|
+
grid-row: 1;
|
|
258
|
+
align-self: end;
|
|
259
|
+
padding-block-start: 0;
|
|
260
|
+
padding-block-end: var(--xh-space-2);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/* —— 文字 —— */
|
|
264
|
+
[data-scope='timeline'][data-part='title'] {
|
|
265
|
+
color: var(--xh-timeline-title-fg, var(--xh-fg-default));
|
|
266
|
+
font-size: var(--xh-timeline-title-font-size, var(--xh-_timeline-title-font-size));
|
|
267
|
+
font-weight: var(--xh-timeline-title-font-weight, var(--xh-text-label-weight));
|
|
268
|
+
line-height: var(--xh-leading-tight);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
[data-scope='timeline'][data-part='description'] {
|
|
272
|
+
color: var(--xh-timeline-description-fg, var(--xh-fg-muted));
|
|
273
|
+
font-size: var(--xh-timeline-description-font-size, var(--xh-text-body-size));
|
|
274
|
+
line-height: var(--xh-leading-normal);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
[data-scope='timeline'][data-part='time'] {
|
|
278
|
+
color: var(--xh-timeline-time-fg, var(--xh-fg-subtle));
|
|
279
|
+
font-size: var(--xh-timeline-time-font-size, var(--xh-_timeline-caption-font-size));
|
|
280
|
+
/* 等宽数字:时刻里的 1 与 7 在比例字体里宽度不同,不定住会让整列时间左右抖一格 */
|
|
281
|
+
font-variant-numeric: tabular-nums;
|
|
282
|
+
line-height: var(--xh-leading-normal);
|
|
283
|
+
}
|
|
284
|
+
}
|
package/css/toast.css
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
@layer xihan.motion {
|
|
2
|
+
/*
|
|
3
|
+
* 九个位共用同一条进出场,位移不跟着落位走:要按位置分方向,就得从 toaster 的
|
|
4
|
+
* data-placement 反向选到卡片上,等于把一个组件的动效写进另一个组件的选择器里,
|
|
5
|
+
* 两边任意一侧改名都会静默失效。竖向一小段位移在九个位上都说得通。
|
|
6
|
+
*/
|
|
7
|
+
@keyframes xh-toast-in {
|
|
8
|
+
from {
|
|
9
|
+
opacity: 0;
|
|
10
|
+
transform: translateY(8px) scale(0.98);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
to {
|
|
14
|
+
opacity: 1;
|
|
15
|
+
transform: none;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@keyframes xh-toast-out {
|
|
20
|
+
from {
|
|
21
|
+
opacity: 1;
|
|
22
|
+
transform: none;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
to {
|
|
26
|
+
opacity: 0;
|
|
27
|
+
transform: translateY(4px) scale(0.96);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* 通知是自己冒出来的,用户没按任何键:减弱动效的诉求在这里比别处更硬。
|
|
32
|
+
!important 在低层里反而更强(重要声明的层序是反的),足以压住组件层的动画。 */
|
|
33
|
+
@media (prefers-reduced-motion: reduce) {
|
|
34
|
+
[data-scope='toast'][data-part] {
|
|
35
|
+
animation-duration: 0.01ms !important;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@layer xihan.components {
|
|
41
|
+
[data-scope='toast'][data-part='root'] {
|
|
42
|
+
display: flex;
|
|
43
|
+
flex-direction: column;
|
|
44
|
+
gap: var(--xh-toast-gap, var(--xh-space-1));
|
|
45
|
+
inline-size: var(--xh-toast-w, 20rem);
|
|
46
|
+
/* 摞是整面视口宽的定位面且带内边距,取 100% 就等于自动让出两侧留白,
|
|
47
|
+
窄屏上不必再算一遍 viewport 减法 */
|
|
48
|
+
max-inline-size: 100%;
|
|
49
|
+
padding-block: var(--xh-toast-py, var(--xh-space-3));
|
|
50
|
+
padding-inline: var(--xh-toast-px, var(--xh-space-4));
|
|
51
|
+
border: var(--xh-stroke-thin) solid var(--xh-toast-border, var(--xh-border-default));
|
|
52
|
+
/* 语气色条做成起始侧的粗边,而不是伪元素:圆角与阴影都不用再对第二遍。
|
|
53
|
+
两条必须排在 border 简写之后,否则被简写重置回细边与常规描边色。
|
|
54
|
+
颜色由 connect 从 type 派生的 data-tone 经语气层给出,作者写 --xh-toast-accent
|
|
55
|
+
就能把某一条整体改色;两者都没有时退回原来的 info 蓝 */
|
|
56
|
+
border-inline-start-width: var(--xh-toast-accent-width, 4px);
|
|
57
|
+
border-inline-start-color: var(--xh-toast-accent, var(--xh-_tone-soft, var(--xh-color-info-500)));
|
|
58
|
+
border-radius: var(--xh-toast-radius, var(--xh-shape-surface));
|
|
59
|
+
background: var(--xh-toast-bg, var(--xh-bg-surface-raised));
|
|
60
|
+
color: var(--xh-toast-fg, var(--xh-fg-default));
|
|
61
|
+
box-shadow: var(--xh-toast-shadow, var(--xh-elevation-3));
|
|
62
|
+
font-size: var(--xh-toast-font-size, var(--xh-font-size-sm));
|
|
63
|
+
line-height: var(--xh-toast-leading, var(--xh-leading-normal));
|
|
64
|
+
/* 摞的定位面整面铺开且不吃指针,卡片必须自己吃回来:
|
|
65
|
+
悬停按住计时、按钮可点、焦点进出,全指着这一条 */
|
|
66
|
+
pointer-events: auto;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* loading 说的是「事情还没完」,不是好消息也不是坏消息,走中性灰。
|
|
70
|
+
它不是语气:连接层给它派生的 neutral 档是 500,而这里要的是 fg-muted(600) */
|
|
71
|
+
[data-scope='toast'][data-part='root'][data-type='loading'] {
|
|
72
|
+
border-inline-start-color: var(--xh-toast-accent, var(--xh-fg-muted));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* 退场窗口走完连接层给的是 hidden 属性,而上面声明了 display——
|
|
76
|
+
漏掉这条,已经"卸载"的通知会一直杵在屏幕上,且再也没人来收 */
|
|
77
|
+
[data-scope='toast'][data-part='root'][hidden] {
|
|
78
|
+
display: none;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* 生命周期只有三段,unmounted 那段由上面的 [hidden] 收走,这里只管前两段 */
|
|
82
|
+
[data-scope='toast'][data-part='root'][data-state='visible'] {
|
|
83
|
+
animation: xh-toast-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* forwards 不能省:退场窗口(removeDelay 默认 200ms)比退场动画长,
|
|
87
|
+
不定住末帧的话动画跑完会弹回全不透明,卡片在被收起前先闪一下 */
|
|
88
|
+
[data-scope='toast'][data-part='root'][data-state='dismissing'] {
|
|
89
|
+
animation: xh-toast-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
[data-scope='toast'][data-part='title'] {
|
|
93
|
+
/* 作者可能把它写成 h2/p,外边距得先归零,否则卡片内的行距对不上 */
|
|
94
|
+
margin: 0;
|
|
95
|
+
color: var(--xh-toast-title-fg, var(--xh-fg-default));
|
|
96
|
+
font-size: var(--xh-toast-title-font-size, var(--xh-font-size-md));
|
|
97
|
+
font-weight: var(--xh-toast-title-font-weight, var(--xh-font-weight-semibold));
|
|
98
|
+
line-height: var(--xh-toast-title-leading, var(--xh-leading-tight));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
[data-scope='toast'][data-part='description'] {
|
|
102
|
+
margin: 0;
|
|
103
|
+
color: var(--xh-toast-description-fg, var(--xh-fg-muted));
|
|
104
|
+
font-size: var(--xh-toast-description-font-size, var(--xh-font-size-sm));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* 没给文案的那一个仍然要留在 DOM 里(aria-labelledby / aria-describedby 指着它),
|
|
108
|
+
但空盒子会照吃一份 gap,卡片看着就多出一截莫名的留白 */
|
|
109
|
+
[data-scope='toast'][data-part='title']:empty,
|
|
110
|
+
[data-scope='toast'][data-part='description']:empty {
|
|
111
|
+
display: none;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
[data-scope='toast'][data-part='title'][hidden],
|
|
115
|
+
[data-scope='toast'][data-part='description'][hidden] {
|
|
116
|
+
display: none;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
[data-scope='toast'][data-part='action-trigger'] {
|
|
120
|
+
/* 作为卡片直接子元素时不跟着拉满整行;被作者包进一行里时它只影响交叉轴,无副作用 */
|
|
121
|
+
align-self: flex-start;
|
|
122
|
+
display: inline-flex;
|
|
123
|
+
align-items: center;
|
|
124
|
+
justify-content: center;
|
|
125
|
+
block-size: var(--xh-toast-action-h, var(--xh-control-h-sm));
|
|
126
|
+
padding-inline: var(--xh-toast-action-px, var(--xh-control-px-sm));
|
|
127
|
+
border: var(--xh-stroke-thin) solid var(--xh-toast-action-border, var(--xh-border-default));
|
|
128
|
+
border-radius: var(--xh-toast-action-radius, var(--xh-shape-control));
|
|
129
|
+
background: var(--xh-toast-action-bg, var(--xh-bg-subtle));
|
|
130
|
+
color: var(--xh-toast-action-fg, var(--xh-fg-default));
|
|
131
|
+
/* 按钮不继承字族字号,不接回来的话卡片里会突然冒出一段系统 UI 字体 */
|
|
132
|
+
font: inherit;
|
|
133
|
+
font-weight: var(--xh-toast-action-font-weight, var(--xh-font-weight-medium));
|
|
134
|
+
line-height: var(--xh-leading-none);
|
|
135
|
+
cursor: pointer;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
[data-scope='toast'][data-part='action-trigger'][hidden] {
|
|
139
|
+
display: none;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
[data-scope='toast'][data-part='action-trigger']:hover:not(:disabled) {
|
|
143
|
+
background: var(--xh-toast-action-bg-hover, var(--xh-bg-subtle-hover));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
[data-scope='toast'][data-part='action-trigger']:active:not(:disabled) {
|
|
147
|
+
background: var(--xh-toast-action-bg-active, var(--xh-bg-subtle-active));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
[data-scope='toast'][data-part='action-trigger']:focus-visible {
|
|
151
|
+
outline: var(--xh-ring-width) solid var(--xh-ring-focus);
|
|
152
|
+
outline-offset: var(--xh-ring-offset);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
[data-scope='toast'][data-part='close-trigger'] {
|
|
156
|
+
align-self: flex-start;
|
|
157
|
+
/* 和操作按钮同处一行时把叉推到行尾;单独作为卡片子元素时则贴到行内末侧 */
|
|
158
|
+
margin-inline-start: auto;
|
|
159
|
+
display: inline-flex;
|
|
160
|
+
align-items: center;
|
|
161
|
+
justify-content: center;
|
|
162
|
+
block-size: var(--xh-toast-close-size, var(--xh-control-h-sm));
|
|
163
|
+
inline-size: var(--xh-toast-close-size, var(--xh-control-h-sm));
|
|
164
|
+
border: 0;
|
|
165
|
+
border-radius: var(--xh-toast-close-radius, var(--xh-shape-control));
|
|
166
|
+
background: transparent;
|
|
167
|
+
color: var(--xh-toast-close-fg, var(--xh-fg-muted));
|
|
168
|
+
font: inherit;
|
|
169
|
+
line-height: var(--xh-leading-none);
|
|
170
|
+
cursor: pointer;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* closable=false 时连接层同时给了 hidden 与 disabled,这条是它真正收起的那一半:
|
|
174
|
+
上面声明了 display,不补回来就会留下一个按不动的叉,比压根没有叉更让人困惑 */
|
|
175
|
+
[data-scope='toast'][data-part='close-trigger'][hidden] {
|
|
176
|
+
display: none;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
[data-scope='toast'][data-part='close-trigger']:hover:not(:disabled) {
|
|
180
|
+
background: var(--xh-toast-close-bg-hover, var(--xh-bg-subtle));
|
|
181
|
+
color: var(--xh-toast-close-fg-hover, var(--xh-fg-default));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* 单体控件用原生 disabled。常规路径下它与 hidden 同时出现看不见,
|
|
185
|
+
但作者若在覆盖层把收起改了回来,至少还能看出这颗叉是按不动的 */
|
|
186
|
+
[data-scope='toast'][data-part='close-trigger']:disabled {
|
|
187
|
+
color: var(--xh-fg-disabled);
|
|
188
|
+
cursor: not-allowed;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
[data-scope='toast'][data-part='close-trigger']:focus-visible {
|
|
192
|
+
outline: var(--xh-ring-width) solid var(--xh-ring-focus);
|
|
193
|
+
outline-offset: var(--xh-ring-offset);
|
|
194
|
+
}
|
|
195
|
+
}
|
package/css/toaster.css
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
@layer xihan.components {
|
|
2
|
+
/* 地标容器不占布局:通知落在哪儿由九个 group 各自的 fixed 说了算,
|
|
3
|
+
root 只是给读屏一个可跳转的落点。它若留下一个块盒,写在页面中段的用法
|
|
4
|
+
(按钮和队列摆在一起)就会凭空多出一行空白。
|
|
5
|
+
另一件必须提防的事:fixed 认的是视口,祖先上一旦出现
|
|
6
|
+
transform / filter / backdrop-filter / contain,包含块就被抢走,整摞通知立刻贴错位置——
|
|
7
|
+
所以这里连 position 都不碰,也别往覆盖钩子里加这几个属性。 */
|
|
8
|
+
[data-scope='toaster'][data-part='root'] {
|
|
9
|
+
display: contents;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/* 只要声明过 display,就盖掉了 UA 的 [hidden]{display:none},收起态得自己补回来 */
|
|
13
|
+
[data-scope='toaster'][data-part='root'][hidden] {
|
|
14
|
+
display: none;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/*
|
|
18
|
+
* 一摞 = 一整面视口大小的定位面:贴边靠 padding,九个位靠 justify/align 表达。
|
|
19
|
+
* 不用四角 inset 逐个摆,是因为 column flex 的 align-items 走的是行内轴,
|
|
20
|
+
* RTL 下 start/end 自动跟着翻;写死 inset-inline-start 的话居中那三个位
|
|
21
|
+
* 还得再配一次 translate,方向一变就错。
|
|
22
|
+
* 条目次序按队列原样(旧的在前):贴底那三个位于是新的离屏幕边最近,符合直觉。
|
|
23
|
+
*/
|
|
24
|
+
[data-scope='toaster'][data-part='group'] {
|
|
25
|
+
position: fixed;
|
|
26
|
+
inset-block: 0;
|
|
27
|
+
inset-inline: 0;
|
|
28
|
+
z-index: var(--xh-layer-toast);
|
|
29
|
+
display: flex;
|
|
30
|
+
flex-direction: column;
|
|
31
|
+
/* 基线按默认落位 bottom-end 写,下面只覆盖跟它不同的那几个位 */
|
|
32
|
+
justify-content: flex-end;
|
|
33
|
+
align-items: center;
|
|
34
|
+
padding-block: var(--xh-toaster-inset, var(--xh-space-6));
|
|
35
|
+
padding-inline: var(--xh-toaster-inset, var(--xh-space-6));
|
|
36
|
+
/* 摞内间距由连接层写进内联 style(两个适配器共用同一份),这里绝不重复写:
|
|
37
|
+
写了也会被内联盖掉,只会让人误以为间距归样式层管 */
|
|
38
|
+
/* 整面铺开就等于罩住了整块视口。面本身不吃指针,只有卡片吃(见 toast.css),
|
|
39
|
+
否则九个位一叠,底下的页面就再也点不动了 */
|
|
40
|
+
pointer-events: none;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
[data-scope='toaster'][data-part='group'][hidden] {
|
|
44
|
+
display: none;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* 空摞直接撤掉:九个位同时挂九张全屏定位面,白白多九个合成层 */
|
|
48
|
+
[data-scope='toaster'][data-part='group'][data-empty] {
|
|
49
|
+
display: none;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* 块轴落位:^= 一次吃掉某一行的三个位(top / top-start / top-end) */
|
|
53
|
+
[data-scope='toaster'][data-part='group'][data-placement^='top'] {
|
|
54
|
+
justify-content: flex-start;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
[data-scope='toaster'][data-part='group'][data-placement^='middle'] {
|
|
58
|
+
justify-content: center;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* 行内轴落位:不带后缀的 top / middle / bottom 用基线的居中 */
|
|
62
|
+
[data-scope='toaster'][data-part='group'][data-placement$='-start'] {
|
|
63
|
+
align-items: flex-start;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
[data-scope='toaster'][data-part='group'][data-placement$='-end'] {
|
|
67
|
+
align-items: flex-end;
|
|
68
|
+
}
|
|
69
|
+
}
|