@fragment-build/ui 3.2.3 → 4.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/README.md +73 -0
- package/components/code-snippet/CodeSnippet.d.ts +9 -0
- package/components/code-snippet/CodeSnippet.js +14 -4
- package/components/code-snippet/CodeSnippet.js.map +1 -1
- package/components/code-snippet/code-snippet.css +18 -0
- package/components/segmented-control/SegmentedControl.d.ts +16 -0
- package/components/segmented-control/SegmentedControl.js +18 -0
- package/components/segmented-control/SegmentedControl.js.map +1 -0
- package/components/segmented-control/SegmentedControl.styles.d.ts +46 -0
- package/components/segmented-control/SegmentedControl.styles.js +20 -0
- package/components/segmented-control/SegmentedControl.styles.js.map +1 -0
- package/components/segmented-control/segmented-control.css +41 -0
- package/fragments/empty-state/EmptyState.d.ts +12 -0
- package/fragments/empty-state/EmptyState.js +12 -0
- package/fragments/empty-state/EmptyState.js.map +1 -0
- package/fragments/empty-state/EmptyState.styles.d.ts +55 -0
- package/fragments/empty-state/EmptyState.styles.js +23 -0
- package/fragments/empty-state/EmptyState.styles.js.map +1 -0
- package/fragments/empty-state/empty-state.css +39 -0
- package/fragments/form/FormCard.d.ts +14 -0
- package/fragments/form/FormCard.js +14 -0
- package/fragments/form/FormCard.js.map +1 -0
- package/fragments/form/FormCard.styles.d.ts +49 -0
- package/fragments/form/FormCard.styles.js +23 -0
- package/fragments/form/FormCard.styles.js.map +1 -0
- package/fragments/form/FormSection.d.ts +3 -1
- package/fragments/form/FormSection.js +2 -2
- package/fragments/form/FormSection.js.map +1 -1
- package/fragments/form/FormSection.styles.d.ts +6 -0
- package/fragments/form/FormSection.styles.js +2 -0
- package/fragments/form/FormSection.styles.js.map +1 -1
- package/fragments/form/form-card.css +42 -0
- package/fragments/form/form-section.css +22 -10
- package/fragments/form/form.css +1 -1
- package/fragments/navbar/navbar.css +2 -2
- package/fragments/page/PageHeader.d.ts +7 -2
- package/fragments/page/PageHeader.js +3 -3
- package/fragments/page/PageHeader.js.map +1 -1
- package/fragments/page/PageHeader.styles.d.ts +30 -21
- package/fragments/page/PageHeader.styles.js +10 -0
- package/fragments/page/PageHeader.styles.js.map +1 -1
- package/fragments/page/page-header.css +23 -3
- package/fragments/save-bar/SaveBar.d.ts +12 -0
- package/fragments/save-bar/SaveBar.js +15 -0
- package/fragments/save-bar/SaveBar.js.map +1 -0
- package/fragments/save-bar/SaveBar.styles.d.ts +37 -0
- package/fragments/save-bar/SaveBar.styles.js +16 -0
- package/fragments/save-bar/SaveBar.styles.js.map +1 -0
- package/fragments/save-bar/save-bar.css +28 -0
- package/fragments/shell/shell.css +8 -4
- package/fragments/sidebar/Sidebar.js +4 -1
- package/fragments/sidebar/Sidebar.js.map +1 -1
- package/fragments/sidebar/sidebar.css +52 -30
- package/fragments/stat/StatCard.d.ts +29 -0
- package/fragments/stat/StatCard.js +20 -0
- package/fragments/stat/StatCard.js.map +1 -0
- package/fragments/stat/StatCard.styles.d.ts +85 -0
- package/fragments/stat/StatCard.styles.js +26 -0
- package/fragments/stat/StatCard.styles.js.map +1 -0
- package/fragments/stat/stat-card.css +64 -0
- package/fragments/widget/Widget.d.ts +9 -2
- package/fragments/widget/Widget.js +4 -3
- package/fragments/widget/Widget.js.map +1 -1
- package/fragments/widget/Widget.styles.d.ts +51 -0
- package/fragments/widget/Widget.styles.js +12 -0
- package/fragments/widget/Widget.styles.js.map +1 -1
- package/fragments/widget/widget.css +49 -4
- package/index.css +138 -37
- package/index.d.ts +5 -0
- package/index.js +5 -0
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/code-snippet/code-snippet.css +18 -0
- package/src/components/segmented-control/segmented-control.css +41 -0
- package/src/fragments/empty-state/empty-state.css +39 -0
- package/src/fragments/form/form-card.css +42 -0
- package/src/fragments/form/form-section.css +22 -10
- package/src/fragments/form/form.css +1 -1
- package/src/fragments/navbar/navbar.css +2 -2
- package/src/fragments/page/page-header.css +23 -3
- package/src/fragments/save-bar/save-bar.css +28 -0
- package/src/fragments/shell/shell.css +8 -4
- package/src/fragments/sidebar/sidebar.css +52 -30
- package/src/fragments/stat/stat-card.css +64 -0
- package/src/fragments/widget/widget.css +49 -4
- package/src/index.css +138 -37
- package/src/theme.example.css +26 -0
- package/theme.example.css +26 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
@layer components {
|
|
2
|
+
/* Sticky inside the form rather than fixed to the viewport, so it never needs to know how wide the rail is. */
|
|
3
|
+
/* Takes no room while hidden, so a clean form ends where its last field does. */
|
|
4
|
+
.fragment-save-bar {
|
|
5
|
+
@apply pointer-events-none sticky bottom-20 z-30 flex h-0 justify-center overflow-visible md:bottom-6;
|
|
6
|
+
@apply translate-y-4 opacity-0 transition-[opacity,translate] duration-200 motion-reduce:transition-none;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.fragment-save-bar--visible {
|
|
10
|
+
@apply pointer-events-auto mt-6 h-auto translate-y-0 opacity-100;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.fragment-save-bar__bar {
|
|
14
|
+
@apply flex w-full max-w-2xl items-center justify-between gap-4 rounded-2xl border border-border bg-overlay py-2 pr-2 pl-4
|
|
15
|
+
text-sm shadow-lg;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.fragment-save-bar__message {
|
|
19
|
+
@apply flex items-center gap-2 font-medium text-foreground;
|
|
20
|
+
& svg {
|
|
21
|
+
@apply shrink-0 text-warning;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.fragment-save-bar__actions {
|
|
26
|
+
@apply flex shrink-0 items-center gap-1;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -7,16 +7,20 @@
|
|
|
7
7
|
@apply flex-col;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
/*
|
|
11
|
+
* The content column is capped at a width a table or a two-column dashboard still reads well at.
|
|
12
|
+
* Past ~1600px the eye travels too far between a label and its value, and a wide monitor is
|
|
13
|
+
* better spent on margin than on stretching every card.
|
|
14
|
+
*/
|
|
10
15
|
.fragment-shell__main {
|
|
11
|
-
@apply flex-1 min-w-0 mx-auto;
|
|
16
|
+
@apply flex-1 min-w-0 mx-auto w-full;
|
|
12
17
|
|
|
13
18
|
.fragment-shell--horizontal & {
|
|
14
|
-
@apply px-
|
|
19
|
+
@apply px-4 sm:px-6 lg:px-10 pt-6 pb-24 md:pt-8 md:pb-12 max-w-[1600px];
|
|
15
20
|
}
|
|
16
21
|
|
|
17
22
|
.fragment-shell--vertical & {
|
|
18
|
-
@apply px-
|
|
23
|
+
@apply px-4 sm:px-6 lg:px-10 py-6 max-w-[1600px];
|
|
19
24
|
}
|
|
20
|
-
|
|
21
25
|
}
|
|
22
26
|
}
|
|
@@ -4,27 +4,30 @@
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
.fragment-sidebar__collapsed {
|
|
7
|
-
@apply fixed top-0 left-0 border-r border-border
|
|
7
|
+
@apply fixed top-0 left-0 border-r border-border bg-sidebar text-sidebar-foreground
|
|
8
|
+
flex flex-col h-screen gap-4 transition-all z-40;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
.fragment-sidebar__collapsed-body {
|
|
11
|
-
@apply w-
|
|
12
|
+
@apply w-16 items-center flex flex-col gap-6 px-2 pt-5 overflow-y-auto flex-1;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
.fragment-sidebar__collapsed-footer {
|
|
15
|
-
@apply w-
|
|
16
|
+
@apply w-16 items-center flex flex-col gap-3 px-2 pb-4;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
.fragment-sidebar__expanded {
|
|
19
|
-
@apply fixed top-0 left-0 border-r border-border
|
|
20
|
+
@apply fixed top-0 left-0 border-r border-border bg-sidebar text-sidebar-foreground
|
|
21
|
+
flex flex-col h-screen gap-4 overflow-hidden transition-all z-40;
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
.fragment-sidebar__expanded-body {
|
|
23
|
-
@apply w-
|
|
25
|
+
@apply w-64 flex flex-col gap-6 px-3 pt-5 overflow-y-auto flex-1;
|
|
24
26
|
}
|
|
25
27
|
|
|
28
|
+
/* The footer is chrome (account, workspace), so it is tucked behind a hairline. */
|
|
26
29
|
.fragment-sidebar__expanded-footer {
|
|
27
|
-
@apply w-
|
|
30
|
+
@apply w-64 flex flex-col gap-2 px-3 pt-3 pb-4 border-t border-border;
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
.fragment-sidebar__placeholder {
|
|
@@ -32,15 +35,18 @@
|
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
.fragment-sidebar__layout-button-wrapper {
|
|
35
|
-
@apply fixed top-6 transition-all;
|
|
38
|
+
@apply fixed top-6 transition-all z-50;
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
.fragment-sidebar__layout-button {
|
|
39
|
-
@apply h-6! w-6
|
|
42
|
+
@apply h-6! w-6! min-w-6! bg-surface! border border-border shadow-sm text-muted;
|
|
43
|
+
& svg {
|
|
44
|
+
@apply size-3.5;
|
|
45
|
+
}
|
|
40
46
|
}
|
|
41
47
|
|
|
42
48
|
.fragment-sidebar__bottom-nav {
|
|
43
|
-
@apply fixed md:hidden z-50 bottom-0 bg-
|
|
49
|
+
@apply fixed md:hidden z-50 bottom-0 bg-sidebar text-sidebar-foreground flex w-full gap-2 px-2 py-1 border-t border-border;
|
|
44
50
|
& > *:nth-child(n+5):not(:last-child) {
|
|
45
51
|
@apply hidden;
|
|
46
52
|
}
|
|
@@ -67,7 +73,7 @@
|
|
|
67
73
|
.fragment-sidebar__collapsed-nav {
|
|
68
74
|
@apply flex flex-col gap-1;
|
|
69
75
|
& svg {
|
|
70
|
-
@apply size-
|
|
76
|
+
@apply size-[18px]! shrink-0 self-center;
|
|
71
77
|
}
|
|
72
78
|
}
|
|
73
79
|
|
|
@@ -104,26 +110,40 @@
|
|
|
104
110
|
}
|
|
105
111
|
|
|
106
112
|
.fragment-sidebar__custom-item {
|
|
107
|
-
@apply px-
|
|
113
|
+
@apply px-1;
|
|
108
114
|
}
|
|
109
115
|
|
|
110
116
|
.fragment-sidebar__nav {
|
|
111
|
-
@apply text-sm;
|
|
117
|
+
@apply text-sm flex flex-col gap-0.5;
|
|
118
|
+
|
|
119
|
+
/* Group label: quiet, so it organises the list without competing with it. */
|
|
120
|
+
& > .header {
|
|
121
|
+
@apply px-3 pb-1 text-xs font-medium text-muted;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.list-box {
|
|
125
|
+
@apply gap-0.5;
|
|
126
|
+
}
|
|
112
127
|
|
|
113
128
|
.list-box-item {
|
|
114
|
-
@apply px-3! pr-1! py-1! h-
|
|
129
|
+
@apply px-3! pr-1! py-1! h-9! gap-3! rounded-lg! font-normal text-sidebar-foreground/80;
|
|
130
|
+
&:hover, &[data-hovered="true"] {
|
|
131
|
+
background-color: var(--sidebar-default, var(--default)) !important;
|
|
132
|
+
@apply text-sidebar-foreground;
|
|
133
|
+
}
|
|
115
134
|
}
|
|
116
135
|
|
|
117
136
|
& svg {
|
|
118
|
-
@apply size-
|
|
137
|
+
@apply size-[18px] shrink-0 self-center;
|
|
119
138
|
}
|
|
120
139
|
}
|
|
121
140
|
|
|
122
141
|
.fragment-sidebar__accordion-trigger {
|
|
123
|
-
@apply w-full flex items-center gap-3 h-
|
|
124
|
-
text-foreground transition-colors;
|
|
142
|
+
@apply w-full flex items-center gap-3 h-9 px-3 pr-1 py-1 rounded-lg text-sm font-normal cursor-pointer
|
|
143
|
+
text-sidebar-foreground transition-colors;
|
|
125
144
|
&:hover, &[data-hovered="true"] {
|
|
126
|
-
|
|
145
|
+
/* `--default` is a page-level palette colour; opt out per-rail with `--sidebar-default`. */
|
|
146
|
+
background-color: var(--sidebar-default, var(--default)) !important;
|
|
127
147
|
}
|
|
128
148
|
& > .fragment-sidebar__accordion-label {
|
|
129
149
|
@apply flex-1 text-left;
|
|
@@ -131,7 +151,7 @@
|
|
|
131
151
|
}
|
|
132
152
|
|
|
133
153
|
.fragment-sidebar__accordion-trigger--active {
|
|
134
|
-
|
|
154
|
+
color: var(--sidebar-accent-soft-foreground, var(--accent-soft-foreground));
|
|
135
155
|
}
|
|
136
156
|
|
|
137
157
|
.fragment-sidebar__accordion-trigger .accordion__indicator {
|
|
@@ -149,7 +169,7 @@
|
|
|
149
169
|
@apply relative pl-7;
|
|
150
170
|
&::before {
|
|
151
171
|
content: '';
|
|
152
|
-
@apply absolute top-0 bottom-0 w-px bg-foreground opacity-15 rounded-full left-5;
|
|
172
|
+
@apply absolute top-0 bottom-0 w-px bg-sidebar-foreground opacity-15 rounded-full left-5;
|
|
153
173
|
}
|
|
154
174
|
.list-box {
|
|
155
175
|
@apply overflow-visible;
|
|
@@ -157,14 +177,16 @@
|
|
|
157
177
|
}
|
|
158
178
|
|
|
159
179
|
.fragment-sidebar__nav-item--active {
|
|
160
|
-
|
|
180
|
+
/* `--accent-soft` is translucent, so it composites over the rail, not the page. */
|
|
181
|
+
@apply bg-accent-soft! font-medium!;
|
|
182
|
+
color: var(--sidebar-accent-soft-foreground, var(--accent-soft-foreground)) !important;
|
|
161
183
|
}
|
|
162
184
|
|
|
163
185
|
.fragment-sidebar__sub-item {
|
|
164
186
|
@apply pl-8! relative;
|
|
165
187
|
&::before {
|
|
166
188
|
content: '';
|
|
167
|
-
@apply absolute -left-3 ml-px top-1/2 -translate-y-1/2 w-3 h-px bg-foreground opacity-15;
|
|
189
|
+
@apply absolute -left-3 ml-px top-1/2 -translate-y-1/2 w-3 h-px bg-sidebar-foreground opacity-15;
|
|
168
190
|
}
|
|
169
191
|
}
|
|
170
192
|
|
|
@@ -175,13 +197,13 @@
|
|
|
175
197
|
/* Layout modifier variants */
|
|
176
198
|
.fragment-sidebar--auto {
|
|
177
199
|
.fragment-sidebar__expanded {
|
|
178
|
-
@apply w-
|
|
200
|
+
@apply w-16 opacity-0 pointer-events-none xl:w-64 xl:opacity-100 xl:pointer-events-auto;
|
|
179
201
|
}
|
|
180
202
|
.fragment-sidebar__placeholder {
|
|
181
|
-
@apply w-
|
|
203
|
+
@apply w-16 opacity-0 pointer-events-none xl:w-64 xl:opacity-100 xl:pointer-events-auto;
|
|
182
204
|
}
|
|
183
205
|
.fragment-sidebar__layout-button-wrapper {
|
|
184
|
-
@apply left-
|
|
206
|
+
@apply left-16 translate-x-1/2 rotate-180 xl:left-64 xl:-translate-x-1/2 xl:rotate-0;
|
|
185
207
|
}
|
|
186
208
|
.fragment-sidebar__collapsed {
|
|
187
209
|
@apply xl:opacity-0;
|
|
@@ -190,13 +212,13 @@
|
|
|
190
212
|
|
|
191
213
|
.fragment-sidebar--collapsed {
|
|
192
214
|
.fragment-sidebar__expanded {
|
|
193
|
-
@apply w-
|
|
215
|
+
@apply w-16 opacity-0 pointer-events-none;
|
|
194
216
|
}
|
|
195
217
|
.fragment-sidebar__placeholder {
|
|
196
|
-
@apply w-
|
|
218
|
+
@apply w-16 opacity-0 pointer-events-none;
|
|
197
219
|
}
|
|
198
220
|
.fragment-sidebar__layout-button-wrapper {
|
|
199
|
-
@apply left-
|
|
221
|
+
@apply left-16 translate-x-1/2 rotate-180;
|
|
200
222
|
}
|
|
201
223
|
}
|
|
202
224
|
|
|
@@ -205,13 +227,13 @@
|
|
|
205
227
|
@apply opacity-0;
|
|
206
228
|
}
|
|
207
229
|
.fragment-sidebar__expanded {
|
|
208
|
-
@apply w-
|
|
230
|
+
@apply w-64;
|
|
209
231
|
}
|
|
210
232
|
.fragment-sidebar__placeholder {
|
|
211
|
-
@apply w-
|
|
233
|
+
@apply w-64;
|
|
212
234
|
}
|
|
213
235
|
.fragment-sidebar__layout-button-wrapper {
|
|
214
|
-
@apply left-
|
|
236
|
+
@apply left-64 -translate-x-1/2;
|
|
215
237
|
}
|
|
216
238
|
}
|
|
217
239
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
@layer components {
|
|
2
|
+
.fragment-stat-card {
|
|
3
|
+
@apply p-4 gap-2 min-w-0;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.fragment-stat-card__top {
|
|
7
|
+
@apply flex items-center justify-between gap-2;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.fragment-stat-card__label {
|
|
11
|
+
@apply flex items-center gap-1 text-xs font-medium text-muted min-w-0;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.fragment-stat-card__help {
|
|
15
|
+
@apply inline-flex text-muted/70 hover:text-foreground cursor-help rounded-full outline-none
|
|
16
|
+
focus-visible:ring-2 focus-visible:ring-focus;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.fragment-stat-card__icon {
|
|
20
|
+
@apply flex size-8 shrink-0 items-center justify-center rounded-lg bg-default text-muted;
|
|
21
|
+
& svg {
|
|
22
|
+
@apply size-4;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.fragment-stat-card__value-row {
|
|
27
|
+
@apply flex flex-wrap items-baseline gap-x-2 gap-y-1;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* Proportional figures: a standalone number with tabular digits looks loosely set. */
|
|
31
|
+
.fragment-stat-card__value {
|
|
32
|
+
@apply text-2xl font-semibold tracking-tight text-foreground truncate;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.fragment-stat-card__delta {
|
|
36
|
+
@apply inline-flex items-center gap-0.5 rounded-md px-1.5 py-0.5 text-xs font-medium;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.fragment-stat-card__delta--positive {
|
|
40
|
+
@apply bg-success-soft text-success-soft-foreground;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.fragment-stat-card__delta--negative {
|
|
44
|
+
@apply bg-danger-soft text-danger-soft-foreground;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.fragment-stat-card__delta--neutral {
|
|
48
|
+
@apply bg-default text-muted;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.fragment-stat-card__hint {
|
|
52
|
+
@apply text-xs text-muted;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.fragment-stat-card__skeleton {
|
|
56
|
+
@apply h-8 w-24 rounded-lg;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.fragment-stat-card--emphasis {
|
|
60
|
+
.fragment-stat-card__value {
|
|
61
|
+
@apply text-3xl;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -1,18 +1,59 @@
|
|
|
1
1
|
@layer components {
|
|
2
2
|
.fragment-widget {
|
|
3
|
-
@apply
|
|
3
|
+
@apply p-5 gap-4 min-w-0;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
.fragment-widget__header {
|
|
7
|
-
@apply p-0;
|
|
7
|
+
@apply p-0 flex flex-row items-start gap-3;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.fragment-widget__header-start {
|
|
11
|
+
@apply shrink-0;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.fragment-widget__heading {
|
|
15
|
+
@apply min-w-0 flex-1;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.fragment-widget__title {
|
|
19
|
+
@apply text-sm font-semibold;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.fragment-widget__description {
|
|
23
|
+
@apply mt-0.5 text-xs text-muted;
|
|
8
24
|
}
|
|
9
25
|
|
|
10
26
|
.fragment-widget__header-end {
|
|
11
|
-
@apply ml-auto;
|
|
27
|
+
@apply ml-auto flex items-center gap-2 shrink-0;
|
|
12
28
|
}
|
|
13
29
|
|
|
14
30
|
.fragment-widget__content {
|
|
15
|
-
@apply p-0 overflow-visible;
|
|
31
|
+
@apply p-0 overflow-visible min-w-0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.fragment-widget__footer {
|
|
35
|
+
@apply -mx-5 -mb-5 mt-auto px-5 py-3 border-t border-separator text-xs text-muted;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.fragment-widget--padding-sm {
|
|
39
|
+
@apply p-4 gap-3;
|
|
40
|
+
|
|
41
|
+
.fragment-widget__footer {
|
|
42
|
+
@apply -mx-4 -mb-4 px-4;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Edge-to-edge content: the header keeps its inset, the body does not. */
|
|
47
|
+
.fragment-widget--flush {
|
|
48
|
+
@apply p-0 gap-0;
|
|
49
|
+
|
|
50
|
+
.fragment-widget__header {
|
|
51
|
+
@apply px-5 pt-5 pb-4;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.fragment-widget__footer {
|
|
55
|
+
@apply mx-0 mb-0;
|
|
56
|
+
}
|
|
16
57
|
}
|
|
17
58
|
|
|
18
59
|
.fragment-widget--sm {
|
|
@@ -26,4 +67,8 @@
|
|
|
26
67
|
.fragment-widget--lg {
|
|
27
68
|
@apply col-span-1 lg:col-span-2 2xl:col-span-3;
|
|
28
69
|
}
|
|
70
|
+
|
|
71
|
+
.fragment-widget--full {
|
|
72
|
+
@apply col-span-full;
|
|
73
|
+
}
|
|
29
74
|
}
|
package/src/index.css
CHANGED
|
@@ -1,37 +1,65 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/*
|
|
2
|
+
* Type scale.
|
|
3
|
+
*
|
|
4
|
+
* Tailwind's own steps (12 / 14 / 16). Fragment used to pin `text-xs` to 13px and `text-sm` to
|
|
5
|
+
* 15px, which made every HeroUI control one step larger than the component library was drawn for
|
|
6
|
+
* and pushed dashboards towards a consumer-app density. Body copy is set to `text-sm` below, which
|
|
7
|
+
* is the size HeroUI's fields, list boxes and tables already use, so running text and controls now
|
|
8
|
+
* share one size.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/*
|
|
12
|
+
* Fragment surface roles.
|
|
13
|
+
*
|
|
14
|
+
* `inline` so the utilities compile to `var(--sidebar)` / `var(--navbar)` and are
|
|
15
|
+
* therefore resolved on the element, not frozen at `:root`. Each falls back to the
|
|
16
|
+
* page role, so a theme that never declares them renders exactly as before.
|
|
17
|
+
*/
|
|
18
|
+
@theme inline {
|
|
19
|
+
--color-sidebar: var(--sidebar, var(--background));
|
|
20
|
+
--color-sidebar-foreground: var(--sidebar-foreground, var(--foreground));
|
|
21
|
+
|
|
22
|
+
--color-navbar: var(--navbar, var(--background));
|
|
23
|
+
--color-navbar-foreground: var(--navbar-foreground, var(--foreground));
|
|
4
24
|
}
|
|
5
25
|
|
|
6
26
|
@layer base {
|
|
7
27
|
body {
|
|
8
|
-
@apply bg-background text-foreground;
|
|
28
|
+
@apply bg-background text-foreground text-sm antialiased;
|
|
9
29
|
}
|
|
10
30
|
|
|
31
|
+
/*
|
|
32
|
+
* Page title, section, card. A dashboard has many more card titles than page titles, so the
|
|
33
|
+
* steps are tight and the weight carries the hierarchy rather than the size.
|
|
34
|
+
*/
|
|
11
35
|
h1 {
|
|
12
|
-
@apply text-
|
|
36
|
+
@apply text-2xl font-semibold tracking-tight;
|
|
13
37
|
}
|
|
14
38
|
|
|
15
39
|
h2 {
|
|
16
|
-
@apply text-
|
|
40
|
+
@apply text-lg font-semibold tracking-tight;
|
|
17
41
|
}
|
|
18
42
|
|
|
19
43
|
h3 {
|
|
20
|
-
@apply text-
|
|
44
|
+
@apply text-base font-semibold;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
h4 {
|
|
48
|
+
@apply text-sm font-semibold;
|
|
21
49
|
}
|
|
22
50
|
}
|
|
23
51
|
|
|
24
52
|
@layer components {
|
|
53
|
+
/*
|
|
54
|
+
* Fields keep HeroUI's own height and padding. They get a hairline border instead of the
|
|
55
|
+
* default shadow, so a form reads as a set of crisp boxes on a flat surface.
|
|
56
|
+
*/
|
|
25
57
|
.input, .textarea, .select__trigger {
|
|
26
|
-
@apply border border-border
|
|
58
|
+
@apply border border-border shadow-none;
|
|
27
59
|
}
|
|
28
60
|
|
|
29
|
-
.input-group, .search-field__group, .number-field__group{
|
|
30
|
-
@apply border border-border shadow-none
|
|
31
|
-
|
|
32
|
-
.input-group__input, .search-field__input, .number-field__input {
|
|
33
|
-
@apply px-6 py-4;
|
|
34
|
-
}
|
|
61
|
+
.input-group, .search-field__group, .number-field__group {
|
|
62
|
+
@apply border border-border shadow-none;
|
|
35
63
|
}
|
|
36
64
|
|
|
37
65
|
.slider {
|
|
@@ -50,14 +78,6 @@
|
|
|
50
78
|
}
|
|
51
79
|
}
|
|
52
80
|
|
|
53
|
-
.select__indicator {
|
|
54
|
-
@apply right-4;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
.select__trigger:has(.select__indicator) {
|
|
58
|
-
@apply pr-10;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
81
|
.select__popover {
|
|
62
82
|
@apply rounded-xl;
|
|
63
83
|
}
|
|
@@ -65,18 +85,18 @@
|
|
|
65
85
|
.tabs {
|
|
66
86
|
&:not(.tabs--secondary) {
|
|
67
87
|
.tabs__list {
|
|
68
|
-
@apply bg-
|
|
69
|
-
|
|
70
|
-
border-radius: calc(var(--radius-
|
|
88
|
+
@apply bg-default p-1;
|
|
89
|
+
|
|
90
|
+
border-radius: calc(var(--radius-xl) + 0.25rem);
|
|
71
91
|
}
|
|
72
92
|
|
|
73
93
|
.tabs__indicator {
|
|
74
|
-
@apply
|
|
94
|
+
@apply bg-surface shadow-sm;
|
|
75
95
|
}
|
|
76
96
|
|
|
77
97
|
.tabs__tab {
|
|
78
98
|
&[data-selected="true"] {
|
|
79
|
-
@apply text-surface-
|
|
99
|
+
@apply text-surface-foreground;
|
|
80
100
|
}
|
|
81
101
|
}
|
|
82
102
|
|
|
@@ -86,7 +106,7 @@
|
|
|
86
106
|
}
|
|
87
107
|
|
|
88
108
|
.tabs__tab {
|
|
89
|
-
@apply h-
|
|
109
|
+
@apply h-8 px-4 text-sm text-nowrap;
|
|
90
110
|
}
|
|
91
111
|
|
|
92
112
|
.tabs__list {
|
|
@@ -95,25 +115,89 @@
|
|
|
95
115
|
}
|
|
96
116
|
}
|
|
97
117
|
|
|
118
|
+
/* Underline tabs size to their label, so a short list sits left instead of spreading out. */
|
|
98
119
|
&.tabs--secondary {
|
|
120
|
+
.tabs__list[data-orientation="horizontal"] {
|
|
121
|
+
@apply gap-6;
|
|
122
|
+
}
|
|
123
|
+
|
|
99
124
|
.tabs__tab {
|
|
100
|
-
@apply h-
|
|
125
|
+
@apply h-11 w-auto min-w-0 px-0.5 gap-2;
|
|
126
|
+
& svg {
|
|
127
|
+
@apply size-4;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/*
|
|
131
|
+
* A static underline for tab lists that render no <Tabs.Indicator>. The animated indicator
|
|
132
|
+
* slides from the previous tab with an inline translate, and when the selection is decided
|
|
133
|
+
* during hydration (a tab read from the URL) its first frame can stick, leaving the line
|
|
134
|
+
* under the wrong tab.
|
|
135
|
+
*/
|
|
136
|
+
&[data-selected="true"]:not(:has(.tabs__indicator))::after {
|
|
137
|
+
content: '';
|
|
138
|
+
@apply absolute inset-x-0 bottom-0 h-0.5 bg-accent;
|
|
139
|
+
}
|
|
101
140
|
}
|
|
102
141
|
}
|
|
103
142
|
}
|
|
104
143
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
144
|
+
/*
|
|
145
|
+
* The primary table as one flat card: a hairline frame, a header band and row dividers, instead
|
|
146
|
+
* of HeroUI's grey frame with a white card inset in it — which on a tinted page reads as a box
|
|
147
|
+
* in a box. Rows get a visible hover, since the default (`surface` at 40%) vanishes on white.
|
|
148
|
+
*/
|
|
149
|
+
.table-root--primary {
|
|
150
|
+
@apply border border-border bg-surface p-0;
|
|
151
|
+
|
|
152
|
+
border-radius: var(--radius-3xl);
|
|
153
|
+
box-shadow: var(--fragment-card-shadow, none);
|
|
108
154
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
@apply h-12 px-6;
|
|
155
|
+
.table__column {
|
|
156
|
+
@apply py-2.5 whitespace-nowrap;
|
|
112
157
|
|
|
113
|
-
|
|
114
|
-
|
|
158
|
+
&::after {
|
|
159
|
+
content: none;
|
|
115
160
|
}
|
|
116
161
|
}
|
|
162
|
+
|
|
163
|
+
.table__header {
|
|
164
|
+
@apply border-b border-separator;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.table__body tr td {
|
|
168
|
+
border-radius: 0 !important;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.table__cell {
|
|
172
|
+
@apply border-b border-separator;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.table__body tr:last-child .table__cell {
|
|
176
|
+
@apply border-b-0;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.table__row {
|
|
180
|
+
@media (hover: hover) {
|
|
181
|
+
&:hover .table__cell,
|
|
182
|
+
&[data-hovered="true"] .table__cell {
|
|
183
|
+
@apply bg-surface-secondary;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.table__footer {
|
|
189
|
+
@apply border-t border-separator;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/*
|
|
194
|
+
* Cards sit apart from the page by a hairline and an optional, very soft lift. The lift is a
|
|
195
|
+
* token so a theme on a white page (where the border is all there is) can turn it off.
|
|
196
|
+
*/
|
|
197
|
+
.card {
|
|
198
|
+
@apply border border-border;
|
|
199
|
+
|
|
200
|
+
box-shadow: var(--fragment-card-shadow, none);
|
|
117
201
|
}
|
|
118
202
|
}
|
|
119
203
|
|
|
@@ -124,10 +208,15 @@
|
|
|
124
208
|
@import "./fragments/widget/widget.css";
|
|
125
209
|
@import "./components/grid/grid.css";
|
|
126
210
|
@import "./components/multi-switch/multi-switch.css";
|
|
211
|
+
@import "./components/segmented-control/segmented-control.css";
|
|
127
212
|
@import "./components/code/code.css";
|
|
128
213
|
@import "./components/code-snippet/code-snippet.css";
|
|
129
214
|
@import "./fragments/form/form.css";
|
|
130
215
|
@import "./fragments/form/form-section.css";
|
|
216
|
+
@import "./fragments/form/form-card.css";
|
|
217
|
+
@import "./fragments/save-bar/save-bar.css";
|
|
218
|
+
@import "./fragments/stat/stat-card.css";
|
|
219
|
+
@import "./fragments/empty-state/empty-state.css";
|
|
131
220
|
|
|
132
221
|
.light,
|
|
133
222
|
.default,
|
|
@@ -137,6 +226,12 @@
|
|
|
137
226
|
--background: #fff;
|
|
138
227
|
--border: rgba(0, 0, 0, 0.07);
|
|
139
228
|
--surface-secondary: rgba(0, 0, 0, 0.02);
|
|
229
|
+
|
|
230
|
+
/* Fragment surfaces: default to the page surface */
|
|
231
|
+
--sidebar: var(--background);
|
|
232
|
+
--sidebar-foreground: var(--foreground);
|
|
233
|
+
--navbar: var(--background);
|
|
234
|
+
--navbar-foreground: var(--foreground);
|
|
140
235
|
}
|
|
141
236
|
|
|
142
237
|
.dark,
|
|
@@ -146,6 +241,12 @@
|
|
|
146
241
|
--foreground: var(--snow);
|
|
147
242
|
--border: rgba(255, 255, 255, 0.07);
|
|
148
243
|
--surface-secondary: rgba(255, 255, 255, 0.02);
|
|
244
|
+
|
|
245
|
+
/* Fragment surfaces: default to the page surface */
|
|
246
|
+
--sidebar: var(--background);
|
|
247
|
+
--sidebar-foreground: var(--foreground);
|
|
248
|
+
--navbar: var(--background);
|
|
249
|
+
--navbar-foreground: var(--foreground);
|
|
149
250
|
}
|
|
150
251
|
|
|
151
252
|
/* @import "./theme.example.css"; */
|
package/src/theme.example.css
CHANGED
|
@@ -43,6 +43,23 @@
|
|
|
43
43
|
--surface-tertiary: rgba(0, 0, 0, 0.9);
|
|
44
44
|
--surface-tertiary-foreground: #FCFAF6;
|
|
45
45
|
|
|
46
|
+
/*
|
|
47
|
+
* Fragment surfaces — Sidebar / Navbar.
|
|
48
|
+
* Omit them and each fragment stays on the page surface, exactly as before.
|
|
49
|
+
* Set --sidebar to give the rail its own ground; --sidebar-foreground moves the ink with it.
|
|
50
|
+
*/
|
|
51
|
+
--sidebar: #F7F5F0;
|
|
52
|
+
--sidebar-foreground: var(--foreground);
|
|
53
|
+
--navbar: var(--background);
|
|
54
|
+
--navbar-foreground: var(--foreground);
|
|
55
|
+
|
|
56
|
+
/*
|
|
57
|
+
* Optional. The states inside the rail are page-level palette roles, so they do not
|
|
58
|
+
* follow --sidebar on their own. Set these when the rail contrasts with the page.
|
|
59
|
+
*/
|
|
60
|
+
/* --sidebar-default: rgba(0, 0, 0, 0.04); */
|
|
61
|
+
/* --sidebar-accent-soft-foreground: color-mix(in oklab, var(--accent) 70%, var(--sidebar-foreground) 30%); */
|
|
62
|
+
|
|
46
63
|
--success: oklch(73.29% 0.1968 123.30);
|
|
47
64
|
--success-foreground: rgba(0, 0, 0, 0.9);
|
|
48
65
|
--warning: oklch(78.19% 0.1612 44.82);
|
|
@@ -91,6 +108,15 @@
|
|
|
91
108
|
--surface-secondary-foreground: oklch(99.11% 0.0000 24.57);
|
|
92
109
|
--surface-tertiary: oklch(27.21% 0.0000 24.57);
|
|
93
110
|
--surface-tertiary-foreground: oklch(99.11% 0.0000 24.57);
|
|
111
|
+
|
|
112
|
+
/* Fragment surfaces — Sidebar / Navbar */
|
|
113
|
+
--sidebar: oklch(16.00% 0.0000 24.57);
|
|
114
|
+
--sidebar-foreground: var(--foreground);
|
|
115
|
+
--navbar: var(--background);
|
|
116
|
+
--navbar-foreground: var(--foreground);
|
|
117
|
+
|
|
118
|
+
/* --sidebar-default: rgba(255, 255, 255, 0.06); */
|
|
119
|
+
/* --sidebar-accent-soft-foreground: color-mix(in oklab, var(--accent) 80%, var(--sidebar-foreground) 30%); */
|
|
94
120
|
--warning: oklch(82.03% 0.1388 48.83);
|
|
95
121
|
--warning-foreground: oklch(21.03% 0.0059 48.83);
|
|
96
122
|
}
|