Vesture
Get started

Philosophy

Why Vesture

Vesture doesn't claim to have more components than anything else, or the flashiest defaults. It makes a small number of architectural bets deliberately, and holds every component to them without exception. Those bets are what you're actually adopting when you install it.

One contract, not a theme file per component

Most component libraries let you override colors with a config object or CSS variables sprinkled per-component. Vesture inverts that: every component is written against `vars` from @vesture/tokens and is physically incapable of rendering a hardcoded color, radius, or font. A theme is a complete implementation of that contract, checked by TypeScript at compile time — createTheme() won't build until every slot has a value, so a new theme can't accidentally leave a component unstyled.

Zero-runtime styling

Styles are written in vanilla-extract — CSS-in-TypeScript that compiles to real .css files at build time. There's no styled-components-style runtime injecting <style> tags on every render, no className computation cost during interaction. What ships to the browser is a stylesheet and static class names, same as if you'd hand-written the CSS.

Accessibility as a default, not a checklist

Calendar and Tabs implement full roving-tabindex keyboard navigation. Modal is a real focus trap with scroll locking. DropdownMenu, Popover, and Tooltip are built on Floating UI for collision-aware positioning instead of hand-rolled offset math. Both shipped themes were tuned so white text on solid success/warning/danger surfaces clears WCAG AA contrast (4.5:1) — not just against the default theme, against any theme built on the same contract, because the check lives in the theme file, not the component.

A DataGrid built for real datasets

Not a wrapper around a demo-friendly table. Row virtualization is computed directly from scroll position, row height, and overscan — no black-box dependency. Columns resize, pin to an edge, sort, and filter; rows edit inline; and a serverSide flag hands sorting, filtering, and pagination off to your own API when the dataset is too large for the client.

Composable by construction

Every single-element component forwards its ref and spreads native HTML attributes through. Button is a real <button>. Input is a real <input>. That means the library composes with form libraries, animation libraries, and design overrides without an escape hatch — because you're never actually blocked from the DOM node underneath.

See composition patterns and token overrides →

A release process with a paper trail

Versioning and changelogs are driven by Changesets — every release traces back to a written, reviewable description of what changed, generated automatically rather than hand-edited after the fact.

Where it makes tradeoffs

  • Native controls (Select, Checkbox, Radio) wrap the real DOM element rather than a fully custom-rendered listbox — you get correct platform behavior for free, at the cost of styling reach the platform itself limits.
  • vanilla-extract requires a build step that understands .css.tsfiles. That's a solved problem in Next.js, Vite, and most modern toolchains, but it's a dependency worth knowing about before you adopt it.
  • Two themes ship today: a neutral default and a warm retro theme that exists specifically to prove the contract is genuinely swappable, not to be a design system on its own.

Try the difference yourself

Every demo throughout these docs is the real published package — not screenshots, not a design file. Toggle the theme in the header and watch every component on the page restyle at once.

terminal
npm install @vesture/react @vesture/tokens