ReviewOS

also looking at this

stacks/lastsoulultra

LSU's own coaching surface, and what auditing the site turned up

#12
Merged glennmichael123 wants to merge claude/last-soul-ultra-web-6e0718 into main
86 files +3943 -1190

Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.

public/assets/styles/production/not-found.cssadded+14-0
Changes to public/assets/styles/production/not-found.css
@@ -0,0 +1,14 @@
1/* /404.
2
3 The page used to be written in six class names that exist in no stylesheet
4 on this site - h-page, display, ink, faint, muted and btn-accent - left
5 over from an earlier design system, so its heading, eyebrow, body copy and
6 primary button all rendered unstyled. It is written in the site's own
7 vocabulary now: the same .phero block the legal pages use, and the .btn and
8 .btn-outline pair from base.css.
9
10 All this sheet adds is the row the two buttons sit in, and the bottom
11 padding .phero does not carry. */
12
13.not-found-actions{display:flex;flex-wrap:wrap;gap:14px;margin-top:10px}
14.phero:last-child{padding-bottom:clamp(90px,12vw,160px)}
resources/assets/styles/site/not-found.cssadded+14-0
Changes to resources/assets/styles/site/not-found.css
@@ -0,0 +1,14 @@
1/* /404.
2
3 The page used to be written in six class names that exist in no stylesheet
4 on this site - h-page, display, ink, faint, muted and btn-accent - left
5 over from an earlier design system, so its heading, eyebrow, body copy and
6 primary button all rendered unstyled. It is written in the site's own
7 vocabulary now: the same .phero block the legal pages use, and the .btn and
8 .btn-outline pair from base.css.
9
10 All this sheet adds is the row the two buttons sit in, and the bottom
11 padding .phero does not carry. */
12
13.not-found-actions{display:flex;flex-wrap:wrap;gap:14px;margin-top:10px}
14.phero:last-child{padding-bottom:clamp(90px,12vw,160px)}
resources/views/404.stxmodified+20-24
Changes to resources/views/404.stx
@@ -1,6 +1,7 @@
1@extends('layouts/LSU')
2@section('content')
13<script server>
2import { liveRaceState } from '../data/live'
3import { race } from '../data/race'
4import { liveRaceState } from '~/resources/data/live'
45
56/* The head partial reads `currentPage`. This page used to declare pageTitle,
67 pageDescription and noindex as loose names nothing looked at, so a 404
@@ -8,30 +9,25 @@ import { race } from '../data/race'
89const currentPage = {
910 title: 'not found - Last Soul Ultra',
1011 description: 'That page is not here.',
11 styles: ['/assets/styles/production/base.css'],
12 styles: ['/assets/styles/production/base.css', '/assets/styles/production/not-found.css'],
1213 noindex: true,
1314}
1415const canonicalPath = '/404'
1516const state = await liveRaceState()
1617</script>
17<!DOCTYPE html>
18<html lang="en">
19 <head>@include('head')</head>
20 <body>
21 <SiteNav current="/404" />
22 <main id="main" class="mx-auto px-5 py-20 lg:px-8 lg:py-32 max-w-[1100px]">
23 <p class="faint label">404</p>
24 <h1 class="mt-4 h-page max-w-[14ch] display ink">you missed the cut-off.</h1>
25 <p class="mt-6 max-w-[46ch] leading-relaxed text-base muted">That page is not here. The race, thankfully, still is.</p>
26 <div class="flex flex-wrap gap-3 mt-9">
27 <a class="btn btn-accent" href="/">back to the start</a>
28 @if(state !== 'upcoming')
29 <a class="btn btn-outline" href="/results">{{ state === 'finished' ? 'the result' : 'live results' }}</a>
30 @else
31 <a class="btn btn-outline" href="/format">the format</a>
32 @endif
33 </div>
34 </main>
35 <SiteFooter />
36 </body>
37</html>
18<section class="phero">
19 <div class="phero-copy">
20 <div class="kicker">404</div>
21 <h1>You missed the cut-off.</h1>
22 <p>That page is not here. The race, thankfully, still is.</p>
23 <div class="not-found-actions">
24 <a class="btn" href="/">Back to the start</a>
25 @if(state !== 'upcoming')
26 <a class="btn-outline" href="/results">{{ state === 'finished' ? 'The result' : 'Live results' }}</a>
27 @else
28 <a class="btn-outline" href="/format">The format</a>
29 @endif
30 </div>
31 </div>
32</section>
33@endsection
tests/seo.test.tsmodified+17-0
Changes to tests/seo.test.ts
@@ -158,4 +158,21 @@ describe('the page that is not found', () => {
158158 // Nothing crawls it, so it carries no graph.
159159 expect(html).not.toContain('application/ld+json')
160160 })
161
162 it('wears the same chrome as every other page', async () => {
163 const path = resolve('resources/views/404.stx')
164 const html = await processDirectives(await Bun.file(path).text(), {}, path, {
165 enabled: true,
166 cache: false,
167 componentsDir: resolve('resources/components'),
168 layoutsDir: resolve('resources/layouts'),
169 partialsDir: resolve('resources/partials'),
170 }, new Set())
171
172 // It used to write its own html, head and body, and so was the one page
173 // with no skip link, no focusable main and the wrong lang.
174 expect(html).toContain('class="lsu-skip"')
175 expect(html).toContain('<main id="main" tabindex="-1">')
176 expect(html).toContain('lang="en-GB"')
177 })
161178})