@autumnsgrove/groveengine 0.6.4 → 0.6.5
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/dist/auth/session.d.ts +2 -2
- package/dist/components/admin/FloatingToolbar.svelte +373 -0
- package/dist/components/admin/FloatingToolbar.svelte.d.ts +17 -0
- package/dist/components/admin/MarkdownEditor.svelte +26 -347
- package/dist/components/admin/MarkdownEditor.svelte.d.ts +1 -1
- package/dist/components/admin/composables/index.d.ts +0 -2
- package/dist/components/admin/composables/index.js +0 -2
- package/dist/components/custom/MobileTOC.svelte +20 -13
- package/dist/components/quota/UpgradePrompt.svelte +1 -1
- package/dist/server/services/database.d.ts +138 -0
- package/dist/server/services/database.js +234 -0
- package/dist/server/services/index.d.ts +5 -1
- package/dist/server/services/index.js +24 -2
- package/dist/server/services/turnstile.d.ts +66 -0
- package/dist/server/services/turnstile.js +131 -0
- package/dist/server/services/users.d.ts +104 -0
- package/dist/server/services/users.js +158 -0
- package/dist/styles/README.md +50 -0
- package/dist/styles/vine-pattern.css +24 -0
- package/dist/types/turnstile.d.ts +42 -0
- package/dist/ui/components/forms/TurnstileWidget.svelte +111 -0
- package/dist/ui/components/forms/TurnstileWidget.svelte.d.ts +14 -0
- package/dist/ui/components/primitives/dialog/dialog-overlay.svelte +1 -1
- package/dist/ui/components/primitives/sheet/sheet-overlay.svelte +1 -1
- package/dist/ui/components/ui/Logo.svelte +161 -23
- package/dist/ui/components/ui/Logo.svelte.d.ts +4 -10
- package/dist/ui/tokens/fonts.d.ts +69 -0
- package/dist/ui/tokens/fonts.js +341 -0
- package/dist/ui/tokens/index.d.ts +6 -5
- package/dist/ui/tokens/index.js +7 -6
- package/package.json +22 -21
- package/static/fonts/alagard.ttf +0 -0
- package/static/robots.txt +487 -0
- package/LICENSE +0 -378
- package/dist/components/admin/composables/useCommandPalette.svelte.d.ts +0 -87
- package/dist/components/admin/composables/useCommandPalette.svelte.js +0 -158
- package/dist/components/admin/composables/useSlashCommands.svelte.d.ts +0 -104
- package/dist/components/admin/composables/useSlashCommands.svelte.js +0 -215
|
@@ -7,8 +7,21 @@
|
|
|
7
7
|
* when placed in an accent-colored context, or can be overridden.
|
|
8
8
|
*
|
|
9
9
|
* The trunk defaults to Grove's classic bark brown (#5d4037).
|
|
10
|
+
*
|
|
11
|
+
* @example Loading state (breathing animation)
|
|
12
|
+
* ```svelte
|
|
13
|
+
* <Logo breathing />
|
|
14
|
+
* <Logo breathing breathingSpeed="slow" />
|
|
15
|
+
* ```
|
|
16
|
+
* Note: breathing is intended for single loading indicators, not lists.
|
|
10
17
|
*/
|
|
11
18
|
|
|
19
|
+
import { tweened } from 'svelte/motion';
|
|
20
|
+
import { cubicInOut } from 'svelte/easing';
|
|
21
|
+
import { browser } from '$app/environment';
|
|
22
|
+
|
|
23
|
+
type BreathingSpeed = 'slow' | 'normal' | 'fast';
|
|
24
|
+
|
|
12
25
|
interface Props {
|
|
13
26
|
class?: string;
|
|
14
27
|
/** Foliage color - defaults to currentColor (inherits accent) */
|
|
@@ -19,8 +32,10 @@
|
|
|
19
32
|
monochrome?: boolean;
|
|
20
33
|
/** Add subtle sway animation */
|
|
21
34
|
animate?: boolean;
|
|
22
|
-
/** Add breathing animation (for loading states) */
|
|
35
|
+
/** Add breathing animation (for loading states, not lists) */
|
|
23
36
|
breathing?: boolean;
|
|
37
|
+
/** Breathing animation speed - 'slow' (1500ms), 'normal' (800ms), 'fast' (400ms) */
|
|
38
|
+
breathingSpeed?: BreathingSpeed;
|
|
24
39
|
}
|
|
25
40
|
|
|
26
41
|
let {
|
|
@@ -29,9 +44,28 @@
|
|
|
29
44
|
trunkColor,
|
|
30
45
|
monochrome = false,
|
|
31
46
|
animate = false,
|
|
32
|
-
breathing = false
|
|
47
|
+
breathing = false,
|
|
48
|
+
breathingSpeed = 'normal'
|
|
33
49
|
}: Props = $props();
|
|
34
50
|
|
|
51
|
+
// Breathing speed presets (duration per half-cycle in ms)
|
|
52
|
+
const BREATHING_SPEEDS = {
|
|
53
|
+
slow: 1500, // 3s full cycle - calm, meditative
|
|
54
|
+
normal: 800, // 1.6s full cycle - balanced
|
|
55
|
+
fast: 400 // 0.8s full cycle - urgent
|
|
56
|
+
} as const;
|
|
57
|
+
|
|
58
|
+
// Respect user's reduced motion preference (reactive to system changes)
|
|
59
|
+
const reducedMotionQuery = browser ? window.matchMedia('(prefers-reduced-motion: reduce)') : null;
|
|
60
|
+
let prefersReducedMotion = $state(reducedMotionQuery?.matches ?? false);
|
|
61
|
+
|
|
62
|
+
$effect(() => {
|
|
63
|
+
if (!reducedMotionQuery) return;
|
|
64
|
+
const handler = (e: MediaQueryListEvent) => { prefersReducedMotion = e.matches; };
|
|
65
|
+
reducedMotionQuery.addEventListener('change', handler);
|
|
66
|
+
return () => reducedMotionQuery.removeEventListener('change', handler);
|
|
67
|
+
});
|
|
68
|
+
|
|
35
69
|
// Classic bark brown from the nature palette
|
|
36
70
|
const BARK_BROWN = '#5d4037';
|
|
37
71
|
|
|
@@ -41,8 +75,85 @@
|
|
|
41
75
|
? foliageColor
|
|
42
76
|
: (trunkColor ?? BARK_BROWN);
|
|
43
77
|
|
|
44
|
-
//
|
|
45
|
-
const
|
|
78
|
+
// Breathing animation using tweened store (duration set dynamically in $effect)
|
|
79
|
+
const breathValue = tweened(0, { easing: cubicInOut });
|
|
80
|
+
|
|
81
|
+
// Animation loop for breathing effect
|
|
82
|
+
// Re-runs when breathing or breathingSpeed changes, ensuring reactive duration updates
|
|
83
|
+
$effect(() => {
|
|
84
|
+
const duration = BREATHING_SPEEDS[breathingSpeed];
|
|
85
|
+
|
|
86
|
+
// Disable animation if breathing is off or user prefers reduced motion
|
|
87
|
+
if (!breathing || prefersReducedMotion) {
|
|
88
|
+
// Use half the breathing duration for smoother exit transition
|
|
89
|
+
breathValue.set(0, { duration: Math.min(duration / 2, 300) });
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let cancelled = false;
|
|
94
|
+
|
|
95
|
+
async function pulse() {
|
|
96
|
+
while (!cancelled) {
|
|
97
|
+
await breathValue.set(1, { duration });
|
|
98
|
+
if (cancelled) break;
|
|
99
|
+
await breathValue.set(0, { duration });
|
|
100
|
+
if (cancelled) break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
pulse();
|
|
105
|
+
|
|
106
|
+
return () => {
|
|
107
|
+
cancelled = true;
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Expansion values for breathing animation (in SVG units, tied to viewBox 417×512.238)
|
|
112
|
+
// These are absolute values within the SVG coordinate system, so they scale
|
|
113
|
+
// proportionally with the logo regardless of rendered size.
|
|
114
|
+
const expansion = $derived($breathValue * 22);
|
|
115
|
+
const diagExpansion = $derived($breathValue * 16); // ~16px at 45° angles
|
|
116
|
+
|
|
117
|
+
// Individual branch transforms
|
|
118
|
+
const leftTransform = $derived(`translate(${-expansion}, 0)`);
|
|
119
|
+
const rightTransform = $derived(`translate(${expansion}, 0)`);
|
|
120
|
+
const topTransform = $derived(`translate(0, ${-expansion})`);
|
|
121
|
+
const topLeftTransform = $derived(`translate(${-diagExpansion}, ${-diagExpansion})`);
|
|
122
|
+
const topRightTransform = $derived(`translate(${diagExpansion}, ${-diagExpansion})`);
|
|
123
|
+
const bottomLeftTransform = $derived(`translate(${-diagExpansion}, ${diagExpansion})`);
|
|
124
|
+
const bottomRightTransform = $derived(`translate(${diagExpansion}, ${diagExpansion})`);
|
|
125
|
+
|
|
126
|
+
// Build animation classes (sway only, breathing uses transforms)
|
|
127
|
+
const animationClass = $derived(animate && !breathing ? 'grove-logo-sway' : '');
|
|
128
|
+
|
|
129
|
+
// Decomposed foliage paths (8 pieces) for breathing animation
|
|
130
|
+
// Original path: "M0 173.468h126.068l-89.622-85.44 49.591-50.985 85.439 87.829V0h74.086v124.872L331 37.243l49.552 50.785-89.58 85.24H417v70.502H290.252l90.183 87.629L331 381.192 208.519 258.11 86.037 381.192l-49.591-49.591 90.218-87.631H0v-70.502z"
|
|
131
|
+
// Decomposed by tracing path commands and isolating geometric boundaries where arms meet center.
|
|
132
|
+
// If modifying, ensure pieces align at rest (breathValue=0) to match the original silhouette.
|
|
133
|
+
|
|
134
|
+
// Center anchor - the hub where all branches connect (stays stationary)
|
|
135
|
+
const centerPath = "M126 173.468 L171.476 124.872 L171.476 173.468 L126 173.468 M245.562 124.872 L290.972 173.268 L245.562 173.268 L245.562 124.872 M126.664 243.97 L171.476 243.97 L171.476 173.468 L126 173.468 L126.664 243.97 M290.252 243.77 L245.562 243.77 L245.562 173.268 L290.972 173.268 L290.252 243.77 M171.476 243.97 L208.519 258.11 L245.562 243.77 L245.562 173.268 L171.476 173.468 L171.476 243.97";
|
|
136
|
+
|
|
137
|
+
// Left horizontal bar
|
|
138
|
+
const leftBarPath = "M0 173.468 L126 173.468 L126.664 243.97 L0 243.97 Z";
|
|
139
|
+
|
|
140
|
+
// Right horizontal bar
|
|
141
|
+
const rightBarPath = "M290.972 173.268 L417 173.268 L417 243.77 L290.252 243.77 Z";
|
|
142
|
+
|
|
143
|
+
// Top vertical bar
|
|
144
|
+
const topBarPath = "M171.476 0 L245.562 0 L245.562 124.872 L171.476 124.872 Z";
|
|
145
|
+
|
|
146
|
+
// Top-left diagonal branch (arrow shape)
|
|
147
|
+
const topLeftDiagPath = "M126.068 173.468 L36.446 88.028 L86.037 37.043 L171.476 124.872 Z";
|
|
148
|
+
|
|
149
|
+
// Top-right diagonal branch (arrow shape)
|
|
150
|
+
const topRightDiagPath = "M245.562 124.872 L331 37.243 L380.552 88.028 L290.972 173.268 Z";
|
|
151
|
+
|
|
152
|
+
// Bottom-left diagonal branch (arrow shape)
|
|
153
|
+
const bottomLeftDiagPath = "M126.664 243.97 L36.446 331.601 L86.037 381.192 L208.519 258.11 L171.476 243.97 Z";
|
|
154
|
+
|
|
155
|
+
// Bottom-right diagonal branch (arrow shape)
|
|
156
|
+
const bottomRightDiagPath = "M290.252 243.77 L380.435 331.399 L331 381.192 L208.519 258.11 L245.562 243.77 Z";
|
|
46
157
|
</script>
|
|
47
158
|
|
|
48
159
|
<svg
|
|
@@ -51,10 +162,53 @@
|
|
|
51
162
|
viewBox="0 0 417 512.238"
|
|
52
163
|
aria-label="Grove logo"
|
|
53
164
|
>
|
|
54
|
-
<!-- Trunk -->
|
|
165
|
+
<!-- Trunk (always static) -->
|
|
55
166
|
<path fill={actualTrunkColor} d="M171.274 344.942h74.09v167.296h-74.09V344.942z"/>
|
|
56
|
-
|
|
57
|
-
|
|
167
|
+
|
|
168
|
+
{#if breathing}
|
|
169
|
+
<!-- Decomposed foliage with breathing animation -->
|
|
170
|
+
|
|
171
|
+
<!-- Center anchor (stationary) -->
|
|
172
|
+
<path fill={foliageColor} d={centerPath}/>
|
|
173
|
+
|
|
174
|
+
<!-- Left horizontal bar -->
|
|
175
|
+
<g transform={leftTransform}>
|
|
176
|
+
<path fill={foliageColor} d={leftBarPath}/>
|
|
177
|
+
</g>
|
|
178
|
+
|
|
179
|
+
<!-- Right horizontal bar -->
|
|
180
|
+
<g transform={rightTransform}>
|
|
181
|
+
<path fill={foliageColor} d={rightBarPath}/>
|
|
182
|
+
</g>
|
|
183
|
+
|
|
184
|
+
<!-- Top vertical bar -->
|
|
185
|
+
<g transform={topTransform}>
|
|
186
|
+
<path fill={foliageColor} d={topBarPath}/>
|
|
187
|
+
</g>
|
|
188
|
+
|
|
189
|
+
<!-- Top-left diagonal -->
|
|
190
|
+
<g transform={topLeftTransform}>
|
|
191
|
+
<path fill={foliageColor} d={topLeftDiagPath}/>
|
|
192
|
+
</g>
|
|
193
|
+
|
|
194
|
+
<!-- Top-right diagonal -->
|
|
195
|
+
<g transform={topRightTransform}>
|
|
196
|
+
<path fill={foliageColor} d={topRightDiagPath}/>
|
|
197
|
+
</g>
|
|
198
|
+
|
|
199
|
+
<!-- Bottom-left diagonal -->
|
|
200
|
+
<g transform={bottomLeftTransform}>
|
|
201
|
+
<path fill={foliageColor} d={bottomLeftDiagPath}/>
|
|
202
|
+
</g>
|
|
203
|
+
|
|
204
|
+
<!-- Bottom-right diagonal -->
|
|
205
|
+
<g transform={bottomRightTransform}>
|
|
206
|
+
<path fill={foliageColor} d={bottomRightDiagPath}/>
|
|
207
|
+
</g>
|
|
208
|
+
{:else}
|
|
209
|
+
<!-- Original single foliage path (for non-breathing state) -->
|
|
210
|
+
<path fill={foliageColor} d="M0 173.468h126.068l-89.622-85.44 49.591-50.985 85.439 87.829V0h74.086v124.872L331 37.243l49.552 50.785-89.58 85.24H417v70.502H290.252l90.183 87.629L331 381.192 208.519 258.11 86.037 381.192l-49.591-49.591 90.218-87.631H0v-70.502z"/>
|
|
211
|
+
{/if}
|
|
58
212
|
</svg>
|
|
59
213
|
|
|
60
214
|
<style>
|
|
@@ -63,24 +217,8 @@
|
|
|
63
217
|
50% { transform: rotate(1deg); }
|
|
64
218
|
}
|
|
65
219
|
|
|
66
|
-
@keyframes grove-logo-breathe {
|
|
67
|
-
0%, 100% {
|
|
68
|
-
transform: scale(1);
|
|
69
|
-
opacity: 0.7;
|
|
70
|
-
}
|
|
71
|
-
50% {
|
|
72
|
-
transform: scale(1.05);
|
|
73
|
-
opacity: 1;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
220
|
.grove-logo-sway {
|
|
78
221
|
transform-origin: center bottom;
|
|
79
222
|
animation: grove-logo-sway 4s ease-in-out infinite;
|
|
80
223
|
}
|
|
81
|
-
|
|
82
|
-
.grove-logo-breathe {
|
|
83
|
-
transform-origin: center center;
|
|
84
|
-
animation: grove-logo-breathe 2s ease-in-out infinite;
|
|
85
|
-
}
|
|
86
224
|
</style>
|
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Grove Logo Component
|
|
3
|
-
*
|
|
4
|
-
* A logo that respects the user's accent color by default.
|
|
5
|
-
* The foliage uses `currentColor` which inherits from --accent-color
|
|
6
|
-
* when placed in an accent-colored context, or can be overridden.
|
|
7
|
-
*
|
|
8
|
-
* The trunk defaults to Grove's classic bark brown (#5d4037).
|
|
9
|
-
*/
|
|
1
|
+
type BreathingSpeed = 'slow' | 'normal' | 'fast';
|
|
10
2
|
interface Props {
|
|
11
3
|
class?: string;
|
|
12
4
|
/** Foliage color - defaults to currentColor (inherits accent) */
|
|
@@ -17,8 +9,10 @@ interface Props {
|
|
|
17
9
|
monochrome?: boolean;
|
|
18
10
|
/** Add subtle sway animation */
|
|
19
11
|
animate?: boolean;
|
|
20
|
-
/** Add breathing animation (for loading states) */
|
|
12
|
+
/** Add breathing animation (for loading states, not lists) */
|
|
21
13
|
breathing?: boolean;
|
|
14
|
+
/** Breathing animation speed - 'slow' (1500ms), 'normal' (800ms), 'fast' (400ms) */
|
|
15
|
+
breathingSpeed?: BreathingSpeed;
|
|
22
16
|
}
|
|
23
17
|
declare const Logo: import("svelte").Component<Props, {}, "">;
|
|
24
18
|
type Logo = ReturnType<typeof Logo>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grove Design System - Font Tokens
|
|
3
|
+
*
|
|
4
|
+
* Complete font catalog with metadata, CDN URLs, and helper utilities.
|
|
5
|
+
* All fonts are served from cdn.grove.place for optimal performance.
|
|
6
|
+
*/
|
|
7
|
+
/** CDN base URL for all font assets */
|
|
8
|
+
export declare const FONT_CDN_BASE = "https://cdn.grove.place/fonts";
|
|
9
|
+
/** Font category for organizing fonts */
|
|
10
|
+
export type FontCategory = "default" | "accessibility" | "sans-serif" | "serif" | "monospace" | "display";
|
|
11
|
+
/** Font format for @font-face src declarations */
|
|
12
|
+
export type FontFormat = "truetype" | "opentype";
|
|
13
|
+
/** Complete font definition with metadata */
|
|
14
|
+
export interface FontDefinition {
|
|
15
|
+
/** Unique identifier used in database and fontMap */
|
|
16
|
+
id: string;
|
|
17
|
+
/** Display name shown to users */
|
|
18
|
+
name: string;
|
|
19
|
+
/** Font file name on CDN */
|
|
20
|
+
file: string;
|
|
21
|
+
/** Font format (truetype or opentype) */
|
|
22
|
+
format: FontFormat;
|
|
23
|
+
/** CSS font-family name (may include spaces) */
|
|
24
|
+
fontFamily: string;
|
|
25
|
+
/** Category for organizing fonts */
|
|
26
|
+
category: FontCategory;
|
|
27
|
+
/** Brief description of the font's purpose/style */
|
|
28
|
+
description: string;
|
|
29
|
+
/** CSS fallback stack */
|
|
30
|
+
fallback: string[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Complete catalog of available fonts
|
|
34
|
+
* These fonts are served from cdn.grove.place
|
|
35
|
+
*/
|
|
36
|
+
export declare const fonts: readonly FontDefinition[];
|
|
37
|
+
/** All valid font IDs */
|
|
38
|
+
export type FontId = (typeof fonts)[number]["id"];
|
|
39
|
+
/** Map of font IDs to their definitions */
|
|
40
|
+
export declare const fontById: Record<FontId, FontDefinition>;
|
|
41
|
+
/** Get fonts by category */
|
|
42
|
+
export declare function getFontsByCategory(category: FontCategory): FontDefinition[];
|
|
43
|
+
/** Get CDN URL for a font */
|
|
44
|
+
export declare function getFontUrl(fontIdOrFile: string): string;
|
|
45
|
+
/** Get complete CSS font-family value with fallbacks */
|
|
46
|
+
export declare function getFontStack(fontId: FontId): string;
|
|
47
|
+
/**
|
|
48
|
+
* Generate @font-face CSS for a single font
|
|
49
|
+
* @param fontId - Font ID to generate CSS for
|
|
50
|
+
* @returns CSS @font-face declaration string
|
|
51
|
+
*/
|
|
52
|
+
export declare function generateFontFace(fontId: FontId): string;
|
|
53
|
+
/**
|
|
54
|
+
* Generate @font-face CSS for multiple fonts
|
|
55
|
+
* @param fontIds - Array of font IDs (defaults to all fonts)
|
|
56
|
+
* @returns CSS string with all @font-face declarations
|
|
57
|
+
*/
|
|
58
|
+
export declare function generateAllFontFaces(fontIds?: FontId[]): string;
|
|
59
|
+
/**
|
|
60
|
+
* Font map matching the format used in +layout.svelte
|
|
61
|
+
* Maps font IDs to their complete CSS font-family values
|
|
62
|
+
*/
|
|
63
|
+
export declare const fontMap: Record<FontId, string>;
|
|
64
|
+
/** Default font ID */
|
|
65
|
+
export declare const DEFAULT_FONT: FontId;
|
|
66
|
+
/** Total number of available fonts */
|
|
67
|
+
export declare const FONT_COUNT: number;
|
|
68
|
+
/** Font categories with human-readable labels */
|
|
69
|
+
export declare const fontCategoryLabels: Record<FontCategory, string>;
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grove Design System - Font Tokens
|
|
3
|
+
*
|
|
4
|
+
* Complete font catalog with metadata, CDN URLs, and helper utilities.
|
|
5
|
+
* All fonts are served from cdn.grove.place for optimal performance.
|
|
6
|
+
*/
|
|
7
|
+
/** CDN base URL for all font assets */
|
|
8
|
+
export const FONT_CDN_BASE = "https://cdn.grove.place/fonts";
|
|
9
|
+
/**
|
|
10
|
+
* Complete catalog of available fonts
|
|
11
|
+
* These fonts are served from cdn.grove.place
|
|
12
|
+
*/
|
|
13
|
+
export const fonts = [
|
|
14
|
+
// Default
|
|
15
|
+
{
|
|
16
|
+
id: "lexend",
|
|
17
|
+
name: "Lexend",
|
|
18
|
+
file: "Lexend-Regular.ttf",
|
|
19
|
+
format: "truetype",
|
|
20
|
+
fontFamily: "Lexend",
|
|
21
|
+
category: "default",
|
|
22
|
+
description: "Modern, highly readable sans-serif. Grove default.",
|
|
23
|
+
fallback: [
|
|
24
|
+
"-apple-system",
|
|
25
|
+
"BlinkMacSystemFont",
|
|
26
|
+
"Segoe UI",
|
|
27
|
+
"Roboto",
|
|
28
|
+
"sans-serif",
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
// Accessibility fonts
|
|
32
|
+
{
|
|
33
|
+
id: "atkinson",
|
|
34
|
+
name: "Atkinson Hyperlegible",
|
|
35
|
+
file: "AtkinsonHyperlegible-Regular.ttf",
|
|
36
|
+
format: "truetype",
|
|
37
|
+
fontFamily: "Atkinson Hyperlegible",
|
|
38
|
+
category: "accessibility",
|
|
39
|
+
description: "Designed for low vision readers. Maximum character distinction.",
|
|
40
|
+
fallback: [
|
|
41
|
+
"-apple-system",
|
|
42
|
+
"BlinkMacSystemFont",
|
|
43
|
+
"Segoe UI",
|
|
44
|
+
"Roboto",
|
|
45
|
+
"sans-serif",
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: "opendyslexic",
|
|
50
|
+
name: "OpenDyslexic",
|
|
51
|
+
file: "OpenDyslexic-Regular.otf",
|
|
52
|
+
format: "opentype",
|
|
53
|
+
fontFamily: "OpenDyslexic",
|
|
54
|
+
category: "accessibility",
|
|
55
|
+
description: "Weighted bottoms reduce letter confusion for dyslexic readers.",
|
|
56
|
+
fallback: [
|
|
57
|
+
"-apple-system",
|
|
58
|
+
"BlinkMacSystemFont",
|
|
59
|
+
"Segoe UI",
|
|
60
|
+
"Roboto",
|
|
61
|
+
"sans-serif",
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: "luciole",
|
|
66
|
+
name: "Luciole",
|
|
67
|
+
file: "Luciole-Regular.ttf",
|
|
68
|
+
format: "truetype",
|
|
69
|
+
fontFamily: "Luciole",
|
|
70
|
+
category: "accessibility",
|
|
71
|
+
description: "French accessibility font designed for visually impaired readers.",
|
|
72
|
+
fallback: [
|
|
73
|
+
"-apple-system",
|
|
74
|
+
"BlinkMacSystemFont",
|
|
75
|
+
"Segoe UI",
|
|
76
|
+
"Roboto",
|
|
77
|
+
"sans-serif",
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
// Modern sans-serif fonts
|
|
81
|
+
{
|
|
82
|
+
id: "nunito",
|
|
83
|
+
name: "Nunito",
|
|
84
|
+
file: "Nunito-Regular.ttf",
|
|
85
|
+
format: "truetype",
|
|
86
|
+
fontFamily: "Nunito",
|
|
87
|
+
category: "sans-serif",
|
|
88
|
+
description: "Friendly rounded sans-serif. Warm and approachable.",
|
|
89
|
+
fallback: [
|
|
90
|
+
"-apple-system",
|
|
91
|
+
"BlinkMacSystemFont",
|
|
92
|
+
"Segoe UI",
|
|
93
|
+
"Roboto",
|
|
94
|
+
"sans-serif",
|
|
95
|
+
],
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
id: "quicksand",
|
|
99
|
+
name: "Quicksand",
|
|
100
|
+
file: "Quicksand-Regular.ttf",
|
|
101
|
+
format: "truetype",
|
|
102
|
+
fontFamily: "Quicksand",
|
|
103
|
+
category: "sans-serif",
|
|
104
|
+
description: "Geometric sans with rounded terminals. Light and modern.",
|
|
105
|
+
fallback: [
|
|
106
|
+
"-apple-system",
|
|
107
|
+
"BlinkMacSystemFont",
|
|
108
|
+
"Segoe UI",
|
|
109
|
+
"Roboto",
|
|
110
|
+
"sans-serif",
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: "manrope",
|
|
115
|
+
name: "Manrope",
|
|
116
|
+
file: "Manrope-Regular.ttf",
|
|
117
|
+
format: "truetype",
|
|
118
|
+
fontFamily: "Manrope",
|
|
119
|
+
category: "sans-serif",
|
|
120
|
+
description: "Professional geometric sans. Clean and contemporary.",
|
|
121
|
+
fallback: [
|
|
122
|
+
"-apple-system",
|
|
123
|
+
"BlinkMacSystemFont",
|
|
124
|
+
"Segoe UI",
|
|
125
|
+
"Roboto",
|
|
126
|
+
"sans-serif",
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
id: "instrument-sans",
|
|
131
|
+
name: "Instrument Sans",
|
|
132
|
+
file: "InstrumentSans-Regular.ttf",
|
|
133
|
+
format: "truetype",
|
|
134
|
+
fontFamily: "Instrument Sans",
|
|
135
|
+
category: "sans-serif",
|
|
136
|
+
description: "Low contrast sans with humanist touches. Elegant simplicity.",
|
|
137
|
+
fallback: [
|
|
138
|
+
"-apple-system",
|
|
139
|
+
"BlinkMacSystemFont",
|
|
140
|
+
"Segoe UI",
|
|
141
|
+
"Roboto",
|
|
142
|
+
"sans-serif",
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
id: "plus-jakarta-sans",
|
|
147
|
+
name: "Plus Jakarta Sans",
|
|
148
|
+
file: "PlusJakartaSans-Regular.ttf",
|
|
149
|
+
format: "truetype",
|
|
150
|
+
fontFamily: "Plus Jakarta Sans",
|
|
151
|
+
category: "sans-serif",
|
|
152
|
+
description: "Contemporary geometric sans. Balanced and versatile.",
|
|
153
|
+
fallback: [
|
|
154
|
+
"-apple-system",
|
|
155
|
+
"BlinkMacSystemFont",
|
|
156
|
+
"Segoe UI",
|
|
157
|
+
"Roboto",
|
|
158
|
+
"sans-serif",
|
|
159
|
+
],
|
|
160
|
+
},
|
|
161
|
+
// Serif fonts
|
|
162
|
+
{
|
|
163
|
+
id: "cormorant",
|
|
164
|
+
name: "Cormorant",
|
|
165
|
+
file: "Cormorant-Regular.ttf",
|
|
166
|
+
format: "truetype",
|
|
167
|
+
fontFamily: "Cormorant",
|
|
168
|
+
category: "serif",
|
|
169
|
+
description: "Elegant display serif inspired by Garamond. Refined and classic.",
|
|
170
|
+
fallback: ["Georgia", "Times New Roman", "serif"],
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
id: "bodoni-moda",
|
|
174
|
+
name: "Bodoni Moda",
|
|
175
|
+
file: "BodoniModa-Regular.ttf",
|
|
176
|
+
format: "truetype",
|
|
177
|
+
fontFamily: "Bodoni Moda",
|
|
178
|
+
category: "serif",
|
|
179
|
+
description: "High contrast modern serif. Bold and sophisticated.",
|
|
180
|
+
fallback: ["Georgia", "Times New Roman", "serif"],
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
id: "lora",
|
|
184
|
+
name: "Lora",
|
|
185
|
+
file: "Lora-Regular.ttf",
|
|
186
|
+
format: "truetype",
|
|
187
|
+
fontFamily: "Lora",
|
|
188
|
+
category: "serif",
|
|
189
|
+
description: "Well-balanced contemporary serif. Excellent for body text.",
|
|
190
|
+
fallback: ["Georgia", "Times New Roman", "serif"],
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
id: "eb-garamond",
|
|
194
|
+
name: "EB Garamond",
|
|
195
|
+
file: "EBGaramond-Regular.ttf",
|
|
196
|
+
format: "truetype",
|
|
197
|
+
fontFamily: "EB Garamond",
|
|
198
|
+
category: "serif",
|
|
199
|
+
description: "Revival of classic Garamond. Timeless book typography.",
|
|
200
|
+
fallback: ["Georgia", "Times New Roman", "serif"],
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
id: "merriweather",
|
|
204
|
+
name: "Merriweather",
|
|
205
|
+
file: "Merriweather-Regular.ttf",
|
|
206
|
+
format: "truetype",
|
|
207
|
+
fontFamily: "Merriweather",
|
|
208
|
+
category: "serif",
|
|
209
|
+
description: "Designed for screen reading. Excellent legibility.",
|
|
210
|
+
fallback: ["Georgia", "Times New Roman", "serif"],
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
id: "fraunces",
|
|
214
|
+
name: "Fraunces",
|
|
215
|
+
file: "Fraunces-Regular.ttf",
|
|
216
|
+
format: "truetype",
|
|
217
|
+
fontFamily: "Fraunces",
|
|
218
|
+
category: "serif",
|
|
219
|
+
description: 'Soft serif with "wonky" optical axes. Warm personality.',
|
|
220
|
+
fallback: ["Georgia", "Times New Roman", "serif"],
|
|
221
|
+
},
|
|
222
|
+
// Monospace fonts
|
|
223
|
+
{
|
|
224
|
+
id: "ibm-plex-mono",
|
|
225
|
+
name: "IBM Plex Mono",
|
|
226
|
+
file: "IBMPlexMono-Regular.ttf",
|
|
227
|
+
format: "truetype",
|
|
228
|
+
fontFamily: "IBM Plex Mono",
|
|
229
|
+
category: "monospace",
|
|
230
|
+
description: "Corporate monospace with human warmth. Great for code.",
|
|
231
|
+
fallback: ["Courier New", "Consolas", "monospace"],
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
id: "cozette",
|
|
235
|
+
name: "Cozette",
|
|
236
|
+
file: "CozetteVector.ttf",
|
|
237
|
+
format: "truetype",
|
|
238
|
+
fontFamily: "Cozette",
|
|
239
|
+
category: "monospace",
|
|
240
|
+
description: "Bitmap-style vector font. Retro terminal aesthetic.",
|
|
241
|
+
fallback: ["Courier New", "Consolas", "monospace"],
|
|
242
|
+
},
|
|
243
|
+
// Display/special fonts
|
|
244
|
+
{
|
|
245
|
+
id: "alagard",
|
|
246
|
+
name: "Alagard",
|
|
247
|
+
file: "alagard.ttf",
|
|
248
|
+
format: "truetype",
|
|
249
|
+
fontFamily: "Alagard",
|
|
250
|
+
category: "display",
|
|
251
|
+
description: "Pixel art medieval display font. Fantasy and gaming.",
|
|
252
|
+
fallback: ["fantasy", "cursive"],
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
id: "calistoga",
|
|
256
|
+
name: "Calistoga",
|
|
257
|
+
file: "Calistoga-Regular.ttf",
|
|
258
|
+
format: "truetype",
|
|
259
|
+
fontFamily: "Calistoga",
|
|
260
|
+
category: "display",
|
|
261
|
+
description: "Casual brush serif. Friendly headlines.",
|
|
262
|
+
fallback: ["Georgia", "serif"],
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
id: "caveat",
|
|
266
|
+
name: "Caveat",
|
|
267
|
+
file: "Caveat-Regular.ttf",
|
|
268
|
+
format: "truetype",
|
|
269
|
+
fontFamily: "Caveat",
|
|
270
|
+
category: "display",
|
|
271
|
+
description: "Handwritten script. Personal and informal.",
|
|
272
|
+
fallback: ["cursive", "sans-serif"],
|
|
273
|
+
},
|
|
274
|
+
];
|
|
275
|
+
/** Map of font IDs to their definitions */
|
|
276
|
+
export const fontById = Object.fromEntries(fonts.map((font) => [font.id, font]));
|
|
277
|
+
/** Get fonts by category */
|
|
278
|
+
export function getFontsByCategory(category) {
|
|
279
|
+
return fonts.filter((font) => font.category === category);
|
|
280
|
+
}
|
|
281
|
+
/** Get CDN URL for a font */
|
|
282
|
+
export function getFontUrl(fontIdOrFile) {
|
|
283
|
+
// If it's a font ID, look up the file
|
|
284
|
+
const font = fonts.find((f) => f.id === fontIdOrFile);
|
|
285
|
+
const file = font ? font.file : fontIdOrFile;
|
|
286
|
+
return `${FONT_CDN_BASE}/${file}`;
|
|
287
|
+
}
|
|
288
|
+
/** Get complete CSS font-family value with fallbacks */
|
|
289
|
+
export function getFontStack(fontId) {
|
|
290
|
+
const font = fontById[fontId];
|
|
291
|
+
if (!font)
|
|
292
|
+
return fontById.lexend.fallback.join(", ");
|
|
293
|
+
const primary = font.fontFamily.includes(" ")
|
|
294
|
+
? `'${font.fontFamily}'`
|
|
295
|
+
: font.fontFamily;
|
|
296
|
+
return [primary, ...font.fallback].join(", ");
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Generate @font-face CSS for a single font
|
|
300
|
+
* @param fontId - Font ID to generate CSS for
|
|
301
|
+
* @returns CSS @font-face declaration string
|
|
302
|
+
*/
|
|
303
|
+
export function generateFontFace(fontId) {
|
|
304
|
+
const font = fontById[fontId];
|
|
305
|
+
if (!font)
|
|
306
|
+
return "";
|
|
307
|
+
return `@font-face {
|
|
308
|
+
font-family: '${font.fontFamily}';
|
|
309
|
+
src: url('${getFontUrl(font.file)}') format('${font.format}');
|
|
310
|
+
font-weight: normal;
|
|
311
|
+
font-style: normal;
|
|
312
|
+
font-display: swap;
|
|
313
|
+
}`;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Generate @font-face CSS for multiple fonts
|
|
317
|
+
* @param fontIds - Array of font IDs (defaults to all fonts)
|
|
318
|
+
* @returns CSS string with all @font-face declarations
|
|
319
|
+
*/
|
|
320
|
+
export function generateAllFontFaces(fontIds) {
|
|
321
|
+
const ids = fontIds ?? fonts.map((f) => f.id);
|
|
322
|
+
return ids.map(generateFontFace).filter(Boolean).join("\n\n");
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Font map matching the format used in +layout.svelte
|
|
326
|
+
* Maps font IDs to their complete CSS font-family values
|
|
327
|
+
*/
|
|
328
|
+
export const fontMap = Object.fromEntries(fonts.map((font) => [font.id, getFontStack(font.id)]));
|
|
329
|
+
/** Default font ID */
|
|
330
|
+
export const DEFAULT_FONT = "lexend";
|
|
331
|
+
/** Total number of available fonts */
|
|
332
|
+
export const FONT_COUNT = fonts.length;
|
|
333
|
+
/** Font categories with human-readable labels */
|
|
334
|
+
export const fontCategoryLabels = {
|
|
335
|
+
default: "Default",
|
|
336
|
+
accessibility: "Accessibility",
|
|
337
|
+
"sans-serif": "Sans-Serif",
|
|
338
|
+
serif: "Serif",
|
|
339
|
+
monospace: "Monospace",
|
|
340
|
+
display: "Display & Special",
|
|
341
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
5
|
-
export * from
|
|
1
|
+
export * from "./colors.js";
|
|
2
|
+
export * from "./typography.js";
|
|
3
|
+
export * from "./spacing.js";
|
|
4
|
+
export * from "./effects.js";
|
|
5
|
+
export * from "./animation.js";
|
|
6
|
+
export * from "./fonts.js";
|
|
6
7
|
export declare const TOKENS_VERSION = "0.2.0";
|