Get started

One package, one stylesheet, and every token is yours to override

The design system ships as rift-ds, the same package this site is built with. There is no configuration API or theme provider: theming is plain CSS custom properties. Import the token stylesheet, use the components, and re-theme by redefining tokens. (The one provider in the library is ToastProvider, needed only if you use the toast queue via useToast.) Try it live in the playground: it generates the exact CSS you would paste into your app.

Quick start with an agent

Building with a coding agent? One command teaches it the system before you write anything: it installs the generated agent skill into your project and prints the MCP connect line, so the agent knows the components, the tokens, and the theming contract from its first session. The docs for your agent section below has everything it sets up.

bash
npx rift-ds init

Install

React 19+ (react and react-dom) is the only required peer dependency (recharts is an optional extra, for the recharts-backed charts). Everything else (component CSS, both themes, the Material Symbols icon font) is bundled. The package is ESM-only: use a bundler that handles CSS and font imports (Vite, Next.js, webpack), with TypeScript's moduleResolution set to "bundler" (or "nodenext").

bash
npm install rift-ds
app.tsxtsx
// Load the tokens once — primitives, semantic tokens, and both themes.
import 'rift-ds/tokens/tokens.css';

// Then import components — from the barrel…
import { Button, Card, Badge } from 'rift-ds';

// …or by deep path (what this site does):
import { Button } from 'rift-ds/components/Button/Button';

// Optional: only if you render raw .material-symbols-rounded spans —
// any component import already loads the icon font for you.
import 'rift-ds/fonts/material-symbols.css';
tsx
// The recharts-backed charts live behind their own entry so that peer
// dependency stays optional — the dependency-free chart pieces
// (ContributionGraph, GanttChart, Gauge, LegendTile, Sparkline) come from the main barrel.
import { BarChart, LineChart } from 'rift-ds/charts';

Or pull single components

The site serves a shadcn-compatible registry, so the shadcn CLI can install any component as source you own instead of a package you depend on. One add brings the component, the components it builds on, and the shared base (tokens, theme presets, icon font, behavior hooks) into a rift folder in your project, imports intact. The index at /r/registry.json lists every component. The CLI expects a components.json and a tsconfig.json in your project; if you have neither, npx shadcn init creates them.

bash
npx shadcn@latest add https://rift-ds.com/r/button.json

Or clone the repo

The package is one way in; the source is another. The whole system is MIT licensed, this site included, so you can clone the repo, run it, and keep whatever parts serve you: the components and tokens, the generators and validators that hold the docs to the code, or the specs the system is built from. One install brings up both the Storybook sandbox and this documentation site.

bash
git clone https://github.com/robritacca-dotcom/rift-ds.git
cd rift-ds
npm install          # one install; the website is an npm workspace
npm run storybook    # the component sandbox, or:
npm run dev -w website   # this whole docs site, locally

Dark mode

Every semantic token has a light and a dark value. Set data-theme="dark" on the root element to switch. There are no prefers-color-scheme queries in components, so your app decides when.

html
<!-- Light is the default; flip the whole system with one attribute -->
<html data-theme="dark">

Bring your own font

No text face is bundled, on purpose: the type is yours to choose. The whole scale chains to one family token, so you load any font however your stack prefers and point the token at it. Want headings in one face and body copy in another? The scale chains through two family roles, both defaulting to the primary token, so you split them instead. The shipped themes prove the range: 11 looks mixing serif, sans, grotesk and mono pairings over the same components. Try pairings live in the playground.

css
/* The whole type scale chains to one token.
   Load any font (Google Fonts, next/font, self-hosted), then: */
:root {
  --font-family-primary: 'Inter', sans-serif;
}

/* Or mix faces: display and heading styles read one family role,
   body styles read the other. Both default to the primary family,
   so overriding either role alone leaves the rest untouched. */
:root {
  --font-family-heading: 'Fraunces', serif;
  --font-family-body: 'Inter', sans-serif;
}

Re-theme with primitives

Tokens are three tiers: primitives hold the raw values, semantic tokens reference primitives, components use semantic tokens. That chain is build-enforced, which is what makes a primitive override cascade through the entire system, both themes included.

css
/* Every semantic token references a primitive, so overriding a
   primitive re-themes everything built on it — in both themes.
   The action colour is theme-split: light fills run teal-08/09/10,
   dark inverts to teal-05/04/03. Re-key those steps to rebrand —
   or copy a complete override from the playground. */
:root {
  --primitive-teal-08: #6D31D3;  /* light fill */
  --primitive-teal-09: #4C2293;  /* light hover */
  --primitive-teal-10: #2E1560;  /* light active, dark label */
  --primitive-teal-05: #A78BFA;  /* dark fill */
  --primitive-teal-04: #C4B5FD;  /* dark hover */
  --primitive-teal-03: #DDD6FE;  /* dark active */

  /* Pill buttons become rounded rectangles */
  --primitive-radius-pill: 12px;
}

Semantic tokens are fair game too when you want to change one meaning without touching the ramp it comes from:

css
/* Prefer surgical changes? Override a semantic token directly —
   scope the dark value under the theme attribute. */
:root {
  --color-status-info-border: #345AC4;
}
[data-theme="dark"] {
  --color-status-info-border: #7F99E3;
}

Tune the icons

Icons ship as the full Material Symbols variable font, so every axis Google exposes is a custom property: fill, stroke weight, and grade, settable at any scope from one icon to the whole app. Optical size is automatic. And nothing couples you to the bundled font: every icon prop in the library accepts your own element as well as a Material name, so a Lucide or any other icon set drops straight in. Draw custom SVGs with currentColor and they inherit text colour the way the bundled glyphs do.

