Reference

Components

30 components, identical in four frameworks. Wherever one needs a focus trap, keyboard navigation or managed ARIA, that behaviour is written once in a framework-free core and shared by all four, rather than reimplemented per adapter.

Install

pnpm add @crosskit-ui/react

# then, once, at your app entry
import "@crosskit-ui/styles";
pnpm add @crosskit-ui/vue

# then, once, at your app entry
import "@crosskit-ui/styles";
pnpm add @crosskit-ui/svelte

# then, once, at your app entry
import "@crosskit-ui/styles";
pnpm add @crosskit-ui/angular

# angular.json
"styles": ["@crosskit-ui/styles", "src/styles.css"]

One stylesheet, imported once. Consumers need no Tailwind of their own — it is an authoring tool here, not a runtime dependency.

How the styling works

Every component emits data-scope, data-part and data-state — never a class name. Your ownclass lands on the root untouched, and because everything shipped sits inside a CSS cascade layer, your unlayered rules win regardless of specificity.

Retheming is CSS custom properties: about 48 semantic --ck-*variables, with dark mode a single [data-theme="dark"] block.

Primitives

Layout

Forms

Overlays

ModalThe overlay template every other one copies, built on the primitives in `@crosskit-ui/core` with no runtime dependency. Rendering is gated on presence, never on `open`, which is what keeps `data-state="closed"` on screen long enough for the exit animation to run.DrawerThe same behaviour as Modal — about fifteen lines differ. It is distinguished in CSS by `data-ck="drawer"` plus `data-placement`, since both share `data-scope="dialog"`.TooltipDeletes the largest file in v0 — three state variables, four refs, three effects, hand-rolled viewport clamping and a manually managed portal container. The trigger wraps your element rather than cloning props onto it.PopoverThe interactive half of Tooltip, and the reason the two are separate components rather than one with a flag. A tooltip DESCRIBES its trigger, so a screen reader flattens its contents into the trigger's description; a popover is a thing the trigger opens, so a button inside it stays a button. New in v2, React first.Menu / DropdownReplaces v0's Dropdown + Menu + MenuItem trio with one data-driven component. v0 took a whole element as its trigger, so the common case put a button inside a button; v1 removed the choice by generating the button itself, and React's v2 Dropdown takes the element back and never wraps it — so your own Button stays exactly one button.ToastThe toast queue is a plain store with no framework in it, so the toaster is a module-level singleton. The three lines below are identical in React, Vue, Svelte and Angular — no provider, no inject, no DI token in any of them. React builds its queue with createToastQueue(); the other three still use createToaster() until they move across.@zag-js/toast

Disclosure

Data display