React · Vue · Svelte · Angular

Write the component once.

One behaviour core, one stylesheet, and thin adapters for four frameworks. Accessible by construction — focus traps, keyboard navigation and ARIA are written once and shared, not reimplemented as each framework's own copy.

Get startedBrowse Storybookpnpm add @crosskit-ui/react

Live

These are the real components.

Rendered from @crosskit-ui/react on this page. The other three adapters emit identical markup, so they look identical too.

One API

Same props. Every framework.

Prop names are identical everywhere. Only the binding syntax is idiomatic to each framework.

import { Button } from "@crosskit-ui/react";
import "@crosskit-ui/styles";

export function Save() {
  return (
    <Button variant="primary" icon="check" onClick={save}>
      Save changes
    </Button>
  );
}
<script setup lang="ts">
import { Button } from "@crosskit-ui/vue";
import "@crosskit-ui/styles";
</script>

<template>
  <Button variant="primary" icon="check" @click="save">
    Save changes
  </Button>
</template>
<script lang="ts">
  import { Button } from "@crosskit-ui/svelte";
  import "@crosskit-ui/styles";
</script>

<Button variant="primary" icon="check" onclick={save}>
  Save changes
</Button>
import { CkButton } from "@crosskit-ui/angular";
// angular.json -> styles: ["@crosskit-ui/styles"]

@Component({
  imports: [CkButton],
  template: `
    <button ckButton variant="primary" icon="check" (click)="save()">
      Save changes
    </button>
  `,
})
export class SaveComponent {}

Accessibility

Focus traps you didn't have to write.

Behaviour lives in one framework-free core — the same tested implementation in all four adapters, instead of four chances to get it wrong.

Theming

Retheme with CSS variables.

Around 48 semantic tokens. Override them in a stylesheet loaded after ours — no rebuild, no fork, and no Tailwind required to consume it.

Built to stay honest.

No utility classes

Components emit data-scope anddata-part, never class strings. One stylesheet serves every framework, and your own CSS still wins.

Tailwind optional

The CSS ships precompiled. Tailwind is our build-time dependency, not yours. Use it if you like — an opt-in bridge maps our tokens onto your utilities.

Dark mode, once

Flip data-theme on html. No dark: variant repeated inside every component.

Install

Two lines.

pnpm add @crosskit-ui/react     # or /vue, /svelte, /angular

// then, once, anywhere in your app:
import "@crosskit-ui/styles";

Requires React 19, Vue 3.5, Svelte 5 or Angular 20+. ESM only. MIT licensed.