@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,134 @@
1
+ <script lang="ts">
2
+ import { cn } from '../utils.js';
3
+ import Label from './Label.svelte';
4
+ import Input from './Input.svelte';
5
+ import Textarea from './Textarea.svelte';
6
+
7
+ interface Props {
8
+ /** Label text */
9
+ label: string;
10
+ /** Input ID for association */
11
+ id: string;
12
+ /** Input type (text, email, password, tel, url, number, textarea) */
13
+ type?: 'text' | 'email' | 'password' | 'tel' | 'url' | 'number' | 'textarea';
14
+ /** Field value (bindable) */
15
+ value?: string;
16
+ /** Error message */
17
+ error?: string;
18
+ /** Help text / hint */
19
+ hint?: string;
20
+ /** Disabled state */
21
+ disabled?: boolean;
22
+ /** Required field */
23
+ required?: boolean;
24
+ /** Placeholder text */
25
+ placeholder?: string;
26
+ /** Autocomplete attribute */
27
+ autocomplete?: AutoFill;
28
+ /** Name attribute */
29
+ name?: string;
30
+ /** Number of rows for textarea */
31
+ rows?: number;
32
+ /** Readonly state */
33
+ readonly?: boolean;
34
+ /** Additional class for container */
35
+ class?: string;
36
+ /** Callback when value changes */
37
+ onValueChange?: (value: string) => void;
38
+ /** Additional props */
39
+ [key: string]: unknown;
40
+ }
41
+
42
+ let {
43
+ label,
44
+ id,
45
+ type = 'text',
46
+ value = $bindable(''),
47
+ error,
48
+ hint,
49
+ disabled = false,
50
+ required = false,
51
+ placeholder,
52
+ autocomplete,
53
+ name,
54
+ rows = 3,
55
+ readonly = false,
56
+ class: className,
57
+ onValueChange,
58
+ ...restProps
59
+ }: Props = $props();
60
+
61
+ // Generate IDs for aria associations (derived to react to id changes)
62
+ const errorId = $derived(`${id}-error`);
63
+ const hintId = $derived(`${id}-hint`);
64
+
65
+ // Compute aria-describedby based on error/hint presence
66
+ // Error takes precedence over hint
67
+ const ariaDescribedBy = $derived.by(() => {
68
+ if (error) return errorId;
69
+ if (hint) return hintId;
70
+ return undefined;
71
+ });
72
+
73
+ // Input styling with error state
74
+ const inputClasses = $derived(error ? 'border-destructive focus-visible:ring-destructive' : '');
75
+ </script>
76
+
77
+ <div class={cn('space-y-2', className)}>
78
+ <!-- Label with required indicator -->
79
+ <Label for={id} {disabled}>
80
+ {label}
81
+ {#if required}
82
+ <span class="text-destructive ml-0.5" aria-hidden="true">*</span>
83
+ <span class="sr-only">(required)</span>
84
+ {/if}
85
+ </Label>
86
+
87
+ <!-- Input or Textarea based on type -->
88
+ {#if type === 'textarea'}
89
+ <Textarea
90
+ {id}
91
+ bind:value
92
+ {placeholder}
93
+ {disabled}
94
+ {readonly}
95
+ {required}
96
+ {name}
97
+ {rows}
98
+ {onValueChange}
99
+ aria-invalid={error ? 'true' : undefined}
100
+ aria-describedby={ariaDescribedBy}
101
+ class={inputClasses}
102
+ {...restProps}
103
+ />
104
+ {:else}
105
+ <Input
106
+ {id}
107
+ {type}
108
+ bind:value
109
+ {placeholder}
110
+ {disabled}
111
+ {readonly}
112
+ {required}
113
+ {name}
114
+ {autocomplete}
115
+ {onValueChange}
116
+ aria-invalid={error ? 'true' : undefined}
117
+ aria-describedby={ariaDescribedBy}
118
+ class={inputClasses}
119
+ {...restProps}
120
+ />
121
+ {/if}
122
+
123
+ <!-- Error message (takes precedence over hint) -->
124
+ {#if error}
125
+ <p id={errorId} class="text-sm text-destructive" role="alert" aria-live="polite">
126
+ {error}
127
+ </p>
128
+ {:else if hint}
129
+ <!-- Hint text -->
130
+ <p id={hintId} class="text-sm text-muted-foreground">
131
+ {hint}
132
+ </p>
133
+ {/if}
134
+ </div>
@@ -0,0 +1,37 @@
1
+ interface Props {
2
+ /** Label text */
3
+ label: string;
4
+ /** Input ID for association */
5
+ id: string;
6
+ /** Input type (text, email, password, tel, url, number, textarea) */
7
+ type?: 'text' | 'email' | 'password' | 'tel' | 'url' | 'number' | 'textarea';
8
+ /** Field value (bindable) */
9
+ value?: string;
10
+ /** Error message */
11
+ error?: string;
12
+ /** Help text / hint */
13
+ hint?: string;
14
+ /** Disabled state */
15
+ disabled?: boolean;
16
+ /** Required field */
17
+ required?: boolean;
18
+ /** Placeholder text */
19
+ placeholder?: string;
20
+ /** Autocomplete attribute */
21
+ autocomplete?: AutoFill;
22
+ /** Name attribute */
23
+ name?: string;
24
+ /** Number of rows for textarea */
25
+ rows?: number;
26
+ /** Readonly state */
27
+ readonly?: boolean;
28
+ /** Additional class for container */
29
+ class?: string;
30
+ /** Callback when value changes */
31
+ onValueChange?: (value: string) => void;
32
+ /** Additional props */
33
+ [key: string]: unknown;
34
+ }
35
+ declare const FormField: import("svelte").Component<Props, {}, "value">;
36
+ type FormField = ReturnType<typeof FormField>;
37
+ export default FormField;
@@ -0,0 +1,61 @@
1
+ <script lang="ts">
2
+ import { cn } from '../utils.js';
3
+
4
+ interface Props {
5
+ type?: string;
6
+ value?: string | number;
7
+ placeholder?: string;
8
+ disabled?: boolean;
9
+ readonly?: boolean;
10
+ required?: boolean;
11
+ name?: string;
12
+ id?: string;
13
+ autocomplete?: AutoFill;
14
+ /** Input mode hint for mobile keyboards */
15
+ inputmode?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search';
16
+ class?: string;
17
+ onValueChange?: (value: string) => void;
18
+ [key: string]: unknown;
19
+ }
20
+
21
+ let {
22
+ type = 'text',
23
+ value = $bindable(''),
24
+ placeholder,
25
+ disabled = false,
26
+ readonly = false,
27
+ required = false,
28
+ name,
29
+ id,
30
+ autocomplete,
31
+ inputmode,
32
+ class: className,
33
+ onValueChange,
34
+ ...restProps
35
+ }: Props = $props();
36
+
37
+ function handleInput(e: Event) {
38
+ const target = e.target as HTMLInputElement;
39
+ value = target.value;
40
+ onValueChange?.(target.value);
41
+ }
42
+ </script>
43
+
44
+ <input
45
+ {type}
46
+ {value}
47
+ {placeholder}
48
+ {disabled}
49
+ {readonly}
50
+ {required}
51
+ {name}
52
+ {id}
53
+ {autocomplete}
54
+ {inputmode}
55
+ class={cn(
56
+ 'flex h-11 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
57
+ className
58
+ )}
59
+ oninput={handleInput}
60
+ {...restProps}
61
+ />
@@ -0,0 +1,19 @@
1
+ interface Props {
2
+ type?: string;
3
+ value?: string | number;
4
+ placeholder?: string;
5
+ disabled?: boolean;
6
+ readonly?: boolean;
7
+ required?: boolean;
8
+ name?: string;
9
+ id?: string;
10
+ autocomplete?: AutoFill;
11
+ /** Input mode hint for mobile keyboards */
12
+ inputmode?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search';
13
+ class?: string;
14
+ onValueChange?: (value: string) => void;
15
+ [key: string]: unknown;
16
+ }
17
+ declare const Input: import("svelte").Component<Props, {}, "value">;
18
+ type Input = ReturnType<typeof Input>;
19
+ export default Input;
@@ -0,0 +1,33 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { Label as LabelPrimitive } from 'bits-ui';
4
+ import { cn } from '../utils.js';
5
+
6
+ interface Props {
7
+ for?: string;
8
+ disabled?: boolean;
9
+ class?: string;
10
+ children: Snippet;
11
+ [key: string]: unknown;
12
+ }
13
+
14
+ let {
15
+ for: htmlFor,
16
+ disabled = false,
17
+ class: className,
18
+ children,
19
+ ...restProps
20
+ }: Props = $props();
21
+ </script>
22
+
23
+ <LabelPrimitive.Root
24
+ for={htmlFor}
25
+ class={cn(
26
+ 'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 text-gray-700 dark:text-gray-300',
27
+ disabled && 'opacity-50 cursor-not-allowed',
28
+ className
29
+ )}
30
+ {...restProps}
31
+ >
32
+ {@render children()}
33
+ </LabelPrimitive.Root>
@@ -0,0 +1,11 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ for?: string;
4
+ disabled?: boolean;
5
+ class?: string;
6
+ children: Snippet;
7
+ [key: string]: unknown;
8
+ }
9
+ declare const Label: import("svelte").Component<Props, {}, "">;
10
+ type Label = ReturnType<typeof Label>;
11
+ export default Label;
@@ -0,0 +1,124 @@
1
+ <script lang="ts">
2
+ import { cn } from '../utils.js';
3
+
4
+ interface Props {
5
+ /** Width in pixels */
6
+ width?: number;
7
+ /** Height in pixels */
8
+ height?: number;
9
+ /** Enables loading animation */
10
+ loading?: boolean;
11
+ /** Color variant for different backgrounds */
12
+ variant?: 'light' | 'dark';
13
+ /** Additional CSS classes */
14
+ class?: string;
15
+ [key: string]: unknown;
16
+ }
17
+
18
+ let {
19
+ width = 40,
20
+ height = 40,
21
+ loading = false,
22
+ variant = 'dark',
23
+ class: className,
24
+ ...restProps
25
+ }: Props = $props();
26
+
27
+ // Color definitions
28
+ const colors = {
29
+ dark: {
30
+ fill: '#C50F22', // Brand red
31
+ fillLoading: '#ffffff', // White when loading
32
+ stroke: '#C50F22', // Red stroke when loading
33
+ },
34
+ light: {
35
+ fill: '#ffffff', // White
36
+ fillLoading: '#C50F22', // Red when loading
37
+ stroke: '#ffffff', // White stroke when loading
38
+ },
39
+ };
40
+
41
+ const currentColors = $derived(colors[variant]);
42
+ const fillColor = $derived(loading ? currentColors.fillLoading : currentColors.fill);
43
+ const strokeColor = $derived(currentColors.stroke);
44
+
45
+ // SVG paths for the Classic Homes floral/rose logo
46
+ const logoPaths = [
47
+ // Left leaf/petal
48
+ 'M9.902 16.6c.3.7 1.7.8 1-.6-.6-1.4-2.3-5.1-6.1-6.3-1.7-.5-3.7.4-4.3 1.6-.4.7-1.1 2.9.5 4.3.8.7 1.9 1.2 3.6 1 .4-.1.9-.5.6-.8-.5-.4-1.9-1.7-1.1-2.7.7-.8 1.4-.6 2-.3.4.2 2.1 1 3.7 3.6l.1.2z',
49
+ // Right leaf/petal
50
+ 'M14.602 16.6c-.3.7-1.7.8-1-.6.6-1.4 2.3-5.1 6.1-6.3 1.7-.5 3.7.4 4.3 1.6.4.7 1.1 2.9-.5 4.3-.8.7-1.9 1.2-3.6 1-.4-.1-.9-.5-.6-.8.5-.4 1.9-1.7 1.1-2.7-.7-.8-1.4-.6-2-.3-.4.2-2.1 1-3.7 3.6l-.1.2z',
51
+ // Center stem/bud
52
+ 'M12.202 0c.7 1.3 2.1 3.2 2.3 5.3.1 1.5-.8 8.3-2.3 11.7-1.5-3.5-2.4-10.3-2.3-11.7.2-2.1 1.6-4 2.3-5.3z',
53
+ // Base bar
54
+ 'M9.002 18.1c-.8 0-.7.8-.1.8h6.7c.7 0 .7-.8-.1-.8h-6.5z',
55
+ // Pot/vase center
56
+ 'M13.802 24.8c-1.1-1.1-.9-4.6-.9-4.6h-1.3s.2 3.6-.9 4.6h3.1z',
57
+ // Pot/vase right detail
58
+ 'M14.202 22.5c-.1-.3-.5-1.4-.6-2.3 0-.2.6-.5.7.1.1.6.4 1.6.6 1.9.3.5-.4.9-.7.3z',
59
+ // Pot/vase left detail
60
+ 'M10.302 22.5c.1-.3.5-1.4.6-2.2 0-.3-.5-.6-.7 0-.1.6-.4 1.6-.6 1.9-.3.5.4.9.7.3z',
61
+ ];
62
+ </script>
63
+
64
+ <div
65
+ class={cn('inline-flex items-center justify-center', className)}
66
+ style="width: {width}px; height: {height}px;"
67
+ role={loading ? 'status' : 'img'}
68
+ aria-label={loading ? 'Loading' : 'Classic Homes'}
69
+ {...restProps}
70
+ >
71
+ <svg
72
+ width="100%"
73
+ height="100%"
74
+ viewBox="-1 0 26 25"
75
+ fill="none"
76
+ xmlns="http://www.w3.org/2000/svg"
77
+ aria-hidden="true"
78
+ class={cn(loading && 'loading-logo-animated')}
79
+ >
80
+ <!-- Main filled paths -->
81
+ <g fill={fillColor} class="transition-all duration-300">
82
+ {#each logoPaths as path}
83
+ <path d={path} />
84
+ {/each}
85
+ </g>
86
+
87
+ <!-- Tracing stroke overlay (only visible when loading) -->
88
+ {#if loading}
89
+ <g
90
+ fill="none"
91
+ stroke={strokeColor}
92
+ stroke-width="0.5"
93
+ stroke-dasharray="10 4"
94
+ class="loading-logo-trace"
95
+ >
96
+ {#each logoPaths as path}
97
+ <path d={path} />
98
+ {/each}
99
+ </g>
100
+ {/if}
101
+ </svg>
102
+
103
+ {#if loading}
104
+ <span class="sr-only">Loading</span>
105
+ {/if}
106
+ </div>
107
+
108
+ <style>
109
+ @keyframes trace {
110
+ to {
111
+ stroke-dashoffset: -14;
112
+ }
113
+ }
114
+
115
+ .loading-logo-trace {
116
+ animation: trace 2s linear infinite;
117
+ }
118
+
119
+ @media (prefers-reduced-motion: reduce) {
120
+ .loading-logo-trace {
121
+ animation: none;
122
+ }
123
+ }
124
+ </style>
@@ -0,0 +1,16 @@
1
+ interface Props {
2
+ /** Width in pixels */
3
+ width?: number;
4
+ /** Height in pixels */
5
+ height?: number;
6
+ /** Enables loading animation */
7
+ loading?: boolean;
8
+ /** Color variant for different backgrounds */
9
+ variant?: 'light' | 'dark';
10
+ /** Additional CSS classes */
11
+ class?: string;
12
+ [key: string]: unknown;
13
+ }
14
+ declare const LoadingLogo: import("svelte").Component<Props, {}, "">;
15
+ type LoadingLogo = ReturnType<typeof LoadingLogo>;
16
+ export default LoadingLogo;
@@ -0,0 +1,237 @@
1
+ <script lang="ts">
2
+ import { cn } from '../utils.js';
3
+ import Badge from './Badge.svelte';
4
+
5
+ interface Props {
6
+ /** Layout variant */
7
+ variant?: 'horizontal' | 'stacked' | 'icon';
8
+ /** Color mode for different backgrounds */
9
+ color?: 'dark' | 'light' | 'mono-dark' | 'mono-light';
10
+ /** Optional subtitle (e.g., "CHAPI", "MY HOME") */
11
+ subtitle?: string;
12
+ /** Environment indicator (LOCAL, DEV, DEMO) */
13
+ environment?: 'local' | 'dev' | 'demo';
14
+ /** Size preset */
15
+ size?: 'sm' | 'md' | 'lg';
16
+ /** Additional CSS classes */
17
+ class?: string;
18
+ /** Accessible label - set to empty string if wrapped in a link with its own label */
19
+ ariaLabel?: string;
20
+ [key: string]: unknown;
21
+ }
22
+
23
+ let {
24
+ variant = 'horizontal',
25
+ color = 'dark',
26
+ subtitle,
27
+ environment,
28
+ size = 'md',
29
+ class: className,
30
+ ariaLabel = 'Classic Homes',
31
+ ...restProps
32
+ }: Props = $props();
33
+
34
+ // Size configurations
35
+ const sizes = {
36
+ sm: { icon: 24, title: '1rem', subtitle: '0.7rem', badge: '0.5rem', gap: '0.375rem' },
37
+ md: { icon: 32, title: '1.2rem', subtitle: '0.85rem', badge: '0.6rem', gap: '0.5rem' },
38
+ lg: { icon: 40, title: '1.4rem', subtitle: '1rem', badge: '0.7rem', gap: '0.5rem' },
39
+ };
40
+
41
+ // Environment badge configuration
42
+ const environmentConfig = {
43
+ local: { label: 'LOCAL', variant: 'info' as const },
44
+ dev: { label: 'DEV', variant: 'warning' as const },
45
+ demo: { label: 'DEMO', variant: 'secondary' as const },
46
+ };
47
+
48
+ const envConfig = $derived(environment ? environmentConfig[environment] : null);
49
+
50
+ const currentSize = $derived(sizes[size]);
51
+
52
+ // Color definitions based on brand guidelines
53
+ const colors = {
54
+ dark: {
55
+ logo: '#C50F22', // Brand red
56
+ title: '#000000', // Black for light backgrounds
57
+ subtitle: '#7a7a7a', // Gray
58
+ },
59
+ light: {
60
+ logo: '#ffffff',
61
+ title: '#ffffff',
62
+ subtitle: 'rgba(255, 255, 255, 0.8)',
63
+ },
64
+ 'mono-dark': {
65
+ logo: '#002e3f', // Navy
66
+ title: '#002e3f',
67
+ subtitle: '#7a7a7a',
68
+ },
69
+ 'mono-light': {
70
+ logo: '#ffffff',
71
+ title: '#ffffff',
72
+ subtitle: 'rgba(255, 255, 255, 0.8)',
73
+ },
74
+ };
75
+
76
+ const currentColors = $derived(colors[color]);
77
+
78
+ // SVG paths for the Classic Homes floral/rose logo
79
+ const logoPaths = [
80
+ // Left leaf/petal
81
+ 'M9.902 16.6c.3.7 1.7.8 1-.6-.6-1.4-2.3-5.1-6.1-6.3-1.7-.5-3.7.4-4.3 1.6-.4.7-1.1 2.9.5 4.3.8.7 1.9 1.2 3.6 1 .4-.1.9-.5.6-.8-.5-.4-1.9-1.7-1.1-2.7.7-.8 1.4-.6 2-.3.4.2 2.1 1 3.7 3.6l.1.2z',
82
+ // Right leaf/petal
83
+ 'M14.602 16.6c-.3.7-1.7.8-1-.6.6-1.4 2.3-5.1 6.1-6.3 1.7-.5 3.7.4 4.3 1.6.4.7 1.1 2.9-.5 4.3-.8.7-1.9 1.2-3.6 1-.4-.1-.9-.5-.6-.8.5-.4 1.9-1.7 1.1-2.7-.7-.8-1.4-.6-2-.3-.4.2-2.1 1-3.7 3.6l-.1.2z',
84
+ // Center stem/bud
85
+ 'M12.202 0c.7 1.3 2.1 3.2 2.3 5.3.1 1.5-.8 8.3-2.3 11.7-1.5-3.5-2.4-10.3-2.3-11.7.2-2.1 1.6-4 2.3-5.3z',
86
+ // Base bar
87
+ 'M9.002 18.1c-.8 0-.7.8-.1.8h6.7c.7 0 .7-.8-.1-.8h-6.5z',
88
+ // Pot/vase center
89
+ 'M13.802 24.8c-1.1-1.1-.9-4.6-.9-4.6h-1.3s.2 3.6-.9 4.6h3.1z',
90
+ // Pot/vase right detail
91
+ 'M14.202 22.5c-.1-.3-.5-1.4-.6-2.3 0-.2.6-.5.7.1.1.6.4 1.6.6 1.9.3.5-.4.9-.7.3z',
92
+ // Pot/vase left detail
93
+ 'M10.302 22.5c.1-.3.5-1.4.6-2.2 0-.3-.5-.6-.7 0-.1.6-.4 1.6-.6 1.9-.3.5.4.9.7.3z',
94
+ ];
95
+ </script>
96
+
97
+ {#if variant === 'icon'}
98
+ <!-- Icon only -->
99
+ <div
100
+ class={cn('shrink-0', className)}
101
+ style="width: {currentSize.icon}px; height: {currentSize.icon}px;"
102
+ role={ariaLabel ? 'img' : undefined}
103
+ aria-label={ariaLabel || undefined}
104
+ {...restProps}
105
+ >
106
+ <svg
107
+ width="100%"
108
+ height="100%"
109
+ viewBox="0 0 25 25"
110
+ fill="none"
111
+ xmlns="http://www.w3.org/2000/svg"
112
+ aria-hidden="true"
113
+ >
114
+ <g fill={currentColors.logo}>
115
+ {#each logoPaths as path}
116
+ <path d={path} />
117
+ {/each}
118
+ </g>
119
+ </svg>
120
+ </div>
121
+ {:else if variant === 'stacked'}
122
+ <!-- Stacked layout (icon above text) -->
123
+ <div
124
+ class={cn('flex flex-col items-center justify-start shrink-0', className)}
125
+ style="gap: {currentSize.gap};"
126
+ role={ariaLabel ? 'img' : undefined}
127
+ aria-label={ariaLabel || undefined}
128
+ {...restProps}
129
+ >
130
+ <div style="width: {currentSize.icon}px; height: {currentSize.icon}px;">
131
+ <svg
132
+ width="100%"
133
+ height="100%"
134
+ viewBox="0 0 25 25"
135
+ fill="none"
136
+ xmlns="http://www.w3.org/2000/svg"
137
+ aria-hidden="true"
138
+ >
139
+ <g fill={currentColors.logo}>
140
+ {#each logoPaths as path}
141
+ <path d={path} />
142
+ {/each}
143
+ </g>
144
+ </svg>
145
+ </div>
146
+ <div
147
+ class="flex flex-col items-center justify-start"
148
+ aria-hidden={ariaLabel ? 'true' : undefined}
149
+ >
150
+ <span
151
+ class="leading-tight font-medium m-0 p-0"
152
+ style="font-family: 'Figtree', system-ui, sans-serif; font-size: {currentSize.title}; color: {currentColors.title};"
153
+ >
154
+ Classic Homes
155
+ </span>
156
+ {#if subtitle || envConfig}
157
+ <div class="flex flex-row items-center justify-center gap-1.5">
158
+ {#if envConfig}
159
+ <Badge
160
+ variant={envConfig.variant}
161
+ class="font-bold px-1.5 py-0"
162
+ style="font-size: {currentSize.badge}; height: auto; line-height: 1.4;"
163
+ >
164
+ {envConfig.label}
165
+ </Badge>
166
+ {/if}
167
+ {#if subtitle}
168
+ <span
169
+ class="uppercase font-black leading-none m-0 p-0"
170
+ style="font-family: 'Figtree', system-ui, sans-serif; font-size: {currentSize.subtitle}; color: {currentColors.subtitle};"
171
+ >
172
+ {subtitle}
173
+ </span>
174
+ {/if}
175
+ </div>
176
+ {/if}
177
+ </div>
178
+ </div>
179
+ {:else}
180
+ <!-- Horizontal layout (default) -->
181
+ <div
182
+ class={cn('flex flex-row items-center justify-start shrink-0', className)}
183
+ style="gap: {currentSize.gap};"
184
+ role={ariaLabel ? 'img' : undefined}
185
+ aria-label={ariaLabel || undefined}
186
+ {...restProps}
187
+ >
188
+ <div style="width: {currentSize.icon}px; height: {currentSize.icon}px;">
189
+ <svg
190
+ width="100%"
191
+ height="100%"
192
+ viewBox="0 0 25 25"
193
+ fill="none"
194
+ xmlns="http://www.w3.org/2000/svg"
195
+ aria-hidden="true"
196
+ >
197
+ <g fill={currentColors.logo}>
198
+ {#each logoPaths as path}
199
+ <path d={path} />
200
+ {/each}
201
+ </g>
202
+ </svg>
203
+ </div>
204
+ <div
205
+ class="flex flex-col items-start justify-start"
206
+ aria-hidden={ariaLabel ? 'true' : undefined}
207
+ >
208
+ <span
209
+ class="leading-tight font-medium m-0 p-0 whitespace-nowrap"
210
+ style="font-family: 'Figtree', system-ui, sans-serif; font-size: {currentSize.title}; color: {currentColors.title};"
211
+ >
212
+ Classic Homes
213
+ </span>
214
+ {#if subtitle || envConfig}
215
+ <div class="flex flex-row items-center justify-end gap-1.5 w-full">
216
+ {#if envConfig}
217
+ <Badge
218
+ variant={envConfig.variant}
219
+ class="font-bold px-1.5 py-0"
220
+ style="font-size: {currentSize.badge}; height: auto; line-height: 1.4;"
221
+ >
222
+ {envConfig.label}
223
+ </Badge>
224
+ {/if}
225
+ {#if subtitle}
226
+ <span
227
+ class="uppercase font-black leading-none m-0 p-0"
228
+ style="font-family: 'Figtree', system-ui, sans-serif; font-size: {currentSize.subtitle}; color: {currentColors.subtitle};"
229
+ >
230
+ {subtitle}
231
+ </span>
232
+ {/if}
233
+ </div>
234
+ {/if}
235
+ </div>
236
+ </div>
237
+ {/if}
@@ -0,0 +1,20 @@
1
+ interface Props {
2
+ /** Layout variant */
3
+ variant?: 'horizontal' | 'stacked' | 'icon';
4
+ /** Color mode for different backgrounds */
5
+ color?: 'dark' | 'light' | 'mono-dark' | 'mono-light';
6
+ /** Optional subtitle (e.g., "CHAPI", "MY HOME") */
7
+ subtitle?: string;
8
+ /** Environment indicator (LOCAL, DEV, DEMO) */
9
+ environment?: 'local' | 'dev' | 'demo';
10
+ /** Size preset */
11
+ size?: 'sm' | 'md' | 'lg';
12
+ /** Additional CSS classes */
13
+ class?: string;
14
+ /** Accessible label - set to empty string if wrapped in a link with its own label */
15
+ ariaLabel?: string;
16
+ [key: string]: unknown;
17
+ }
18
+ declare const LogoMain: import("svelte").Component<Props, {}, "">;
19
+ type LogoMain = ReturnType<typeof LogoMain>;
20
+ export default LogoMain;