@autumnsgrove/groveengine 0.6.5 → 0.8.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 (183) hide show
  1. package/LICENSE +378 -0
  2. package/dist/auth/index.d.ts +1 -2
  3. package/dist/auth/index.js +8 -4
  4. package/dist/auth/session.d.ts +12 -31
  5. package/dist/auth/session.js +5 -103
  6. package/dist/components/custom/ContentWithGutter.svelte +22 -25
  7. package/dist/ui/components/content/RoadmapPreview.svelte +91 -0
  8. package/dist/ui/components/content/RoadmapPreview.svelte.d.ts +36 -0
  9. package/dist/ui/components/content/index.d.ts +1 -0
  10. package/dist/ui/components/content/index.js +1 -0
  11. package/dist/ui/components/nature/Logo.svelte +224 -0
  12. package/dist/ui/components/nature/Logo.svelte.d.ts +14 -0
  13. package/dist/ui/components/nature/botanical/Acorn.svelte +48 -0
  14. package/dist/ui/components/nature/botanical/Acorn.svelte.d.ts +8 -0
  15. package/dist/ui/components/nature/botanical/Berry.svelte +67 -0
  16. package/dist/ui/components/nature/botanical/Berry.svelte.d.ts +8 -0
  17. package/dist/ui/components/nature/botanical/DandelionPuff.svelte +98 -0
  18. package/dist/ui/components/nature/botanical/DandelionPuff.svelte.d.ts +8 -0
  19. package/dist/ui/components/nature/botanical/FallingLeavesLayer.svelte +170 -0
  20. package/dist/ui/components/nature/botanical/FallingLeavesLayer.svelte.d.ts +35 -0
  21. package/dist/ui/components/nature/botanical/FallingPetalsLayer.svelte +174 -0
  22. package/dist/ui/components/nature/botanical/FallingPetalsLayer.svelte.d.ts +25 -0
  23. package/dist/ui/components/nature/botanical/Leaf.svelte +77 -0
  24. package/dist/ui/components/nature/botanical/Leaf.svelte.d.ts +10 -0
  25. package/dist/ui/components/nature/botanical/LeafFalling.svelte +186 -0
  26. package/dist/ui/components/nature/botanical/LeafFalling.svelte.d.ts +22 -0
  27. package/dist/ui/components/nature/botanical/PetalFalling.svelte +266 -0
  28. package/dist/ui/components/nature/botanical/PetalFalling.svelte.d.ts +25 -0
  29. package/dist/ui/components/nature/botanical/PineCone.svelte +61 -0
  30. package/dist/ui/components/nature/botanical/PineCone.svelte.d.ts +7 -0
  31. package/dist/ui/components/nature/botanical/Vine.svelte +102 -0
  32. package/dist/ui/components/nature/botanical/Vine.svelte.d.ts +11 -0
  33. package/dist/ui/components/nature/botanical/index.d.ts +10 -0
  34. package/dist/ui/components/nature/botanical/index.js +11 -0
  35. package/dist/ui/components/nature/creatures/Bee.svelte +78 -0
  36. package/dist/ui/components/nature/creatures/Bee.svelte.d.ts +9 -0
  37. package/dist/ui/components/nature/creatures/Bird.svelte +94 -0
  38. package/dist/ui/components/nature/creatures/Bird.svelte.d.ts +11 -0
  39. package/dist/ui/components/nature/creatures/BirdFlying.svelte +83 -0
  40. package/dist/ui/components/nature/creatures/BirdFlying.svelte.d.ts +9 -0
  41. package/dist/ui/components/nature/creatures/Bluebird.svelte +95 -0
  42. package/dist/ui/components/nature/creatures/Bluebird.svelte.d.ts +12 -0
  43. package/dist/ui/components/nature/creatures/Butterfly.svelte +87 -0
  44. package/dist/ui/components/nature/creatures/Butterfly.svelte.d.ts +9 -0
  45. package/dist/ui/components/nature/creatures/Cardinal.svelte +95 -0
  46. package/dist/ui/components/nature/creatures/Cardinal.svelte.d.ts +12 -0
  47. package/dist/ui/components/nature/creatures/Chickadee.svelte +97 -0
  48. package/dist/ui/components/nature/creatures/Chickadee.svelte.d.ts +12 -0
  49. package/dist/ui/components/nature/creatures/Deer.svelte +95 -0
  50. package/dist/ui/components/nature/creatures/Deer.svelte.d.ts +9 -0
  51. package/dist/ui/components/nature/creatures/Firefly.svelte +111 -0
  52. package/dist/ui/components/nature/creatures/Firefly.svelte.d.ts +10 -0
  53. package/dist/ui/components/nature/creatures/Owl.svelte +91 -0
  54. package/dist/ui/components/nature/creatures/Owl.svelte.d.ts +9 -0
  55. package/dist/ui/components/nature/creatures/Rabbit.svelte +90 -0
  56. package/dist/ui/components/nature/creatures/Rabbit.svelte.d.ts +9 -0
  57. package/dist/ui/components/nature/creatures/Robin.svelte +98 -0
  58. package/dist/ui/components/nature/creatures/Robin.svelte.d.ts +12 -0
  59. package/dist/ui/components/nature/creatures/Squirrel.svelte +97 -0
  60. package/dist/ui/components/nature/creatures/Squirrel.svelte.d.ts +9 -0
  61. package/dist/ui/components/nature/creatures/index.d.ts +13 -0
  62. package/dist/ui/components/nature/creatures/index.js +14 -0
  63. package/dist/ui/components/nature/ground/Bush.svelte +57 -0
  64. package/dist/ui/components/nature/ground/Bush.svelte.d.ts +10 -0
  65. package/dist/ui/components/nature/ground/Crocus.svelte +83 -0
  66. package/dist/ui/components/nature/ground/Crocus.svelte.d.ts +12 -0
  67. package/dist/ui/components/nature/ground/Daffodil.svelte +75 -0
  68. package/dist/ui/components/nature/ground/Daffodil.svelte.d.ts +11 -0
  69. package/dist/ui/components/nature/ground/Fern.svelte +72 -0
  70. package/dist/ui/components/nature/ground/Fern.svelte.d.ts +10 -0
  71. package/dist/ui/components/nature/ground/FlowerWild.svelte +60 -0
  72. package/dist/ui/components/nature/ground/FlowerWild.svelte.d.ts +10 -0
  73. package/dist/ui/components/nature/ground/GrassTuft.svelte +49 -0
  74. package/dist/ui/components/nature/ground/GrassTuft.svelte.d.ts +10 -0
  75. package/dist/ui/components/nature/ground/Log.svelte +42 -0
  76. package/dist/ui/components/nature/ground/Log.svelte.d.ts +7 -0
  77. package/dist/ui/components/nature/ground/Mushroom.svelte +48 -0
  78. package/dist/ui/components/nature/ground/Mushroom.svelte.d.ts +9 -0
  79. package/dist/ui/components/nature/ground/MushroomCluster.svelte +41 -0
  80. package/dist/ui/components/nature/ground/MushroomCluster.svelte.d.ts +8 -0
  81. package/dist/ui/components/nature/ground/Rock.svelte +59 -0
  82. package/dist/ui/components/nature/ground/Rock.svelte.d.ts +8 -0
  83. package/dist/ui/components/nature/ground/Stump.svelte +44 -0
  84. package/dist/ui/components/nature/ground/Stump.svelte.d.ts +8 -0
  85. package/dist/ui/components/nature/ground/Tulip.svelte +79 -0
  86. package/dist/ui/components/nature/ground/Tulip.svelte.d.ts +11 -0
  87. package/dist/ui/components/nature/ground/index.d.ts +12 -0
  88. package/dist/ui/components/nature/ground/index.js +13 -0
  89. package/dist/ui/components/nature/index.d.ts +28 -0
  90. package/dist/ui/components/nature/index.js +38 -0
  91. package/dist/ui/components/nature/palette.d.ts +491 -0
  92. package/dist/ui/components/nature/palette.js +384 -0
  93. package/dist/ui/components/nature/sky/Cloud.svelte +122 -0
  94. package/dist/ui/components/nature/sky/Cloud.svelte.d.ts +11 -0
  95. package/dist/ui/components/nature/sky/CloudWispy.svelte +79 -0
  96. package/dist/ui/components/nature/sky/CloudWispy.svelte.d.ts +9 -0
  97. package/dist/ui/components/nature/sky/Moon.svelte +60 -0
  98. package/dist/ui/components/nature/sky/Moon.svelte.d.ts +9 -0
  99. package/dist/ui/components/nature/sky/Rainbow.svelte +101 -0
  100. package/dist/ui/components/nature/sky/Rainbow.svelte.d.ts +8 -0
  101. package/dist/ui/components/nature/sky/Star.svelte +84 -0
  102. package/dist/ui/components/nature/sky/Star.svelte.d.ts +10 -0
  103. package/dist/ui/components/nature/sky/StarCluster.svelte +85 -0
  104. package/dist/ui/components/nature/sky/StarCluster.svelte.d.ts +9 -0
  105. package/dist/ui/components/nature/sky/StarShooting.svelte +90 -0
  106. package/dist/ui/components/nature/sky/StarShooting.svelte.d.ts +9 -0
  107. package/dist/ui/components/nature/sky/Sun.svelte +70 -0
  108. package/dist/ui/components/nature/sky/Sun.svelte.d.ts +9 -0
  109. package/dist/ui/components/nature/sky/index.d.ts +8 -0
  110. package/dist/ui/components/nature/sky/index.js +9 -0
  111. package/dist/ui/components/nature/structural/Birdhouse.svelte +53 -0
  112. package/dist/ui/components/nature/structural/Birdhouse.svelte.d.ts +8 -0
  113. package/dist/ui/components/nature/structural/Bridge.svelte +65 -0
  114. package/dist/ui/components/nature/structural/Bridge.svelte.d.ts +7 -0
  115. package/dist/ui/components/nature/structural/FencePost.svelte +54 -0
  116. package/dist/ui/components/nature/structural/FencePost.svelte.d.ts +8 -0
  117. package/dist/ui/components/nature/structural/GardenGate.svelte +70 -0
  118. package/dist/ui/components/nature/structural/GardenGate.svelte.d.ts +8 -0
  119. package/dist/ui/components/nature/structural/Lantern.svelte +113 -0
  120. package/dist/ui/components/nature/structural/Lantern.svelte.d.ts +10 -0
  121. package/dist/ui/components/nature/structural/Lattice.svelte +89 -0
  122. package/dist/ui/components/nature/structural/Lattice.svelte.d.ts +8 -0
  123. package/dist/ui/components/nature/structural/LatticeWithVine.svelte +89 -0
  124. package/dist/ui/components/nature/structural/LatticeWithVine.svelte.d.ts +11 -0
  125. package/dist/ui/components/nature/structural/StonePath.svelte +48 -0
  126. package/dist/ui/components/nature/structural/StonePath.svelte.d.ts +7 -0
  127. package/dist/ui/components/nature/structural/index.d.ts +8 -0
  128. package/dist/ui/components/nature/structural/index.js +9 -0
  129. package/dist/ui/components/nature/trees/TreeAspen.svelte +163 -0
  130. package/dist/ui/components/nature/trees/TreeAspen.svelte.d.ts +11 -0
  131. package/dist/ui/components/nature/trees/TreeBirch.svelte +186 -0
  132. package/dist/ui/components/nature/trees/TreeBirch.svelte.d.ts +11 -0
  133. package/dist/ui/components/nature/trees/TreeCherry.svelte +108 -0
  134. package/dist/ui/components/nature/trees/TreeCherry.svelte.d.ts +11 -0
  135. package/dist/ui/components/nature/trees/TreePine.svelte +79 -0
  136. package/dist/ui/components/nature/trees/TreePine.svelte.d.ts +11 -0
  137. package/dist/ui/components/nature/trees/index.d.ts +4 -0
  138. package/dist/ui/components/nature/trees/index.js +5 -0
  139. package/dist/ui/components/nature/water/LilyPad.svelte +99 -0
  140. package/dist/ui/components/nature/water/LilyPad.svelte.d.ts +10 -0
  141. package/dist/ui/components/nature/water/Pond.svelte +104 -0
  142. package/dist/ui/components/nature/water/Pond.svelte.d.ts +8 -0
  143. package/dist/ui/components/nature/water/Reeds.svelte +85 -0
  144. package/dist/ui/components/nature/water/Reeds.svelte.d.ts +11 -0
  145. package/dist/ui/components/nature/water/Stream.svelte +98 -0
  146. package/dist/ui/components/nature/water/Stream.svelte.d.ts +8 -0
  147. package/dist/ui/components/nature/water/index.d.ts +4 -0
  148. package/dist/ui/components/nature/water/index.js +5 -0
  149. package/dist/ui/components/nature/weather/SnowfallLayer.svelte +175 -0
  150. package/dist/ui/components/nature/weather/SnowfallLayer.svelte.d.ts +25 -0
  151. package/dist/ui/components/nature/weather/Snowflake.svelte +99 -0
  152. package/dist/ui/components/nature/weather/Snowflake.svelte.d.ts +11 -0
  153. package/dist/ui/components/nature/weather/SnowflakeFalling.svelte +162 -0
  154. package/dist/ui/components/nature/weather/SnowflakeFalling.svelte.d.ts +23 -0
  155. package/dist/ui/components/nature/weather/index.d.ts +3 -0
  156. package/dist/ui/components/nature/weather/index.js +4 -0
  157. package/dist/ui/components/ui/Glass.svelte +158 -0
  158. package/dist/ui/components/ui/Glass.svelte.d.ts +52 -0
  159. package/dist/ui/components/ui/GlassButton.svelte +157 -0
  160. package/dist/ui/components/ui/GlassButton.svelte.d.ts +39 -0
  161. package/dist/ui/components/ui/GlassCard.svelte +160 -0
  162. package/dist/ui/components/ui/GlassCard.svelte.d.ts +39 -0
  163. package/dist/ui/components/ui/GlassConfirmDialog.svelte +208 -0
  164. package/dist/ui/components/ui/GlassConfirmDialog.svelte.d.ts +52 -0
  165. package/dist/ui/components/ui/GlassLogo.svelte +422 -0
  166. package/dist/ui/components/ui/GlassLogo.svelte.d.ts +23 -0
  167. package/dist/ui/components/ui/GlassNavbar.svelte +120 -0
  168. package/dist/ui/components/ui/GlassNavbar.svelte.d.ts +42 -0
  169. package/dist/ui/components/ui/GlassOverlay.svelte +93 -0
  170. package/dist/ui/components/ui/GlassOverlay.svelte.d.ts +33 -0
  171. package/dist/ui/components/ui/Logo.svelte +47 -52
  172. package/dist/ui/components/ui/Logo.svelte.d.ts +4 -3
  173. package/dist/ui/components/ui/index.d.ts +7 -0
  174. package/dist/ui/components/ui/index.js +8 -0
  175. package/dist/ui/styles/grove.css +151 -1
  176. package/dist/utils/gutter.d.ts +2 -8
  177. package/dist/utils/markdown.d.ts +1 -0
  178. package/dist/utils/markdown.js +32 -11
  179. package/package.json +31 -22
  180. package/static/fonts/alagard.ttf +0 -0
  181. package/static/robots.txt +34 -1
  182. package/dist/auth/jwt.d.ts +0 -20
  183. package/dist/auth/jwt.js +0 -123
