Skip to main content

Dark Mode and Theming

as-folio ships with a polished dark mode implementation that matches al-folio’s visual identity. The toggle is in the navbar (☀/☽ icon). Preference is persisted to localStorage and restored before the page renders to avoid any flash of unstyled content.

How it works

1. FOUC prevention

An inline <script> in <head> — before any stylesheet — reads localStorage and sets data-theme="dark" on <html> before the browser paints:

// Runs synchronously before first paint
const theme = localStorage.getItem('theme') ?? 'light';
document.documentElement.setAttribute('data-theme', theme);

Because this script is is:inline in Astro, it is never deferred or bundled — it runs at the earliest possible moment.

2. CSS custom properties

All colours live in src/styles/_colors.css as CSS custom properties, mirroring al-folio’s variable names exactly:

:root {
  --global-bg-color: #ffffff;
  --global-text-color: #000000;
  --global-theme-color: #b509ac;
  /* ... */
}

[data-theme='dark'] {
  --global-bg-color: #1c1c1c;
  --global-text-color: #ffffff;
  --global-theme-color: #b509ac; /* same accent in dark */
  /* ... */
}

Every component references these variables rather than hard-coded colours, so toggling data-theme instantly re-paints the whole page without a single JavaScript re-render.

3. The toggle component

ThemeToggle.astro writes to localStorage and updates data-theme:

<script>
  const toggle = document.getElementById('theme-toggle');
  toggle?.addEventListener('click', () => {
    const current = document.documentElement.getAttribute('data-theme');
    const next = current === 'dark' ? 'light' : 'dark';
    document.documentElement.setAttribute('data-theme', next);
    localStorage.setItem('theme', next);
  });
</script>

4. Giscus comment theme sync

When comments are enabled, Giscus.astro observes data-theme changes with a MutationObserver and posts a message to the Giscus <iframe> to switch its own theme:

new MutationObserver(() => {
  const theme = document.documentElement.getAttribute('data-theme');
  giscusFrame.contentWindow.postMessage(
    { giscus: { setConfig: { theme: theme === 'dark' ? 'dark' : 'light' } } },
    'https://giscus.app',
  );
}).observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] });

Customising the accent colour

The theme colour is set in src/config/site.ts as a comment/reference, but the actual CSS variables are in src/styles/_colors.css. To change the accent, update --global-theme-color (and optionally --global-hover-color) in both :root and [data-theme="dark"]:

:root {
  --global-theme-color: #0070f3; /* Vercel blue */
  --global-hover-color: #005cc5;
}

That’s all — every badge, link highlight, tag pill, and interactive element picks it up automatically.