Files
summercms/modules/surf/errorpage.html
Jakub Zych b1236cbfd4 feat(surf): branded HTML error pages for browser navigations
A browser navigation (Sec-Fetch-Mode navigate, or GET/HEAD without it; no
X-Requested-With; Accept naming text/html above every JSON type; not /api,
/_<name> or the admin prefix) that hits a framework-generated 404, 405 or
500 gets a self-contained SummerCMS page: unmatched routes and wrong
methods at the ServeMux, failed Where/WhereIn constraints, overlap-family
misses, the unpublished uploads route and recovered panics. Every other
request keeps the exact stdlib text, wire's opaque JSON 500 or the bare
raw-group 500.

Details (method, path, X-Request-Id) only with app.debug or outside
production. Applications replace the page with a published
surf.ErrorPageRenderer; http.error_pages.enabled false turns it off.
2026-10-20 03:12:25 +02:00

122 lines
3.4 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex">
<title>{{.Status}} · {{.Title}} · SummerCMS</title>
<link rel="icon" type="image/webp" href="{{.Logo}}">
<style>
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 24px;
background-color: #233148;
background-image: radial-gradient(ellipse 80% 50% at 50% 0%, rgba(245, 197, 90, 0.10), rgba(245, 197, 90, 0) 70%);
color: #e9edf3;
font-family: Roboto, system-ui, -apple-system, "Segoe UI", sans-serif;
font-size: 16px;
-webkit-font-smoothing: antialiased;
}
.card {
width: 100%;
max-width: 480px;
padding: 40px 32px;
background: #29344a;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 16px;
box-shadow: 0 24px 48px rgba(0, 0, 0, 0.28), 0 2px 6px rgba(0, 0, 0, 0.18);
text-align: center;
}
.brand {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
margin-bottom: 24px;
}
.brand img { width: 60px; height: 60px; display: block; }
.wordmark { font-size: 18px; font-weight: 700; letter-spacing: 0.01em; color: #e9edf3; }
.gold { color: #fbd77e; }
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
.gold {
background-image: linear-gradient(135deg, #fde9b4 0%, #f5c55a 100%);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
}
.status {
display: inline-block;
margin: 0;
font-size: 72px;
font-weight: 700;
line-height: 1;
letter-spacing: -0.02em;
}
h1 { margin: 16px 0 8px; font-size: 22px; font-weight: 600; line-height: 1.3; color: #e9edf3; }
.message { margin: 0 0 28px; color: #a3adbd; line-height: 1.6; }
.home {
display: inline-block;
padding: 10px 24px;
background: #f5c55a;
color: #1d2940;
border-radius: 999px;
font-weight: 600;
text-decoration: none;
transition: background-color 0.15s ease;
}
.home:hover { background: #fbd77e; }
.home:focus-visible { outline: 2px solid #fbe3a6; outline-offset: 3px; }
.details {
margin: 28px 0 0;
padding: 14px 16px;
background: #18223a;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 6px;
display: grid;
grid-template-columns: auto 1fr;
gap: 6px 14px;
text-align: left;
font-family: "Roboto Mono", ui-monospace, monospace;
font-size: 13px;
}
.details dt { color: #8a94a3; }
.details dd { margin: 0; color: #c5ccd6; word-break: break-all; }
.caption { margin: 28px 0 0; font-size: 12px; color: #8a94a3; }
@media (max-width: 520px) {
body { padding: 16px; }
.card { padding: 28px 20px; }
.status { font-size: 56px; }
}
</style>
</head>
<body>
<main class="card">
<div class="brand">
<img src="{{.Logo}}" width="60" height="60" alt="SummerCMS">
<span class="wordmark">Summer<span class="gold">CMS</span></span>
</div>
<p class="status gold">{{.Status}}</p>
<h1>{{.Title}}</h1>
<p class="message">{{.Message}}</p>
<a class="home" href="{{.Home}}">Go to the home page</a>
{{- if .ShowDetails}}
<dl class="details">
<dt>Status</dt><dd>{{.Status}}</dd>
<dt>Method</dt><dd>{{.Method}}</dd>
<dt>Path</dt><dd>{{.Path}}</dd>
{{- if .RequestID}}
<dt>Request ID</dt><dd>{{.RequestID}}</dd>
{{- end}}
</dl>
{{- end}}
<p class="caption">Powered by SummerCMS</p>
</main>
</body>
</html>