SPB Git

spb/svgarden Public

SVGarden — searchable bank of 74 self-contained SVG+CSS animation snippets (svgarden.dev)

HTML 79.2% Astro 10.3% JavaScript 6% CSS 3.7% Shell 0.8%

feat(snippets): grow backgrounds category (+5 next-level)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Simon-Pierre Boucher committed 7 h ago (Aug 10, 2026) parent 1a5f56a

Showing 5 changed files with +502 and −0

added snippets/backgrounds/aurora-veil.html +100 −0
@@ -0,0 +1,100 @@
1 +<!--
2 + ============================================================
3 + SVGarden — https://www.svgarden.dev
4 + Author : Simon-Pierre Boucher
5 + Contact: contact@spboucher.ai
6 + File : snippets/backgrounds/aurora-veil.html
7 + Desc : Northern-lights aurora ribbons drifting over a night sky
8 + ============================================================
9 +-->
10 +<!--svgarden
11 +title: Aurora veil
12 +slug: aurora-veil
13 +category: backgrounds
14 +tags: [background, aurora, blend-mode, blur]
15 +difficulty: advanced
16 +techniques: ["mix-blend-mode", "blur()", periodic-paths, translate-property, layered-opacity]
17 +how_it_works: >
18 + Each ribbon is one very wide stroked path whose curve repeats every 300
19 + units, so animating the modern translate property by exactly one period
20 + loops with no visible seam. A heavy CSS blur() turns the hard strokes into
21 + soft light, and mix-blend-mode: screen makes overlapping ribbons add their
22 + light together the way real aurora curtains do against a dark sky. The
23 + three layers drift at 1×, 1.6× and 2.3× the base duration, so the
24 + composition slowly evolves and only realigns once every few minutes.
25 + Where blend modes are unavailable the ribbons simply composite as
26 + translucent color — dimmer, but never broken.
27 +support: mix-blend-mode on SVG elements needs a modern browser; without it the ribbons fall back to plain translucency.
28 +customizable:
29 + - { var: "--sg-color", label: "Aurora color", type: color, default: "#52E6A7" }
30 + - { var: "--sg-size", label: "Height", type: range, min: 120, max: 320, default: 200, unit: px }
31 + - { var: "--sg-duration", label: "Drift speed", type: range, min: 6, max: 40, step: 1, default: 14, unit: s }
32 +created: 2026-08-10
33 +-->
34 +<div class="sg-aurora-veil" style="--sg-color:#52E6A7; --sg-size:200px; --sg-duration:14s;">
35 + <svg viewBox="0 0 600 200" preserveAspectRatio="xMidYMid slice" aria-hidden="true" focusable="false">
36 + <defs>
37 + <linearGradient id="sg-aurora-veil-sky" x1="0" y1="0" x2="0" y2="1">
38 + <stop offset="0" stop-color="#071022" />
39 + <stop offset="1" stop-color="#161031" />
40 + </linearGradient>
41 + </defs>
42 + <rect class="sg-aurora-veil-bg" x="0" y="0" width="600" height="200" fill="url(#sg-aurora-veil-sky)" />
43 + <g class="sg-aurora-veil-stars">
44 + <circle cx="60" cy="38" r="1.1" /><circle cx="170" cy="22" r="0.9" />
45 + <circle cx="292" cy="46" r="1.2" /><circle cx="405" cy="18" r="0.8" />
46 + <circle cx="498" cy="52" r="1.1" /><circle cx="552" cy="26" r="0.9" />
47 + </g>
48 + <path class="sg-aurora-veil-r1" d="M-20 84 c75 -30 225 30 300 0 c75 -30 225 30 300 0 c75 -30 225 30 300 0" />
49 + <path class="sg-aurora-veil-r2" d="M-20 112 c90 -22 210 24 300 0 c90 -22 210 24 300 0 c90 -22 210 24 300 0" />
50 + <path class="sg-aurora-veil-r3" d="M-20 62 c70 -18 230 20 300 0 c70 -18 230 20 300 0 c70 -18 230 20 300 0" />
51 + </svg>
52 +</div>
53 +<style>
54 + .sg-aurora-veil {
55 + width: 100%;
56 + min-width: 260px;
57 + height: var(--sg-size);
58 + overflow: hidden;
59 + border-radius: 8px;
60 + }
61 + .sg-aurora-veil svg {
62 + display: block;
63 + width: 100%;
64 + height: 100%;
65 + }
66 + .sg-aurora-veil-stars circle {
67 + fill: #cdd6f4;
68 + opacity: 0.6;
69 + }
70 + .sg-aurora-veil path {
71 + fill: none;
72 + stroke-linecap: round;
73 + mix-blend-mode: screen;
74 + animation: sg-aurora-veil-drift var(--sg-duration) linear infinite;
75 + }
76 + .sg-aurora-veil-r1 {
77 + stroke: var(--sg-color);
78 + stroke-width: 38;
79 + filter: blur(13px);
80 + opacity: 0.75;
81 + }
82 + .sg-aurora-veil-r2 {
83 + stroke: #7f77dd;
84 + stroke-width: 30;
85 + filter: blur(15px);
86 + opacity: 0.6;
87 + animation-duration: calc(var(--sg-duration) * 1.6);
88 + }
89 + .sg-aurora-veil-r3 {
90 + stroke: #3ec7de;
91 + stroke-width: 22;
92 + filter: blur(11px);
93 + opacity: 0.5;
94 + animation-duration: calc(var(--sg-duration) * 2.3);
95 + }
96 + @keyframes sg-aurora-veil-drift {
97 + from { translate: 0 0; }
98 + to { translate: -300px 0; }
99 + }
100 +</style>
added snippets/backgrounds/constellation-drift.html +103 −0
@@ -0,0 +1,103 @@
1 +<!--
2 + ============================================================
3 + SVGarden — https://www.svgarden.dev
4 + Author : Simon-Pierre Boucher
5 + Contact: contact@spboucher.ai
6 + File : snippets/backgrounds/constellation-drift.html
7 + Desc : Drifting star field with twinkling constellation lines
8 + ============================================================
9 +-->
10 +<!--svgarden
11 +title: Constellation drift
12 +slug: constellation-drift
13 +category: backgrounds
14 +tags: [background, stars, constellation, ambient]
15 +difficulty: intermediate
16 +techniques: [svg-use-tiling, translate-property, animation-delay, seamless-tiling]
17 +how_it_works: >
18 + The whole star field is drawn once as a 320-unit tile, then a <use> element
19 + stamps a second copy exactly one tile-width to the right. Animating the
20 + group's translate by that same 320 units means frame one and the last frame
21 + are pixel-identical — the seamless-loop rule is: duplicate, offset by one
22 + period, translate by one period. Twinkling is a single opacity keyframe
23 + shared by every star; scattering the stars across three delay groups breaks
24 + the synchronization so the sky shimmers instead of blinking. Styles applied
25 + to the original tile automatically render in the <use> clone, so the
26 + twinkle comes along for free.
27 +customizable:
28 + - { var: "--sg-color", label: "Star color", type: color, default: "#CFD8FF" }
29 + - { var: "--sg-size", label: "Height", type: range, min: 100, max: 300, default: 170, unit: px }
30 + - { var: "--sg-duration", label: "Drift speed", type: range, min: 10, max: 80, step: 1, default: 32, unit: s }
31 +created: 2026-08-10
32 +-->
33 +<div class="sg-constellation-drift" style="--sg-color:#CFD8FF; --sg-size:170px; --sg-duration:32s;">
34 + <svg viewBox="0 0 320 180" preserveAspectRatio="xMidYMid slice" aria-hidden="true" focusable="false">
35 + <defs>
36 + <linearGradient id="sg-constellation-drift-sky" x1="0" y1="0" x2="0" y2="1">
37 + <stop offset="0" stop-color="#0a1228" />
38 + <stop offset="1" stop-color="#151b38" />
39 + </linearGradient>
40 + </defs>
41 + <rect x="0" y="0" width="320" height="180" fill="url(#sg-constellation-drift-sky)" />
42 + <g class="sg-constellation-drift-roll">
43 + <g id="sg-constellation-drift-tile">
44 + <polyline class="sg-constellation-drift-link" points="34,48 78,30 122,58 150,34" />
45 + <polyline class="sg-constellation-drift-link" points="190,120 232,96 268,128 298,102" />
46 + <polyline class="sg-constellation-drift-link" points="60,140 96,116 140,148" />
47 + <circle class="sg-constellation-drift-s1" cx="34" cy="48" r="1.8" />
48 + <circle class="sg-constellation-drift-s2" cx="78" cy="30" r="1.4" />
49 + <circle class="sg-constellation-drift-s3" cx="122" cy="58" r="1.7" />
50 + <circle class="sg-constellation-drift-s1" cx="150" cy="34" r="1.3" />
51 + <circle class="sg-constellation-drift-s2" cx="190" cy="120" r="1.6" />
52 + <circle class="sg-constellation-drift-s3" cx="232" cy="96" r="1.9" />
53 + <circle class="sg-constellation-drift-s1" cx="268" cy="128" r="1.4" />
54 + <circle class="sg-constellation-drift-s2" cx="298" cy="102" r="1.6" />
55 + <circle class="sg-constellation-drift-s3" cx="60" cy="140" r="1.5" />
56 + <circle class="sg-constellation-drift-s1" cx="96" cy="116" r="1.8" />
57 + <circle class="sg-constellation-drift-s2" cx="140" cy="148" r="1.3" />
58 + <circle class="sg-constellation-drift-s3" cx="205" cy="24" r="1.2" />
59 + <circle class="sg-constellation-drift-s1" cx="255" cy="52" r="1.0" />
60 + <circle class="sg-constellation-drift-s2" cx="20" cy="90" r="1.1" />
61 + <circle class="sg-constellation-drift-s3" cx="305" cy="160" r="1.2" />
62 + </g>
63 + <use href="#sg-constellation-drift-tile" x="320" />
64 + </g>
65 + </svg>
66 +</div>
67 +<style>
68 + .sg-constellation-drift {
69 + width: 100%;
70 + min-width: 240px;
71 + height: var(--sg-size);
72 + overflow: hidden;
73 + border-radius: 8px;
74 + }
75 + .sg-constellation-drift svg {
76 + display: block;
77 + width: 100%;
78 + height: 100%;
79 + }
80 + .sg-constellation-drift-roll {
81 + animation: sg-constellation-drift-move var(--sg-duration) linear infinite;
82 + }
83 + .sg-constellation-drift-link {
84 + fill: none;
85 + stroke: var(--sg-color);
86 + stroke-width: 0.6;
87 + opacity: 0.25;
88 + }
89 + .sg-constellation-drift circle {
90 + fill: var(--sg-color);
91 + animation: sg-constellation-drift-twinkle 3.8s ease-in-out infinite;
92 + }
93 + .sg-constellation-drift-s2 { animation-delay: -1.3s; }
94 + .sg-constellation-drift-s3 { animation-delay: -2.6s; }
95 + @keyframes sg-constellation-drift-move {
96 + from { translate: 0 0; }
97 + to { translate: -320px 0; }
98 + }
99 + @keyframes sg-constellation-drift-twinkle {
100 + 0%, 100% { opacity: 1; }
101 + 50% { opacity: 0.3; }
102 + }
103 +</style>
added snippets/backgrounds/mesh-wave-grid.html +93 −0
@@ -0,0 +1,93 @@
1 +<!--
2 + ============================================================
3 + SVGarden — https://www.svgarden.dev
4 + Author : Simon-Pierre Boucher
5 + Contact: contact@spboucher.ai
6 + File : snippets/backgrounds/mesh-wave-grid.html
7 + Desc : Pseudo-3D wireframe wave grid rolling toward the viewer
8 + ============================================================
9 +-->
10 +<!--svgarden
11 +title: Mesh wave grid
12 +slug: mesh-wave-grid
13 +category: backgrounds
14 +tags: [background, wireframe, pseudo-3d, smil]
15 +difficulty: advanced
16 +techniques: [SMIL-path-morph, perspective-spacing, phase-offset-begin, layered-opacity]
17 +how_it_works: >
18 + There is no 3D here — the depth is drawn, not computed. Row spacing widens
19 + from 14 units at the horizon to 26 near the viewer, stroke opacity climbs
20 + from 0.25 to 0.9, and static rays fan out from a vanishing point; those
21 + three perspective cues do all the work. Each row then morphs between the
22 + same wave shape and its inverted twin via SMIL, and giving row n a begin
23 + offset of −0.55n seconds turns identical animations into a phase gradient,
24 + which reads as a swell rolling diagonally across the mesh. Same-structure
25 + d strings (one M, two relative curves) keep every tween smooth.
26 +customizable:
27 + - { var: "--sg-color", label: "Grid color", type: color, default: "#7F77DD" }
28 + - { var: "--sg-bg", label: "Sky color", type: color, default: "#0D0B1E" }
29 + - { var: "--sg-size", label: "Height", type: range, min: 100, max: 280, default: 170, unit: px }
30 +created: 2026-08-10
31 +-->
32 +<div class="sg-mesh-wave-grid" style="--sg-color:#7F77DD; --sg-bg:#0D0B1E; --sg-size:170px;">
33 + <svg viewBox="0 0 400 210" preserveAspectRatio="none" aria-hidden="true" focusable="false">
34 + <defs>
35 + <linearGradient id="sg-mesh-wave-grid-sky" x1="0" y1="0" x2="0" y2="1">
36 + <stop class="sg-mesh-wave-grid-skytop" offset="0" />
37 + <stop offset="1" stop-color="#05060d" />
38 + </linearGradient>
39 + </defs>
40 + <rect x="0" y="0" width="400" height="210" fill="url(#sg-mesh-wave-grid-sky)" />
41 + <g class="sg-mesh-wave-grid-rays">
42 + <line x1="200" y1="26" x2="-60" y2="210" /><line x1="200" y1="26" x2="30" y2="210" />
43 + <line x1="200" y1="26" x2="115" y2="210" /><line x1="200" y1="26" x2="200" y2="210" />
44 + <line x1="200" y1="26" x2="285" y2="210" /><line x1="200" y1="26" x2="370" y2="210" />
45 + <line x1="200" y1="26" x2="460" y2="210" />
46 + </g>
47 + <g class="sg-mesh-wave-grid-rows">
48 + <path opacity="0.25"><animate attributeName="d" dur="5s" begin="0s" repeatCount="indefinite"
49 + values="M0 40 c 66 -3 134 3 200 0 c 66 -3 134 3 200 0;M0 40 c 66 3 134 -3 200 0 c 66 3 134 -3 200 0;M0 40 c 66 -3 134 3 200 0 c 66 -3 134 3 200 0" /></path>
50 + <path opacity="0.32"><animate attributeName="d" dur="5s" begin="-0.55s" repeatCount="indefinite"
51 + values="M0 54 c 66 -4 134 4 200 0 c 66 -4 134 4 200 0;M0 54 c 66 4 134 -4 200 0 c 66 4 134 -4 200 0;M0 54 c 66 -4 134 4 200 0 c 66 -4 134 4 200 0" /></path>
52 + <path opacity="0.4"><animate attributeName="d" dur="5s" begin="-1.1s" repeatCount="indefinite"
53 + values="M0 70 c 66 -6 134 6 200 0 c 66 -6 134 6 200 0;M0 70 c 66 6 134 -6 200 0 c 66 6 134 -6 200 0;M0 70 c 66 -6 134 6 200 0 c 66 -6 134 6 200 0" /></path>
54 + <path opacity="0.5"><animate attributeName="d" dur="5s" begin="-1.65s" repeatCount="indefinite"
55 + values="M0 88 c 66 -7 134 7 200 0 c 66 -7 134 7 200 0;M0 88 c 66 7 134 -7 200 0 c 66 7 134 -7 200 0;M0 88 c 66 -7 134 7 200 0 c 66 -7 134 7 200 0" /></path>
56 + <path opacity="0.6"><animate attributeName="d" dur="5s" begin="-2.2s" repeatCount="indefinite"
57 + values="M0 108 c 66 -9 134 9 200 0 c 66 -9 134 9 200 0;M0 108 c 66 9 134 -9 200 0 c 66 9 134 -9 200 0;M0 108 c 66 -9 134 9 200 0 c 66 -9 134 9 200 0" /></path>
58 + <path opacity="0.7"><animate attributeName="d" dur="5s" begin="-2.75s" repeatCount="indefinite"
59 + values="M0 130 c 66 -10 134 10 200 0 c 66 -10 134 10 200 0;M0 130 c 66 10 134 -10 200 0 c 66 10 134 -10 200 0;M0 130 c 66 -10 134 10 200 0 c 66 -10 134 10 200 0" /></path>
60 + <path opacity="0.8"><animate attributeName="d" dur="5s" begin="-3.3s" repeatCount="indefinite"
61 + values="M0 154 c 66 -12 134 12 200 0 c 66 -12 134 12 200 0;M0 154 c 66 12 134 -12 200 0 c 66 12 134 -12 200 0;M0 154 c 66 -12 134 12 200 0 c 66 -12 134 12 200 0" /></path>
62 + <path opacity="0.9"><animate attributeName="d" dur="5s" begin="-3.85s" repeatCount="indefinite"
63 + values="M0 180 c 66 -13 134 13 200 0 c 66 -13 134 13 200 0;M0 180 c 66 13 134 -13 200 0 c 66 13 134 -13 200 0;M0 180 c 66 -13 134 13 200 0 c 66 -13 134 13 200 0" /></path>
64 + </g>
65 + </svg>
66 +</div>
67 +<style>
68 + .sg-mesh-wave-grid {
69 + width: 100%;
70 + min-width: 240px;
71 + height: var(--sg-size);
72 + overflow: hidden;
73 + border-radius: 8px;
74 + }
75 + .sg-mesh-wave-grid svg {
76 + display: block;
77 + width: 100%;
78 + height: 100%;
79 + }
80 + .sg-mesh-wave-grid-skytop {
81 + stop-color: var(--sg-bg);
82 + }
83 + .sg-mesh-wave-grid-rays line {
84 + stroke: var(--sg-color);
85 + stroke-width: 1;
86 + opacity: 0.12;
87 + }
88 + .sg-mesh-wave-grid-rows path {
89 + fill: none;
90 + stroke: var(--sg-color);
91 + stroke-width: 1.5;
92 + }
93 +</style>
added snippets/backgrounds/rain-window.html +112 −0
@@ -0,0 +1,112 @@
1 +<!--
2 + ============================================================
3 + SVGarden — https://www.svgarden.dev
4 + Author : Simon-Pierre Boucher
5 + Contact: contact@spboucher.ai
6 + File : snippets/backgrounds/rain-window.html
7 + Desc : Rain streaks on a window with wet-glass refraction
8 + ============================================================
9 +-->
10 +<!--svgarden
11 +title: Rain on a window
12 +slug: rain-window
13 +category: backgrounds
14 +tags: [background, rain, turbulence, displacement]
15 +difficulty: advanced
16 +techniques: [feTurbulence, feDisplacementMap, animateMotion, staggered-delays]
17 +how_it_works: >
18 + The refraction is the trick worth stealing: the backdrop gradient is drawn
19 + twice, and the top copy runs through feTurbulence noise feeding a
20 + feDisplacementMap, which shoves its pixels around like light bending through
21 + wet glass. A SMIL animate slowly mutates the noise's baseFrequency so the
22 + distortion crawls instead of freezing. In front, eight streak lines share
23 + one falling keyframe with scattered durations and negative delays, while
24 + two heavier droplets wander down a wobbly animateMotion path — the slow
25 + droplets against fast streaks give the scene its depth.
26 +customizable:
27 + - { var: "--sg-color", label: "Glass tint", type: color, default: "#31445A" }
28 + - { var: "--sg-size", label: "Height", type: range, min: 100, max: 300, default: 180, unit: px }
29 + - { var: "--sg-duration", label: "Rain speed", type: range, min: 0.8, max: 4, step: 0.1, default: 1.6, unit: s }
30 +created: 2026-08-10
31 +-->
32 +<div class="sg-rain-window" style="--sg-color:#31445A; --sg-size:180px; --sg-duration:1.6s;">
33 + <svg viewBox="0 0 320 180" preserveAspectRatio="xMidYMid slice" aria-hidden="true" focusable="false">
34 + <defs>
35 + <linearGradient id="sg-rain-window-sky" x1="0" y1="0" x2="0" y2="1">
36 + <stop class="sg-rain-window-tint" offset="0" />
37 + <stop offset="1" stop-color="#0f1722" />
38 + </linearGradient>
39 + <filter id="sg-rain-window-warp" x="-10%" y="-10%" width="120%" height="120%">
40 + <feTurbulence type="fractalNoise" baseFrequency="0.012 0.05" numOctaves="2" result="sg-noise">
41 + <animate attributeName="baseFrequency" dur="9s" repeatCount="indefinite"
42 + values="0.012 0.05;0.016 0.06;0.012 0.05" />
43 + </feTurbulence>
44 + <feDisplacementMap in="SourceGraphic" in2="sg-noise" scale="16" />
45 + </filter>
46 + </defs>
47 + <rect x="0" y="0" width="320" height="180" fill="url(#sg-rain-window-sky)" />
48 + <rect class="sg-rain-window-wet" x="-20" y="-20" width="360" height="220" fill="url(#sg-rain-window-sky)" />
49 + <g class="sg-rain-window-streaks">
50 + <line x1="30" y1="-30" x2="27" y2="-6" />
51 + <line x1="74" y1="-42" x2="71" y2="-14" />
52 + <line x1="118" y1="-26" x2="115" y2="-4" />
53 + <line x1="162" y1="-38" x2="159" y2="-10" />
54 + <line x1="206" y1="-30" x2="203" y2="-8" />
55 + <line x1="248" y1="-44" x2="245" y2="-16" />
56 + <line x1="284" y1="-28" x2="281" y2="-6" />
57 + <line x1="308" y1="-40" x2="305" y2="-14" />
58 + </g>
59 + <circle class="sg-rain-window-drop1" cx="96" cy="0" r="2.6">
60 + <animateMotion dur="8s" begin="-2s" repeatCount="indefinite"
61 + path="M0 -10 q 4 30 -3 58 q -5 26 2 52 q 5 26 -2 50 q -4 20 1 44" />
62 + </circle>
63 + <circle class="sg-rain-window-drop2" cx="228" cy="0" r="2.1">
64 + <animateMotion dur="11s" begin="-6s" repeatCount="indefinite"
65 + path="M0 -10 q -4 34 3 62 q 5 28 -3 54 q -4 24 3 48 q 3 18 -2 40" />
66 + </circle>
67 + </svg>
68 +</div>
69 +<style>
70 + .sg-rain-window {
71 + width: 100%;
72 + min-width: 240px;
73 + height: var(--sg-size);
74 + overflow: hidden;
75 + border-radius: 8px;
76 + }
77 + .sg-rain-window svg {
78 + display: block;
79 + width: 100%;
80 + height: 100%;
81 + }
82 + .sg-rain-window-tint {
83 + stop-color: var(--sg-color);
84 + }
85 + .sg-rain-window-wet {
86 + filter: url(#sg-rain-window-warp);
87 + opacity: 0.75;
88 + }
89 + .sg-rain-window-streaks line {
90 + stroke: #dbe6f3;
91 + stroke-width: 1.4;
92 + stroke-linecap: round;
93 + opacity: 0.32;
94 + animation: sg-rain-window-fall var(--sg-duration) linear infinite;
95 + }
96 + .sg-rain-window-streaks line:nth-of-type(2) { animation-duration: calc(var(--sg-duration) * 0.8); animation-delay: calc(var(--sg-duration) * -0.4); }
97 + .sg-rain-window-streaks line:nth-of-type(3) { animation-duration: calc(var(--sg-duration) * 1.25); animation-delay: calc(var(--sg-duration) * -0.9); }
98 + .sg-rain-window-streaks line:nth-of-type(4) { animation-duration: calc(var(--sg-duration) * 0.7); animation-delay: calc(var(--sg-duration) * -0.2); }
99 + .sg-rain-window-streaks line:nth-of-type(5) { animation-duration: calc(var(--sg-duration) * 1.1); animation-delay: calc(var(--sg-duration) * -0.7); }
100 + .sg-rain-window-streaks line:nth-of-type(6) { animation-duration: calc(var(--sg-duration) * 0.85); animation-delay: calc(var(--sg-duration) * -0.55); }
101 + .sg-rain-window-streaks line:nth-of-type(7) { animation-duration: calc(var(--sg-duration) * 1.3); animation-delay: calc(var(--sg-duration) * -0.3); }
102 + .sg-rain-window-streaks line:nth-of-type(8) { animation-duration: calc(var(--sg-duration) * 0.75); animation-delay: calc(var(--sg-duration) * -0.65); }
103 + .sg-rain-window-drop1,
104 + .sg-rain-window-drop2 {
105 + fill: #e7eef8;
106 + opacity: 0.5;
107 + }
108 + @keyframes sg-rain-window-fall {
109 + from { transform: translateY(0); }
110 + to { transform: translateY(230px); }
111 + }
112 +</style>
added snippets/backgrounds/topo-lines-morph.html +94 −0
@@ -0,0 +1,94 @@
1 +<!--
2 + ============================================================
3 + SVGarden — https://www.svgarden.dev
4 + Author : Simon-Pierre Boucher
5 + Contact: contact@spboucher.ai
6 + File : snippets/backgrounds/topo-lines-morph.html
7 + Desc : Topographic contour lines slowly morphing, map-style
8 + ============================================================
9 +-->
10 +<!--svgarden
11 +title: Topo lines morph
12 +slug: topo-lines-morph
13 +category: backgrounds
14 +tags: [background, topographic, morph, smil]
15 +difficulty: advanced
16 +techniques: [SMIL-path-morph, matched-path-structure, staggered-durations, layered-opacity]
17 +how_it_works: >
18 + Five nested contour rings each morph between two variants of themselves with
19 + a SMIL animate on the d attribute — and as with every path morph, the two
20 + variants must share the exact same command skeleton (here one M plus four
21 + cubics) or the browser snaps instead of tweening. The meditative quality
22 + comes from the timing design: every ring gets a different duration and a
23 + negative begin offset, so no two contours ever breathe in phase, exactly
24 + like elevation lines redrawn between survey years. Opacity falls off toward
25 + the outer rings to fake the fading detail of a real map.
26 +customizable:
27 + - { var: "--sg-color", label: "Contour color", type: color, default: "#7F77DD" }
28 + - { var: "--sg-bg", label: "Map background", type: color, default: "#101623" }
29 + - { var: "--sg-size", label: "Height", type: range, min: 100, max: 300, default: 180, unit: px }
30 +created: 2026-08-10
31 +-->
32 +<div class="sg-topo-lines-morph" style="--sg-color:#7F77DD; --sg-bg:#101623; --sg-size:180px;">
33 + <svg viewBox="0 0 240 160" preserveAspectRatio="xMidYMid slice" aria-hidden="true" focusable="false">
34 + <rect class="sg-topo-lines-morph-paper" x="0" y="0" width="240" height="160" />
35 + <g class="sg-topo-lines-morph-rings">
36 + <path opacity="0.95">
37 + <animate attributeName="d" dur="14s" begin="-2s" repeatCount="indefinite"
38 + values="M130 78 C130 84.6 124.6 90 118 90 C111.4 90 106 84.6 106 78 C106 71.4 111.4 66 118 66 C124.6 66 130 71.4 130 78 Z;
39 + M133 77 C132 84 125 92 117 91 C110 90 104 84 105 77 C106 70 112 65 119 66 C126 67 134 70 133 77 Z;
40 + M130 78 C130 84.6 124.6 90 118 90 C111.4 90 106 84.6 106 78 C106 71.4 111.4 66 118 66 C124.6 66 130 71.4 130 78 Z" />
41 + </path>
42 + <path opacity="0.8">
43 + <animate attributeName="d" dur="18s" begin="-5s" repeatCount="indefinite"
44 + values="M144 78 C144 92.4 132.4 104 118 104 C103.6 104 92 92.4 92 78 C92 63.6 103.6 52 118 52 C132.4 52 144 63.6 144 78 Z;
45 + M149 75 C147 90 134 108 116 106 C100 104 88 92 90 75 C92 60 104 49 120 51 C136 53 151 60 149 75 Z;
46 + M144 78 C144 92.4 132.4 104 118 104 C103.6 104 92 92.4 92 78 C92 63.6 103.6 52 118 52 C132.4 52 144 63.6 144 78 Z" />
47 + </path>
48 + <path opacity="0.65">
49 + <animate attributeName="d" dur="22s" begin="-8s" repeatCount="indefinite"
50 + values="M158 78 C158 100 140 118 118 118 C96 118 78 100 78 78 C78 56 96 38 118 38 C140 38 158 56 158 78 Z;
51 + M164 74 C162 96 142 122 116 119 C92 116 72 98 75 74 C78 52 96 35 121 38 C144 41 166 52 164 74 Z;
52 + M158 78 C158 100 140 118 118 118 C96 118 78 100 78 78 C78 56 96 38 118 38 C140 38 158 56 158 78 Z" />
53 + </path>
54 + <path opacity="0.5">
55 + <animate attributeName="d" dur="26s" begin="-11s" repeatCount="indefinite"
56 + values="M172 78 C172 107.8 147.8 132 118 132 C88.2 132 64 107.8 64 78 C64 48.2 88.2 24 118 24 C147.8 24 172 48.2 172 78 Z;
57 + M179 73 C176 102 152 136 115 132 C82 128 58 106 61 73 C64 44 88 21 122 25 C152 29 182 44 179 73 Z;
58 + M172 78 C172 107.8 147.8 132 118 132 C88.2 132 64 107.8 64 78 C64 48.2 88.2 24 118 24 C147.8 24 172 48.2 172 78 Z" />
59 + </path>
60 + <path opacity="0.35">
61 + <animate attributeName="d" dur="30s" begin="-14s" repeatCount="indefinite"
62 + values="M186 78 C186 115.5 155.5 146 118 146 C80.5 146 50 115.5 50 78 C50 40.5 80.5 10 118 10 C155.5 10 186 40.5 186 78 Z;
63 + M193 72 C189 108 158 150 114 145 C76 141 44 112 48 72 C52 36 82 8 124 12 C158 15 197 36 193 72 Z;
64 + M186 78 C186 115.5 155.5 146 118 146 C80.5 146 50 115.5 50 78 C50 40.5 80.5 10 118 10 C155.5 10 186 40.5 186 78 Z" />
65 + </path>
66 + </g>
67 + <circle class="sg-topo-lines-morph-peak" cx="118" cy="78" r="2" />
68 + </svg>
69 +</div>
70 +<style>
71 + .sg-topo-lines-morph {
72 + width: 100%;
73 + min-width: 240px;
74 + height: var(--sg-size);
75 + overflow: hidden;
76 + border-radius: 8px;
77 + }
78 + .sg-topo-lines-morph svg {
79 + display: block;
80 + width: 100%;
81 + height: 100%;
82 + }
83 + .sg-topo-lines-morph-paper {
84 + fill: var(--sg-bg);
85 + }
86 + .sg-topo-lines-morph-rings path {
87 + fill: none;
88 + stroke: var(--sg-color);
89 + stroke-width: 1.6;
90 + }
91 + .sg-topo-lines-morph-peak {
92 + fill: var(--sg-color);
93 + }
94 +</style>
95