@esuiss/ui
v0.18.0
Published
Copy-paste React components with a clean, minimal design, built with Tailwind CSS v4 on your choice of three primitive bases. CLI for Next.js and React (Vite), plus React Native, Expo, SwiftUI and Jetpack Compose components.
Maintainers
Readme
@esuiss/ui
Copy-paste React components with a clean, minimal design, built with Tailwind CSS v4 on your choice of three primitive bases, Base UI by default.
Documentation · Components · CLI reference · GitHub
@esuiss/ui is a command-line tool. It does not ship components as a dependency. Instead, it copies their source into your project so you own the code and can change it however you like.
- 67 accessible components and 5 hooks, from buttons and dialogs to sidebars, charts, and chat views
- A refined color palette for light and dark mode, written in oklch
- Phosphor icons
- Next.js and React (Vite) with TypeScript
- React Native and Expo, SwiftUI and Jetpack Compose components with the same commands
- No runtime dependency on this CLI: it runs with
npxand is never installed in your project
Requirements
- Next.js or React with Vite
- TypeScript
- Tailwind CSS 4.2 or later (installed for you if missing)
- Node.js 20 or later
Mobile apps
Run the same commands in an Expo or React Native app, an Xcode project or an Android Gradle module, and the CLI adds the matching React Native, SwiftUI or Compose sources:
npx @esuiss/ui@latest init
npx @esuiss/ui@latest add dialog
npx @esuiss/ui@latest add dialog --platform swift
npx @esuiss/ui@latest add dialog --platform kotlinReact Native projects can pick a styling with --styling uniwind|nativewind|stylesheet; SwiftUI and Compose files go to --path <dir> when given. In a Swift package with several targets, pass --path, or pick the target from a list. When a folder holds more than one project, choose one with --platform web|rn|swift|kotlin.
Quick start
Set up a project once:
npx @esuiss/ui@latest initThen add components:
npx @esuiss/ui@latest add button dialog sidebarUse them:
import { Button } from "@/components/ui/button"
export function Example() {
return <Button>Continue</Button>
}Registry CLI
The React items are also a registry under the @esuiss namespace, so any project with a components.json can add them without this CLI:
npx shadcn@latest add @esuiss/button @esuiss/dialog
npx shadcn@latest add @esuiss/theme-suissUntil the namespace is listed in the public registry directory, register it once in components.json:
{
"registries": {
"@esuiss": "https://suiss.app/ui/r/{style}/{name}.json"
}
}Every item is published for three primitive bases under the same name. A radix-*, new-york or default style in components.json installs the second base, an aria-* style the third, and every other style the default base. This CLI follows the same style, or --base. init --base saves the base as the style in components.json, creating the file when needed, so later add runs use it without the flag.
What init does
- Detects Next.js or Vite and checks for TypeScript and Tailwind CSS 4.2+.
- Adds the
@/*import alias totsconfig.jsonif it is missing. - Installs and wires up Tailwind CSS if needed (
postcss.config.mjsfor Next.js,vite.config.tsfor Vite). - Writes the theme stylesheet to the
tailwind.cssfile named incomponents.json, otherwise toapp/globals.css(Next.js) orsrc/index.css(Vite). It asks before replacing a stylesheet that already has content. - Sets up the font: text uses the system font on macOS and Inter everywhere else. Inter is loaded with
next/font/googlein the root layout (Next.js), or with@fontsource-variable/interimported from the stylesheet (Vite). - Installs
tw-animate-cssandshadcn, whose stylesheets provide the animations, variants, and utilities the components rely on. - Adds the
buttoncomponent. With--overwriteit replaces a localbuttonthat differs from the registry.
Commands
init
npx @esuiss/ui@latest init [options]add
npx @esuiss/ui@latest add
npx @esuiss/ui@latest add <component...> [options]
npx @esuiss/ui@latest add patterns <pattern...> [options]
npx @esuiss/ui@latest add interactions <interaction...> [options]
npx @esuiss/ui@latest add charts <chart...> [options]
npx @esuiss/ui@latest add blocks <block...> [options]Run add without names to pick from a list: choose components, patterns, interactions, charts or blocks, type to filter, select with space and press enter. add patterns (or another group) without names opens the list for that group. Outside an interactive terminal, add without names prints the available names instead.
Components are written to components/ui/, patterns to components/patterns/, interactions to components/interactions/, charts to components/charts/, blocks to components/blocks/ and hooks to hooks/ under your @/* alias root. Components they depend on are added too, and missing npm packages are installed with your package manager (npm, pnpm, yarn, or bun): the packageManager field in package.json decides, then the package manager that runs the CLI (pnpm dlx, yarn dlx or bunx), then the lockfile of the project or its workspace.
Options
| Option | Description |
| --- | --- |
| -o, --overwrite | Replace local files that differ from the registry, including the components they depend on (init too) |
| --diff | Show how your local files differ from the registry, for every file --overwrite would replace |
| -a, --all | Add every component except theme, or every pattern, interaction, chart or block with add patterns / add interactions / add charts / add blocks |
| -y, --yes | Skip confirmation prompts and the pick list |
| -b, --base <base\|radix\|aria> | Primitive base of React components, read from the style in components.json when omitted, otherwise base. init saves it there |
| -p, --platform <web\|rn\|swift\|kotlin> | Pick the platform when the folder holds more than one project |
| -s, --styling <uniwind\|nativewind\|stylesheet> | React Native styling, detected from the project when omitted |
| --path <dir> | Where SwiftUI or Compose files go, relative to the project |
| -c, --cwd <dir> | Run in another project directory |
| -h, --help | Show help, or the help of a command with init --help and add --help |
| -v, --version | Show the version |
Updating components
Components are your code, so updates never overwrite your changes silently. Review the latest version first, then replace your copy if you want it:
npx @esuiss/ui@latest add button --diff
npx @esuiss/ui@latest add button --overwrite--diff and --overwrite cover the same files: the components you name and every component they depend on. Running add for a component you already have keeps your file and tells you when it differs from the registry.
Each CLI version downloads the registry from the release tag of that version, so a pinned version keeps working when the registry changes. Use @latest for the newest components.
Theming
Your stylesheet holds the whole theme:
@import "tailwindcss";
@import "tw-animate-css";
@import "shadcn/tailwind.css";
@import "@fontsource-variable/inter";
@custom-variant dark (&:is(.dark *));
:root {
--radius: 0.625rem;
--background: oklch(1 0 0);
--foreground: oklch(0.2316 0.0038 286.09);
--primary: oklch(0.5629 0.1933 256.16);
--primary-foreground: oklch(1 0 0);
}
.dark {
--background: oklch(0 0 0);
--foreground: oklch(0.9707 0.0026 286.29);
--primary: oklch(0.669 0.1808 251.84);
--primary-foreground: oklch(1 0 0);
}
@theme inline {
--font-sans: -apple-system, BlinkMacSystemFont, "Inter Variable", sans-serif;
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
}The web components use the common theme variables, so themes written with the same names drop in:
- Base:
background,foreground,card,popoverand their-foregroundtext colors. - Actions:
primary,secondary,accent,destructive, withprimary-foreground,secondary-foregroundandaccent-foreground. - Quiet:
mutedandmuted-foregroundfor secondary text. - Lines and focus:
border,input,ring. - Charts:
chart-1tochart-5. - Sidebar:
sidebar,sidebar-foreground,sidebar-primary,sidebar-primary-foreground,sidebar-accent,sidebar-accent-foreground,sidebar-border,sidebar-ring.
States use opacity rather than extra variables (hover:bg-primary/80, bg-destructive/10), and disabled elements use disabled:opacity-50. Status colors come from the Tailwind palette, such as text-green-600 dark:text-green-400.
- Change a color: edit its value under
:root(light) and.dark(dark). - Add a color: define it under
:rootand.dark, then map it in@theme inline, for example--color-brand: var(--brand);. Classes such asbg-brandandtext-brandbecome available. - Change the font: set
--font-sansin@theme inline. It uses the system font on macOS and Inter elsewhere. - Corner radius:
--radiusscales every rounded component.
Dark mode
Dark mode is enabled by the dark class on <html>; every variable above switches with it. If you want a theme switch, add the theme component. It is not added by add --all, so projects that stay in one appearance never get it:
npx @esuiss/ui add themeIt gives you ThemeProvider, useTheme, ThemeToggle and ThemeScript. The provider remembers the choice (light, dark or system) and follows the system setting when it is system. ThemeScript sets the class before the page paints, so there is no flash of the wrong theme.
With Next.js:
import { ThemeProvider, ThemeScript } from "@/components/ui/theme"
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<head>
<ThemeScript />
</head>
<body>
<ThemeProvider>{children}</ThemeProvider>
</body>
</html>
)
}With Vite, wrap your app in ThemeProvider and put the same script in index.html, before your app script:
<script>
(()=>{try{var t=localStorage.getItem("theme")||"system";var d=t==="dark"||(t==="system"&&matchMedia("(prefers-color-scheme: dark)").matches);var r=document.documentElement;r.classList.toggle("dark",d);r.style.colorScheme=d?"dark":"light"}catch(e){}})()
</script>ThemeToggle switches with an animated reveal built on the View Transitions API. Choose the effect (circle, rectangle, polygon or circle-blur), where it starts with origin, and add blur for a softer edge:
<ThemeToggle effect="circle" origin="top-right" />
<ThemeToggle effect="rectangle" origin="bottom-up" blur />Use setTheme from useTheme to build your own control. Pass a transition as the second argument to animate it, or leave it out to switch instantly. Browsers without View Transitions, and visitors who prefer reduced motion, switch instantly either way.
const { theme, setTheme } = useTheme()
setTheme("system", { effect: "circle", origin: "top-center" })Components
accordion, alert, alert-dialog, alert-sheet, aspect-ratio, attachment, avatar, badge, breadcrumb, bubble, button, button-group, calendar, card, carousel, chart, checkbox, collapsible, combobox, command, context-menu, dialog, direction, drawer, dropdown-menu, empty, field, fullscreen-menu, hover-card, input, input-group, input-otp, item, kbd, label, marker, menubar, message, message-scroller, native-select, navigation-menu, pagination, popover, progress, questionnaire, radio-group, resizable, scroll-area, select, separator, sheet, sidebar, skeleton, slider, spinner, switch, tab-bar, table, tabs, textarea, theme, toast, toggle, toggle-group, tooltip, wheel-picker, widget
Hooks: use-message-scroller, use-mobile, use-platform, use-questionnaire, use-scrollbar-gap
Patterns
Patterns combine several components into one ready-made piece, such as a date and time picker. They are not added by init or add --all; add them by name, or all at once:
npx @esuiss/ui add patterns date-picker
npx @esuiss/ui add patterns --allThe components a pattern uses are added with it.
Interactions
Interactions add a behavior to something you already have, such as swiping a row to reveal actions. They are optional extras, so they are not added by init or add --all; add them by name, or all at once:
npx @esuiss/ui add interactions swipe-actions
npx @esuiss/ui add interactions --allThe components an interaction uses are added with it.
Charts
Charts are ready-made charts built on the chart component. They are optional extras, so they are not added by init or add --all; add them by name, or all at once:
npx @esuiss/ui add charts <name>
npx @esuiss/ui add charts --allThe chart component and anything else a chart uses are added with it.
Blocks
Blocks are ready-made sections of a page built from the components. They are optional extras, so they are not added by init or add --all; add them by name, or all at once:
npx @esuiss/ui add blocks <name>
npx @esuiss/ui add blocks --allThe components a block uses are added with it.
Troubleshooting
- "Tailwind CSS 4.2 or later is required": upgrade with
npm install tailwindcss@latestand runinitagain. - "No
@/*import alias found": runinitfirst, or add"paths": { "@/*": ["./src/*"] }tocompilerOptionsintsconfig.json. - A newly published version is not found: npm can take a few minutes to make new versions available. Run
npx --prefer-online @esuiss/ui@latest ....
License
MIT
