@svgrid/create 1.0.2 → 1.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svgrid/create",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "Scaffold a Svelte app powered by SvGrid in one command: npm create @svgrid@latest",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -22,6 +22,6 @@
22
22
  "vite": "^8.0.10"
23
23
  },
24
24
  "dependencies": {
25
- "@svgrid/grid": "^1.0.0"
25
+ "@svgrid/grid": "^1.0.2"
26
26
  }
27
27
  }
@@ -1,18 +1,176 @@
1
1
  @import 'tailwindcss';
2
2
 
3
- /* SvGrid ships its own scoped styles. These custom properties let you theme
4
- the grid to match the dashboard shell. Tweak freely. */
3
+ /* Tailwind's `dark:` modifier follows the `data-theme` attribute the layout
4
+ * writes onto <html>, so utility classes and the grid's own --sg-* tokens
5
+ * switch together. */
6
+ @custom-variant dark (&:where(html[data-theme='dark'], html[data-theme='dark'] *));
7
+
8
+ /* ---------------------------------------------------------------------------
9
+ * SvGrid theme tokens. These are the SAME tokens the svgrid.com demos use, so
10
+ * the grid looks identical here out of the box. The dashboard shell reads them
11
+ * through the --app-* aliases below, so the whole app themes as one. Edit any
12
+ * value to rebrand.
13
+ * ------------------------------------------------------------------------- */
5
14
  :root {
6
- --app-bg: #f8fafc;
7
- --app-panel: #ffffff;
8
- --app-border: #e2e8f0;
9
- --app-fg: #0f172a;
10
- --app-muted: #64748b;
11
- --app-accent: #4f46e5;
15
+ --sg-bg: #ffffff;
16
+ --sg-fg: #0f172a;
17
+ --sg-muted: #64748b;
18
+ --sg-border: #e2e8f0;
19
+ --sg-header-bg: #f1f5f9;
20
+ --sg-header-fg: #0f172a;
21
+ --sg-row-alt-bg: #f8fafc;
22
+ --sg-row-hover-bg: #eef2ff;
23
+ --sg-row-height: 36px;
24
+ --sg-focus-ring: 0 0 0 2px #2563eb;
25
+ --sg-selection-bg: #dbeafe;
26
+ --sg-accent: #2563eb;
27
+ --sg-pill-active: #dcfce7;
28
+ --sg-pill-active-fg: #166534;
29
+ --sg-pill-pending: #fef9c3;
30
+ --sg-pill-pending-fg: #854d0e;
31
+ --sg-pill-inactive: #fee2e2;
32
+ --sg-pill-inactive-fg: #991b1b;
33
+ --sg-input-bg: #ffffff;
34
+ --sg-input-border: #cbd5e1;
35
+
36
+ /* Dashboard shell aliases - map the chrome to the grid tokens. */
37
+ --app-bg: var(--sg-row-alt-bg);
38
+ --app-panel: var(--sg-bg);
39
+ --app-border: var(--sg-border);
40
+ --app-fg: var(--sg-fg);
41
+ --app-muted: var(--sg-muted);
42
+ --app-accent: var(--sg-accent);
43
+ }
44
+
45
+ html[data-theme='dark'] {
46
+ --sg-bg: #181d27;
47
+ --sg-fg: #e2e8f0;
48
+ --sg-muted: #94a3b8;
49
+ --sg-border: #374151;
50
+ --sg-header-bg: #1e2433;
51
+ --sg-header-fg: #e2e8f0;
52
+ --sg-row-alt-bg: #1b2230;
53
+ --sg-row-hover-bg: #232b3c;
54
+ --sg-selection-bg: #1d3a73;
55
+ --sg-pill-active: #064e3b;
56
+ --sg-pill-active-fg: #6ee7b7;
57
+ --sg-pill-pending: #713f12;
58
+ --sg-pill-pending-fg: #fde68a;
59
+ --sg-pill-inactive: #7f1d1d;
60
+ --sg-pill-inactive-fg: #fecaca;
61
+ --sg-input-bg: #1a2130;
62
+ --sg-input-border: #2c3548;
63
+ --sg-accent: #3b82f6;
64
+ --sg-scrollbar-bg: #1a2130;
65
+ --sg-scrollbar-border: rgba(148, 163, 184, 0.08);
66
+ --sg-scrollbar-arrow: #64748b;
67
+ --sg-scrollbar-arrow-hover: #e2e8f0;
68
+ --sg-scrollbar-arrow-hover-bg: #2a3142;
69
+ --sg-scrollbar-arrow-active: #f1f5f9;
70
+ --sg-scrollbar-arrow-active-bg: #374151;
71
+ --sg-scrollbar-arrow-disabled: #475569;
72
+ --sg-scrollbar-thumb: #475569;
73
+ --sg-scrollbar-thumb-hover: #64748b;
74
+ --sg-scrollbar-thumb-active: #94a3b8;
75
+ /* The shell panels sit a touch above the page in dark mode. */
76
+ --app-panel: #1e2433;
77
+ color-scheme: dark;
78
+ }
79
+
80
+ html[data-theme='light'] {
81
+ color-scheme: light;
12
82
  }
