@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.
Files changed (63) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +16 -0
  3. package/dist/accordion/accordion.css +194 -0
  4. package/dist/alert/alert.css +138 -0
  5. package/dist/autocomplete/autocomplete.css +193 -0
  6. package/dist/avatar/avatar.css +142 -0
  7. package/dist/avatar-group/avatar-group.css +42 -0
  8. package/dist/badge/badge.css +74 -0
  9. package/dist/breadcrumb/breadcrumb.css +71 -0
  10. package/dist/button/button.css +318 -0
  11. package/dist/button/index.d.ts +1 -0
  12. package/dist/button/index.js +6 -0
  13. package/dist/button-group/button-group.css +108 -0
  14. package/dist/card/card.css +219 -0
  15. package/dist/carousel/carousel.css +170 -0
  16. package/dist/checkbox/checkbox.css +98 -0
  17. package/dist/chunk-K45KLI3Y.js +74 -0
  18. package/dist/collapsible/collapsible.css +36 -0
  19. package/dist/combobox/combobox.css +106 -0
  20. package/dist/combobox/combobox.tomselect.css +251 -0
  21. package/dist/config-CARtrJ7I.d.ts +61 -0
  22. package/dist/dialog/dialog.css +258 -0
  23. package/dist/drawer/drawer.css +318 -0
  24. package/dist/empty-state/empty-state.css +138 -0
  25. package/dist/field/field.css +70 -0
  26. package/dist/icon-box/icon-box.css +64 -0
  27. package/dist/illustration/illustration.css +103 -0
  28. package/dist/index.d.ts +14 -0
  29. package/dist/index.js +60 -0
  30. package/dist/indicator/indicator.css +84 -0
  31. package/dist/input/input.css +220 -0
  32. package/dist/input-group/input-group.css +141 -0
  33. package/dist/kbd/kbd.css +55 -0
  34. package/dist/link/link.css +28 -0
  35. package/dist/list-group/list-group.css +261 -0
  36. package/dist/media/media.css +115 -0
  37. package/dist/menu/menu.css +237 -0
  38. package/dist/meter/meter.css +124 -0
  39. package/dist/navbar/navbar.css +170 -0
  40. package/dist/page/page.css +95 -0
  41. package/dist/pagination/pagination.css +125 -0
  42. package/dist/placeholders/placeholders.css +58 -0
  43. package/dist/popover/popover.css +251 -0
  44. package/dist/progress/progress.css +139 -0
  45. package/dist/radio/radio.css +81 -0
  46. package/dist/scroll-area/scroll-area.css +25 -0
  47. package/dist/scroll-area/scroll-area.overlayscrollbars.css +42 -0
  48. package/dist/select/select.css +282 -0
  49. package/dist/separator/separator.css +26 -0
  50. package/dist/sidebar/sidebar.css +493 -0
  51. package/dist/slider/slider.css +159 -0
  52. package/dist/spinner/spinner.css +65 -0
  53. package/dist/switch/switch.css +91 -0
  54. package/dist/table/table.css +284 -0
  55. package/dist/tabs/tabs.css +137 -0
  56. package/dist/textarea/textarea.css +99 -0
  57. package/dist/timeline/timeline.css +271 -0
  58. package/dist/toast/toast.css +267 -0
  59. package/dist/toggle/toggle.css +125 -0
  60. package/dist/toggle-group/toggle-group.css +87 -0
  61. package/dist/tooltip/tooltip.css +95 -0
  62. package/package.json +46 -0
  63. 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
+ }