Docs
From the library to your app.
Teseria isn't a package you install. Each element is a pair of source files you drop into your project and own from then on: rename it, restyle it, take it apart. These are the few things every element has in common.
Client components for React 19, with no provider to wrap and no runtime of ours.
Springs come from framer-motion and icons from lucide-react. The Install step lists what each one needs.
Every color, corner and font comes from tokens.css, which you add once.
Add an element
Open any element and switch its sheet to Code. It walks you through three steps:
- Install gives the one
npm iline for the packages the element imports. Run it once; most elements share the same two. - Files holds the component, its stylesheet and
tokens.css. Keep the component and its CSS side by side, in a folder of their own if you like.tokens.cssis the same for every element, so you only ever add it once. - Usage is a ready import you can paste and render.
Some elements also come as a registry item, and their Install step opens with an npx shadcn@latest add line that writes the files for you.
import { Switch } from "./switch/Switch";
import "./switch/Switch.css";
import "./tokens.css"; // once, anywhere above your elements
export function Settings() {
return <Switch label="Email me updates" defaultChecked />;
}Props are typed at the top of each component. Every one has a default, so an element renders with none, and you add only the ones you want to change. Elements that show pictures default to Teseria's demo art, which only exists on this site: pass your own images through their props.
Next.js and friends
- Next.js App Router. Every component already starts with
"use client", so you can render it straight from a server page or layout. - Vite, Remix, Astro islands. Nothing special: import the component and its CSS like any other.
- Tailwind and other CSS. Each element's classes carry a short prefix of their own, like
tsw-for Switch, and its resets are wrapped in:where(), so your utilities still win wherever they apply.
Colors and dark mode
No element hard-codes a color. They all read a small set of variables, every one prefixed --ts- so it can't clash with names you already have. Keep the defaults in tokens.css, or point the names at your own theme:
/* your theme already has these; point Teseria's names at them */
:root {
--ts-accent: var(--brand);
--ts-font: var(--font-sans);
--ts-page: var(--background);
--ts-fg: var(--foreground);
}- Surfaces
--ts-pagethe ground,--ts-raisedcards and popovers,--ts-sunkenwells and tracks,--ts-tint-1to-3neutral fills from light to deep- Text
--ts-fgdown through-secondary,-tertiary,-mutedand-faint;--ts-fg-inversefor text on anfgfill;--ts-fg-rgbas bare numbers for washes likergba(var(--ts-fg-rgb), 0.08)- Lines
--ts-borderfor hairlines,--ts-edgethe soft shadow a filled surface wears instead of an outline- Signals
--ts-accent,--ts-focus,--ts-success,--ts-warning,--ts-danger
Dark mode
Light is the default. Put data-theme="dark" on any ancestor to switch an element, or leave it and the element follows the visitor's system setting; data-theme="light" pins it to light. If your app toggles dark mode with a class instead, redefine the same variables under that class.
Corners and type
Controls such as buttons, chips and inputs are always pills. Everything else takes one of three radii, so a whole screen can be squared off or softened from one place:
--ts-r-panel(24px) for dialogs, sheets and cards--ts-r-pop(18px) for menus, popovers and toasts--ts-r-inner(12px) for thumbnails and cards inside cards
Type comes from --ts-font. Its default is Inter, falling back to the system sans.
Sound
Teseria's sounds aren't audio files. One TypeScript file, sound.ts, synthesizes each one with the Web Audio API the moment it plays, with no dependencies and nothing to download. Elements never play sound on their own. One that has something worth hearing takes an onSound prop and calls it with a cue name; you choose whether it plays.
import { play } from "./sound";
// an element names the moment; you decide what plays
<Switch label="Sounds" onSound={(cue) => play(cue)} />
// or call it from your own code, after a click, tap or key press
play("done");
play("push", { at: 0.7 }); // at: 0..1, a little lower or higher
play("key", { gap: 40 }); // gap: skip repeats closer than 40 ms- Browsers wait for a gesture. Audio starts only after the visitor has clicked, tapped or pressed a key, so the first sound should come from one of those.
- Give people the off switch.
sound.toggle()mutes and unmutes and remembers the choice in local storage, andsound.enabled()tells you which it is. Wire it to a setting. - Keep sound for moments. A toggle flipped, a step done, a drop landed. The cues are short and quiet on purpose; scattered across every hover they turn to noise.
Hear every cue, and see which elements play it, on the Sounds page; any of them can also be saved as a WAV. The free engine covers the soft voice and every sound the free elements make. Pro adds the other voices and the hand-made sounds.
Your AI editor
Teseria runs an MCP server, so an agent in your editor can search the library and write an element straight into your project. Ask in plain words: "add Teseria's switch to the settings page" or "play a sound when this form saves".
- Sign in and open Connect MCP. Make a token there: it's shown once, so copy it before you close the dialog.
- Pick your editor in the same dialog for its ready-made setup. It covers Claude Code, Cursor, VS Code, Windsurf, Codex, Gemini CLI and Grok.
claude mcp add --transport http teseria https://teseria.dev/api/mcp --header "Authorization: Bearer <your token>"
The server answers four tools: search_elements, get_element, get_sound_engine and about_teseria. Free elements and the free sound engine come back with or without a token. With a Pro token, Pro elements and every sound come back too. Making a new token replaces the old one, so a leaked token is one click from useless.
Accessibility
Every element works from the keyboard and announces itself to screen readers, shows a focus ring when it's reached with Tab, and calms its motion when the system asks for reduced motion. If you restyle one, keep those three: the focus ring reads --ts-focus, and the motion checks live inside the component.
Pro and the license
Free elements and sounds, with their code, are yours to use in personal and commercial work, client projects included. Pro elements show their preview and code once you have a plan, Yearly or Lifetime: see Pricing. On any plan you can ship elements inside your own products, but you can't resell or republish them as a kit, template or library. The full terms are in the license.
Help
Something broken, unclear or missing? Pick Feedback from the menu at the top right of any page, or write to [email protected]. Say which element, and paste the error if there is one.