13
83
 
14
84
  html,
15
85
  body {
16
86
  background: var(--app-bg);
17
87
  color: var(--app-fg);
88
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial,
89
+ sans-serif;
90
+ }
91
+
92
+ /* ---------------------------------------------------------------------------
93
+ * Optional polish. The grid themes itself from the --sg-* tokens above, so it
94
+ * already follows light / dark with no extra CSS. These rules only add table
95
+ * niceties the grid leaves to the app: cell gridlines, zebra striping, row
96
+ * hover, and a styled pagination bar. Delete any you don't want.
97
+ * ------------------------------------------------------------------------- */
98
+
99
+ /* Cell gridlines (the grid draws none by default). */
100
+ .sv-grid-column,
101
+ .sv-grid-cell {
102
+ border-right: 1px solid var(--sg-border) !important;
103
+ border-bottom: 1px solid var(--sg-border) !important;
104
+ }
105
+ .sv-grid-table tr > :last-child.sv-grid-column,
106
+ .sv-grid-table tr > :last-child.sv-grid-cell {
107
+ border-right: 0 !important;
108
+ }
109
+
110
+ /* Zebra striping + row hover. */
111
+ .sv-grid-table tbody tr:nth-child(even) .sv-grid-cell {
112
+ background: var(--sg-row-alt-bg) !important;
113
+ }
114
+ .sv-grid-table tbody tr:hover .sv-grid-cell {
115
+ background: var(--sg-row-hover-bg) !important;
116
+ }
117
+ /* Selection wins over zebra (declared after, same specificity). */
118
+ .sv-grid-table tbody tr.sv-grid-row-selected .sv-grid-cell,
119
+ .sv-grid-table tbody tr .sv-grid-cell[data-selected-range='true'] {
120
+ background: var(--sg-selection-bg) !important;
121
+ }
122
+
123
+ /* Pagination bar: layout + theme (the grid ships the markup, not the styling). */
124
+ .sv-grid-pagination {
125
+ display: flex !important;
126
+ align-items: center !important;
127
+ justify-content: flex-end !important;
128
+ gap: 24px !important;
129
+ padding: 12px 16px !important;
130
+ border: 1px solid var(--sg-border) !important;
131
+ border-top: 0 !important;
132
+ border-radius: 0 0 6px 6px !important;
133
+ background: var(--sg-header-bg) !important;
134
+ color: var(--sg-fg) !important;
135
+ font-size: 13px !important;
136
+ }
137
+ .sv-grid-pagination-pagesize {
138
+ display: inline-flex;
139
+ align-items: center;
140
+ gap: 8px;
141
+ color: var(--sg-muted);
142
+ }
143
+ .sv-grid-pagination-pagesize select,
144
+ .sv-grid-pagination-btn {
145
+ border: 1px solid var(--sg-input-border);
146
+ background: var(--sg-input-bg);
147
+ color: var(--sg-fg);
148
+ border-radius: 5px;
149
+ height: 28px;
150
+ font: inherit;
151
+ font-size: 13px;
152
+ cursor: pointer;
153
+ }
154
+ .sv-grid-pagination-btn {
155
+ display: inline-flex !important;
156
+ align-items: center !important;
157
+ justify-content: center !important;
158
+ width: 28px !important;
159
+ border: 0 !important;
160
+ background: transparent !important;
161
+ font-size: 16px !important;
162
+ }
163
+ .sv-grid-pagination-btn:hover:not(:disabled) {
164
+ background: var(--sg-input-bg) !important;
165
+ color: var(--sg-accent) !important;
166
+ }
167
+ .sv-grid-pagination-btn:disabled {
168
+ color: var(--sg-muted) !important;
169
+ opacity: 0.4 !important;
170
+ cursor: default !important;
171
+ }
172
+ .sv-grid-pagination-nav {
173
+ display: inline-flex;
174
+ align-items: center;
175
+ gap: 4px;
18
176
  }
