@wovenui/components 0.0.0-stage → 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.
- package/LICENSE +21 -0
- package/README.md +284 -2
- package/dist/components.json +4204 -0
- package/dist/llms-full.txt +2839 -0
- package/dist/wovenui-kit.css +5002 -0
- package/dist/wovenui-kit.css.map +1 -0
- package/dist/wovenui-kit.iife.js +532 -0
- package/dist/wovenui-kit.js +529 -0
- package/dist/wovenui-kit.min.css +2 -0
- package/dist/wovenui-kit.min.css.map +1 -0
- package/package.json +268 -4
- package/src/_close.scss +45 -0
- package/src/_helpers.scss +11 -0
- package/src/_kit.scss +124 -0
- package/src/_shared.scss +10 -0
- package/src/components/_accordion.scss +132 -0
- package/src/components/_alert.scss +161 -0
- package/src/components/_autocomplete.scss +129 -0
- package/src/components/_avatar-group.scss +80 -0
- package/src/components/_avatar.scss +134 -0
- package/src/components/_badge.scss +91 -0
- package/src/components/_breadcrumb.scss +115 -0
- package/src/components/_btn-group.scss +113 -0
- package/src/components/_button.scss +301 -0
- package/src/components/_card.scss +120 -0
- package/src/components/_checkbox.scss +168 -0
- package/src/components/_code-block.scss +78 -0
- package/src/components/_confirm-modal.scss +75 -0
- package/src/components/_data-table.scss +79 -0
- package/src/components/_desc-list.scss +180 -0
- package/src/components/_divider.scss +66 -0
- package/src/components/_dot.scss +115 -0
- package/src/components/_drawer.scss +200 -0
- package/src/components/_dropdown.scss +182 -0
- package/src/components/_empty-state.scss +146 -0
- package/src/components/_field-group.scss +68 -0
- package/src/components/_field-message.scss +75 -0
- package/src/components/_field.scss +127 -0
- package/src/components/_form-actions.scss +58 -0
- package/src/components/_info-row.scss +69 -0
- package/src/components/_input-group.scss +87 -0
- package/src/components/_input.scss +278 -0
- package/src/components/_list.scss +76 -0
- package/src/components/_modal.scss +168 -0
- package/src/components/_page-header.scss +85 -0
- package/src/components/_pagination.scss +138 -0
- package/src/components/_panel.scss +69 -0
- package/src/components/_popover.scss +154 -0
- package/src/components/_progress-circle.scss +123 -0
- package/src/components/_progress.scss +122 -0
- package/src/components/_radio.scss +162 -0
- package/src/components/_segmented.scss +183 -0
- package/src/components/_select.scss +154 -0
- package/src/components/_sidebar.scss +300 -0
- package/src/components/_skeleton.scss +90 -0
- package/src/components/_spinner.scss +84 -0
- package/src/components/_stat.scss +92 -0
- package/src/components/_table.scss +460 -0
- package/src/components/_tabs.scss +327 -0
- package/src/components/_tag.scss +143 -0
- package/src/components/_text.scss +42 -0
- package/src/components/_textarea.scss +57 -0
- package/src/components/_timeline.scss +196 -0
- package/src/components/_title.scss +41 -0
- package/src/components/_toast.scss +221 -0
- package/src/components/_toggle.scss +171 -0
- package/src/components/_toolbar.scss +74 -0
- package/src/components/_topbar.scss +204 -0
- package/src/entries/accordion.scss +4 -0
- package/src/entries/alert.scss +4 -0
- package/src/entries/autocomplete.scss +4 -0
- package/src/entries/avatar-group.scss +4 -0
- package/src/entries/avatar.scss +4 -0
- package/src/entries/badge.scss +4 -0
- package/src/entries/breadcrumb.scss +4 -0
- package/src/entries/btn-group.scss +4 -0
- package/src/entries/button.scss +4 -0
- package/src/entries/card.scss +4 -0
- package/src/entries/checkbox.scss +4 -0
- package/src/entries/code-block.scss +4 -0
- package/src/entries/confirm-modal.scss +4 -0
- package/src/entries/data-table.scss +4 -0
- package/src/entries/desc-list.scss +4 -0
- package/src/entries/divider.scss +4 -0
- package/src/entries/dot.scss +4 -0
- package/src/entries/drawer.scss +4 -0
- package/src/entries/dropdown.scss +4 -0
- package/src/entries/empty-state.scss +4 -0
- package/src/entries/field-group.scss +4 -0
- package/src/entries/field-message.scss +4 -0
- package/src/entries/field.scss +4 -0
- package/src/entries/form-actions.scss +4 -0
- package/src/entries/info-row.scss +4 -0
- package/src/entries/input-group.scss +4 -0
- package/src/entries/input.scss +4 -0
- package/src/entries/list.scss +4 -0
- package/src/entries/modal.scss +4 -0
- package/src/entries/page-header.scss +4 -0
- package/src/entries/pagination.scss +4 -0
- package/src/entries/panel.scss +4 -0
- package/src/entries/popover.scss +4 -0
- package/src/entries/progress-circle.scss +4 -0
- package/src/entries/progress.scss +4 -0
- package/src/entries/radio.scss +4 -0
- package/src/entries/segmented.scss +4 -0
- package/src/entries/select.scss +4 -0
- package/src/entries/sidebar.scss +4 -0
- package/src/entries/skeleton.scss +4 -0
- package/src/entries/spinner.scss +4 -0
- package/src/entries/stat.scss +4 -0
- package/src/entries/table.scss +4 -0
- package/src/entries/tabs.scss +4 -0
- package/src/entries/tag.scss +4 -0
- package/src/entries/text.scss +4 -0
- package/src/entries/textarea.scss +4 -0
- package/src/entries/timeline.scss +4 -0
- package/src/entries/title.scss +4 -0
- package/src/entries/toast.scss +4 -0
- package/src/entries/toggle.scss +4 -0
- package/src/entries/toolbar.scss +4 -0
- package/src/entries/topbar.scss +4 -0
- package/src/index.scss +7 -0
- package/src/js/autocomplete.js +86 -0
- package/src/js/data-table.js +141 -0
- package/src/js/index.js +34 -0
- package/src/js/input-clear.js +25 -0
- package/src/js/package.json +3 -0
- package/src/js/tabs.js +73 -0
- package/src/js/toast.js +161 -0
- package/src/js/util.js +21 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
////
|
|
2
|
+
//// @component wu-sidebar
|
|
3
|
+
//// Vertical navigation with sections, link lists and collapsible groups.
|
|
4
|
+
////
|
|
5
|
+
//// Use a `nav` with an `aria-label`, and mark the current link with `aria-current="page"`. A collapsible group is a `details` with the `wu-sidebar-group` class and a `summary` with `wu-sidebar-toggle`; the toggle may also be a `button` with `aria-expanded`.
|
|
6
|
+
////
|
|
7
|
+
//// On narrow screens put the sidebar in a `dialog` with `wu-drawer inline-start`, where it fills the drawer.
|
|
8
|
+
////
|
|
9
|
+
//// Icon rail: add `collapsed`; each link holds an `svg` and a `wu-sidebar-label`, shown as a tooltip on hover or focus.
|
|
10
|
+
////
|
|
11
|
+
//// @since 1.0.0
|
|
12
|
+
//// @needs none
|
|
13
|
+
//// @js none
|
|
14
|
+
//// @example html
|
|
15
|
+
//// <nav class="wu-sidebar" aria-label="Main">
|
|
16
|
+
//// <div class="wu-sidebar-section">
|
|
17
|
+
//// <h2 class="wu-sidebar-heading">Workspace</h2>
|
|
18
|
+
//// <ul class="wu-sidebar-list">
|
|
19
|
+
//// <li><a class="wu-sidebar-link" href="/dashboard" aria-current="page">Dashboard</a></li>
|
|
20
|
+
//// <li>
|
|
21
|
+
//// <details class="wu-sidebar-group" open>
|
|
22
|
+
//// <summary class="wu-sidebar-toggle">Projects</summary>
|
|
23
|
+
//// <ul class="wu-sidebar-list">
|
|
24
|
+
//// <li><a class="wu-sidebar-link" href="/projects/apollo">Apollo</a></li>
|
|
25
|
+
//// </ul>
|
|
26
|
+
//// </details>
|
|
27
|
+
//// </li>
|
|
28
|
+
//// </ul>
|
|
29
|
+
//// </div>
|
|
30
|
+
//// <div class="wu-sidebar-footer">Signed in as Sam</div>
|
|
31
|
+
//// </nav>
|
|
32
|
+
//// @example html
|
|
33
|
+
//// <nav class="wu-sidebar collapsed pinned" aria-label="Main">
|
|
34
|
+
//// <ul class="wu-sidebar-list">
|
|
35
|
+
//// <li>
|
|
36
|
+
//// <a class="wu-sidebar-link" href="/settings">
|
|
37
|
+
//// <svg aria-hidden="true" viewBox="0 0 24 24"><circle cx="12" cy="12" r="4" /></svg>
|
|
38
|
+
//// <span class="wu-sidebar-label">Settings</span>
|
|
39
|
+
//// </a>
|
|
40
|
+
//// </li>
|
|
41
|
+
//// </ul>
|
|
42
|
+
//// </nav>
|
|
43
|
+
//// @modifier pinned - Sticks beside scrolling content and scrolls on its own @since 1.0.0
|
|
44
|
+
//// @modifier collapsed - Icon rail: labels and headings are visually hidden, and a label shows as a tooltip on hover or focus @since 1.0.0
|
|
45
|
+
//// @modifier animated - Animates the width, the chevron and the opening of groups when the user allows motion; `wu-animated` on an ancestor does the same @since 1.0.0
|
|
46
|
+
//// @part wu-sidebar-header - Top row for a logo or title @since 1.0.0
|
|
47
|
+
//// @part wu-sidebar-footer - Bottom row, pushed to the end @since 1.0.0
|
|
48
|
+
//// @part wu-sidebar-section - Group of a heading and a list @since 1.0.0
|
|
49
|
+
//// @part wu-sidebar-heading - Small muted section heading @since 1.0.0
|
|
50
|
+
//// @part wu-sidebar-list - Vertical list of links; nested inside a group it is indented @since 1.0.0
|
|
51
|
+
//// @part wu-sidebar-group - A `details` element that collapses a nested list @since 1.0.0
|
|
52
|
+
//// @part wu-sidebar-toggle - Row that opens or closes a group, with a chevron @since 1.0.0
|
|
53
|
+
//// @part wu-sidebar-link - Navigation link row @since 1.0.0
|
|
54
|
+
//// @part wu-sidebar-label - Text label of a link; hidden and shown as a tooltip when collapsed @since 1.0.0
|
|
55
|
+
//// @variable --wu-sidebar--Width [16rem] - Sidebar width @since 1.0.0
|
|
56
|
+
//// @variable --wu-sidebar--CollapsedWidth [calc(1em + 3rem)] - Width of the icon rail @since 1.0.0
|
|
57
|
+
//// @state aria-current="page" - The current link: tinted fill and a bar at the inline start @since 1.0.0
|
|
58
|
+
//// @state open | aria-expanded="true" - The chevron points down @since 1.0.0
|
|
59
|
+
////
|
|
60
|
+
|
|
61
|
+
@use "@wovenui/core/mixins" as woven;
|
|
62
|
+
|
|
63
|
+
.wu-sidebar {
|
|
64
|
+
display: flex;
|
|
65
|
+
flex-direction: column;
|
|
66
|
+
gap: woven.token(space-4);
|
|
67
|
+
inline-size: var(--wu-sidebar--Width, #{woven.token(space-64)});
|
|
68
|
+
max-inline-size: 100%;
|
|
69
|
+
padding: woven.token(space-3);
|
|
70
|
+
border-inline-end: woven.token(border-width-1) solid woven.token(border-regular);
|
|
71
|
+
background: woven.token(bg-surface);
|
|
72
|
+
color: woven.token(text-regular);
|
|
73
|
+
font-size: woven.token(font-size-sm);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.wu-drawer > .wu-sidebar {
|
|
77
|
+
flex: 1;
|
|
78
|
+
inline-size: auto;
|
|
79
|
+
border-inline-end: 0;
|
|
80
|
+
overflow-y: auto;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Sticks beside scrolling content and scrolls on its own
|
|
84
|
+
.wu-sidebar.pinned {
|
|
85
|
+
position: sticky;
|
|
86
|
+
inset-block-start: 0;
|
|
87
|
+
align-self: flex-start;
|
|
88
|
+
max-block-size: 100dvh;
|
|
89
|
+
overflow-y: auto;
|
|
90
|
+
overscroll-behavior: contain;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.wu-sidebar-header,
|
|
94
|
+
.wu-sidebar-footer {
|
|
95
|
+
display: flex;
|
|
96
|
+
align-items: center;
|
|
97
|
+
gap: woven.token(space-2);
|
|
98
|
+
padding-inline: woven.token(space-3);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.wu-sidebar-footer {
|
|
102
|
+
margin-block-start: auto;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.wu-sidebar-section {
|
|
106
|
+
display: flex;
|
|
107
|
+
flex-direction: column;
|
|
108
|
+
gap: woven.token(space-1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.wu-sidebar-heading {
|
|
112
|
+
margin: 0;
|
|
113
|
+
padding-inline: woven.token(space-3);
|
|
114
|
+
color: woven.token(text-muted);
|
|
115
|
+
font-size: woven.token(font-size-xs);
|
|
116
|
+
font-weight: woven.token(font-weight-semibold);
|
|
117
|
+
line-height: woven.token(line-height-normal);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.wu-sidebar-list {
|
|
121
|
+
display: flex;
|
|
122
|
+
flex-direction: column;
|
|
123
|
+
gap: woven.token(space-1);
|
|
124
|
+
margin: 0;
|
|
125
|
+
padding: 0;
|
|
126
|
+
list-style: none;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.wu-sidebar-group > .wu-sidebar-list {
|
|
130
|
+
margin-block-start: woven.token(space-1);
|
|
131
|
+
padding-inline-start: woven.token(space-4);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Link and group toggle share one row look; the toggle may also be a <button aria-expanded>
|
|
135
|
+
.wu-sidebar-link,
|
|
136
|
+
.wu-sidebar-toggle {
|
|
137
|
+
position: relative;
|
|
138
|
+
display: flex;
|
|
139
|
+
align-items: center;
|
|
140
|
+
gap: woven.token(space-2);
|
|
141
|
+
width: 100%;
|
|
142
|
+
min-height: woven.token(control-height-sm);
|
|
143
|
+
padding-block: 0;
|
|
144
|
+
padding-inline: woven.token(space-3);
|
|
145
|
+
border: 0;
|
|
146
|
+
border-radius: woven.token(radius-sm);
|
|
147
|
+
background: transparent;
|
|
148
|
+
color: woven.token(text-muted);
|
|
149
|
+
font: inherit;
|
|
150
|
+
font-weight: woven.token(font-weight-medium);
|
|
151
|
+
text-align: start;
|
|
152
|
+
text-decoration: none;
|
|
153
|
+
cursor: pointer;
|
|
154
|
+
transition-property: background-color, color;
|
|
155
|
+
transition-duration: woven.token(duration-150);
|
|
156
|
+
transition-timing-function: woven.token(ease-out);
|
|
157
|
+
|
|
158
|
+
&:hover {
|
|
159
|
+
background: woven.token(bg-hover);
|
|
160
|
+
color: woven.token(text-regular);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Inset ring, so a scrolling sidebar does not clip it
|
|
164
|
+
&:focus-visible {
|
|
165
|
+
outline: woven.token(border-focus-width) solid woven.token(border-focus);
|
|
166
|
+
outline-offset: calc(-1 * #{woven.token(border-focus-width)});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
> svg {
|
|
170
|
+
flex-shrink: 0;
|
|
171
|
+
width: 1em;
|
|
172
|
+
height: 1em;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Current page: tint plus a bar, so color is not the only signal
|
|
177
|
+
.wu-sidebar-link[aria-current="page"] {
|
|
178
|
+
background: woven.token(bg-selected);
|
|
179
|
+
color: woven.token(text-brand);
|
|
180
|
+
|
|
181
|
+
&::before {
|
|
182
|
+
content: "";
|
|
183
|
+
position: absolute;
|
|
184
|
+
inset-block: woven.token(space-1);
|
|
185
|
+
inset-inline-start: 0;
|
|
186
|
+
width: woven.token(border-width-2);
|
|
187
|
+
border-radius: woven.token(radius-full);
|
|
188
|
+
background: woven.token(brand);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.wu-sidebar-toggle {
|
|
193
|
+
list-style: none;
|
|
194
|
+
|
|
195
|
+
&::-webkit-details-marker {
|
|
196
|
+
display: none;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Chevron drawn with two borders: points to the end when closed, down when open
|
|
200
|
+
&::after {
|
|
201
|
+
content: "";
|
|
202
|
+
flex: none;
|
|
203
|
+
width: 0.4em;
|
|
204
|
+
height: 0.4em;
|
|
205
|
+
margin-inline-start: auto;
|
|
206
|
+
border-right: woven.token(border-width-2) solid currentcolor;
|
|
207
|
+
border-bottom: woven.token(border-width-2) solid currentcolor;
|
|
208
|
+
rotate: -45deg;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
&:dir(rtl)::after {
|
|
212
|
+
rotate: 135deg;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.wu-sidebar-group[open] > .wu-sidebar-toggle::after,
|
|
217
|
+
.wu-sidebar-toggle[aria-expanded="true"]::after {
|
|
218
|
+
rotate: 45deg;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.wu-sidebar-group:has([aria-current="page"]) > .wu-sidebar-toggle {
|
|
222
|
+
color: woven.token(text-regular);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Icon rail: labels hidden visually but kept for screen readers, shown as a tooltip at the inline end on hover or focus
|
|
226
|
+
.wu-sidebar.collapsed {
|
|
227
|
+
inline-size: var(--wu-sidebar--CollapsedWidth, calc(1em + 4 * #{woven.token(space-3)}));
|
|
228
|
+
overflow: visible;
|
|
229
|
+
|
|
230
|
+
.wu-sidebar-heading,
|
|
231
|
+
.wu-sidebar-label {
|
|
232
|
+
position: absolute;
|
|
233
|
+
width: 1px;
|
|
234
|
+
height: 1px;
|
|
235
|
+
overflow: hidden;
|
|
236
|
+
clip-path: inset(50%);
|
|
237
|
+
white-space: nowrap;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.wu-sidebar-group > .wu-sidebar-list {
|
|
241
|
+
padding-inline-start: 0;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.wu-sidebar-toggle::after {
|
|
245
|
+
display: none;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
:is(.wu-sidebar-link, .wu-sidebar-toggle):is(:hover, :focus-visible) > .wu-sidebar-label {
|
|
249
|
+
inset-block-start: 50%;
|
|
250
|
+
inset-inline-start: 100%;
|
|
251
|
+
z-index: woven.token(z-tooltip);
|
|
252
|
+
width: auto;
|
|
253
|
+
height: auto;
|
|
254
|
+
margin-inline-start: woven.token(space-2);
|
|
255
|
+
padding-block: woven.token(space-1);
|
|
256
|
+
padding-inline: woven.token(space-2);
|
|
257
|
+
border-radius: woven.token(radius-xs);
|
|
258
|
+
background: woven.token(text-regular);
|
|
259
|
+
color: woven.token(text-inverted);
|
|
260
|
+
box-shadow: woven.token(shadow-md);
|
|
261
|
+
font-weight: woven.token(font-weight-normal);
|
|
262
|
+
clip-path: none;
|
|
263
|
+
pointer-events: none;
|
|
264
|
+
translate: 0 -50%;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Opt-in motion: the chevron turns and groups slide open
|
|
269
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
270
|
+
.wu-sidebar.animated,
|
|
271
|
+
.wu-animated .wu-sidebar {
|
|
272
|
+
.wu-sidebar-toggle::after {
|
|
273
|
+
transition: rotate woven.token(duration-200) woven.token(ease-out);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.wu-sidebar.animated,
|
|
278
|
+
.wu-animated .wu-sidebar {
|
|
279
|
+
transition: inline-size woven.token(duration-200) woven.token(ease-out);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
@supports (interpolate-size: allow-keywords) and selector(::details-content) {
|
|
283
|
+
.wu-sidebar.animated,
|
|
284
|
+
.wu-animated .wu-sidebar {
|
|
285
|
+
interpolate-size: allow-keywords;
|
|
286
|
+
|
|
287
|
+
.wu-sidebar-group::details-content {
|
|
288
|
+
block-size: 0;
|
|
289
|
+
overflow: clip;
|
|
290
|
+
transition:
|
|
291
|
+
block-size woven.token(duration-200) woven.token(ease-out),
|
|
292
|
+
content-visibility woven.token(duration-200) allow-discrete;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.wu-sidebar-group[open]::details-content {
|
|
296
|
+
block-size: auto;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
////
|
|
2
|
+
//// @component wu-skeleton
|
|
3
|
+
//// Placeholder shapes shown while content loads.
|
|
4
|
+
////
|
|
5
|
+
//// Placeholders are decorative: add `aria-hidden="true"` to each skeleton and `aria-busy="true"` to the region being loaded, plus visible or hidden Loading text. Remove both when the content arrives.
|
|
6
|
+
////
|
|
7
|
+
//// The default shape is a block. Adjacent `text` skeletons stack with a gap, and a last `text` line is shorter.
|
|
8
|
+
////
|
|
9
|
+
//// @since 1.0.0
|
|
10
|
+
//// @needs none
|
|
11
|
+
//// @js none
|
|
12
|
+
//// @example html
|
|
13
|
+
//// <div aria-busy="true">
|
|
14
|
+
//// <span class="sr-only">Loading</span>
|
|
15
|
+
//// <div class="wu-skeleton circle animated" aria-hidden="true"></div>
|
|
16
|
+
//// <div class="wu-skeleton text animated" aria-hidden="true"></div>
|
|
17
|
+
//// <div class="wu-skeleton text animated" aria-hidden="true"></div>
|
|
18
|
+
//// </div>
|
|
19
|
+
//// @example html
|
|
20
|
+
//// <div class="wu-skeleton" aria-hidden="true" style="--wu-skeleton--Height: 8rem"></div>
|
|
21
|
+
//// @modifier text - One line of text: height of `1em` @since 1.0.0
|
|
22
|
+
//// @modifier circle - Round shape for an avatar @since 1.0.0
|
|
23
|
+
//// @modifier animated - Shimmer sweep, staggered across siblings where supported, when the user allows motion; `wu-animated` on an ancestor does the same @since 1.0.0
|
|
24
|
+
//// @variable --wu-skeleton--Width [100%] - Width; a last `text` line defaults to 60% @since 1.0.0
|
|
25
|
+
//// @variable --wu-skeleton--Height [6rem] - Height; `text` defaults to 1em @since 1.0.0
|
|
26
|
+
//// @variable --wu-skeleton--Size [2.5rem] - Width and height of a `circle` @since 1.0.0
|
|
27
|
+
////
|
|
28
|
+
|
|
29
|
+
@use "@wovenui/core/mixins" as woven;
|
|
30
|
+
|
|
31
|
+
$base: color-mix(in srgb, #{woven.token(text-regular)} 10%, transparent);
|
|
32
|
+
$shine: color-mix(in srgb, #{woven.token(text-regular)} 6%, transparent);
|
|
33
|
+
|
|
34
|
+
.wu-skeleton {
|
|
35
|
+
display: block;
|
|
36
|
+
width: var(--wu-skeleton--Width, 100%);
|
|
37
|
+
height: var(--wu-skeleton--Height, #{woven.token(space-24)});
|
|
38
|
+
border-radius: woven.token(radius-md);
|
|
39
|
+
background: linear-gradient(90deg, transparent 25%, #{$shine} 50%, transparent 75%) 150% 0 / 200% 100% no-repeat #{$base};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.wu-skeleton.text {
|
|
43
|
+
height: var(--wu-skeleton--Height, 1em);
|
|
44
|
+
border-radius: woven.token(radius-sm);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.wu-skeleton.text + .wu-skeleton.text {
|
|
48
|
+
margin-block-start: woven.token(space-2);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Shorter last line of a paragraph
|
|
52
|
+
.wu-skeleton.text:last-child:not(:first-child) {
|
|
53
|
+
width: var(--wu-skeleton--Width, 60%);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.wu-skeleton.circle {
|
|
57
|
+
flex-shrink: 0;
|
|
58
|
+
width: var(--wu-skeleton--Size, #{woven.token(space-10)});
|
|
59
|
+
height: var(--wu-skeleton--Size, #{woven.token(space-10)});
|
|
60
|
+
border-radius: woven.token(radius-full);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@media (forced-colors: active) {
|
|
64
|
+
.wu-skeleton {
|
|
65
|
+
border: woven.token(border-width-1) solid GrayText;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Opt-in shimmer, a staggered wave where sibling-index() is supported; it sweeps in the reading direction
|
|
70
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
71
|
+
.wu-skeleton.animated,
|
|
72
|
+
.wu-animated .wu-skeleton {
|
|
73
|
+
animation: wu-skeleton-shimmer 1.6s ease-in-out infinite;
|
|
74
|
+
animation-delay: calc((sibling-index() - 1) * 80ms);
|
|
75
|
+
|
|
76
|
+
&:dir(rtl) {
|
|
77
|
+
animation-direction: reverse;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@keyframes wu-skeleton-shimmer {
|
|
83
|
+
from {
|
|
84
|
+
background-position: 150% 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
to {
|
|
88
|
+
background-position: -50% 0;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
////
|
|
2
|
+
//// @component wu-spinner
|
|
3
|
+
//// Loading ring in the current text color, with an overlay for busy regions.
|
|
4
|
+
////
|
|
5
|
+
//// Pair it with `role="status"` and hidden text, or use `aria-hidden="true"` next to visible text. Reduced motion is handled by core reset.
|
|
6
|
+
////
|
|
7
|
+
//// The overlay covers its parent, dims the content and centers a spinner with optional text. The parent becomes the positioning box unless it already has a position.
|
|
8
|
+
////
|
|
9
|
+
//// @since 1.0.0
|
|
10
|
+
//// @needs none
|
|
11
|
+
//// @js none
|
|
12
|
+
//// @example html
|
|
13
|
+
//// <span class="wu-spinner" role="status"><span class="sr-only">Loading</span></span>
|
|
14
|
+
//// <button class="wu-btn" type="button" aria-busy="true"><span class="wu-spinner sm" aria-hidden="true"></span> Saving</button>
|
|
15
|
+
//// @example html
|
|
16
|
+
//// <div aria-busy="true">
|
|
17
|
+
//// <div inert>Orders table</div>
|
|
18
|
+
//// <div class="wu-spinner-overlay"><span class="wu-spinner" aria-hidden="true"></span><span>Loading orders</span></div>
|
|
19
|
+
//// </div>
|
|
20
|
+
//// @modifier sm | lg - Smaller (1rem) or larger (2rem, thicker band) ring @since 1.0.0
|
|
21
|
+
//// @part wu-spinner-overlay - Layer that covers a busy region and centers a spinner with text @since 1.0.0
|
|
22
|
+
//// @variable --wu-spinner--Size [1.25rem] - Width and height of the ring @since 1.0.0
|
|
23
|
+
//// @variable --wu-spinner-overlay--Background [85% surface] - Overlay background @since 1.0.0
|
|
24
|
+
////
|
|
25
|
+
|
|
26
|
+
@use "@wovenui/core/mixins" as woven;
|
|
27
|
+
|
|
28
|
+
.wu-spinner {
|
|
29
|
+
display: inline-block;
|
|
30
|
+
flex-shrink: 0;
|
|
31
|
+
width: var(--wu-spinner--Size, #{woven.token(space-5)});
|
|
32
|
+
height: var(--wu-spinner--Size, #{woven.token(space-5)});
|
|
33
|
+
border: woven.token(border-width-2) solid color-mix(in srgb, currentcolor 25%, transparent);
|
|
34
|
+
border-block-start-color: currentcolor;
|
|
35
|
+
border-radius: woven.token(radius-full);
|
|
36
|
+
vertical-align: middle;
|
|
37
|
+
animation: wu-spinner-rotate 0.75s linear infinite;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.wu-spinner.sm {
|
|
41
|
+
width: var(--wu-spinner--Size, #{woven.token(space-4)});
|
|
42
|
+
height: var(--wu-spinner--Size, #{woven.token(space-4)});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.wu-spinner.lg {
|
|
46
|
+
width: var(--wu-spinner--Size, #{woven.token(space-8)});
|
|
47
|
+
height: var(--wu-spinner--Size, #{woven.token(space-8)});
|
|
48
|
+
border-width: woven.token(border-width-4);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
:where(:has(> .wu-spinner-overlay)) {
|
|
52
|
+
position: relative;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.wu-spinner-overlay {
|
|
56
|
+
position: absolute;
|
|
57
|
+
inset: 0;
|
|
58
|
+
z-index: woven.token(z-hover);
|
|
59
|
+
display: flex;
|
|
60
|
+
flex-direction: column;
|
|
61
|
+
align-items: center;
|
|
62
|
+
justify-content: center;
|
|
63
|
+
gap: woven.token(space-2);
|
|
64
|
+
padding: woven.token(space-4);
|
|
65
|
+
border-radius: inherit;
|
|
66
|
+
background: var(--wu-spinner-overlay--Background, color-mix(in srgb, #{woven.token(bg-surface)} 85%, transparent));
|
|
67
|
+
color: woven.token(text-muted);
|
|
68
|
+
font-size: woven.token(font-size-sm);
|
|
69
|
+
text-align: center;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.wu-spinner-overlay > :not(.wu-spinner) {
|
|
73
|
+
padding: woven.token(space-1) woven.token(space-3);
|
|
74
|
+
border-radius: woven.token(radius-full);
|
|
75
|
+
background: woven.token(bg-surface);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// No reduced-motion rule: core's reset already stops animation under reduced motion
|
|
79
|
+
|
|
80
|
+
@keyframes wu-spinner-rotate {
|
|
81
|
+
to {
|
|
82
|
+
rotate: 360deg;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
////
|
|
2
|
+
//// @component wu-stat
|
|
3
|
+
//// One figure with a label, a large value in tabular figures and an optional change line.
|
|
4
|
+
////
|
|
5
|
+
//// Keep the signed number as text in the change line; the arrow and the color repeat it, they never replace it.
|
|
6
|
+
////
|
|
7
|
+
//// @since 1.0.0
|
|
8
|
+
//// @needs none
|
|
9
|
+
//// @js none
|
|
10
|
+
//// @example html
|
|
11
|
+
//// <div class="wu-stat">
|
|
12
|
+
//// <span class="wu-stat-label">Active users</span>
|
|
13
|
+
//// <span class="wu-stat-value">1,284</span>
|
|
14
|
+
//// <span class="wu-stat-delta success" data-trend="up">+12% from last month</span>
|
|
15
|
+
//// </div>
|
|
16
|
+
//// @example html
|
|
17
|
+
//// <div class="wu-stat lg">
|
|
18
|
+
//// <span class="wu-stat-label">Open tickets</span>
|
|
19
|
+
//// <span class="wu-stat-value">37</span>
|
|
20
|
+
//// <span class="wu-stat-delta" data-trend="down">-5 this week</span>
|
|
21
|
+
//// </div>
|
|
22
|
+
//// @modifier sm | lg - Smaller or larger value @since 1.0.0
|
|
23
|
+
//// @modifier success | danger - On the change line: success or danger text color @since 1.0.0
|
|
24
|
+
//// @part wu-stat-label - Muted label @since 1.0.0
|
|
25
|
+
//// @part wu-stat-value - Large value @since 1.0.0
|
|
26
|
+
//// @part wu-stat-delta - Change line, muted by default @since 1.0.0
|
|
27
|
+
//// @state data-trend="up" | data-trend="down" - On the change line: draws an up or down arrow @since 1.0.0
|
|
28
|
+
////
|
|
29
|
+
|
|
30
|
+
@use "@wovenui/core/mixins" as woven;
|
|
31
|
+
|
|
32
|
+
.wu-stat {
|
|
33
|
+
display: flex;
|
|
34
|
+
flex-direction: column;
|
|
35
|
+
gap: woven.token(space-1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.wu-stat-label {
|
|
39
|
+
color: woven.token(text-muted);
|
|
40
|
+
font-size: woven.token(font-size-sm);
|
|
41
|
+
font-weight: woven.token(font-weight-medium);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.wu-stat-value {
|
|
45
|
+
color: woven.token(text-regular);
|
|
46
|
+
font-size: woven.token(font-size-3xl);
|
|
47
|
+
font-variant-numeric: tabular-nums;
|
|
48
|
+
font-weight: woven.token(font-weight-semibold);
|
|
49
|
+
line-height: woven.token(line-height-tight);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Change line: keep the signed number as text; data-trend draws the arrow, role words set the tone
|
|
53
|
+
.wu-stat-delta {
|
|
54
|
+
display: inline-flex;
|
|
55
|
+
align-items: center;
|
|
56
|
+
gap: woven.token(space-1);
|
|
57
|
+
color: woven.token(text-muted);
|
|
58
|
+
font-size: woven.token(font-size-sm);
|
|
59
|
+
font-variant-numeric: tabular-nums;
|
|
60
|
+
font-weight: woven.token(font-weight-medium);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.wu-stat-delta[data-trend]::before {
|
|
64
|
+
content: "";
|
|
65
|
+
width: woven.token(space-2);
|
|
66
|
+
height: woven.token(space-2);
|
|
67
|
+
background: currentcolor;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.wu-stat-delta[data-trend="up"]::before {
|
|
71
|
+
clip-path: polygon(50% 0, 100% 100%, 0 100%);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.wu-stat-delta[data-trend="down"]::before {
|
|
75
|
+
clip-path: polygon(0 0, 100% 0, 50% 100%);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.wu-stat-delta.success {
|
|
79
|
+
color: woven.token(text-success);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.wu-stat-delta.danger {
|
|
83
|
+
color: woven.token(text-danger);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.wu-stat.sm .wu-stat-value {
|
|
87
|
+
font-size: woven.token(font-size-xl);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.wu-stat.lg .wu-stat-value {
|
|
91
|
+
font-size: woven.token(font-size-4xl);
|
|
92
|
+
}
|