refactor 404 page layout and styles for improved user experience

This commit is contained in:
Fabian Schieder 2026-03-02 23:14:21 +01:00
parent 494585281f
commit 7c10e0ffe7

696
404.php
View File

@ -5,470 +5,39 @@ http_response_code(404);
// Request-Daten absichern // Request-Daten absichern
$requestUri = htmlspecialchars($_SERVER['REQUEST_URI'] ?? '', ENT_QUOTES, 'UTF-8'); $requestUri = htmlspecialchars($_SERVER['REQUEST_URI'] ?? '', ENT_QUOTES, 'UTF-8');
$method = htmlspecialchars($_SERVER['REQUEST_METHOD'] ?? '', ENT_QUOTES, 'UTF-8'); $method = htmlspecialchars($_SERVER['REQUEST_METHOD'] ?? '', ENT_QUOTES, 'UTF-8');
$ip = htmlspecialchars($_SERVER['REMOTE_ADDR'] ?? '', ENT_QUOTES, 'UTF-8');
// Optional: einfaches Logging // Optional: einfaches Logging
error_log("[404] $ip $method $requestUri"); error_log("[404] " . ($_SERVER['REMOTE_ADDR'] ?? '') . " $method $requestUri");
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="de"> <html lang="de">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="assets/images/favicon.ico" sizes="any"> <link rel="icon" href="assets/images/favicon.ico" sizes="any">
<link rel="shortcut icon" href="assets/images/favicon.ico"> <link rel="stylesheet" href="style.css">
<title>404 Seite nicht gefunden | Geizkragen</title> <title>404 Seite nicht gefunden | Geizkragen</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800;900&display=swap');
:root {
--bg-main: #151923;
--bg-header: #1f2937;
--color-primary: #2563eb;
--color-primary-hover: #1d4ed8;
--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 {
font-family: 'Inter', system-ui, -apple-system, sans-serif;
background: var(--bg-main);
color: var(--text-invert);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
position: relative;
}
/* ===== ANIMATED BACKGROUND ===== */
.bg-grid {
position: fixed;
inset: 0;
background-image:
linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px),
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;
padding: 2rem;
max-width: 650px;
width: 95%;
animation: fadeInUp 0.8s ease-out;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(40px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* ===== GLITCH 404 ===== */
.error-code {
font-size: clamp(8rem, 20vw, 14rem);
font-weight: 900;
line-height: 1;
letter-spacing: -0.04em;
position: relative;
display: inline-block;
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;
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;
padding: 0.4rem 1rem;
border-radius: 8px;
margin: 1rem 0 1.5rem;
word-break: break-all;
max-width: 100%;
}
/* ===== BUTTONS ===== */
.error-card__actions {
display: flex;
gap: 0.75rem;
justify-content: center;
flex-wrap: wrap;
margin-top: 1.5rem;
}
.btn {
display: inline-flex;
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;
}
/* ===== BROKEN LINK ICON ===== */
.error-icon-top {
margin-bottom: 1.5rem;
animation: fadeInUp 0.8s ease-out 0.1s both;
display: flex;
justify-content: center;
}
.error-icon-top__circle {
width: 72px;
height: 72px;
border-radius: 50%;
background: linear-gradient(135deg, rgba(37,99,235,0.12), rgba(139,92,246,0.12));
border: 1px solid rgba(255,255,255,0.06);
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.error-icon-top__circle::before {
content: '';
position: absolute;
inset: -4px;
border-radius: 50%;
border: 2px dashed rgba(37,99,235,0.2);
animation: spinSlow 12s linear infinite;
}
@keyframes spinSlow {
to { transform: rotate(360deg); }
}
.error-icon-top__circle svg {
width: 32px;
height: 32px;
stroke: #8b5cf6;
opacity: 0.8;
}
/* ===== 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>
</head> </head>
<body> <body>
<!-- Animated background elements --> <main>
<div class="bg-grid"></div> <div class="container" style="
<div class="bg-glow bg-glow--blue"></div> display: flex;
<div class="bg-glow bg-glow--green"></div> flex-direction: column;
<div class="bg-glow bg-glow--purple"></div> align-items: center;
<div class="particles" id="particles"></div> justify-content: center;
min-height: 100vh;
<div class="error-container"> text-align: center;
padding-block: 4rem;
<!-- Broken Link Icon --> ">
<div class="error-icon-top"> <!-- 404 Glitch-Zahl -->
<div class="error-icon-top__circle">
<svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"/>
<line x1="8" y1="12" x2="16" y2="12"/>
</svg>
</div>
</div>
<!-- Glitch 404 Code -->
<div class="error-code" aria-hidden="true">404</div> <div class="error-code" aria-hidden="true">404</div>
<!-- Main Card --> <!-- Card -->
<div class="error-card"> <div class="error-card">
<div class="error-card__icon"> <div class="error-card__icon">
<svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" width="28" height="28">
<circle cx="11" cy="11" r="8"/> <circle cx="11" cy="11" r="8"/>
<path d="M21 21l-4.35-4.35"/> <path d="M21 21l-4.35-4.35"/>
<line x1="8" y1="11" x2="14" y2="11"/> <line x1="8" y1="11" x2="14" y2="11"/>
@ -483,76 +52,201 @@ error_log("[404] $ip $method $requestUri");
<div class="error-card__path"><?php echo $requestUri; ?></div> <div class="error-card__path"><?php echo $requestUri; ?></div>
<div class="error-card__actions"> <div class="error-card__actions">
<a href="/" class="btn btn--primary"> <a href="index.php" class="btn btn--primary">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" width="18" height="18">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/> <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"/> <polyline points="9 22 9 12 15 12 15 22"/>
</svg> </svg>
Zur Startseite Zur Startseite
</a> </a>
<button onclick="history.back()" class="btn btn--ghost"> <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"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" width="18" height="18">
<line x1="19" y1="12" x2="5" y2="12"/> <line x1="19" y1="12" x2="5" y2="12"/>
<polyline points="12 19 5 12 12 5"/> <polyline points="12 19 5 12 12 5"/>
</svg> </svg>
Zurück Zurück
</button> </button>
</div> </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.store</span>
</div> </div>
</div> </div>
</main>
</div> <style>
</div> /* ── 404 Glitch-Zahl ── */
.error-code {
<script> font-size: clamp(7rem, 18vw, 12rem);
// Create floating particles font-weight: 900;
const container = document.getElementById('particles'); line-height: 1;
const count = 30; letter-spacing: -0.04em;
background: linear-gradient(135deg, var(--color-primary), #4f46e5, var(--color-accent));
for (let i = 0; i < count; i++) { background-size: 200% 200%;
const p = document.createElement('div'); -webkit-background-clip: text;
p.classList.add('particle'); -webkit-text-fill-color: transparent;
background-clip: text;
const size = Math.random() * 4 + 2; animation: gradientShift 4s ease-in-out infinite;
p.style.width = size + 'px'; margin-bottom: 1.5rem;
p.style.height = size + 'px'; position: relative;
p.style.left = Math.random() * 100 + '%'; display: inline-block;
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 .error-code::before,
const code = document.querySelector('.error-code'); .error-code::after {
document.addEventListener('mousemove', (e) => { content: '404';
const x = (e.clientX / window.innerWidth - 0.5) * 10; position: absolute;
const y = (e.clientY / window.innerHeight - 0.5) * 10; top: 0; left: 0; right: 0;
code.style.transform = `translate(${x}px, ${y}px)`; overflow: hidden;
}); background: inherit;
</script> -webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.error-code::before {
clip-path: inset(0 0 65% 0);
animation: glitch1 3s infinite linear alternate-reverse;
}
.error-code::after {
clip-path: inset(65% 0 0 0);
animation: glitch2 3s infinite linear alternate-reverse;
}
@keyframes glitch1 {
0%,92% { transform: translate(0); }
93% { transform: translate(-6px,-2px); }
94% { transform: translate(3px,1px); }
95% { transform: translate(-2px,2px); }
96%,100%{ transform: translate(0); }
}
@keyframes glitch2 {
0%,90% { transform: translate(0); }
91% { transform: translate(4px,2px); }
92% { transform: translate(-5px,-1px); }
93% { transform: translate(2px,-2px); }
94%,100%{ transform: translate(0); }
}
/* ── Card ── */
.error-card {
background: rgba(31, 41, 55, 0.55);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-xl);
padding: 2.5rem 2.5rem 2rem;
max-width: 520px;
width: 100%;
box-shadow: var(--shadow-md), 0 0 60px var(--color-primary-glow);
animation: fadeInUp 0.6s ease-out 0.1s both;
}
.error-card__icon {
width: 56px;
height: 56px;
margin: 0 auto 1.25rem;
background: var(--color-primary-soft);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: var(--color-primary);
animation: pulse-glow 3s ease-in-out infinite;
}
.error-card__title {
font-size: 1.4rem;
font-weight: 800;
color: var(--text-primary);
margin-bottom: 0.6rem;
}
.error-card__text {
color: var(--text-muted);
font-size: 0.95rem;
line-height: 1.6;
margin-bottom: 0.25rem;
}
.error-card__path {
display: inline-block;
background: var(--color-primary-soft);
border: 1px solid rgba(39, 74, 151, 0.25);
color: #6b8fd8;
font-family: 'Courier New', Courier, monospace;
font-size: 0.82rem;
font-weight: 600;
padding: 0.35rem 0.9rem;
border-radius: var(--radius-md);
margin: 1rem 0 1.5rem;
word-break: break-all;
max-width: 100%;
}
/* ── Buttons ── */
.error-card__actions {
display: flex;
gap: 0.75rem;
justify-content: center;
flex-wrap: wrap;
}
.btn {
display: inline-flex;
align-items: center;
gap: 0.45rem;
padding: 0.7rem 1.4rem;
border-radius: var(--radius-md);
font-size: 0.9rem;
font-weight: 600;
font-family: var(--font-family);
text-decoration: none;
cursor: pointer;
border: none;
transition: all var(--transition-smooth);
}
.btn--primary {
background: var(--color-primary);
color: #fff;
box-shadow: 0 4px 14px var(--color-primary-glow);
}
.btn--primary:hover {
background: var(--color-primary-hover);
transform: translateY(-2px);
box-shadow: 0 8px 24px var(--color-primary-glow);
color: #fff;
}
.btn--ghost {
background: rgba(255,255,255,0.04);
color: var(--text-muted);
border: 1px solid var(--border-subtle);
}
.btn--ghost:hover {
background: rgba(255,255,255,0.09);
color: var(--text-primary);
transform: translateY(-2px);
}
/* ── Responsive ── */
@media (max-width: 480px) {
.error-card {
padding: 1.8rem 1.25rem 1.5rem;
}
.error-card__actions {
flex-direction: column;
}
.btn {
justify-content: center;
width: 100%;
}
}
</style>
</body> </body>
</html> </html>