@classic-homes/theme-svelte 0.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.
Files changed (124) hide show
  1. package/README.md +305 -0
  2. package/dist/lib/components/Alert.svelte +51 -0
  3. package/dist/lib/components/Alert.svelte.d.ts +9 -0
  4. package/dist/lib/components/AlertDescription.svelte +16 -0
  5. package/dist/lib/components/AlertDescription.svelte.d.ts +9 -0
  6. package/dist/lib/components/AlertDialog.svelte +136 -0
  7. package/dist/lib/components/AlertDialog.svelte.d.ts +79 -0
  8. package/dist/lib/components/AlertTitle.svelte +16 -0
  9. package/dist/lib/components/AlertTitle.svelte.d.ts +9 -0
  10. package/dist/lib/components/Avatar.svelte +56 -0
  11. package/dist/lib/components/Avatar.svelte.d.ts +26 -0
  12. package/dist/lib/components/AvatarFallback.svelte +31 -0
  13. package/dist/lib/components/AvatarFallback.svelte.d.ts +17 -0
  14. package/dist/lib/components/AvatarImage.svelte +29 -0
  15. package/dist/lib/components/AvatarImage.svelte.d.ts +12 -0
  16. package/dist/lib/components/Badge.svelte +73 -0
  17. package/dist/lib/components/Badge.svelte.d.ts +11 -0
  18. package/dist/lib/components/Button.svelte +130 -0
  19. package/dist/lib/components/Button.svelte.d.ts +17 -0
  20. package/dist/lib/components/Card.svelte +58 -0
  21. package/dist/lib/components/Card.svelte.d.ts +26 -0
  22. package/dist/lib/components/CardContent.svelte +16 -0
  23. package/dist/lib/components/CardContent.svelte.d.ts +9 -0
  24. package/dist/lib/components/CardDescription.svelte +16 -0
  25. package/dist/lib/components/CardDescription.svelte.d.ts +9 -0
  26. package/dist/lib/components/CardFooter.svelte +16 -0
  27. package/dist/lib/components/CardFooter.svelte.d.ts +9 -0
  28. package/dist/lib/components/CardHeader.svelte +16 -0
  29. package/dist/lib/components/CardHeader.svelte.d.ts +9 -0
  30. package/dist/lib/components/CardTitle.svelte +16 -0
  31. package/dist/lib/components/CardTitle.svelte.d.ts +9 -0
  32. package/dist/lib/components/Checkbox.svelte +65 -0
  33. package/dist/lib/components/Checkbox.svelte.d.ts +14 -0
  34. package/dist/lib/components/DataTable.svelte +334 -0
  35. package/dist/lib/components/DataTable.svelte.d.ts +103 -0
  36. package/dist/lib/components/Dialog.svelte +111 -0
  37. package/dist/lib/components/Dialog.svelte.d.ts +22 -0
  38. package/dist/lib/components/DropdownMenu.svelte +135 -0
  39. package/dist/lib/components/DropdownMenu.svelte.d.ts +33 -0
  40. package/dist/lib/components/FileUpload.svelte +448 -0
  41. package/dist/lib/components/FileUpload.svelte.d.ts +42 -0
  42. package/dist/lib/components/FormField.svelte +134 -0
  43. package/dist/lib/components/FormField.svelte.d.ts +37 -0
  44. package/dist/lib/components/Input.svelte +61 -0
  45. package/dist/lib/components/Input.svelte.d.ts +19 -0
  46. package/dist/lib/components/Label.svelte +33 -0
  47. package/dist/lib/components/Label.svelte.d.ts +11 -0
  48. package/dist/lib/components/LoadingLogo.svelte +124 -0
  49. package/dist/lib/components/LoadingLogo.svelte.d.ts +16 -0
  50. package/dist/lib/components/LogoMain.svelte +237 -0
  51. package/dist/lib/components/LogoMain.svelte.d.ts +20 -0
  52. package/dist/lib/components/PageHeader.svelte +90 -0
  53. package/dist/lib/components/PageHeader.svelte.d.ts +28 -0
  54. package/dist/lib/components/Section.svelte +44 -0
  55. package/dist/lib/components/Section.svelte.d.ts +28 -0
  56. package/dist/lib/components/Select.svelte +174 -0
  57. package/dist/lib/components/Select.svelte.d.ts +32 -0
  58. package/dist/lib/components/Separator.svelte +29 -0
  59. package/dist/lib/components/Separator.svelte.d.ts +9 -0
  60. package/dist/lib/components/Skeleton.svelte +35 -0
  61. package/dist/lib/components/Skeleton.svelte.d.ts +7 -0
  62. package/dist/lib/components/Spinner.svelte +50 -0
  63. package/dist/lib/components/Spinner.svelte.d.ts +8 -0
  64. package/dist/lib/components/Switch.svelte +56 -0
  65. package/dist/lib/components/Switch.svelte.d.ts +14 -0
  66. package/dist/lib/components/TabPanel.svelte +44 -0
  67. package/dist/lib/components/TabPanel.svelte.d.ts +12 -0
  68. package/dist/lib/components/Tabs.svelte +125 -0
  69. package/dist/lib/components/Tabs.svelte.d.ts +19 -0
  70. package/dist/lib/components/Textarea.svelte +54 -0
  71. package/dist/lib/components/Textarea.svelte.d.ts +16 -0
  72. package/dist/lib/components/Toast.svelte +116 -0
  73. package/dist/lib/components/Toast.svelte.d.ts +12 -0
  74. package/dist/lib/components/ToastContainer.svelte +56 -0
  75. package/dist/lib/components/ToastContainer.svelte.d.ts +8 -0
  76. package/dist/lib/components/Tooltip.svelte +55 -0
  77. package/dist/lib/components/Tooltip.svelte.d.ts +18 -0
  78. package/dist/lib/components/layout/AppShell.svelte +82 -0
  79. package/dist/lib/components/layout/AppShell.svelte.d.ts +44 -0
  80. package/dist/lib/components/layout/DashboardLayout.svelte +248 -0
  81. package/dist/lib/components/layout/DashboardLayout.svelte.d.ts +62 -0
  82. package/dist/lib/components/layout/Footer.svelte +130 -0
  83. package/dist/lib/components/layout/Footer.svelte.d.ts +32 -0
  84. package/dist/lib/components/layout/FormPageLayout.svelte +92 -0
  85. package/dist/lib/components/layout/FormPageLayout.svelte.d.ts +33 -0
  86. package/dist/lib/components/layout/Header.svelte +94 -0
  87. package/dist/lib/components/layout/Header.svelte.d.ts +30 -0
  88. package/dist/lib/components/layout/PublicLayout.svelte +180 -0
  89. package/dist/lib/components/layout/PublicLayout.svelte.d.ts +39 -0
  90. package/dist/lib/components/layout/QuickLinks.svelte +112 -0
  91. package/dist/lib/components/layout/QuickLinks.svelte.d.ts +27 -0
  92. package/dist/lib/components/layout/Sidebar.svelte +243 -0
  93. package/dist/lib/components/layout/Sidebar.svelte.d.ts +48 -0
  94. package/dist/lib/composables/index.d.ts +8 -0
  95. package/dist/lib/composables/index.js +10 -0
  96. package/dist/lib/composables/useAsync.svelte.d.ts +102 -0
  97. package/dist/lib/composables/useAsync.svelte.js +210 -0
  98. package/dist/lib/composables/useForm.svelte.d.ts +123 -0
  99. package/dist/lib/composables/useForm.svelte.js +245 -0
  100. package/dist/lib/index.d.ts +65 -0
  101. package/dist/lib/index.js +83 -0
  102. package/dist/lib/performance.d.ts +79 -0
  103. package/dist/lib/performance.js +170 -0
  104. package/dist/lib/schemas/auth.d.ts +410 -0
  105. package/dist/lib/schemas/auth.js +216 -0
  106. package/dist/lib/schemas/common.d.ts +267 -0
  107. package/dist/lib/schemas/common.js +268 -0
  108. package/dist/lib/schemas/index.d.ts +24 -0
  109. package/dist/lib/schemas/index.js +32 -0
  110. package/dist/lib/stores/sidebar.svelte.d.ts +25 -0
  111. package/dist/lib/stores/sidebar.svelte.js +38 -0
  112. package/dist/lib/stores/theme.svelte.d.ts +72 -0
  113. package/dist/lib/stores/theme.svelte.js +150 -0
  114. package/dist/lib/stores/toast.svelte.d.ts +62 -0
  115. package/dist/lib/stores/toast.svelte.js +93 -0
  116. package/dist/lib/types/components.d.ts +85 -0
  117. package/dist/lib/types/components.js +7 -0
  118. package/dist/lib/types/layout.d.ts +258 -0
  119. package/dist/lib/types/layout.js +7 -0
  120. package/dist/lib/utils.d.ts +6 -0
  121. package/dist/lib/utils.js +9 -0
  122. package/dist/lib/validation.d.ts +101 -0
  123. package/dist/lib/validation.js +170 -0
  124. package/package.json +56 -0