@@ -0,0 +1,99 @@
1
+ <!--
2
+ Grove — A place to Be
3
+ Copyright (c) 2025 Autumn Brown
4
+ Licensed under AGPL-3.0
5
+ -->
6
+ <script lang="ts">
7
+ import { winter } from '../palette';
8
+
9
+ type SnowflakeVariant = 'crystal' | 'simple' | 'star' | 'delicate' | 'dot';
10
+
11
+ interface Props {
12
+ class?: string;
13
+ color?: string;
14
+ variant?: SnowflakeVariant;
15
+ /** Opacity for depth effect */
16
+ opacity?: number;
17
+ }
18
+
19
+ let {
20
+ class: className = 'w-4 h-4',
21
+ color = winter.snow,
22
+ variant = 'crystal',
23
+ opacity = 1
24
+ }: Props = $props();
25
+ </script>
26
+
27
+ <!-- Snowflake - various crystalline shapes -->
28
+ <svg
29
+ class={className}
30
+ xmlns="http://www.w3.org/2000/svg"
31
+ viewBox="0 0 40 40"
32
+ style="opacity: {opacity};"
33
+ >
34
+ {#if variant === 'crystal'}
35
+ <!-- Classic 6-pointed crystal snowflake -->
36
+ <g fill="none" stroke={color} stroke-width="1.5" stroke-linecap="round">
37
+ <!-- Main axes -->
38
+ <line x1="20" y1="4" x2="20" y2="36" />
39
+ <line x1="6" y1="12" x2="34" y2="28" />
40
+ <line x1="6" y1="28" x2="34" y2="12" />
41
+ <!-- Branch details on vertical axis -->
42
+ <line x1="20" y1="8" x2="16" y2="12" />
43
+ <line x1="20" y1="8" x2="24" y2="12" />
44
+ <line x1="20" y1="32" x2="16" y2="28" />
45
+ <line x1="20" y1="32" x2="24" y2="28" />
46
+ <!-- Branch details on diagonal axes -->
47
+ <line x1="10" y1="14" x2="12" y2="10" />
48
+ <line x1="10" y1="14" x2="8" y2="18" />
49
+ <line x1="30" y1="26" x2="28" y2="30" />
50
+ <line x1="30" y1="26" x2="32" y2="22" />
51
+ <line x1="10" y1="26" x2="12" y2="30" />
52
+ <line x1="10" y1="26" x2="8" y2="22" />
53
+ <line x1="30" y1="14" x2="28" y2="10" />
54
+ <line x1="30" y1="14" x2="32" y2="18" />
55
+ </g>
56
+ <!-- Center crystal -->
57
+ <circle fill={color} cx="20" cy="20" r="2" />
58
+ {:else if variant === 'simple'}
59
+ <!-- Simple 6-pointed star snowflake -->
60
+ <g fill="none" stroke={color} stroke-width="2" stroke-linecap="round">
61
+ <line x1="20" y1="5" x2="20" y2="35" />
62
+ <line x1="7" y1="12.5" x2="33" y2="27.5" />
63
+ <line x1="7" y1="27.5" x2="33" y2="12.5" />
64
+ </g>
65
+ <circle fill={color} cx="20" cy="20" r="3" />
66
+ {:else if variant === 'star'}
67
+ <!-- Decorative star snowflake with filled points -->
68
+ <polygon
69
+ fill={color}
70
+ points="20,2 22,15 35,12 25,20 35,28 22,25 20,38 18,25 5,28 15,20 5,12 18,15"
71
+ />
72
+ <circle fill={color} cx="20" cy="20" r="4" opacity="0.8" />
73
+ {:else if variant === 'delicate'}
74
+ <!-- Delicate branching snowflake -->
75
+ <g fill="none" stroke={color} stroke-width="1" stroke-linecap="round">
76
+ <!-- Main spokes -->
77
+ <line x1="20" y1="4" x2="20" y2="36" />
78
+ <line x1="6" y1="12" x2="34" y2="28" />
79
+ <line x1="6" y1="28" x2="34" y2="12" />
80
+ <!-- Delicate inner branches -->
81
+ <line x1="20" y1="10" x2="14" y2="13" />
82
+ <line x1="20" y1="10" x2="26" y2="13" />
83
+ <line x1="20" y1="30" x2="14" y2="27" />
84
+ <line x1="20" y1="30" x2="26" y2="27" />
85
+ <!-- Tiny end crystals -->
86
+ <circle cx="20" cy="4" r="1.5" fill={color} />
87
+ <circle cx="20" cy="36" r="1.5" fill={color} />
88
+ <circle cx="6" cy="12" r="1.5" fill={color} />
89
+ <circle cx="34" cy="28" r="1.5" fill={color} />
90
+ <circle cx="6" cy="28" r="1.5" fill={color} />
91
+ <circle cx="34" cy="12" r="1.5" fill={color} />
92
+ </g>
93
+ <circle fill={color} cx="20" cy="20" r="2.5" />
94
+ {:else if variant === 'dot'}
95
+ <!-- Simple snow dot/orb (for distant flakes) -->
96
+ <circle fill={color} cx="20" cy="20" r="8" />
97
+ <circle fill="white" cx="17" cy="17" r="2" opacity="0.6" />
98
+ {/if}
99
+ </svg>
@@ -0,0 +1,11 @@
1
+ type SnowflakeVariant = 'crystal' | 'simple' | 'star' | 'delicate' | 'dot';
2
+ interface Props {
3
+ class?: string;
4
+ color?: string;
5
+ variant?: SnowflakeVariant;
6
+ /** Opacity for depth effect */
7
+ opacity?: number;
8
+ }
9
+ declare const Snowflake: import("svelte").Component<Props, {}, "">;
10
+ type Snowflake = ReturnType<typeof Snowflake>;
11
+ export default Snowflake;
@@ -0,0 +1,162 @@
1
+ <!--
2
+ Grove — A place to Be
3
+ Copyright (c) 2025 Autumn Brown
4
+ Licensed under AGPL-3.0
5
+ -->
6
+ <script lang="ts">
7
+ import { winter } from '../palette';
8
+ import Snowflake from './Snowflake.svelte';
9
+ import { browser } from '$app/environment';
10
+
11
+ type SnowflakeVariant = 'crystal' | 'simple' | 'star' | 'delicate' | 'dot';
12
+
13
+ interface Props {
14
+ class?: string;
15
+ color?: string;
16
+ variant?: SnowflakeVariant;
17
+ /** Fall animation duration in seconds */
18
+ duration?: number;
19
+ /** Animation delay in seconds */
20
+ delay?: number;
21
+ /** Horizontal drift amount in vw units */
22
+ drift?: number;
23
+ /** Vertical fall distance in vh units */
24
+ fallDistance?: number;
25
+ /** Enable falling animation */
26
+ animate?: boolean;
27
+ /** Random seed for rotation variation */
28
+ seed?: number;
29
+ /** Opacity for depth effect */
30
+ opacity?: number;
31
+ }
32
+
33
+ let {
34
+ class: className = 'w-4 h-4',
35
+ color = winter.snow,
36
+ variant = 'crystal',
37
+ duration = 12,
38
+ delay = 0,
39
+ drift = 0,
40
+ fallDistance = 100,
41
+ animate = true,
42
+ seed = 0,
43
+ opacity = 1
44
+ }: Props = $props();
45
+
46
+ // Deterministic rotation based on seed - gentle swaying for snow
47
+ const initialRotation = $derived((seed * 37) % 360);
48
+ const rotationDirection = $derived(seed % 2 === 0 ? 1 : -1);
49
+ const rotationAmount = $derived(180 + (seed % 180)); // 180-360 degrees total rotation - gentler than petals
50
+
51
+ // Gentle horizontal sway - snow drifts more subtly than petals
52
+ const swayAmplitude = $derived(40 + (seed % 60)); // 40-100px horizontal sway - lighter than petals
53
+ const swayFrequency = $derived(1 + (seed % 3)); // 1-3 complete waves during fall - fewer than petals
54
+
55
+ // Unique animation name to prevent conflicts
56
+ const animId = $derived(`snow-${seed}`);
57
+
58
+ // Inject dynamic keyframes at runtime
59
+ $effect(() => {
60
+ if (!browser || !animate) return;
61
+
62
+ const styleId = `snow-style-${seed}`;
63
+ // Check if already injected
64
+ if (document.getElementById(styleId)) return;
65
+
66
+ const style = document.createElement('style');
67
+ style.id = styleId;
68
+ // Create gentle drifting motion with sine wave horizontal movement
69
+ // Snowflakes are lighter and more delicate than petals
70
+ style.textContent = `
71
+ @keyframes ${animId}-fall {
72
+ 0% {
73
+ transform: translateY(0) translateX(0) rotate(${initialRotation}deg);
74
+ opacity: 0;
75
+ }
76
+ 5% {
77
+ opacity: ${opacity};
78
+ }
79
+ 12.5% {
80
+ transform: translateY(${fallDistance * 0.125}vh) translateX(${Math.sin(Math.PI * 0.25 * swayFrequency) * swayAmplitude + drift * 0.125}px) rotate(${initialRotation + rotationAmount * rotationDirection * 0.125}deg);
81
+ }
82
+ 25% {
83
+ transform: translateY(${fallDistance * 0.25}vh) translateX(${Math.sin(Math.PI * 0.5 * swayFrequency) * swayAmplitude + drift * 0.25}px) rotate(${initialRotation + rotationAmount * rotationDirection * 0.25}deg);
84
+ }
85
+ 37.5% {
86
+ transform: translateY(${fallDistance * 0.375}vh) translateX(${Math.sin(Math.PI * 0.75 * swayFrequency) * swayAmplitude + drift * 0.375}px) rotate(${initialRotation + rotationAmount * rotationDirection * 0.375}deg);
87
+ }
88
+ 50% {
89
+ transform: translateY(${fallDistance * 0.5}vh) translateX(${Math.sin(Math.PI * swayFrequency) * swayAmplitude + drift * 0.5}px) rotate(${initialRotation + rotationAmount * rotationDirection * 0.5}deg);
90
+ }
91
+ 62.5% {
92
+ transform: translateY(${fallDistance * 0.625}vh) translateX(${Math.sin(Math.PI * 1.25 * swayFrequency) * swayAmplitude + drift * 0.625}px) rotate(${initialRotation + rotationAmount * rotationDirection * 0.625}deg);
93
+ }
94
+ 75% {
95
+ transform: translateY(${fallDistance * 0.75}vh) translateX(${Math.sin(Math.PI * 1.5 * swayFrequency) * swayAmplitude + drift * 0.75}px) rotate(${initialRotation + rotationAmount * rotationDirection * 0.75}deg);
96
+ }
97
+ 87.5% {
98
+ transform: translateY(${fallDistance * 0.875}vh) translateX(${Math.sin(Math.PI * 1.75 * swayFrequency) * swayAmplitude + drift * 0.875}px) rotate(${initialRotation + rotationAmount * rotationDirection * 0.875}deg);
99
+ }
100
+ 95% {
101
+ opacity: ${opacity};
102
+ }
103
+ 100% {
104
+ transform: translateY(${fallDistance}vh) translateX(${Math.sin(Math.PI * 2 * swayFrequency) * swayAmplitude + drift}px) rotate(${initialRotation + rotationAmount * rotationDirection}deg);
105
+ opacity: 0;
106
+ }
107
+ }
108
+ @keyframes ${animId}-flutter {
109
+ 0%, 100% {
110
+ transform: rotateX(0deg) rotateY(0deg) scale(1);
111
+ }
112
+ 20% {
113
+ transform: rotateX(20deg) rotateY(25deg) scale(0.95);
114
+ }
115
+ 40% {
116
+ transform: rotateX(-15deg) rotateY(-20deg) scale(1.05);
117
+ }
118
+ 60% {
119
+ transform: rotateX(25deg) rotateY(20deg) scale(0.92);
120
+ }
121
+ 80% {
122
+ transform: rotateX(-20deg) rotateY(-25deg) scale(1.03);
123
+ }
124
+ }
125
+ `;
126
+ document.head.appendChild(style);
127
+
128
+ return () => {
129
+ // Cleanup on component destroy
130
+ const el = document.getElementById(styleId);
131
+ if (el) el.remove();
132
+ };
133
+ });
134
+ </script>
135
+
136
+ <!-- Snowflake - delicate and drifting -->
137
+ <!-- Wrapper div for fall animation (translateY/X/rotate), inner div for flutter (3D rotations) -->
138
+ <div
139
+ class="snowflake-wrapper"
140
+ style="{animate ? `animation: ${animId}-fall ${duration}s linear ${delay}s infinite;` : `opacity: ${opacity};`}"
141
+ >
142
+ <div
143
+ class="snowflake-flutter"
144
+ style="{animate ? `animation: ${animId}-flutter ${duration / 3}s ease-in-out ${delay}s infinite;` : ''}"
145
+ >
146
+ <Snowflake class={className} {color} {variant} opacity={1} />
147
+ </div>
148
+ </div>
149
+
150
+ <style>
151
+ /* Dynamic keyframes are injected via $effect */
152
+ .snowflake-wrapper {
153
+ display: inline-block;
154
+ will-change: transform, opacity;
155
+ }
156
+
157
+ .snowflake-flutter {
158
+ display: inline-block;
159
+ transform-style: preserve-3d;
160
+ will-change: transform;
161
+ }
162
+ </style>
@@ -0,0 +1,23 @@
1
+ type SnowflakeVariant = 'crystal' | 'simple' | 'star' | 'delicate' | 'dot';
2
+ interface Props {
3
+ class?: string;
4
+ color?: string;
5
+ variant?: SnowflakeVariant;
6
+ /** Fall animation duration in seconds */
7
+ duration?: number;
8
+ /** Animation delay in seconds */
9
+ delay?: number;
10
+ /** Horizontal drift amount in vw units */
11
+ drift?: number;
12
+ /** Vertical fall distance in vh units */
13
+ fallDistance?: number;
14
+ /** Enable falling animation */
15
+ animate?: boolean;
16
+ /** Random seed for rotation variation */
17
+ seed?: number;
18
+ /** Opacity for depth effect */
19
+ opacity?: number;
20
+ }
21
+ declare const SnowflakeFalling: import("svelte").Component<Props, {}, "">;
22
+ type SnowflakeFalling = ReturnType<typeof SnowflakeFalling>;
23
+ export default SnowflakeFalling;
@@ -0,0 +1,3 @@
1
+ export { default as SnowfallLayer } from './SnowfallLayer.svelte';
2
+ export { default as Snowflake } from './Snowflake.svelte';
3
+ export { default as SnowflakeFalling } from './SnowflakeFalling.svelte';
@@ -0,0 +1,4 @@
1
+ // Weather components - seasonal weather effects
2
+ export { default as SnowfallLayer } from './SnowfallLayer.svelte';
3
+ export { default as Snowflake } from './Snowflake.svelte';
4
+ export { default as SnowflakeFalling } from './SnowflakeFalling.svelte';
@@ -0,0 +1,158 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import type { HTMLAttributes } from "svelte/elements";
4
+ import { cn } from "../../utils";
5
+
6
+ /**
7
+ * Glass component for creating glassmorphism effects
8
+ *
9
+ * A reusable component that provides translucent, frosted-glass effects
10
+ * perfect for overlays, cards, navbars, and text containers while
11
+ * maintaining visibility of background elements.
12
+ *
13
+ * @example Basic usage
14
+ * ```svelte
15
+ * <Glass variant="card">
16
+ * <p>Content with glass background</p>
17
+ * </Glass>
18
+ * ```
19
+ *
20
+ * @example As a navbar
21
+ * ```svelte
22
+ * <Glass variant="surface" as="header" class="sticky top-0">
23
+ * <nav>...</nav>
24
+ * </Glass>
25
+ * ```
26
+ *
27
+ * @example Accent tint for callouts
28
+ * ```svelte
29
+ * <Glass variant="accent" intensity="light" class="p-6 rounded-xl">
30
+ * <p>Important message here</p>
31
+ * </Glass>
32
+ * ```
33
+ */
34
+
35
+ type Variant =
36
+ | "surface" // Headers, navbars - high opacity, subtle blur
37
+ | "overlay" // Modal backdrops - dark, medium blur
38
+ | "card" // Content cards - medium opacity, clean look
39
+ | "tint" // Text containers - light background for readability
40
+ | "accent" // Accent-colored glass for callouts/highlights
41
+ | "muted"; // Subtle background, barely visible
42
+
43
+ type Intensity =
44
+ | "none" // No blur (just transparency)
45
+ | "light" // backdrop-blur-sm (4px)
46
+ | "medium" // backdrop-blur (8px)
47
+ | "strong"; // backdrop-blur-md (12px)
48
+
49
+ type Element = "div" | "section" | "article" | "aside" | "header" | "footer" | "nav" | "main";
50
+
51
+ interface Props extends HTMLAttributes<HTMLElement> {
52
+ /** Visual style variant */
53
+ variant?: Variant;
54
+ /** Blur intensity */
55
+ intensity?: Intensity;
56
+ /** HTML element to render */
57
+ as?: Element;
58
+ /** Include subtle border */
59
+ border?: boolean;
60
+ /** Include shadow */
61
+ shadow?: boolean;
62
+ /** Additional CSS classes */
63
+ class?: string;
64
+ /** Content */
65
+ children?: Snippet;
66
+ }
67
+
68
+ let {
69
+ variant = "card",
70
+ intensity = "light",
71
+ as: element = "div",
72
+ border = true,
73
+ shadow = false,
74
+ class: className,
75
+ children,
76
+ ...restProps
77
+ }: Props = $props();
78
+
79
+ // Background colors per variant - warm grove tones, translucent for glass effect
80
+ const variantClasses: Record<Variant, string> = {
81
+ // High opacity for sticky headers/navbars (uses background color)
82
+ surface: "bg-background/90 dark:bg-background/90",
83
+
84
+ // Dark overlay for modals/sheets
85
+ overlay: "bg-black/50 dark:bg-black/60",
86
+
87
+ // Medium opacity for content cards - translucent with grove warmth
88
+ card: "bg-white/60 dark:bg-emerald-950/25",
89
+
90
+ // Light tint for text readability
91
+ tint: "bg-white/50 dark:bg-emerald-950/20",
92
+
93
+ // Accent-colored glass for highlights/callouts
94
+ accent: "bg-accent/25 dark:bg-accent/15",
95
+
96
+ // Barely visible, very subtle
97
+ muted: "bg-white/30 dark:bg-emerald-950/15"
98
+ };
99
+
100
+ // Blur intensity classes - default to medium blur for true glass effect
101
+ const intensityClasses: Record<Intensity, string> = {
102
+ none: "",
103
+ light: "backdrop-blur", // 8px
104
+ medium: "backdrop-blur-md", // 12px
105
+ strong: "backdrop-blur-lg" // 16px
106
+ };
107
+
108
+ // Border classes per variant - subtle borders that complement the glass
109
+ const borderClasses: Record<Variant, string> = {
110
+ surface: "border-border",
111
+ overlay: "border-white/10",
112
+ card: "border-white/40 dark:border-emerald-800/25",
113
+ tint: "border-white/30 dark:border-emerald-800/20",
114
+ accent: "border-accent/30 dark:border-accent/20",
115
+ muted: "border-white/20 dark:border-emerald-800/15"
116
+ };
117
+
118
+ // Shadow classes
119
+ const shadowClasses: Record<Variant, string> = {
120
+ surface: "shadow-sm",
121
+ overlay: "shadow-2xl",
122
+ card: "shadow-sm",
123
+ tint: "shadow-sm",
124
+ accent: "shadow-sm",
125
+ muted: ""
126
+ };
127
+
128
+ const computedClass = $derived(
129
+ cn(
130
+ variantClasses[variant],
131
+ intensityClasses[intensity],
132
+ border && `border ${borderClasses[variant]}`,
133
+ shadow && shadowClasses[variant],
134
+ className
135
+ )
136
+ );
137
+ </script>
138
+
139
+ <!--
140
+ Glassmorphism component for Grove
141
+
142
+ CSS Properties used:
143
+ - backdrop-filter: blur() - Creates the frosted glass effect
144
+ - Background with alpha - Semi-transparent backgrounds (e.g., bg-white/80)
145
+ - Border with alpha - Subtle borders that complement the glass effect
146
+
147
+ Browser Support:
148
+ - backdrop-filter is supported in all modern browsers
149
+ - Falls back gracefully to solid backgrounds in older browsers
150
+ -->
151
+
152
+ <svelte:element
153
+ this={element}
154
+ class={computedClass}
155
+ {...restProps}
156
+ >
157
+ {#if children}{@render children()}{/if}
158
+ </svelte:element>
@@ -0,0 +1,52 @@
1
+ import type { Snippet } from "svelte";
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+ /**
4
+ * Glass component for creating glassmorphism effects
5
+ *
6
+ * A reusable component that provides translucent, frosted-glass effects
7
+ * perfect for overlays, cards, navbars, and text containers while
8
+ * maintaining visibility of background elements.
9
+ *
10
+ * @example Basic usage
11
+ * ```svelte
12
+ * <Glass variant="card">
13
+ * <p>Content with glass background</p>
14
+ * </Glass>
15
+ * ```
16
+ *
17
+ * @example As a navbar
18
+ * ```svelte
19
+ * <Glass variant="surface" as="header" class="sticky top-0">
20
+ * <nav>...</nav>
21
+ * </Glass>
22
+ * ```
23
+ *
24
+ * @example Accent tint for callouts
25
+ * ```svelte
26
+ * <Glass variant="accent" intensity="light" class="p-6 rounded-xl">
27
+ * <p>Important message here</p>
28
+ * </Glass>
29
+ * ```
30
+ */
31
+ type Variant = "surface" | "overlay" | "card" | "tint" | "accent" | "muted";
32
+ type Intensity = "none" | "light" | "medium" | "strong";
33
+ type Element = "div" | "section" | "article" | "aside" | "header" | "footer" | "nav" | "main";
34
+ interface Props extends HTMLAttributes<HTMLElement> {
35
+ /** Visual style variant */
36
+ variant?: Variant;
37
+ /** Blur intensity */
38
+ intensity?: Intensity;
39
+ /** HTML element to render */
40
+ as?: Element;
41
+ /** Include subtle border */
42
+ border?: boolean;
43
+ /** Include shadow */
44
+ shadow?: boolean;
45
+ /** Additional CSS classes */
46
+ class?: string;
47
+ /** Content */
48
+ children?: Snippet;
49
+ }
50
+ declare const Glass: import("svelte").Component<Props, {}, "">;
51
+ type Glass = ReturnType<typeof Glass>;
52
+ export default Glass;
@@ -0,0 +1,157 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import type { HTMLButtonAttributes, HTMLAnchorAttributes } from "svelte/elements";
4
+ import { cn } from "../../utils";
5
+
6
+ /**
7
+ * GlassButton - A button with glassmorphism styling
8
+ *
9
+ * Beautiful translucent buttons with backdrop blur effects.
10
+ * Perfect for floating actions, overlays, and modern UI designs.
11
+ *
12
+ * @example Basic glass button
13
+ * ```svelte
14
+ * <GlassButton>Click me</GlassButton>
15
+ * ```
16
+ *
17
+ * @example Accent glass button
18
+ * ```svelte
19
+ * <GlassButton variant="accent">Subscribe</GlassButton>
20
+ * ```
21
+ *
22
+ * @example Ghost glass with icon
23
+ * ```svelte
24
+ * <GlassButton variant="ghost" size="icon">
25
+ * <X class="w-4 h-4" />
26
+ * </GlassButton>
27
+ * ```
28
+ */
29
+
30
+ type GlassVariant =
31
+ | "default" // Light translucent background
32
+ | "accent" // Accent-colored glass
33
+ | "dark" // Dark translucent background
34
+ | "ghost" // No background until hover
35
+ | "outline"; // Transparent with glass border
36
+
37
+ type ButtonSize = "sm" | "md" | "lg" | "icon";
38
+
39
+ interface Props extends Omit<HTMLButtonAttributes, "class"> {
40
+ variant?: GlassVariant;
41
+ size?: ButtonSize;
42
+ disabled?: boolean;
43
+ href?: string;
44
+ class?: string;
45
+ children?: Snippet;
46
+ ref?: HTMLButtonElement | HTMLAnchorElement | null;
47
+ }
48
+
49
+ let {
50
+ variant = "default",
51
+ size = "md",
52
+ disabled = false,
53
+ href,
54
+ class: className,
55
+ children,
56
+ ref = $bindable(null),
57
+ type = "button",
58
+ ...restProps
59
+ }: Props = $props();
60
+
61
+ // Base styles shared by all variants - medium blur for glass effect
62
+ const baseClasses = `
63
+ inline-flex items-center justify-center gap-2
64
+ font-medium rounded-lg
65
+ transition-all duration-200
66
+ backdrop-blur-md
67
+ outline-none
68
+ focus-visible:ring-2 focus-visible:ring-accent/50 focus-visible:ring-offset-2
69
+ disabled:opacity-50 disabled:pointer-events-none
70
+ [&_svg]:w-4 [&_svg]:h-4 [&_svg]:shrink-0
71
+ `.trim().replace(/\s+/g, ' ');
72
+
73
+ // Variant-specific styles - warm grove tones with true glass transparency
74
+ const variantClasses: Record<GlassVariant, string> = {
75
+ default: `
76
+ bg-white/60 dark:bg-emerald-950/25
77
+ border border-white/40 dark:border-emerald-800/25
78
+ text-foreground
79
+ hover:bg-white/75 dark:hover:bg-emerald-950/35
80
+ hover:border-white/50 dark:hover:border-emerald-700/30
81
+ shadow-sm hover:shadow-md
82
+ `.trim().replace(/\s+/g, ' '),
83
+
84
+ accent: `
85
+ bg-accent/70 dark:bg-accent/60
86
+ border border-accent/40 dark:border-accent/30
87
+ text-white
88
+ hover:bg-accent/85 dark:hover:bg-accent/75
89
+ hover:border-accent/60 dark:hover:border-accent/50
90
+ shadow-sm hover:shadow-md shadow-accent/20
91
+ `.trim().replace(/\s+/g, ' '),
92
+
93
+ dark: `
94
+ bg-slate-900/50 dark:bg-slate-950/50
95
+ border border-slate-700/30 dark:border-slate-600/30
96
+ text-white
97
+ hover:bg-slate-900/65 dark:hover:bg-slate-950/65
98
+ hover:border-slate-600/40 dark:hover:border-slate-500/35
99
+ shadow-md hover:shadow-lg
100
+ `.trim().replace(/\s+/g, ' '),
101
+
102
+ ghost: `
103
+ bg-transparent
104
+ border border-transparent
105
+ text-foreground
106
+ hover:bg-white/40 dark:hover:bg-emerald-950/25
107
+ hover:border-white/25 dark:hover:border-emerald-800/20
108
+ `.trim().replace(/\s+/g, ' '),
109
+
110
+ outline: `
111
+ bg-transparent
112
+ border border-white/40 dark:border-emerald-800/30
113
+ text-foreground
114
+ hover:bg-white/25 dark:hover:bg-emerald-950/20
115
+ hover:border-white/55 dark:hover:border-emerald-700/40
116
+ `.trim().replace(/\s+/g, ' ')
117
+ };
118
+
119
+ // Size-specific styles
120
+ const sizeClasses: Record<ButtonSize, string> = {
121
+ sm: "h-8 px-3 text-sm",
122
+ md: "h-10 px-4 text-sm",
123
+ lg: "h-12 px-6 text-base",
124
+ icon: "h-10 w-10 p-0"
125
+ };
126
+
127
+ const computedClass = $derived(
128
+ cn(
129
+ baseClasses,
130
+ variantClasses[variant],
131
+ sizeClasses[size],
132
+ className
133
+ )
134
+ );
135
+ </script>
136
+
137
+ {#if href && !disabled}
138
+ <a
139
+ bind:this={ref}
140
+ {href}
141
+ class={computedClass}
142
+ aria-disabled={disabled}
143
+ {...restProps}
144
+ >
145
+ {#if children}{@render children()}{/if}
146
+ </a>
147
+ {:else}
148
+ <button
149
+ bind:this={ref}
150
+ {type}
151
+ {disabled}
152
+ class={computedClass}
153
+ {...restProps}
154
+ >
155
+ {#if children}{@render children()}{/if}
156
+ </button>
157
+ {/if}