Tokens and Theming
Batoi UIF themes should be driven by explicit tokens instead of one-off visual overrides.
The Theme Builder example demonstrates how a client-side tool can adjust tokens and export application-ready CSS.
Token Pipeline
Core Tokens
| Token | Purpose |
|---|---|
--uif-brand | Primary brand/action color |
--uif-accent | Supporting accent color |
--uif-neutral | Main text and structural color |
--uif-surface | Primary surface color |
--uif-radius | Control and card radius |
--uif-density | Spacing density |
--uif-font | Font stack |
UIF 2.2.0 also provides semantic typography weights, line heights, tracking, readable content measures, responsive title sizes, and text utilities. The default stack uses system fallbacks and does not require a webfont download.
Use helpers such as uif-page-title, uif-section-title, uif-component-title, uif-label, uif-caption, and uif-prose to preserve hierarchy. Size, weight, line-height, alignment, muted-text, and semantic-color utilities are available for smaller adjustments.
Example CSS
:root {
--uif-brand: #0f70b7;
--uif-accent: #00a999;
--uif-neutral: #1e293b;
--uif-surface: #ffffff;
--uif-radius: 8px;
--uif-density: 1rem;
--uif-font: Inter, Arial, sans-serif;
}Guidance
- Keep token names stable.
- Avoid per-page color overrides where tokens are enough.
- Preview themes against forms, cards, tables, and primary actions.
- Export CSS and JSON when a theme needs to move between applications.
Implement Light and Dark Modes
Define both modes with the same semantic token names. Components should consume surface, text, border, brand, and status tokens without testing the current mode themselves.
:root,
[data-theme="light"] {
color-scheme: light;
--uif-surface: #ffffff;
--uif-surface-elevated: #f8fafc;
--uif-text: #1e293b;
--uif-text-muted: #64748b;
--uif-border: #cbd5e1;
}
[data-theme="dark"] {
color-scheme: dark;
--uif-surface: #101a2a;
--uif-surface-elevated: #172235;
--uif-text: #f1f5f9;
--uif-text-muted: #b6c2d1;
--uif-border: #3b4a60;
}Use an explicit user choice when one exists and otherwise follow the operating-system preference. Persist only the mode name, not a complete token payload.
const root = document.documentElement;
const stored = localStorage.getItem('theme');
const preferred = matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
function applyTheme(theme) {
const next = theme === 'dark' ? 'dark' : 'light';
root.dataset.theme = next;
root.style.colorScheme = next;
}
applyTheme(stored || preferred);
document.querySelector('[data-theme-toggle]')?.addEventListener('click', () => {
const next = root.dataset.theme === 'dark' ? 'light' : 'dark';
localStorage.setItem('theme', next);
applyTheme(next);
});Place the initial mode script in the document head when possible so the correct surface is applied before first paint. A toggle must have an accessible name and expose its current state. Verify text, links, focus indicators, controls, disabled states, alerts, tables, charts, images, and code blocks in both modes. Do not invert photographs or logos with a blanket CSS filter.
The UIF 2.3.0 examples follow this pattern: the selected mode is persisted, applied before the example renders, and paired with the appropriate light or dark UIF logo asset.