@@ -0,0 +1,248 @@
1
+ <script lang="ts">
2
+ /**
3
+ * DashboardLayout - Complete dashboard layout with sidebar and header
4
+ *
5
+ * Features:
6
+ * - Responsive sidebar (collapsible on desktop, drawer on mobile)
7
+ * - Sticky header with page title and customizable content
8
+ * - Light/dark sidebar variants
9
+ * - Optional back link navigation
10
+ * - Main content area with proper spacing
11
+ * - Integrated with sidebar store for state management
12
+ */
13
+ import type { Snippet } from 'svelte';
14
+ import type { NavSection, NavItem, User, QuickLink } from '../../types/layout.js';
15
+ import { cn } from '../../utils.js';
16
+ import { sidebarStore } from '../../stores/sidebar.svelte.js';
17
+ import AppShell from './AppShell.svelte';
18
+ import Sidebar from './Sidebar.svelte';
19
+
20
+ interface BackLink {
21
+ /** Link label (e.g., "Back to Dashboard") */
22
+ label: string;
23
+ /** Link URL */
24
+ href: string;
25
+ /** Icon name for the icon snippet */
26
+ icon?: string;
27
+ }
28
+
29
+ interface Props {
30
+ /** Navigation sections for sidebar */
31
+ navigation: NavSection[];
32
+ /** Current user data */
33
+ user?: User;
34
+ /** Application name */
35
+ appName?: string;
36
+ /** Page title displayed in header */
37
+ pageTitle?: string;
38
+ /** Back link shown at top of sidebar navigation */
39
+ backLink?: BackLink;
40
+ /** Sidebar visual variant */
41
+ sidebarVariant?: 'light' | 'dark';
42
+ /** Whether sidebar starts collapsed */
43
+ sidebarCollapsed?: boolean;
44
+ /** Whether to show the header */
45
+ showHeader?: boolean;
46
+ /** Custom logo snippet */
47
+ logo?: Snippet;
48
+ /** Custom icon renderer for nav items */
49
+ icon?: Snippet<[NavItem]>;
50
+ /** Quick links displayed at bottom of sidebar */
51
+ quickLinks?: QuickLink[];
52
+ /** Quick links display mode: 'list' for stacked with labels, 'icons' for horizontal icons only */
53
+ quickLinksDisplay?: 'list' | 'icons';
54
+ /** Custom icon renderer for quick links */
55
+ quickLinkIcon?: Snippet<[QuickLink]>;
56
+ /** Custom content at start of header (after toggle button) */
57
+ headerStart?: Snippet;
58
+ /** Custom content at end of header */
59
+ headerEnd?: Snippet;
60
+ /** Custom user menu snippet */
61
+ userMenu?: Snippet<[User]>;
62
+ /** Sidebar footer content */
63
+ sidebarFooter?: Snippet;
64
+ /** Main content */
65
+ children: Snippet;
66
+ }
67
+
68
+ let {
69
+ navigation,
70
+ user,
71
+ appName = 'Dashboard',
72
+ pageTitle,
73
+ backLink,
74
+ sidebarVariant = 'light',
75
+ sidebarCollapsed = false,
76
+ showHeader = true,
77
+ logo,
78
+ icon,
79
+ quickLinks,
80
+ quickLinksDisplay = 'list',
81
+ quickLinkIcon,
82
+ headerStart,
83
+ headerEnd,
84
+ userMenu,
85
+ sidebarFooter,
86
+ children,
87
+ }: Props = $props();
88
+
89
+ // Create navigation with back link prepended if provided
90
+ const effectiveNavigation = $derived(
91
+ backLink
92
+ ? [
93
+ {
94
+ id: '__back',
95
+ items: [
96
+ {
97
+ id: '__back-link',
98
+ name: backLink.label,
99
+ href: backLink.href,
100
+ icon: backLink.icon || 'arrow-left',
101
+ },
102
+ ],
103
+ },
104
+ ...navigation,
105
+ ]
106
+ : navigation
107
+ );
108
+
109
+ // Reactive media query for mobile detection
110
+ let isMobile = $state(false);
111
+
112
+ $effect(() => {
113
+ if (typeof window !== 'undefined') {
114
+ const mediaQuery = window.matchMedia('(max-width: 1023px)');
115
+ isMobile = mediaQuery.matches;
116
+ sidebarStore.setMobile(isMobile);
117
+
118
+ const handler = (e: MediaQueryListEvent) => {
119
+ isMobile = e.matches;
120
+ sidebarStore.setMobile(e.matches);
121
+ };
122
+
123
+ mediaQuery.addEventListener('change', handler);
124
+ return () => mediaQuery.removeEventListener('change', handler);
125
+ }
126
+ });
127
+
128
+ // Initialize sidebar state
129
+ $effect(() => {
130
+ if (!sidebarCollapsed && !isMobile) {
131
+ sidebarStore.open();
132
+ }
133
+ });
134
+
135
+ const sidebarWidth = $derived(isMobile ? 0 : sidebarStore.isOpen ? 256 : 64);
136
+ </script>
137
+
138
+ <AppShell>
139
+ <!-- Sidebar -->
140
+ <Sidebar
141
+ navigation={effectiveNavigation}
142
+ variant={sidebarVariant}
143
+ collapsed={!sidebarStore.isOpen && !isMobile}
144
+ {isMobile}
145
+ mobileOpen={isMobile && sidebarStore.isOpen}
146
+ onClose={() => sidebarStore.close()}
147
+ {logo}
148
+ {icon}
149
+ {quickLinks}
150
+ {quickLinksDisplay}
151
+ {quickLinkIcon}
152
+ footer={sidebarFooter}
153
+ />
154
+
155
+ <!-- Main Content Area -->
156
+ <div
157
+ class="flex flex-1 flex-col transition-all duration-300"
158
+ style="margin-left: {sidebarWidth}px;"
159
+ >
160
+ {#if showHeader}
161
+ <header
162
+ class="sticky top-0 z-30 flex h-16 items-center justify-between border-b border-black bg-background px-4 lg:px-6"
163
+ >
164
+ <div class="flex items-center gap-4">
165
+ <!-- Mobile Menu Button -->
166
+ {#if isMobile}
167
+ <button
168
+ class="rounded-md p-2 hover:bg-accent lg:hidden"
169
+ onclick={() => sidebarStore.toggle()}
170
+ aria-label="Open menu"
171
+ >
172
+ <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
173
+ <path
174
+ stroke-linecap="round"
175
+ stroke-linejoin="round"
176
+ stroke-width="2"
177
+ d="M4 6h16M4 12h16M4 18h16"
178
+ />
179
+ </svg>
180
+ </button>
181
+ {:else}
182
+ <!-- Collapse Toggle Button (Desktop) -->
183
+ <button
184
+ class="rounded-md p-2 hover:bg-accent"
185
+ onclick={() => sidebarStore.toggle()}
186
+ aria-label={sidebarStore.isOpen ? 'Collapse sidebar' : 'Expand sidebar'}
187
+ >
188
+ <svg
189
+ class={cn('h-5 w-5 transition-transform', !sidebarStore.isOpen && 'rotate-180')}
190
+ fill="none"
191
+ viewBox="0 0 24 24"
192
+ stroke="currentColor"
193
+ >
194
+ <path
195
+ stroke-linecap="round"
196
+ stroke-linejoin="round"
197
+ stroke-width="2"
198
+ d="M11 19l-7-7 7-7m8 14l-7-7 7-7"
199
+ />
200
+ </svg>
201
+ </button>
202
+ {/if}
203
+
204
+ <!-- Page Title -->
205
+ {#if pageTitle}
206
+ <h2 class="text-lg font-semibold">{pageTitle}</h2>
207
+ {/if}
208
+
209
+ <!-- Custom Header Start -->
210
+ {#if headerStart}
211
+ {@render headerStart()}
212
+ {/if}
213
+ </div>
214
+
215
+ <div class="flex items-center gap-4">
216
+ <!-- Custom Header End -->
217
+ {#if headerEnd}
218
+ {@render headerEnd()}
219
+ {:else if user}
220
+ {#if userMenu}
221
+ {@render userMenu(user)}
222
+ {:else}
223
+ <!-- Default User Display -->
224
+ <div class="flex items-center gap-3">
225
+ <div
226
+ class="flex h-8 w-8 items-center justify-center rounded-full bg-primary text-sm font-medium text-primary-foreground"
227
+ >
228
+ {user.initials || user.name.charAt(0).toUpperCase()}
229
+ </div>
230
+ <div class="hidden md:block">
231
+ <p class="text-sm font-medium">{user.name}</p>
232
+ {#if user.email}
233
+ <p class="text-xs text-muted-foreground">{user.email}</p>
234
+ {/if}
235
+ </div>
236
+ </div>
237
+ {/if}
238
+ {/if}
239
+ </div>
240
+ </header>
241
+ {/if}
242
+
243
+ <!-- Main Content -->
244
+ <main id="main-content" class="flex-1 bg-content-bg p-4 lg:p-6">
245
+ {@render children()}
246
+ </main>
247
+ </div>
248
+ </AppShell>
@@ -0,0 +1,62 @@
1
+ /**
2
+ * DashboardLayout - Complete dashboard layout with sidebar and header
3
+ *
4
+ * Features:
5
+ * - Responsive sidebar (collapsible on desktop, drawer on mobile)
6
+ * - Sticky header with page title and customizable content
7
+ * - Light/dark sidebar variants
8
+ * - Optional back link navigation
9
+ * - Main content area with proper spacing
10
+ * - Integrated with sidebar store for state management
11
+ */
12
+ import type { Snippet } from 'svelte';
13
+ import type { NavSection, NavItem, User, QuickLink } from '../../types/layout.js';
14
+ interface BackLink {
15
+ /** Link label (e.g., "Back to Dashboard") */
16
+ label: string;
17
+ /** Link URL */
18
+ href: string;
19
+ /** Icon name for the icon snippet */
20
+ icon?: string;
21
+ }
22
+ interface Props {
23
+ /** Navigation sections for sidebar */
24
+ navigation: NavSection[];
25
+ /** Current user data */
26
+ user?: User;
27
+ /** Application name */
28
+ appName?: string;
29
+ /** Page title displayed in header */
30
+ pageTitle?: string;
31
+ /** Back link shown at top of sidebar navigation */
32
+ backLink?: BackLink;
33
+ /** Sidebar visual variant */
34
+ sidebarVariant?: 'light' | 'dark';
35
+ /** Whether sidebar starts collapsed */
36
+ sidebarCollapsed?: boolean;
37
+ /** Whether to show the header */
38
+ showHeader?: boolean;
39
+ /** Custom logo snippet */
40
+ logo?: Snippet;
41
+ /** Custom icon renderer for nav items */
42
+ icon?: Snippet<[NavItem]>;
43
+ /** Quick links displayed at bottom of sidebar */
44
+ quickLinks?: QuickLink[];
45
+ /** Quick links display mode: 'list' for stacked with labels, 'icons' for horizontal icons only */
46
+ quickLinksDisplay?: 'list' | 'icons';
47
+ /** Custom icon renderer for quick links */
48
+ quickLinkIcon?: Snippet<[QuickLink]>;
49
+ /** Custom content at start of header (after toggle button) */
50
+ headerStart?: Snippet;
51
+ /** Custom content at end of header */
52
+ headerEnd?: Snippet;
53
+ /** Custom user menu snippet */
54
+ userMenu?: Snippet<[User]>;
55
+ /** Sidebar footer content */
56
+ sidebarFooter?: Snippet;
57
+ /** Main content */
58
+ children: Snippet;
59
+ }
60
+ declare const DashboardLayout: import("svelte").Component<Props, {}, "">;
61
+ type DashboardLayout = ReturnType<typeof DashboardLayout>;
62
+ export default DashboardLayout;
@@ -0,0 +1,130 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Footer - Site footer component
4
+ *
5
+ * Features:
6
+ * - Link sections
7
+ * - Copyright text
8
+ * - Logo option
9
+ * - Custom content support
10
+ * - Strong accent border option (matches brand guidelines)
11
+ * - Dark variant for branded footers
12
+ */
13
+ import type { Snippet } from 'svelte';
14
+ import type { NavSection } from '../../types/layout.js';
15
+ import { cn } from '../../utils.js';
16
+ import LogoMain from '../LogoMain.svelte';
17
+
18
+ interface Props {
19
+ /** Link sections */
20
+ links?: NavSection[];
21
+ /** Copyright text */
22
+ copyright?: string;
23
+ /** Show logo in footer */
24
+ showLogo?: boolean;
25
+ /** Use strong accent border (10px top border) */
26
+ strongBorder?: boolean;
27
+ /** Dark variant with brand colors */
28
+ variant?: 'default' | 'dark';
29
+ /** Custom content */
30
+ children?: Snippet;
31
+ /** Additional classes */
32
+ class?: string;
33
+ }
34
+
35
+ let {
36
+ links = [],
37
+ copyright,
38
+ showLogo = true,
39
+ strongBorder = false,
40
+ variant = 'default',
41
+ children,
42
+ class: className,
43
+ }: Props = $props();
44
+
45
+ const currentYear = new Date().getFullYear();
46
+ const defaultCopyright = `${currentYear} Classic Homes. All rights reserved.`;
47
+
48
+ const isDark = $derived(variant === 'dark');
49
+ </script>
50
+
51
+ <footer
52
+ class={cn(
53
+ // Base styles
54
+ isDark ? 'bg-[#50504f] text-white' : 'bg-muted/30',
55
+ // Border styles - strong accent border or standard
56
+ strongBorder ? 'border-t-[10px] border-[#787878]' : 'border-t border-black',
57
+ className
58
+ )}
59
+ >
60
+ <div class="mx-auto max-w-7xl px-4 py-12 sm:px-6 lg:px-8">
61
+ {#if links.length > 0}
62
+ <div class="grid grid-cols-2 gap-8 md:grid-cols-4">
63
+ {#each links as section}
64
+ <div>
65
+ {#if section.title}
66
+ <h3 class={cn('text-lg font-bold mb-4', isDark ? 'text-white' : 'text-foreground')}>
67
+ {section.title}
68
+ </h3>
69
+ {/if}
70
+ <ul class="mt-4 space-y-2">
71
+ {#each section.items as item}
72
+ <li>
73
+ <a
74
+ href={item.href}
75
+ class={cn(
76
+ 'text-sm transition-colors',
77
+ isDark
78
+ ? 'text-white hover:text-primary'
79
+ : 'text-muted-foreground hover:text-foreground'
80
+ )}
81
+ target={item.external ? '_blank' : undefined}
82
+ rel={item.external ? 'noopener noreferrer' : undefined}
83
+ >
84
+ {item.name}
85
+ {#if item.external}
86
+ <svg
87
+ class="ml-1 inline-block h-3 w-3"
88
+ fill="none"
89
+ viewBox="0 0 24 24"
90
+ stroke="currentColor"
91
+ >
92
+ <path
93
+ stroke-linecap="round"
94
+ stroke-linejoin="round"
95
+ stroke-width="2"
96
+ d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
97
+ />
98
+ </svg>
99
+ {/if}
100
+ </a>
101
+ </li>
102
+ {/each}
103
+ </ul>
104
+ </div>
105
+ {/each}
106
+ </div>
107
+ {/if}
108
+
109
+ {#if children}
110
+ <div class={cn('mt-8 border-t pt-8', isDark ? 'border-gray-600' : 'border-black')}>
111
+ {@render children()}
112
+ </div>
113
+ {/if}
114
+
115
+ <div
116
+ class={cn(
117
+ 'mt-8 flex flex-col items-center justify-between gap-4 border-t pt-8 md:flex-row',
118
+ isDark ? 'border-gray-600' : 'border-black'
119
+ )}
120
+ >
121
+ {#if showLogo}
122
+ <LogoMain variant="horizontal" color={isDark ? 'light' : 'dark'} size="sm" />
123
+ {/if}
124
+
125
+ <p class={cn('text-center text-sm', isDark ? 'text-gray-400' : 'text-muted-foreground')}>
126
+ {copyright || defaultCopyright}
127
+ </p>
128
+ </div>
129
+ </div>
130
+ </footer>
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Footer - Site footer component
3
+ *
4
+ * Features:
5
+ * - Link sections
6
+ * - Copyright text
7
+ * - Logo option
8
+ * - Custom content support
9
+ * - Strong accent border option (matches brand guidelines)
10
+ * - Dark variant for branded footers
11
+ */
12
+ import type { Snippet } from 'svelte';
13
+ import type { NavSection } from '../../types/layout.js';
14
+ interface Props {
15
+ /** Link sections */
16
+ links?: NavSection[];
17
+ /** Copyright text */
18
+ copyright?: string;
19
+ /** Show logo in footer */
20
+ showLogo?: boolean;
21
+ /** Use strong accent border (10px top border) */
22
+ strongBorder?: boolean;
23
+ /** Dark variant with brand colors */
24
+ variant?: 'default' | 'dark';
25
+ /** Custom content */
26
+ children?: Snippet;
27
+ /** Additional classes */
28
+ class?: string;
29
+ }
30
+ declare const Footer: import("svelte").Component<Props, {}, "">;
31
+ type Footer = ReturnType<typeof Footer>;
32
+ export default Footer;
@@ -0,0 +1,92 @@
1
+ <script lang="ts">
2
+ /**
3
+ * FormPageLayout - Specialized layout for form pages
4
+ *
5
+ * Features:
6
+ * - Centered title and description header
7
+ * - Optional notice cards section
8
+ * - Two-column layout with optional sidebar
9
+ * - Help text footer
10
+ * - Responsive design (sidebar above main on mobile)
11
+ * - Light sage background for visual distinction
12
+ */
13
+ import type { Snippet } from 'svelte';
14
+ import { cn } from '../../utils.js';
15
+
16
+ interface Props {
17
+ /** Page title (h1) */
18
+ title: string;
19
+ /** Page description text */
20
+ description: string;
21
+ /** Footer help text (supports HTML) */
22
+ helpText?: string;
23
+ /** Show the notices section (renders notices snippet if provided) */
24
+ showNotices?: boolean;
25
+ /** Main form content */
26
+ children: Snippet;
27
+ /** Optional notices content (alert cards, etc.) */
28
+ notices?: Snippet;
29
+ /** Optional sidebar content */
30
+ sidebar?: Snippet;
31
+ /** Additional classes for the container */
32
+ class?: string;
33
+ }
34
+
35
+ let {
36
+ title,
37
+ description,
38
+ helpText,
39
+ showNotices = true,
40
+ children,
41
+ notices,
42
+ sidebar,
43
+ class: className,
44
+ }: Props = $props();
45
+
46
+ const hasSidebar = $derived(sidebar !== undefined);
47
+ </script>
48
+
49
+ <div class={cn('min-h-screen bg-brand-core-4-tint py-8 sm:py-12', className)}>
50
+ <div class="mx-auto max-w-5xl px-4 sm:px-6 lg:px-8">
51
+ <!-- Header Section -->
52
+ <header class="mb-8 text-center">
53
+ <h1 class="text-3xl font-light tracking-tight text-brand-core-2 md:text-4xl">
54
+ {title}
55
+ </h1>
56
+ <p class="mt-3 text-base text-gray-600 md:text-lg">
57
+ {description}
58
+ </p>
59
+ </header>
60
+
61
+ <!-- Notices Section -->
62
+ {#if showNotices && notices}
63
+ <div class="mb-6">
64
+ {@render notices()}
65
+ </div>
66
+ {/if}
67
+
68
+ <!-- Main Content Area -->
69
+ <div class={cn('gap-8', hasSidebar ? 'grid lg:grid-cols-[1fr,320px]' : 'mx-auto max-w-2xl')}>
70
+ <!-- Sidebar (shows first on mobile when present) -->
71
+ {#if hasSidebar}
72
+ <aside class="order-first lg:order-last">
73
+ {@render sidebar!()}
74
+ </aside>
75
+ {/if}
76
+
77
+ <!-- Main Content -->
78
+ <main id="form-content" class={hasSidebar ? 'order-last lg:order-first' : ''}>
79
+ {@render children()}
80
+ </main>
81
+ </div>
82
+
83
+ <!-- Help Text Footer -->
84
+ {#if helpText}
85
+ <footer class="mt-8 text-center">
86
+ <p class="text-sm text-gray-500">
87
+ {@html helpText}
88
+ </p>
89
+ </footer>
90
+ {/if}
91
+ </div>
92
+ </div>
@@ -0,0 +1,33 @@
1
+ /**
2
+ * FormPageLayout - Specialized layout for form pages
3
+ *
4
+ * Features:
5
+ * - Centered title and description header
6
+ * - Optional notice cards section
7
+ * - Two-column layout with optional sidebar
8
+ * - Help text footer
9
+ * - Responsive design (sidebar above main on mobile)
10
+ * - Light sage background for visual distinction
11
+ */
12
+ import type { Snippet } from 'svelte';
13
+ interface Props {
14
+ /** Page title (h1) */
15
+ title: string;
16
+ /** Page description text */
17
+ description: string;
18
+ /** Footer help text (supports HTML) */
19
+ helpText?: string;
20
+ /** Show the notices section (renders notices snippet if provided) */
21
+ showNotices?: boolean;
22
+ /** Main form content */
23
+ children: Snippet;
24
+ /** Optional notices content (alert cards, etc.) */
25
+ notices?: Snippet;
26
+ /** Optional sidebar content */
27
+ sidebar?: Snippet;
28
+ /** Additional classes for the container */
29
+ class?: string;
30
+ }
31
+ declare const FormPageLayout: import("svelte").Component<Props, {}, "">;
32
+ type FormPageLayout = ReturnType<typeof FormPageLayout>;
33
+ export default FormPageLayout;
@@ -0,0 +1,94 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Header - Application header component
4
+ *
5
+ * Features:
6
+ * - Hamburger menu button for mobile
7
+ * - Customizable start and end slots
8
+ * - Optional navigation slot with uppercase styling
9
+ * - Responsive design
10
+ * - Stronger border styling to match brand guidelines
11
+ */
12
+ import type { Snippet } from 'svelte';
13
+ import { cn } from '../../utils.js';
14
+
15
+ interface Props {
16
+ /** Whether to show hamburger menu button (mobile) */
17
+ showMenuButton?: boolean;
18
+ /** Callback when menu button is clicked */
19
+ onMenuClick?: () => void;
20
+ /** Custom start content (left side) */
21
+ start?: Snippet;
22
+ /** Navigation content (center/right on desktop, hidden on mobile) */
23
+ nav?: Snippet;
24
+ /** Custom end content (right side) */
25
+ end?: Snippet;
26
+ /** Use stronger/thicker border */
27
+ strongBorder?: boolean;
28
+ /** Additional classes */
29
+ class?: string;
30
+ }
31
+
32
+ let {
33
+ showMenuButton = false,
34
+ onMenuClick,
35
+ start,
36
+ nav,
37
+ end,
38
+ strongBorder = false,
39
+ class: className,
40
+ }: Props = $props();
41
+ </script>
42
+
43
+ <!--
44
+ Header Layout Dimensions:
45
+ - h-16 (64px): Standard header height, matches sidebar logo section
46
+ - z-30: Below sidebar (z-50) and overlays (z-40), above main content
47
+ - px-4 / lg:px-6: Responsive horizontal padding (16px mobile, 24px desktop)
48
+ -->
49
+ <header
50
+ class={cn(
51
+ 'sticky top-0 z-30 flex h-16 items-center justify-between bg-background px-4 lg:px-6',
52
+ strongBorder ? 'border-b-2 border-black' : 'border-b border-black',
53
+ className
54
+ )}
55
+ >
56
+ <div class="flex items-center gap-4">
57
+ <!-- Mobile Menu Button -->
58
+ {#if showMenuButton}
59
+ <button
60
+ class="rounded-md p-2 hover:bg-accent lg:hidden"
61
+ onclick={() => onMenuClick?.()}
62
+ aria-label="Open menu"
63
+ >
64
+ <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
65
+ <path
66
+ stroke-linecap="round"
67
+ stroke-linejoin="round"
68
+ stroke-width="2"
69
+ d="M4 6h16M4 12h16M4 18h16"
70
+ />
71
+ </svg>
72
+ </button>
73
+ {/if}
74
+
75
+ <!-- Start Content -->
76
+ {#if start}
77
+ {@render start()}
78
+ {/if}
79
+ </div>
80
+
81
+ <!-- Navigation (hidden on mobile, shown on desktop) -->
82
+ {#if nav}
83
+ <nav class="hidden lg:flex items-center gap-6" aria-label="Main navigation">
84
+ {@render nav()}
85
+ </nav>
86
+ {/if}
87
+
88
+ <!-- End Content -->
89
+ {#if end}
90
+ <div class="flex items-center gap-4">
91
+ {@render end()}
92
+ </div>
93
+ {/if}
94
+ </header>