Installation

Add @iammikz/srui to a Tailwind v4 project.

srui is a regular npm package. You need React 18+ and Tailwind CSS v4.

1. Install the package

pnpm add @iammikz/srui

2. Import the styles

In your global CSS entry (the file that imports tailwindcss), add srui's token contract, its animations, and every preset you want available:

@import "tailwindcss";
@import "@iammikz/srui/theme.css";
@import "@iammikz/srui/animations.css";
@import "@iammikz/srui/presets/flat.css";
@import "@iammikz/srui/presets/glass.css";
@import "@iammikz/srui/presets/neumorphic.css";
@import "@iammikz/srui/presets/skeuomorphic.css";
@source "../node_modules/@iammikz/srui/dist";
Why is @source required?

Tailwind v4 automatically scans your project for class names but skips node_modules by default. srui's component classes live inside node_modules/@iammikz/srui/dist, so without the explicit @source line the utilities those components rely on (bg-primary, surface, …) are never generated and everything renders unstyled.

3. Wrap your app in UIProvider

UIProvider holds the active preset (data-style on <html>) and color scheme (the dark class), and persists both to localStorage.

To avoid a flash of the wrong theme before React hydrates, also inline the exported noFlashScript() into your document head:

import { createRoot } from "react-dom/client";
import { UIProvider, noFlashScript } from "@iammikz/srui";
import App from "./App";

createRoot(document.getElementById("root")!).render(
  <UIProvider>
    <App />
  </UIProvider>,
);
<script>
  {/* replace this comment with the output of noFlashScript() */}
</script>

The copy-pasteable script (rendered here from noFlashScript()):

(function(){try{var s=localStorage.getItem('srui-style');var c=localStorage.getItem('srui-scheme');var sv=(s==='glass'||s==='neumorphic'||s==='skeuomorphic'||s==='flat')?s:'flat';var cv=(c==='light'||c==='dark'||c==='system')?c:'system';var dark=cv==='dark'||(cv==='system'&&window.matchMedia('(prefers-color-scheme: dark)').matches);var e=document.documentElement;e.setAttribute('data-style',sv);e.classList.toggle('dark',dark);e.style.colorScheme=dark?'dark':'light';}catch(err){}})();

It reads the persisted srui-style / srui-scheme keys and applies data-style + the dark class before first paint.

Not using Tailwind? The precompiled fallback

If your project doesn't use Tailwind v4, import the precompiled stylesheet instead of steps 2's imports — it contains the tokens, presets, base styles and every utility srui's components use (compiled with prefix(srui) so it can't collide with your own classes):

@import "@iammikz/srui/styles.css";

Skip the @source line in this setup — there is nothing for Tailwind to scan.

Vite setup walkthrough

Vite is this project's own supported consumer path. Assuming a standard npm create vite React+TS app:

import { createRoot } from "react-dom/client";
import { UIProvider, noFlashScript } from "@iammikz/srui";
import App from "./App";

createRoot(document.getElementById("root")!).render(
  <UIProvider>
    <App />
  </UIProvider>,
);

Add the plugin and point Tailwind at your CSS entry:

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";

export default defineConfig({
  plugins: [react(), tailwindcss()],
});

And the no-flash script inline in index.html's <head>:

(function(){try{var s=localStorage.getItem('srui-style');var c=localStorage.getItem('srui-scheme');var sv=(s==='glass'||s==='neumorphic'||s==='skeuomorphic'||s==='flat')?s:'flat';var cv=(c==='light'||c==='dark'||c==='system')?c:'system';var dark=cv==='dark'||(cv==='system'&&window.matchMedia('(prefers-color-scheme: dark)').matches);var e=document.documentElement;e.setAttribute('data-style',sv);e.classList.toggle('dark',dark);e.style.colorScheme=dark?'dark':'light';}catch(err){}})();

That's the whole setup — the same recipe this site's own apps/docs uses.