Skip to content

feat: support dark mode - #217

Merged
magnus-madsen merged 2 commits into
masterfrom
dark-mode
Aug 9, 2026
Merged

feat: support dark mode#217
magnus-madsen merged 2 commits into
masterfrom
dark-mode

Conversation

@magnus-madsen

Copy link
Copy Markdown
Member

Adds dark mode across all ten pages. The site follows the OS setting on first visit and offers a toggle in the navbar; once used, the choice persists and outranks the OS from then on.

How it works

Bootstrap 5.3's dark palette is driven entirely by data-bs-theme — the precompiled stylesheet we import contains no prefers-color-scheme rules at all, so a pure-CSS approach would have needed a Sass build step and would have ruled out a manual toggle. Instead an is:inline script at the top of <head> reads localStorage, falls back to matchMedia, and stamps the attribute on <html> before the first paint, so there is no flash of the wrong theme.

Colours

The three surfaces the site is made of — the page, the card, and the code blocks — are now named as custom properties, which is what lets the dark theme swap all of them in one place.

The brand red and green are redefined alongside them. Both were darkened specifically to clear 4.5:1 against white, as the comments in global.css record, and both fall to 3.4:1 on a dark card. The dark theme takes the same two colours the other way instead:

light dark
link / .text-primary #a63839 6.47:1 #ef8b8c 6.42:1
.text-success #008938 4.53:1 #00b34a 5.54:1

The green keeps hue 145 at full saturation exactly as the light one does; only its lightness moves. Ratios were measured rather than estimated, against a checker validated on the existing values the comments already state.

The navbar needs its own surface in dark mode: .bg-dark resolves to #212529, which is also Bootstrap's dark --bs-body-bg and therefore the card, so the bar would have dissolved into the page behind it. Lifting it one step leaves inactive nav links at 5.20:1, down from 5.67:1 and still clear of 4.5.

Code snippets

Snippets now ship both Plus themes at once through Shiki's dual-theme output. The existing correction that unifies Flix keywords on keyword.control's magenta is applied to each theme in its own magenta — Light+'s #AF00DB is illegible on a dark background. Note that Astro renames Shiki's class to astro-code on the way out, so the theme switch keys off that rather than the documented .shiki.

Images

Screenshots, figures, and sponsor logos keep a white ground in both themes, via a single .plate class. Nearly every image on the site carries an alpha channel and the sponsor marks are dark ink on transparency, so on a dark page they would otherwise have gone dark-on-dark. Recolouring them was not an option — several are other organisations' marks. In light mode the plate is the colour of the card underneath, so it changes nothing there.

Verification

astro check and astro build are both clean. Visually checked across all ten pages in both themes.

🤖 Generated with Claude Code

magnus-madsen and others added 2 commits August 9, 2026 19:34
Follows the OS setting on first visit and offers a toggle in the navbar,
which persists the choice and from then on outranks the OS.

Bootstrap 5.3's dark palette is driven entirely by data-bs-theme; the
precompiled stylesheet we import carries no prefers-color-scheme rules at
all. The media query is therefore read by an inline script in the head,
which stamps the attribute on <html> before the first paint so the page
never flashes the wrong theme.

The three surfaces the site is made of - page, card, and code block - are
now named as custom properties so the dark theme can swap all of them in
one place. The brand red and green are redefined alongside them: both were
darkened specifically to clear 4.5:1 against white and fall to 3.4:1 on a
dark card, so the dark theme takes them the other way instead, to 6.4:1 and
5.5:1. The green keeps its hue and saturation; only its lightness moves.

Code snippets ship both Plus themes at once via Shiki's dual-theme output,
with the existing magenta keyword correction applied to each in its own
magenta - the light one is illegible on a dark background.

Screenshots, figures, and sponsor logos keep a white ground in both themes.
Nearly all of them carry an alpha channel, and the sponsor marks are dark
ink on transparency, so on a dark page they would otherwise have gone
dark-on-dark. Recolouring them is not an option; several are other
organisations' marks.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Removes --flix-bg-card, which was #ffffff light and #212529 dark - the
same two values as --bs-body-bg, which .card, .list-group and .dropdown
already resolve to. The card was being kept in step with a palette we do
not own, by hand; .page now reads that token directly.

The navbar had no name at all: its colour came from .bg-dark in light
mode and from overriding --bs-dark-rgb underneath it in dark mode, two
idioms for one surface, the second of them only to get out of the way of
.bg-dark's !important. It becomes --flix-bg-navbar, painted by .menu,
with .bg-dark dropped from the markup. The light value is what .bg-dark
was already painting, so the light navbar is unchanged.

The toggle no longer restates what base .btn computes for itself: it was
setting background-color: transparent, which .btn already applies via
--bs-btn-bg, on the strength of a comment claiming the --bs-btn-* tokens
are undefined without a variant. They are not. Pointing --bs-btn-color
and --bs-btn-hover-color at the navbar's colours does the same job and
collapses the separate :hover rule.

The two icon rules become one. The restore was dead: the button is a
flex container, so whichever icon is left standing is blockified whatever
its display says. The :not() form also leaves the moon showing if the
script never runs, matching the light theme the page falls back to.

Drops the `shiki` type import, which is not a dependency here and
resolved only through astro, and derives each theme's name from the one
it extends so the two cannot drift apart.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@magnus-madsen
magnus-madsen merged commit ef7ea2e into master Aug 9, 2026
2 checks passed
@magnus-madsen
magnus-madsen deleted the dark-mode branch August 9, 2026 17:52
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