feat: add themed backgrounds and enhanced styling to 404 page

Transform 404 page into a vibrant, playful experience with:

Themed Backgrounds:
- Each HTTP status code has custom gradient background
- Animated radial glow effects that pulse with theme colors
- Smooth transitions between themes (0.6s ease-in-out)
- 14 unique color schemes matching easter egg personality

Enhanced Typography:
- Responsive font sizes (1.75rem mobile → 4rem desktop)
- Black font weight for maximum impact
- Dynamic text colors matching each theme
- Glowing text shadows for easter egg modes
- Tight letter spacing (-0.02em) for modern look

Navigation Buttons:
- Added emoji icons (🏠 🎮 )
- Lift-and-scale hover animation
- Colored shadows matching button colors
- Responsive sizing for mobile
- Smooth cubic-bezier transitions

Responsive Layout:
- Increased spacing between abacus and text (2-4rem)
- Mobile-optimized gaps and padding
- Text pushed down to prevent overlap with large abacus
- Smaller screens get appropriate scaling

Dynamic Hints:
- Changes based on active easter egg
- "Try other codes..." vs "Click beads to discover more..."
- Themed text color and opacity
- Italic, medium weight for subtle emphasis

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Thomas Hallock 2025-11-08 14:36:02 -06:00
parent 41de25238f
commit dd14062112
1 changed files with 262 additions and 132 deletions

View File

