@autumnsgrove/groveengine 0.7.0 → 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.
- package/dist/ui/components/content/RoadmapPreview.svelte +91 -0
- package/dist/ui/components/content/RoadmapPreview.svelte.d.ts +36 -0
- package/dist/ui/components/content/index.d.ts +1 -0
- package/dist/ui/components/content/index.js +1 -0
- package/dist/ui/components/nature/Logo.svelte +224 -0
- package/dist/ui/components/nature/Logo.svelte.d.ts +14 -0
- package/dist/ui/components/nature/botanical/Acorn.svelte +48 -0
- package/dist/ui/components/nature/botanical/Acorn.svelte.d.ts +8 -0
- package/dist/ui/components/nature/botanical/Berry.svelte +67 -0
- package/dist/ui/components/nature/botanical/Berry.svelte.d.ts +8 -0
- package/dist/ui/components/nature/botanical/DandelionPuff.svelte +98 -0
- package/dist/ui/components/nature/botanical/DandelionPuff.svelte.d.ts +8 -0
- package/dist/ui/components/nature/botanical/FallingLeavesLayer.svelte +170 -0
- package/dist/ui/components/nature/botanical/FallingLeavesLayer.svelte.d.ts +35 -0
- package/dist/ui/components/nature/botanical/FallingPetalsLayer.svelte +174 -0
- package/dist/ui/components/nature/botanical/FallingPetalsLayer.svelte.d.ts +25 -0
- package/dist/ui/components/nature/botanical/Leaf.svelte +77 -0
- package/dist/ui/components/nature/botanical/Leaf.svelte.d.ts +10 -0
- package/dist/ui/components/nature/botanical/LeafFalling.svelte +186 -0
- package/dist/ui/components/nature/botanical/LeafFalling.svelte.d.ts +22 -0
- package/dist/ui/components/nature/botanical/PetalFalling.svelte +266 -0
- package/dist/ui/components/nature/botanical/PetalFalling.svelte.d.ts +25 -0
- package/dist/ui/components/nature/botanical/PineCone.svelte +61 -0
- package/dist/ui/components/nature/botanical/PineCone.svelte.d.ts +7 -0
- package/dist/ui/components/nature/botanical/Vine.svelte +102 -0
- package/dist/ui/components/nature/botanical/Vine.svelte.d.ts +11 -0
- package/dist/ui/components/nature/botanical/index.d.ts +10 -0
- package/dist/ui/components/nature/botanical/index.js +11 -0
- package/dist/ui/components/nature/creatures/Bee.svelte +78 -0
- package/dist/ui/components/nature/creatures/Bee.svelte.d.ts +9 -0
- package/dist/ui/components/nature/creatures/Bird.svelte +94 -0
- package/dist/ui/components/nature/creatures/Bird.svelte.d.ts +11 -0
- package/dist/ui/components/nature/creatures/BirdFlying.svelte +83 -0
- package/dist/ui/components/nature/creatures/BirdFlying.svelte.d.ts +9 -0
- package/dist/ui/components/nature/creatures/Bluebird.svelte +95 -0
- package/dist/ui/components/nature/creatures/Bluebird.svelte.d.ts +12 -0
- package/dist/ui/components/nature/creatures/Butterfly.svelte +87 -0
- package/dist/ui/components/nature/creatures/Butterfly.svelte.d.ts +9 -0
- package/dist/ui/components/nature/creatures/Cardinal.svelte +95 -0
- package/dist/ui/components/nature/creatures/Cardinal.svelte.d.ts +12 -0
- package/dist/ui/components/nature/creatures/Chickadee.svelte +97 -0
- package/dist/ui/components/nature/creatures/Chickadee.svelte.d.ts +12 -0
- package/dist/ui/components/nature/creatures/Deer.svelte +95 -0
- package/dist/ui/components/nature/creatures/Deer.svelte.d.ts +9 -0
- package/dist/ui/components/nature/creatures/Firefly.svelte +111 -0
- package/dist/ui/components/nature/creatures/Firefly.svelte.d.ts +10 -0
- package/dist/ui/components/nature/creatures/Owl.svelte +91 -0
- package/dist/ui/components/nature/creatures/Owl.svelte.d.ts +9 -0
- package/dist/ui/components/nature/creatures/Rabbit.svelte +90 -0
- package/dist/ui/components/nature/creatures/Rabbit.svelte.d.ts +9 -0
- package/dist/ui/components/nature/creatures/Robin.svelte +98 -0
- package/dist/ui/components/nature/creatures/Robin.svelte.d.ts +12 -0
- package/dist/ui/components/nature/creatures/Squirrel.svelte +97 -0
- package/dist/ui/components/nature/creatures/Squirrel.svelte.d.ts +9 -0
- package/dist/ui/components/nature/creatures/index.d.ts +13 -0
- package/dist/ui/components/nature/creatures/index.js +14 -0
- package/dist/ui/components/nature/ground/Bush.svelte +57 -0
- package/dist/ui/components/nature/ground/Bush.svelte.d.ts +10 -0
- package/dist/ui/components/nature/ground/Crocus.svelte +83 -0
- package/dist/ui/components/nature/ground/Crocus.svelte.d.ts +12 -0
- package/dist/ui/components/nature/ground/Daffodil.svelte +75 -0
- package/dist/ui/components/nature/ground/Daffodil.svelte.d.ts +11 -0
- package/dist/ui/components/nature/ground/Fern.svelte +72 -0
- package/dist/ui/components/nature/ground/Fern.svelte.d.ts +10 -0
- package/dist/ui/components/nature/ground/FlowerWild.svelte +60 -0
- package/dist/ui/components/nature/ground/FlowerWild.svelte.d.ts +10 -0
- package/dist/ui/components/nature/ground/GrassTuft.svelte +49 -0
- package/dist/ui/components/nature/ground/GrassTuft.svelte.d.ts +10 -0
- package/dist/ui/components/nature/ground/Log.svelte +42 -0
- package/dist/ui/components/nature/ground/Log.svelte.d.ts +7 -0
- package/dist/ui/components/nature/ground/Mushroom.svelte +48 -0
- package/dist/ui/components/nature/ground/Mushroom.svelte.d.ts +9 -0
- package/dist/ui/components/nature/ground/MushroomCluster.svelte +41 -0
- package/dist/ui/components/nature/ground/MushroomCluster.svelte.d.ts +8 -0
- package/dist/ui/components/nature/ground/Rock.svelte +59 -0
- package/dist/ui/components/nature/ground/Rock.svelte.d.ts +8 -0
- package/dist/ui/components/nature/ground/Stump.svelte +44 -0
- package/dist/ui/components/nature/ground/Stump.svelte.d.ts +8 -0
- package/dist/ui/components/nature/ground/Tulip.svelte +79 -0
- package/dist/ui/components/nature/ground/Tulip.svelte.d.ts +11 -0
- package/dist/ui/components/nature/ground/index.d.ts +12 -0
- package/dist/ui/components/nature/ground/index.js +13 -0
- package/dist/ui/components/nature/index.d.ts +28 -0
- package/dist/ui/components/nature/index.js +38 -0
- package/dist/ui/components/nature/palette.d.ts +491 -0
- package/dist/ui/components/nature/palette.js +384 -0
- package/dist/ui/components/nature/sky/Cloud.svelte +122 -0
- package/dist/ui/components/nature/sky/Cloud.svelte.d.ts +11 -0
- package/dist/ui/components/nature/sky/CloudWispy.svelte +79 -0
- package/dist/ui/components/nature/sky/CloudWispy.svelte.d.ts +9 -0
- package/dist/ui/components/nature/sky/Moon.svelte +60 -0
- package/dist/ui/components/nature/sky/Moon.svelte.d.ts +9 -0
- package/dist/ui/components/nature/sky/Rainbow.svelte +101 -0
- package/dist/ui/components/nature/sky/Rainbow.svelte.d.ts +8 -0
- package/dist/ui/components/nature/sky/Star.svelte +84 -0
- package/dist/ui/components/nature/sky/Star.svelte.d.ts +10 -0
- package/dist/ui/components/nature/sky/StarCluster.svelte +85 -0
- package/dist/ui/components/nature/sky/StarCluster.svelte.d.ts +9 -0
- package/dist/ui/components/nature/sky/StarShooting.svelte +90 -0
- package/dist/ui/components/nature/sky/StarShooting.svelte.d.ts +9 -0
- package/dist/ui/components/nature/sky/Sun.svelte +70 -0
- package/dist/ui/components/nature/sky/Sun.svelte.d.ts +9 -0
- package/dist/ui/components/nature/sky/index.d.ts +8 -0
- package/dist/ui/components/nature/sky/index.js +9 -0
- package/dist/ui/components/nature/structural/Birdhouse.svelte +53 -0
- package/dist/ui/components/nature/structural/Birdhouse.svelte.d.ts +8 -0
- package/dist/ui/components/nature/structural/Bridge.svelte +65 -0
- package/dist/ui/components/nature/structural/Bridge.svelte.d.ts +7 -0
- package/dist/ui/components/nature/structural/FencePost.svelte +54 -0
- package/dist/ui/components/nature/structural/FencePost.svelte.d.ts +8 -0
- package/dist/ui/components/nature/structural/GardenGate.svelte +70 -0
- package/dist/ui/components/nature/structural/GardenGate.svelte.d.ts +8 -0
- package/dist/ui/components/nature/structural/Lantern.svelte +113 -0
- package/dist/ui/components/nature/structural/Lantern.svelte.d.ts +10 -0
- package/dist/ui/components/nature/structural/Lattice.svelte +89 -0
- package/dist/ui/components/nature/structural/Lattice.svelte.d.ts +8 -0
- package/dist/ui/components/nature/structural/LatticeWithVine.svelte +89 -0
- package/dist/ui/components/nature/structural/LatticeWithVine.svelte.d.ts +11 -0
- package/dist/ui/components/nature/structural/StonePath.svelte +48 -0
- package/dist/ui/components/nature/structural/StonePath.svelte.d.ts +7 -0
- package/dist/ui/components/nature/structural/index.d.ts +8 -0
- package/dist/ui/components/nature/structural/index.js +9 -0
- package/dist/ui/components/nature/trees/TreeAspen.svelte +163 -0
- package/dist/ui/components/nature/trees/TreeAspen.svelte.d.ts +11 -0
- package/dist/ui/components/nature/trees/TreeBirch.svelte +186 -0
- package/dist/ui/components/nature/trees/TreeBirch.svelte.d.ts +11 -0
- package/dist/ui/components/nature/trees/TreeCherry.svelte +108 -0
- package/dist/ui/components/nature/trees/TreeCherry.svelte.d.ts +11 -0
- package/dist/ui/components/nature/trees/TreePine.svelte +79 -0
- package/dist/ui/components/nature/trees/TreePine.svelte.d.ts +11 -0
- package/dist/ui/components/nature/trees/index.d.ts +4 -0
- package/dist/ui/components/nature/trees/index.js +5 -0
- package/dist/ui/components/nature/water/LilyPad.svelte +99 -0
- package/dist/ui/components/nature/water/LilyPad.svelte.d.ts +10 -0
- package/dist/ui/components/nature/water/Pond.svelte +104 -0
- package/dist/ui/components/nature/water/Pond.svelte.d.ts +8 -0
- package/dist/ui/components/nature/water/Reeds.svelte +85 -0
- package/dist/ui/components/nature/water/Reeds.svelte.d.ts +11 -0
- package/dist/ui/components/nature/water/Stream.svelte +98 -0
- package/dist/ui/components/nature/water/Stream.svelte.d.ts +8 -0
- package/dist/ui/components/nature/water/index.d.ts +4 -0
- package/dist/ui/components/nature/water/index.js +5 -0
- package/dist/ui/components/nature/weather/SnowfallLayer.svelte +175 -0
- package/dist/ui/components/nature/weather/SnowfallLayer.svelte.d.ts +25 -0
- package/dist/ui/components/nature/weather/Snowflake.svelte +99 -0
- package/dist/ui/components/nature/weather/Snowflake.svelte.d.ts +11 -0
- package/dist/ui/components/nature/weather/SnowflakeFalling.svelte +162 -0
- package/dist/ui/components/nature/weather/SnowflakeFalling.svelte.d.ts +23 -0
- package/dist/ui/components/nature/weather/index.d.ts +3 -0
- package/dist/ui/components/nature/weather/index.js +4 -0
- package/dist/ui/components/ui/GlassLogo.svelte +422 -0
- package/dist/ui/components/ui/GlassLogo.svelte.d.ts +23 -0
- package/dist/ui/components/ui/GlassNavbar.svelte +120 -0
- package/dist/ui/components/ui/GlassNavbar.svelte.d.ts +42 -0
- package/dist/ui/components/ui/Logo.svelte +47 -52
- package/dist/ui/components/ui/Logo.svelte.d.ts +4 -3
- package/dist/ui/components/ui/index.d.ts +2 -0
- package/dist/ui/components/ui/index.js +2 -0
- package/dist/ui/styles/grove.css +15 -1
- package/package.json +11 -1
|
@@ -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 { accents, greens } from '../palette';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
class?: string;
|
|
11
|
+
padColor?: string;
|
|
12
|
+
flowerColor?: string;
|
|
13
|
+
hasFlower?: boolean;
|
|
14
|
+
animate?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let {
|
|
18
|
+
class: className = 'w-8 h-6',
|
|
19
|
+
padColor,
|
|
20
|
+
flowerColor,
|
|
21
|
+
hasFlower = false,
|
|
22
|
+
animate = true
|
|
23
|
+
}: Props = $props();
|
|
24
|
+
|
|
25
|
+
const pad = $derived(padColor ?? greens.grove);
|
|
26
|
+
const flower = $derived(flowerColor ?? accents.flower.white);
|
|
27
|
+
const padDark = $derived(greens.deepGreen);
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<!-- Lily pad with optional flower -->
|
|
31
|
+
<svg class="{className} {animate ? 'float' : ''}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 45">
|
|
32
|
+
<!-- Pad shadow -->
|
|
33
|
+
<ellipse fill="rgba(0,0,0,0.1)" cx="30" cy="32" rx="25" ry="12" />
|
|
34
|
+
|
|
35
|
+
<!-- Lily pad -->
|
|
36
|
+
<path
|
|
37
|
+
fill={pad}
|
|
38
|
+
d="M30 20
|
|
39
|
+
Q55 20 55 32
|
|
40
|
+
Q55 42 30 42
|
|
41
|
+
Q5 42 5 32
|
|
42
|
+
Q5 20 30 20
|
|
43
|
+
L30 32 Z"
|
|
44
|
+
/>
|
|
45
|
+
|
|
46
|
+
<!-- Pad notch (V-cut) -->
|
|
47
|
+
<path
|
|
48
|
+
fill="#7dd3fc"
|
|
49
|
+
d="M30 20 L25 32 L30 32 L35 32 L30 20"
|
|
50
|
+
opacity="0.5"
|
|
51
|
+
/>
|
|
52
|
+
|
|
53
|
+
<!-- Pad veins -->
|
|
54
|
+
<path fill="none" stroke={padDark} stroke-width="0.5" d="M30 32 Q20 28 10 32" opacity="0.3" />
|
|
55
|
+
<path fill="none" stroke={padDark} stroke-width="0.5" d="M30 32 Q40 28 50 32" opacity="0.3" />
|
|
56
|
+
<path fill="none" stroke={padDark} stroke-width="0.5" d="M30 32 Q25 38 15 40" opacity="0.3" />
|
|
57
|
+
<path fill="none" stroke={padDark} stroke-width="0.5" d="M30 32 Q35 38 45 40" opacity="0.3" />
|
|
58
|
+
|
|
59
|
+
{#if hasFlower}
|
|
60
|
+
<!-- Water lily flower -->
|
|
61
|
+
<g class={animate ? 'bloom' : ''}>
|
|
62
|
+
<!-- Outer petals -->
|
|
63
|
+
<ellipse fill={flower} cx="30" cy="15" rx="4" ry="8" />
|
|
64
|
+
<ellipse fill={flower} cx="22" cy="18" rx="4" ry="7" transform="rotate(-30 22 18)" />
|
|
65
|
+
<ellipse fill={flower} cx="38" cy="18" rx="4" ry="7" transform="rotate(30 38 18)" />
|
|
66
|
+
<ellipse fill={flower} cx="20" cy="24" rx="3" ry="6" transform="rotate(-60 20 24)" />
|
|
67
|
+
<ellipse fill={flower} cx="40" cy="24" rx="3" ry="6" transform="rotate(60 40 24)" />
|
|
68
|
+
|
|
69
|
+
<!-- Inner petals -->
|
|
70
|
+
<ellipse fill={flower} cx="30" cy="17" rx="3" ry="5" />
|
|
71
|
+
<ellipse fill={flower} cx="26" cy="19" rx="2.5" ry="4" transform="rotate(-20 26 19)" />
|
|
72
|
+
<ellipse fill={flower} cx="34" cy="19" rx="2.5" ry="4" transform="rotate(20 34 19)" />
|
|
73
|
+
|
|
74
|
+
<!-- Center -->
|
|
75
|
+
<circle fill="#fbbf24" cx="30" cy="20" r="3" />
|
|
76
|
+
</g>
|
|
77
|
+
{/if}
|
|
78
|
+
</svg>
|
|
79
|
+
|
|
80
|
+
<style>
|
|
81
|
+
@keyframes float {
|
|
82
|
+
0%, 100% { transform: translateY(0) rotate(0deg); }
|
|
83
|
+
50% { transform: translateY(-2px) rotate(1deg); }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@keyframes bloom {
|
|
87
|
+
0%, 100% { transform: scale(1); }
|
|
88
|
+
50% { transform: scale(1.02); }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.float {
|
|
92
|
+
animation: float 4s ease-in-out infinite;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.bloom {
|
|
96
|
+
transform-origin: center bottom;
|
|
97
|
+
animation: bloom 5s ease-in-out infinite;
|
|
98
|
+
}
|
|
99
|
+
</style>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
class?: string;
|
|
3
|
+
padColor?: string;
|
|
4
|
+
flowerColor?: string;
|
|
5
|
+
hasFlower?: boolean;
|
|
6
|
+
animate?: boolean;
|
|
7
|
+
}
|
|
8
|
+
declare const LilyPad: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type LilyPad = ReturnType<typeof LilyPad>;
|
|
10
|
+
export default LilyPad;
|
|
@@ -0,0 +1,104 @@
|
|
|
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 { accents } from '../palette';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
class?: string;
|
|
11
|
+
color?: string;
|
|
12
|
+
animate?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
class: className = 'w-20 h-12',
|
|
17
|
+
color,
|
|
18
|
+
animate = true
|
|
19
|
+
}: Props = $props();
|
|
20
|
+
|
|
21
|
+
const waterColor = $derived(color ?? accents.water.surface);
|
|
22
|
+
const deepColor = $derived(accents.water.deep);
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<!-- Pond with subtle ripple effect -->
|
|
26
|
+
<svg class={className} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 60">
|
|
27
|
+
<defs>
|
|
28
|
+
<ellipse id="pond-shape" cx="60" cy="35" rx="55" ry="25" />
|
|
29
|
+
<clipPath id="pond-clip">
|
|
30
|
+
<use href="#pond-shape" />
|
|
31
|
+
</clipPath>
|
|
32
|
+
</defs>
|
|
33
|
+
|
|
34
|
+
<!-- Water body -->
|
|
35
|
+
<ellipse fill={deepColor} cx="60" cy="35" rx="55" ry="25" />
|
|
36
|
+
<ellipse fill={waterColor} cx="60" cy="32" rx="52" ry="22" opacity="0.7" />
|
|
37
|
+
|
|
38
|
+
<!-- Ripples -->
|
|
39
|
+
<g clip-path="url(#pond-clip)">
|
|
40
|
+
<ellipse
|
|
41
|
+
fill="none"
|
|
42
|
+
stroke="white"
|
|
43
|
+
stroke-width="1"
|
|
44
|
+
cx="40"
|
|
45
|
+
cy="30"
|
|
46
|
+
rx="8"
|
|
47
|
+
ry="4"
|
|
48
|
+
opacity="0.3"
|
|
49
|
+
class={animate ? 'ripple ripple-1' : ''}
|
|
50
|
+
/>
|
|
51
|
+
<ellipse
|
|
52
|
+
fill="none"
|
|
53
|
+
stroke="white"
|
|
54
|
+
stroke-width="0.5"
|
|
55
|
+
cx="40"
|
|
56
|
+
cy="30"
|
|
57
|
+
rx="15"
|
|
58
|
+
ry="7"
|
|
59
|
+
opacity="0.2"
|
|
60
|
+
class={animate ? 'ripple ripple-2' : ''}
|
|
61
|
+
/>
|
|
62
|
+
<ellipse
|
|
63
|
+
fill="none"
|
|
64
|
+
stroke="white"
|
|
65
|
+
stroke-width="1"
|
|
66
|
+
cx="75"
|
|
67
|
+
cy="38"
|
|
68
|
+
rx="6"
|
|
69
|
+
ry="3"
|
|
70
|
+
opacity="0.25"
|
|
71
|
+
class={animate ? 'ripple ripple-3' : ''}
|
|
72
|
+
/>
|
|
73
|
+
</g>
|
|
74
|
+
|
|
75
|
+
<!-- Reflection highlight -->
|
|
76
|
+
<ellipse fill="white" cx="45" cy="28" rx="12" ry="5" opacity="0.15" />
|
|
77
|
+
|
|
78
|
+
<!-- Edge shadow -->
|
|
79
|
+
<ellipse
|
|
80
|
+
fill="none"
|
|
81
|
+
stroke="rgba(0,0,0,0.1)"
|
|
82
|
+
stroke-width="2"
|
|
83
|
+
cx="60"
|
|
84
|
+
cy="35"
|
|
85
|
+
rx="55"
|
|
86
|
+
ry="25"
|
|
87
|
+
/>
|
|
88
|
+
</svg>
|
|
89
|
+
|
|
90
|
+
<style>
|
|
91
|
+
@keyframes ripple {
|
|
92
|
+
0% { transform: scale(0.8); opacity: 0.4; }
|
|
93
|
+
100% { transform: scale(1.5); opacity: 0; }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.ripple {
|
|
97
|
+
transform-origin: center center;
|
|
98
|
+
animation: ripple 3s ease-out infinite;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.ripple-1 { animation-delay: 0s; }
|
|
102
|
+
.ripple-2 { animation-delay: 0.5s; }
|
|
103
|
+
.ripple-3 { animation-delay: 1.5s; }
|
|
104
|
+
</style>
|
|
@@ -0,0 +1,85 @@
|
|
|
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 type { Season } from '../palette';
|
|
8
|
+
import { greens, autumn, bark } from '../palette';
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
class?: string;
|
|
12
|
+
color?: string;
|
|
13
|
+
season?: Season;
|
|
14
|
+
animate?: boolean;
|
|
15
|
+
variant?: 'cattail' | 'grass';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
class: className = 'w-6 h-12',
|
|
20
|
+
color,
|
|
21
|
+
season = 'summer',
|
|
22
|
+
animate = true,
|
|
23
|
+
variant = 'cattail'
|
|
24
|
+
}: Props = $props();
|
|
25
|
+
|
|
26
|
+
// Reeds turn golden-brown in autumn
|
|
27
|
+
const defaultColor = $derived(
|
|
28
|
+
season === 'autumn' ? autumn.straw : greens.deepGreen
|
|
29
|
+
);
|
|
30
|
+
const reedColor = $derived(color ?? defaultColor);
|
|
31
|
+
const cattailColor = $derived(bark.bark);
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<!-- Reeds/Cattails -->
|
|
35
|
+
<svg class={className} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 100">
|
|
36
|
+
{#if variant === 'cattail'}
|
|
37
|
+
<!-- Cattail stalks with heads -->
|
|
38
|
+
<g class={animate ? 'sway sway-1' : ''}>
|
|
39
|
+
<path fill="none" stroke={reedColor} stroke-width="2" d="M15 100 Q14 70 16 40 Q17 25 15 15" />
|
|
40
|
+
<ellipse fill={cattailColor} cx="15" cy="22" rx="4" ry="10" />
|
|
41
|
+
<path fill={reedColor} d="M15 12 L13 8 L15 10 L17 8 Z" />
|
|
42
|
+
</g>
|
|
43
|
+
|
|
44
|
+
<g class={animate ? 'sway sway-2' : ''}>
|
|
45
|
+
<path fill="none" stroke={reedColor} stroke-width="2" d="M30 100 Q32 65 30 35 Q28 18 30 5" />
|
|
46
|
+
<ellipse fill={cattailColor} cx="30" cy="15" rx="4" ry="12" />
|
|
47
|
+
<path fill={reedColor} d="M30 3 L28 0 L30 2 L32 0 Z" />
|
|
48
|
+
</g>
|
|
49
|
+
|
|
50
|
+
<g class={animate ? 'sway sway-3' : ''}>
|
|
51
|
+
<path fill="none" stroke={reedColor} stroke-width="1.5" d="M42 100 Q40 75 42 50 Q44 30 42 20" />
|
|
52
|
+
<ellipse fill={cattailColor} cx="42" cy="27" rx="3" ry="8" />
|
|
53
|
+
<path fill={reedColor} d="M42 19 L40 16 L42 18 L44 16 Z" />
|
|
54
|
+
</g>
|
|
55
|
+
{:else}
|
|
56
|
+
<!-- Simple grass reeds -->
|
|
57
|
+
<g class={animate ? 'sway sway-1' : ''}>
|
|
58
|
+
<path fill={reedColor} d="M10 100 L8 100 Q6 60 10 20 Q12 15 10 10 Q12 15 14 20 Q18 60 16 100 Z" />
|
|
59
|
+
</g>
|
|
60
|
+
|
|
61
|
+
<g class={animate ? 'sway sway-2' : ''}>
|
|
62
|
+
<path fill={reedColor} d="M25 100 L23 100 Q20 55 25 15 Q27 8 25 2 Q28 8 30 15 Q35 55 32 100 Z" />
|
|
63
|
+
</g>
|
|
64
|
+
|
|
65
|
+
<g class={animate ? 'sway sway-3' : ''}>
|
|
66
|
+
<path fill={reedColor} d="M40 100 L38 100 Q36 65 40 30 Q42 22 40 15 Q43 22 45 30 Q48 65 46 100 Z" />
|
|
67
|
+
</g>
|
|
68
|
+
{/if}
|
|
69
|
+
</svg>
|
|
70
|
+
|
|
71
|
+
<style>
|
|
72
|
+
@keyframes sway {
|
|
73
|
+
0%, 100% { transform: rotate(0deg); }
|
|
74
|
+
50% { transform: rotate(2deg); }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.sway {
|
|
78
|
+
transform-origin: center bottom;
|
|
79
|
+
animation: sway 3s ease-in-out infinite;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.sway-1 { animation-delay: 0s; }
|
|
83
|
+
.sway-2 { animation-delay: 0.4s; }
|
|
84
|
+
.sway-3 { animation-delay: 0.8s; }
|
|
85
|
+
</style>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Season } from '../palette';
|
|
2
|
+
interface Props {
|
|
3
|
+
class?: string;
|
|
4
|
+
color?: string;
|
|
5
|
+
season?: Season;
|
|
6
|
+
animate?: boolean;
|
|
7
|
+
variant?: 'cattail' | 'grass';
|
|
8
|
+
}
|
|
9
|
+
declare const Reeds: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type Reeds = ReturnType<typeof Reeds>;
|
|
11
|
+
export default Reeds;
|
|
@@ -0,0 +1,98 @@
|
|
|
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 { accents } from '../palette';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
class?: string;
|
|
11
|
+
color?: string;
|
|
12
|
+
animate?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
class: className = 'w-24 h-8',
|
|
17
|
+
color,
|
|
18
|
+
animate = true
|
|
19
|
+
}: Props = $props();
|
|
20
|
+
|
|
21
|
+
const waterColor = $derived(color ?? accents.water.surface);
|
|
22
|
+
const deepColor = $derived(accents.water.deep);
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<!-- Flowing stream -->
|
|
26
|
+
<svg class={className} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 150 40">
|
|
27
|
+
<defs>
|
|
28
|
+
<linearGradient id="stream-gradient" x1="0%" y1="0%" x2="0%" y2="100%">
|
|
29
|
+
<stop offset="0%" stop-color={waterColor} />
|
|
30
|
+
<stop offset="100%" stop-color={deepColor} />
|
|
31
|
+
</linearGradient>
|
|
32
|
+
</defs>
|
|
33
|
+
|
|
34
|
+
<!-- Stream bed -->
|
|
35
|
+
<path
|
|
36
|
+
fill="url(#stream-gradient)"
|
|
37
|
+
d="M0 15 Q30 10 50 18 Q80 28 100 20 Q120 12 150 17
|
|
38
|
+
L150 30 Q120 35 100 28 Q80 22 50 30 Q30 35 0 28 Z"
|
|
39
|
+
/>
|
|
40
|
+
|
|
41
|
+
<!-- Flow lines -->
|
|
42
|
+
<g class={animate ? 'flow' : ''}>
|
|
43
|
+
<path
|
|
44
|
+
fill="none"
|
|
45
|
+
stroke="white"
|
|
46
|
+
stroke-width="1"
|
|
47
|
+
d="M10 20 Q25 18 40 22"
|
|
48
|
+
opacity="0.3"
|
|
49
|
+
/>
|
|
50
|
+
<path
|
|
51
|
+
fill="none"
|
|
52
|
+
stroke="white"
|
|
53
|
+
stroke-width="1"
|
|
54
|
+
d="M50 24 Q70 20 90 23"
|
|
55
|
+
opacity="0.25"
|
|
56
|
+
/>
|
|
57
|
+
<path
|
|
58
|
+
fill="none"
|
|
59
|
+
stroke="white"
|
|
60
|
+
stroke-width="1"
|
|
61
|
+
d="M100 21 Q120 18 140 22"
|
|
62
|
+
opacity="0.3"
|
|
63
|
+
/>
|
|
64
|
+
</g>
|
|
65
|
+
|
|
66
|
+
<!-- Sparkles/highlights -->
|
|
67
|
+
<g class={animate ? 'sparkle' : ''}>
|
|
68
|
+
<circle fill="white" cx="30" cy="20" r="1.5" opacity="0.4" />
|
|
69
|
+
<circle fill="white" cx="75" cy="23" r="1" opacity="0.3" />
|
|
70
|
+
<circle fill="white" cx="115" cy="20" r="1.5" opacity="0.35" />
|
|
71
|
+
</g>
|
|
72
|
+
|
|
73
|
+
<!-- Bank shadows -->
|
|
74
|
+
<path
|
|
75
|
+
fill="rgba(0,0,0,0.1)"
|
|
76
|
+
d="M0 15 Q30 10 50 18 Q80 28 100 20 Q120 12 150 17 L150 15 Q120 10 100 18 Q80 26 50 16 Q30 8 0 13 Z"
|
|
77
|
+
/>
|
|
78
|
+
</svg>
|
|
79
|
+
|
|
80
|
+
<style>
|
|
81
|
+
@keyframes flow {
|
|
82
|
+
0% { transform: translateX(0); }
|
|
83
|
+
100% { transform: translateX(10px); }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@keyframes sparkle {
|
|
87
|
+
0%, 100% { opacity: 1; }
|
|
88
|
+
50% { opacity: 0.5; }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.flow {
|
|
92
|
+
animation: flow 2s linear infinite;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.sparkle {
|
|
96
|
+
animation: sparkle 1.5s ease-in-out infinite;
|
|
97
|
+
}
|
|
98
|
+
</style>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Water components - ponds, streams, and aquatic elements
|
|
2
|
+
export { default as LilyPad } from './LilyPad.svelte';
|
|
3
|
+
export { default as Pond } from './Pond.svelte';
|
|
4
|
+
export { default as Reeds } from './Reeds.svelte';
|
|
5
|
+
export { default as Stream } from './Stream.svelte';
|
|
@@ -0,0 +1,175 @@
|
|
|
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 { browser } from '$app/environment';
|
|
8
|
+
import { winter } from '../palette';
|
|
9
|
+
import SnowflakeFalling from './SnowflakeFalling.svelte';
|
|
10
|
+
|
|
11
|
+
type SnowflakeVariant = 'crystal' | 'simple' | 'star' | 'delicate' | 'dot';
|
|
12
|
+
|
|
13
|
+
// Check for reduced motion preference
|
|
14
|
+
const prefersReducedMotion = browser
|
|
15
|
+
? window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
16
|
+
: false;
|
|
17
|
+
|
|
18
|
+
interface Props {
|
|
19
|
+
/** Total number of snowflakes */
|
|
20
|
+
count?: number;
|
|
21
|
+
/** Base z-index for the snow layer */
|
|
22
|
+
zIndex?: number;
|
|
23
|
+
/** Enable snowfall animation */
|
|
24
|
+
enabled?: boolean;
|
|
25
|
+
/** Opacity range for snowflakes (depth affects final value) */
|
|
26
|
+
opacity?: { min: number; max: number };
|
|
27
|
+
/** Fall duration range in seconds (slower = gentler snowfall) */
|
|
28
|
+
fallDuration?: { min: number; max: number };
|
|
29
|
+
/** Horizontal drift range in vw units (higher = more wind) */
|
|
30
|
+
driftRange?: number;
|
|
31
|
+
/** Maximum spawn delay in seconds (staggers initial appearance) */
|
|
32
|
+
spawnDelay?: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let {
|
|
36
|
+
count = 60,
|
|
37
|
+
zIndex = 50,
|
|
38
|
+
enabled = true,
|
|
39
|
+
opacity = { min: 0.4, max: 0.9 },
|
|
40
|
+
fallDuration = { min: 10, max: 18 },
|
|
41
|
+
driftRange = 15,
|
|
42
|
+
spawnDelay = 12
|
|
43
|
+
}: Props = $props();
|
|
44
|
+
|
|
45
|
+
// Derived animation constants from props
|
|
46
|
+
const FALL_DISTANCE = { min: 100, max: 120 } as const;
|
|
47
|
+
|
|
48
|
+
// Reduce snowflake count for reduced motion or use fewer for performance
|
|
49
|
+
const actualCount = $derived(prefersReducedMotion ? Math.floor(count / 4) : count);
|
|
50
|
+
|
|
51
|
+
const snowflakeVariants: SnowflakeVariant[] = ['crystal', 'simple', 'star', 'delicate', 'dot'];
|
|
52
|
+
|
|
53
|
+
interface Snowflake {
|
|
54
|
+
id: number;
|
|
55
|
+
x: number;
|
|
56
|
+
y: number;
|
|
57
|
+
size: number;
|
|
58
|
+
variant: SnowflakeVariant;
|
|
59
|
+
duration: number;
|
|
60
|
+
delay: number;
|
|
61
|
+
drift: number;
|
|
62
|
+
opacity: number;
|
|
63
|
+
fallDistance: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Deterministic hash for natural distribution (returns 0-1 range)
|
|
67
|
+
// Using different prime multipliers for each property ensures varied but consistent results
|
|
68
|
+
function hashRandom(seed: number): number {
|
|
69
|
+
const hash = Math.abs(Math.sin(seed * 12.9898) * 43758.5453);
|
|
70
|
+
return hash - Math.floor(hash); // Fractional part gives 0-1 range
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Generate snowflakes across the viewport
|
|
74
|
+
// All randomization is deterministic to prevent SSR hydration mismatches
|
|
75
|
+
function generateSnowflakes(): Snowflake[] {
|
|
76
|
+
const snowflakes: Snowflake[] = [];
|
|
77
|
+
|
|
78
|
+
for (let i = 0; i < actualCount; i++) {
|
|
79
|
+
// Use different seed multipliers for each property to get varied distributions
|
|
80
|
+
const xRand = hashRandom(i * 7);
|
|
81
|
+
const yRand = hashRandom(i * 11);
|
|
82
|
+
const depthRand = hashRandom(i * 13);
|
|
83
|
+
const durationRand = hashRandom(i * 17);
|
|
84
|
+
const delayRand = hashRandom(i * 19);
|
|
85
|
+
const driftRand = hashRandom(i * 23);
|
|
86
|
+
const distanceRand = hashRandom(i * 29);
|
|
87
|
+
|
|
88
|
+
// Distribute across full width with some randomness
|
|
89
|
+
const x = (i / actualCount) * 100 + (xRand - 0.5) * 10;
|
|
90
|
+
|
|
91
|
+
// Start positions: most above viewport, some within for immediate visibility
|
|
92
|
+
// This creates a natural "already snowing" effect on page load
|
|
93
|
+
const y = yRand < 0.3
|
|
94
|
+
? yRand * 80 // 30% start within viewport (0-24% from top)
|
|
95
|
+
: -5 - yRand * 15; // 70% start above viewport
|
|
96
|
+
|
|
97
|
+
// Depth-based sizing algorithm:
|
|
98
|
+
// depthFactor 0.0 = far away (small, simple dots, lower opacity)
|
|
99
|
+
// depthFactor 1.0 = close up (large, detailed crystals, higher opacity)
|
|
100
|
+
// This creates a parallax-like depth effect with smaller background flakes
|
|
101
|
+
const depthFactor = depthRand;
|
|
102
|
+
const size = 8 + depthFactor * 16; // 8-24px range for better visibility
|
|
103
|
+
|
|
104
|
+
// Variant selection based on depth:
|
|
105
|
+
// - Far (0.0-0.3): tiny dots for distant snow
|
|
106
|
+
// - Mid (0.3-0.5): simple shapes
|
|
107
|
+
// - Close (0.5-1.0): detailed crystal/star/delicate variants (excludes dot)
|
|
108
|
+
let variant: SnowflakeVariant;
|
|
109
|
+
if (depthFactor < 0.3) {
|
|
110
|
+
variant = 'dot';
|
|
111
|
+
} else if (depthFactor < 0.5) {
|
|
112
|
+
variant = 'simple';
|
|
113
|
+
} else {
|
|
114
|
+
// Use % 4 intentionally to select from first 4 variants (crystal, simple, star, delicate)
|
|
115
|
+
// This excludes 'dot' which is reserved for distant snowflakes
|
|
116
|
+
variant = snowflakeVariants[Math.floor(hashRandom(i) * 4)] as SnowflakeVariant;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Snowflakes starting in viewport get minimal delay for immediate visibility
|
|
120
|
+
const isInViewport = y >= 0;
|
|
121
|
+
const actualDelay = isInViewport ? delayRand * 2 : delayRand * spawnDelay;
|
|
122
|
+
|
|
123
|
+
snowflakes.push({
|
|
124
|
+
id: i,
|
|
125
|
+
x,
|
|
126
|
+
y,
|
|
127
|
+
size,
|
|
128
|
+
variant,
|
|
129
|
+
duration: fallDuration.min + durationRand * (fallDuration.max - fallDuration.min),
|
|
130
|
+
delay: actualDelay,
|
|
131
|
+
drift: (driftRand - 0.5) * driftRange,
|
|
132
|
+
opacity: opacity.min + depthFactor * (opacity.max - opacity.min),
|
|
133
|
+
fallDistance: FALL_DISTANCE.min + distanceRand * (FALL_DISTANCE.max - FALL_DISTANCE.min)
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return snowflakes;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Generate snowflakes once
|
|
141
|
+
const snowflakes = $derived(generateSnowflakes());
|
|
142
|
+
</script>
|
|
143
|
+
|
|
144
|
+
{#if enabled}
|
|
145
|
+
<!-- Snowfall layer - covers the page content area -->
|
|
146
|
+
<div
|
|
147
|
+
class="absolute inset-0 pointer-events-none overflow-hidden"
|
|
148
|
+
style="z-index: {zIndex};"
|
|
149
|
+
>
|
|
150
|
+
{#each snowflakes as flake (flake.id)}
|
|
151
|
+
<div
|
|
152
|
+
class="absolute"
|
|
153
|
+
style="
|
|
154
|
+
left: {flake.x}%;
|
|
155
|
+
top: {flake.y}%;
|
|
156
|
+
width: {flake.size}px;
|
|
157
|
+
height: {flake.size}px;
|
|
158
|
+
"
|
|
159
|
+
>
|
|
160
|
+
<SnowflakeFalling
|
|
161
|
+
class="w-full h-full"
|
|
162
|
+
variant={flake.variant}
|
|
163
|
+
color={winter.snow}
|
|
164
|
+
duration={flake.duration}
|
|
165
|
+
delay={flake.delay}
|
|
166
|
+
drift={flake.drift}
|
|
167
|
+
fallDistance={flake.fallDistance}
|
|
168
|
+
opacity={flake.opacity}
|
|
169
|
+
seed={flake.id}
|
|
170
|
+
animate={true}
|
|
171
|
+
/>
|
|
172
|
+
</div>
|
|
173
|
+
{/each}
|
|
174
|
+
</div>
|
|
175
|
+
{/if}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
/** Total number of snowflakes */
|
|
3
|
+
count?: number;
|
|
4
|
+
/** Base z-index for the snow layer */
|
|
5
|
+
zIndex?: number;
|
|
6
|
+
/** Enable snowfall animation */
|
|
7
|
+
enabled?: boolean;
|
|
8
|
+
/** Opacity range for snowflakes (depth affects final value) */
|
|
9
|
+
opacity?: {
|
|
10
|
+
min: number;
|
|
11
|
+
max: number;
|
|
12
|
+
};
|
|
13
|
+
/** Fall duration range in seconds (slower = gentler snowfall) */
|
|
14
|
+
fallDuration?: {
|
|
15
|
+
min: number;
|
|
16
|
+
max: number;
|
|
17
|
+
};
|
|
18
|
+
/** Horizontal drift range in vw units (higher = more wind) */
|
|
19
|
+
driftRange?: number;
|
|
20
|
+
/** Maximum spawn delay in seconds (staggers initial appearance) */
|
|
21
|
+
spawnDelay?: number;
|
|
22
|
+
}
|
|
23
|
+
declare const SnowfallLayer: import("svelte").Component<Props, {}, "">;
|
|
24
|
+
type SnowfallLayer = ReturnType<typeof SnowfallLayer>;
|
|
25
|
+
export default SnowfallLayer;
|