Skip to content
Kura UI
Docs

Getting started

Kura UI needs React 19 and Tailwind CSS v4. Use the npm package, or copy the source with the shadcn CLI.

1a. Install from npm

npm install @kura-ui/react
app/globals.css
@import "tailwindcss";
@import "@kura-ui/react/styles.css";
import { ShimmerText } from "@kura-ui/react/shimmer-text";

export function Status() {
  return <ShimmerText>Thinking…</ShimmerText>;
}

Each component is its own entry point (@kura-ui/react/<slug>) — tree-shakeable, with "use client" only where needed. Hooks live at @kura-ui/react/hooks, utilities (cn, motion presets) at @kura-ui/react/utils.

1b. Or copy the source (shadcn)

npx shadcn@latest add https://kuro.abstergo.fyi/r/kura-theme.json
npx shadcn@latest add https://kuro.abstergo.fyi/r/shimmer-text.json

2. Dark mode

Tokens switch under a .dark class or [data-theme="dark"] on any ancestor (works with next-themes). Components never use dark: variants.

3. Design language: Kura Signal

Everything is still until the model speaks. Warm paper-and-ink neutrals, one jade “signal” accent reserved for AI activity, hairline borders, layered shadows and short, interruptible motion. Full guide in DESIGN.md.

accent
accent-subtle
ink
danger
success
warning
Motion presets — @kura-ui/react/utils
springs = {
  "snappy": {
    "type": "spring",
    "visualDuration": 0.2,
    "bounce": 0.1
  },
  "smooth": {
    "type": "spring",
    "visualDuration": 0.35,
    "bounce": 0
  },
  "gentle": {
    "type": "spring",
    "visualDuration": 0.5,
    "bounce": 0.05
  },
  "bouncy": {
    "type": "spring",
    "visualDuration": 0.4,
    "bounce": 0.25
  }
}

durations = {"instant":0.09,"fast":0.15,"base":0.22,"slow":0.32,"slower":0.48}
easings   = {"out":[0.23,1,0.32,1],"inOut":[0.77,0,0.175,1],"in":[0.55,0,1,0.45]}

Hooks

useControllableState

One state API for components that support controlled (value/onChange) and uncontrolled (defaultValue) usage.

import { useControllableState } from "@kura-ui/react/hooks";

const [open, setOpen] = useControllableState({ value: props.open, defaultValue: false, onChange: props.onOpenChange });

useReducedMotion

Live, SSR-safe prefers-reduced-motion flag (false on the server).

import { useReducedMotion } from "@kura-ui/react/hooks";

const reduced = useReducedMotion();
<motion.div transition={reduced ? { duration: 0 } : springs.smooth} />

useHotkey

Bind a keyboard shortcut on document (or a target). `mod` = ⌘ on Apple, Ctrl elsewhere. Exact modifier matching.

import { useHotkey } from "@kura-ui/react/hooks";

useHotkey(["mod", "enter"], () => submit());

useIsApplePlatform

Hydration-safe macOS/iOS detection for rendering ⌘ vs Ctrl.

import { useIsApplePlatform } from "@kura-ui/react/hooks";

const apple = useIsApplePlatform();
const label = apple ? "⌘K" : "Ctrl K";

useAudioAnalyser

Web Audio analysis of a mic MediaStream or <audio>/<video> element → level, log-spaced bands and waveform, with a deterministic simulated mode (speech | music | silence) for demos and tests. SSR-safe.

import { useAudioAnalyser } from "@kura-ui/react/hooks";

const rec = useMediaRecorder();
const analyser = useAudioAnalyser({ source: rec.stream, simulate: !rec.stream && 'silence', bands: 24 });
const { bands } = useAudioFrame(analyser, { fps: 30 });

useAudioFrame

Throttled React-state snapshot of a useAudioAnalyser for simple DOM visualisations.

import { useAudioFrame } from "@kura-ui/react/hooks";

const { level } = useAudioFrame(analyser, { fps: 20 });

useMediaRecorder

MediaRecorder wrapper with explicit permission/denied/unsupported states, pause/resume, duration and a final Blob.

import { useMediaRecorder } from "@kura-ui/react/hooks";

const rec = useMediaRecorder({ onStop: (blob) => upload(blob) });
<button onClick={rec.isRecording ? rec.stop : rec.start}>…</button>

useSpeechRecognition

Web Speech API speech-to-text with interim results; reports unsupported (e.g. Firefox) so you can fall back to recording + server transcription.

import { useSpeechRecognition } from "@kura-ui/react/hooks";

const speech = useSpeechRecognition({ lang: 'en-US' });
<textarea value={speech.transcript} readOnly />

Next: browse components or connect your agent.