Colors
@vyui/kit ships six configurable semantic colors — primary, secondary, success, info, warning, error — plus neutral, which is always present but kept out of the configurable list (mirrors nuxt/ui, which excludes neutral from theme.colors too). Source of truth: packages/kit/src/theme/color-constants.js.
Default semantic → Tailwind-palette mapping:
| Semantic | Palette |
|---|---|
primary | green |
secondary | blue |
success | emerald |
info | sky |
warning | amber |
error | red |
neutral | slate |
Not the obvious 1:1 (success isn't stock green, info isn't stock blue): Tailwind's base yellow / blue / green read too washed out at shade-500 for contrast on white, so amber / sky / emerald stand in.
Three CSS-variable tiers
packages/kit/src/style.css defines every color as three tiers of custom property. This is the mental model to keep in your head when overriding or reading theme files:
- Palette —
--ui-color-{semantic}-{50..950}, the full Tailwind shade scale per semantic color (e.g.--ui-color-primary-500: theme('colors.green.500')). Powers ramp utilities:bg-primary-500,border-error-200. - Mode —
--ui-{semantic}(e.g.--ui-primary), a single active shade per color mode (-500in light,-400in dark). Powers shorthand utilities:bg-primary,text-error. - Semantic / surface — role-based tokens about UI role, not a specific color, that flip entirely between light and dark:
text-dimmed/muted/toned/default/highlighted/inverted,bg-default/muted/elevated/accented/inverted,border-default/muted/accented/inverted. Component themes use these for text, surfaces, and borders instead of rawneutral-*classes, so components adapt to dark mode automatically with zerodark:variants.
One nuance that trips people up: the palette tier's neutral ramp does not invert. bg-neutral-100 is the literal same slate shade in light or dark mode — picking a raw ramp class is a "you picked a literal shade" contract. Only the semantic/surface tier flips. Reach for bg-muted, not bg-neutral-100, for anything that should adapt to dark mode.
Configuring colors
Two ways to customize, depending on how far you need to go.
Rebrand via CSS only — override the --ui-color-{semantic}-{shade} variables in your own stylesheet, after importing @vyui/kit/style.css. No build step, no config file:
@import '@vyui/kit/style.css';
:root {
--ui-color-primary-500: #6366f1;
--ui-color-primary-600: #4f46e5;
}
This only rebrands a color already in the configurable set — it doesn't add a new slot.
Add a new color slot (e.g. tertiary) — the Tailwind preset has to generate the classes and the runtime has to select the same set, so author the color list once with defineVyuiConfig (from the light @vyui/kit/config entry — safe to import in a Tailwind config, it never pulls component code) and feed it to both planes:
import { defineVyuiConfig } from '@vyui/kit/config'
import { COLORS } from '@vyui/kit/tailwind'
export default defineVyuiConfig({
theme: { colors: [...COLORS, 'tertiary'] },
})
import vyuiConfig from './vyui.config'
import { createVyuiPreset } from '@vyui/kit/tailwind'
export default {
presets: [lynxPreset, createVyuiPreset(vyuiConfig)],
}
import vyuiConfig from './vyui.config'
import { provideVyUI } from '@vyui/kit' // or: app.use(VyUI, vyuiConfig) on web
provideVyUI(app, vyuiConfig)
createVyuiPreset dev-warns if a listed color has no backing --ui-color-* var, so a "class resolves to nothing" mistake surfaces at build time instead of on device. To also type-check tertiary on every component's color prop:
declare module '@vyui/kit' {
interface VyuiColorRegistry { tertiary: true }
}
Then add the --ui-color-tertiary-{50..950} CSS-var block — generate the .d.ts and the CSS together with:
node node_modules/@vyui/kit/scripts/gen-colors.mjs --colors tertiary=indigo
See the @vyui/kit README for the full flow, or Installation for how defineVyuiConfig fits alongside the rest of the plugin setup.