Theming

FrontAlign's theming system is built around a single idea: the theme lives in one attribute on the document root, and everything else reacts to it. There is no theme context, no provider tree, and no runtime CSS generation — just a fa-theme attribute on document.documentElement and CSS that reads it.

This keeps theming framework-agnostic at the core. The JavaScript layer is only responsible for deciding what the attribute should be and persisting that decision — the actual visual change is handled entirely by CSS.

The Model

LayerResponsibility
CSSDefines what each theme looks like, scoped to the fa-theme attribute selector.
Runtime (JS)Decides the active theme, writes the attribute, persists the choice, and notifies listeners.
Root elementdocument.documentElement — the single source of truth for the current theme.

Because the attribute lives on <html>, the theme is available before hydration, doesn't flicker on route changes in SPAs, and can be read by any script or stylesheet without going through JavaScript state.

Why a Data Attribute

Most theming approaches either swap a class on <body> or rely on a CSS custom property toggled via JavaScript on every render. FrontAlign uses an attribute instead for a few reasons:

  • One write, no diffing. Setting or removing fa-theme="dark" is a single DOM operation — there's no class list to reconcile.
  • CSS-native. [fa-theme="dark"] { ... } is a plain attribute selector; no preprocessor or build step is required to consume it.
  • Framework-agnostic. The attribute doesn't care whether it was set by vanilla JS, a React hook, or inline a <script> tag in <head> — any of them can drive the same CSS.

What Ships in This Category

PageCovers
Dark Mode

The DarkMode manager — toggle creation, system preference sync, localStorage persistence, onChange callbacks, and the darkModeChange event.

React Integration

The useDarkMode hook and provider setup for React and Next.js applications, including SSR-safe initialization.

Shared Behavior Across Theming

A few rules apply consistently across every piece of the theming system, regardless of which component sets the attribute:

  • No saved preference, no opinion. Until a user makes an explicit choice, the system preference (prefers-color-scheme) wins.
  • Manual choice always wins. Once a user toggles a theme, that choice is persisted and takes priority over system changes from that point on.
  • SSR-safe by default. Runtime code checks for window and document before touching them, so nothing breaks during server rendering.
  • Cleanup is explicit. Anything that attaches listeners or creates DOM nodes exposes a dispose() method — important in SPAs and React, where components mount and unmount repeatedly.

This alone restores the saved or system-preferred theme, creates a toggle button, and wires up persistence. Everything past this point — custom buttons, custom containers, callbacks, the React hook — is a refinement of this same default.

FrontAlign