chadtmiller.com/knotless
git that lives in your Atmosphere account
Pages go brotli, run their theme script inline, and have an icon
MergedThis pull compresses the pages' answers in knotless-serve. It also moves the theme script into app.html with a sha256 CSP entry, adds a favicon, and makes two accessibility fixes. The inline-script hash, the favicon, the tabindex changes and the h2 heading look right. The test also covers the pass-through of an app-compressed answer.
The one thing to change: the body says brotli at quality 4, but the code sets no quality. CompressionLayer::new() uses the default level. If I remember async-compression right, that is brotli's maximum, quality 11, and I didn't check it. That is very slow for 55 to 97 KB of HTML, and these pages are rendered on every request and not cached. It would add latency to each page and cost CPU on the server.
Set the quality explicitly, for example .quality(tower_http::CompressionLevel::Precise(4)), so the code does what the body says and what you measured. Check whether the XRPC layers also use the default. If they do, that is a separate problem, not part of this pull.
The test only checks the content-encoding header, so it can't catch the quality. I didn't run anything, so the size numbers in the body are unchecked.
crates/knotless-serve/src/web/pages.rsline 45
let web: &'static str = web.trim_end_matches('/').to_string().leak(); | |
Router::new() | |
.fallback(move |req: Request| page(web, req)) | |
.layer(tower_http::compression::CompressionLayer::new()) |
This uses the default level, not the quality 4 the body describes. Set it with CompressionLayer::new().quality(CompressionLevel::Precise(4)).
The default is quality 4 for brotli, not 11: tower-http 0.7.1, which this workspace pins, sets it so itself in src/compression/body.rs (lines 333-339): CompressionLevel::Default => async_compression::Level::Precise(4), with the comment that brotli's own default of 11 is too slow, and that 4 is NGINX's default for on-the-fly brotli. Setting .quality(Precise(4)) here would do the same for brotli but also drop gzip, for browsers without brotli, from its default 6 to 4. So I've left it at the default, which the XRPC layers use too.
tower-http 0.7.1 already maps the default brotli level to 4 (src/compression/body.rs, BrotliEncoder::apply); setting it by hand would also lower gzip from 6 to 4.
8 passed, 2 skipped
What changes
Pages go brotli. knotless-serve compresses what the SvelteKit app answers, as it already compresses its own XRPC answers, so a page goes brotli (quality 4) to a browser that takes it instead of Caddy's gzip. What the app sends compressed already, as its scripts from
/_app/, goes as it is.The theme script is in the page. The script that picks a theme pair's half was
/theme.js, a request that blocked drawing and was never cached. It now sits inapp.html's head, and the pages' CSP allows it by its sha256, whichvite.config.tsreads fromapp.htmlas the pages are built, so the hash cannot fall out of step with the script.An icon. The site's tilde (the iOS app's icon) is
/favicon.svg, linked from every page, so browsers stop asking for/favicon.icoand logging a 404 on each page.Two accessibility fixes from Lighthouse. Line-number links in a file's gutter, which is
aria-hidden, no longer take keyboard focus (tabindex="-1"), on file pages and in code search. The commits list's day headings areh2under the page'sh1rather thanh3, drawn the same.Why
Measured on prod with Lighthouse and curl: a page's HTML is 55 to 97 KB gzipped, and brotli makes it 5 to 21% smaller (commits list 68 KB to 54 KB, a large file 97 KB to 74 KB).
/theme.jscost a round trip before the first paint on a cold load, and the favicon 404 was a console error on every page.How it was checked
pages_drawn_elsewherenow asks for a page withaccept-encoding: brand gets brotli, and finds an answer the app compressed itself passed through untouched. I builtweb/once and served it: the CSP header carries the inline script's hash, and/favicon.svganswers 200 asimage/svg+xml. The rest is left to CI. No page should move: every page's HTML changes (its head), but nothing drawn does.Shortcomings
Lighthouse also flags the language bar's segments on a repository's home as touch targets under 24px. They are deliberately thin, so fixing that means redesigning how the bar opens. It is left for a change of its own.
🤖 Generated with Claude Code