GitHub ↗

theming

Three orthogonal axes — color scheme, accent, and design language — driven by CSS attributes.


Fluixi UI themes through three independent attributes on any element (usually <html>), so they compose freely — dark + violet + Material all at once:

Axis Attribute Values
Color scheme data-theme light · dark · high-contrast
Accent data-accent blue (default) · violet · rose · green
Design language data-ui fluixi (default) · material

Every component reads semantic CSS variables (--flx-ui-color-primary, …), so changing an attribute re-themes the whole tree with no per-component work.

Setup

Inject the token variables once, and import the default skin:

import { injectTokens } from '@fluixi-ui/tokens/theme';
import '@fluixi-ui/tokens/preset.css';

injectTokens();

Driving the axes

Each axis is just an attribute. Drive them reactively with createTheme from @fluixi/core — one store per axis. It detects, persists, applies, follows system preference, and ships a no-flash head script.

import { createTheme } from '@fluixi/core';

const scheme = createTheme({
  attribute: 'data-theme',
  themes: ['light', 'dark', 'high-contrast'],
  default: 'light',
  system: { dark: 'dark', light: 'light' }, // follow prefers-color-scheme
});

const accent = createTheme({ attribute: 'data-accent', themes: ['blue', 'violet', 'rose', 'green'], default: 'blue' });

scheme.init();   // apply on client mount
accent.init();

scheme.toggle(); // cycle through the theme list
accent.set('violet');

@fluixi-ui/tokens also offers a scheme-only createThemeController (light/dark/ high-contrast + system) if you only need the color scheme.

Accent palettes

data-accent swaps only the accent scale — blue is the built-in default; violet, rose, and green are alternates (Radix violet / crimson / grass). Every color-primary / focus / selection token derives from the accent, so a single attribute recolors the whole UI. Add your own by extending the token stylesheet with a [data-accent='…'] block that overrides --flx-ui-accent-1…12.

Design language (skins)

data-ui switches the visual language. fluixi is the default clean skin; @fluixi-ui/skin-material is a Material Design skin. Load a skin's CSS only when it's active with createTheme's load hook:

const skin = createTheme({
  attribute: 'data-ui',
  themes: ['fluixi', 'material'],
  default: 'fluixi',
  load: (s) => s === 'material' && import('@fluixi-ui/skin-material/styles.css'),
});
skin.init();

No-flash SSR

When server-rendering, drop the head script so the attribute is set before first paint (no theme flash before hydration):

<head>{/* … */}
  <script innerHTML={scheme.headScript()} />
</head>

Scoped theming

The attributes work on any element, not just the root — so a subtree can carry its own scheme or accent. The semantic layer is re-emitted in every scope, so nested regions re-resolve correctly:

<section data-theme="dark" data-accent="rose">
  <!-- dark + rose here, regardless of the page theme -->
</section>

Overriding tokens

The token variables and the default skin live in CSS @layers (flx.tokens, flx.components, flx.skin, in that order). Your CSS stays unlayered, so it always wins — override a token anywhere:

:root { --flx-ui-color-primary: #7c3aed; }

Part of the Fluixi UI component library. Made with ☕ by the Fluixi team.