404lab 2.0.2 → 2.0.4

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.
@@ -10,18 +10,20 @@ export const TEMPLATES = {
10
10
  stoneage: "StoneAge",
11
11
  amongus: "AmongUs",
12
12
  blueglitch: "BlueGlitch",
13
+ buggame: "BugGame",
13
14
  geeksforgeeks: "GeeksforGeeks",
14
15
  google: "Google",
15
16
  macos: "MacOs",
16
17
  modern: "ModernPage",
17
18
  particles: "Particles",
18
19
  poet: "Poet",
19
- retro: "RetroTv",
20
+ retrotv: "RetroTv",
20
21
  simple: "SimplePage",
21
22
  snow: "Snow",
22
23
  strangethings: "StrangerThings",
23
- terminal: "Terminal404",
24
- vercel: "Vercel"
24
+ terminal: "Terminal",
25
+ vercel: "Vercel",
26
+ void: "Void",
25
27
  };
26
28
 
27
29
  export function getTemplateKeys() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "404lab",
3
- "version": "2.0.2",
3
+ "version": "2.0.4",
4
4
  "description": "A CLI tool for generating beautiful custom 404 pages in Next.js projects",
5
5
  "type": "module",
6
6
  "preferGlobal": true,
@@ -3,14 +3,22 @@
3
3
  import { useEffect, useRef, useState } from "react";
4
4
  import Image from "next/image";
5
5
  import Link from "next/link";
6
- import { motion } from "framer-motion";
6
+ import { VT323 } from "next/font/google";
7
7
  import { cn } from "@/components/ui/cn";
8
8
 
