Files
gw-svelte/src/routes/+error.svelte
T

214 lines
4.5 KiB
Svelte
Raw Normal View History

2026-05-02 09:43:32 +12:00
<script lang="ts">
import { page } from '$app/stores';
$: status = $page.status;
$: errorMessage = $page.error?.message ?? '';
$: isNotFound = status === 404;
$: heading = isNotFound ? 'This page wandered off' : 'Something went wrong';
$: subtitle = isNotFound
? 'The page you were looking for cant be found. It may have moved, or the link might be a little chewed up.'
: 'We hit a snag loading this page. Try heading home and well get you back on the trail.';
</script>
<svelte:head>
<title>{status} · GoodWalk</title>
<meta name="robots" content="noindex" />
</svelte:head>
<main class="error-page">
<div class="error-bg" aria-hidden="true">
<span class="paw paw-1">🐾</span>
<span class="paw paw-2">🐾</span>
<span class="paw paw-3">🐾</span>
<span class="paw paw-4">🐾</span>
<span class="paw paw-5">🐾</span>
</div>
<div class="error-content">
<div class="error-status">{status}</div>
<h1 class="error-heading">{heading}</h1>
<p class="error-subtitle">{subtitle}</p>
{#if errorMessage && errorMessage !== heading}
<p class="error-detail">{errorMessage}</p>
{/if}
<div class="error-actions">
<a href="/" class="btn btn-yellow">Take me home</a>
<a href="/booking" class="btn btn-outline">Book a Meet &amp; Greet</a>
</div>
</div>
</main>
<style>
.error-page {
position: relative;
min-height: 100dvh;
display: grid;
place-items: center;
background:
radial-gradient(circle at 20% 15%, #2a3e2a 0%, transparent 55%),
radial-gradient(circle at 80% 85%, #1a261a 0%, transparent 55%),
var(--green);
color: #fff;
overflow: hidden;
padding: 48px 24px;
}
.error-bg {
position: absolute;
inset: 0;
pointer-events: none;
overflow: hidden;
}
.paw {
position: absolute;
user-select: none;
color: var(--yellow);
opacity: 0.07;
line-height: 1;
}
.paw-1 {
top: 6%;
left: 5%;
font-size: 110px;
transform: rotate(-18deg);
}
.paw-2 {
top: 14%;
right: 8%;
font-size: 140px;
transform: rotate(22deg);
}
.paw-3 {
bottom: 10%;
left: 9%;
font-size: 160px;
transform: rotate(35deg);
}
.paw-4 {
bottom: 6%;
right: 6%;
font-size: 120px;
transform: rotate(-12deg);
}
.paw-5 {
top: 48%;
left: 50%;
font-size: 80px;
transform: translate(-50%, -50%) rotate(8deg);
opacity: 0.04;
}
.error-content {
position: relative;
text-align: center;
max-width: 640px;
z-index: 1;
animation: rise 0.5s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.error-status {
font-family: var(--font-head);
font-size: clamp(120px, 22vw, 220px);
font-weight: 700;
color: var(--yellow);
line-height: 0.9;
letter-spacing: -0.04em;
margin-bottom: 16px;
text-shadow: 0 6px 32px rgba(0, 0, 0, 0.25);
}
.error-heading {
font-family: var(--font-head);
font-size: clamp(28px, 4vw, 40px);
font-weight: 600;
line-height: 1.2;
margin: 0 0 16px;
color: #fff;
}
.error-subtitle {
font-size: clamp(15px, 1.6vw, 17px);
line-height: 1.65;
color: rgba(255, 255, 255, 0.78);
margin: 0 auto 16px;
max-width: 520px;
}
.error-detail {
display: inline-block;
font-size: 12px;
letter-spacing: 0.04em;
color: rgba(255, 255, 255, 0.55);
margin: 0 0 32px;
padding: 6px 14px;
background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 100px;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.error-content :not(.error-detail) + .error-actions {
margin-top: 36px;
}
.error-actions {
display: flex;
gap: 14px;
justify-content: center;
flex-wrap: wrap;
margin-top: 8px;
}
@keyframes rise {
from {
opacity: 0;
transform: translateY(12px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (prefers-reduced-motion: reduce) {
.error-content {
animation: none;
}
}
@media (max-width: 560px) {
.error-page {
padding: 32px 20px;
}
.paw-1 {
font-size: 70px;
}
.paw-2 {
font-size: 86px;
}
.paw-3 {
font-size: 96px;
}
.paw-4 {
font-size: 76px;
}
.paw-5 {
display: none;
}
.error-actions {
flex-direction: column;
align-items: stretch;
width: 100%;
max-width: 320px;
margin-left: auto;
margin-right: auto;
}
.error-actions :global(.btn) {
width: 100%;
}
}
</style>