Skip to content

refactor: move the theme toggle into a file of its own - #219

Merged
magnus-madsen merged 1 commit into
masterfrom
extract-theme-toggle
Aug 9, 2026
Merged

refactor: move the theme toggle into a file of its own#219
magnus-madsen merged 1 commit into
masterfrom
extract-theme-toggle

Conversation

@magnus-madsen

Copy link
Copy Markdown
Member

Moves the theme toggle out of the <script> block in Layout.astro and into src/scripts/theme-toggle.ts.

The motivation is typechecking: a script block in an .astro file is not checked, a .ts file is. astro check goes from 25 files to 26 and passes clean. The block left behind becomes a third import alongside Bootstrap's two, which reads as a list of what runs on the page rather than as a place where some of it happens to live.

Nothing changes in the output

Astro was already bundling this block into its own file, so this is a source move only. The emitted bundle is byte-identical, down to the content hash:

before after
bundle Layout.astro_astro_type_script_index_0_lang.DHQd3bgp.js same hash
size 23,047 bytes 23,047 bytes

The built HTML carries the same three script tags, and the inline head script is untouched.

Why the other half stays in the layout

The theme is picked by an is:inline script in <head> that has to run before the first paint. That one cannot move:

  • It cannot be an import, because a bundled module is deferred — which is the flash it exists to prevent.
  • It cannot take a src, because that puts a blocking request in front of the paint it is trying to get ahead of.

Inlining it from a file with ?raw and set:html does work — I checked, and it would even save about 100 bytes a page, since the file's indentation is shallower than the .astro nesting. It was not worth it: the comment above that script explains why it must be inline and pre-paint, and moving the code away from that reasoning makes the subtle part easier to break. ?raw also injects verbatim, so the file has to stay plain .js forever or it ships broken TypeScript to the browser with nothing to catch it.

🤖 Generated with Claude Code

A script block in an .astro file is not typechecked; a .ts file is. The
toggle handler and the prefers-color-scheme listener move out to
src/scripts/theme-toggle.ts, and the block that held them becomes a third
import alongside Bootstrap's two - a list of what runs on the page rather
than a place where some of it happens to live.

Nothing changes in the output. Astro was already bundling this block to
its own file, so the emitted bundle is byte-identical, down to the content
hash: Layout.astro_astro_type_script_index_0_lang.DHQd3bgp.js, 23047
bytes, before and after. astro check goes from 25 files to 26.

The theme's other half stays where it is. It has to be is:inline in the
head and run before the first paint, so it cannot be an import, and giving
it a src would put a blocking request in front of the paint it exists to
get ahead of. Inlining it from a file via ?raw does work - it would even
save about 100 bytes a page on indentation - but it splits the reasoning
about why that script is inline away from the script, and leaves a file
that silently ships broken if anyone types TypeScript into it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@magnus-madsen
magnus-madsen merged commit 4351fc0 into master Aug 9, 2026
2 checks passed
@magnus-madsen
magnus-madsen deleted the extract-theme-toggle branch August 9, 2026 18:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant