seblify 0.1.7 → 0.1.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 +21 -0
- package/README.md +218 -2
- package/dist/components/AppShell.svelte +136 -136
- package/dist/components/Banner.svelte +56 -56
- package/dist/components/Button.svelte +64 -64
- package/dist/components/Icon.svelte +67 -67
- package/dist/components/Sidebar.svelte +106 -106
- package/dist/components/Snackbar.svelte +26 -26
- package/dist/components/Toast.svelte +27 -27
- package/dist/components/component-doc.d.ts +1 -1
- package/dist/components/display/annotation/badge/badge.thumbnail.png +0 -0
- package/package.json +23 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sebastian Andresen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
|
+
[](https://www.npmjs.com/package/seblify)
|
|
2
|
+
[](https://www.npmjs.com/package/seblify)
|
|
3
|
+
|
|
4
|
+
[](https://seblify.com)
|
|
5
|
+
|
|
1
6
|
# Seblify
|
|
2
7
|
|
|
3
|
-
|
|
8
|
+
[comment]: <> (==== ========================= ====)
|
|
9
|
+
[comment]: <> (=== AUTOGENERATED - DO NOT EDIT ===)
|
|
10
|
+
[comment]: <> (==== ========================= ====)
|
|
11
|
+
[comment]: <> (= edit scripts/readme/template.md =)
|
|
12
|
+
[comment]: <> (==== ========================= ====)
|
|
4
13
|
|
|
5
|
-
Svelte component library for
|
|
14
|
+
Svelte UI component library for building themed interfaces.
|
|
6
15
|
|
|
7
16
|
## Installation
|
|
8
17
|
|
|
@@ -16,3 +25,210 @@ npm install seblify
|
|
|
16
25
|
import { Button, Badge } from 'seblify';
|
|
17
26
|
import 'seblify/theme.css';
|
|
18
27
|
```
|
|
28
|
+
|
|
29
|
+
## Components
|
|
30
|
+
|
|
31
|
+
[](https://seblify.com/components)
|
|
32
|
+
[](https://seblify.com/components)
|
|
33
|
+
[](https://seblify.com/components)
|
|
34
|
+
|
|
35
|
+
Explore the supported components in this library. Components are grouped by purpose and linked to their full documentation, including usage examples, props, accessibility notes, and design-system guidance.
|
|
36
|
+
|
|
37
|
+
### Quick links
|
|
38
|
+
|
|
39
|
+
- [Input Components](https://seblify.com/sections/action)
|
|
40
|
+
- [Display Components](https://seblify.com/sections/display)
|
|
41
|
+
- [Framework Components](https://seblify.com/sections/framework)
|
|
42
|
+
- [Design System](https://seblify.com/design-system)
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Input Components <small>· 6 items</small>
|
|
47
|
+
|
|
48
|
+
Components that let users make choices, submit data, and trigger behavior.
|
|
49
|
+
|
|
50
|
+
<details>
|
|
51
|
+
<summary><strong>Input Fields</strong></summary>
|
|
52
|
+
|
|
53
|
+
Text-like controls for entering structured user input.
|
|
54
|
+
|
|
55
|
+
| Component | Summary | Status |
|
|
56
|
+
| --- | --- | --- |
|
|
57
|
+
| [Pin Input](https://seblify.com/components/pin-input) | A segmented numeric input for short verification codes. | ⚪ Draft |
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
</details>
|
|
62
|
+
|
|
63
|
+
<br />
|
|
64
|
+
|
|
65
|
+
<details>
|
|
66
|
+
<summary><strong>Selectable</strong></summary>
|
|
67
|
+
|
|
68
|
+
Controls for selecting boolean, range, or option-based values.
|
|
69
|
+
|
|
70
|
+
| Component | Summary | Status |
|
|
71
|
+
| --- | --- | --- |
|
|
72
|
+
| [Checkbox](https://seblify.com/components/checkbox) | A boolean selection control for one option in a form or settings flow. | ⚪ Draft |
|
|
73
|
+
| [SegmentControl](https://seblify.com/components/segment-control) | A compact single-selection control for switching between a small set of options. | ⚪ Draft |
|
|
74
|
+
| [Slider](https://seblify.com/components/slider) | A range input for choosing an approximate numeric value. | ⚪ Draft |
|
|
75
|
+
| [Switch](https://seblify.com/components/switch) | A two-state control for immediately turning a setting on or off. | ⚪ Draft |
|
|
76
|
+
| [ThemeSwitch](https://seblify.com/components/theme-switch) | A segmented control for selecting system, light, or dark color mode. | ⚪ Draft |
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
</details>
|
|
81
|
+
|
|
82
|
+
## Display Components <small>· 8 items</small>
|
|
83
|
+
|
|
84
|
+
Components for showing content, feedback, media, and motion.
|
|
85
|
+
|
|
86
|
+
<details>
|
|
87
|
+
<summary><strong>Annotation</strong></summary>
|
|
88
|
+
|
|
89
|
+
Small attached indicators, labels, and supporting markers.
|
|
90
|
+
|
|
91
|
+
| Component | Summary | Status |
|
|
92
|
+
| --- | --- | --- |
|
|
93
|
+
| [Badge](https://seblify.com/components/badge) | A small attached indicator for counts, labels, and presence states around another item. | ⚪ Draft |
|
|
94
|
+
| [Tag](https://seblify.com/components/tag) | A compact text marker for statuses, categories, and item labels. | ⚪ Draft |
|
|
95
|
+
| [TagGroup](https://seblify.com/components/tag-group) | A wrapping inline group for tags with the standard tag spacing. | ⚪ Draft |
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
</details>
|
|
100
|
+
|
|
101
|
+
<br />
|
|
102
|
+
|
|
103
|
+
<details>
|
|
104
|
+
<summary><strong>Loading</strong></summary>
|
|
105
|
+
|
|
106
|
+
Progress indicators and waiting states.
|
|
107
|
+
|
|
108
|
+
| Component | Summary | Status |
|
|
109
|
+
| --- | --- | --- |
|
|
110
|
+
| [Loading Dots](https://seblify.com/components/loading-dots) | A compact animated loading indicator for inline waiting states. | ⚪ Draft |
|
|
111
|
+
| [Spinner](https://seblify.com/components/spinner) | A circular loading indicator for indeterminate progress. | ⚪ Draft |
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
</details>
|
|
116
|
+
|
|
117
|
+
<br />
|
|
118
|
+
|
|
119
|
+
<details>
|
|
120
|
+
<summary><strong>Typography</strong></summary>
|
|
121
|
+
|
|
122
|
+
Text primitives and typographic treatments for readable interfaces.
|
|
123
|
+
|
|
124
|
+
| Component | Summary | Status |
|
|
125
|
+
| --- | --- | --- |
|
|
126
|
+
| [Eyebrow](https://seblify.com/components/eyebrow) | A short contextual label that sits above a heading or dense content block. | ⚪ Draft |
|
|
127
|
+
| [Highlight](https://seblify.com/components/highlight) | Highlights case-insensitive text matches inside a plain text string. | ⚪ Draft |
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
</details>
|
|
132
|
+
|
|
133
|
+
<br />
|
|
134
|
+
|
|
135
|
+
<details>
|
|
136
|
+
<summary><strong>Media</strong></summary>
|
|
137
|
+
|
|
138
|
+
Icons, images, video, and other visual media components.
|
|
139
|
+
|
|
140
|
+
| Component | Summary | Status |
|
|
141
|
+
| --- | --- | --- |
|
|
142
|
+
| [Icon](https://seblify.com/components/icon) | A convenient registry-based icon component for Seblify SVG icons. | ⚪ Draft |
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
</details>
|
|
147
|
+
|
|
148
|
+
## Framework Components <small>· 10 items</small>
|
|
149
|
+
|
|
150
|
+
Layout, navigation, overlays, and surface primitives.
|
|
151
|
+
|
|
152
|
+
<details>
|
|
153
|
+
<summary><strong>Align</strong></summary>
|
|
154
|
+
|
|
155
|
+
Small layout primitives for spacing and aligning content.
|
|
156
|
+
|
|
157
|
+
| Component | Summary | Status |
|
|
158
|
+
| --- | --- | --- |
|
|
159
|
+
| [Cluster](https://seblify.com/components/cluster) | A wrapping horizontal layout primitive that spaces items with a configurable gap. | ⚪ Draft |
|
|
160
|
+
| [Grid](https://seblify.com/components/grid) | A responsive grid layout primitive with automatic columns or an explicit column count. | ⚪ Draft |
|
|
161
|
+
| [Row](https://seblify.com/components/row) | A horizontal layout primitive that spaces items with a configurable gap. | ⚪ Draft |
|
|
162
|
+
| [Stack](https://seblify.com/components/stack) | A vertical layout primitive that spaces items with a configurable gap. | ⚪ Draft |
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
</details>
|
|
167
|
+
|
|
168
|
+
<br />
|
|
169
|
+
|
|
170
|
+
<details>
|
|
171
|
+
<summary><strong>Container</strong></summary>
|
|
172
|
+
|
|
173
|
+
Page and region containers that define app structure.
|
|
174
|
+
|
|
175
|
+
| Component | Summary | Status |
|
|
176
|
+
| --- | --- | --- |
|
|
177
|
+
| [AppShell](https://seblify.com/components/app-shell) | A full-page application frame with sidebar, header, and content regions. | ⚪ Draft |
|
|
178
|
+
| [PageHeader](https://seblify.com/components/page-header) | A page-level heading block with optional breadcrumb, eyebrow, summary, and actions. | ⚪ Draft |
|
|
179
|
+
| [Sidebar](https://seblify.com/components/sidebar) | A responsive side container for persistent or overlay navigation. | ⚪ Draft |
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
</details>
|
|
184
|
+
|
|
185
|
+
<br />
|
|
186
|
+
|
|
187
|
+
<details>
|
|
188
|
+
<summary><strong>Navigation</strong></summary>
|
|
189
|
+
|
|
190
|
+
Components that help users understand location and move around.
|
|
191
|
+
|
|
192
|
+
| Component | Summary | Status |
|
|
193
|
+
| --- | --- | --- |
|
|
194
|
+
| [Breadcrumb](https://seblify.com/components/breadcrumb) | A contextual navigation trail for showing page hierarchy. | ⚪ Draft |
|
|
195
|
+
| [Sidenav](https://seblify.com/components/sidenav) | A hierarchical side navigation for sections, links, and nested pages. | ⚪ Draft |
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
</details>
|
|
200
|
+
|
|
201
|
+
<br />
|
|
202
|
+
|
|
203
|
+
<details>
|
|
204
|
+
<summary><strong>Surface</strong></summary>
|
|
205
|
+
|
|
206
|
+
Framing primitives for cards, papers, tiles, and other contained regions.
|
|
207
|
+
|
|
208
|
+
| Component | Summary | Status |
|
|
209
|
+
| --- | --- | --- |
|
|
210
|
+
| [Paper](https://seblify.com/components/paper) | A themed container for grouping content with configurable variant, tone, padding, border, and elevation. | ⚪ Draft |
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
</details>
|
|
215
|
+
|
|
216
|
+
## Design System
|
|
217
|
+
|
|
218
|
+
Theme, tokens, and styling guidance for Seblify.
|
|
219
|
+
|
|
220
|
+
| Guide | Summary |
|
|
221
|
+
| --- | --- |
|
|
222
|
+
| [Component API](https://seblify.com/design-system/component-api) | Shared prop naming rules for variants, tones, size, slots, radius, placement, and contextual inheritance. |
|
|
223
|
+
| [Icons](https://seblify.com/design-system/icons) | Browse all generated Seblify icons, grouped automatically from the SVG source folders. |
|
|
224
|
+
| [Theme](https://seblify.com/design-system/theme) | Seblify themes use CSS variables for light, dark, system, and custom color modes. |
|
|
225
|
+
|
|
226
|
+
## Release Notes <small>· 0.1.8</small>
|
|
227
|
+
|
|
228
|
+
_May 14 2026_ · _1 item updated_
|
|
229
|
+
|
|
230
|
+
Recent changes are summarized below. For the full release history, see the [changelog](https://seblify.com/release-notes).
|
|
231
|
+
|
|
232
|
+
### [other](https://seblify.com/components/other)
|
|
233
|
+
|
|
234
|
+
- Temporary task note for release note workflow
|
|
@@ -1,136 +1,136 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { Snippet } from 'svelte';
|
|
3
|
-
|
|
4
|
-
interface Props {
|
|
5
|
-
banners?: Snippet;
|
|
6
|
-
children?: Snippet;
|
|
7
|
-
drawer?: Snippet;
|
|
8
|
-
snackbars?: Snippet;
|
|
9
|
-
sidebar?: Snippet;
|
|
10
|
-
toasts?: Snippet;
|
|
11
|
-
sidebarWidth?: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
let {
|
|
15
|
-
banners,
|
|
16
|
-
children,
|
|
17
|
-
drawer,
|
|
18
|
-
snackbars,
|
|
19
|
-
sidebar,
|
|
20
|
-
toasts,
|
|
21
|
-
sidebarWidth = '280px',
|
|
22
|
-
}: Props = $props();
|
|
23
|
-
</script>
|
|
24
|
-
|
|
25
|
-
<div class="app-shell" style:--sidebar-width={sidebarWidth}>
|
|
26
|
-
{#if banners}
|
|
27
|
-
<div class="banner-stack" aria-label="Page alerts">
|
|
28
|
-
{@render banners()}
|
|
29
|
-
</div>
|
|
30
|
-
{/if}
|
|
31
|
-
|
|
32
|
-
<div class="app-body">
|
|
33
|
-
{#if sidebar}
|
|
34
|
-
<aside class="sidebar">
|
|
35
|
-
{@render sidebar()}
|
|
36
|
-
</aside>
|
|
37
|
-
{/if}
|
|
38
|
-
|
|
39
|
-
<main class="main-content" tabindex="-1">
|
|
40
|
-
{@render children?.()}
|
|
41
|
-
</main>
|
|
42
|
-
</div>
|
|
43
|
-
|
|
44
|
-
{#if toasts}
|
|
45
|
-
<div class="toast-region" aria-live="polite" aria-label="Confirmations">
|
|
46
|
-
{@render toasts()}
|
|
47
|
-
</div>
|
|
48
|
-
{/if}
|
|
49
|
-
|
|
50
|
-
{#if snackbars}
|
|
51
|
-
<div class="snackbar-region" aria-live="polite" aria-label="Notifications">
|
|
52
|
-
{@render snackbars()}
|
|
53
|
-
</div>
|
|
54
|
-
{/if}
|
|
55
|
-
|
|
56
|
-
{#if drawer}
|
|
57
|
-
<div class="drawer-layer">
|
|
58
|
-
{@render drawer()}
|
|
59
|
-
</div>
|
|
60
|
-
{/if}
|
|
61
|
-
</div>
|
|
62
|
-
|
|
63
|
-
<style>
|
|
64
|
-
.app-shell {
|
|
65
|
-
display: flex;
|
|
66
|
-
flex-direction: column;
|
|
67
|
-
min-height: 100vh;
|
|
68
|
-
background: var(--app-shell-background, var(--seblify-color-page, #f4f6f8));
|
|
69
|
-
color: var(--app-shell-color, var(--seblify-color-text, #172033));
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
.banner-stack {
|
|
73
|
-
position: relative;
|
|
74
|
-
z-index: 100;
|
|
75
|
-
display: grid;
|
|
76
|
-
width: 100%;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
.app-body {
|
|
80
|
-
flex: 1;
|
|
81
|
-
display: grid;
|
|
82
|
-
grid-template-columns: var(--sidebar-width) minmax(0, 1fr);
|
|
83
|
-
min-height: 0;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
.sidebar {
|
|
87
|
-
min-width: 0;
|
|
88
|
-
background: transparent;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
.main-content {
|
|
92
|
-
min-width: 0;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
.toast-region {
|
|
96
|
-
position: fixed;
|
|
97
|
-
top: 16px;
|
|
98
|
-
left: 50%;
|
|
99
|
-
z-index: 650;
|
|
100
|
-
display: grid;
|
|
101
|
-
gap: 8px;
|
|
102
|
-
width: min(420px, calc(100vw - 32px));
|
|
103
|
-
transform: translateX(-50%);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
.snackbar-region {
|
|
107
|
-
position: fixed;
|
|
108
|
-
right: 16px;
|
|
109
|
-
bottom: 16px;
|
|
110
|
-
z-index: 700;
|
|
111
|
-
display: grid;
|
|
112
|
-
gap: 8px;
|
|
113
|
-
width: min(360px, calc(100vw - 32px));
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
.drawer-layer {
|
|
117
|
-
position: fixed;
|
|
118
|
-
inset: 0;
|
|
119
|
-
z-index: 1000;
|
|
120
|
-
pointer-events: none;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
.drawer-layer :global(*) {
|
|
124
|
-
pointer-events: auto;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
@media (max-width: 760px) {
|
|
128
|
-
.app-body {
|
|
129
|
-
grid-template-columns: 1fr;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
.sidebar {
|
|
133
|
-
border-bottom: 0;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
</style>
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
banners?: Snippet;
|
|
6
|
+
children?: Snippet;
|
|
7
|
+
drawer?: Snippet;
|
|
8
|
+
snackbars?: Snippet;
|
|
9
|
+
sidebar?: Snippet;
|
|
10
|
+
toasts?: Snippet;
|
|
11
|
+
sidebarWidth?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let {
|
|
15
|
+
banners,
|
|
16
|
+
children,
|
|
17
|
+
drawer,
|
|
18
|
+
snackbars,
|
|
19
|
+
sidebar,
|
|
20
|
+
toasts,
|
|
21
|
+
sidebarWidth = '280px',
|
|
22
|
+
}: Props = $props();
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<div class="app-shell" style:--sidebar-width={sidebarWidth}>
|
|
26
|
+
{#if banners}
|
|
27
|
+
<div class="banner-stack" aria-label="Page alerts">
|
|
28
|
+
{@render banners()}
|
|
29
|
+
</div>
|
|
30
|
+
{/if}
|
|
31
|
+
|
|
32
|
+
<div class="app-body">
|
|
33
|
+
{#if sidebar}
|
|
34
|
+
<aside class="sidebar">
|
|
35
|
+
{@render sidebar()}
|
|
36
|
+
</aside>
|
|
37
|
+
{/if}
|
|
38
|
+
|
|
39
|
+
<main class="main-content" tabindex="-1">
|
|
40
|
+
{@render children?.()}
|
|
41
|
+
</main>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
{#if toasts}
|
|
45
|
+
<div class="toast-region" aria-live="polite" aria-label="Confirmations">
|
|
46
|
+
{@render toasts()}
|
|
47
|
+
</div>
|
|
48
|
+
{/if}
|
|
49
|
+
|
|
50
|
+
{#if snackbars}
|
|
51
|
+
<div class="snackbar-region" aria-live="polite" aria-label="Notifications">
|
|
52
|
+
{@render snackbars()}
|
|
53
|
+
</div>
|
|
54
|
+
{/if}
|
|
55
|
+
|
|
56
|
+
{#if drawer}
|
|
57
|
+
<div class="drawer-layer">
|
|
58
|
+
{@render drawer()}
|
|
59
|
+
</div>
|
|
60
|
+
{/if}
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<style>
|
|
64
|
+
.app-shell {
|
|
65
|
+
display: flex;
|
|
66
|
+
flex-direction: column;
|
|
67
|
+
min-height: 100vh;
|
|
68
|
+
background: var(--app-shell-background, var(--seblify-color-page, #f4f6f8));
|
|
69
|
+
color: var(--app-shell-color, var(--seblify-color-text, #172033));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.banner-stack {
|
|
73
|
+
position: relative;
|
|
74
|
+
z-index: 100;
|
|
75
|
+
display: grid;
|
|
76
|
+
width: 100%;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.app-body {
|
|
80
|
+
flex: 1;
|
|
81
|
+
display: grid;
|
|
82
|
+
grid-template-columns: var(--sidebar-width) minmax(0, 1fr);
|
|
83
|
+
min-height: 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.sidebar {
|
|
87
|
+
min-width: 0;
|
|
88
|
+
background: transparent;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.main-content {
|
|
92
|
+
min-width: 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.toast-region {
|
|
96
|
+
position: fixed;
|
|
97
|
+
top: 16px;
|
|
98
|
+
left: 50%;
|
|
99
|
+
z-index: 650;
|
|
100
|
+
display: grid;
|
|
101
|
+
gap: 8px;
|
|
102
|
+
width: min(420px, calc(100vw - 32px));
|
|
103
|
+
transform: translateX(-50%);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.snackbar-region {
|
|
107
|
+
position: fixed;
|
|
108
|
+
right: 16px;
|
|
109
|
+
bottom: 16px;
|
|
110
|
+
z-index: 700;
|
|
111
|
+
display: grid;
|
|
112
|
+
gap: 8px;
|
|
113
|
+
width: min(360px, calc(100vw - 32px));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.drawer-layer {
|
|
117
|
+
position: fixed;
|
|
118
|
+
inset: 0;
|
|
119
|
+
z-index: 1000;
|
|
120
|
+
pointer-events: none;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.drawer-layer :global(*) {
|
|
124
|
+
pointer-events: auto;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
@media (max-width: 760px) {
|
|
128
|
+
.app-body {
|
|
129
|
+
grid-template-columns: 1fr;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.sidebar {
|
|
133
|
+
border-bottom: 0;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
</style>
|
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { Snippet } from 'svelte';
|
|
3
|
-
|
|
4
|
-
type BannerTone = 'info' | 'success' | 'warning' | 'error';
|
|
5
|
-
|
|
6
|
-
interface Props {
|
|
7
|
-
children?: Snippet;
|
|
8
|
-
tone?: BannerTone;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
let { children, tone = 'info' }: Props = $props();
|
|
12
|
-
</script>
|
|
13
|
-
|
|
14
|
-
<section
|
|
15
|
-
class="banner"
|
|
16
|
-
class:success={tone === 'success'}
|
|
17
|
-
class:warning={tone === 'warning'}
|
|
18
|
-
class:error={tone === 'error'}
|
|
19
|
-
>
|
|
20
|
-
<div class="content">
|
|
21
|
-
{@render children?.()}
|
|
22
|
-
</div>
|
|
23
|
-
</section>
|
|
24
|
-
|
|
25
|
-
<style>
|
|
26
|
-
.banner {
|
|
27
|
-
width: 100%;
|
|
28
|
-
border-bottom: 1px solid var(--seblify-color-info-border, #bfd1ea);
|
|
29
|
-
background: var(--seblify-color-info-surface, #eaf3ff);
|
|
30
|
-
color: var(--seblify-color-info-text, #102f55);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
.content {
|
|
34
|
-
padding: 10px 16px;
|
|
35
|
-
font-size: 14px;
|
|
36
|
-
line-height: 1.4;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
.success {
|
|
40
|
-
border-bottom-color: var(--seblify-color-success-border, #b8ddc7);
|
|
41
|
-
background: var(--seblify-color-success-surface, #e8f8ee);
|
|
42
|
-
color: var(--seblify-color-success-text, #143d25);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
.warning {
|
|
46
|
-
border-bottom-color: var(--seblify-color-warning-border, #e8d28f);
|
|
47
|
-
background: var(--seblify-color-warning-surface, #fff6d8);
|
|
48
|
-
color: var(--seblify-color-warning-text, #4d3900);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
.error {
|
|
52
|
-
border-bottom-color: var(--seblify-color-error-border, #efb6b6);
|
|
53
|
-
background: var(--seblify-color-error-surface, #fff0f0);
|
|
54
|
-
color: var(--seblify-color-error-text, #5a1717);
|
|
55
|
-
}
|
|
56
|
-
</style>
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
type BannerTone = 'info' | 'success' | 'warning' | 'error';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
children?: Snippet;
|
|
8
|
+
tone?: BannerTone;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let { children, tone = 'info' }: Props = $props();
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<section
|
|
15
|
+
class="banner"
|
|
16
|
+
class:success={tone === 'success'}
|
|
17
|
+
class:warning={tone === 'warning'}
|
|
18
|
+
class:error={tone === 'error'}
|
|
19
|
+
>
|
|
20
|
+
<div class="content">
|
|
21
|
+
{@render children?.()}
|
|
22
|
+
</div>
|
|
23
|
+
</section>
|
|
24
|
+
|
|
25
|
+
<style>
|
|
26
|
+
.banner {
|
|
27
|
+
width: 100%;
|
|
28
|
+
border-bottom: 1px solid var(--seblify-color-info-border, #bfd1ea);
|
|
29
|
+
background: var(--seblify-color-info-surface, #eaf3ff);
|
|
30
|
+
color: var(--seblify-color-info-text, #102f55);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.content {
|
|
34
|
+
padding: 10px 16px;
|
|
35
|
+
font-size: 14px;
|
|
36
|
+
line-height: 1.4;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.success {
|
|
40
|
+
border-bottom-color: var(--seblify-color-success-border, #b8ddc7);
|
|
41
|
+
background: var(--seblify-color-success-surface, #e8f8ee);
|
|
42
|
+
color: var(--seblify-color-success-text, #143d25);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.warning {
|
|
46
|
+
border-bottom-color: var(--seblify-color-warning-border, #e8d28f);
|
|
47
|
+
background: var(--seblify-color-warning-surface, #fff6d8);
|
|
48
|
+
color: var(--seblify-color-warning-text, #4d3900);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.error {
|
|
52
|
+
border-bottom-color: var(--seblify-color-error-border, #efb6b6);
|
|
53
|
+
background: var(--seblify-color-error-surface, #fff0f0);
|
|
54
|
+
color: var(--seblify-color-error-text, #5a1717);
|
|
55
|
+
}
|
|
56
|
+
</style>
|
|
@@ -1,64 +1,64 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { Snippet } from 'svelte';
|
|
3
|
-
|
|
4
|
-
type ButtonVariant = 'primary' | 'secondary';
|
|
5
|
-
type ButtonType = 'button' | 'submit' | 'reset';
|
|
6
|
-
|
|
7
|
-
interface Props {
|
|
8
|
-
children?: Snippet;
|
|
9
|
-
disabled?: boolean;
|
|
10
|
-
onclick?: (event: MouseEvent) => void;
|
|
11
|
-
type?: ButtonType;
|
|
12
|
-
variant?: ButtonVariant;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
let {
|
|
16
|
-
children,
|
|
17
|
-
disabled = false,
|
|
18
|
-
onclick,
|
|
19
|
-
type = 'button',
|
|
20
|
-
variant = 'primary',
|
|
21
|
-
}: Props = $props();
|
|
22
|
-
</script>
|
|
23
|
-
|
|
24
|
-
<button {disabled} {onclick} {type} class:secondary={variant === 'secondary'}>
|
|
25
|
-
{@render children?.()}
|
|
26
|
-
</button>
|
|
27
|
-
|
|
28
|
-
<style>
|
|
29
|
-
button {
|
|
30
|
-
min-height: 36px;
|
|
31
|
-
border: 1px solid var(--seblify-color-fill-accent, #146a6f);
|
|
32
|
-
border-radius: 6px;
|
|
33
|
-
padding: 0 14px;
|
|
34
|
-
background: var(--seblify-color-fill-accent, #146a6f);
|
|
35
|
-
color: var(--seblify-color-fill-accent-text, #ffffff);
|
|
36
|
-
font: inherit;
|
|
37
|
-
font-weight: 600;
|
|
38
|
-
cursor: pointer;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
button:hover:not(:disabled) {
|
|
42
|
-
background: var(--seblify-color-fill-accent-hover, #0d4d52);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
button:focus-visible {
|
|
46
|
-
outline: 3px solid var(--seblify-color-focus-ring, #9bd4d8);
|
|
47
|
-
outline-offset: 2px;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
button:disabled {
|
|
51
|
-
cursor: not-allowed;
|
|
52
|
-
opacity: var(--seblify-opacity-disabled, 0.56);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.secondary {
|
|
56
|
-
border-color: var(--seblify-color-border, #c8d2df);
|
|
57
|
-
background: var(--seblify-color-surface, #ffffff);
|
|
58
|
-
color: var(--seblify-color-text, #172033);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
.secondary:hover:not(:disabled) {
|
|
62
|
-
background: var(--seblify-color-surface-hover, #f1f4f9);
|
|
63
|
-
}
|
|
64
|
-
</style>
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
type ButtonVariant = 'primary' | 'secondary';
|
|
5
|
+
type ButtonType = 'button' | 'submit' | 'reset';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
children?: Snippet;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
onclick?: (event: MouseEvent) => void;
|
|
11
|
+
type?: ButtonType;
|
|
12
|
+
variant?: ButtonVariant;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
children,
|
|
17
|
+
disabled = false,
|
|
18
|
+
onclick,
|
|
19
|
+
type = 'button',
|
|
20
|
+
variant = 'primary',
|
|
21
|
+
}: Props = $props();
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<button {disabled} {onclick} {type} class:secondary={variant === 'secondary'}>
|
|
25
|
+
{@render children?.()}
|
|
26
|
+
</button>
|
|
27
|
+
|
|
28
|
+
<style>
|
|
29
|
+
button {
|
|
30
|
+
min-height: 36px;
|
|
31
|
+
border: 1px solid var(--seblify-color-fill-accent, #146a6f);
|
|
32
|
+
border-radius: 6px;
|
|
33
|
+
padding: 0 14px;
|
|
34
|
+
background: var(--seblify-color-fill-accent, #146a6f);
|
|
35
|
+
color: var(--seblify-color-fill-accent-text, #ffffff);
|
|
36
|
+
font: inherit;
|
|
37
|
+
font-weight: 600;
|
|
38
|
+
cursor: pointer;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
button:hover:not(:disabled) {
|
|
42
|
+
background: var(--seblify-color-fill-accent-hover, #0d4d52);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
button:focus-visible {
|
|
46
|
+
outline: 3px solid var(--seblify-color-focus-ring, #9bd4d8);
|
|
47
|
+
outline-offset: 2px;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
button:disabled {
|
|
51
|
+
cursor: not-allowed;
|
|
52
|
+
opacity: var(--seblify-opacity-disabled, 0.56);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.secondary {
|
|
56
|
+
border-color: var(--seblify-color-border, #c8d2df);
|
|
57
|
+
background: var(--seblify-color-surface, #ffffff);
|
|
58
|
+
color: var(--seblify-color-text, #172033);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.secondary:hover:not(:disabled) {
|
|
62
|
+
background: var(--seblify-color-surface-hover, #f1f4f9);
|
|
63
|
+
}
|
|
64
|
+
</style>
|
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
const iconModules = import.meta.glob("../icons/**/*.svg", {
|
|
3
|
-
eager: true,
|
|
4
|
-
import: "default"
|
|
5
|
-
});
|
|
6
|
-
|
|
7
|
-
interface Props {
|
|
8
|
-
alt?: string;
|
|
9
|
-
name?: string;
|
|
10
|
-
size?: number;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
let { alt = "", name = "", size = 24 }: Props = $props();
|
|
14
|
-
|
|
15
|
-
function normalizeNameToPath(value: unknown): string | null {
|
|
16
|
-
const safe = typeof value === "string" ? value.trim() : "";
|
|
17
|
-
|
|
18
|
-
if (safe.length === 0) {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const parts = safe.split("-").filter((part) => part.length > 0);
|
|
23
|
-
|
|
24
|
-
if (parts.length < 2) {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const folder = parts[0];
|
|
29
|
-
const file = parts.slice(1).join("-");
|
|
30
|
-
|
|
31
|
-
return `../icons/${folder}/${file}.svg`;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
let groupedPath = $derived(normalizeNameToPath(name));
|
|
35
|
-
let iconPath = $derived(
|
|
36
|
-
(groupedPath != null ? iconModules[groupedPath] : undefined) ??
|
|
37
|
-
iconModules[`../icons/${name}.svg`] ??
|
|
38
|
-
""
|
|
39
|
-
);
|
|
40
|
-
</script>
|
|
41
|
-
|
|
42
|
-
{#if iconPath}
|
|
43
|
-
<span
|
|
44
|
-
role={alt ? "img" : undefined}
|
|
45
|
-
aria-label={alt || undefined}
|
|
46
|
-
aria-hidden={alt ? undefined : "true"}
|
|
47
|
-
style:--icon-url={`url('${iconPath}')`}
|
|
48
|
-
style:width={`${size / 16}rem`}
|
|
49
|
-
style:height={`${size / 16}rem`}
|
|
50
|
-
></span>
|
|
51
|
-
{/if}
|
|
52
|
-
|
|
53
|
-
<style>
|
|
54
|
-
span {
|
|
55
|
-
display: inline-block;
|
|
56
|
-
flex: 0 0 auto;
|
|
57
|
-
background-color: currentColor;
|
|
58
|
-
mask-image: var(--icon-url);
|
|
59
|
-
mask-position: center;
|
|
60
|
-
mask-repeat: no-repeat;
|
|
61
|
-
mask-size: contain;
|
|
62
|
-
-webkit-mask-image: var(--icon-url);
|
|
63
|
-
-webkit-mask-position: center;
|
|
64
|
-
-webkit-mask-repeat: no-repeat;
|
|
65
|
-
-webkit-mask-size: contain;
|
|
66
|
-
}
|
|
67
|
-
</style>
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
const iconModules = import.meta.glob("../icons/**/*.svg", {
|
|
3
|
+
eager: true,
|
|
4
|
+
import: "default"
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
alt?: string;
|
|
9
|
+
name?: string;
|
|
10
|
+
size?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let { alt = "", name = "", size = 24 }: Props = $props();
|
|
14
|
+
|
|
15
|
+
function normalizeNameToPath(value: unknown): string | null {
|
|
16
|
+
const safe = typeof value === "string" ? value.trim() : "";
|
|
17
|
+
|
|
18
|
+
if (safe.length === 0) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const parts = safe.split("-").filter((part) => part.length > 0);
|
|
23
|
+
|
|
24
|
+
if (parts.length < 2) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const folder = parts[0];
|
|
29
|
+
const file = parts.slice(1).join("-");
|
|
30
|
+
|
|
31
|
+
return `../icons/${folder}/${file}.svg`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let groupedPath = $derived(normalizeNameToPath(name));
|
|
35
|
+
let iconPath = $derived(
|
|
36
|
+
(groupedPath != null ? iconModules[groupedPath] : undefined) ??
|
|
37
|
+
iconModules[`../icons/${name}.svg`] ??
|
|
38
|
+
""
|
|
39
|
+
);
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
{#if iconPath}
|
|
43
|
+
<span
|
|
44
|
+
role={alt ? "img" : undefined}
|
|
45
|
+
aria-label={alt || undefined}
|
|
46
|
+
aria-hidden={alt ? undefined : "true"}
|
|
47
|
+
style:--icon-url={`url('${iconPath}')`}
|
|
48
|
+
style:width={`${size / 16}rem`}
|
|
49
|
+
style:height={`${size / 16}rem`}
|
|
50
|
+
></span>
|
|
51
|
+
{/if}
|
|
52
|
+
|
|
53
|
+
<style>
|
|
54
|
+
span {
|
|
55
|
+
display: inline-block;
|
|
56
|
+
flex: 0 0 auto;
|
|
57
|
+
background-color: currentColor;
|
|
58
|
+
mask-image: var(--icon-url);
|
|
59
|
+
mask-position: center;
|
|
60
|
+
mask-repeat: no-repeat;
|
|
61
|
+
mask-size: contain;
|
|
62
|
+
-webkit-mask-image: var(--icon-url);
|
|
63
|
+
-webkit-mask-position: center;
|
|
64
|
+
-webkit-mask-repeat: no-repeat;
|
|
65
|
+
-webkit-mask-size: contain;
|
|
66
|
+
}
|
|
67
|
+
</style>
|
|
@@ -1,106 +1,106 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { Snippet } from 'svelte';
|
|
3
|
-
|
|
4
|
-
interface SidebarNavItem {
|
|
5
|
-
active?: boolean;
|
|
6
|
-
href?: string;
|
|
7
|
-
label: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
interface Props {
|
|
11
|
-
bottomItems?: SidebarNavItem[];
|
|
12
|
-
logo?: Snippet;
|
|
13
|
-
topItems?: SidebarNavItem[];
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
let { bottomItems = [], logo, topItems = [] }: Props = $props();
|
|
17
|
-
</script>
|
|
18
|
-
|
|
19
|
-
<aside class="sidebar">
|
|
20
|
-
{#if logo}
|
|
21
|
-
<div class="brand">
|
|
22
|
-
{@render logo()}
|
|
23
|
-
</div>
|
|
24
|
-
{/if}
|
|
25
|
-
|
|
26
|
-
<nav class="nav-section top" aria-label="Primary navigation">
|
|
27
|
-
{#each topItems as item}
|
|
28
|
-
<a
|
|
29
|
-
class="nav-item"
|
|
30
|
-
class:active={item.active}
|
|
31
|
-
href={item.href ?? '#'}
|
|
32
|
-
aria-current={item.active ? 'page' : undefined}
|
|
33
|
-
>
|
|
34
|
-
{item.label}
|
|
35
|
-
</a>
|
|
36
|
-
{/each}
|
|
37
|
-
</nav>
|
|
38
|
-
|
|
39
|
-
{#if bottomItems.length > 0}
|
|
40
|
-
<nav class="nav-section bottom" aria-label="Secondary navigation">
|
|
41
|
-
{#each bottomItems as item}
|
|
42
|
-
<a
|
|
43
|
-
class="nav-item"
|
|
44
|
-
class:active={item.active}
|
|
45
|
-
href={item.href ?? '#'}
|
|
46
|
-
aria-current={item.active ? 'page' : undefined}
|
|
47
|
-
>
|
|
48
|
-
{item.label}
|
|
49
|
-
</a>
|
|
50
|
-
{/each}
|
|
51
|
-
</nav>
|
|
52
|
-
{/if}
|
|
53
|
-
</aside>
|
|
54
|
-
|
|
55
|
-
<style>
|
|
56
|
-
.sidebar {
|
|
57
|
-
display: flex;
|
|
58
|
-
flex-direction: column;
|
|
59
|
-
width: 16.5rem;
|
|
60
|
-
max-width: 100%;
|
|
61
|
-
min-height: 100%;
|
|
62
|
-
background: var(--seblify-color-surface-inverse, #172033);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
.brand {
|
|
66
|
-
padding: 1.5rem 1rem;
|
|
67
|
-
color: var(--seblify-color-text-inverse, #ffffff);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
.nav-section {
|
|
71
|
-
display: grid;
|
|
72
|
-
gap: 0.25rem;
|
|
73
|
-
margin: 0 1rem;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
.top {
|
|
77
|
-
align-content: start;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
.bottom {
|
|
81
|
-
margin-top: auto;
|
|
82
|
-
padding-bottom: 1rem;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
.nav-item {
|
|
86
|
-
display: block;
|
|
87
|
-
width: 100%;
|
|
88
|
-
border-radius: 0.375rem;
|
|
89
|
-
padding: 0.75rem 1rem;
|
|
90
|
-
color: var(--seblify-color-text-inverse, #ffffff);
|
|
91
|
-
font-size: 0.875rem;
|
|
92
|
-
line-height: 1.25rem;
|
|
93
|
-
text-decoration: none;
|
|
94
|
-
box-sizing: border-box;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
.nav-item:hover {
|
|
98
|
-
background: var(--seblify-color-surface-inverse-hover, #263043);
|
|
99
|
-
color: var(--seblify-color-text-inverse, #ffffff);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
.active {
|
|
103
|
-
background: var(--seblify-color-surface-inverse-hover, #263043);
|
|
104
|
-
color: var(--seblify-color-text-inverse, #ffffff);
|
|
105
|
-
}
|
|
106
|
-
</style>
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
interface SidebarNavItem {
|
|
5
|
+
active?: boolean;
|
|
6
|
+
href?: string;
|
|
7
|
+
label: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
bottomItems?: SidebarNavItem[];
|
|
12
|
+
logo?: Snippet;
|
|
13
|
+
topItems?: SidebarNavItem[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let { bottomItems = [], logo, topItems = [] }: Props = $props();
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<aside class="sidebar">
|
|
20
|
+
{#if logo}
|
|
21
|
+
<div class="brand">
|
|
22
|
+
{@render logo()}
|
|
23
|
+
</div>
|
|
24
|
+
{/if}
|
|
25
|
+
|
|
26
|
+
<nav class="nav-section top" aria-label="Primary navigation">
|
|
27
|
+
{#each topItems as item}
|
|
28
|
+
<a
|
|
29
|
+
class="nav-item"
|
|
30
|
+
class:active={item.active}
|
|
31
|
+
href={item.href ?? '#'}
|
|
32
|
+
aria-current={item.active ? 'page' : undefined}
|
|
33
|
+
>
|
|
34
|
+
{item.label}
|
|
35
|
+
</a>
|
|
36
|
+
{/each}
|
|
37
|
+
</nav>
|
|
38
|
+
|
|
39
|
+
{#if bottomItems.length > 0}
|
|
40
|
+
<nav class="nav-section bottom" aria-label="Secondary navigation">
|
|
41
|
+
{#each bottomItems as item}
|
|
42
|
+
<a
|
|
43
|
+
class="nav-item"
|
|
44
|
+
class:active={item.active}
|
|
45
|
+
href={item.href ?? '#'}
|
|
46
|
+
aria-current={item.active ? 'page' : undefined}
|
|
47
|
+
>
|
|
48
|
+
{item.label}
|
|
49
|
+
</a>
|
|
50
|
+
{/each}
|
|
51
|
+
</nav>
|
|
52
|
+
{/if}
|
|
53
|
+
</aside>
|
|
54
|
+
|
|
55
|
+
<style>
|
|
56
|
+
.sidebar {
|
|
57
|
+
display: flex;
|
|
58
|
+
flex-direction: column;
|
|
59
|
+
width: 16.5rem;
|
|
60
|
+
max-width: 100%;
|
|
61
|
+
min-height: 100%;
|
|
62
|
+
background: var(--seblify-color-surface-inverse, #172033);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.brand {
|
|
66
|
+
padding: 1.5rem 1rem;
|
|
67
|
+
color: var(--seblify-color-text-inverse, #ffffff);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.nav-section {
|
|
71
|
+
display: grid;
|
|
72
|
+
gap: 0.25rem;
|
|
73
|
+
margin: 0 1rem;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.top {
|
|
77
|
+
align-content: start;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.bottom {
|
|
81
|
+
margin-top: auto;
|
|
82
|
+
padding-bottom: 1rem;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.nav-item {
|
|
86
|
+
display: block;
|
|
87
|
+
width: 100%;
|
|
88
|
+
border-radius: 0.375rem;
|
|
89
|
+
padding: 0.75rem 1rem;
|
|
90
|
+
color: var(--seblify-color-text-inverse, #ffffff);
|
|
91
|
+
font-size: 0.875rem;
|
|
92
|
+
line-height: 1.25rem;
|
|
93
|
+
text-decoration: none;
|
|
94
|
+
box-sizing: border-box;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.nav-item:hover {
|
|
98
|
+
background: var(--seblify-color-surface-inverse-hover, #263043);
|
|
99
|
+
color: var(--seblify-color-text-inverse, #ffffff);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.active {
|
|
103
|
+
background: var(--seblify-color-surface-inverse-hover, #263043);
|
|
104
|
+
color: var(--seblify-color-text-inverse, #ffffff);
|
|
105
|
+
}
|
|
106
|
+
</style>
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { Snippet } from 'svelte';
|
|
3
|
-
|
|
4
|
-
interface Props {
|
|
5
|
-
children?: Snippet;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
let { children }: Props = $props();
|
|
9
|
-
</script>
|
|
10
|
-
|
|
11
|
-
<section class="snackbar" role="status">
|
|
12
|
-
{@render children?.()}
|
|
13
|
-
</section>
|
|
14
|
-
|
|
15
|
-
<style>
|
|
16
|
-
.snackbar {
|
|
17
|
-
border: 1px solid var(--seblify-color-border-inverse, #263043);
|
|
18
|
-
border-radius: 6px;
|
|
19
|
-
padding: 12px 14px;
|
|
20
|
-
background: var(--seblify-color-surface-inverse, #172033);
|
|
21
|
-
color: var(--seblify-color-text-inverse, #ffffff);
|
|
22
|
-
box-shadow: var(--seblify-shadow-raised, 0 12px 28px rgb(23 32 51 / 22%));
|
|
23
|
-
font-size: 14px;
|
|
24
|
-
line-height: 1.4;
|
|
25
|
-
}
|
|
26
|
-
</style>
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
children?: Snippet;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
let { children }: Props = $props();
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<section class="snackbar" role="status">
|
|
12
|
+
{@render children?.()}
|
|
13
|
+
</section>
|
|
14
|
+
|
|
15
|
+
<style>
|
|
16
|
+
.snackbar {
|
|
17
|
+
border: 1px solid var(--seblify-color-border-inverse, #263043);
|
|
18
|
+
border-radius: 6px;
|
|
19
|
+
padding: 12px 14px;
|
|
20
|
+
background: var(--seblify-color-surface-inverse, #172033);
|
|
21
|
+
color: var(--seblify-color-text-inverse, #ffffff);
|
|
22
|
+
box-shadow: var(--seblify-shadow-raised, 0 12px 28px rgb(23 32 51 / 22%));
|
|
23
|
+
font-size: 14px;
|
|
24
|
+
line-height: 1.4;
|
|
25
|
+
}
|
|
26
|
+
</style>
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { Snippet } from 'svelte';
|
|
3
|
-
|
|
4
|
-
interface Props {
|
|
5
|
-
children?: Snippet;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
let { children }: Props = $props();
|
|
9
|
-
</script>
|
|
10
|
-
|
|
11
|
-
<section class="toast" role="status">
|
|
12
|
-
{@render children?.()}
|
|
13
|
-
</section>
|
|
14
|
-
|
|
15
|
-
<style>
|
|
16
|
-
.toast {
|
|
17
|
-
border: 1px solid var(--seblify-color-success-border, #b8ddc7);
|
|
18
|
-
border-radius: 999px;
|
|
19
|
-
padding: 8px 14px;
|
|
20
|
-
background: var(--seblify-color-success-surface, #e8f8ee);
|
|
21
|
-
color: var(--seblify-color-success-text, #143d25);
|
|
22
|
-
box-shadow: var(--seblify-shadow-status, 0 10px 24px rgb(20 61 37 / 14%));
|
|
23
|
-
font-size: 14px;
|
|
24
|
-
line-height: 1.4;
|
|
25
|
-
text-align: center;
|
|
26
|
-
}
|
|
27
|
-
</style>
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
children?: Snippet;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
let { children }: Props = $props();
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<section class="toast" role="status">
|
|
12
|
+
{@render children?.()}
|
|
13
|
+
</section>
|
|
14
|
+
|
|
15
|
+
<style>
|
|
16
|
+
.toast {
|
|
17
|
+
border: 1px solid var(--seblify-color-success-border, #b8ddc7);
|
|
18
|
+
border-radius: 999px;
|
|
19
|
+
padding: 8px 14px;
|
|
20
|
+
background: var(--seblify-color-success-surface, #e8f8ee);
|
|
21
|
+
color: var(--seblify-color-success-text, #143d25);
|
|
22
|
+
box-shadow: var(--seblify-shadow-status, 0 10px 24px rgb(20 61 37 / 14%));
|
|
23
|
+
font-size: 14px;
|
|
24
|
+
line-height: 1.4;
|
|
25
|
+
text-align: center;
|
|
26
|
+
}
|
|
27
|
+
</style>
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "seblify",
|
|
3
3
|
"private": false,
|
|
4
|
-
"
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"version": "0.1.8",
|
|
5
6
|
"type": "module",
|
|
6
7
|
"scripts": {
|
|
7
8
|
"build": "svelte-package --input src --output dist --tsconfig ./tsconfig.json",
|
|
8
9
|
"generate:icons": "node ./scripts/generate-icons.mjs",
|
|
9
|
-
"
|
|
10
|
+
"doc": "node ./scripts/change-log/document-change.mjs",
|
|
10
11
|
"change-log:validate": "node ./scripts/change-log/validate-pending.mjs",
|
|
11
|
-
"
|
|
12
|
+
"readme:generate": "node ./scripts/readme/generate-readme.mjs",
|
|
13
|
+
"prepare-release": "node ./scripts/prepare-release.mjs",
|
|
12
14
|
"prepublishOnly": "pnpm run generate:icons && pnpm run build"
|
|
13
15
|
},
|
|
14
16
|
"svelte": "./dist/index.js",
|
|
@@ -58,9 +60,26 @@
|
|
|
58
60
|
"prompts": "^2.4.2",
|
|
59
61
|
"yaml": "^2.9.0"
|
|
60
62
|
},
|
|
61
|
-
"homepage": "https://seblify.com",
|
|
62
63
|
"publishConfig": {
|
|
63
64
|
"access": "public",
|
|
64
65
|
"provenance": false
|
|
66
|
+
},
|
|
67
|
+
"description": "Svelte UI component library for building themed interfaces",
|
|
68
|
+
"keywords": [
|
|
69
|
+
"svelte",
|
|
70
|
+
"sveltekit",
|
|
71
|
+
"ui",
|
|
72
|
+
"component",
|
|
73
|
+
"component-library",
|
|
74
|
+
"design-system",
|
|
75
|
+
"theme",
|
|
76
|
+
"theming",
|
|
77
|
+
"css-variables",
|
|
78
|
+
"accessibility",
|
|
79
|
+
"custom-theme"
|
|
80
|
+
],
|
|
81
|
+
"homepage": "https://seblify.com",
|
|
82
|
+
"bugs": {
|
|
83
|
+
"email": "support.seblify@gmail.com"
|
|
65
84
|
}
|
|
66
85
|
}
|