@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,124 @@
|
|
|
1
|
+
/* @stisla/style — Meter. Ported from src/scss/components/_meter.scss. A bar visualizing a value within
|
|
2
|
+
* a known range (storage used, score vs target) — static measurement, vs progress's work-in-motion.
|
|
3
|
+
* Shares progress's anatomy but drops the in-motion modifiers and adds segmented stacking + an optional
|
|
4
|
+
* legend. References the @theme tokens (colors var(--color-*), spacing/sizes --spacing(n), type
|
|
5
|
+
* var(--text-*) / var(--font-weight-*) / var(--leading-*)). Knobs are --meter-*; sizes compact/roomy →
|
|
6
|
+
* sm/lg; pill radius literal 9999px. @layer components. Authoring rules: ../../../../PORTING.md */
|
|
7
|
+
|
|
8
|
+
@layer components {
|
|
9
|
+
.meter {
|
|
10
|
+
display: grid;
|
|
11
|
+
grid-template-columns: 1fr auto;
|
|
12
|
+
column-gap: --spacing(4);
|
|
13
|
+
row-gap: var(--meter-header-gap, --spacing(1));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* === Caption === */
|
|
17
|
+
.meter__label {
|
|
18
|
+
grid-column: 1;
|
|
19
|
+
font-size: var(--meter-label-font-size, var(--text-sm));
|
|
20
|
+
font-weight: var(--meter-label-font-weight, var(--font-weight-medium));
|
|
21
|
+
color: var(--meter-label-color, var(--color-foreground));
|
|
22
|
+
line-height: var(--leading-snug);
|
|
23
|
+
}
|
|
24
|
+
.meter__value {
|
|
25
|
+
grid-column: 2;
|
|
26
|
+
font-size: var(--meter-label-font-size, var(--text-sm));
|
|
27
|
+
color: var(--meter-value-color, var(--color-muted-foreground));
|
|
28
|
+
line-height: var(--leading-snug);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* === Track === flex row so multiple bars sit side by side; the segment gap doesn't apply at edges. */
|
|
32
|
+
.meter__track {
|
|
33
|
+
grid-column: 1 / -1;
|
|
34
|
+
display: flex;
|
|
35
|
+
gap: var(--meter-segment-gap, 1px);
|
|
36
|
+
height: var(--meter-height, --spacing(2));
|
|
37
|
+
background-color: var(--meter-bg, var(--color-neutral));
|
|
38
|
+
border-radius: var(--meter-radius, 9999px);
|
|
39
|
+
overflow: hidden;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* === Bar === sized by inline width %; default 0 so an unsized bar stays out of the way. */
|
|
43
|
+
.meter__bar {
|
|
44
|
+
display: block;
|
|
45
|
+
width: 0;
|
|
46
|
+
height: 100%;
|
|
47
|
+
background-color: var(--meter-bar-bg, var(--color-primary));
|
|
48
|
+
flex-shrink: 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* === Intent modifiers === */
|
|
52
|
+
.meter__bar--primary {
|
|
53
|
+
--meter-bar-bg: var(--color-primary);
|
|
54
|
+
}
|
|
55
|
+
.meter__bar--success {
|
|
56
|
+
--meter-bar-bg: var(--color-success);
|
|
57
|
+
}
|
|
58
|
+
.meter__bar--warning {
|
|
59
|
+
--meter-bar-bg: var(--color-warning);
|
|
60
|
+
}
|
|
61
|
+
.meter__bar--danger {
|
|
62
|
+
--meter-bar-bg: var(--color-danger);
|
|
63
|
+
}
|
|
64
|
+
.meter__bar--info {
|
|
65
|
+
--meter-bar-bg: var(--color-info);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* === Legend === optional row beneath the track; each dot reuses --meter-bar-bg. */
|
|
69
|
+
.meter__legend {
|
|
70
|
+
grid-column: 1 / -1;
|
|
71
|
+
display: flex;
|
|
72
|
+
flex-wrap: wrap;
|
|
73
|
+
align-items: center;
|
|
74
|
+
column-gap: var(--meter-legend-item-gap, --spacing(3.5));
|
|
75
|
+
row-gap: --spacing(1);
|
|
76
|
+
margin-block-start: var(--meter-legend-row-gap, --spacing(2));
|
|
77
|
+
font-size: var(--meter-legend-font-size, var(--text-xs));
|
|
78
|
+
color: var(--meter-legend-color, var(--color-muted-foreground));
|
|
79
|
+
line-height: var(--leading-snug);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.meter__legend-item {
|
|
83
|
+
display: inline-flex;
|
|
84
|
+
align-items: center;
|
|
85
|
+
gap: --spacing(1.5);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.meter__legend-dot {
|
|
89
|
+
display: inline-block;
|
|
90
|
+
width: var(--meter-legend-dot-size, --spacing(2));
|
|
91
|
+
height: var(--meter-legend-dot-size, --spacing(2));
|
|
92
|
+
background-color: var(--meter-bar-bg, var(--color-primary));
|
|
93
|
+
border-radius: 9999px;
|
|
94
|
+
flex-shrink: 0;
|
|
95
|
+
}
|
|
96
|
+
.meter__legend-dot--primary {
|
|
97
|
+
--meter-bar-bg: var(--color-primary);
|
|
98
|
+
}
|
|
99
|
+
.meter__legend-dot--success {
|
|
100
|
+
--meter-bar-bg: var(--color-success);
|
|
101
|
+
}
|
|
102
|
+
.meter__legend-dot--warning {
|
|
103
|
+
--meter-bar-bg: var(--color-warning);
|
|
104
|
+
}
|
|
105
|
+
.meter__legend-dot--danger {
|
|
106
|
+
--meter-bar-bg: var(--color-danger);
|
|
107
|
+
}
|
|
108
|
+
.meter__legend-dot--info {
|
|
109
|
+
--meter-bar-bg: var(--color-info);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* === Size modifiers (base = md) === */
|
|
113
|
+
.meter--sm {
|
|
114
|
+
--meter-height: --spacing(1);
|
|
115
|
+
}
|
|
116
|
+
.meter--lg {
|
|
117
|
+
--meter-height: --spacing(4);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* Block — fills its container width per SPEC §9 escape ramp. */
|
|
121
|
+
.meter--block {
|
|
122
|
+
width: 100%;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/* @stisla/style — Navbar. Ported from src/scss/components/_navbar.scss. A Stisla-original top bar: a
|
|
2
|
+
* brand mark, a row of nav links, and trailing extras in a flex row; below the lg breakpoint the nav
|
|
3
|
+
* and extras fold into a column behind a hamburger toggle. Driven by Stisla's own JS (a
|
|
4
|
+
* [data-navbar-toggle] click flips [data-state] on .navbar__menu), so its classes are Stisla-native
|
|
5
|
+
* BEM — not lib-coupled. References the @theme tokens: colors var(--color-*), sizes/spacing
|
|
6
|
+
* --spacing(n), type var(--leading-*), radius var(--radius-*); only no-namespace customs use --st-*
|
|
7
|
+
* (duration). Knobs are --navbar-*. State via [data-state] / [aria-disabled], never is-*. The mobile
|
|
8
|
+
* toggle ships with the JS layer. @layer components. Authoring rules: ../../../../PORTING.md */
|
|
9
|
+
|
|
10
|
+
@layer components {
|
|
11
|
+
.navbar {
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-wrap: wrap;
|
|
14
|
+
align-items: center;
|
|
15
|
+
gap: var(--navbar-gap, --spacing(3));
|
|
16
|
+
padding: var(--navbar-padding-block, --spacing(2.5)) var(--navbar-padding-inline, --spacing(4));
|
|
17
|
+
background-color: var(--navbar-bg, var(--color-background));
|
|
18
|
+
color: var(--navbar-color, var(--color-foreground));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* Container-inside-navbar — when the inner row is wrapped in a .container, the bar's inline padding
|
|
22
|
+
zeroes out and the container takes over the clamp; padding-block rides the container so the bar
|
|
23
|
+
height matches a paddingless bar. */
|
|
24
|
+
.navbar:has(> .container),
|
|
25
|
+
.navbar:has(> .container-fluid) {
|
|
26
|
+
--navbar-padding-inline: 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.navbar > .container,
|
|
30
|
+
.navbar > .container-fluid {
|
|
31
|
+
display: flex;
|
|
32
|
+
flex-wrap: wrap;
|
|
33
|
+
align-items: center;
|
|
34
|
+
gap: var(--navbar-gap, --spacing(3));
|
|
35
|
+
padding-block: var(--navbar-padding-block, --spacing(2.5));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* Brand — wordmark / logo. Hard height + line-height 1 matches the button chip geometry so the row
|
|
39
|
+
reads as one band; same inline inset as a button chip so brand text sits on the button x-grid. */
|
|
40
|
+
.navbar__brand {
|
|
41
|
+
display: inline-flex;
|
|
42
|
+
align-items: center;
|
|
43
|
+
height: var(--navbar-button-height, --spacing(9));
|
|
44
|
+
padding-block: var(--navbar-brand-padding-block, 0);
|
|
45
|
+
padding-inline: var(--navbar-brand-padding-inline, var(--navbar-button-padding-inline, --spacing(3)));
|
|
46
|
+
color: var(--navbar-color, var(--color-foreground));
|
|
47
|
+
line-height: var(--leading-none);
|
|
48
|
+
text-decoration: none;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* Toggle — square hamburger; hidden by default, shown below lg. */
|
|
52
|
+
.navbar__toggle {
|
|
53
|
+
display: none;
|
|
54
|
+
align-items: center;
|
|
55
|
+
justify-content: center;
|
|
56
|
+
width: var(--navbar-toggle-size, --spacing(9));
|
|
57
|
+
height: var(--navbar-toggle-size, --spacing(9));
|
|
58
|
+
padding: 0;
|
|
59
|
+
background-color: transparent;
|
|
60
|
+
color: var(--navbar-color, var(--color-foreground));
|
|
61
|
+
border: 0;
|
|
62
|
+
border-radius: var(--navbar-button-radius, calc(var(--radius-md) - --spacing(1)));
|
|
63
|
+
cursor: pointer;
|
|
64
|
+
transition: background-color var(--navbar-transition-duration, var(--transition-duration-fast)) ease;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.navbar__toggle:hover,
|
|
68
|
+
.navbar__toggle:focus-visible {
|
|
69
|
+
background-color: var(--navbar-button-bg-hover, var(--color-accent));
|
|
70
|
+
color: var(--navbar-button-color-hover, var(--color-accent-foreground));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* Menu — collapsing wrapper. Above lg, a transparent flex row beside the brand; below lg it folds. */
|
|
74
|
+
.navbar__menu {
|
|
75
|
+
display: flex;
|
|
76
|
+
flex: 1 1 auto;
|
|
77
|
+
align-items: center;
|
|
78
|
+
gap: var(--navbar-gap, --spacing(3));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* Nav — link list; reboot zeroes the ul margin/padding + list-style, only the flex layout is set. */
|
|
82
|
+
.navbar__nav {
|
|
83
|
+
display: flex;
|
|
84
|
+
align-items: center;
|
|
85
|
+
gap: var(--navbar-button-gap, --spacing(1));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* Button — ghost chip; soft accent on hover/focus, highlight when it marks the current page. Hard
|
|
89
|
+
height + line-height 1 keeps the chip a clean band regardless of font metrics. */
|
|
90
|
+
.navbar__button {
|
|
91
|
+
display: inline-flex;
|
|
92
|
+
align-items: center;
|
|
93
|
+
height: var(--navbar-button-height, --spacing(9));
|
|
94
|
+
padding-block: var(--navbar-button-padding-block, 0);
|
|
95
|
+
padding-inline: var(--navbar-button-padding-inline, --spacing(3));
|
|
96
|
+
border-radius: var(--navbar-button-radius, calc(var(--radius-md) - --spacing(1)));
|
|
97
|
+
background-color: transparent;
|
|
98
|
+
color: var(--navbar-button-color, var(--color-foreground));
|
|
99
|
+
line-height: var(--leading-none);
|
|
100
|
+
text-decoration: none;
|
|
101
|
+
transition:
|
|
102
|
+
background-color var(--navbar-transition-duration, var(--transition-duration-fast)) ease,
|
|
103
|
+
color var(--navbar-transition-duration, var(--transition-duration-fast)) ease;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.navbar__button:hover,
|
|
107
|
+
.navbar__button:focus-visible {
|
|
108
|
+
background-color: var(--navbar-button-bg-hover, var(--color-accent));
|
|
109
|
+
color: var(--navbar-button-color-hover, var(--color-accent-foreground));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.navbar__button[data-state="active"] {
|
|
113
|
+
background-color: var(--navbar-button-bg-active, var(--color-highlight));
|
|
114
|
+
color: var(--navbar-button-color-active, var(--color-highlight-foreground));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.navbar__button[aria-disabled="true"] {
|
|
118
|
+
opacity: 0.55;
|
|
119
|
+
pointer-events: none;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* === Above the collapse breakpoint (Tailwind `lg`) === nav grabs the leading edge so siblings
|
|
123
|
+
(search, status, action) fall to the trailing edge. */
|
|
124
|
+
@variant lg {
|
|
125
|
+
.navbar__nav {
|
|
126
|
+
margin-inline-end: auto;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* === Below the collapse breakpoint (Tailwind `max-lg`) === the menu folds behind the toggle. */
|
|
131
|
+
@variant max-lg {
|
|
132
|
+
.navbar__toggle {
|
|
133
|
+
display: inline-flex;
|
|
134
|
+
margin-inline-start: auto;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.navbar__menu {
|
|
138
|
+
display: none;
|
|
139
|
+
flex: 1 0 100%;
|
|
140
|
+
flex-direction: column;
|
|
141
|
+
align-items: stretch;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.navbar__menu[data-state="open"] {
|
|
145
|
+
display: flex;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.navbar__nav {
|
|
149
|
+
flex-direction: column;
|
|
150
|
+
align-items: stretch;
|
|
151
|
+
width: 100%;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.navbar__button {
|
|
155
|
+
width: 100%;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* Block — fills its container width per the SPEC escape ramp. */
|
|
160
|
+
.navbar--block {
|
|
161
|
+
width: 100%;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
@media (prefers-reduced-motion: reduce) {
|
|
165
|
+
.navbar__button,
|
|
166
|
+
.navbar__toggle {
|
|
167
|
+
transition-duration: 0.01ms;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/* @stisla/style — Page. Ported from src/scss/components/_page.scss. A pure-CSS layout primitive for
|
|
2
|
+
* what sits inside <main>: a flex-column flow whose row-gap owns the rhythm, so header / sections /
|
|
3
|
+
* section-headers are siblings with zero outer margin (the reboot already zeroes h1–h6 + p). Opinionated
|
|
4
|
+
* heading cadences (--title h3-ish, --section-title h5-ish) let consumers drop the recurring heading
|
|
5
|
+
* utility. References the @theme tokens: colors var(--color-*), spacing --spacing(n), type
|
|
6
|
+
* var(--text-*) / var(--leading-*) / var(--font-weight-*). Knobs are --page-* (fallback-default, so a
|
|
7
|
+
* scope retunes any gap and it cascades — custom props inherit). @layer components.
|
|
8
|
+
* Authoring rules: ../../../../PORTING.md */
|
|
9
|
+
|
|
10
|
+
@layer components {
|
|
11
|
+
/* The frame is the flow when used without a container; .page__body re-establishes the section
|
|
12
|
+
rhythm for the container-nested case. */
|
|
13
|
+
.page,
|
|
14
|
+
.page__body {
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: column;
|
|
17
|
+
gap: var(--page-section-gap, --spacing(6));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* Header — flex row, heading leading, optional actions trailing; wraps actions to a new line on
|
|
21
|
+
narrow viewports. space-between is the fallback for the two-bare-children case; with a headline
|
|
22
|
+
group present its flex: 1 1 auto pushes actions to the end. */
|
|
23
|
+
.page__header {
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-wrap: wrap;
|
|
26
|
+
align-items: center;
|
|
27
|
+
justify-content: space-between;
|
|
28
|
+
gap: var(--page-header-gap, --spacing(4));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* Heading group — title + description stack with a small gap; flex: 1 1 auto grows it into the row,
|
|
32
|
+
min-width: 0 keeps a long title wrapping instead of forcing actions off-screen. */
|
|
33
|
+
.page__headline {
|
|
34
|
+
display: flex;
|
|
35
|
+
flex: 1 1 auto;
|
|
36
|
+
flex-direction: column;
|
|
37
|
+
gap: var(--page-heading-gap, --spacing(1));
|
|
38
|
+
min-width: 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* Title — opinionated h3-ish cadence. */
|
|
42
|
+
.page__title {
|
|
43
|
+
font-size: var(--page-title-font-size, var(--text-xl));
|
|
44
|
+
font-weight: var(--page-title-font-weight, var(--font-weight-semibold));
|
|
45
|
+
line-height: var(--leading-tight);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* Description — optional muted helper copy under the title (muted baked in, like other __description
|
|
49
|
+
slots). */
|
|
50
|
+
.page__description {
|
|
51
|
+
font-size: var(--page-description-font-size, var(--text-sm));
|
|
52
|
+
font-weight: var(--page-description-font-weight, var(--font-weight-normal));
|
|
53
|
+
line-height: var(--leading-normal);
|
|
54
|
+
color: var(--page-description-color, var(--color-muted-foreground));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* Actions slot — shared by .page__header and .page__section-header; flex-shrink: 0 keeps a
|
|
58
|
+
multi-button row from squishing (the header's wrap drops it to the next line instead). */
|
|
59
|
+
.page__action {
|
|
60
|
+
display: flex;
|
|
61
|
+
flex-shrink: 0;
|
|
62
|
+
gap: var(--page-action-gap, --spacing(2));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* Section — mini-layout per block; the .page parent owns the inter-section rhythm. */
|
|
66
|
+
.page__section {
|
|
67
|
+
display: flex;
|
|
68
|
+
flex-direction: column;
|
|
69
|
+
gap: var(--page-section-inner-gap, --spacing(4));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* Section header — the header recipe at a tighter scale; shares .page__action. */
|
|
73
|
+
.page__section-header {
|
|
74
|
+
display: flex;
|
|
75
|
+
flex-wrap: wrap;
|
|
76
|
+
align-items: center;
|
|
77
|
+
justify-content: space-between;
|
|
78
|
+
gap: var(--page-section-header-gap, --spacing(3));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* Section title — opinionated h5-ish cadence. */
|
|
82
|
+
.page__section-title {
|
|
83
|
+
font-size: var(--page-section-title-font-size, var(--text-lg));
|
|
84
|
+
font-weight: var(--page-section-title-font-weight, var(--font-weight-semibold));
|
|
85
|
+
line-height: var(--leading-snug);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* Section description — the section-scale counterpart to .page__description. */
|
|
89
|
+
.page__section-description {
|
|
90
|
+
font-size: var(--page-section-description-font-size, var(--text-sm));
|
|
91
|
+
font-weight: var(--page-section-description-font-weight, var(--font-weight-normal));
|
|
92
|
+
line-height: var(--leading-normal);
|
|
93
|
+
color: var(--page-section-description-color, var(--color-muted-foreground));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/* @stisla/style — Pagination. Ported from src/scss/components/_pagination.scss. A row of standalone
|
|
2
|
+
* page chips. Rest transparent; hover paints accent; current page (data-state="active" /
|
|
3
|
+
* aria-current="page") paints highlight. Heights match the .button cadence. References the @theme
|
|
4
|
+
* tokens (colors var(--color-*), spacing/sizes --spacing(n), type var(--text-*) / var(--leading-*),
|
|
5
|
+
* radius var(--radius-*)); only no-namespace customs use --st-* (border-width, duration). Knobs are
|
|
6
|
+
* --pagination-*; sizes compact/roomy → sm/lg. State via attributes / native :disabled, no is-*.
|
|
7
|
+
* @layer components. Authoring rules: ../../../../PORTING.md */
|
|
8
|
+
|
|
9
|
+
@layer components {
|
|
10
|
+
.pagination {
|
|
11
|
+
display: flex;
|
|
12
|
+
flex-wrap: wrap;
|
|
13
|
+
align-items: center;
|
|
14
|
+
gap: var(--pagination-gap, --spacing(1));
|
|
15
|
+
font-size: var(--pagination-font-size, var(--text-sm));
|
|
16
|
+
/* Strip default list chrome — works whether root is <ul>/<ol>/<div>. */
|
|
17
|
+
padding-inline-start: 0;
|
|
18
|
+
list-style: none;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* === Chip === hard height + min-width === height so a single glyph lands square. */
|
|
22
|
+
.pagination__button {
|
|
23
|
+
display: inline-flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
justify-content: center;
|
|
26
|
+
gap: var(--pagination-button-inner-gap, --spacing(1.5));
|
|
27
|
+
height: var(--pagination-button-height, --spacing(9));
|
|
28
|
+
min-width: var(--pagination-button-height, --spacing(9));
|
|
29
|
+
padding-block: var(--pagination-button-padding-block, 0);
|
|
30
|
+
padding-inline: var(--pagination-button-padding-inline, --spacing(3));
|
|
31
|
+
background-color: var(--pagination-button-bg, transparent);
|
|
32
|
+
color: var(--pagination-button-color, var(--color-foreground));
|
|
33
|
+
border: var(--pagination-button-border-width, var(--st-border-width)) solid
|
|
34
|
+
var(--pagination-button-border-color, transparent);
|
|
35
|
+
border-radius: var(--pagination-button-radius, var(--radius-md));
|
|
36
|
+
text-decoration: none;
|
|
37
|
+
font: inherit;
|
|
38
|
+
font-size: inherit;
|
|
39
|
+
line-height: var(--leading-none);
|
|
40
|
+
cursor: default;
|
|
41
|
+
transition:
|
|
42
|
+
background-color var(--pagination-transition-duration, var(--transition-duration-fast)) ease,
|
|
43
|
+
color var(--pagination-transition-duration, var(--transition-duration-fast)) ease;
|
|
44
|
+
|
|
45
|
+
/* Icon — 1em scales with font-size so --sm shrinks icons proportionally (no per-size override). */
|
|
46
|
+
> :is(svg, i) {
|
|
47
|
+
width: 1em;
|
|
48
|
+
height: 1em;
|
|
49
|
+
flex-shrink: 0;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* Interactive chips opt in via element selector; <span> edge markers stay quiet. */
|
|
54
|
+
a.pagination__button,
|
|
55
|
+
button.pagination__button {
|
|
56
|
+
cursor: pointer;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
a.pagination__button:focus-visible,
|
|
60
|
+
button.pagination__button:focus-visible {
|
|
61
|
+
outline: 2px solid var(--pagination-button-ring, var(--color-ring));
|
|
62
|
+
outline-offset: 2px;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* Hover only on a quiet chip — an active or disabled chip must not flash the accent surface. */
|
|
66
|
+
a.pagination__button:hover:not([data-state="active"], [aria-current="page"], [aria-disabled="true"]),
|
|
67
|
+
button.pagination__button:hover:not([data-state="active"], [aria-current="page"], [aria-disabled="true"]):not(:disabled) {
|
|
68
|
+
background-color: var(--pagination-button-bg-hover, var(--color-accent));
|
|
69
|
+
color: var(--pagination-button-color-hover, var(--color-accent-foreground));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* === Current page === */
|
|
73
|
+
.pagination__button[data-state="active"],
|
|
74
|
+
.pagination__button[aria-current="page"] {
|
|
75
|
+
background-color: var(--pagination-button-bg-active, var(--color-highlight));
|
|
76
|
+
color: var(--pagination-button-color-active, var(--color-highlight-foreground));
|
|
77
|
+
border-color: var(--pagination-button-bg-active, var(--color-highlight));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* === Disabled === */
|
|
81
|
+
.pagination__button[aria-disabled="true"],
|
|
82
|
+
.pagination__button:disabled {
|
|
83
|
+
color: var(--pagination-button-color-disabled, var(--color-muted-foreground));
|
|
84
|
+
pointer-events: none;
|
|
85
|
+
cursor: not-allowed;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* === Ellipsis === non-interactive truncation marker; same footprint, no hover/focus/border. */
|
|
89
|
+
.pagination__ellipsis {
|
|
90
|
+
display: inline-flex;
|
|
91
|
+
align-items: center;
|
|
92
|
+
justify-content: center;
|
|
93
|
+
height: var(--pagination-button-height, --spacing(9));
|
|
94
|
+
min-width: var(--pagination-button-height, --spacing(9));
|
|
95
|
+
color: var(--pagination-button-color-disabled, var(--color-muted-foreground));
|
|
96
|
+
pointer-events: none;
|
|
97
|
+
user-select: none;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* === Size modifiers (base = md) === */
|
|
101
|
+
.pagination--sm {
|
|
102
|
+
--pagination-button-height: --spacing(7);
|
|
103
|
+
--pagination-button-padding-inline: --spacing(2);
|
|
104
|
+
--pagination-font-size: var(--text-xs);
|
|
105
|
+
}
|
|
106
|
+
.pagination--lg {
|
|
107
|
+
--pagination-button-height: --spacing(11);
|
|
108
|
+
--pagination-button-padding-inline: --spacing(4);
|
|
109
|
+
--pagination-font-size: var(--text-base);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* === Alignment === */
|
|
113
|
+
.pagination--center {
|
|
114
|
+
justify-content: center;
|
|
115
|
+
}
|
|
116
|
+
.pagination--end {
|
|
117
|
+
justify-content: flex-end;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@media (prefers-reduced-motion: reduce) {
|
|
121
|
+
.pagination__button {
|
|
122
|
+
transition: none;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/* @stisla/style — Placeholders. Ported from src/scss/components/_placeholders.scss. A skeleton
|
|
2
|
+
* stand-in for content that hasn't loaded: apply .placeholder to any inline element and size it with
|
|
3
|
+
* a width utility or inline style. References the @theme tokens: colors var(--color-*), radius
|
|
4
|
+
* var(--radius-*). Knobs are --placeholder-* (fallback-default). The 1em height is font-relative on
|
|
5
|
+
* purpose (the bar scales with its text); the wave mask stops and the 2s ambient loops are bespoke
|
|
6
|
+
* (no token). Sizes --compact/--roomy → --sm/--lg. @layer components.
|
|
7
|
+
* Authoring rules: ../../../../PORTING.md */
|
|
8
|
+
|
|
9
|
+
@layer components {
|
|
10
|
+
.placeholder {
|
|
11
|
+
display: inline-block;
|
|
12
|
+
min-height: var(--placeholder-height, 1em);
|
|
13
|
+
vertical-align: middle;
|
|
14
|
+
cursor: wait;
|
|
15
|
+
background: var(--placeholder-bg, var(--color-muted-foreground));
|
|
16
|
+
opacity: var(--placeholder-opacity, 0.5);
|
|
17
|
+
border-radius: var(--placeholder-radius, var(--radius-sm));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* === Sizes (base = md) === */
|
|
21
|
+
.placeholder--sm {
|
|
22
|
+
--placeholder-height: 0.75em;
|
|
23
|
+
}
|
|
24
|
+
.placeholder--lg {
|
|
25
|
+
--placeholder-height: 1.5em;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* === Animations === modifiers on the bar itself; reduced motion stops them (a paused bar still
|
|
29
|
+
reads as "not loaded" from its shape, so the animation is decoration). */
|
|
30
|
+
.placeholder--glow {
|
|
31
|
+
animation: st-placeholder-glow 2s ease-in-out infinite;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.placeholder--wave {
|
|
35
|
+
mask-image: linear-gradient(130deg, #000 55%, rgba(0, 0, 0, 0.65) 75%, #000 95%);
|
|
36
|
+
mask-size: 200% 100%;
|
|
37
|
+
animation: st-placeholder-wave 2s linear infinite;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@media (prefers-reduced-motion: reduce) {
|
|
41
|
+
.placeholder--glow,
|
|
42
|
+
.placeholder--wave {
|
|
43
|
+
animation: none;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@keyframes st-placeholder-glow {
|
|
49
|
+
50% {
|
|
50
|
+
opacity: 0.2;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@keyframes st-placeholder-wave {
|
|
55
|
+
100% {
|
|
56
|
+
mask-position: -200% 0%;
|
|
57
|
+
}
|
|
58
|
+
}
|