enhance 404 error page with animated background, improved styling, and interactive elements

This commit is contained in:
Fabian Schieder 2026-02-27 22:06:25 +01:00
parent 923b6a8c6c
commit 7f943c6224

523
404.php
View File

@ -15,88 +15,513 @@ error_log("[404] $ip $method $requestUri");
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>404 Seite nicht gefunden</title> <link rel="icon" href="assets/images/favicon.ico" sizes="any">
<title>404 Seite nicht gefunden | Geizkragen</title>
<style> <style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800;900&display=swap');
:root { :root {
--bg: #0f172a; --bg-main: #151923;
--card: #1e293b; --bg-header: #1f2937;
--accent: #3b82f6; --color-primary: #2563eb;
--text: #e2e8f0; --color-primary-hover: #1d4ed8;
--muted: #94a3b8; --color-accent: #10b981;
--text-invert: #ffffff;
--text-muted: #94a3b8;
--card-bg: rgba(31, 41, 55, 0.65);
--card-border: rgba(255,255,255,0.06);
--glow: rgba(37, 99, 235, 0.25);
}
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
} }
body { body {
margin: 0; font-family: 'Inter', system-ui, -apple-system, sans-serif;
font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif; background: var(--bg-main);
background: linear-gradient(135deg, #0f172a, #1e293b); color: var(--text-invert);
color: var(--text); min-height: 100vh;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 100vh; overflow: hidden;
position: relative;
} }
.card { /* ===== ANIMATED BACKGROUND ===== */
background: var(--card); .bg-grid {
padding: 3rem; position: fixed;
border-radius: 16px; inset: 0;
box-shadow: 0 20px 40px rgba(0,0,0,0.4); background-image:
max-width: 500px; linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px),
width: 90%; linear-gradient(90deg, rgba(255,255,255,0.03) 1px, transparent 1px);
background-size: 60px 60px;
animation: gridMove 20s linear infinite;
z-index: 0;
}
@keyframes gridMove {
0% { transform: translate(0, 0); }
100% { transform: translate(60px, 60px); }
}
.bg-glow {
position: fixed;
width: 600px;
height: 600px;
border-radius: 50%;
filter: blur(120px);
opacity: 0.15;
z-index: 0;
animation: float 8s ease-in-out infinite;
}
.bg-glow--blue {
background: var(--color-primary);
top: -200px;
right: -100px;
}
.bg-glow--green {
background: var(--color-accent);
bottom: -200px;
left: -100px;
animation-delay: -4s;
}
.bg-glow--purple {
background: #8b5cf6;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 400px;
height: 400px;
opacity: 0.08;
animation-delay: -2s;
}
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-40px); }
}
/* ===== PARTICLES ===== */
.particles {
position: fixed;
inset: 0;
z-index: 0;
overflow: hidden;
}
.particle {
position: absolute;
width: 4px;
height: 4px;
background: var(--color-primary);
border-radius: 50%;
opacity: 0;
animation: particleFloat linear infinite;
}
@keyframes particleFloat {
0% {
opacity: 0;
transform: translateY(100vh) scale(0);
}
10% { opacity: 0.7; }
90% { opacity: 0.7; }
100% {
opacity: 0;
transform: translateY(-10vh) scale(1);
}
}
/* ===== MAIN CONTENT ===== */
.error-container {
position: relative;
z-index: 10;
text-align: center; text-align: center;
padding: 2rem;
max-width: 650px;
width: 95%;
animation: fadeInUp 0.8s ease-out;
} }
h1 { @keyframes fadeInUp {
font-size: 4rem; from {
margin: 0; opacity: 0;
color: var(--accent); transform: translateY(40px);
}
to {
opacity: 1;
transform: translateY(0);
}
} }
p { /* ===== GLITCH 404 ===== */
color: var(--muted); .error-code {
margin: 1rem 0; font-size: clamp(8rem, 20vw, 14rem);
} font-weight: 900;
line-height: 1;
a { letter-spacing: -0.04em;
position: relative;
display: inline-block; display: inline-block;
margin-top: 1.5rem; background: linear-gradient(135deg, var(--color-primary), #8b5cf6, var(--color-accent));
padding: 0.75rem 1.5rem; background-size: 200% 200%;
border-radius: 8px; -webkit-background-clip: text;
background: var(--accent); -webkit-text-fill-color: transparent;
color: white; background-clip: text;
text-decoration: none; animation: gradientShift 4s ease-in-out infinite;
margin-bottom: 0.5rem;
}
.error-code::before,
.error-code::after {
content: '404';
position: absolute;
top: 0;
left: 0;
right: 0;
overflow: hidden;
background: linear-gradient(135deg, var(--color-primary), #8b5cf6, var(--color-accent));
background-size: 200% 200%;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.error-code::before {
animation: glitch1 3s infinite linear alternate-reverse, gradientShift 4s ease-in-out infinite;
clip-path: inset(0 0 65% 0);
}
.error-code::after {
animation: glitch2 3s infinite linear alternate-reverse, gradientShift 4s ease-in-out infinite;
clip-path: inset(65% 0 0 0);
}
@keyframes gradientShift {
0%, 100% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
}
@keyframes glitch1 {
0%, 92% { transform: translate(0); }
93% { transform: translate(-8px, -2px); }
94% { transform: translate(4px, 1px); }
95% { transform: translate(-3px, 2px); }
96%, 100% { transform: translate(0); }
}
@keyframes glitch2 {
0%, 90% { transform: translate(0); }
91% { transform: translate(5px, 2px); }
92% { transform: translate(-6px, -1px); }
93% { transform: translate(3px, -2px); }
94%, 100% { transform: translate(0); }
}
/* ===== CARD ===== */
.error-card {
background: var(--card-bg);
backdrop-filter: blur(24px);
-webkit-backdrop-filter: blur(24px);
border: 1px solid var(--card-border);
border-radius: 24px;
padding: 2.5rem 2.5rem 2rem;
box-shadow:
0 25px 60px rgba(0,0,0,0.4),
0 0 80px var(--glow);
animation: fadeInUp 0.8s ease-out 0.2s both;
}
.error-card__icon {
width: 64px;
height: 64px;
margin: 0 auto 1.5rem;
background: linear-gradient(135deg, rgba(37,99,235,0.15), rgba(139,92,246,0.15));
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
animation: pulse 3s ease-in-out infinite;
}
.error-card__icon svg {
width: 30px;
height: 30px;
stroke: var(--color-primary);
}
@keyframes pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(37,99,235,0.3); }
50% { box-shadow: 0 0 0 16px rgba(37,99,235,0); }
}
.error-card__title {
font-size: 1.5rem;
font-weight: 800;
margin-bottom: 0.75rem;
color: var(--text-invert);
}
.error-card__text {
color: var(--text-muted);
font-size: 1rem;
line-height: 1.6;
margin-bottom: 0.5rem;
}
.error-card__path {
display: inline-block;
background: rgba(37,99,235,0.1);
border: 1px solid rgba(37,99,235,0.2);
color: var(--color-primary);
font-family: 'Courier New', Courier, monospace;
font-size: 0.85rem;
font-weight: 600; font-weight: 600;
transition: 0.2s ease; padding: 0.4rem 1rem;
border-radius: 8px;
margin: 1rem 0 1.5rem;
word-break: break-all;
max-width: 100%;
} }
a:hover { /* ===== BUTTONS ===== */
background: #2563eb; .error-card__actions {
transform: translateY(-2px); display: flex;
gap: 0.75rem;
justify-content: center;
flex-wrap: wrap;
margin-top: 1.5rem;
} }
.footer { .btn {
margin-top: 2rem; display: inline-flex;
font-size: 0.8rem; align-items: center;
gap: 0.5rem;
padding: 0.8rem 1.6rem;
border-radius: 12px;
font-size: 0.95rem;
font-weight: 600;
text-decoration: none;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
border: none;
font-family: inherit;
}
.btn--primary {
background: linear-gradient(135deg, var(--color-primary), #4f46e5);
color: white;
box-shadow: 0 4px 15px rgba(37,99,235,0.35);
}
.btn--primary:hover {
transform: translateY(-3px);
box-shadow: 0 8px 25px rgba(37,99,235,0.5);
}
.btn--primary:active {
transform: translateY(-1px);
}
.btn--ghost {
background: rgba(255,255,255,0.05);
color: var(--text-muted);
border: 1px solid rgba(255,255,255,0.1);
}
.btn--ghost:hover {
background: rgba(255,255,255,0.1);
color: var(--text-invert);
transform: translateY(-3px);
}
.btn svg {
width: 18px;
height: 18px;
}
/* ===== DIVIDER ===== */
.error-card__divider {
height: 1px;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.08), transparent);
margin: 1.5rem 0 1rem;
}
/* ===== FOOTER INFO ===== */
.error-card__meta {
display: flex;
justify-content: center;
gap: 2rem;
flex-wrap: wrap;
}
.error-card__meta-item {
font-size: 0.75rem;
color: #475569;
display: flex;
align-items: center;
gap: 0.35rem;
}
.error-card__meta-item span {
color: #64748b; color: #64748b;
} }
/* ===== LOGO ===== */
.error-logo {
margin-bottom: 2rem;
animation: fadeInUp 0.8s ease-out 0.1s both;
}
.error-logo img {
height: 40px;
opacity: 0.7;
transition: opacity 0.3s ease;
}
.error-logo img:hover {
opacity: 1;
}
/* ===== RESPONSIVE ===== */
@media (max-width: 480px) {
.error-card {
padding: 1.8rem 1.5rem 1.5rem;
border-radius: 18px;
}
.error-card__actions {
flex-direction: column;
}
.btn {
justify-content: center;
width: 100%;
}
.error-card__meta {
flex-direction: column;
gap: 0.5rem;
align-items: center;
}
}
</style> </style>
</head> </head>
<body> <body>
<div class="card"> <!-- Animated background elements -->
<h1>Seite nicht gefunden</h1> <div class="bg-grid"></div>
<p>Die angeforderte Seite wurde nicht gefunden.</p> <div class="bg-glow bg-glow--blue"></div>
<p><strong><?php echo $requestUri; ?></strong></p> <div class="bg-glow bg-glow--green"></div>
<div class="bg-glow bg-glow--purple"></div>
<div class="particles" id="particles"></div>
<a href="/">Zur Startseite</a> <div class="error-container">
<div class="footer"> <!-- Logo -->
Anfrage: <?php echo $method; ?> <br> <a href="/" class="error-logo">
Server: fabianschieder.com <img src="/assets/images/logoText.png" alt="Geizkragen">
</a>
<!-- Glitch 404 Code -->
<div class="error-code" aria-hidden="true">404</div>
<!-- Main Card -->
<div class="error-card">
<div class="error-card__icon">
<svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="11" cy="11" r="8"/>
<path d="M21 21l-4.35-4.35"/>
<line x1="8" y1="11" x2="14" y2="11"/>
</svg>
</div>
<h1 class="error-card__title">Seite nicht gefunden</h1>
<p class="error-card__text">
Die Seite, die du suchst, existiert leider nicht oder wurde verschoben.
</p>
<div class="error-card__path"><?php echo $requestUri; ?></div>
<div class="error-card__actions">
<a href="/" class="btn btn--primary">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
<polyline points="9 22 9 12 15 12 15 22"/>
</svg>
Zur Startseite
</a>
<button onclick="history.back()" class="btn btn--ghost">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="19" y1="12" x2="5" y2="12"/>
<polyline points="12 19 5 12 12 5"/>
</svg>
Zurück
</button>
</div>
<div class="error-card__divider"></div>
<div class="error-card__meta">
<div class="error-card__meta-item">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#475569" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.127.96.361 1.903.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91"/>
</svg>
<span><?php echo $method; ?> Request</span>
</div>
<div class="error-card__meta-item">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#475569" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="2" y="2" width="20" height="20" rx="5" ry="5"/>
<path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z"/>
<line x1="17.5" y1="6.5" x2="17.51" y2="6.5"/>
</svg>
<span>geizkragen.at</span>
</div> </div>
</div> </div>
</div>
</div>
<script>
// Create floating particles
const container = document.getElementById('particles');
const count = 30;
for (let i = 0; i < count; i++) {
const p = document.createElement('div');
p.classList.add('particle');
const size = Math.random() * 4 + 2;
p.style.width = size + 'px';
p.style.height = size + 'px';
p.style.left = Math.random() * 100 + '%';
p.style.animationDuration = (Math.random() * 8 + 6) + 's';
p.style.animationDelay = (Math.random() * 10) + 's';
// Random color between primary, accent, and purple
const colors = ['#2563eb', '#10b981', '#8b5cf6', '#3b82f6', '#6366f1'];
p.style.background = colors[Math.floor(Math.random() * colors.length)];
container.appendChild(p);
}
// Subtle mouse parallax on the error code
const code = document.querySelector('.error-code');
document.addEventListener('mousemove', (e) => {
const x = (e.clientX / window.innerWidth - 0.5) * 10;
const y = (e.clientY / window.innerHeight - 0.5) * 10;
code.style.transform = `translate(${x}px, ${y}px)`;
});
</script>
</body> </body>
</html> </html>