Core

Theming · Light, Dark & Custom

Themes are driven entirely by design tokens. Switch with a single data-theme attribute on <html>, toggle from JS via ng.theme, or define your own brand theme by overriding --ng-* variables. No duplicate CSS.

1 · The data-theme attribute

Light is the default; dark is built in. Set the theme declaratively on the root element — every --ng-* token adapts, no extra stylesheet.

index.html
<!-- on the <html> tag -->
<html data-theme="dark"> … </html>

2 · Switch from JavaScript — ng.theme

The ng_theme.js module exposes a small API on window.ng.theme. set() persists the choice; apply() only previews it.

theme.js
// window.ng.theme (requires ng_theme.js)
ng.theme.set('dark');      // set + persist  (null = auto)
ng.theme.get();            // 'auto' | 'light' | 'dark' | '<name>'
ng.theme.toggle();         // light <-> dark
ng.theme.apply('light');   // preview only, no persist

Wire it to any control — e.g. a toggle button:

index.html
<button onclick="ng.theme.toggle()">Toggle theme</button>

3 · React to changes — ng:theme:change

Every change dispatches ng:theme:change on <html>, with the new value in detail.theme.

theme.js
document.documentElement.addEventListener('ng:theme:change', (e) => {
    console.log(e.detail.theme);   // 'light' | 'dark' | '<name>' | null
});

4 · Persistence (no flash)

The choice is stored in localStorage under ng-theme and restored automatically. To avoid a flash of the wrong theme on load, set the attribute before the stylesheet, in the <head>:

index.html
<!-- in <head>, BEFORE the stylesheet — prevents the flash -->
<script>try{document.documentElement.setAttribute('data-theme',
localStorage.getItem('ng-theme')||'light');}catch(e){}</script>

5 · Custom theme (your brand)

A custom theme is just a named data-theme that overrides the design tokens you care about. Define it in CSS, then activate it by name.

custom.css
/* a named custom theme = override design tokens */
[data-theme="ocean"] {
    --ng-primary-bg: #0e7490;
    --ng-primary-hover: #0c6378;
    --ng-surface: #0b1f24;
    /* …only the --ng-* tokens you need */
}
theme.js
ng.theme.set('ocean');   // activate the custom theme

Override only the tokens you need (brand colors, surfaces). Everything else inherits the framework defaults. Interactive theme switching requires ng_theme.js in your foot.

6 · Font — bring your own

Nexigrid ships font-free: the default typeface is the native system stack (system-ui → San Francisco / Segoe UI / Roboto) exposed as --ng-font-base — zero download, no layout shift. To use your own font, load it and put it first in the base stack:

font.css
/* Nexigrid is FONT-FREE: default = native system stack, via --ng-font-base:
   system-ui, -apple-system, "Segoe UI", Roboto, sans-serif  (0 download, 0 CLS). */

/* Want your own font (e.g. Inter)? 1) load it, 2) put it first in the stack: */
@font-face {
    font-family: 'Inter';
    src: url('/fonts/inter.woff2') format('woff2');
    font-display: swap;
}
:root {
    --ng-font-base: 'Inter', system-ui, -apple-system, sans-serif;
}

One override on --ng-font-base cascades to headings and body everywhere. (This site itself runs on the system stack.)