@@ -1,5 +1,5 @@
1
1
  <!doctype html>
2
- <html lang="en" class="h-full">
2
+ <html lang="en" class="h-full" data-theme="dark">
3
3
  <head>
4
4
  <meta charset="utf-8" />
5
5
  <link rel="icon" href="%sveltekit.assets%/favicon.svg" />
@@ -8,6 +8,14 @@
8
8
  name="description"
9
9
  content="Admin dashboard starter built with SvelteKit and SvGrid - the Svelte 5 data grid."
10
10
  />
11
+ <!-- Apply the saved theme before first paint (defaults to dark) so there's
12
+ no light-mode flash. The layout keeps this in sync afterwards. -->
13
+ <script>
14
+ try {
15
+ var t = localStorage.getItem('theme')
16
+ document.documentElement.setAttribute('data-theme', t === 'light' ? 'light' : 'dark')
17
+ } catch (e) {}
18
+ </script>
11
19
  %sveltekit.head%
12
20
  </head>
13
21
  <body data-sveltekit-preload-data="hover" class="h-full">
@@ -7,8 +7,8 @@
7
7
  </script>
8
8
 
9
9
  <div
10
- class="rounded-xl border bg-white p-4 shadow-sm"
11
- style="border-color: var(--app-border);"
10
+ class="rounded-xl border p-4 shadow-sm"
11
+ style="border-color: var(--app-border); background: var(--app-panel);"
12
12
  >
13
13
  <p class="text-xs font-medium uppercase tracking-wide" style="color: var(--app-muted);">
14
14
  {label}
@@ -0,0 +1,20 @@
1
+ import { browser } from '$app/environment'
2
+
3
+ export type Theme = 'light' | 'dark'
4
+
5
+ /** Read the persisted theme. Defaults to dark (matches app.html). */
6
+ export function readTheme(): Theme {
7
+ if (!browser) return 'dark'
8
+ return localStorage.getItem('theme') === 'light' ? 'light' : 'dark'
9
+ }
10
+
11
+ /** Write the theme to <html data-theme> and persist it. */
12
+ export function applyTheme(theme: Theme): void {
13
+ if (!browser) return
14
+ document.documentElement.setAttribute('data-theme', theme)
15
+ try {
16
+ localStorage.setItem('theme', theme)
17
+ } catch {
18
+ /* storage may be unavailable (private mode) - ignore */
19
+ }
20
+ }
@@ -1,9 +1,22 @@
1
1
  <script lang="ts">
2
2
  import '../app.css'
3
3
  import { page } from '$app/state'
4
+ import { onMount } from 'svelte'
5
+ import { readTheme, applyTheme, type Theme } from '$lib/theme'
4
6
 
5
7
  let { children } = $props()
6
8
 
