@stisla/style 3.0.0-beta.8
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 +9 -0
- package/README.md +16 -0
- package/dist/accordion/accordion.css +194 -0
- package/dist/alert/alert.css +138 -0
- package/dist/autocomplete/autocomplete.css +193 -0
- package/dist/avatar/avatar.css +142 -0
- package/dist/avatar-group/avatar-group.css +42 -0
- package/dist/badge/badge.css +74 -0
- package/dist/breadcrumb/breadcrumb.css +71 -0
- package/dist/button/button.css +318 -0
- package/dist/button/index.d.ts +1 -0
- package/dist/button/index.js +6 -0
- package/dist/button-group/button-group.css +108 -0
- package/dist/card/card.css +219 -0
- package/dist/carousel/carousel.css +170 -0
- package/dist/checkbox/checkbox.css +98 -0
- package/dist/chunk-K45KLI3Y.js +74 -0
- package/dist/collapsible/collapsible.css +36 -0
- package/dist/combobox/combobox.css +106 -0
- package/dist/combobox/combobox.tomselect.css +251 -0
- package/dist/config-CARtrJ7I.d.ts +61 -0
- package/dist/dialog/dialog.css +258 -0
- package/dist/drawer/drawer.css +318 -0
- package/dist/empty-state/empty-state.css +138 -0
- package/dist/field/field.css +70 -0
- package/dist/icon-box/icon-box.css +64 -0
- package/dist/illustration/illustration.css +103 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +60 -0
- package/dist/indicator/indicator.css +84 -0
- package/dist/input/input.css +220 -0
- package/dist/input-group/input-group.css +141 -0
- package/dist/kbd/kbd.css +55 -0
- package/dist/link/link.css +28 -0
- package/dist/list-group/list-group.css +261 -0
- package/dist/media/media.css +115 -0
- package/dist/menu/menu.css +237 -0
- package/dist/meter/meter.css +124 -0
- package/dist/navbar/navbar.css +170 -0
- package/dist/page/page.css +95 -0
- package/dist/pagination/pagination.css +125 -0
- package/dist/placeholders/placeholders.css +58 -0
- package/dist/popover/popover.css +251 -0
- package/dist/progress/progress.css +139 -0
- package/dist/radio/radio.css +81 -0
- package/dist/scroll-area/scroll-area.css +25 -0
- package/dist/scroll-area/scroll-area.overlayscrollbars.css +42 -0
- package/dist/select/select.css +282 -0
- package/dist/separator/separator.css +26 -0
- package/dist/sidebar/sidebar.css +493 -0
- package/dist/slider/slider.css +159 -0
- package/dist/spinner/spinner.css +65 -0
- package/dist/switch/switch.css +91 -0
- package/dist/table/table.css +284 -0
- package/dist/tabs/tabs.css +137 -0
- package/dist/textarea/textarea.css +99 -0
- package/dist/timeline/timeline.css +271 -0
- package/dist/toast/toast.css +267 -0
- package/dist/toggle/toggle.css +125 -0
- package/dist/toggle-group/toggle-group.css +87 -0
- package/dist/tooltip/tooltip.css +95 -0
- package/package.json +46 -0
- package/src/theme.css +151 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/* @stisla/style — Popover. Ported from src/scss/components/_popover.scss. A surface-tier overlay
|
|
2
|
+
* panel (bg = surface, solid border) positioned by the JS layer (Floating UI), shown via
|
|
3
|
+
* [data-state="open"] + [data-placement]. Two shapes share the block: a plain popover (title + body
|
|
4
|
+
* in the root's padding) and a panel popover (a __header drops the root padding so header/footer
|
|
5
|
+
* dividers span edge-to-edge, each part owning its padding). References the @theme tokens: colors
|
|
6
|
+
* var(--color-*), sizes/spacing --spacing(n), type var(--text-*) / var(--leading-*) /
|
|
7
|
+
* var(--font-weight-*), radius var(--radius-*), shadow var(--shadow-*); only no-namespace customs
|
|
8
|
+
* use --st-* (border-width, duration, z-index); z-index routes through the z-index scale (--z-index-popover). Knobs are
|
|
9
|
+
* --popover-*. Positioning + focus-trap + dismiss ship with the JS layer. @layer components.
|
|
10
|
+
* Authoring rules: ../../../../PORTING.md */
|
|
11
|
+
|
|
12
|
+
@layer components {
|
|
13
|
+
/* position: absolute (not fixed) so the popover scrolls with the page alongside its trigger; the
|
|
14
|
+
JS layer portals it to <body> on open so ancestor overflow: hidden can't crop it. */
|
|
15
|
+
.popover {
|
|
16
|
+
position: absolute;
|
|
17
|
+
top: 0;
|
|
18
|
+
left: 0;
|
|
19
|
+
z-index: var(--popover-z-index, var(--z-index-popover));
|
|
20
|
+
display: none;
|
|
21
|
+
min-width: var(--popover-min-width, --spacing(52));
|
|
22
|
+
max-width: var(--popover-max-width, --spacing(69));
|
|
23
|
+
padding: var(--popover-padding-block, --spacing(4))
|
|
24
|
+
var(--popover-padding-inline, --spacing(4));
|
|
25
|
+
margin: 0;
|
|
26
|
+
color: var(--popover-color, var(--color-foreground));
|
|
27
|
+
background-color: var(--popover-bg, var(--color-surface));
|
|
28
|
+
border: var(--popover-border-width, var(--st-border-width)) solid
|
|
29
|
+
var(--popover-border-color, var(--color-border));
|
|
30
|
+
border-radius: var(--popover-radius, var(--radius-lg));
|
|
31
|
+
box-shadow: var(--popover-shadow, var(--shadow-md));
|
|
32
|
+
opacity: 0;
|
|
33
|
+
/* Resting transform = "above the trigger, easing up from below"; the per-placement overrides
|
|
34
|
+
below flip the direction so the entrance always reads as moving away from the anchored edge. */
|
|
35
|
+
transform: translateY(--spacing(1));
|
|
36
|
+
transition:
|
|
37
|
+
opacity var(--popover-transition-duration, var(--transition-duration-normal)) ease,
|
|
38
|
+
transform var(--popover-transition-duration, var(--transition-duration-normal)) ease;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.popover[data-state="open"] {
|
|
42
|
+
display: block;
|
|
43
|
+
opacity: 1;
|
|
44
|
+
transform: none;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* Per-placement resting transform — Floating UI's resolved placement (which may differ from the
|
|
48
|
+
requested side after flip()) sets [data-placement], so the entrance tracks the anchored edge. */
|
|
49
|
+
.popover[data-placement^="bottom"] {
|
|
50
|
+
transform: translateY(calc(--spacing(1) * -1));
|
|
51
|
+
}
|
|
52
|
+
.popover[data-placement^="left"] {
|
|
53
|
+
transform: translateX(--spacing(1));
|
|
54
|
+
}
|
|
55
|
+
.popover[data-placement^="right"] {
|
|
56
|
+
transform: translateX(calc(--spacing(1) * -1));
|
|
57
|
+
}
|
|
58
|
+
.popover[data-placement^="bottom"][data-state="open"],
|
|
59
|
+
.popover[data-placement^="left"][data-state="open"],
|
|
60
|
+
.popover[data-placement^="right"][data-state="open"] {
|
|
61
|
+
transform: none;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* === Title === */
|
|
65
|
+
.popover__title {
|
|
66
|
+
margin-block-end: var(--popover-title-margin-block-end, --spacing(2));
|
|
67
|
+
color: var(--popover-title-color, var(--color-foreground));
|
|
68
|
+
font-size: var(--popover-title-font-size, var(--text-base));
|
|
69
|
+
font-weight: var(--popover-title-font-weight, var(--font-weight-semibold));
|
|
70
|
+
line-height: var(--leading-tight);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* Clear the close chip when both title and close are present. :has() is baseline (Safari 16.4+). */
|
|
74
|
+
.popover:has(.popover__close) .popover__title {
|
|
75
|
+
padding-inline-end: calc(var(--popover-close-size, --spacing(6)) + --spacing(2));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* === Body === */
|
|
79
|
+
.popover__body {
|
|
80
|
+
margin: 0;
|
|
81
|
+
color: var(--popover-body-color, var(--color-muted-foreground));
|
|
82
|
+
font-size: var(--popover-body-font-size, var(--text-sm));
|
|
83
|
+
line-height: var(--popover-body-line-height, var(--leading-normal));
|
|
84
|
+
padding-block: var(--popover-body-padding-block, 0);
|
|
85
|
+
padding-inline: var(--popover-body-padding-inline, 0);
|
|
86
|
+
}
|
|
87
|
+
/* A body holding interactive children opts back into --color-foreground via a class or inline
|
|
88
|
+
override at the consumer; the default muted color keeps text-only popovers reading as context. */
|
|
89
|
+
|
|
90
|
+
/* === Panel parts (header / footer) === opt-in structure for panel-shaped popovers. A __header
|
|
91
|
+
present means "this is a panel": the root drops its padding so the dividers run edge-to-edge,
|
|
92
|
+
and header / body / footer each own their padding. :has() is baseline (Safari 16.4+). */
|
|
93
|
+
.popover:has(.popover__header) {
|
|
94
|
+
padding: 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.popover__header {
|
|
98
|
+
display: flex;
|
|
99
|
+
align-items: center;
|
|
100
|
+
gap: var(--popover-header-gap, --spacing(2));
|
|
101
|
+
padding: var(--popover-header-padding-block, --spacing(3))
|
|
102
|
+
var(--popover-header-padding-inline, --spacing(4));
|
|
103
|
+
border-block-end: var(--popover-border-width, var(--st-border-width)) solid
|
|
104
|
+
var(--popover-border-color, var(--color-border));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* Title inside a header is the row's leading child — drop its standalone bottom margin; the header
|
|
108
|
+
padding owns the rhythm now. */
|
|
109
|
+
.popover__header .popover__title {
|
|
110
|
+
margin-block-end: 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* Trailing action in the header (e.g. "mark all as read"). The auto margin is on the action, so
|
|
114
|
+
leading content packs to the start and only the action moves to the end. */
|
|
115
|
+
.popover__action {
|
|
116
|
+
display: inline-flex;
|
|
117
|
+
align-items: center;
|
|
118
|
+
gap: --spacing(2);
|
|
119
|
+
margin-inline-start: auto;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* In a panel the body sits between the dividers and owns its padding, since the root no longer
|
|
123
|
+
supplies any. A list component dropped straight in brings its own padding instead of __body. */
|
|
124
|
+
.popover:not(.popover--menu):has(.popover__header),
|
|
125
|
+
.popover:not(.popover--menu):has(.popover__footer) {
|
|
126
|
+
--popover-body-padding-block: --spacing(2);
|
|
127
|
+
--popover-body-padding-inline: --spacing(4);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.popover__footer {
|
|
131
|
+
padding: var(--popover-footer-padding-block, --spacing(3))
|
|
132
|
+
var(--popover-footer-padding-inline, --spacing(4));
|
|
133
|
+
border-block-start: var(--popover-border-width, var(--st-border-width)) solid
|
|
134
|
+
var(--popover-border-color, var(--color-border));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/* === Close chip === optional dismiss; positioned absolutely so it doesn't disturb the title/body
|
|
138
|
+
flow. A 1.5rem ghost chip with --color-accent hover. The pill radius is a stand-alone corner
|
|
139
|
+
affordance, not concentric math against the frame. */
|
|
140
|
+
.popover__close {
|
|
141
|
+
position: absolute;
|
|
142
|
+
top: var(--popover-padding-block, --spacing(4));
|
|
143
|
+
inset-inline-end: var(--popover-padding-inline, --spacing(4));
|
|
144
|
+
display: inline-flex;
|
|
145
|
+
align-items: center;
|
|
146
|
+
justify-content: center;
|
|
147
|
+
width: var(--popover-close-size, --spacing(6));
|
|
148
|
+
height: var(--popover-close-size, --spacing(6));
|
|
149
|
+
padding: 0;
|
|
150
|
+
color: var(--popover-close-color, var(--color-muted-foreground));
|
|
151
|
+
background: transparent;
|
|
152
|
+
border: 0;
|
|
153
|
+
border-radius: 9999px;
|
|
154
|
+
cursor: pointer;
|
|
155
|
+
transition:
|
|
156
|
+
color var(--popover-transition-duration, var(--transition-duration-normal)) ease,
|
|
157
|
+
background-color var(--popover-transition-duration, var(--transition-duration-normal)) ease;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.popover__close:hover {
|
|
161
|
+
color: var(--popover-close-color-hover, var(--color-foreground));
|
|
162
|
+
background-color: var(--popover-close-bg-hover, var(--color-accent));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.popover__close:focus-visible {
|
|
166
|
+
outline: 2px solid var(--color-ring);
|
|
167
|
+
outline-offset: 2px;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.popover__close > svg,
|
|
171
|
+
.popover__close > i {
|
|
172
|
+
width: 1em;
|
|
173
|
+
height: 1em;
|
|
174
|
+
font-size: var(--text-base);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/* === Arrow === a square rotated 45°. The JS arrow() middleware sets the cross-axis offset; the
|
|
178
|
+
per-placement rules below pin the placement-axis offset so half the diamond sits inside the
|
|
179
|
+
surface (covered by --popover-bg) and the other half sticks out as the point. The two
|
|
180
|
+
outward-facing diagonals carry the border so the arrow continues the frame seamlessly. */
|
|
181
|
+
.popover__arrow {
|
|
182
|
+
position: absolute;
|
|
183
|
+
width: var(--popover-arrow-size, --spacing(3));
|
|
184
|
+
height: var(--popover-arrow-size, --spacing(3));
|
|
185
|
+
background-color: var(--popover-bg, var(--color-surface));
|
|
186
|
+
transform: rotate(45deg);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/* CSS rotates clockwise; after rotate(45deg) each original edge maps to a diamond diagonal. For
|
|
190
|
+
each placement the two outward-facing diagonals get border; the inner two stay borderless. */
|
|
191
|
+
|
|
192
|
+
/* Top placement → arrow at the popover's bottom, pointing down (original right + bottom edges). */
|
|
193
|
+
.popover[data-placement^="top"] .popover__arrow {
|
|
194
|
+
bottom: calc(var(--popover-arrow-size, --spacing(3)) * -0.5);
|
|
195
|
+
border-right: var(--popover-border-width, var(--st-border-width)) solid
|
|
196
|
+
var(--popover-border-color, var(--color-border));
|
|
197
|
+
border-bottom: var(--popover-border-width, var(--st-border-width)) solid
|
|
198
|
+
var(--popover-border-color, var(--color-border));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/* Bottom placement → arrow at the popover's top, pointing up (original left + top edges). */
|
|
202
|
+
.popover[data-placement^="bottom"] .popover__arrow {
|
|
203
|
+
top: calc(var(--popover-arrow-size, --spacing(3)) * -0.5);
|
|
204
|
+
border-top: var(--popover-border-width, var(--st-border-width)) solid
|
|
205
|
+
var(--popover-border-color, var(--color-border));
|
|
206
|
+
border-left: var(--popover-border-width, var(--st-border-width)) solid
|
|
207
|
+
var(--popover-border-color, var(--color-border));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/* Left placement → arrow at the popover's right, pointing right (original top + right edges). */
|
|
211
|
+
.popover[data-placement^="left"] .popover__arrow {
|
|
212
|
+
right: calc(var(--popover-arrow-size, --spacing(3)) * -0.5);
|
|
213
|
+
border-top: var(--popover-border-width, var(--st-border-width)) solid
|
|
214
|
+
var(--popover-border-color, var(--color-border));
|
|
215
|
+
border-right: var(--popover-border-width, var(--st-border-width)) solid
|
|
216
|
+
var(--popover-border-color, var(--color-border));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* Right placement → arrow at the popover's left, pointing left (original left + bottom edges). */
|
|
220
|
+
.popover[data-placement^="right"] .popover__arrow {
|
|
221
|
+
left: calc(var(--popover-arrow-size, --spacing(3)) * -0.5);
|
|
222
|
+
border-bottom: var(--popover-border-width, var(--st-border-width)) solid
|
|
223
|
+
var(--popover-border-color, var(--color-border));
|
|
224
|
+
border-left: var(--popover-border-width, var(--st-border-width)) solid
|
|
225
|
+
var(--popover-border-color, var(--color-border));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/* === Menu-style popover === a popover hosting a .menu/.media list; tightens the body padding and
|
|
229
|
+
hands the borrowed row primitive its surface vars (read by .media). */
|
|
230
|
+
.popover--menu {
|
|
231
|
+
--popover-body-padding-block: --spacing(1);
|
|
232
|
+
--popover-body-padding-inline: --spacing(1);
|
|
233
|
+
|
|
234
|
+
--media-padding-inline: --spacing(3);
|
|
235
|
+
--media-padding-block: --spacing(2);
|
|
236
|
+
--media-border-color: transparent;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.popover--menu .popover__body {
|
|
240
|
+
display: flex;
|
|
241
|
+
flex-direction: column;
|
|
242
|
+
gap: var(--popover-body-gap, --spacing(1));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
@media (prefers-reduced-motion: reduce) {
|
|
246
|
+
.popover,
|
|
247
|
+
.popover__close {
|
|
248
|
+
transition: none;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/* @stisla/style — Progress. Ported from src/scss/components/_progress.scss. A thin bar that fills as
|
|
2
|
+
* work completes, with optional label + value caption. The wrapper is a 2-col grid (label | value)
|
|
3
|
+
* with the track spanning both. References the @theme tokens (colors var(--color-*), spacing/sizes
|
|
4
|
+
* --spacing(n), type var(--text-*) / var(--font-weight-*) / var(--leading-*)). Knobs are --progress-*.
|
|
5
|
+
* Sizes compact/roomy → sm/lg; pill radius literal 9999px. Indeterminate state via the
|
|
6
|
+
* [data-indeterminate] attribute (the legacy used a state class; dropped per §11). @layer components.
|
|
7
|
+
* Authoring rules: ../../../../PORTING.md */
|
|
8
|
+
|
|
9
|
+
@layer components {
|
|
10
|
+
.progress {
|
|
11
|
+
display: grid;
|
|
12
|
+
grid-template-columns: 1fr auto;
|
|
13
|
+
column-gap: --spacing(4);
|
|
14
|
+
row-gap: var(--progress-header-gap, --spacing(1));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* === Caption === label left, value right; either may be omitted (track spans both columns). */
|
|
18
|
+
.progress__label {
|
|
19
|
+
grid-column: 1;
|
|
20
|
+
font-size: var(--progress-label-font-size, var(--text-sm));
|
|
21
|
+
font-weight: var(--progress-label-font-weight, var(--font-weight-medium));
|
|
22
|
+
color: var(--progress-label-color, var(--color-foreground));
|
|
23
|
+
line-height: var(--leading-snug);
|
|
24
|
+
}
|
|
25
|
+
.progress__value {
|
|
26
|
+
grid-column: 2;
|
|
27
|
+
font-size: var(--progress-label-font-size, var(--text-sm));
|
|
28
|
+
color: var(--progress-value-color, var(--color-muted-foreground));
|
|
29
|
+
line-height: var(--leading-snug);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* === Track === the recessed pill; owns height, neutral bg, and the overflow clip. */
|
|
33
|
+
.progress__track {
|
|
34
|
+
grid-column: 1 / -1;
|
|
35
|
+
display: block;
|
|
36
|
+
height: var(--progress-height, --spacing(2));
|
|
37
|
+
background-color: var(--progress-bg, var(--color-neutral));
|
|
38
|
+
border-radius: var(--progress-radius, 9999px);
|
|
39
|
+
overflow: hidden;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* === Bar === sized by inline width %; default 0 so an unsized bar sits invisible. */
|
|
43
|
+
.progress__bar {
|
|
44
|
+
position: relative;
|
|
45
|
+
display: block;
|
|
46
|
+
width: 0;
|
|
47
|
+
height: 100%;
|
|
48
|
+
background-color: var(--progress-bar-bg, var(--color-primary));
|
|
49
|
+
overflow: hidden;
|
|
50
|
+
transition: width var(--progress-bar-transition-duration, 0.6s) ease;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* === Intent modifiers === the bar carries the color, not the track. */
|
|
54
|
+
.progress__bar--primary {
|
|
55
|
+
--progress-bar-bg: var(--color-primary);
|
|
56
|
+
}
|
|
57
|
+
.progress__bar--success {
|
|
58
|
+
--progress-bar-bg: var(--color-success);
|
|
59
|
+
}
|
|
60
|
+
.progress__bar--warning {
|
|
61
|
+
--progress-bar-bg: var(--color-warning);
|
|
62
|
+
}
|
|
63
|
+
.progress__bar--danger {
|
|
64
|
+
--progress-bar-bg: var(--color-danger);
|
|
65
|
+
}
|
|
66
|
+
.progress__bar--info {
|
|
67
|
+
--progress-bar-bg: var(--color-info);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* === Animated shimmer === a narrow gradient band sweeps across in the first ~third of the cycle,
|
|
71
|
+
then sits off-screen the rest (so the loop reads as an occasional sheen, not a treadmill). */
|
|
72
|
+
.progress__bar--animated::after {
|
|
73
|
+
content: "";
|
|
74
|
+
position: absolute;
|
|
75
|
+
inset: 0;
|
|
76
|
+
background: linear-gradient(
|
|
77
|
+
90deg,
|
|
78
|
+
transparent 0%,
|
|
79
|
+
var(--progress-shimmer-color, color-mix(in oklch, white 30%, transparent)) 50%,
|
|
80
|
+
transparent 100%
|
|
81
|
+
);
|
|
82
|
+
transform: translateX(-100%);
|
|
83
|
+
animation: st-progress-shimmer var(--progress-shimmer-duration, 3s) linear infinite;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* === Indeterminate === fixed-width bar sweeps across; the shimmer is suppressed (the moving bar is
|
|
87
|
+
the indicator). */
|
|
88
|
+
.progress[data-indeterminate] .progress__bar {
|
|
89
|
+
width: 50%;
|
|
90
|
+
animation: st-progress-indeterminate var(--progress-indeterminate-duration, 1.5s) ease-in-out infinite;
|
|
91
|
+
}
|
|
92
|
+
.progress[data-indeterminate] .progress__bar::after {
|
|
93
|
+
display: none;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* === Size modifiers (base = md) === */
|
|
97
|
+
.progress--sm {
|
|
98
|
+
--progress-height: --spacing(1);
|
|
99
|
+
}
|
|
100
|
+
.progress--lg {
|
|
101
|
+
--progress-height: --spacing(4);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@media (prefers-reduced-motion: reduce) {
|
|
105
|
+
.progress__bar {
|
|
106
|
+
transition: none;
|
|
107
|
+
}
|
|
108
|
+
.progress__bar--animated::after,
|
|
109
|
+
.progress[data-indeterminate] .progress__bar {
|
|
110
|
+
animation: none;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* Block — fills its container width per SPEC §9 escape ramp. */
|
|
115
|
+
.progress--block {
|
|
116
|
+
width: 100%;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@keyframes st-progress-shimmer {
|
|
121
|
+
0% {
|
|
122
|
+
transform: translateX(-100%);
|
|
123
|
+
}
|
|
124
|
+
33% {
|
|
125
|
+
transform: translateX(100%);
|
|
126
|
+
}
|
|
127
|
+
100% {
|
|
128
|
+
transform: translateX(100%);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@keyframes st-progress-indeterminate {
|
|
133
|
+
0% {
|
|
134
|
+
transform: translateX(-100%);
|
|
135
|
+
}
|
|
136
|
+
100% {
|
|
137
|
+
transform: translateX(200%);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/* @stisla/style — Radio. Ported from src/scss/components/_radio.scss + the form-check-base mixin.
|
|
2
|
+
* Native <input type="radio"> styled as a small round dot. References the @theme tokens (colors
|
|
3
|
+
* var(--color-*), spacing --spacing(n)); only no-namespace customs use --st-* (border-width). Knobs
|
|
4
|
+
* are --radio-* (fallback-default). @layer components.
|
|
5
|
+
*
|
|
6
|
+
* The shared check base is DUPLICATED here with the --radio-* namespace (keep in sync with
|
|
7
|
+
* checkbox.css). Radius is 50% (circle). :indeterminate is intentionally NOT styled — a radio matches
|
|
8
|
+
* it when no radio in the group is checked, and that "no selection" state keeps the unchecked
|
|
9
|
+
* affordance (and accepts hover). State is native, no is-*.
|
|
10
|
+
* Authoring rules: ../../../../PORTING.md */
|
|
11
|
+
|
|
12
|
+
@layer components {
|
|
13
|
+
.radio {
|
|
14
|
+
flex-shrink: 0;
|
|
15
|
+
width: var(--radio-size, --spacing(4));
|
|
16
|
+
height: var(--radio-size, --spacing(4));
|
|
17
|
+
margin: 0;
|
|
18
|
+
vertical-align: middle;
|
|
19
|
+
background-color: var(--radio-bg, var(--color-surface));
|
|
20
|
+
background-repeat: no-repeat;
|
|
21
|
+
background-position: center;
|
|
22
|
+
background-size: 100% 100%;
|
|
23
|
+
border: var(--radio-border-width, var(--st-border-width)) solid
|
|
24
|
+
var(--radio-border-color, var(--color-border-strong));
|
|
25
|
+
border-radius: var(--radio-radius, 50%);
|
|
26
|
+
appearance: none;
|
|
27
|
+
cursor: pointer;
|
|
28
|
+
transition:
|
|
29
|
+
background-color var(--transition-duration-fast) ease,
|
|
30
|
+
background-position 0.15s ease,
|
|
31
|
+
border-color var(--transition-duration-fast) ease,
|
|
32
|
+
box-shadow var(--transition-duration-fast) ease;
|
|
33
|
+
print-color-adjust: exact;
|
|
34
|
+
|
|
35
|
+
&:focus-visible {
|
|
36
|
+
outline: none;
|
|
37
|
+
border-color: var(--color-primary);
|
|
38
|
+
box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-ring) 25%, transparent);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* Hover — only when idle. Skip focus / invalid / checked / disabled (:indeterminate absent by
|
|
42
|
+
design — it's the "no selection yet" state and keeps the unchecked affordance). */
|
|
43
|
+
&:hover:not(
|
|
44
|
+
:disabled,
|
|
45
|
+
:focus-visible,
|
|
46
|
+
:checked,
|
|
47
|
+
[aria-invalid="true"],
|
|
48
|
+
:user-invalid
|
|
49
|
+
) {
|
|
50
|
+
border-color: color-mix(in oklch, var(--color-border-strong) 80%, var(--color-foreground));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
&[aria-invalid="true"],
|
|
54
|
+
&:user-invalid {
|
|
55
|
+
border-color: var(--color-danger);
|
|
56
|
+
}
|
|
57
|
+
&[aria-invalid="true"]:focus-visible,
|
|
58
|
+
&:user-invalid:focus-visible {
|
|
59
|
+
border-color: var(--color-danger);
|
|
60
|
+
box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-danger) 25%, transparent);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
&:disabled {
|
|
64
|
+
opacity: 0.55;
|
|
65
|
+
cursor: not-allowed;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* Checked — solid primary fill, dot painted via background-image (literal white, same reason as
|
|
69
|
+
.checkbox: data: URLs can't read CSS vars). */
|
|
70
|
+
&:checked {
|
|
71
|
+
--radio-indicator: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='4' fill='white'/%3E%3C/svg%3E");
|
|
72
|
+
background-color: var(--radio-bg-checked, var(--color-primary));
|
|
73
|
+
border-color: var(--radio-bg-checked, var(--color-primary));
|
|
74
|
+
background-image: var(--radio-indicator);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@media (prefers-reduced-motion: reduce) {
|
|
78
|
+
transition: none;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* @stisla/style — Scroll area (portable contract). Ported from src/scss/components/_scroll-area.scss.
|
|
2
|
+
* This file is the design contract any implementation shares: the .scroll-area root (a clipped scroll
|
|
3
|
+
* container that works with native scrollbars and no JS), the --scroll-area-* token knobs, and the
|
|
4
|
+
* --bordered modifier.
|
|
5
|
+
*
|
|
6
|
+
* The vanilla JS binding (OverlayScrollbars' .os-scrollbar / .os-viewport DOM, with our knobs
|
|
7
|
+
* forwarded to its --os-* var surface) lives in the sibling scroll-area.overlayscrollbars.css — load
|
|
8
|
+
* it alongside this file only when you use the OverlayScrollbars-backed scroll area.
|
|
9
|
+
*
|
|
10
|
+
* References the @theme tokens: colors var(--color-*), radius var(--radius-*); only no-namespace
|
|
11
|
+
* customs use --st-* (border-width). Knobs are --scroll-area-*. @layer components.
|
|
12
|
+
* Authoring rules: ../../../../PORTING.md */
|
|
13
|
+
|
|
14
|
+
@layer components {
|
|
15
|
+
.scroll-area {
|
|
16
|
+
position: relative;
|
|
17
|
+
overflow: hidden;
|
|
18
|
+
border-radius: var(--scroll-area-radius, var(--radius-md));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.scroll-area--bordered {
|
|
22
|
+
border: var(--scroll-area-border-width, var(--st-border-width)) solid
|
|
23
|
+
var(--scroll-area-border-color, var(--color-border));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/* @stisla/style — Scroll area / OverlayScrollbars adapter (vanilla-JS binding). The scroll-area's
|
|
2
|
+
* vanilla implementation is backed by OverlayScrollbars; this file forwards the --scroll-area-* knobs
|
|
3
|
+
* (defined by scroll-area.css) onto the plugin's --os-* var surface on its .os-scrollbar DOM. It is a
|
|
4
|
+
* lib-coupled stylesheet; a different scrollbar lib (or a React/Vue impl) would provide its own
|
|
5
|
+
* adapter and reuse the same --scroll-area-* tokens.
|
|
6
|
+
*
|
|
7
|
+
* Load AFTER scroll-area.css. The plugin's bundled CSS resets every --os-* var to 0 / `none` on
|
|
8
|
+
* .os-scrollbar (blocking inheritance from the host), and the JS injects that stylesheet inside
|
|
9
|
+
* `@layer foundation`, so this rule in @layer components outranks the reset and specificity resolves
|
|
10
|
+
* cleanly. The fallback-defaults live here (not on the .scroll-area root) so a consumer override of
|
|
11
|
+
* --scroll-area-* on the root inherits down to this descendant and wins. These .os-* selectors are a
|
|
12
|
+
* third-party lib's vocabulary, not Stisla state.
|
|
13
|
+
*
|
|
14
|
+
* References the @theme tokens (colors var(--color-*), sizes/spacing --spacing(n), radius
|
|
15
|
+
* var(--radius-*)). Knobs are --scroll-area-*. @layer components. Authoring rules: ../../../../PORTING.md */
|
|
16
|
+
|
|
17
|
+
@layer components {
|
|
18
|
+
.scroll-area .os-scrollbar {
|
|
19
|
+
--os-size: var(--scroll-area-bar-size, --spacing(2.5));
|
|
20
|
+
/* Two padding axes (OverlayScrollbars' own vocabulary): `perpendicular` is the gap across the
|
|
21
|
+
bar's short side, `axis` the gap along its length. They aren't fixed inline/block — which
|
|
22
|
+
physical axis each maps to flips with the bar's orientation — so they keep the lib's names.
|
|
23
|
+
Each per-axis knob falls back to the --scroll-area-bar-padding shorthand for uniform tuning. */
|
|
24
|
+
--os-padding-perpendicular: var(--scroll-area-bar-padding-perpendicular, var(--scroll-area-bar-padding, --spacing(0.5)));
|
|
25
|
+
--os-padding-axis: var(--scroll-area-bar-padding-axis, var(--scroll-area-bar-padding, --spacing(0.5)));
|
|
26
|
+
--os-track-bg: var(--scroll-area-track-bg, transparent);
|
|
27
|
+
--os-handle-bg: var(--scroll-area-handle-bg, color-mix(in oklch, var(--color-foreground) 25%, transparent));
|
|
28
|
+
--os-handle-bg-hover: var(--scroll-area-handle-bg-hover, color-mix(in oklch, var(--color-foreground) 45%, transparent));
|
|
29
|
+
--os-handle-bg-active: var(--scroll-area-handle-bg-active, color-mix(in oklch, var(--color-foreground) 65%, transparent));
|
|
30
|
+
--os-handle-border-radius: var(--scroll-area-bar-radius, 9999px);
|
|
31
|
+
--os-handle-min-size: var(--scroll-area-handle-min-size, --spacing(6));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* The plugin transitions the handle itself but doesn't gate it behind prefers-reduced-motion; zero
|
|
35
|
+
it so the cross-system reduced-motion commitment holds. */
|
|
36
|
+
@media (prefers-reduced-motion: reduce) {
|
|
37
|
+
.scroll-area .os-scrollbar-handle,
|
|
38
|
+
.scroll-area .os-scrollbar {
|
|
39
|
+
transition: none;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|