css
/* The bundled Material Symbols font keeps every Google axis live. */
:root {
  --material-symbols-fill: 1;      /* 0 line · 1 filled */
  --material-symbols-weight: 300;  /* 100-700 stroke thickness */
  --material-symbols-grade: 0;     /* -50-200 contrast tuning */
}
tsx
// Every icon prop takes a Material name or your own element.
import { Search } from 'lucide-react';

<Input iconLeft="search" />
<Input iconLeft={<Search size={20} />} />

Ship a theme

Complete looks ship in the package as generated stylesheets. Import the aggregate once, set one attribute on the root element, and the whole product follows: light and dark, the action family, the ambience, and the chart colours together. Remove the attribute to return to the shipped look.

tsx
// Every shipped theme, one generated stylesheet each, plus this aggregate.
import 'rift-ds/tokens/presets/presets.css';

<html data-brand="terminal">

The shipped themes: Smoke, Getaway, Ember, Volt, Zest, Forest, Terminal, Blueprint, Velvet, Bubblegum. Each also loads alone from tokens/presets/<id>.css, and the theme dots on the home page swap the same attribute, so every look here is the one a consumer gets.

Chat and agent UI

The ai category installs with the rest of the package: chat surface primitives (Chat thread, Chat message, Composer, Chat header, Model picker), agent-state components (Agent status, Agent plan, Reasoning, Tool call), session surfaces (Thread panel, Thread tabs, Usage card), and supporting pieces such as Prose and the citation chips. They are components like any other here, themed by the same tokens, and they render whatever conversation you hand them.

What the package does not ship is the conversation itself. You bring the state (the transcript, which turn is streaming), a transport that talks to your backend, and a server-side endpoint holding your LLM API key. Keys stay on the server; nothing in the package or your client code ever holds one. This site's chat is the reference implementation: and you are looking at those components at work.

Docs for your agent

The documentation also serves machines. The site exposes a Model Context Protocol endpoint at /api/mcp: connect any MCP client and your coding agent can query the component list, the exact prop contract of every component, the token registry, and the site's published content while it builds. The prop data is generated from the same JSDoc as the package's type declarations, so it always matches what npm ships. No key or account is needed; everything it serves is already public.

Claude Codebash
claude mcp add --transport http rift-ds https://rift-ds.com/api/mcp
.cursor/mcp.jsonjson
{
  "mcpServers": {
    "rift-ds": { "url": "https://rift-ds.com/api/mcp" }
  }
}
.vscode/mcp.jsonjson
{
  "servers": {
    "rift-ds": { "type": "http", "url": "https://rift-ds.com/api/mcp" }
  }
}

Once connected, each of these is answered by one tool:

  • “Which components are in the ai category?”list_components
  • “What props does DataTable take?”get_component
  • “Which semantic tokens control motion?”list_tokens
  • “What does this site say about design tokens?”search_site
  • “How do I install the library and set up dark mode?”get_setup

The same contracts are served as plain files too: append .md to any component URL for its prop table as markdown, or use the copy button in a component page's header to put it on the clipboard for your agent.

The MCP tools answer on demand. For knowledge an agent carries into every session, there is also a generated agent skill: two markdown files built from the same registries, covering install, theming and the full component catalogue. One command fetches the current pair from this site into a project's .claude/skills/, where skill-capable agents load them automatically:

bash
npx rift-ds init

The files regenerate with every deploy, so re-run the command to refresh them. No npm nearby? The same pair is one curl each:

bash
curl --create-dirs -o .claude/skills/rift-design-system/SKILL.md https://rift-ds.com/skill/rift-design-system/SKILL.md
curl --create-dirs -o .claude/skills/rift-design-system/references/components.md https://rift-ds.com/skill/rift-design-system/references/components.md

Not sure your agent needs any of this? Paste this check into it before it writes code with the package. Every answer is in the skill and one MCP call away; a model working from generic React patterns misses all three.

text
Before writing any rift-ds code, answer these:

1. Which import path serves the recharts-backed charts?
2. Which attribute switches the system to dark mode?
3. Which provider, if any, does the library need, and for what?

If any answer is a guess, run `npx rift-ds init` to install
the agent docs, or connect the MCP endpoint, then check again.

Ambient background

Shader field is the one component that asks more of you than an import. It renders a WebGL2 field of soft light sources that read your colour tokens at runtime, so it re-themes with everything else. But it fills a positioned ancestor you provide, and it can fail on hardware you do not control. So it never decides what to paint instead of itself: it reports pending, active or unavailable, and one fallback covers every failure. It also checks prefers-reduced-motion itself, since the CSS motion tokens cannot see an animation loop.

tsx
import { ShaderField, type ShaderFieldStatus } from 'rift-ds';

const [status, setStatus] = useState<ShaderFieldStatus>('pending');

// Note: the fallback paints on 'unavailable', not on 'not active'.
<div style={{ position: 'fixed', inset: 0, zIndex: -1 }}>
  {status === 'unavailable' && <YourCssFallback />}
  <ShaderField params={{ streak: 0.4 }} onStatusChange={setStatus} />
</div>

The background behind this page is that component, with eight blurred CSS discs kept painted underneath as its fallback.

See it live

The playground applies these overrides to a full page in real time (navigation, components, the type specimen, and the chat widget): pick a brand colour, tint the neutrals, reshape the radii, swap the font, then copy the generated CSS.

Built with

ReactUI library, the required peer dependency
ViteDev server and story test runner
StorybookEvery component, every variant
npmPublished with provenance on every release
GitHubSource, CI, and releases
FigmaWhere the foundation was designed