Skip to content

Commit 49a3399

Browse files
authored
fix(ui): unify branded error pages (#7057)
* fix(ui): unify branded error pages * fix(desktop): match canonical chip chrome * test(ui): update error chip mock * fix(desktop): package offline font reliably
1 parent b7b0be8 commit 49a3399

18 files changed

Lines changed: 299 additions & 239 deletions

File tree

apps/desktop/e2e/smoke.spec.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,5 +110,18 @@ test.describe('desktop shell smoke', () => {
110110
const window = await app.firstWindow()
111111
await window.waitForSelector('#retry', { timeout: 30_000 })
112112
expect(window.url().startsWith('file:')).toBe(true)
113+
await expect(window.locator('.wordmark')).toBeVisible()
114+
await expect(window.locator('.wordmark')).toHaveAttribute('aria-label', 'Sim')
115+
await expect(window.locator('#title')).toHaveText('Can’t connect to Sim')
116+
await expect(window.locator('#status')).toHaveText('Check status')
117+
await expect
118+
.poll(() => window.evaluate(() => document.fonts.check('16px "Season Sans"')))
119+
.toBe(true)
120+
await expect(window.locator('#retry')).toHaveCSS('height', '30px')
121+
await expect(window.locator('#retry')).toHaveCSS('border-radius', '8px')
122+
await expect(window.locator('#retry')).toHaveCSS('padding-left', '8px')
123+
await expect(window.locator('#retry')).toHaveCSS('font-size', '14px')
124+
await expect(window.locator('#retry')).toHaveCSS('line-height', '20px')
125+
await expect(window.locator('#retry')).toHaveCSS('text-align', 'left')
113126
})
114127
})