@ -1,199 +1,289 @@
'use client'
"use client";
import Link from 'next/link'
import { useState, useEffect } from 'react'
import type { CustomBeadContent } from '@soroban/abacus-react'
import { AbacusReact, useAbacusDisplay } from '@soroban/abacus-react'
import { PageWithNav } from '@/components/PageWithNav'
import { css } from '../../styled-system/css'
import { stack } from '../../styled-system/patterns'
import Link from "next/link";
import { useState, useEffect } from "react";
import type { CustomBeadContent } from "@soroban/abacus-react";
import { AbacusReact, useAbacusDisplay } from "@soroban/abacus-react";
import { PageWithNav } from "@/components/PageWithNav";
import { css } from "../../styled-system/css";
import { stack } from "../../styled-system/patterns";
// HTTP Status Code Easter Eggs with dynamic bead rendering
// HTTP Status Code Easter Eggs with dynamic bead rendering and themed backgrounds
const STATUS_CODE_EASTER_EGGS: Record<
number,
{ customBeadContent: CustomBeadContent; message: string }
{
customBeadContent: CustomBeadContent;
message: string;
bgGradient?: string;
textColor?: string;
glowColor?: string;
}
> = {
200: {
customBeadContent: { type: 'emoji-function', value: (bead) => (bead.active ? '✅' : '⭕') },
customBeadContent: {
type: "emoji-function",
value: (bead) => (bead.active ? "✅" : "⭕"),
},
message: "Everything's counting perfectly!",
bgGradient: "linear-gradient(135deg, #059669 0%, #10b981 100%)",
textColor: "#d1fae5",
glowColor: "rgba(16, 185, 129, 0.3)",
},
201: {
customBeadContent: {
type: 'emoji-function',
value: (bead) => (bead.type === 'heaven' ? '🥚' : '🐣'),
type: "emoji-function",
value: (bead) => (bead.type === "heaven" ? "🥚" : "🐣"),
},
message: 'Something new has been counted into existence!',
message: "Something new has been counted into existence!",
bgGradient: "linear-gradient(135deg, #fef3c7 0%, #fde68a 100%)",
textColor: "#78350f",
glowColor: "rgba(253, 230, 138, 0.4)",
},
301: {
customBeadContent: {
type: 'emoji-function',
value: (bead) => (bead.active ? '🚚' : '📦'),
type: "emoji-function",
value: (bead) => (bead.active ? "🚚" : "📦"),
},
message: 'These numbers have permanently relocated!',
message: "These numbers have permanently relocated!",
bgGradient: "linear-gradient(135deg, #e0e7ff 0%, #c7d2fe 100%)",
textColor: "#3730a3",
glowColor: "rgba(199, 210, 254, 0.4)",
},
400: {
customBeadContent: {
type: 'emoji-function',
value: (bead) => (bead.active ? '❌' : '❓'),
type: "emoji-function",
value: (bead) => (bead.active ? "❌" : "❓"),
},
message: "Those numbers don't make sense!",
bgGradient: "linear-gradient(135deg, #fee2e2 0%, #fecaca 100%)",
textColor: "#991b1b",
glowColor: "rgba(254, 202, 202, 0.4)",
},
401: {
customBeadContent: {
type: 'emoji-function',
value: (bead) => (bead.active ? '🔒' : '🔑'),
type: "emoji-function",
value: (bead) => (bead.active ? "🔒" : "🔑"),
},
message: 'These numbers are classified!',
message: "These numbers are classified!",
bgGradient: "linear-gradient(135deg, #1f2937 0%, #374151 100%)",
textColor: "#fbbf24",
glowColor: "rgba(251, 191, 36, 0.3)",
},
403: {
customBeadContent: {
type: 'emoji-function',
value: (bead) => (bead.type === 'heaven' ? '🚫' : '⛔'),
type: "emoji-function",
value: (bead) => (bead.type === "heaven" ? "🚫" : "⛔"),
},
message: "You're not allowed to count these numbers!",
bgGradient: "linear-gradient(135deg, #450a0a 0%, #7f1d1d 100%)",
textColor: "#fef2f2",
glowColor: "rgba(127, 29, 29, 0.5)",
},
418: {
customBeadContent: { type: 'emoji', value: '🫖' },
customBeadContent: { type: "emoji", value: "🫖" },
message: "Perhaps you're pouring in the wrong direction?",
bgGradient: "linear-gradient(135deg, #d1fae5 0%, #a7f3d0 100%)",
textColor: "#064e3b",
glowColor: "rgba(167, 243, 208, 0.4)",
},
420: {
customBeadContent: {
type: 'emoji-function',
type: "emoji-function",
value: (bead) => {
const emojis = ['🌿', '🍃', '🌱', '🪴']
return emojis[bead.position % emojis.length] || '🌿'
const emojis = ["🌿", "🍃", "🌱", "🪴"];
return emojis[bead.position % emojis.length] || "🌿";
},
},
message: 'Whoa dude, these numbers are like... relative, man',
message: "Whoa dude, these numbers are like... relative, man",
bgGradient:
"linear-gradient(135deg, #6ee7b7 20%, #34d399 50%, #10b981 80%)",
textColor: "#022c22",
glowColor: "rgba(52, 211, 153, 0.6)",
},
451: {
customBeadContent: {
type: 'emoji-function',
value: (bead) => (bead.active ? '🤐' : '▓'),
type: "emoji-function",
value: (bead) => (bead.active ? "🤐" : "▓"),
},
message: '[REDACTED] - This number has been removed by the Ministry of Mathematics',
message:
"[REDACTED] - This number has been removed by the Ministry of Mathematics",
bgGradient: "linear-gradient(135deg, #111827 0%, #1f2937 100%)",
textColor: "#9ca3af",
glowColor: "rgba(156, 163, 175, 0.2)",
},
500: {
customBeadContent: {
type: 'emoji-function',
type: "emoji-function",
value: (bead) => {
const fireEmojis = ['🔥', '💥', '⚠️']
return bead.active ? fireEmojis[bead.position % fireEmojis.length] || '🔥' : '💨'
const fireEmojis = ["🔥", "💥", "⚠️"];
return bead.active
? fireEmojis[bead.position % fireEmojis.length] || "🔥"
: "💨";
},
},
message: 'The abacus has caught fire!',
message: "The abacus has caught fire!",
bgGradient:
"linear-gradient(135deg, #dc2626 0%, #ef4444 50%, #f97316 100%)",
textColor: "#fff7ed",
glowColor: "rgba(239, 68, 68, 0.6)",
},
503: {
customBeadContent: {
type: 'emoji-function',
type: "emoji-function",
value: (bead) => {
const tools = ['🔧', '🔨', '🪛', '⚙️']
return bead.active ? tools[bead.placeValue % tools.length] || '🔧' : '⚪'
const tools = ["🔧", "🔨", "🪛", "⚙️"];
return bead.active
? tools[bead.placeValue % tools.length] || "🔧"
: "⚪";
},
},
message: "Pardon our dust, we're upgrading the beads!",
bgGradient: "linear-gradient(135deg, #fef3c7 0%, #fde047 100%)",
textColor: "#713f12",
glowColor: "rgba(253, 224, 71, 0.4)",
},
666: {
customBeadContent: {
type: 'emoji-function',
type: "emoji-function",
value: (bead) => {
const demons = ['😈', '👹', '👺', '💀']
return bead.active ? demons[bead.position % demons.length] || '😈' : '🔥'
const demons = ["😈", "👹", "👺", "💀"];
return bead.active
? demons[bead.position % demons.length] || "😈"
: "🔥";
},
},
message: 'Your soul now belongs to arithmetic!',
message: "Your soul now belongs to arithmetic!",
bgGradient:
"linear-gradient(135deg, #7c2d12 0%, #991b1b 50%, #450a0a 100%)",
textColor: "#fef2f2",
glowColor: "rgba(153, 27, 27, 0.7)",
},
777: {
customBeadContent: {
type: 'emoji-function',
type: "emoji-function",
value: (bead) => {
const lucky = ['🎰', '🍀', '💰', '🎲', '⭐']
return bead.active ? lucky[bead.placeValue % lucky.length] || '🎰' : '⚪'
const lucky = ["🎰", "🍀", "💰", "🎲", "⭐"];
return bead.active
? lucky[bead.placeValue % lucky.length] || "🎰"
: "⚪";
},
},
message: "Jackpot! You've mastered the soroban!",
bgGradient:
"linear-gradient(135deg, #fbbf24 0%, #f59e0b 50%, #d97706 100%)",
textColor: "#422006",
glowColor: "rgba(251, 191, 36, 0.6)",
},
911: {
customBeadContent: {
type: 'emoji-function',
type: "emoji-function",
value: (bead) => {
const emergency = ['🚨', '🚑', '🚒', '👮']
return bead.active ? emergency[bead.position % emergency.length] || '🚨' : '⚫'
const emergency = ["🚨", "🚑", "🚒", "👮"];
return bead.active
? emergency[bead.position % emergency.length] || "🚨"
: "⚫";
},
},
message: 'EMERGENCY: Someone needs help with their math homework!',
message: "EMERGENCY: Someone needs help with their math homework!",
bgGradient: "linear-gradient(135deg, #dc2626 0%, #b91c1c 100%)",
textColor: "#fef2f2",
glowColor: "rgba(220, 38, 38, 0.5)",
},
}
};
export default function NotFound() {
const [abacusValue, setAbacusValue] = useState(404)
const [activeEasterEgg, setActiveEasterEgg] = useState<number | null>(null)
const { updateConfig, resetToDefaults } = useAbacusDisplay()
const [abacusValue, setAbacusValue] = useState(404);
const [activeEasterEgg, setActiveEasterEgg] = useState<number | null>(null);
const { updateConfig, resetToDefaults } = useAbacusDisplay();
// Easter egg activation - update global abacus config when special codes are entered
useEffect(() => {
const easterEgg = STATUS_CODE_EASTER_EGGS[abacusValue]
const easterEgg = STATUS_CODE_EASTER_EGGS[abacusValue];
if (easterEgg && activeEasterEgg !== abacusValue) {
setActiveEasterEgg(abacusValue)
setActiveEasterEgg(abacusValue);
// Update global abacus display config to use custom beads
// This affects ALL abaci rendered in the app until page reload!
updateConfig({
beadShape: 'custom',
beadShape: "custom",
customBeadContent: easterEgg.customBeadContent,
})
});
// Store active easter egg in window so it persists across navigation
;(window as any).__easterEggMode = abacusValue
(window as any).__easterEggMode = abacusValue;
} else if (!easterEgg && activeEasterEgg !== null) {
// User changed away from an easter egg code - reset to defaults
setActiveEasterEgg(null)
resetToDefaults()
;(window as any).__easterEggMode = null
setActiveEasterEgg(null);
resetToDefaults();
(window as any).__easterEggMode = null;
}
}, [abacusValue, activeEasterEgg, updateConfig, resetToDefaults])
}, [abacusValue, activeEasterEgg, updateConfig, resetToDefaults]);
// Get current theme
const currentTheme = activeEasterEgg
? STATUS_CODE_EASTER_EGGS[activeEasterEgg]
: null;
return (
<PageWithNav>
<div
className={css({
minHeight: 'calc(100vh - 64px)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
bg: 'bg.canvas',
padding: '2rem',
minHeight: "calc(100vh - 64px)",
display: "flex",
alignItems: "center",
justifyContent: "center",
background: currentTheme?.bgGradient || "bg.canvas",
padding: { base: "1rem", sm: "2rem" },
transition: "background 0.6s ease-in-out",
position: "relative",
overflow: "hidden",
})}
>
{/* Animated glow effect */}
{currentTheme?.glowColor && (
<div
className={css({
position: "absolute",
inset: 0,
background: `radial-gradient(circle at 50% 40%, ${currentTheme.glowColor} 0%, transparent 70%)`,
animation: "pulse 3s ease-in-out infinite",
pointerEvents: "none",
})}
/>
)}
<div
className={stack({
gap: '2rem',
alignItems: 'center',
textAlign: 'center',
maxWidth: '600px',
gap: { base: "1.5rem", sm: "2rem", md: "3rem" },
alignItems: "center",
textAlign: "center",
maxWidth: "900px",
width: "100%",
position: "relative",
zIndex: 1,
})}
>
{/* Interactive Abacus */}
<div
className={css({
position: 'relative',
width: '100%',
maxWidth: { base: '90vw', sm: '500px', md: '600px', lg: '700px' },
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
position: "relative",
width: "100%",
maxWidth: { base: "90vw", sm: "500px", md: "600px", lg: "700px" },
display: "flex",
alignItems: "center",
justifyContent: "center",
})}
>
<div
className={css({
transform: {
base: 'scale(1.5)',
sm: 'scale(2)',
md: 'scale(2.5)',
lg: 'scale(3)',
base: "scale(1.5)",
sm: "scale(2)",
md: "scale(2.5)",
lg: "scale(3)",
},
transformOrigin: 'center',
transformOrigin: "center",
})}
>
<AbacusReact
@ -206,13 +296,29 @@ export default function NotFound() {
</div>
{/* Main message */}
<div className={stack({ gap: '1rem' })}>
<div
className={stack({
gap: "1rem",
marginTop: { base: "2rem", sm: "3rem", md: "4rem" },
})}
>
<h1
className={css({
fontSize: '3rem',
fontWeight: 'bold',
color: 'text.primary',
lineHeight: '1.2',
fontSize: {
base: "1.75rem",
sm: "2.5rem",
md: "3.5rem",
lg: "4rem",
},
fontWeight: "black",
color: currentTheme?.textColor || "text.primary",
lineHeight: "1.1",
textShadow: currentTheme?.glowColor
? `0 0 20px ${currentTheme.glowColor}, 0 0 40px ${currentTheme.glowColor}`
: "none",
transition: "all 0.6s ease-in-out",
letterSpacing: "-0.02em",
px: { base: "1rem", sm: "2rem" },
})}
>
{activeEasterEgg
@ -224,86 +330,110 @@ export default function NotFound() {
{/* Navigation links */}
<div
className={css({
display: 'flex',
gap: '1rem',
flexWrap: 'wrap',
justifyContent: 'center',
display: "flex",
gap: { base: "0.75rem", sm: "1rem" },
flexWrap: "wrap",
justifyContent: "center",
marginTop: { base: "1rem", sm: "1.5rem" },
})}
>
<Link
href="/"
className={css({
px: '2rem',
py: '1rem',
bg: 'blue.500',
color: 'white',
borderRadius: '0.5rem',
fontWeight: 'semibold',
textDecoration: 'none',
transition: 'all 0.2s',
px: { base: "1.5rem", sm: "2rem" },
py: { base: "0.75rem", sm: "1rem" },
bg: "blue.600",
color: "white",
borderRadius: "0.75rem",
fontWeight: "bold",
fontSize: { base: "0.875rem", sm: "1rem" },
textDecoration: "none",
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1)",
_hover: {
bg: 'blue.600',
transform: 'translateY(-2px)',
bg: "blue.700",
transform: "translateY(-4px) scale(1.05)",
boxShadow: "0 10px 20px rgba(37, 99, 235, 0.4)",
},
_active: {
transform: "translateY(-2px)",
},
})}
>
Home
🏠 Home
</Link>
<Link
href="/games"
className={css({
px: '2rem',
py: '1rem',
bg: 'purple.500',
color: 'white',
borderRadius: '0.5rem',
fontWeight: 'semibold',
textDecoration: 'none',
transition: 'all 0.2s',
px: { base: "1.5rem", sm: "2rem" },
py: { base: "0.75rem", sm: "1rem" },
bg: "purple.600",
color: "white",
borderRadius: "0.75rem",
fontWeight: "bold",
fontSize: { base: "0.875rem", sm: "1rem" },
textDecoration: "none",
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1)",
_hover: {
bg: 'purple.600',
transform: 'translateY(-2px)',
bg: "purple.700",
transform: "translateY(-4px) scale(1.05)",
boxShadow: "0 10px 20px rgba(147, 51, 234, 0.4)",
},
_active: {
transform: "translateY(-2px)",
},
})}
>
Games
🎮 Games
</Link>
<Link
href="/create"
className={css({
px: '2rem',
py: '1rem',
bg: 'green.500',
color: 'white',
borderRadius: '0.5rem',
fontWeight: 'semibold',
textDecoration: 'none',
transition: 'all 0.2s',
px: { base: "1.5rem", sm: "2rem" },
py: { base: "0.75rem", sm: "1rem" },
bg: "green.600",
color: "white",
borderRadius: "0.75rem",
fontWeight: "bold",
fontSize: { base: "0.875rem", sm: "1rem" },
textDecoration: "none",
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1)",
_hover: {
bg: 'green.600',
transform: 'translateY(-2px)',
bg: "green.700",
transform: "translateY(-4px) scale(1.05)",
boxShadow: "0 10px 20px rgba(34, 197, 94, 0.4)",
},
_active: {
transform: "translateY(-2px)",
},
})}
>
Create
Create
</Link>
</div>
{/* Easter egg hint */}
<p
className={css({
fontSize: '0.875rem',
color: 'text.secondary',
opacity: 0.6,
marginTop: '2rem',
fontSize: { base: "0.75rem", sm: "0.875rem" },
color: currentTheme?.textColor || "text.secondary",
opacity: currentTheme ? 0.8 : 0.6,
marginTop: { base: "1.5rem", sm: "2rem" },
fontStyle: "italic",
transition: "all 0.6s ease-in-out",
fontWeight: "medium",
})}
>
Try other HTTP status codes...
{activeEasterEgg
? "✨ Click the beads to discover more codes..."
: "🔍 Try other HTTP status codes..."}
</p>
</div>
</div>
</PageWithNav>
)
);
}