9
+ // Theme: dark by default (see app.html), toggleable + persisted.
10
+ let theme = $state<Theme>('dark')
11
+ onMount(() => {
12
+ theme = readTheme()
13
+ applyTheme(theme)
14
+ })
15
+ function toggleTheme() {
16
+ theme = theme === 'dark' ? 'light' : 'dark'
17
+ applyTheme(theme)
18
+ }
19
+
7
20
  const nav = [
8
21
  { href: '/', label: 'Overview', icon: '▦' },
9
22
  { href: '/orders', label: 'Orders', icon: '🧾' },
@@ -18,8 +31,8 @@
18
31
  <div class="flex h-full min-h-screen">
19
32
  <!-- Sidebar -->
20
33
  <aside
21
- class="hidden w-60 shrink-0 flex-col border-r bg-white md:flex"
22
- style="border-color: var(--app-border);"
34
+ class="hidden w-60 shrink-0 flex-col border-r md:flex"
35
+ style="border-color: var(--app-border); background: var(--app-panel);"
23
36
  >
24
37
  <div class="flex items-center gap-2 px-5 py-4">
25
38
  <span
@@ -57,19 +70,42 @@
57
70
  <!-- Main -->
58
71
  <div class="flex min-w-0 flex-1 flex-col">
59
72
  <header
60
- class="flex items-center justify-between border-b bg-white px-6 py-3"
61
- style="border-color: var(--app-border);"
73
+ class="flex items-center justify-between border-b px-6 py-3"
74
+ style="border-color: var(--app-border); background: var(--app-panel);"
62
75
  >
63
76
  <h1 class="text-base font-semibold" style="color: var(--app-fg);">
64
77
  {nav.find((n) => isActive(n.href))?.label ?? 'Dashboard'}
65
78
  </h1>
66
- <a
67
- href="https://www.svgrid.com/docs"
68
- target="_blank"
69
- rel="noopener"
70
- class="rounded-lg border px-3 py-1.5 text-sm font-medium"
71
- style="border-color: var(--app-border); color: var(--app-fg);">Docs</a
72
- >
79
+ <div class="flex items-center gap-2">
80
+ <button
81
+ type="button"
82
+ onclick={toggleTheme}
83
+ title={theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'}
84
+ aria-label="Toggle theme"
85
+ class="grid h-9 w-9 place-items-center rounded-lg border"
86
+ style="border-color: var(--app-border); color: var(--app-fg);"
87
+ >
88
+ {#if theme === 'dark'}
89
+ <!-- sun -->
90
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
91
+ <circle cx="12" cy="12" r="4" />
92
+ <path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41" />
93
+ </svg>
94
+ {:else}
95
+ <!-- moon -->
96
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
97
+ <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
98
+ </svg>
99
+ {/if}
100
+ </button>
101
+ <a
102
+ href="https://www.svgrid.com/docs"
103
+ target="_blank"
104
+ rel="noopener"
105
+ class="rounded-lg border px-3 py-1.5 text-sm font-medium"
106
+ style="border-color: var(--app-border); color: var(--app-fg);">Docs</a
107
+ >
108
+ </div>
73
109
  </header>
74
110
 
75
111
  <main class="min-w-0 flex-1 p-6">
@@ -53,7 +53,7 @@
53
53
  <StatCard label="Active customers" value={kpis.customers.toString()} />
54
54
  </div>
55
55
 
56
- <div class="rounded-xl border bg-white p-4 shadow-sm" style="border-color: var(--app-border);">
56
+ <div class="rounded-xl border p-4 shadow-sm" style="border-color: var(--app-border); background: var(--app-panel);">
57
57
  <div class="mb-3 flex items-center justify-between">
58
58
  <h2 class="text-sm font-semibold" style="color: var(--app-fg);">Recent orders</h2>
59
59
  <a class="text-sm font-medium" style="color: var(--app-accent);" href="/orders">View all →</a>
@@ -52,7 +52,7 @@
52
52
  {rows.length} customers · drag the <strong>Plan</strong> column into the group bar to group by plan.
53
53
  </p>
54
54
 
55
- <div class="min-h-0 flex-1 rounded-xl border bg-white p-2 shadow-sm" style="border-color: var(--app-border);">
55
+ <div class="min-h-0 flex-1 rounded-xl border p-2 shadow-sm" style="border-color: var(--app-border); background: var(--app-panel);">
56
56
  {#if browser}
57
57
  <SvGrid
58
58
  data={rows}
@@ -62,7 +62,7 @@
62
62
  </p>
63
63
  </div>
64
64
 
65
- <div class="min-h-0 flex-1 rounded-xl border bg-white p-2 shadow-sm" style="border-color: var(--app-border);">
65
+ <div class="min-h-0 flex-1 rounded-xl border p-2 shadow-sm" style="border-color: var(--app-border); background: var(--app-panel);">
66
66
  {#if browser}
67
67
  <SvGrid
68
68
  data={rows}