apps/desktop/electron-builder.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ files:
1010
- dist/**
1111
- static/**
1212
- package.json
13+
- from: ../sim/public/brand/fonts
14+
to: static
15+
filter:
16+
- SeasonSansUprightsVF.woff2
1317

1418
asar: true
1519

apps/desktop/static/offline.html

Lines changed: 142 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,27 @@
44
<meta charset="utf-8" />
55
<meta
66
http-equiv="Content-Security-Policy"
7-
content="default-src 'none'; style-src 'unsafe-inline'; script-src 'unsafe-inline'"
7+
content="default-src 'none'; font-src 'self'; style-src 'unsafe-inline'; script-src 'unsafe-inline'"
88
/>
9-
<title>Sim Can’t connect</title>
9+
<title>Sim - Can’t connect</title>
1010
<style>
11+
@font-face {
12+
font-family: 'Season Sans';
13+
src:
14+
url('../../sim/public/brand/fonts/SeasonSansUprightsVF.woff2') format('woff2'),
15+
url('./SeasonSansUprightsVF.woff2') format('woff2');
16+
font-style: normal;
17+
font-weight: 300 800;
18+
font-display: block;
19+
}
1120
:root {
12-
color-scheme: light dark;
13-
--bg: #ffffff;
14-
--fg: #0c0c0c;
15-
--muted: #6b6b6b;
16-
--border: #e4e4e4;
17-
--accent: #701ffc;
18-
--accent-fg: #ffffff;
19-
}
20-
@media (prefers-color-scheme: dark) {
21-
:root {
22-
--bg: #0c0c0c;
23-
--fg: #f4f4f4;
24-
--muted: #9a9a9a;
25-
--border: #262626;
26-
}
21+
color-scheme: light;
22+
--bg: #fefefe;
23+
--text-primary: #1a1a1a;
24+
--text-body: #434343;
25+
--text-muted: #7a7a7a;
26+
--text-inverse: #ffffff;
27+
--surface-hover: #f2f2f2;
2728
}
2829
* {
2930
box-sizing: border-box;
@@ -35,111 +36,171 @@
3536
}
3637
body {
3738
display: flex;
38-
align-items: center;
39-
justify-content: center;
39+
flex-direction: column;
40+
min-width: 320px;
41+
padding-top: max(env(titlebar-area-height, 38px), 38px);
4042
background: var(--bg);
41-
color: var(--fg);
42-
font-family:
43-
-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
43+
color: var(--text-primary);
44+
font-family: 'Season Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
4445
-webkit-font-smoothing: antialiased;
4546
user-select: none;
4647
}
47-
main {
48-
max-width: 420px;
49-
padding: 32px;
50-
text-align: center;
48+
header {
49+
flex: none;
50+
width: 100%;
51+
max-width: 1460px;
52+
margin: 0 auto;
53+
padding: 16px 80px;
54+
-webkit-app-region: drag;
55+
}
56+
.wordmark {
57+
display: block;
58+
width: 37px;
59+
height: 18px;
60+
margin: 6px 0;
61+
color: var(--text-body);
5162
}
52-
.mark {
53-
width: 56px;
54-
height: 56px;
55-
margin: 0 auto 20px;
56-
border-radius: 14px;
57-
background: var(--accent);
63+
main {
5864
display: flex;
65+
flex: 1;
66+
width: 100%;
5967
align-items: center;
6068
justify-content: center;
61-
color: var(--accent-fg);
62-
font-size: 26px;
63-
font-weight: 600;
64-
letter-spacing: -0.02em;
69+
padding: 0 16px 64px;
70+
}
71+
.content {
72+
display: flex;
73+
width: 100%;
74+
max-width: 410px;
75+
flex-direction: column;
76+
align-items: center;
77+
gap: 12px;
78+
text-align: center;
6579
}
6680
h1 {
67-
font-size: 17px;
68-
font-weight: 600;
69-
margin: 0 0 8px;
81+
max-width: 100%;
82+
margin: 0;
83+
font-size: 40px;
84+
font-weight: 400;
85+
letter-spacing: -0.02em;
86+
line-height: 1.1;
87+
text-wrap: balance;
7088
}
7189
p {
72-
font-size: 13px;
73-
line-height: 1.5;
74-
color: var(--muted);
75-
margin: 0 0 24px;
90+
max-width: 100%;
91+
margin: 0;
92+
color: var(--text-muted);
93+
font-size: 18px;
94+
line-height: 28px;
7695
}
7796
.actions {
7897
display: flex;
98+
flex-wrap: wrap;
7999
gap: 8px;
80100
justify-content: center;
101+
margin-top: 12px;
81102
}
82103
button {
104+
display: inline-flex;
105+
height: 30px;
106+
align-items: center;
107+
gap: 6px;
83108
appearance: none;
84-
border: 1px solid var(--border);
109+
border: 0;
85110
background: transparent;
86-
color: var(--fg);
111+
color: var(--text-body);
87112
border-radius: 8px;
88-
padding: 7px 14px;
89-
font-size: 13px;
90-
font-weight: 500;
113+
padding: 0 8px;
114+
font: inherit;
115+
font-size: 14px;
116+
line-height: 20px;
117+
text-align: left;
91118
cursor: pointer;
119+
outline: none;
120+
transition:
121+
color 150ms cubic-bezier(0.4, 0, 0.2, 1),
122+
background-color 150ms cubic-bezier(0.4, 0, 0.2, 1),
123+
border-color 150ms cubic-bezier(0.4, 0, 0.2, 1),
124+
text-decoration-color 150ms cubic-bezier(0.4, 0, 0.2, 1),
125+
fill 150ms cubic-bezier(0.4, 0, 0.2, 1),
126+
stroke 150ms cubic-bezier(0.4, 0, 0.2, 1);
127+
-webkit-app-region: no-drag;
128+
}
129+
button .label {
130+
min-width: 0;
131+
flex: 1;
132+
overflow: hidden;
133+
color: currentColor;
134+
text-overflow: ellipsis;
135+
white-space: nowrap;
92136
}
93137
button.primary {
94-
background: var(--accent);
95-
border-color: var(--accent);
96-
color: var(--accent-fg);
97-
}
98-
button:active {
99-
opacity: 0.8;
100-
}
101-
.links {
102-
margin-top: 20px;
103-
font-size: 12px;
104-
}
105-
.links a {
106-
color: var(--muted);
107-
text-decoration: none;
108-
margin: 0 8px;
109-
cursor: pointer;
138+
background: var(--text-primary);
139+
color: var(--text-inverse);
110140
}
111-
.links a:hover {
112-
color: var(--fg);
141+
@media (hover: hover) and (pointer: fine) {
142+
button.primary:hover {
143+
background: var(--text-body);
144+
}
145+
button.secondary:hover {
146+
background: var(--surface-hover);
147+
}
113148
}
114149
#detail {
115-
font-size: 11px;
116-
color: var(--muted);
117-
margin-top: 16px;
150+
min-height: 17px;
151+
margin-top: 4px;
152+
color: var(--text-muted);
118153
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
154+
font-size: 11px;
155+
line-height: 1.5;
156+
}
157+
@media (max-width: 720px) {
158+
header {
159+
padding-right: 32px;
160+
padding-left: 32px;
161+
}
162+
}
163+
@media (max-width: 480px) {
164+
header {
165+
padding-right: 20px;
166+
padding-left: 20px;
167+
}
168+
h1 {
169+
font-size: 34px;
170+
}
119171
}
120172
</style>
121173
</head>
122174
<body>
175+
<header aria-label="Sim">
176+
<svg class="wordmark" viewBox="0 0 441 212" role="img" aria-label="Sim">
177+
<g fill="currentColor">
178+
<path d="M0 160.9H29.51C29.51 169.08 32.46 175.61 38.37 180.48C44.27 185.12 52.25 187.44 62.31 187.44C73.24 187.44 81.65 185.34 87.56 181.14C93.46 176.71 96.41 170.85 96.41 163.55C96.41 158.24 94.77 153.82 91.49 150.28C88.43 146.74 82.75 143.86 74.44 141.65L46.24 135.01C32.03 131.47 21.42 126.05 14.43 118.75C7.65 111.45 4.26 101.83 4.26 89.88C4.26 79.93 6.78 71.3 11.81 64C17.05 56.7 24.16 51.06 33.12 47.08C42.3 43.09 52.8 41.1 64.6 41.1C76.41 41.1 86.57 43.2 95.1 47.41C103.84 51.61 110.62 57.47 115.43 64.99C120.46 72.52 123.08 81.48 123.3 91.87H93.79C93.57 83.47 90.84 76.94 85.59 72.3C80.34 67.65 73.02 65.33 63.62 65.33C54 65.33 46.57 67.43 41.32 71.63C36.07 75.83 33.45 81.59 33.45 88.89C33.45 99.73 41.32 107.14 57.06 111.12L85.26 118.09C98.81 121.19 108.98 126.28 115.76 133.35C122.53 140.21 125.92 149.61 125.92 161.56C125.92 171.74 123.19 180.7 117.73 188.44C112.26 195.96 104.72 201.82 95.1 206.03C85.7 210.01 74.55 212 61.65 212C42.85 212 27.87 207.35 16.72 198.06C5.57 188.77 0 176.38 0 160.9Z" />
179+
<path d="M232.8 212H202.13L202.13 49.76H229.54V77.39C232.8 68.34 239.11 60.66 247.81 54.7C256.73 48.52 267.5 45.43 280.12 45.43C294.26 45.43 306.01 49.29 315.36 57.02C324.72 64.75 330.81 75.01 333.64 87.82H328.09C330.27 75.01 336.25 64.75 346.04 57.02C355.83 49.29 367.9 45.43 382.26 45.43C400.54 45.43 414.89 50.84 425.34 61.66C435.78 72.47 441 87.26 441 106.03V212H410.98V113.65C410.98 100.84 407.71 91.02 401.19 84.17C394.88 77.11 386.29 73.58 375.41 73.58C367.79 73.58 361.05 75.34 355.17 78.88C349.52 82.19 345.06 87.04 341.8 93.45C338.53 99.85 336.9 107.36 336.9 115.97V212H306.55V113.32C306.55 100.51 303.4 90.8 297.09 84.17C290.78 77.33 282.19 73.91 271.31 73.91C263.69 73.91 256.95 75.67 251.08 79.21C245.42 82.52 240.96 87.38 237.7 93.78C234.43 99.96 232.8 107.36 232.8 115.97V212Z" />
180+
<path d="M184.83 20.55C184.83 31.9 175.64 41.1 164.29 41.1C152.95 41.1 143.76 31.9 143.76 20.55C143.76 9.2 152.95 0 164.29 0C175.64 0 184.83 9.2 184.83 20.55Z" />
181+
<path d="M179.43 212H149.16V49.76C153.76 51.91 158.88 53.12 164.29 53.12C169.7 53.12 174.83 51.91 179.43 49.76V212Z" />
182+
</g>
183+
</svg>
184+
</header>
123185
<main>
124-
<div class="mark">S</div>
125-
<h1 id="title">Can’t connect to Sim</h1>
126-
<p id="message">
127-
Sim couldn’t reach the server. Check your internet connection, then try again.
128-
</p>
129-
<div class="actions">
130-
<button class="primary" id="retry">Retry</button>
131-
</div>
132-
<div class="links">
133-
<a id="status">Check status</a>
186+
<div class="content">
187+
<h1 id="title">Can’t connect to Sim</h1>
188+
<p id="message">
189+
Sim couldn’t reach the server. Check your internet connection, then try again.
190+
</p>
191+
<div class="actions">
192+
<button class="primary" id="retry"><span class="label">Retry</span></button>
193+
<button class="secondary" id="status"><span class="label">Check status</span></button>
194+
</div>
195+
<div id="detail" role="status"></div>
134196
</div>
135-
<div id="detail"></div>
136197
</main>
137198
<script>
138199
const params = new URLSearchParams(location.search)
139200
const copy = {
140201
offline: {
141202
title: 'You’re offline',
142-
message: 'Sim needs an internet connection. Reconnect, then try again — we’ll also retry automatically.',
203+
message: 'Sim needs an internet connection. Reconnect, then try again. We’ll also retry automatically.',
143204
},
144205
dns: {
145206
title: 'Can’t find the server',

apps/docs/app/[lang]/not-found.tsx

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,20 @@ export const metadata = {
88
export default function NotFound() {
99
return (
1010
<DocsPage>
11-
<div className='flex min-h-[70vh] flex-col items-center justify-center gap-4 text-center'>
12-
<h1 className='bg-gradient-to-b from-[var(--brand-accent)] to-[var(--brand-accent-hover)] bg-clip-text font-semibold text-8xl text-transparent'>
13-
404
14-
</h1>
15-
<h2 className='font-semibold text-2xl text-[var(--text-primary)]'>Page Not Found</h2>
16-
<p className='text-[var(--text-muted)]'>
17-
The page you're looking for doesn't exist or has been moved.
18-
</p>
19-
<ChipLink href='/' variant='primary'>
20-
Go home
21-
</ChipLink>
11+
<div className='flex min-h-[60vh] flex-col items-center justify-center px-4 py-24 text-center'>
12+
<div className='flex w-full max-w-[410px] flex-col items-center gap-3'>
13+
<h1 className='text-balance text-[40px] text-[var(--text-primary)] leading-[110%] tracking-[-0.02em]'>
14+
Page not found
15+
</h1>
16+
<p className='text-[var(--text-muted)] text-lg'>
17+
The page you&apos;re looking for doesn&apos;t exist or has been moved.
18+
</p>
19+
<div className='mt-3 flex flex-wrap items-center justify-center gap-2'>
20+
<ChipLink href='/' variant='primary'>
21+
Return home
22+
</ChipLink>
23+
</div>
24+
</div>
2225
</div>
2326
</DocsPage>
2427
)
Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ChipLink } from '@sim/emcn'
22
import type { Metadata } from 'next'
3+
import { StatusPage } from '@/components/status-page'
34

45
export const metadata: Metadata = {
56
title: 'Page Not Found',
@@ -8,19 +9,13 @@ export const metadata: Metadata = {
89

910
export default function BlogAuthorNotFound() {
1011
return (
11-
<main
12-
id='main-content'
13-
className='mx-auto flex min-h-[60vh] w-full max-w-[1460px] flex-col items-center justify-center gap-3 px-20 py-24 text-center max-sm:px-5 max-lg:px-8'
12+
<StatusPage
13+
title='Author not found'
14+
description="The author you're looking for doesn't exist or has been moved."
1415
>
15-
<h1 className='text-balance text-[40px] text-[var(--text-primary)] leading-[110%] tracking-[-0.02em]'>
16-
Author not found
17-
</h1>
18-
<p className='text-[var(--text-muted)] text-lg'>
19-
The author you&apos;re looking for doesn&apos;t exist or has been moved.
20-
</p>
21-
<ChipLink variant='primary' href='/blog' className='mt-3'>
16+
<ChipLink variant='primary' href='/blog'>
2217
Browse blog
2318
</ChipLink>
24-
</main>
19+
</StatusPage>
2520
)
2621
}

0 commit comments

Comments
 (0)