Copy URL or Code
Paste to your AI coding assistant and customize:
Done. Your AI handles the rest.
Fully customizable. Four snowflake patterns (orbs, ferns, arrows, stars) with cursor-reactive physics. Customize color, density, speed, and opacity.
/*
================================================================================
AI COMPONENT: MagicSnow
================================================================================
INSTRUCTIONS FOR AI ASSISTANTS (Claude Code, Cursor, Codex, etc.):
- This is a self-contained React component with NO external dependencies
- Copy this entire file to: components/MagicSnow.tsx
- Import with: import MagicSnow from "@/components/MagicSnow"
- Works with Next.js, Vite, Create React App, etc.
================================================================================
PROPS REFERENCE (all optional)
================================================================================
| Prop | Type | Default | Description |
|--------------|--------|----------------------|--------------------------------|
| size | number | 320 | Width and height in pixels |
| cursorRadius | number | 60 | Radius of cursor push effect |
| color | string | "hsl(0, 0%, 100%)" | Snowflake color (any CSS) |
| density | number | 1 | Particle multiplier (0.5-2) |
| speed | number | 1 | Fall speed multiplier (0.5-2) |
| opacity | number | 0.82 | Overall opacity (0-1) |
| className | string | "" | Additional CSS classes |
================================================================================
CUSTOMIZATION EXAMPLES
================================================================================
// Basic usage
<MagicSnow />
// Larger canvas with more snowflakes
<MagicSnow size={600} density={1.5} />
// Colored snowflakes (light blue)
<MagicSnow color="rgba(173, 216, 230, 0.9)" />
// Slower, more ethereal feel
<MagicSnow speed={0.5} opacity={0.6} />
// Stronger cursor push effect
<MagicSnow cursorRadius={120} />
// Full-screen background
<div className="fixed inset-0" style={{ backgroundColor: "#0a0a14" }}>
<MagicSnow size={Math.max(window.innerWidth, window.innerHeight)} />
</div>
================================================================================
ARCHITECTURE NOTES (for AI understanding)
================================================================================
- CrystalSprite: Pre-renders 4 snowflake patterns to offscreen canvas for perf
- Flake: Individual particle with position, rotation, speed properties
- Patterns: Orb (circle), Fern (branched), Arrow (pointed), Star (lines)
- Physics: Cursor repulsion uses distance-based force falloff
- Rendering: Uses requestAnimationFrame for smooth 60fps animation
================================================================================
SOURCE CODE
================================================================================
*/
"use client";
import React, { useRef, useEffect } from "react";
export interface MagicSnowProps {
/** Width and height of the canvas */
size?: number;
/** Cursor repulsion radius */
cursorRadius?: number;
/** Snowflake color (CSS color string) */
color?: string;
/** Particle density multiplier (0.5 = half, 2 = double) */
density?: number;
/** Fall speed multiplier (0.5 = slower, 2 = faster) */
speed?: number;
/** Overall opacity (0-1) */
opacity?: number;
/** Additional CSS classes */
className?: string;
}
type Pointer = {
range: number;
x?: number;
y?: number;
};
const FlakeStyle = {
Orb: 0,
Fern: 1,
Arrow: 2,
Star: 3,
} as const;
type FlakeType = (typeof FlakeStyle)[keyof typeof FlakeStyle];
const rand = (min: number = 0, max: number = 1) => {
const val = crypto.getRandomValues(new Uint32Array(1))[0] / 2 ** 32;
return min + val * (max - min);
};
class CrystalSprite {
readonly canvas: HTMLCanvasElement;
private readonly ctx: CanvasRenderingContext2D | null;
private readonly style: FlakeType;
private readonly stroke = 1;
private readonly extent = 10;
constructor(style: FlakeType, color: string = "hsl(0, 0%, 100%)") {
this.canvas = document.createElement("canvas");
this.ctx = this.canvas.getContext("2d");
this.style = style;
const { devicePixelRatio } = window;
const dim = this.extent * 2 * devicePixelRatio;
this.canvas.width = dim;
this.canvas.height = dim;
if (this.ctx) {
this.ctx.fillStyle = color;
this.ctx.strokeStyle = color;
this.ctx.lineCap = "round";
this.ctx.lineJoin = "round";
this.ctx.lineWidth = this.stroke;
this.ctx.scale(devicePixelRatio, devicePixelRatio);
this.render();
}
}
private render(): void {
this.ctx?.save();
this.ctx?.translate(this.extent, this.extent);
if (this.style === FlakeStyle.Orb) {
this.renderOrb();
return;
}
const arms = 6;
for (let i = 0; i < arms; i++) {
this.ctx?.rotate(Math.PI / (arms / 2));
switch (this.style) {
case FlakeStyle.Fern:
this.renderFern();
break;
case FlakeStyle.Arrow:
this.renderArrow();
break;
default:
this.renderStar();
}
}
this.ctx?.restore();
}
private renderStar(): void {
const len = this.extent - 1;
this.ctx?.beginPath();
this.ctx?.moveTo(0, 0);
this.ctx?.lineTo(0, len);
this.ctx?.closePath();
this.ctx?.stroke();
}
private renderFern(): void {
const len = this.extent - 0.5;
const branchPos = -len * 0.52;
const branchLen = len * 0.33;
this.ctx?.beginPath();
this.ctx?.moveTo(0, 0);
this.ctx?.lineTo(0, -len);
this.ctx?.moveTo(0, branchPos);
this.ctx?.lineTo(-branchLen, branchPos - branchLen);
this.ctx?.moveTo(0, branchPos);
this.ctx?.lineTo(branchLen, branchPos - branchLen);
this.ctx?.closePath();
this.ctx?.stroke();
}
private renderOrb(): void {
this.ctx?.beginPath();
this.ctx?.arc(0, 0, this.extent / 2, 0, Math.PI * 2);
this.ctx?.closePath();
this.ctx?.fill();
}
private renderArrow(): void {
const len = this.extent - 0.5;
const tipStart = -len * 0.58;
const tipEnd = -len;
const tipWidth = len * 0.22;
this.ctx?.beginPath();
this.ctx?.moveTo(0, 0);
this.ctx?.lineTo(0, -len * 0.5);
this.ctx?.moveTo(0, tipEnd);
this.ctx?.lineTo(-tipWidth, tipStart);
this.ctx?.lineTo(0, tipStart + len * 0.1);
this.ctx?.lineTo(tipWidth, tipStart);
this.ctx?.closePath();
this.ctx?.stroke();
this.ctx?.fill();
}
}
class Flake {
private x: number;
private y: number;
private angle = rand(0, Math.PI);
private readonly pushForce = 48;
private readonly sprite: HTMLCanvasElement;
private readonly extent: number;
private readonly spin: number;
private readonly driftX: number;
private readonly fallSpeed: number;
constructor(
width: number,
height: number,
extent: number,
sprite: HTMLCanvasElement,
speedMultiplier: number = 1
) {
this.x = rand(0, width);
this.y = rand(0, height);
this.extent = extent;
this.sprite = sprite;
this.spin = rand(-0.012, 0.012) * speedMultiplier;
this.driftX = rand(-0.22, 0.22) * speedMultiplier;
this.fallSpeed = rand(1, 3) * speedMultiplier;
}
draw(ctx: CanvasRenderingContext2D): void {
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.angle);
ctx.drawImage(
this.sprite,
-this.extent,
-this.extent,
this.extent * 2,
this.extent * 2
);
ctx.restore();
}
update(pointer: Pointer, width: number, height: number): void {
this.x += this.driftX;
this.y += this.fallSpeed;
this.angle += this.spin;
this.angle %= 2 * Math.PI;
const dx = (pointer.x ?? -pointer.range) - this.x;
const dy = (pointer.y ?? -pointer.range) - this.y;
const dist = Math.hypot(dx, dy);
if (dist < pointer.range) {
const forceX = dx / dist;
const forceY = dy / dist;
const strength = (pointer.range - dist) / pointer.range;
const repelX = forceX * strength * this.pushForce;
const repelY = forceY * strength * this.pushForce;
this.x -= repelX;
this.y -= repelY;
}
const exitLeft = this.x < -pointer.range;
const exitRight = this.x > width + pointer.range;
const exitBottom = this.y > height + pointer.range;
if (exitLeft || exitRight || exitBottom) {
this.x = rand(0, width);
this.y = -this.extent;
}
}
}
/**
* Magic Snow - Interactive snowfall animation with cursor push effect
* Move your cursor over the canvas to push snowflakes away
*/
export default function MagicSnow({
size = 320,
cursorRadius = 60,
color = "hsl(0, 0%, 100%)",
density = 1,
speed = 1,
opacity = 0.82,
className = "",
}: MagicSnowProps) {
const canvasRef = useRef<HTMLCanvasElement>(null);
const pointer = useRef<Pointer>({ range: cursorRadius });
const frameRef = useRef(0);
const flakes = useRef<Flake[]>([]);
useEffect(() => {
const canvas = canvasRef.current;
const ctx = canvas?.getContext("2d");
if (!canvas || !ctx) return;
const sprites: CrystalSprite[] = [];
for (let s = 0; s <= 3; ++s) {
sprites.push(new CrystalSprite(s as FlakeType, color));
}
const animate = () => {
const { width, height } = getDimensions();
ctx.clearRect(0, 0, width, height);
ctx.globalAlpha = opacity;
flakes.current.forEach((flake) => {
flake.update(pointer.current, width, height);
flake.draw(ctx);
});
frameRef.current = requestAnimationFrame(animate);
};
const spawnFlakes = () => {
const { width, height } = getDimensions();
const baseCount = Math.round((width * height) / 850);
const maxCount = 1000;
const count = Math.min(Math.round(baseCount * density), maxCount);
const minExtent = 3;
const maxExtent = 9;
flakes.current = [];
for (let i = 0; i < count; i++) {
const extent = rand(minExtent, maxExtent);
const style = Math.round(rand(0, 3)) as FlakeType;
const flake = new Flake(width, height, extent, sprites[style].canvas, speed);
flakes.current.push(flake);
}
};
const getDimensions = () => {
const { devicePixelRatio } = window;
const width = canvas.width / devicePixelRatio;
const height = canvas.height / devicePixelRatio;
return { width, height };
};
const onPointerMove = (e: Event) => {
const event = e as PointerEvent;
const rect = canvas.getBoundingClientRect();
pointer.current.x = event.clientX - rect.left;
pointer.current.y = event.clientY - rect.top;
};
const onPointerLeave = () => {
const range = pointer.current.range;
pointer.current.x = -range;
pointer.current.y = -range;
};
const setup = () => {
const { devicePixelRatio } = window;
canvas.width = size * devicePixelRatio;
canvas.height = size * devicePixelRatio;
canvas.style.width = size + "px";
canvas.style.height = size + "px";
ctx.scale(devicePixelRatio, devicePixelRatio);
spawnFlakes();
};
setup();
animate();
const events = {
pointerdown: onPointerMove,
pointermove: onPointerMove,
pointerout: onPointerLeave,
pointerup: onPointerLeave,
};
Object.entries(events).forEach(([evt, handler]) => {
canvas.addEventListener(evt, handler);
});
return () => {
Object.entries(events).forEach(([evt, handler]) => {
canvas.removeEventListener(evt, handler);
});
cancelAnimationFrame(frameRef.current);
};
}, [size, cursorRadius, color, density, speed, opacity]);
return (
<canvas
ref={canvasRef}
className={`block touch-none select-none ${className}`}
aria-label="Snow falling with cursor-controlled push effect"
style={{
width: size,
height: size,
}}
/>
);
}
export { MagicSnow };/*
================================================================================
AI COMPONENT: DripDrop
================================================================================
INSTRUCTIONS FOR AI ASSISTANTS (Claude Code, Cursor, Codex, etc.):
- This is a self-contained React component with NO external dependencies
- Copy this entire file to: components/DripDrop.tsx
- Import with: import DripDrop from "@/components/DripDrop"
- Works with Next.js, Vite, Create React App, etc.
================================================================================
PROPS REFERENCE (all optional)
================================================================================
| Prop | Type | Default | Description |
|--------------|-------------|----------|----------------------------------|
| size | number | 320 | Width and height in pixels |
| cursorRadius | number | 60 | Radius of cursor push effect |
| variant | RainVariant | "bubble" | "bubble" (transparent) or "blue" |
| color | string | - | Custom color (overrides variant) |
| density | number | 1 | Particle multiplier (0.5-2) |
| speed | number | 1 | Fall speed multiplier (0.5-2) |
| opacity | number | 0.84 | Overall opacity (0-1) |
| className | string | "" | Additional CSS classes |
================================================================================
CUSTOMIZATION EXAMPLES
================================================================================
// Basic usage (bubble variant)
<DripDrop />
// Blue rain drops
<DripDrop variant="blue" />
// Custom purple rain
<DripDrop color="rgba(147, 112, 219, 0.8)" />
// Heavier rainfall
<DripDrop density={2} speed={1.5} />
// Gentle drizzle
<DripDrop density={0.5} speed={0.7} opacity={0.5} />
// Larger canvas
<DripDrop size={600} />
// Full-screen background
<div className="fixed inset-0" style={{ backgroundColor: "#1a1a2e" }}>
<DripDrop size={Math.max(window.innerWidth, window.innerHeight)} />
</div>
================================================================================
ARCHITECTURE NOTES (for AI understanding)
================================================================================
- DropletSprite: Pre-renders teardrop shape with gradients/highlights
- Droplet: Individual raindrop with position, speed, splash callback
- Ripple: Splash particle that spawns when droplet hits bottom
- Variants: "bubble" (transparent with highlights), "blue" (colored)
- Physics: Cursor repulsion + gravity on splash particles
================================================================================
SOURCE CODE
================================================================================
*/
"use client";
import React, { useRef, useEffect } from "react";
export type RainVariant = "bubble" | "blue";
export interface DripDropProps {
/** Width and height of the canvas */
size?: number;
/** Cursor repulsion radius */
cursorRadius?: number;
/** Rain style variant */
variant?: RainVariant;
/** Custom color for droplets (overrides variant color) */
color?: string;
/** Particle density multiplier (0.5 = half, 2 = double) */
density?: number;
/** Fall speed multiplier (0.5 = slower, 2 = faster) */
speed?: number;
/** Overall opacity (0-1) */
opacity?: number;
/** Additional CSS classes */
className?: string;
}
type Pointer = {
range: number;
x?: number;
y?: number;
};
const rand = (min: number = 0, max: number = 1) => {
const val = crypto.getRandomValues(new Uint32Array(1))[0] / 2 ** 32;
return min + val * (max - min);
};
class DropletSprite {
readonly canvas: HTMLCanvasElement;
private readonly ctx: CanvasRenderingContext2D | null;
private readonly style: RainVariant;
private readonly customColor?: string;
constructor(style: RainVariant, customColor?: string) {
this.canvas = document.createElement("canvas");
this.ctx = this.canvas.getContext("2d");
this.style = style;
this.customColor = customColor;
const { devicePixelRatio } = window;
const w = 4 * devicePixelRatio;
const h = 10 * devicePixelRatio;
this.canvas.width = w;
this.canvas.height = h;
if (this.ctx) {
this.ctx.scale(devicePixelRatio, devicePixelRatio);
this.render();
}
}
private render(): void {
if (!this.ctx) return;
const w = 4;
const h = 10;
// Draw teardrop/droplet shape
this.ctx.beginPath();
this.ctx.moveTo(w / 2, 0);
this.ctx.bezierCurveTo(w / 2 - 1, h * 0.28, 0, h * 0.48, w / 2, h);
this.ctx.bezierCurveTo(w, h * 0.48, w / 2 + 1, h * 0.28, w / 2, 0);
this.ctx.closePath();
if (this.customColor) {
// Custom color mode
this.ctx.fillStyle = this.customColor;
this.ctx.fill();
// Highlight
this.ctx.beginPath();
this.ctx.ellipse(w / 2 - 1, h * 0.33, 1, 2, 0, 0, Math.PI * 2);
this.ctx.fillStyle = "rgba(255, 255, 255, 0.58)";
this.ctx.fill();
} else if (this.style === "blue") {
// Blue colored rain
const gradient = this.ctx.createLinearGradient(0, 0, w, h);
gradient.addColorStop(0, "rgba(98, 147, 235, 0.88)");
gradient.addColorStop(0.5, "rgba(133, 178, 253, 0.94)");
gradient.addColorStop(1, "rgba(98, 147, 235, 0.88)");
this.ctx.fillStyle = gradient;
this.ctx.fill();
// Highlight
this.ctx.beginPath();
this.ctx.ellipse(w / 2 - 1, h * 0.33, 1, 2, 0, 0, Math.PI * 2);
this.ctx.fillStyle = "rgba(255, 255, 255, 0.58)";
this.ctx.fill();
} else {
// Transparent bubble effect (default)
const gradient = this.ctx.createRadialGradient(
w * 0.28,
h * 0.28,
0,
w / 2,
h / 2,
h * 0.58
);
gradient.addColorStop(0, "rgba(255, 255, 255, 0.38)");
gradient.addColorStop(0.28, "rgba(255, 255, 255, 0.14)");
gradient.addColorStop(0.68, "rgba(255, 255, 255, 0.04)");
gradient.addColorStop(1, "rgba(255, 255, 255, 0.08)");
this.ctx.fillStyle = gradient;
this.ctx.fill();
// White outline for bubble edge
this.ctx.strokeStyle = "rgba(255, 255, 255, 0.48)";
this.ctx.lineWidth = 0.38;
this.ctx.stroke();
// Main highlight (top-left shine)
this.ctx.beginPath();
this.ctx.ellipse(w * 0.33, h * 0.23, 0.72, 1.22, -0.28, 0, Math.PI * 2);
this.ctx.fillStyle = "rgba(255, 255, 255, 0.78)";
this.ctx.fill();
// Secondary small highlight
this.ctx.beginPath();
this.ctx.arc(w * 0.58, h * 0.43, 0.38, 0, Math.PI * 2);
this.ctx.fillStyle = "rgba(255, 255, 255, 0.48)";
this.ctx.fill();
}
}
}
class Ripple {
x: number;
y: number;
vx: number;
vy: number;
life: number;
duration: number;
extent: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
this.vx = rand(-1.9, 1.9);
this.vy = rand(-2.8, -1.1);
this.life = 1;
this.duration = rand(14, 24);
this.extent = rand(1.1, 2.9);
}
update(): boolean {
this.x += this.vx;
this.y += this.vy;
this.vy += 0.14; // gravity
this.life -= 1 / this.duration;
return this.life <= 0;
}
draw(ctx: CanvasRenderingContext2D): void {
ctx.beginPath();
ctx.arc(this.x, this.y, this.extent * this.life, 0, Math.PI * 2);
ctx.fillStyle = "rgba(255, 255, 255, 0.78)";
ctx.globalAlpha = this.life * 0.58;
ctx.fill();
}
}
class Droplet {
private x: number;
private y: number;
private readonly pushForce = 28;
private readonly sprite: HTMLCanvasElement;
private readonly height: number;
private readonly driftX: number;
private readonly fallRate: number;
private readonly breadth: number;
constructor(
canvasWidth: number,
canvasHeight: number,
height: number,
sprite: HTMLCanvasElement,
speedMultiplier: number = 1
) {
this.x = rand(0, canvasWidth);
this.y = rand(-canvasHeight, 0);
this.height = height;
this.breadth = height * 0.38;
this.driftX = rand(-0.14, 0.14) * speedMultiplier;
this.fallRate = rand(2.1, 3.9) * speedMultiplier;
this.sprite = sprite;
}
draw(ctx: CanvasRenderingContext2D): void {
ctx.save();
ctx.translate(this.x, this.y);
ctx.drawImage(this.sprite, -this.breadth / 2, 0, this.breadth, this.height);
ctx.restore();
}
update(
pointer: Pointer,
canvasWidth: number,
canvasHeight: number,
onImpact: (x: number, y: number) => void
): void {
this.x += this.driftX;
this.y += this.fallRate;
// Pointer repulsion
const dx = (pointer.x ?? -pointer.range) - this.x;
const dy = (pointer.y ?? -pointer.range) - this.y;
const dist = Math.hypot(dx, dy);
if (dist < pointer.range) {
const forceX = dx / dist;
const forceY = dy / dist;
const strength = (pointer.range - dist) / pointer.range;
this.x -= forceX * strength * this.pushForce;
this.y -= forceY * strength * this.pushForce;
}
// Reset when hitting bottom - create ripple
if (this.y > canvasHeight) {
onImpact(this.x, canvasHeight);
this.x = rand(0, canvasWidth);
this.y = rand(-48, -12);
}
// Wrap horizontally
if (this.x < -pointer.range) {
this.x = canvasWidth + pointer.range;
} else if (this.x > canvasWidth + pointer.range) {
this.x = -pointer.range;
}
}
}
/**
* Drip Drop - Interactive rain animation with water droplets
* Move your cursor over the canvas to push raindrops away
*/
export default function DripDrop({
size = 320,
cursorRadius = 60,
variant = "bubble",
color,
density = 1,
speed = 1,
opacity = 0.84,
className = "",
}: DripDropProps) {
const canvasRef = useRef<HTMLCanvasElement>(null);
const pointer = useRef<Pointer>({ range: cursorRadius });
const frameRef = useRef(0);
const droplets = useRef<Droplet[]>([]);
const ripples = useRef<Ripple[]>([]);
useEffect(() => {
const canvas = canvasRef.current;
const ctx = canvas?.getContext("2d");
if (!canvas || !ctx) return;
const sprite = new DropletSprite(variant, color);
const animate = () => {
const { width, height } = getDimensions();
ctx.clearRect(0, 0, width, height);
// Update and draw ripples
ctx.globalAlpha = 1;
ripples.current = ripples.current.filter((ripple) => {
ripple.draw(ctx);
return !ripple.update();
});
// Update and draw droplets
ctx.globalAlpha = opacity;
droplets.current.forEach((drop) => {
drop.update(pointer.current, width, height, (x, y) => {
// Create ripple particles
for (let i = 0; i < 3; i++) {
ripples.current.push(new Ripple(x, y));
}
});
drop.draw(ctx);
});
frameRef.current = requestAnimationFrame(animate);
};
const spawnDroplets = () => {
const { width, height } = getDimensions();
const baseCount = Math.round((width * height) / 1180);
const count = Math.min(Math.round(baseCount * density), 480);
droplets.current = [];
ripples.current = [];
for (let i = 0; i < count; i++) {
const h = rand(5.2, 9.8);
droplets.current.push(new Droplet(width, height, h, sprite.canvas, speed));
}
};
const getDimensions = () => {
const { devicePixelRatio } = window;
const width = canvas.width / devicePixelRatio;
const height = canvas.height / devicePixelRatio;
return { width, height };
};
const onPointerMove = (e: Event) => {
const event = e as PointerEvent;
const rect = canvas.getBoundingClientRect();
pointer.current.x = event.clientX - rect.left;
pointer.current.y = event.clientY - rect.top;
};
const onPointerLeave = () => {
pointer.current.x = -pointer.current.range;
pointer.current.y = -pointer.current.range;
};
const setup = () => {
const { devicePixelRatio } = window;
canvas.width = size * devicePixelRatio;
canvas.height = size * devicePixelRatio;
canvas.style.width = size + "px";
canvas.style.height = size + "px";
ctx.scale(devicePixelRatio, devicePixelRatio);
spawnDroplets();
};
setup();
animate();
const events = {
pointerdown: onPointerMove,
pointermove: onPointerMove,
pointerout: onPointerLeave,
pointerup: onPointerLeave,
};
Object.entries(events).forEach(([evt, handler]) => {
canvas.addEventListener(evt, handler);
});
return () => {
Object.entries(events).forEach(([evt, handler]) => {
canvas.removeEventListener(evt, handler);
});
cancelAnimationFrame(frameRef.current);
};
}, [size, cursorRadius, variant, color, density, speed, opacity]);
return (
<canvas
ref={canvasRef}
className={`block touch-none select-none ${className}`}
aria-label="Rain falling with cursor-controlled push effect"
style={{
width: size,
height: size,
}}
/>
);
}
export { DripDrop };import MagicSnow from "@/components/MagicSnow";
// Basic usage - 320x320 canvas with white snowflakes
export default function SnowSection() {
return (
<div className="flex items-center justify-center h-screen" style={{ backgroundColor: "#0a0a14" }}>
<MagicSnow />
</div>
);
}
// Larger canvas with more snowflakes
export function DenseSnow() {
return (
<div style={{ backgroundColor: "#0a0a14" }}>
<MagicSnow size={600} density={1.5} />
</div>
);
}
// Light blue colored snowflakes
export function BlueSnow() {
return (
<div style={{ backgroundColor: "#0a0a14" }}>
<MagicSnow color="rgba(173, 216, 230, 0.9)" />
</div>
);
}
// Slower, ethereal snowfall
export function GentleSnow() {
return (
<div style={{ backgroundColor: "#0a0a14" }}>
<MagicSnow speed={0.5} opacity={0.6} />
</div>
);
}
// Stronger cursor push effect
export function InteractiveSnow() {
return (
<div style={{ backgroundColor: "#0a0a14" }}>
<MagicSnow size={400} cursorRadius={120} />
</div>
);
}
// Full-screen hero background
export function HeroWithSnow() {
return (
<div className="relative h-screen" style={{ backgroundColor: "#0a0a14" }}>
<div className="absolute inset-0 flex items-center justify-center opacity-60">
<MagicSnow size={800} />
</div>
<div className="relative z-10 flex items-center justify-center h-full">
<h1 className="text-white text-5xl font-bold">Winter Wonderland</h1>
</div>
</div>
);
}import DripDrop from "@/components/DripDrop";
// Basic usage - transparent bubble droplets
export default function RainSection() {
return (
<div className="flex items-center justify-center h-screen" style={{ backgroundColor: "#1a1a2e" }}>
<DripDrop />
</div>
);
}
// Blue rain variant
export function BlueRain() {
return (
<div style={{ backgroundColor: "#1a1a2e" }}>
<DripDrop variant="blue" />
</div>
);
}
// Custom purple rain
export function PurpleRain() {
return (
<div style={{ backgroundColor: "#1a1a2e" }}>
<DripDrop color="rgba(147, 112, 219, 0.8)" />
</div>
);
}
// Heavy rainfall
export function HeavyRain() {
return (
<div style={{ backgroundColor: "#1a1a2e" }}>
<DripDrop density={2} speed={1.5} />
</div>
);
}
// Gentle drizzle
export function Drizzle() {
return (
<div style={{ backgroundColor: "#1a1a2e" }}>
<DripDrop density={0.5} speed={0.7} opacity={0.5} />
</div>
);
}
// Full-screen hero background
export function HeroWithRain() {
return (
<div className="relative h-screen" style={{ backgroundColor: "#1a1a2e" }}>
<div className="absolute inset-0 flex items-center justify-center opacity-70">
<DripDrop size={800} />
</div>
<div className="relative z-10 flex items-center justify-center h-full">
<h1 className="text-white text-5xl font-bold">Rainy Day Vibes</h1>
</div>
</div>
);
}