@somaui/ui
v1.0.1
Published
A Shadcn inspired version of RizzUI
Downloads
18
Maintainers
Readme
Why SomaUI?
SomaUI is a Shadcn-inspired version of RizzUI. Its designed for developers who want beautiful, accessible components. The cli is available at https://www.npmjs.com/package/@somaui/cli
Features
- 🎨 Modern Design - Clean, minimal components that look great out of the box
- ⚡️ Lightweight - Tree-shakeable exports, import only what you need
- 🎯 Type-Safe - Full TypeScript support with excellent autocomplete
- ♿️ Accessible - Built with accessibility in mind, following WCAG guidelines
- 🎭 Customizable - Easy theming with CSS variables, no config files needed
- 🌙 Dark Mode - Built-in dark mode support that just works
- 📦 Zero Config - Works with Tailwind CSS v4's CSS-first approach
- 🚀 React 19 Ready - Built for the latest React features
Quick Start
Requirements
- React: 19.1.0 or higher
- Node.js: 18.0.0 or higher
- Package Manager: pnpm 9.0.0+ (recommended), npm, yarn, or bun
Installation
Install SomaUI and its peer dependencies:
# Using pnpm (recommended)
pnpm add @somaui/ui @headlessui/react @floating-ui/react @tailwindcss/postcss tailwind-variants tailwind-merge
# Using npm
npm install @somaui/ui @headlessui/react @floating-ui/react @tailwindcss/postcss tailwind-variants tailwind-merge
# Using yarn
yarn add @somaui/ui @headlessui/react @floating-ui/react @tailwindcss/postcss tailwind-variants tailwind-merge
# Using bun
bun add @somaui/ui @headlessui/react @floating-ui/react @tailwindcss/postcss tailwind-variants tailwind-mergeConfigure Tailwind CSS v4
SomaUI uses Tailwind CSS v4 with CSS-first configuration. No tailwind.config.js needed! 🎉
Create or update your app/globals.css (or src/app/globals.css for Next.js):
@import 'tailwindcss';
@source '../../node_modules/@somaui/ui/dist';
@plugin '@tailwindcss/forms';
/* ⚠️ Required: Dark mode variant */
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
:root {
/* ⚠️ Required: SomaUI color variables */
--background: oklch(100% 0 0); /* #ffffff */
--foreground: oklch(40.17% 0 0); /* #484848 */
--muted: oklch(91.58% 0 0); /* #e3e3e3 */
--muted-foreground: oklch(66% 0 0); /* #929292 */
/* Border tokens */
--border-radius: 0.5rem; /* 8px */
--border-width: 0.0625rem; /* 1px */
--border-color: oklch(90.37% 0 0); /* #dfdfdf */
/* Text tokens */
--text-primary: oklch(0% 0 0); /* #000000 */
--text-secondary: oklch(40.17% 0 0); /* #484848 */
/* Primary colors */
--primary-lighter: oklch(91.58% 0 0); /* #e3e3e3 */
--primary: oklch(17.76% 0 0); /* #111111 */
--primary-dark: oklch(0% 0 0); /* #000000 */
--primary-foreground: oklch(100% 0 0); /* #ffffff */
/* Secondary colors */
--secondary-lighter: oklch(91.99% 0.0386 276.02); /* #dde3ff */
--secondary: oklch(50.51% 0.2633 276.95); /* #4e36f5 */
--secondary-dark: oklch(45.41% 0.2431 277.06); /* #432ad8 */
--secondary-foreground: oklch(100% 0 0); /* #ffffff */
/* Danger colors */
--red-lighter: oklch(89.99% 0.0393 14); /* #f7d4d6 */
--red: oklch(59.6% 0.2445 29.23); /* #ee0000 */
--red-dark: oklch(51.71% 0.2121 29.2338); /* #c50000 */
/* Warning colors */
--orange-lighter: oklch(95.67% 0.0452 84.5695); /* #ffefcf */
--orange: oklch(78.37% 0.1587 72.99); /* #f5a623 */
--orange-dark: oklch(54.83% 0.1339 53.95); /* #ab570a */
/* Info colors */
--blue-lighter: oklch(91.66% 0.0404 257.5078); /* #d3e5ff */
--blue: oklch(57.31% 0.2144 258.25); /* #0070f3 */
--blue-dark: oklch(51.58% 0.1888 258.27); /* #0761d1 */
/* Success colors */
--green-lighter: oklch(92.79% 0.086 155.61); /* #b9f9cf */
--green: oklch(64.01% 0.1776 148.74); /* #11a849 */
--green-dark: oklch(53.79% 0.1441 149.52); /* #11843c */
}
/* ⚠️ Required: Dark theme */
[data-theme='dark'] {
--background: oklch(14.11% 0.0112 275.23); /* #08090e */
--foreground: oklch(90.37% 0 0); /* #dfdfdf */
--muted: oklch(32.11% 0 0); /* #333333 */
--muted-foreground: oklch(51.03% 0 0); /* #666666 */
--border-color: oklch(91.58% 0 0); /* #e3e3e3 */
--text-primary: oklch(100% 0 0); /* #ffffff */
--text-secondary: oklch(51.03% 0 0); /* #666666 */
--primary-lighter: oklch(25.2% 0 0); /* #222222 */
--primary: oklch(95.81% 0 0); /* #f1f1f1 */
--primary-dark: oklch(100% 0 0); /* #ffffff */
--primary-foreground: oklch(0% 0 0); /* #000000 */
--secondary-lighter: oklch(26.35% 0.1154 280.96); /* #1f165a */
--secondary-dark: oklch(85.2% 0.0733 276.238); /* #c1cbff */
--red-lighter: oklch(27.08% 0.1111 29.23); /* #500000 */
--red-dark: oklch(86.69% 0.0714 18.6304); /* #ffc1c1 */
--orange-lighter: oklch(28.29% 0.0698 49.34); /* #441d04 */
--orange-dark: oklch(93.15% 0.1175 98.83); /* #fcea8b */
--blue-lighter: oklch(32.05% 0.0873 254.4); /* #0d335e */
--blue-dark: oklch(90.53% 0.0611 225.72); /* #b5e9ff */
--green-lighter: oklch(27.23% 0.0672 152.71); /* #033016 */
--green-dark: oklch(92.79% 0.086 155.61); /* #b9f9cf */
}
/* ⚠️ Required: Map CSS variables to Tailwind colors */
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--border-radius: var(--border-radius);
--color-border: var(--border-color);
--color-text-primary: var(--text-primary);
--color-text-secondary: var(--text-secondary);
--color-primary-lighter: var(--primary-lighter);
--color-primary: var(--primary);
--color-primary-dark: var(--primary-dark);
--color-primary-foreground: var(--primary-foreground);
--color-secondary-lighter: var(--secondary-lighter);
--color-secondary: var(--secondary);
--color-secondary-dark: var(--secondary-dark);
--color-secondary-foreground: var(--secondary-foreground);
--color-red-lighter: var(--red-lighter);
--color-red: var(--red);
--color-red-dark: var(--red-dark);
--color-orange-lighter: var(--orange-lighter);
--color-orange: var(--orange);
--color-orange-dark: var(--orange-dark);
--color-blue-lighter: var(--blue-lighter);
--color-blue: var(--blue);
--color-blue-dark: var(--blue-dark);
--color-green-lighter: var(--green-lighter);
--color-green: var(--green);
--color-green-dark: var(--green-dark);
}
/* ----------------------------------- */
/* Autofill Styles */
/* ----------------------------------- */
/* Override browser autofill background for all input components */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active,
textarea:-webkit-autofill,
textarea:-webkit-autofill:hover,
textarea:-webkit-autofill:focus,
textarea:-webkit-autofill:active {
-webkit-box-shadow: 0 0 0 1000px transparent inset !important;
-webkit-text-fill-color: inherit !important;
transition: background-color 5000s ease-in-out 0s;
caret-color: inherit;
}
/* Firefox autofill */
input:-moz-autofill,
input:-moz-autofill:hover,
input:-moz-autofill:focus,
input:-moz-autofill:active,
textarea:-moz-autofill,
textarea:-moz-autofill:hover,
textarea:-moz-autofill:focus,
textarea:-moz-autofill:active {
background-color: transparent !important;
color: inherit !important;
transition: background-color 5000s ease-in-out 0s;
}PostCSS Configuration
Update your postcss.config.mjs:
const config = {
plugins: {
'@tailwindcss/postcss': {},
},
};
export default config;Usage
That's it! You're ready to use SomaUI components. Import them individually for optimal tree-shaking:
import { Button } from '@somaui/ui/button';
import { Input } from '@somaui/ui/input';
export default function App() {
return (
<div>
<Button>Click me</Button>
<Input placeholder="Enter your name" />
</div>
);
}💡 Tip: Import from general path (e.g., @somaui/ui) instead of the specific point for barrell import. Note that this is not condusive for tree-shaking and smaller bundle sizes.
Theme Support
Theme Provider Setup
SomaUI works seamlessly with next-themes for theme switching:
'use client';
import { ThemeProvider as NextThemeProvider } from 'next-themes';
export function ThemeProvider({ children }: React.PropsWithChildren<{}>) {
return (
<NextThemeProvider
enableSystem
defaultTheme="system"
disableTransitionOnChange
>
{children}
</NextThemeProvider>
);
}Theme Switcher Component
Here's a ready-to-use theme switcher:
'use client';
import React from 'react';
import { useTheme } from 'next-themes';
import { ActionIcon } from '@somaui/ui/action-icon';
import { Dropdown } from '@somaui/ui/dropdown';
import { MoonIcon, SunIcon } from '@heroicons/react/24/outline';
export function ThemeSwitcher() {
const { setTheme } = useTheme();
return (
<Dropdown>
<Dropdown.Trigger>
<ActionIcon variant="outline">
<SunIcon className="size-5 dark:hidden" />
<MoonIcon className="absolute hidden size-5 dark:block" />
<span className="sr-only">Toggle theme</span>
</ActionIcon>
</Dropdown.Trigger>
<Dropdown.Menu>
<Dropdown.Item onClick={() => setTheme('light')}>Light</Dropdown.Item>
<Dropdown.Item onClick={() => setTheme('dark')}>Dark</Dropdown.Item>
<Dropdown.Item onClick={() => setTheme('system')}>System</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
);
}Documentation
📚 Full documentation available at somaui.com
Explore component APIs, examples, and best practices in our comprehensive docs.
Contributing
We love contributions! Whether it's fixing bugs, adding features, or improving documentation, your help makes SomaUI better for everyone.
- 🐛 Found a bug? Open an issue
- 💡 Have an idea? Start a discussion
- 🔧 Want to contribute? Check out our Contributing Guidelines
License
Licensed under the MIT License - feel free to use SomaUI in your projects! 🎉
