//
Twenty ready-made palettes, how to apply one, and how to let your own users choose.
--radius, written as one stylesheet. There is no config file and no build step — you import a stylesheet and every component follows, because components only ever 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.
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.
/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 write the attribute with setPalette from useTheme. It remembers the choice in localStorage and suppresses transitions during the swap, the same way the light/dark toggle does. This needs ThemeProvider in your root layout — see the Dark Mode page for that setup.
1"use client";23import { useTheme } from "xiod-ui/theme-provider";45export function PaletteSwitcher() {6 const { palette, setPalette } = useTheme();78 return (9 <div className="flex gap-2">10 <button onClick={() => setPalette("cobalt")}>Cobalt</button>11 <button onClick={() => setPalette("lagoon")}>Lagoon</button>1213 {/* undefined clears the attribute and falls back to the base tokens */}14 <button onClick={() => setPalette(undefined)}>Neutral</button>15 </div>16 );17}Import only the palettes you actually offer. Each scoped stylesheet is a few hundred bytes gzipped, but they all ship on every page.
Neutral
The base theme, shipped with the library.
—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/sorbetCel
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/cinder*-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.All three are enforced by a check that runs on every build, so a palette that fails any of them cannot ship.
1@import "xiod-ui/styles";2@import "xiod-ui/themes/cobalt";34/* Cobalt, but squarer and with a deeper blue. */5:root {6 --radius: 0.25rem;7 --primary: #1d4ed8;8}The Customization page covers this properly — the full token set as a starter template, how to scope an override to one part of the app, and the mistakes that make an override silently do nothing.