Skip to content

Commit 88704fe

Browse files
committed
feat: implement global error boundary for graceful fallback UI
1 parent 176a2bc commit 88704fe

1 file changed

Lines changed: 137 additions & 0 deletions

File tree

apps/web/src/routes/+error.svelte

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
<script>
2+
import { page } from '$app/stores';
3+
4+
function handleRefresh() {
5+
window.location.reload();
6+
}
7+
</script>
8+
9+
<svelte:head>
10+
<title>Error | DevCard</title>
11+
</svelte:head>
12+
13+
<div class="error-container">
14+
<div class="error-card glass">
15+
<div class="error-icon">
16+
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
17+
<circle cx="12" cy="12" r="10"></circle>
18+
<line x1="12" y1="8" x2="12" y2="12"></line>
19+
<line x1="12" y1="16" x2="12.01" y2="16"></line>
20+
</svg>
21+
</div>
22+
23+
<h1 class="gradient-text">Oops! Something went wrong</h1>
24+
25+
<p class="error-message">
26+
We encountered an unexpected error while loading this developer card.
27+
<br>
28+
{#if $page.error?.message}
29+
<span class="error-detail">{$page.status}: {$page.error.message}</span>
30+
{/if}
31+
</p>
32+
33+
<div class="action-buttons">
34+
<button class="btn-primary" onclick={handleRefresh}>
35+
Try Again
36+
</button>
37+
<a href="/" class="btn-secondary">
38+
Go Home
39+
</a>
40+
</div>
41+
</div>
42+
</div>
43+
44+
<style>
45+
.error-container {
46+
display: flex;
47+
align-items: center;
48+
justify-content: center;
49+
min-height: 100vh;
50+
padding: 1.5rem;
51+
background: var(--bg-page);
52+
}
53+
54+
.error-card {
55+
display: flex;
56+
flex-direction: column;
57+
align-items: center;
58+
text-align: center;
59+
max-width: 550px;
60+
width: 100%;
61+
padding: 3rem 2rem;
62+
border-radius: var(--radius-lg);
63+
animation: slideUp 0.5s cubic-bezier(0.16, 1, 0.3, 1);
64+
}
65+
66+
.error-icon {
67+
color: #ef4444;
68+
margin-bottom: 1.5rem;
69+
background: rgba(239, 68, 68, 0.1);
70+
padding: 1.25rem;
71+
border-radius: 50%;
72+
box-shadow: 0 4px 14px -6px rgba(239, 68, 68, 0.4);
73+
}
74+
75+
h1 {
76+
font-size: 2.25rem;
77+
margin-bottom: 1rem;
78+
}
79+
80+
.error-message {
81+
color: var(--text-secondary);
82+
font-size: 1.1rem;
83+
line-height: 1.6;
84+
margin-bottom: 2.5rem;
85+
}
86+
87+
.error-detail {
88+
display: inline-block;
89+
margin-top: 1rem;
90+
font-size: 0.9rem;
91+
padding: 0.75rem 1rem;
92+
background: var(--bg-secondary);
93+
border: 1px solid var(--border);
94+
border-radius: var(--radius);
95+
color: var(--text-muted);
96+
font-family: monospace;
97+
word-break: break-word;
98+
}
99+
100+
.action-buttons {
101+
display: flex;
102+
gap: 1.25rem;
103+
flex-wrap: wrap;
104+
justify-content: center;
105+
}
106+
107+
@keyframes slideUp {
108+
from {
109+
opacity: 0;
110+
transform: translateY(20px);
111+
}
112+
to {
113+
opacity: 1;
114+
transform: translateY(0);
115+
}
116+
}
117+
118+
@media (max-width: 640px) {
119+
.error-card {
120+
padding: 2.5rem 1.5rem;
121+
}
122+
123+
h1 {
124+
font-size: 1.75rem;
125+
}
126+
127+
.action-buttons {
128+
width: 100%;
129+
flex-direction: column;
130+
gap: 1rem;
131+
}
132+
133+
.action-buttons > * {
134+
width: 100%;
135+
}
136+
}
137+
</style>

0 commit comments

Comments
 (0)