Dark Mode Architecture: System Preference Detection vs the Manual Toggle

“Supports dark mode” is one of the cheapest claims a WordPress theme can make and one of the easiest to get almost-right. Toggle it on, the colors flip, the screenshot looks good, ship it. The part that actually separates a dark mode implementation from a dark mode demo never shows up in a screenshot: what happens in the few hundred milliseconds between the browser receiving the HTML and the first paint, and what happens again on every page load after a visitor has made an explicit choice. Get that wrong and you get the single most common dark-mode bug in the wild, a flash of the wrong theme, light for a frame before dark snaps in, or dark-before-light on the way back, every single navigation, forever, because nobody traced the actual mechanism before shipping it.
We’ve written about this failure mode in the abstract before, in Why Your Dark Mode Flashes Before JavaScript Runs, which covers the general shape of the bug and the general shape of the fix: a blocking inline script, run before the stylesheet, before the body paints. This post does something narrower and more useful than restating that pattern. We pulled Brndle’s actual dark-mode source, the JS controller, the Blade layout, the Settings class that generates the CSS, and the stylesheet that consumes it, directly from the public repository and traced the real mechanism end to end: how the three-state model (light, dark, system) gets its first value before any JavaScript executes, how a manual toggle choice survives a page reload, how the actual color values move from a single admin-picked accent hex into custom properties the whole site can read, and whether the flash-of-wrong-theme problem the companion post describes in general is actually solved here, or just assumed to be. The answer has a genuine nuance in it that a feature-bullet description would never surface, and we’re reporting it exactly as we found it.
Table of contents
- The three-state model and the server-rendered first value
- System-preference detection: it’s CSS, not JavaScript
- The manual toggle and how the override actually persists
- How the custom properties actually swap values
- The flash-of-wrong-theme question, confirmed
- A testing checklist for your own site’s dark mode
- FAQ
- The takeaway
The three-state model and the server-rendered first value
Brndle’s dark mode is not a two-state on/off switch with a cookie. It’s a three-state model, light, dark, and system, and the whole architecture is organized around a single source of truth for which state is active at any moment: a data-theme attribute on the <html> element. Every CSS rule that changes between modes keys off that one attribute, and every piece of JavaScript that touches dark mode reads and writes that same attribute rather than keeping its own parallel state.
The admin side of this lives in app/Settings/Settings.php and surfaces through a React settings tab (admin/src/tabs/DarkMode.jsx). Three settings matter: dark_mode_toggle (boolean, whether a visitor-facing toggle button exists at all), dark_mode_default (light, dark, or system, what the site starts in before anyone has touched the toggle), and dark_mode_toggle_position (bottom-left, bottom-right, or header). A view composer, app/View/Composers/Theme.php, reads those three settings on every request and injects them into every Blade view as $showDarkModeToggle, $darkModeDefault, and $darkModeTogglePosition, so no template has to resolve them manually. Confirmed directly from that file:
// Dark Mode
'darkModeDefault' => Settings::get('dark_mode_default', 'light'),
'showDarkModeToggle' => (bool) Settings::get('dark_mode_toggle', true),
'darkModeTogglePosition' => Settings::get('dark_mode_toggle_position', 'bottom-right'),
The actual first value gets decided in resources/views/layouts/app.blade.php, in a PHP block at the very top of the layout, before a single byte of HTML is sent:
$toggleDriven = (bool) ($showDarkModeToggle ?? false);
$initialTheme = in_array($darkModeDefault, ['light', 'dark', 'system'], true) ? $darkModeDefault : 'light';
That value is printed straight into the opening tag of the document:
<html <?php language_attributes(); ?> class="scroll-smooth" data-theme="{{ $initialTheme }}" data-brndle-sticky-mode="{{ $stickyMode }}">
This is the detail that makes everything downstream work: data-theme is never missing, never set by JavaScript as a prerequisite for the page to render correctly, and never dependent on a round trip. It comes out of PHP, on the server, as part of the HTML response itself. A visitor with JavaScript disabled still gets whichever mode the admin configured as the default. Confirmed: if the admin never chooses system, and the visitor never touches a toggle, the site never consults prefers-color-scheme at all, it just renders the configured default, every time, which is the correct and expected behavior for a site that wants one fixed look rather than an adaptive one.
System-preference detection: it’s CSS, not JavaScript
Here’s the first finding worth being precise about, because it cuts against how “system preference detection” usually gets described. In most explanations, detecting the OS theme is framed as a JavaScript job: call window.matchMedia('(prefers-color-scheme: dark)'), check .matches, apply a class. Brndle doesn’t do it that way for the initial render, and the reason is a better reason than “we forgot”, a CSS media query resolves at style-computation time, before any script executes, with zero latency and zero flash risk. JavaScript is strictly slower and strictly riskier for this specific job, so the architecture keeps JS out of it entirely for the first paint.
The real mechanism lives in resources/css/app.css, in a Tailwind v4 @custom-variant block, with a comment explaining exactly why it exists. Tailwind v4 defaults its dark: variant to prefers-color-scheme alone, which would make every dark:bg-foo utility ignore Brndle’s own toggle entirely. The theme rebinds that variant to three selector cases instead, confirmed verbatim from the file:
@custom-variant dark {
&:where([data-theme="dark"], [data-theme="dark"] *) {
@slot;
}
@media (prefers-color-scheme: dark) {
&:where([data-theme="system"], [data-theme="system"] *) {
@slot;
}
&:where(:root:not([data-theme]) *, :root:not([data-theme])) {
@slot;
}
}
}
Three cases, and it’s worth naming what each one actually covers rather than skimming past them as equivalent. Case one, [data-theme="dark"], fires whenever the attribute is literally dark, admin default or user override, doesn’t matter which. Case two is the real system-detection path: [data-theme="system"] wrapped in @media (prefers-color-scheme: dark), meaning dark: utilities only activate when the attribute says system and the OS reports dark. Case three, :root:not([data-theme]) wrapped in the same media query, is a defensive fallback for a scenario that, per the component’s own comment, covers “the brief moment before the FOUC script runs, plus any child theme that drops the data-theme attribute entirely.” In Brndle’s own stock templates that moment doesn’t actually occur, the attribute is always present starting from the very first byte of the <html> tag, printed server-side, so case three is a safety net for forks and child themes rather than something that fires in a stock install. We’re flagging that distinction because the comment reads as if it covers a real timing gap, and on the stock theme, it doesn’t; it’s defensive code for a codepath Brndle itself doesn’t take.
The practical result: if you set the admin default to system, a first-time visitor with no stored preference gets the correct theme for their OS on the very first paint, with no JavaScript involved in that decision at all. The browser resolves the media query the same way it resolves any other CSS at layout time. There’s nothing to flash, because there’s no async step between “page received” and “correct colors shown.”
The manual toggle and how the override actually persists
The visitor-facing toggle is a genuinely separate system from the CSS-level detection above, and it’s the one place real JavaScript does the work. The button itself, in resources/views/partials/components/dark-mode-toggle.blade.php, is described in its own comment as a “dumb partial”, no ID, no inline script, just a <button data-brndle-dark-toggle> with three SVG icons (sun, moon, half-circle) inside it, shown or hidden by a data-brndle-icon attribute match. Every bit of behavior lives in one file, resources/js/dark-mode.js, loaded once and bound to every toggle button on the page via a single delegated controller rather than per-button inline scripts, confirmed by its own module docblock: “Single global handler for every [data-brndle-dark-toggle] button on the page.”
The state machine is small and cycles in one direction: system → dark → light → system. Here is the actual cycle and persistence logic, read directly from the file:
const STORAGE_KEY = 'brndle-theme';
const VALID = ['light', 'dark', 'system'];
function currentState() {
const attr = document.documentElement.getAttribute('data-theme');
return VALID.includes(attr) ? attr : 'system';
}
function nextState(state) {
if (state === 'system') return 'dark';
if (state === 'dark') return 'light';
return 'system';
}
function applyState(state, { announce = true } = {}) {
if (!VALID.includes(state)) state = 'system';
document.documentElement.setAttribute('data-theme', state);
try {
if (state === 'system') {
localStorage.removeItem(STORAGE_KEY);
} else {
localStorage.setItem(STORAGE_KEY, state);
}
} catch (e) {
/* localStorage may be blocked (private mode, quota), ignore */
}
paintButtons(state);
// ...ARIA live-region announcement follows
}
A few things worth pulling out of that, because they’re the parts a generic “it has a toggle” description would skip entirely. First, persistence is asymmetric by design: choosing dark or light writes that literal string to localStorage['brndle-theme'], but choosing system doesn’t write the string "system", it deletes the key. The absence of a stored preference is how “system” is represented, not a stored value of “system.” Second, the whole write path is wrapped in a bare try/catch with no fallback storage, so a visitor in a private-browsing tab or with a full storage quota gets a toggle that still visibly works for the current page view (the attribute still gets set, the icon still updates) but silently reverts to the server-rendered default on the next page load, because nothing persisted. That’s a reasonable, honest tradeoff, failing open rather than throwing an error, but it is a real, confirmed limitation: the toggle’s persistence has no fallback when `localStorage` is unavailable.
Third, and this is the detail most write-ups of a dark-mode toggle skip past: the live prefers-color-scheme listener in this file does not drive the initial page state. It only matters after the page has already loaded, and only when the current state is already system:
if (window.matchMedia) {
const mq = window.matchMedia('(prefers-color-scheme: dark)');
const handler = () => {
if (currentState() === 'system') paintButtons('system');
};
if (mq.addEventListener) mq.addEventListener('change', handler);
else if (mq.addListener) mq.addListener(handler);
}
Its entire job is to keep the toggle button’s icon and ARIA label in sync if a visitor flips their OS theme while the page is still open in system mode, it calls paintButtons, not anything that touches the color tokens. The actual repaint of colors when the OS preference changes is handled by the CSS media query from the previous section, automatically, with no JS in the loop. This JS listener is a UI-polish detail (keep the sun/moon icon honest), not the mechanism that makes dark mode respond to the OS.
How the custom properties actually swap values
This is the part that surprised us most while tracing the source, because it isn’t where you’d expect to find it. The actual dark-mode color values, the specific hex codes for dark surfaces, dark text, the adjusted accent, are not static values sitting in app.css. app.css does contain [data-theme="dark"] selectors, but as we confirmed when we audited Brndle’s 12 color schemes for contrast, every site’s actual palette is derived at runtime from a single admin-picked accent hex, via ColorPalette::generate(). The static rules in app.css are the generic/demo fallback layer; the real per-site values are computed in PHP and injected as an inline <style> block.
That inline block is built by Settings::cssVariables() in app/Settings/Settings.php, and its own docblock states exactly what it produces: a :root {} block for light-mode values, a [data-theme="dark"] {} block for dark-mode values, and a media-wrapped [data-theme="system"] {} block for the same dark values gated behind prefers-color-scheme: dark. Confirmed from the method:
if ($darkModeEnabled) {
$darkStr = self::buildBlock('[data-theme="dark"]', $dark);
$systemStr = '@media(prefers-color-scheme:dark){' . self::buildBlock('[data-theme="system"]', $dark) . '}';
$css = $rootStr . $darkStr . $systemStr;
} else {
$css = $rootStr;
}
Notice the $darkModeEnabled gate just above it: $toggle || $default !== 'light'. If the site owner has disabled the toggle and left the default at light, the dark blocks are never generated at all, no wasted CSS ships for a site that will never use it. That’s a genuinely good, deliberate optimization, and it’s also a useful fact to know while debugging: if you’ve configured a “fixed light” site and dark styles still appear to apply somewhere, the CSS for them shouldn’t exist on that page at all, which narrows the search to a cache or a stale admin setting rather than the generator itself.
This CSS string gets echoed into the page by SettingsServiceProvider::outputCssVariables(), and the method’s own docblock is the single most precise sentence in this whole investigation, worth quoting exactly as written in the source:
/**
* Output CSS custom properties in the document head.
*
* Hooked to wp_head at priority 1. The app.css dark mode fallback
* lives inside @layer theme, so this unlayered output always wins
* regardless of source order (CSS cascade: unlayered > layered).
*
* @return void
*/
public function outputCssVariables(): void
{
$css = Settings::cssVariables();
$css = str_replace(['</style>', '</STYLE>'], '', $css);
echo '<style id="brndle-css-vars">' . $css . '</style>' . "\n";
}
That comment is doing real work, and it answers a question a lot of theme authors get wrong without realizing it: when two CSS rules target the same element with equal specificity, the one that appears later in the document normally wins, which would be a problem here since app.css loads after this inline block (it’s pulled in via @vite further down the <head>). But Tailwind v4’s @theme directive compiles its output inside a CSS cascade layer, and per the CSS Cascading Layers spec, any unlayered style beats any layered style of equal or even lower specificity, independent of source order entirely. Because this inline <style id="brndle-css-vars"> block is plain, unlayered CSS, it wins against Tailwind’s layered tokens even though it’s physically earlier in the document. This is a confirmed, intentional architectural choice documented directly in the code comment, not an inferred side effect, the team building this clearly understood cascade layers and used them on purpose rather than relying on source-order luck.
One more confirmed detail worth flagging precisely: the color-scheme UA hint (which themes a browser’s own scrollbars and form controls) is the one rule that still lives statically in app.css rather than the generated block, because it doesn’t depend on the palette at all, just the mode:
[data-theme="dark"] { color-scheme: dark; }
@media (prefers-color-scheme: dark) {
[data-theme="system"] { color-scheme: dark; }
}
The flash-of-wrong-theme question, confirmed
We went into this expecting to find a gap, because flash-of-wrong-theme is the failure mode most dark-mode implementations get wrong, and the instinct is to assume it until proven otherwise. Having traced every piece involved, the honest finding is that Brndle avoids it, but the mechanism by which it avoids it is split across two completely different techniques, and knowing which one is active in your configuration matters if you’re debugging a report from a real visitor.
The piece that actually prevents the flash for a stored manual preference is a second inline script, separate from the dark-mode.js controller, placed directly in app.blade.php‘s <head>, immediately after <meta charset> and before wp_head() is even called, meaning before the CSS-variables <style> block, before the @vite stylesheet link, before anything that could paint. Confirmed verbatim:
@if ($toggleDriven)
{{-- Toggle mode: applies stored preference synchronously before <body> paints to avoid FOUC. --}}
<script>(function(){try{var s=localStorage.getItem('brndle-theme');if(s==='dark'||s==='light'||s==='system'){document.documentElement.setAttribute('data-theme',s)}}catch(e){}})();</script>
@else
{{-- Fixed mode: admin chose a single theme. Wipe any stale preference a user set before the toggle was disabled. --}}
<script>try{localStorage.removeItem('brndle-theme')}catch(e){}</script>
@endif
Walk the actual sequence for a visitor who previously chose dark on a site whose admin default is light: the server renders <html data-theme="light"> first, matching the admin default, before this script ever runs. Then this blocking inline script executes, synchronously, reads brndle-theme from localStorage, finds "dark", and overwrites the attribute to data-theme="dark", all before wp_head() fires, before the CSS-variables block from the previous section is even echoed, before the stylesheet link exists, before the browser has painted a single pixel. By the time any CSS is parsed against this document, the attribute already says dark, so the dark tokens apply on the very first paint. There’s no intermediate frame where light renders and then gets corrected; the correction happens before rendering starts.
For the system case, there’s nothing to correct in the first place, because, as covered above, that resolution happens via the CSS media query, not a stored value. A first-time visitor on a site configured with a system default gets the correctly-resolved theme on the first paint with no script needed at all.
So, overall verdict, stated plainly: confirmed, flash-of-wrong-theme is actually handled, not just assumed. Both branches of the problem, a stored manual override that disagrees with the server-rendered default, and an unresolved system preference, are covered, by two different and individually appropriate techniques, not one technique stretched to cover both. That’s a better outcome than most dark-mode write-ups find when they actually check.
Two smaller, genuinely real findings worth being honest about rather than smoothing over, because neither showed up until we read the exact logic line by line:
One, a harmless dead branch. The inline FOUC script checks s==='dark'||s==='light'||s==='system', but dark-mode.js‘s own applyState() never writes the literal string "system" to storage, it deletes the key instead, as shown earlier. That means the s==='system' comparison can never evaluate true under the current write logic; nothing currently sets that value. It costs nothing and breaks nothing, but it is dead code, confirmed by reading both files together rather than either one alone.
Two, “system” is opt-in, not ambient. This is the nuance most worth internalizing if you’re configuring a real site: the architecture genuinely supports OS-preference detection, but only when either the admin explicitly sets the site default to system, or a visitor explicitly clicks the toggle into system mode. A site left at the plugin’s own fallback default, light, per Settings::get('dark_mode_default', 'light'), will show a first-time visitor the light theme regardless of their OS setting, every time, until that visitor interacts with the toggle. That’s not a bug; the inline FOUC script’s whole job is reading a stored override, and there’s no override to read on a first visit. It’s simply a fact worth knowing before you tell a client “the site respects visitors’ dark mode preference automatically”, it does, but only once the site (or the visitor) has actually opted into system mode. Out of the box, with the shipped default, it doesn’t.
A testing checklist for your own site’s dark mode
Whether you’re running Brndle or auditing a different theme entirely, these are the specific checks that catch the failure modes this post actually traced, in the order that finds problems fastest.
- Check the view-source HTML, not the rendered DOM. Right-click → View Page Source (not Inspect Element) and confirm the opening
<html>tag already carries a theme attribute or class in the raw response. If it’s missing from the raw HTML and only appears after you inspect the live DOM, your theme state depends on JavaScript running first, which is exactly the setup that flashes. - Throttle to Slow 3G and reload with a stored dark preference. In Chrome DevTools, set Network to “Slow 3G,” set your site to dark via its toggle, then hard-reload. A correct implementation shows dark immediately because the correcting script is tiny and blocking; a broken one shows a visible light flash because the correction waits on a full JS bundle or runs after first paint.
- Open DevTools → Elements and confirm where the FOUC-prevention script sits. It needs to be inline (not an external
<script src>, which adds a network round trip) and it needs to appear before any<link rel="stylesheet">in the<head>, not after. - Toggle to “system,” then flip your OS theme live. macOS: System Settings → Appearance. Windows: Settings → Personalization → Colors. With the page already open and no reload, confirm the colors actually follow the OS change in real time, which proves the media-query binding is live and not just resolved once on load.
- Test in a private/incognito window. This is where blocked or quota-limited
localStorageshows up. Confirm the toggle still visibly works for the current tab even if it can’t persist, and confirm the site falls back to the configured default (not a broken or blank state) on the next load. - Disable JavaScript entirely and reload. The server-rendered default should still apply correctly. If the page renders in the wrong mode or breaks visually with JS off, your initial state depends on a script that should be a PHP/server concern instead.
- Check CSS specificity and cascade-layer order if your theme colors don’t update. If you’re overriding dark-mode tokens via a child theme or custom CSS and the override silently loses, check whether your rule and the theme’s own token rule are in different cascade layers, an unlayered override will always beat a layered one of equal specificity regardless of where it sits in the document, and the reverse is just as true.
- Confirm your “dark mode” claim covers more than body text. Check table borders, code blocks, form inputs, focus rings, and scrollbars specifically, these are the elements most dark-mode implementations forget, because they’re invisible until someone hits them in the actual dark UI rather than the hero section screenshot.
FAQ
Does Brndle use JavaScript to detect my OS’s dark mode preference?
Not for the initial page state. Detecting and responding to prefers-color-scheme for the “system” mode is handled by a CSS @media (prefers-color-scheme: dark) query scoped to [data-theme="system"], which the browser resolves at style-computation time with no script required. A small JavaScript listener in dark-mode.js does watch for live OS theme changes, but only to keep the toggle button’s icon and label in sync, the actual color repaint is CSS, not JS.
Where does Brndle store a visitor’s manual dark/light choice?
localStorage, under the key brndle-theme, storing the literal string "dark" or "light". Choosing “system” doesn’t store the string "system", it deletes the key instead, so the absence of a stored value is how system mode is represented.
Does the dark mode toggle use a class on the body, or something else?
Neither a body class nor a cookie. It’s a data-theme attribute on the <html> element itself, set server-side on first render from the admin’s configured default, then optionally overwritten client-side by a synchronous inline script if a stored preference exists. Every CSS rule and the Tailwind dark: variant both key off that one attribute.
What actually happens if a visitor has localStorage blocked, like in private browsing?
The toggle still works visually for that page view, clicking it still sets the attribute and repaints the icon, because the write to localStorage is wrapped in a try/catch that fails silently. What doesn’t happen is persistence: nothing survives to the next page load, so the visitor lands back on the server-rendered default every time they navigate. There’s no secondary storage fallback (no cookie, no sessionStorage) in the current source.
If I disable the dark mode toggle in settings, does the dark mode CSS still load?
Only if the default mode is also set to something other than light. The exact gate in Settings::cssVariables() is $toggle || $default !== 'light', so a site with the toggle off and the default left at light never generates the dark-mode CSS blocks at all. A site with the toggle off but the default set to dark still gets the dark CSS, it’s just permanently applied with no visitor-facing way to change it.
Where do the actual dark-mode color values come from, are they hardcoded?
No. They’re computed at runtime by ColorPalette::generate() from whichever single accent color the site owner picked (one of the 12 presets, or a custom hex), then injected into the page as an inline <style> block by Settings::cssVariables(). The static [data-theme="dark"] rules inside app.css itself are a generic fallback layer, not the values an actual site uses.
Why doesn’t the inline style block get overridden by app.css, since app.css loads afterward in the page?
Because of CSS cascade layers, not load order. Tailwind v4’s @theme output compiles into a named layer, and per the CSS Cascading Layers specification, any unlayered style always wins against a layered style of equal or lesser specificity, regardless of which one appears later in the document. The inline <style id="brndle-css-vars"> block is plain, unlayered CSS, so it wins even though it’s physically earlier in the <head> than the app.css link that follows it. This is called out explicitly in the PHP docblock, not something we had to infer from testing.
Is there really no flash of the wrong theme at all?
Based on tracing the actual source, no visible flash in either of the two cases that matter: a stored manual override is re-applied by a synchronous, render-blocking inline script before wp_head(), the CSS variables, or the stylesheet link are even reached, and the “system” case needs no correction because it’s resolved natively by a CSS media query rather than a script. The one caveat worth knowing is behavioral, not a flash bug: a first-time visitor’s OS dark preference is only honored if the site’s default is explicitly set to system (or the visitor has already opted into system mode), the shipped fallback default is light, not an automatic OS read.
Can I move the toggle into the header instead of a floating button?
Yes, the dark_mode_toggle_position setting accepts header in addition to bottom-left and bottom-right, and app.blade.php checks that value to decide whether to render the floating partial at all (($darkModeTogglePosition ?? '') !== 'header'), implying the header-integrated version is rendered from within the header template itself rather than the floating button partial.
Does this architecture depend on any dark-mode-specific npm package or framework plugin?
No. dark-mode.js is plain, dependency-free JavaScript with no framework and no third-party dark-mode library. The only build-time involvement is Tailwind v4’s own @custom-variant directive, which is a first-party Tailwind feature, not a separate plugin.
The takeaway
A dark mode feature bullet hides exactly the decisions that determine whether it actually works: where the first value comes from, what runs before paint, and how a manual choice survives the gap between one page load and the next. Traced end to end, Brndle’s real answer is a server-rendered attribute as the single source of truth, a CSS media query doing the system-preference work that most implementations hand to JavaScript, a small and honestly-scoped toggle controller that fails open rather than silently breaking when storage is blocked, and a cascade-layer trick, documented in the code itself, not reverse-engineered by us, that lets a runtime-generated palette beat a static stylesheet without caring about load order. The flash-of-wrong-theme question, the one that actually separates a real implementation from a demo, checks out: both the stored-override case and the system-preference case are genuinely handled, by two different and individually correct mechanisms. The one honest asterisk is that “respects system preference” means the site or the visitor has to opt into system mode first; it isn’t an ambient read of your OS on every first visit. That’s a smaller gap than most audits of a dark-mode claim turn up, and it’s worth saying so plainly rather than inventing a bigger problem to make the post feel more thorough than the source actually supports.
Sources, read on 30 October 2026
- brndle-themes/brndle: resources/js/dark-mode.js, the toggle controller: state cycle, localStorage persistence, matchMedia listener
- brndle-themes/brndle: resources/views/layouts/app.blade.php, the server-rendered
data-themeattribute and the FOUC-prevention inline scripts - brndle-themes/brndle: dark-mode-toggle.blade.php, the toggle button partial
- brndle-themes/brndle: app/View/Composers/Theme.php, the settings-to-view composer
- brndle-themes/brndle: app/Settings/Settings.php,
cssVariables()and the dark-mode-enabled gate - brndle-themes/brndle: app/Providers/SettingsServiceProvider.php,
outputCssVariables()and the cascade-layer docblock - brndle-themes/brndle: resources/css/app.css, the
@custom-variant darkrebinding and the static fallback tokens - W3C: CSS Cascading and Inheritance Level 5, Cascade Layers
- Why Your Dark Mode Flashes Before JavaScript Runs (and How to Fix It), the general pattern this post traces against Brndle’s specific implementation
- Auditing All 12 Brndle Color Schemes for WCAG AA Contrast, where the per-scheme
ColorPalette::generate()values referenced here are audited in full