# Vega UI > Composable, accessible React components on top of real dialogs, popovers and inputs. Themed with CSS tokens, in light and dark. --- # Getting started Install the packages, connect the styles and the theme, and render your first component. ## Requirements - `react` and `react-dom` `^19.0.0` - a bundler that loads CSS imported from packages, such as Vite, webpack or Next.js ## Install ```bash pnpm add @vega-ui/react @vega-ui/theme-core @vega-ui/icons ``` - `@vega-ui/react`: the components and their stylesheet. - `@vega-ui/theme-core`: the light and dark themes. Components read their colors, surfaces and text from its variables, so without it they render unstyled. - `@vega-ui/icons`: the icons used across the examples. `@vega-ui/react` already depends on it, but pnpm and Yarn Plug'n'Play only let the app import packages it lists itself. Install `@vega-ui/i18n` as well when the app uses calendar systems or the date helpers directly. See [Dates and calendar systems](http://vega-ui.dev/docs/i18n/#install). ## Connect Styles Components do not import their CSS. Import the theme and the stylesheet once, at the app entry point, theme first: ```tsx import '@vega-ui/theme-core'; import '@vega-ui/react/style.css'; ``` With CSS layers, declare the order first and import the library into its own layers, so app styles win regardless of specificity: ```css @layer theme, library, app; @import url('@vega-ui/theme-core') layer(theme); @import url('@vega-ui/react/style.css') layer(library); ``` Styles outside any layer win over every layer, so app styles can also stay unlayered. ## Apply A Theme The theme variables are defined on the `.light` and `.dark` classes. Put the class on the root element: ```html ``` Keep it on `` or ``, not on an inner wrapper. `Dialog`, `Drawer` and `Sheet` render into `document.body`, outside the app tree, and only pick up a theme set on an ancestor of `body`. To switch the theme, replace the class. Custom themes are described in [Themes](http://vega-ui.dev/docs/styling/themes/). ## Fonts The default font stack is `Montserrat, Arial, sans-serif`. The library does not load Montserrat: without it in the app, text falls back to Arial. Load Montserrat in the app, or set your own stack: ```css :root { --default-font-family: Inter, system-ui, sans-serif; } ``` Headings follow `--default-font-family`. Code uses `--code-font-family`. ## First Component ```tsx import { Button } from '@vega-ui/react'; export function App() { return ; } ``` For Next.js and server rendering, see [Next.js and SSR](http://vega-ui.dev/docs/nextjs/). ## Common Mistakes - Only `@vega-ui/react/style.css` is imported: without `@vega-ui/theme-core` the variables are undefined. - The theme class is on an inner wrapper: `Dialog`, `Drawer` and `Sheet` open without a theme. - Montserrat is not loaded and the font stack is not replaced: text renders in Arial. - `@vega-ui/icons` is imported but not installed: the build fails to resolve it. ## Working On The Library ```bash pnpm install pnpm storybook pnpm test pnpm docs:validate ``` - `pnpm storybook`: the component playground. - `pnpm test`: the tests of every package; component tests run in Chromium, Firefox and WebKit. - `pnpm --filter @vega-ui/react test`: the component tests only. - `pnpm docs:validate`: checks the structure of the documentation. --- # Styling This section documents the global styling model behind VegaUI. Read it before the Styling page of a component. ## Scope - [Tokens](http://vega-ui.dev/docs/styling/tokens/): global design tokens from `@vega-ui/tokens-core` - [Themes](http://vega-ui.dev/docs/styling/themes/): light and dark theme layers from `@vega-ui/theme-core` - [CSS variables](http://vega-ui.dev/docs/styling/css-variables/): how global and component variables are composed in real apps ## Source Of Truth - `packages/tokens/src/*.css`: raw token definitions - `packages/theme/src/*.css`: semantic theme layers - `packages/ui/src/**/style.module.css`: component-level override points ## Mental Model Use the styling system in this order: 1. import global tokens 2. apply a theme class such as `.light` or `.dark` 3. consume semantic variables in components 4. use component-local `--component-*` variables only for targeted overrides --- # Themes VegaUI ships a separate theme layer in @vega-ui/theme-core. The package entrypoint is packages/theme/src/index.css, which imports both light.css and dark.css. ## Theme Model Themes are class-based: - `.light`: light theme semantic values - `.dark`: dark theme semantic values Each theme imports `@vega-ui/tokens-core` first, then maps raw token scales into semantic application variables such as: - `--background-color` - `--text-color` - `--focus-color` - `--surface-primary` - `--surface-secondary` - `--border-color` - `--fills-primary` - `--label-primary` ## Key Semantic Variables These are the main variables application code and component styles should rely on first. | Variable | Role | Typical Consumers | | --- | --- | --- | | `--background-color` | base app canvas color | app shell backgrounds, thumb-on-track contrast, custom page layout | | `--text-color` | default content color | `Text`, `Heading`, field values, icon triggers, option rows | | `--focus-color` | focus outline color | buttons, links, sliders, radios, checkboxes, segmented control | | `--surface-primary` | primary container surface | `DialogContent`, `DrawerContent`, `SheetContent`, `PopoverContent` | | `--surface-secondary` | floating or transient surface | `SelectListbox`, `TooltipContent`, `TooltipArrow` | | `--border-color` | default structural border | `Card`, `Fieldset`, `TableRow`, `CheckboxCard` | | `--border-color-hover` | interactive border emphasis | hover-state border progression for border-driven surfaces | | `--color-error` | validation and error feedback | `TextField`, `TextArea`, `PinField`, `HelperText` | | `--fills-primary` | accent-selected fill | checked `Checkbox`, checked `Radio`, page control progress | | `--fills-secondary` | neutral track or subdued fill | `Progress`, `Meter`, `MeterStack`, `Slider`, `Code`, off `Switch` | | `--label-primary` | strong indicator label | page control active indicators and derived emphasis fills | | `--label-secondary` | muted secondary text | `Fieldset` legend text, `Code` foreground, secondary metadata | | `--disable-text-color` | disabled primary content | disabled button text, disabled field values, disabled segmented items | | `--disable-background-color` | disabled control surface | disabled `TextField`, `TextArea`, `Select`, `Checkbox`, `Switch`, `ButtonBase` | | `--surface-shadow` | elevated surface shadow | `Dialog`, `Drawer`, `Sheet`, `Popover`, `Tooltip`, `SelectListbox` | ## Semantic Token Types `Key Semantic Variables` is the quick-reference layer. This section is the classification layer: it groups theme variables by semantic family so the system is easier to extend and reason about. | Token | Semantic Type | Meaning In UI | | --- | --- | --- | | `--fills-primary` | `accent-soft-fill` | primary accent fill for selected or active UI | | `--fills-primary-hover` | `accent-soft-fill-hover` | hover state for the primary accent fill | | `--fills-primary-active` | `accent-soft-fill-active` | active state for the primary accent fill | | `--fills-secondary` | `neutral-track-fill` | neutral fill for tracks, rails, and subdued containers | | `--fills-secondary-hover` | `neutral-track-fill-hover` | hover state for neutral track-like surfaces | | `--fills-secondary-active` | `neutral-track-fill-active` | active state for neutral track-like surfaces | | `--fills-tertiary` | `field-surface` | default background for text-entry and field-like controls | | `--fills-tertiary-hover` | `field-surface-hover` | hover state for field surfaces | | `--fills-tertiary-active` | `field-surface-active` | active state for field surfaces | | `--fills-quaternary-hover` | `micro-surface-hover` | lightweight hover fill for icon actions and micro-interactions | | `--fills-quaternary-active` | `micro-surface-active` | active state for micro-interactions | | `--surface-primary` | `container-surface` | main elevated surface for containers such as dialogs or sheets | | `--surface-secondary` | `floating-surface` | floating surface for listboxes, popovers, and tooltips | | `--surface-shadow` | `elevation-shadow` | shadow for elevated surfaces | | `--text-color` | `content-primary` | default readable content color | | `--text-color-inverse` | `content-on-accent` | readable content color on accent-filled backgrounds | | `--label-primary` | `label-strong` | strong UI label or indicator color | | `--label-secondary` | `label-muted` | secondary label or metadata color | | `--label-tertiary` | `label-subtle` | placeholder, helper, and subdued label color | | `--border-color` | `border-default` | default structural border color | | `--border-color-hover` | `border-hover` | hover state for structural borders | | `--border-color-active` | `border-active` | active state for structural borders | | `--disable-background-color` | `disabled-surface` | disabled background and surface fill | | `--disable-text-color` | `disabled-content` | disabled primary text or value color | | `--disable-label-text-color` | `disabled-subtle-content` | disabled secondary, helper, or placeholder text color | | `--disable-border-color` | `disabled-border` | disabled border or indicator color | | `--focus-color` | `focus-ring` | focus ring and outline color | | `--color-error` | `feedback-error` | error and validation feedback color | | `--overlay` | `backdrop-overlay` | backdrop tint for modal and overlay layers | | `--separator-opaque` | `separator-strong` | visible separators, handles, and dividers | ## Semantic Families Think about theme variables in these families first: - `fills-*`: interactive fill semantics for selected, hovered, pressed, or field-like surfaces - `surface-*`: container and floating surface semantics for overlays and elevated UI - `text-*` and `label-*`: readable content and UI-label semantics - `border-*`: structural boundary and border-emphasis semantics - `disable-*`: disabled-state surface, content, and border semantics - `focus-color`, `color-error`, `overlay`, `separator-*`: system and state-feedback semantics ## Semantic Types In Practice These semantic types come from how the UI package actually uses the variables: - `accent-soft-fill`: selected or checked states such as `Checkbox`, `Radio`, and page-control progress - `neutral-track-fill`: tracks and neutral bars such as `Progress`, `Meter`, `MeterStack`, and `Slider` - `field-surface`: input-like controls such as `TextField`, `TextArea`, `PinField`, and `SelectCombobox` - `micro-surface-*`: low-emphasis hover and active surfaces for icon buttons, trigger buttons, and picker items - `container-surface`: primary overlay or panel surfaces such as `DialogContent`, `DrawerContent`, and `SheetContent` - `floating-surface`: floating content surfaces such as `SelectListbox`, `TooltipContent`, and `PopoverContent` - `content-*`, `label-*`, `border-*`, and `disable-*`: text, label, border, and disabled-state semantics reused across the entire kit Use this section to answer “what class of semantic token do I need here?”, then use `Key Semantic Variables` to pick the concrete variable name. ## How To Apply A Theme Put the theme class on ``: ```html ``` `Dialog`, `Drawer` and `Sheet` render into `document.body`, so a class on an inner wrapper does not reach them. ## App Shell With Theme Switcher ```tsx import { useEffect, useState } from 'react'; import { Button } from '@vega-ui/react'; type ThemeName = 'light' | 'dark'; export function ThemeSwitcher() { const [theme, setTheme] = useState('light'); useEffect(() => { document.documentElement.classList.remove('light', 'dark'); document.documentElement.classList.add(theme); }, [theme]); return ( ); } ``` Give the page the theme background and text color: ```css body { background: var(--background-color); color: var(--text-color); } ``` A wrapper with another theme class themes its own subtree, but `Dialog`, `Drawer` and `Sheet` opened from it still take the theme of ``. ## Theme Customization The safest customization path is to define your own theme class and override semantic variables there. ```css @import '@vega-ui/theme-core'; .brand-sunrise { --background-color: #fffaf3; --text-color: #251b10; --surface-primary: #fffdf9; --surface-secondary: #fff7ec; --border-color: #e7d7c2; --focus-color: var(--color-orange-accent-400); --fills-primary: color-mix(in oklab, var(--color-orange-accent-500) 16%, transparent); --fills-primary-hover: color-mix(in oklab, var(--color-orange-accent-500) 22%, transparent); --label-secondary: #7a6653; } ``` Then put that class on `` the same way as `.light` or `.dark`. Customize semantic variables first. Reach for component-local variables only when the change should affect one component family rather than the whole application. ## Light vs Dark Behavior Light and dark themes keep the same semantic variable names. The main difference is how those semantics map onto the raw token scale. In particular, accent colors are inverted in dark mode. For example, `--color-blue-accent-100` points to a lighter raw blue in light mode and a darker raw blue in dark mode. That is why component styles should prefer semantic accent variables over raw palette steps. ## Guidance - put the theme class on ``, not on a wrapper or per component - consume semantic variables in component CSS whenever possible - avoid writing component-specific light/dark overrides when a semantic theme variable already exists - customize semantic variables before touching component-local `--component-*` variables --- # Tokens Global tokens live in @vega-ui/tokens-core: raw scales for color, type, radius, spacing, motion, shadow and layering that the themes build on. The package entrypoint [packages/tokens/src/index.css](https://github.com/vega-ui/ui/blob/main/packages/tokens/src/index.css) imports the token groups used across VegaUI: - `colors.css` - `typography.css` - `radiuses.css` - `spacings.css` - `animations.css` - `shadows.css` - `layers.css` ## Token Categories The token package provides raw building blocks, not component behavior. - colors: palette scales such as `--color-blue-500` - typography: font, size, line-height, and text treatment tokens - radiuses: border-radius scales - spacings: layout and control sizing scales - animations: motion timing and transition tokens - shadows: elevation and surface shadow tokens - layers: z-index and layer ordering tokens ## Color Guidance The strongest rule from [packages/tokens/README.md](https://github.com/vega-ui/ui/blob/main/packages/tokens/README.md) is: - use raw palette tokens only to define semantics - use semantic accent tokens inside components ## Do Not Use Raw Tokens In Components Treat this as a hard rule for normal component styles. Use raw palette tokens such as `--color-blue-500` or `--color-gray-300` only when: - you are defining a semantic theme variable - you are working in the token or theme layer itself - a component doc explicitly documents a raw token hook as public API For regular component CSS, prefer semantic variables such as: - `--text-color` - `--label-primary` - `--border-color` - `--color-blue-accent-500` - `--color-gray-accent-300` - `--fills-primary` Good: ```css color: var(--color-primary-500); border-color: var(--color-gray-accent-300); ``` Avoid in component styles: ```css color: var(--color-blue-500); border-color: var(--color-gray-300); ``` ## Why Tokens And Themes Are Separate Tokens define scales. Themes decide how those scales become semantic application variables. Components should usually consume theme semantics, not raw token values, unless a component contract explicitly documents a raw token hook. --- # CSS variables VegaUI styling is built as a layered CSS variable system. ## Layers 1. raw token variables from `@vega-ui/tokens-core` 2. semantic application variables from `@vega-ui/theme-core` 3. component-local variables from `packages/ui/src/**/style.module.css` ## How The Layers Work Together Example flow: 1. `--color-blue-500` is defined in the token layer 2. the active theme maps that scale into semantic variables such as `--color-blue-accent-500`, `--text-color`, or `--fills-primary` 3. a component may then expose local variables such as `--select-value-color` or `--select-size` That means component docs should distinguish three override levels: - token override: broad system-level change - theme override: semantic application change - component override: targeted local tuning ## Preferred Override Order Start with the broadest safe level: 1. adjust theme semantics when the change should affect many components 2. adjust component-local `--component-*` variables when only one component family should change 3. use direct style overrides only when neither tokens nor documented variables cover the case This rule matters because raw token overrides spread across the whole system quickly, while semantic theme overrides remain predictable and component-local overrides stay contained. ## Example ```tsx ``` In this example: - `--color-primary-700` comes from the semantic theme layer - `--select-size` and `--select-value-color` are component-local variables ## Guidance - do not treat every CSS custom property in source as stable public API - prefer documented semantic variables over raw palette values - keep theme concerns global and component overrides local --- # Forms and validation Vega UI fields are native form controls, so a plain
submits them, validates them and resets them without extra state. ## Native Inputs Do The Work Every field part renders a real element: `TextFieldInput`, `PasswordFieldInput`, `NumberFieldInput`, `PhoneFieldInput`, `DateFieldInput` and `TimeFieldInput` are ``, `TextArea` is a `