9
+ const vt323 = VT323({
10
+ weight: "400",
11
+ subsets: ["latin"],
12
+ display: "swap",
13
+ });
14
+
9
15
  const AmongUs = ({ className }: { className?: string }) => {
10
16
  const containerRef = useRef<HTMLDivElement | null>(null);
11
17
  const [stars, setStars] = useState<{ id: number; x: number; y: number; size: number; speed: number; opacity: number }[]>([]);
18
+ const [mounted, setMounted] = useState(false);
12
19
 
13
20
  useEffect(() => {
21
+ setMounted(true);
14
22
  const initialStars = Array.from({ length: 100 }).map((_, i) => ({
15
23
  id: i,
16
24
  x: Math.random() * 100,
@@ -22,37 +30,20 @@ const AmongUs = ({ className }: { className?: string }) => {
22
30
  setStars(initialStars);
23
31
  }, []);
24
32
 
25
- const typewriterVariants = {
26
- hidden: { opacity: 0 },
27
- visible: {
28
- opacity: 1,
29
- transition: {
30
- staggerChildren: 0.08,
31
- },
32
- },
33
- };
34
-
35
- const letterVariants = {
36
- hidden: { opacity: 0, y: 5 },
37
- visible: { opacity: 1, y: 0 },
38
- };
33
+ const text = "THERE IS 1 IMPOSTOR AMONG US";
39
34
 
40
- const text = "There is 1 Impostor among us";
35
+ if (!mounted) return null;
41
36
 
42
37
  return (
43
38
  <div
44
39
  ref={containerRef}
45
40
  className={cn(
46
- "relative flex h-screen w-full flex-col items-center justify-center overflow-hidden bg-[#000000] text-white",
41
+ "relative flex min-h-screen w-full flex-col items-center justify-center overflow-hidden bg-[#000000] text-white",
42
+ vt323.className,
47
43
  className
48
44
  )}
49
45
  >
50
46
  <style jsx global>{`
51
- @import url('https://fonts.googleapis.com/css2?family=VT323&display=swap');
52
-
53
- .among-us-font {
54
- font-family: 'VT323', monospace;
55
- }
56
47
 
57
48
  .star-field {
58
49
  position: absolute;
@@ -64,6 +55,12 @@ const AmongUs = ({ className }: { className?: string }) => {
64
55
  position: absolute;
65
56
  background: white;
66
57
  border-radius: 50%;
58
+ animation: moveStars linear infinite;
59
+ }
60
+
61
+ @keyframes moveStars {
62
+ from { left: 100%; }
63
+ to { left: -10%; }
67
64
  }
68
65
 
69
66
  .crt-overlay::before {
@@ -95,11 +92,60 @@ const AmongUs = ({ className }: { className?: string }) => {
95
92
  0% { bottom: 100%; }
96
93
  100% { bottom: -100px; }
97
94
  }
95
+
96
+ @keyframes floatRed {
97
+ 0% { left: -25%; top: 40%; transform: rotate(0deg); }
98
+ 25% { top: 45%; }
99
+ 50% { top: 35%; }
100
+ 75% { top: 40%; }
101
+ 100% { left: 125%; top: 40%; transform: rotate(360deg); }
102
+ }
103
+
104
+ @keyframes floatBlue {
105
+ 0% { left: -25%; top: 70%; transform: rotate(0deg); }
106
+ 25% { top: 55%; }
107
+ 50% { top: 65%; }
108
+ 75% { top: 70%; }
109
+ 100% { left: 125%; top: 70%; transform: rotate(-360deg); }
110
+ }
111
+
112
+ .animate-float-red {
113
+ animation: floatRed 20s linear infinite;
114
+ }
115
+
116
+ .animate-float-blue {
117
+ animation: floatBlue 28s linear infinite;
118
+ animation-delay: 12s;
119
+ }
120
+
121
+ @keyframes fadeIn {
122
+ from { opacity: 0; }
123
+ to { opacity: 1; }
124
+ }
125
+
126
+ .fade-in {
127
+ animation: fadeIn 1s ease-out forwards;
128
+ }
129
+
130
+ .fade-in-delayed {
131
+ opacity: 0;
132
+ animation: fadeIn 1s ease-out 3.5s forwards;
133
+ }
134
+
135
+ .fade-in-delayed-2 {
136
+ opacity: 0;
137
+ animation: fadeIn 1s ease-out 5s forwards;
138
+ }
139
+
140
+ .fade-in-delayed-3 {
141
+ opacity: 0;
142
+ animation: fadeIn 1s ease-out 6.5s forwards;
143
+ }
98
144
  `}</style>
99
145
 
100
146
  <div className="star-field">
101
147
  {stars.map((star) => (
102
- <motion.div
148
+ <div
103
149
  key={star.id}
104
150
  className="star"
105
151
  style={{
@@ -109,95 +155,46 @@ const AmongUs = ({ className }: { className?: string }) => {
109
155
  left: `${star.x}%`,
110
156
  opacity: star.opacity,
111
157
  boxShadow: `0 0 ${star.size * 2}px rgba(255, 255, 255, 0.5)`,
112
- }}
113
- animate={{
114
- left: ["100%", "-10%"],
115
- }}
116
- transition={{
117
- duration: 25 / star.speed,
118
- repeat: Infinity,
119
- ease: "linear",
120
- delay: Math.random() * -25,
158
+ animationDuration: `${25 / star.speed}s`,
159
+ animationDelay: `${Math.random() * -25}s`,
121
160
  }}
122
161
  />
123
162
  ))}
124
163
  </div>
125
164
 
126
- <div className="z-10 flex flex-col items-center text-center px-4 among-us-font select-none">
127
- <motion.h1
128
- className="text-8xl md:text-[12rem] font-bold tracking-[0.1em] text-white mb-4 leading-none"
129
- initial={{ opacity: 0, scale: 0.9 }}
130
- animate={{ opacity: 1, scale: 1 }}
131
- transition={{ duration: 1.5, ease: "easeOut" }}
132
- >
165
+ <div className="z-10 flex flex-col items-center text-center px-4 select-none">
166
+ <h1 className="text-7xl sm:text-9xl md:text-[12rem] font-bold tracking-[0.1em] text-white mb-2 sm:mb-4 leading-none fade-in">
133
167
  404
134
- </motion.h1>
168
+ </h1>
135
169
 
136
- <motion.div
137
- className="mb-8 h-12"
138
- variants={typewriterVariants}
139
- initial="hidden"
140
- animate="visible"
141
- >
142
- <h2 className="text-2xl md:text-5xl text-red-500 uppercase font-black tracking-[0.2em]">
143
- {text.split("").map((char, index) => (
144
- <motion.span key={index} variants={letterVariants}>
145
- {char}
146
- </motion.span>
147
- ))}
170
+ <div className="mb-6 sm:mb-8 h-8 sm:h-12 fade-in">
171
+ <h2 className="text-lg xs:text-xl sm:text-3xl md:text-5xl text-red-500 uppercase font-black tracking-[0.15em] sm:tracking-[0.2em] whitespace-nowrap">
172
+ {text}
148
173
  </h2>
149
- </motion.div>
150
-
151
- <motion.div
152
- className="space-y-6"
153
- initial={{ opacity: 0 }}
154
- animate={{ opacity: 1 }}
155
- transition={{ delay: 3.5, duration: 1.2 }}
156
- >
157
- <p className="text-xl md:text-4xl text-gray-400 max-w-2xl mx-auto tracking-wide leading-relaxed">
174
+ </div>
175
+
176
+ <div className="space-y-4 sm:space-y-6 fade-in-delayed">
177
+ <p className="text-lg sm:text-2xl md:text-4xl text-gray-400 max-w-xl md:max-w-2xl mx-auto tracking-wide leading-relaxed">
158
178
  This page was ejected into space.
159
179
  </p>
160
180
 
161
- <motion.p
162
- className="text-lg md:text-3xl text-white tracking-widest"
163
- initial={{ opacity: 0, y: 20 }}
164
- animate={{ opacity: 1, y: 0 }}
165
- transition={{ delay: 5, duration: 1 }}
166
- >
167
- <span className="text-red-600 font-bold px-2">404-Page</span> was not An Impostor.
168
- </motion.p>
181
+ <p className="text-base sm:text-xl md:text-3xl text-white tracking-widest fade-in-delayed-2">
182
+ <span className="text-red-600 font-bold px-1 sm:px-2">404-Page</span> was not An Impostor.
183
+ </p>
169
184
 
170
- <motion.div
171
- className="pt-12"
172
- initial={{ opacity: 0 }}
173
- animate={{ opacity: 1 }}
174
- transition={{ delay: 6.5, duration: 1 }}
175
- >
185
+ <div className="pt-8 sm:pt-12 fade-in-delayed-3">
176
186
  <Link
177
187
  href="/"
178
- className="group relative inline-flex items-center justify-center px-12 py-4 font-bold text-white transition-all duration-300 bg-transparent border-2 border-white/30 hover:border-white hover:bg-white hover:text-black rounded-sm uppercase tracking-[0.3em] text-xl"
188
+ className="group relative inline-flex items-center justify-center px-8 sm:px-12 py-3 sm:py-4 font-bold text-white transition-all duration-300 bg-transparent border-2 border-white/30 hover:border-white hover:bg-white hover:text-black rounded-sm uppercase tracking-[0.2em] sm:tracking-[0.3em] text-lg sm:text-xl"
179
189
  >
180
190
  Return to Ship
181
191
  </Link>
182
- </motion.div>
183
- </motion.div>
192
+ </div>
193
+ </div>
184
194
  </div>
185
195
 
186
196
  <div className="absolute inset-0 pointer-events-none overflow-hidden">
187
- <motion.div
188
- className="absolute w-32 md:w-56 h-auto"
189
- initial={{ left: "-25%", top: "40%", rotate: 0 }}
190
- animate={{
191
- left: "125%",
192
- top: ["40%", "45%", "35%", "40%"],
193
- rotate: 360
194
- }}
195
- transition={{
196
- duration: 20,
197
- repeat: Infinity,
198
- ease: "linear"
199
- }}
200
- >
197
+ <div className="absolute w-32 md:w-56 h-auto animate-float-red" style={{ left: '-25%' }}>
201
198
  <Image
202
199
  src="https://cdn.iconscout.com/icon/free/png-256/free-red-among-us-icon-svg-download-png-2691060.png"
203
200
  alt="Ejected Red"
@@ -206,23 +203,9 @@ const AmongUs = ({ className }: { className?: string }) => {
206
203
  unoptimized
207
204
  className="drop-shadow-[0_0_20px_rgba(255,0,0,0.5)]"
208
205
  />
209
- </motion.div>
206
+ </div>
210
207
 
211
- <motion.div
212
- className="absolute w-24 md:w-40 h-auto"
213
- initial={{ left: "-25%", top: "70%", rotate: 0 }}
214
- animate={{
215
- left: "125%",
216
- top: ["70%", "55%", "65%", "70%"],
217
- rotate: -360
218
- }}
219
- transition={{
220
- duration: 28,
221
- repeat: Infinity,
222
- ease: "linear",
223
- delay: 12
224
- }}
225
- >
208
+ <div className="absolute w-24 md:w-40 h-auto animate-float-blue" style={{ left: '-25%' }}>
226
209
  <Image
227
210
  src="https://cdn.iconscout.com/icon/free/png-256/free-blue-among-us-icon-svg-download-png-2691064.png"
228
211
  alt="Ejected Blue"
@@ -231,7 +214,7 @@ const AmongUs = ({ className }: { className?: string }) => {
231
214
  unoptimized
232
215
  className="drop-shadow-[0_0_20px_rgba(0,0,255,0.5)] opacity-60"
233
216
  />
234
- </motion.div>
217
+ </div>
235
218
  </div>
236
219
 
237
220
  <div className="crt-overlay pointer-events-none absolute inset-0 z-40"></div>
@@ -1,7 +1,6 @@
1
1
  "use client";
2
2
 
3
3
  import { useEffect, useState } from "react";
4
- import { motion, AnimatePresence } from "framer-motion";
5
4
  import Link from "next/link";
6
5
  import { cn } from "@/components/ui/cn";
7
6
 
@@ -128,71 +127,74 @@ const BlueGlitch = ({
128
127
  0% { bottom: 100%; }
129
128
  100% { bottom: -100px; }
130
129
  }
130
+
131
+ @keyframes appear {
132
+ from { opacity: 0; }
133
+ to { opacity: 1; }
134
+ }
135
+
136
+ .animate-appear {
137
+ animation: appear 0.5s ease-out forwards;
138
+ }
131
139
  `}</style>
132
140
 
133
- <div className="crt-container max-w-5xl h-[80vh] border-8 border-double border-[#bbb]/20 rounded-lg overflow-hidden flex flex-col items-center justify-center">
141
+ <div className="crt-container w-full max-w-5xl h-[85vh] sm:h-[80vh] border-4 sm:border-8 border-double border-[#bbb]/20 rounded-lg overflow-hidden flex flex-col items-center justify-center m-4">
134
142
  <div className="scanline" />
135
143
 
136
- <div className="w-full max-w-3xl dos-font text-xs sm:text-lg lg:text-xl space-y-4">
137
- <div className="text-center mb-8">
138
- <motion.div
139
- initial={{ scale: 0.9, opacity: 0 }}
140
- animate={{ scale: 1, opacity: 1 }}
141
- className="inline-block"
144
+ <div className="w-full max-w-[90%] sm:max-w-3xl dos-font text-[10px] xs:text-xs sm:text-lg lg:text-xl space-y-3 sm:space-y-4">
145
+ <div className="text-center mb-6 sm:mb-8">
146
+ <div
147
+ className="inline-block animate-appear"
142
148
  >
143
- <div className="inverted-box text-3xl sm:text-5xl font-bold py-2 px-6">
149
+ <div className="inverted-box text-xl xs:text-2xl sm:text-5xl font-bold py-1.5 xs:py-2 px-4 xs:px-6">
144
150
  WINDOWS
145
151
  </div>
146
- </motion.div>
152
+ </div>
147
153
  </div>
148
154
 
149
- <AnimatePresence>
155
+ <div>
150
156
  {bootStep >= 1 && (
151
- <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }}>
157
+ <div className="animate-appear">
152
158
  A fatal exception 404 has occurred at C0DE:ABAD1DEA.
153
- </motion.div>
159
+ </div>
154
160
  )}
155
161
 
156
162
  {bootStep >= 2 && (
157
- <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }}>
163
+ <div className="animate-appear">
158
164
  * The system could not find the file you requested.
159
- </motion.div>
165
+ </div>
160
166
  )}
161
167
 
162
168
  {bootStep >= 3 && (
163
- <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }}>
169
+ <div className="animate-appear">
164
170
  * Press ANY BUTTON to try finding it again in the void.
165
- </motion.div>
171
+ </div>
166
172
  )}
167
173
 
168
174
  {bootStep >= 4 && (
169
- <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }}>
175
+ <div className="animate-appear">
170
176
  * Contact your network administrator if this continues.
171
- </motion.div>
177
+ </div>
172
178
  )}
173
179
 
174
180
  {bootStep >= 5 && (
175
- <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} className="pt-8">
181
+ <div className="pt-8 animate-appear">
176
182
  Current State: <span className="text-red-500 underline">ERROR_PAGE_NOT_FOUND</span>
177
- </motion.div>
183
+ </div>
178
184
  )}
179
185
 
180
186
  {bootStep >= 6 && (
181
- <motion.div
182
- initial={{ opacity: 0 }}
183
- animate={{ opacity: 1 }}
184
- className="text-center pt-12"
187
+ <div
188
+ className="text-center pt-12 animate-appear"
185
189
  >
186
190
  Press any button to return home
187
191
  <span className="inline-block w-3 h-6 bg-[#bbb] ml-2 animate-pulse align-middle" />
188
- </motion.div>
192
+ </div>
189
193
  )}
190
194
 
191
195
  {bootStep >= 7 && (
192
- <motion.div
193
- initial={{ opacity: 0, y: 20 }}
194
- animate={{ opacity: 1, y: 0 }}
195
- className="flex justify-center pt-8"
196
+ <div
197
+ className="flex justify-center pt-8 animate-appear"
196
198
  >
197
199
  <Link
198
200
  href="/"
@@ -200,9 +202,9 @@ const BlueGlitch = ({
200
202
  >
201
203
  Return to Safety
202
204
  </Link>
203
- </motion.div>
205
+ </div>
204
206
  )}
205
- </AnimatePresence>
207
+ </div>
206
208
  </div>
207
209
 
208
210
  <div className="absolute bottom-4 left-4 opacity-30 text-[10px] dos-font uppercase tracking-tighter">