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
| Layer | Responsibility |
|---|---|
| CSS | Defines 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 element | document.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
| Page | Covers |
|---|---|
| Dark Mode | The |
| React Integration | The |
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
windowanddocumentbefore 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.