404lab 2.0.1 → 2.0.2
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/cli/core/templates.js +1 -0
- package/package.json +3 -3
- package/templates/AmongUs.tsx +213 -119
- package/templates/BlueGlitch.tsx +190 -100
- package/templates/GeeksforGeeks.tsx +125 -37
- package/templates/Google.tsx +97 -29
- package/templates/MacOs.tsx +116 -212
- package/templates/ModernPage.tsx +73 -11
- package/templates/Particles.tsx +173 -67
- package/templates/Poet.tsx +139 -54
- package/templates/RetroTv.tsx +182 -130
- package/templates/SimplePage.tsx +60 -16
- package/templates/Snow.tsx +113 -179
- package/templates/StoneAge.tsx +91 -23
- package/templates/StrangerThings.tsx +79 -193
- package/templates/Terminal404.tsx +124 -242
- package/templates/Vercel.tsx +127 -0
package/templates/Snow.tsx
CHANGED
|
@@ -1,210 +1,144 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useEffect, useRef } from "react";
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
4
|
import Link from "next/link";
|
|
5
|
+
import { motion, AnimatePresence } from "framer-motion";
|
|
6
|
+
import { cn } from "@/components/ui/cn";
|
|
5
7
|
|
|
6
|
-
const Snow = () => {
|
|
7
|
-
const canvasRef = useRef<HTMLCanvasElement
|
|
8
|
+
const Snow = ({ className }: { className?: string }) => {
|
|
9
|
+
const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
10
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
8
11
|
|
|
9
12
|
useEffect(() => {
|
|
10
|
-
const canvas = canvasRef.current
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
let particles: Particle[] = [];
|
|
23
|
-
|
|
24
|
-
const reset = (p: Particle) => {
|
|
25
|
-
p.y = Math.random() * height;
|
|
26
|
-
p.x = Math.random() * width;
|
|
27
|
-
p.dx = Math.random() - 0.5;
|
|
28
|
-
p.dy = Math.random() * 0.5 + 0.5;
|
|
13
|
+
const canvas = canvasRef.current;
|
|
14
|
+
if (!canvas) return;
|
|
15
|
+
const ctx = canvas.getContext("2d");
|
|
16
|
+
if (!ctx) return;
|
|
17
|
+
|
|
18
|
+
let animationFrame: number;
|
|
19
|
+
let particles: { x: number; y: number; size: number; speed: number; opacity: number }[] = [];
|
|
20
|
+
|
|
21
|
+
const resize = () => {
|
|
22
|
+
canvas.width = window.innerWidth;
|
|
23
|
+
canvas.height = window.innerHeight;
|
|
24
|
+
init();
|
|
29
25
|
};
|
|
30
26
|
|
|
31
|
-
|
|
27
|
+
const init = () => {
|
|
32
28
|
particles = [];
|
|
29
|
+
const count = Math.floor((canvas.width * canvas.height) / 8000);
|
|
33
30
|
for (let i = 0; i < count; i++) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
particles.push({
|
|
32
|
+
x: Math.random() * canvas.width,
|
|
33
|
+
y: Math.random() * canvas.height,
|
|
34
|
+
size: Math.random() * 3 + 1,
|
|
35
|
+
speed: Math.random() * 1 + 0.5,
|
|
36
|
+
opacity: Math.random() * 0.5 + 0.3
|
|
37
|
+
});
|
|
37
38
|
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
canvas.width = width;
|
|
44
|
-
canvas.height = height;
|
|
45
|
-
createParticles(Math.floor((width * height) / 10000));
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function animate() {
|
|
49
|
-
ctx.clearRect(0, 0, width, height);
|
|
50
|
-
ctx.fillStyle = "#f6f9fa";
|
|
51
|
-
|
|
52
|
-
particles.forEach((p) => {
|
|
53
|
-
p.y += p.dy;
|
|
54
|
-
p.x += p.dx;
|
|
55
|
-
|
|
56
|
-
if (p.y > height) p.y = 0;
|
|
57
|
-
if (p.x > width) {
|
|
58
|
-
reset(p);
|
|
59
|
-
p.y = 0;
|
|
60
|
-
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const draw = () => {
|
|
42
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
43
|
+
ctx.fillStyle = "white";
|
|
61
44
|
|
|
45
|
+
particles.forEach(p => {
|
|
46
|
+
ctx.globalAlpha = p.opacity;
|
|
62
47
|
ctx.beginPath();
|
|
63
|
-
ctx.arc(p.x, p.y,
|
|
48
|
+
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
|
|
64
49
|
ctx.fill();
|
|
50
|
+
|
|
51
|
+
p.y += p.speed;
|
|
52
|
+
p.x += Math.sin(p.y / 50) * 0.5;
|
|
53
|
+
|
|
54
|
+
if (p.y > canvas.height) {
|
|
55
|
+
p.y = -10;
|
|
56
|
+
p.x = Math.random() * canvas.width;
|
|
57
|
+
}
|
|
65
58
|
});
|
|
66
59
|
|
|
67
|
-
requestAnimationFrame(
|
|
68
|
-
}
|
|
60
|
+
animationFrame = requestAnimationFrame(draw);
|
|
61
|
+
};
|
|
69
62
|
|
|
70
|
-
resize();
|
|
71
|
-
animate();
|
|
72
63
|
window.addEventListener("resize", resize);
|
|
73
|
-
|
|
64
|
+
resize();
|
|
65
|
+
draw();
|
|
66
|
+
|
|
67
|
+
return () => {
|
|
68
|
+
window.removeEventListener("resize", resize);
|
|
69
|
+
cancelAnimationFrame(animationFrame);
|
|
70
|
+
};
|
|
74
71
|
}, []);
|
|
75
72
|
|
|
76
73
|
return (
|
|
77
|
-
<div
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
74
|
+
<div
|
|
75
|
+
className={cn(
|
|
76
|
+
"min-h-screen flex flex-col items-center justify-center overflow-hidden bg-gradient-to-b from-[#b7d1e5] via-[#e8f2f6] to-white relative",
|
|
77
|
+
className
|
|
78
|
+
)}
|
|
79
|
+
>
|
|
80
|
+
<canvas ref={canvasRef} className="absolute inset-0 z-10 pointer-events-none opacity-60" />
|
|
81
|
+
|
|
82
|
+
<div className="absolute inset-0 z-0 bg-[radial-gradient(circle_at_center,_transparent_0%,_rgba(255,255,255,0.4)_100%)]" />
|
|
83
|
+
|
|
84
|
+
<motion.main
|
|
85
|
+
initial={{ opacity: 0, y: 20 }}
|
|
86
|
+
animate={{ opacity: 1, y: 0 }}
|
|
87
|
+
className="z-20 text-center px-4"
|
|
88
|
+
>
|
|
89
|
+
<h1 className="text-4xl md:text-7xl font-bold text-[#5d7399] mb-4 tracking-tight">
|
|
90
|
+
Frozen in Time.
|
|
85
91
|
</h1>
|
|
86
|
-
<
|
|
87
|
-
|
|
92
|
+
<p className="text-[#5d7399]/70 text-lg md:text-2xl mb-12 max-w-2xl mx-auto font-medium">
|
|
93
|
+
The page you are looking for has been buried under a heavy snowfall.
|
|
94
|
+
Let's get you somewhere warmer.
|
|
95
|
+
</p>
|
|
96
|
+
|
|
97
|
+
<Link
|
|
98
|
+
href="/"
|
|
99
|
+
className="inline-flex items-center px-8 py-4 bg-white text-[#5d7399] font-bold rounded-full shadow-lg shadow-blue-200/50 hover:shadow-xl hover:scale-105 active:scale-95 transition-all text-lg border border-blue-50"
|
|
100
|
+
>
|
|
101
|
+
Hitch a ride back home
|
|
88
102
|
</Link>
|
|
103
|
+
</motion.main>
|
|
104
|
+
|
|
105
|
+
<div className="absolute bottom-0 w-full h-[30vh] z-10">
|
|
106
|
+
<svg viewBox="0 0 1440 320" className="absolute bottom-0 w-full h-full drop-shadow-[-20px_-20px_40px_rgba(255,255,255,0.5)]">
|
|
107
|
+
<path fill="#f8f9fa" d="M0,192L48,197.3C96,203,192,213,288,192C384,171,480,117,576,117.3C672,117,768,171,864,197.3C960,224,1056,224,1152,202.7C1248,181,1344,139,1392,117.3L1440,96L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z"></path>
|
|
108
|
+
</svg>
|
|
109
|
+
|
|
110
|
+
<motion.div
|
|
111
|
+
className="absolute bottom-[10%] left-1/2 -translate-x-1/2 z-20"
|
|
112
|
+
onMouseEnter={() => setIsHovered(true)}
|
|
113
|
+
onMouseLeave={() => setIsHovered(false)}
|
|
114
|
+
>
|
|
115
|
+
<div className="relative group cursor-pointer">
|
|
116
|
+
<motion.h2
|
|
117
|
+
className="text-8xl md:text-[12rem] font-black text-[#6b85b2] opacity-20 select-none tracking-tighter"
|
|
118
|
+
animate={isHovered ? { scale: 1.05, opacity: 0.3 } : {}}
|
|
119
|
+
>
|
|
120
|
+
404
|
|
121
|
+
</motion.h2>
|
|
122
|
+
|
|
123
|
+
<motion.div
|
|
124
|
+
className="absolute -right-12 -top-12 w-16 h-16 pointer-events-none"
|
|
125
|
+
animate={isHovered ? { rotate: [0, -20, 10, 0], x: [0, -5, 5, 0] } : {}}
|
|
126
|
+
>
|
|
127
|
+
<div className="w-1 h-20 bg-[#dd4040]/20 absolute left-1/2 -translate-x-1/2 top-0" />
|
|
128
|
+
<div className="w-10 h-8 bg-[#dd4040] absolute bottom-0 left-0 rounded-sm" />
|
|
129
|
+
</motion.div>
|
|
130
|
+
</div>
|
|
131
|
+
</motion.div>
|
|
89
132
|
</div>
|
|
90
133
|
|
|
91
|
-
<div className="
|
|
92
|
-
|
|
93
|
-
<div className="
|
|
94
|
-
|
|
95
|
-
</div>
|
|
134
|
+
<div className="absolute top-10 right-10 flex gap-4 opacity-20">
|
|
135
|
+
{[1,2,3].map(i => (
|
|
136
|
+
<div key={i} className="w-12 h-1 bg-[#5d7399] rounded-full" />
|
|
137
|
+
))}
|
|
96
138
|
</div>
|
|
97
|
-
|
|
98
|
-
<style jsx>{`
|
|
99
|
-
.content {
|
|
100
|
-
height: 100vh;
|
|
101
|
-
position: relative;
|
|
102
|
-
background: linear-gradient(to bottom, #bbcfe1 0%, #e8f2f6 80%);
|
|
103
|
-
overflow: hidden;
|
|
104
|
-
font-family: "Dosis", sans-serif;
|
|
105
|
-
font-size: 32px;
|
|
106
|
-
font-weight: 500;
|
|
107
|
-
color: #5d7399;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
.snow {
|
|
111
|
-
position: absolute;
|
|
112
|
-
inset: 0;
|
|
113
|
-
pointer-events: none;
|
|
114
|
-
z-index: 20;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
.main-text {
|
|
118
|
-
padding: 20vh 20px 0;
|
|
119
|
-
text-align: center;
|
|
120
|
-
line-height: 2em;
|
|
121
|
-
font-size: 5vh;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
.home-link {
|
|
125
|
-
font-size: 0.6em;
|
|
126
|
-
color: inherit;
|
|
127
|
-
text-decoration: none;
|
|
128
|
-
opacity: 0.6;
|
|
129
|
-
border-bottom: 1px dashed rgba(93, 115, 153, 0.5);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
.home-link:hover {
|
|
133
|
-
opacity: 1;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
.ground {
|
|
137
|
-
position: absolute;
|
|
138
|
-
bottom: 0;
|
|
139
|
-
width: 100%;
|
|
140
|
-
height: 160px;
|
|
141
|
-
background: #f6f9fa;
|
|
142
|
-
box-shadow: 0 0 10px 10px #f6f9fa;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
.mound {
|
|
146
|
-
margin-top: -80px;
|
|
147
|
-
font-size: 180px;
|
|
148
|
-
font-weight: 800;
|
|
149
|
-
text-align: center;
|
|
150
|
-
color: #dd4040;
|
|
151
|
-
position: relative;
|
|
152
|
-
pointer-events: none;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
.mound::before {
|
|
156
|
-
content: "";
|
|
157
|
-
position: absolute;
|
|
158
|
-
width: 600px;
|
|
159
|
-
height: 200px;
|
|
160
|
-
left: 50%;
|
|
161
|
-
top: 50px;
|
|
162
|
-
transform: translateX(-50%);
|
|
163
|
-
border-radius: 100%;
|
|
164
|
-
background: linear-gradient(to bottom, #d0deea, #f6f9fa 60px);
|
|
165
|
-
z-index: 1;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
.mound_text {
|
|
169
|
-
transform: rotate(6deg);
|
|
170
|
-
position: relative;
|
|
171
|
-
z-index: 2;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
.mound_spade {
|
|
175
|
-
width: 35px;
|
|
176
|
-
height: 30px;
|
|
177
|
-
position: absolute;
|
|
178
|
-
right: 50%;
|
|
179
|
-
top: 42%;
|
|
180
|
-
margin-right: -250px;
|
|
181
|
-
background: #dd4040;
|
|
182
|
-
transform: rotate(35deg);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
.mound_spade::before {
|
|
186
|
-
content: "";
|
|
187
|
-
position: absolute;
|
|
188
|
-
width: 40%;
|
|
189
|
-
height: 30px;
|
|
190
|
-
bottom: 100%;
|
|
191
|
-
left: 50%;
|
|
192
|
-
transform: translateX(-50%);
|
|
193
|
-
background: #dd4040;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
.mound_spade::after {
|
|
197
|
-
content: "";
|
|
198
|
-
position: absolute;
|
|
199
|
-
width: 100%;
|
|
200
|
-
height: 30px;
|
|
201
|
-
top: -55px;
|
|
202
|
-
border: 10px solid #dd4040;
|
|
203
|
-
border-radius: 4px 4px 20px 20px;
|
|
204
|
-
}
|
|
205
|
-
`}</style>
|
|
206
139
|
</div>
|
|
207
140
|
);
|
|
208
141
|
};
|
|
209
142
|
|
|
210
143
|
export default Snow;
|
|
144
|
+
|
package/templates/StoneAge.tsx
CHANGED
|
@@ -1,38 +1,106 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { motion } from "framer-motion";
|
|
1
4
|
import Link from "next/link";
|
|
5
|
+
import { cn } from "@/components/ui/cn";
|
|
2
6
|
|
|
3
|
-
const StoneAge = () => {
|
|
7
|
+
const StoneAge = ({ className }: { className?: string }) => {
|
|
4
8
|
return (
|
|
5
|
-
<
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
9
|
+
<div
|
|
10
|
+
className={cn(
|
|
11
|
+
"min-h-screen bg-[#fcf8f0] flex flex-col items-center justify-center p-8 overflow-hidden relative font-serif",
|
|
12
|
+
className
|
|
13
|
+
)}
|
|
14
|
+
>
|
|
15
|
+
<style jsx global>{`
|
|
16
|
+
@import url('https://fonts.googleapis.com/css2?family=Arvo:wght@400;700&display=swap');
|
|
17
|
+
|
|
18
|
+
.stone-age-font {
|
|
19
|
+
font-family: 'Arvo', serif;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.paper-texture {
|
|
23
|
+
background-image: url("https://www.transparenttextures.com/patterns/natural-paper.png");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.rock-shadow {
|
|
27
|
+
filter: drop-shadow(0 20px 30px rgba(0,0,0,0.1));
|
|
28
|
+
}
|
|
29
|
+
`}</style>
|
|
30
|
+
|
|
31
|
+
<div className="absolute inset-0 paper-texture opacity-40 pointer-events-none" />
|
|
32
|
+
|
|
33
|
+
<motion.div
|
|
34
|
+
animate={{ y: [0, -10, 0] }}
|
|
35
|
+
transition={{ duration: 6, repeat: Infinity, ease: "easeInOut" }}
|
|
36
|
+
className="absolute top-20 right-[15%] opacity-10 select-none"
|
|
37
|
+
>
|
|
38
|
+
<svg width="200" height="200" viewBox="0 0 200 200">
|
|
39
|
+
<path d="M40 160 L80 40 L120 180 L160 80 L180 160 Z" fill="#4a3b2a" />
|
|
40
|
+
</svg>
|
|
41
|
+
</motion.div>
|
|
42
|
+
|
|
43
|
+
<motion.div
|
|
44
|
+
animate={{ y: [0, -15, 0] }}
|
|
45
|
+
transition={{ duration: 8, repeat: Infinity, ease: "easeInOut", delay: 1 }}
|
|
46
|
+
className="absolute bottom-20 left-[10%] opacity-10 select-none"
|
|
47
|
+
>
|
|
48
|
+
<svg width="250" height="250" viewBox="0 0 200 200">
|
|
49
|
+
<path d="M50 180 L100 20 L150 160 Z" fill="#4a3b2a" />
|
|
50
|
+
</svg>
|
|
51
|
+
</motion.div>
|
|
24
52
|
|
|
53
|
+
<motion.div
|
|
54
|
+
initial={{ opacity: 0, y: 30 }}
|
|
55
|
+
animate={{ opacity: 1, y: 0 }}
|
|
56
|
+
className="relative z-10 w-full max-w-4xl flex flex-col items-center text-center"
|
|
57
|
+
>
|
|
58
|
+
<div className="relative mb-8">
|
|
59
|
+
<motion.div
|
|
60
|
+
animate={{ rotate: [-2, 2, -2] }}
|
|
61
|
+
transition={{ duration: 4, repeat: Infinity, ease: "easeInOut" }}
|
|
62
|
+
className="relative z-20"
|
|
63
|
+
>
|
|
64
|
+
<img
|
|
65
|
+
src="https://cdn.dribbble.com/users/285475/screenshots/2083086/dribbble_1.gif"
|
|
66
|
+
alt="Stone Age Illustration"
|
|
67
|
+
className="w-full max-w-[450px] h-auto rock-shadow rounded-3xl"
|
|
68
|
+
/>
|
|
69
|
+
</motion.div>
|
|
70
|
+
|
|
71
|
+
<div className="absolute -bottom-6 left-1/2 -translate-x-1/2 w-[80%] h-6 bg-black opacity-[0.05] blur-xl rounded-full" />
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<div className="stone-age-font">
|
|
75
|
+
<h1 className="text-4xl md:text-6xl font-black text-[#4a3b2a] mb-4 uppercase tracking-tighter">
|
|
76
|
+
Prehistoric 404
|
|
77
|
+
</h1>
|
|
78
|
+
<p className="text-[#6b5844] text-xl md:text-2xl mb-12 max-w-xl mx-auto font-medium">
|
|
79
|
+
This endpoint hasn't been discovered yet. It's still in the early Jurassic.
|
|
80
|
+
</p>
|
|
81
|
+
|
|
82
|
+
<div className="flex flex-col sm:flex-row gap-6 items-center justify-center">
|
|
25
83
|
<Link
|
|
26
84
|
href="/"
|
|
27
|
-
className="
|
|
85
|
+
className="px-10 py-4 bg-[#4a3b2a] text-[#fcf8f0] font-bold rounded-xl hover:bg-[#5c4a36] transition-all transform hover:-translate-y-1 hover:shadow-xl active:scale-95 text-lg"
|
|
28
86
|
>
|
|
29
|
-
|
|
87
|
+
Back to Future
|
|
30
88
|
</Link>
|
|
89
|
+
<button className="px-10 py-4 border-2 border-[#4a3b2a]/20 text-[#4a3b2a] font-bold rounded-xl hover:bg-[#4a3b2a]/5 transition-all text-lg">
|
|
90
|
+
Explore History
|
|
91
|
+
</button>
|
|
31
92
|
</div>
|
|
32
93
|
</div>
|
|
94
|
+
</motion.div>
|
|
95
|
+
|
|
96
|
+
<div className="absolute bottom-10 w-full px-8 flex justify-between items-center text-[#4a3b2a]/20 font-bold uppercase tracking-[0.2em] text-[10px] sm:text-xs">
|
|
97
|
+
<span>Pleistocene Era</span>
|
|
98
|
+
<div className="h-px flex-1 mx-8 bg-[#4a3b2a]/10" />
|
|
99
|
+
<span>No Connection Found</span>
|
|
33
100
|
</div>
|
|
34
|
-
</
|
|
101
|
+
</div>
|
|
35
102
|
);
|
|
36
103
|
};
|
|
37
104
|
|
|
38
105
|
export default StoneAge;
|
|
106
|
+
|