Ready-made palettes, and letting users switch between them.
A theme is a full set of the tokens on the Colors page, plus --radius, in one stylesheet. Import it and every component follows, because components only read the tokens.
20 palettes ship with the library, in two families. Each one defines both colour schemes, so a theme and dark mode are independent choices rather than a grid you have to maintain.
Import a palette after xiod-ui/styles. It overrides the base tokens outright, so the whole app is that palette and nothing in your markup changes.
1@import "tailwindcss";2@import "xiod-ui/styles";34/* One line. Every token from the Colors page is redefined, light and dark. */5@import "xiod-ui/themes/cobalt";Order matters: the import has to come after xiod-ui/styles, or the base tokens win. You can still override individual tokens below the import — the palette is a starting point, not a lock.
To let your own users pick, import the /scoped variant of each palette instead. A scoped stylesheet only applies under [data-palette="name"], so several can coexist without fighting over the base tokens.
1@import "tailwindcss";2@import "xiod-ui/styles";34/* Scoped: each palette applies only under its own data-palette attribute,5 so importing several is safe — none of them touch the base tokens. */6@import "xiod-ui/themes/cobalt/scoped";7@import "xiod-ui/themes/lagoon/scoped";8@import "xiod-ui/themes/riso/scoped";Then switch palettes with setPalette from useTheme. It writes the attribute to <html>, remembers the choice in localStorage and suppresses transitions during the swap, the same way the light/dark toggle does. A returning visitor's palette applies before the page first paints, like their light or dark choice, so it never flashes the default palette first. This needs ThemeProvider in your root layout — see the Dark Mode page for that setup.
1"use client";23import { Button } from "xiod-ui/button";4import { useTheme } from "xiod-ui/theme-provider";56const PALETTES = [7 { value: "cobalt", label: "Cobalt" },8 { value: "lagoon", label: "Lagoon" },9 { value: undefined, label: "Default" },10];1112export function PaletteSwitcher() {13 const { palette, setPalette } = useTheme();1415 return (16 <div className="flex gap-2">17 {PALETTES.map(({ value, label }) => (18 <Button19 key={label}20 variant={palette === value ? "default" : "outline"}21 onClick={() => setPalette(value)}22 >23 {label}24 </Button>25 ))}26 </div>27 );28}Import only the palettes you actually offer. Each scoped stylesheet is about 1KB gzipped, but they all ship on every page.
Every card below is painted by that palette's real tokens, in whichever colour scheme you are currently reading in. Press Try it to apply one to this entire site.
Restrained enough to ship as-is.
Neutral
The base theme, shipped with the library.
No stylesheet — this is the base token set.
—Clay
Warm cream and terracotta.
xiod-ui/themes/claySepia
Aged paper and tanned leather.
xiod-ui/themes/sepiaSage
Muted garden green.
xiod-ui/themes/sageLagoon
Deep teal on cool mineral.
xiod-ui/themes/lagoonCobalt
Deep cobalt blue on cool grey.
xiod-ui/themes/cobaltIris
Indigo and violet on soft lilac.
xiod-ui/themes/irisRoast
Dark coffee and steamed cream.
xiod-ui/themes/roastCrimson
Oxblood, olive and steel on gunmetal.
xiod-ui/themes/crimsonSorbet
Rose, sky and citrus.
xiod-ui/themes/sorbetLouder, with a point of view.
Cel
Sky blue and marigold, flat and high-key.
xiod-ui/themes/celAfterglow
Sunset warmth by day, cerulean by night.
xiod-ui/themes/afterglowIon
Violet and readout green on instrument grey.
xiod-ui/themes/ionCathode
Hot magenta and phosphor green on cold concrete.
xiod-ui/themes/cathodeRiso
Magenta and cyan misprint on newsprint.
xiod-ui/themes/risoTaffy
Watermelon, sky and lemon.
xiod-ui/themes/taffyMochi
Milky orchid, mint and blush.
xiod-ui/themes/mochiBlacklight
Ultraviolet, acid green and hot pink.
xiod-ui/themes/blacklightToxin
Biohazard green and blood on wet concrete.
xiod-ui/themes/toxinCinder
Molten orange on cold ash.
xiod-ui/themes/cinderBoth families are held to the same bar. The difference is how much personality a palette imposes, not how carefully it was built.
*-foreground token clears 4.5:1 against the surface it actually sits on — including the state foregrounds, which are checked against their own colour tinted over the background at the opacity components really use.destructive fill, and that fill clears 3:1 against the background, so a destructive button is legible as a control and not only as text.The library's build checks all three, so a palette that fails one can't ship.
A palette is only a stylesheet of tokens, so you can start from the closest one and override the tokens that differ below its import. Customization shows how, with the full token set as a template.