FOUC Prevention
Prevent flash of unstyled content with a tiny inline script that runs before first paint.
The Problem
Without FOUC prevention, users see a brief flash of the wrong theme. The page loads with the default (usually light) theme, then JavaScript executes and switches to dark. This flash is jarring and makes the app feel broken.
The fix is a synchronous inline script in the <head> that runs before the browser paints. It reads the stored preference and applies it immediately.
The Script
Under 500 bytes. Add it as an inline script in your <head>:
(function() {
var d = document.documentElement;
var s = 'nightfall-theme';
var a = 'data-theme';
try {
var t = localStorage.getItem(s);
if (t === 'dark' || t === 'light') {
d.setAttribute(a, t);
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
d.setAttribute(a, 'dark');
}
} catch (e) {}
})();Next.js Integration
In Next.js, use dangerouslySetInnerHTML to inline the script:
import { nightfallScript } from 'nightfall-css/script'
export default function RootLayout({ children }) {
return (
<html lang="en" suppressHydrationWarning>
<head>
<script dangerouslySetInnerHTML={{ __html: nightfallScript }} />
</head>
<body>{children}</body>
</html>
)
}The suppressHydrationWarning on the <html> element prevents React from warning about the attribute mismatch between server and client.
Plain HTML
For non-framework projects, paste the script directly:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>My App</title>
<script>
(function() {
var d = document.documentElement;
var t = localStorage.getItem('nightfall-theme');
if (t === 'dark' || t === 'light') {
d.setAttribute('data-theme', t);
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
d.setAttribute('data-theme', 'dark');
}
})();
</script>
<link rel="stylesheet" href="nightfall-theme.css" />
</head>
<body>
<!-- Your content -->
</body>
</html>How It Works
- Inline in head — The script is inline (not an external file), so it executes synchronously before the browser paints.
- Check localStorage — Reads the stored theme preference. If the user previously chose dark mode, it applies immediately.
- Fall back to system preference — If no stored preference exists, checks
prefers-color-scheme: dark. - Set the attribute — Applies
data-theme="dark"on the<html>element before the CSS is evaluated, so the correct theme variables are used from the very first paint. - Error handling — The
try/catchhandles cases where localStorage is disabled (private browsing, iframes).
Custom Storage Key
If you use a custom storageKey in the provider, update the FOUC script to match:
import { createFoucScript } from 'nightfall-css/script'
// Generate a FOUC script with custom options
const script = createFoucScript({
storageKey: 'my-app-theme',
attribute: 'data-color-mode',
defaultDark: false, // Don't default to dark even if system prefers it
})