Clidey UXDocs

30 palettes, one application

A palette is a coordinated set of decisions, rather than a primary color. UX packages surfaces, contrast, typography, shape, and motion together so an entire interface can change direction coherently.

The same components across Paper and Ink, Midnight Noir, and Forest MossThe same components across Paper and Ink, Midnight Noir, and Forest Moss

These captures use the same workspace and data. Only the palette and color mode change.

Try a palette#

Paper & Inkdark mode

Your product, in character.

The same controls and state follow the selected palette.

0 steps forward

Preview tokens are scoped to this example; the documentation theme stays yours.

Choose a default#

TSX

import { ThemeProvider } from '@clidey/ux';
import '@clidey/ux/styles.css';
import '@clidey/ux/theme-fonts.css';

<ThemeProvider defaultPalette="forest-moss" defaultTheme="dark">
  <App />
</ThemeProvider>

defaultPalette is used when there is no valid saved palette. defaultTheme similarly supplies the initial mode. Existing choices in local storage take precedence.

Let the user choose#

TSX

import {
  Select, SelectTrigger, SelectValue, SelectContent, SelectItem,
  themes, useTheme,
} from '@clidey/ux';

export function PalettePicker() {
  const { palette, setPalette } = useTheme();
  return (
    <Select value={palette ?? 'default'}
      onValueChange={value => setPalette(value === 'default' ? null : value)}>
      <SelectTrigger aria-label="Color palette"><SelectValue /></SelectTrigger>
      <SelectContent>
        <SelectItem value="default">Default tokens</SelectItem>
        {themes.map(theme => (
          <SelectItem key={theme.id} value={theme.id}>{theme.name}</SelectItem>
        ))}
      </SelectContent>
    </Select>
  );
}

Keep this picker inside ThemeProvider. Mode and palette have separate persistence keys, so someone can prefer Forest Moss and still follow their operating system's light/dark setting.

The catalog#

Palette IDName
paper-inkPaper & Ink
midnight-noirMidnight Noir
arctic-steelArctic Steel
obsidian-vaultObsidian Vault
nordic-frostNordic Frost
ice-crystalIce Crystal
newspaper-gothicNewspaper Gothic
tokyo-zenTokyo Zen
velvet-roseVelvet Rose
desert-sandstoneDesert Sandstone
cyber-matrixCyber Matrix
hologram-hudHologram HUD
retro-amberRetro Amber
midnight-raidMidnight Raid
berlin-technoBerlin Techno
brutalist-concreteBrutalist Concrete
neon-tokyoNeon Tokyo
vapor-synthVapor Synth
candy-popCandy Pop
seoul-neonSeoul Neon
sao-paulo-carnivalSão Paulo Carnival
aurora-glassAurora Glass
deep-oceanDeep Ocean
sydney-surfSydney Surf
forest-mossForest Moss
ember-forgeEmber Forge
mars-colonyMars Colony
solar-goldSolar Gold
mumbai-spiceMumbai Spice
sahara-duskSahara Dusk

Inspect the definition#

TSX

import { getThemeById } from '@clidey/ux';

const palette = getThemeById('paper-ink');
console.log(palette?.typography, palette?.motion, palette?.surface);

ThemeDefinition includes id, name, description, personality, css, className, dataAttr, typography, motion, and surface. Read metadata from the catalog instead of keeping a second list in your application.

Outside React#

CSS

@import '@clidey/ux/theme-palettes.css';
@import '@clidey/ux/theme-fonts.css';

JavaScript

const root = document.documentElement;
root.classList.add('theme-paper-ink', 'dark');
root.dataset.theme = 'paper-ink';

Remove the previous palette class when switching. These assets share the React catalog's semantic tokens; they do not provide React components or a preference persistence layer.

Make the change meaningful#

Use semantic classes such as bg-background, text-foreground, and border-border in product layouts. A hardcoded white card or gray text will not follow the palette. Test the actual content in both modes: a palette does not replace contrast, focus, chart-legibility, or reduced-motion checks.

Was this page helpful?