npm package: @raxrai/stylelab-ui
Description: Themeable UI library with 8 themes: Minimal, Night, Glass, Neubrutal, Clay, Cyberpunk, Retro, Motion
Latest version: 0.4.1

# StyleLab UI

A themeable, accessible React UI library with **8 themes**: Minimal, Night, Glass, Neubrutal, Clay, Cyberpunk, Retro, and Motion. Built with Tailwind CSS v4, CVA, and Lucide icons. Zero extra dependencies at install time — only **React** and **React DOM** as peers; everything else is bundled.

**Live site (demo, gallery, docs):** [https://stylelab.rajarai.com](https://stylelab.rajarai.com)

---

## Installation

**Next.js or any existing React app** (you already have React):

```bash
npm install @raxrai/stylelab-ui
```

**New project without React** (e.g. empty folder):

```bash
npm install @raxrai/stylelab-ui react react-dom
```

The package lists **react** and **react-dom** as peer dependencies (>=18). Next.js and Create React App already include them, so you don’t install them again. Other runtime deps (cva, clsx, tailwind-merge, lucide-react, framer-motion) are bundled, so no extra install is required.

### 1. Tailwind + styles

Your app must use **Tailwind CSS v4**. Import StyleLab design tokens in your global CSS **after** Tailwind:

```css
@import "tailwindcss";
@import "@raxrai/stylelab-ui/styles";
```

### 2. ThemeProvider

Wrap your app so components receive the active theme:

```tsx
import { ThemeProvider } from "@raxrai/stylelab-ui";

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <ThemeProvider defaultTheme="minimal">{children}</ThemeProvider>
      </body>
    </html>
  );
}
```

Optional props: `storageKey` (default `"stylelab-theme"`) for the localStorage key; `persistTheme` (default `true`). When `persistTheme={false}`, the provider does not read or write localStorage (theme is fixed to `defaultTheme` unless you change it in memory).

### 3. Use components

```tsx
import { Button, Card, useTheme } from "@raxrai/stylelab-ui";

export function MyPage() {
  const { theme, setTheme } = useTheme();
  return (
    <div>
      <Button variant="primary">Click me</Button>
      <Card>Content</Card>
    </div>
  );
}
```

**Themes:** `minimal` | `night` | `glass` | `neubrutal` | `clay` | `cyberpunk` | `retro` | `motion`

---

## What’s in the package

**Components:** Accordion, Alert, Avatar, AvatarGroup, Badge, BentoGrid, Breadcrumbs, Button, Calendar, Card, CommandPalette, DashboardShell, DataTable, DocumentAccordion, Dropdown, Flashcard, GraphicCard, Input, Modal, Navbar, PricingCard, ProgressBar, SectionHeader, Sidebar, Skeleton, Slider, StatsCard, Tabs, Terminal, Timeline, Toast, Toggle, Tooltip.

**Global:** ThemeProvider, useTheme, getThemeClass, THEMES, StyleLabTheme, cn, mergeRefs, getNextListIndex, useFocusTrap, useClickOutside, useKeyboardNavigation. Component roots support **ref** forwarding and **style** / **className** (consumer `className` applied last for overrides).

- **See all components and try themes:** [stylelab.rajarai.com](https://stylelab.rajarai.com)
- **Full API (props, types, hooks):** [docs/COMPONENTS.md](docs/COMPONENTS.md) in this repo.

---

## Component API (overview)

### Button

| Prop        | Type                    | Default    | Description                          |
|------------|--------------------------|------------|--------------------------------------|
| `as`       | `"button"` \| `"a"`      | `"button"` | Polymorphic root element             |
| `variant`  | `"primary"` \| `"secondary"` \| `"ghost"` | `"primary"` | Visual variant                       |
| `size`     | `"sm"` \| `"md"` \| `"lg"` | `"md"`     | Size                                 |
| `isLoading`| `boolean`                | `false`    | Shows spinner and disables            |
| `leftIcon` | `ReactNode`              | —          | Icon before children                 |
| `rightIcon`| `ReactNode`              | —          | Icon after children                  |
| `className`| `string`                 | —          | Merged with CVA classes              |
| `ref`      | `ForwardedRef`           | —          | Forwarded to root DOM element       |

### Input

| Prop         | Type       | Default | Description                    |
|-------------|------------|---------|--------------------------------|
| `label`     | `string`   | —       | Accessible label               |
| `error`     | `string`   | —       | Error message (role="alert")   |
| `helperText`| `string`   | —       | Hint below input               |
| `prefix`    | `ReactNode`| —       | Left addon                     |
| `suffix`    | `ReactNode`| —       | Right addon                    |
| `size`      | `"sm"` \| `"md"` \| `"lg"` | `"md"` | Size (CVA)                     |
| `className` | `string`   | —       | Merged with CVA                |

### Toggle

| Prop              | Type       | Default | Description                    |
|-------------------|------------|---------|--------------------------------|
| `checked`         | `boolean`  | —       | Controlled state               |
| `defaultChecked`  | `boolean`  | `false` | Uncontrolled initial state     |
| `onCheckedChange`| `(v: boolean) => void` | — | Callback on change             |
| `aria-label`      | `string`   | `"Toggle"` | Accessible name             |

Uses a hidden `<input type="checkbox">` for semantics and form compatibility.

### Badge

| Prop       | Type     | Default   | Description          |
|------------|----------|-----------|----------------------|
| `variant`  | `"default"` \| `"success"` \| `"warning"` \| `"error"` | `"default"` | Style variant |
| `dot`      | `boolean`| `false`   | Leading dot indicator |

### Slider

| Prop            | Type     | Default | Description              |
|-----------------|----------|---------|--------------------------|
| `min` / `max`   | `number` | `0` / `100` | Range bounds        |
| `step`          | `number` | `1`     | Step value               |
| `value`         | `number` | —       | Controlled value         |
| `defaultValue`  | `number` | `50`    | Uncontrolled initial     |
| `onValueChange` | `(v: number) => void` | — | Change callback    |
| `size`          | `"sm"` \| `"md"` \| `"lg"` | `"md"` | Track size     |

### Tabs

| Prop           | Type                    | Default | Description                |
|----------------|-------------------------|---------|----------------------------|
| `items`        | `{ label, value, content }[]` | —  | Tab list                   |
| `value`        | `string`                | —       | Controlled active tab      |
| `defaultValue` | `string`                | —       | Uncontrolled initial       |
| `onValueChange`| `(value: string) => void` | —     | Callback on tab change     |

Arrow keys (Left/Right) move focus and change tab; theme from context or `theme` prop.

### Card

| Prop         | Type       | Default | Description              |
|--------------|------------|---------|--------------------------|
| `header`     | `ReactNode`| —       | Optional header slot      |
| `footer`     | `ReactNode`| —       | Optional footer slot      |
| `padding`    | `"none"` \| `"sm"` \| `"md"` | `"md"` | Inner padding        |
| `isHoverable`| `boolean`  | `false` | Hover shadow transition   |

### Accordion

| Prop           | Type     | Default | Description                 |
|----------------|----------|---------|-----------------------------|
| `items`        | `{ id, title, content }[]` | — | Items                       |
| `allowMultiple`| `boolean`| `false` | Allow multiple panels open  |

Uses manual height animation (no external libs).

### Modal

| Prop     | Type       | Default | Description                |
|----------|------------|---------|----------------------------|
| `open`   | `boolean`  | —       | Visibility                 |
| `onClose`| `() => void` | —     | Close handler              |
| `title`  | `string`   | —       | Optional title (and close) |
| `size`   | `"sm"` \| `"md"` \| `"lg"` | `"md"` | Max width            |

Implements focus trap and scroll lock when open.

### Tooltip

| Prop        | Type     | Default | Description        |
|-------------|----------|---------|--------------------|
| `content`   | `ReactNode` | —     | Tooltip body        |
| `placement` | `"top"` \| `"bottom"` \| `"left"` \| `"right"` | `"top"` | Position |

### Dropdown

**Two usage patterns:** (1) **Default trigger** — omit `trigger`; the component shows a button with the selected label or `placeholder`. (2) **Custom trigger** — pass `trigger` with your own element (e.g. a Button or icon); the same list opens on activation.

| Prop           | Type                    | Default | Description              |
|----------------|-------------------------|---------|--------------------------|
| `trigger`     | `ReactNode`             | —       | Custom trigger; omit for default (placeholder/selected label) |
| `placeholder` | `string`                | `"Select…"` | Shown when no value selected (default trigger only) |
| `items`       | `{ value, label, disabled? }[]` | — | Options               |
| `value` / `defaultValue` | `string`        | —       | Controlled / initial     |
| `onValueChange`| `(value: string) => void` | —     | Selection callback       |
| `disabled`     | `boolean`       | `false` | When true, does not open; trigger is non-interactive |

Keyboard: Enter/Space to open, Arrows to move, Enter to select, Escape to close.

---

## Utilities & hooks

```tsx
import { cn, mergeRefs, useFocusTrap, useClickOutside, useKeyboardNavigation, getNextListIndex } from "@raxrai/stylelab-ui";
```

- **cn(...)** – clsx + tailwind-merge for class names.
- **mergeRefs(...refs)** – Combines multiple refs (e.g. forwarded ref + internal ref). Use when a component needs to forward a ref and also attach its own ref.
- **useFocusTrap(active, options?)** – Traps focus in a container ref (e.g. Modal).
- **useClickOutside(ref(s), handler, options?)** – Fires when click is outside ref(s).
- **useKeyboardNavigation({ onEscape, onArrowLeft, onArrowRight, ... })** – Document-level key handlers.
- **getNextListIndex(current, direction, length)** – Index for arrow-key list/tab navigation.

---

## Changelog

### [0.4.1]

- **Navbar** — Mobile menu: click-outside ref now wraps both the bar and the dropdown panel so taps on nav links or buttons (e.g. Log out) run the action instead of only closing the menu. Mobile panel uses an explicit opaque theme-aware background in all themes (readable with glass/clay). Optional `mobileMenuExtra` prop renders content at the bottom of the hamburger overlay (e.g. a Log out button); clicking it closes the menu.

### [0.4.0]

- **Overrides + refs** — Component roots consistently support `style`, consumer `className` overrides (consumer class last), and ref forwarding across the library.
- **Theme fixes** — Card no longer forces text color; Button `ghost` uses `text-inherit` so it can inherit parent text color.
- **Accessibility** — ProgressBar now supports ARIA progress attributes; Toast supports Escape-to-dismiss; Tooltip uses `aria-describedby` when visible.
- **Exports** — `mergeRefs` and common component prop types are re-exported from the package entry.

### [0.3.2]

- **ThemeProvider** — Optional `persistTheme` prop (default `true`). When `persistTheme={false}`, the provider does not read from or write to localStorage: theme stays at `defaultTheme` unless changed in memory. Use for fixed-theme pages (e.g. landing, docs) or nested providers where only the inner tree should persist. Optional `storageKey` (default `"stylelab-theme"`) still applies when `persistTheme` is true.

### [0.3.1]

- **Dropdown** — `disabled` prop: when true, the dropdown does not open and the trigger is non-interactive (opacity, cursor, no popover). Use for empty or loading state.

### [0.2.0]

- **Navbar** — Optional responsive API: pass `logo`, `items` (`{ href, label }[]`), and `right` (e.g. theme switcher). On small screens nav links become a hamburger menu that opens as an overlay (no layout shift), with the theme’s navbar background; glass and clay use an opaque panel so content doesn’t show through. Desktop shows full nav row. Menu closes on link click or outside click. You can still pass `children` for a custom layout. Exported types: `NavbarItem`, `NavbarProps`.
- **ThemeProvider** — Restores theme from `localStorage` on mount; `setTheme` persists the new theme. Optional `storageKey` prop (default `"stylelab-theme"`).
- **Dropdown** — Popover is portaled to `document.body` and positioned with open-up/down logic. Optional `trigger` and `placeholder` (default trigger shows selected/placeholder label). `classNames` for trigger, popover, and item. Theme-aware hover/focus styles. Exported types: `DropdownItem`, `DropdownProps`, `DropdownClassNames`.
- **Card** — `classNames` for root, header, body, footer. Cyberpunk no longer uses the clip-path “glitch cut” (avoids weird look on different backgrounds); neon border and glow remain. Exported types: `CardProps`, `CardClassNames`.
- **Badge** — `bgOverride` and `textOverride` (e.g. hex) when you need specific colors.
- **ProgressBar** — `trackColor` and `activeColor` overrides (e.g. hex).
- **DashboardShell** — Header, nav links, and mobile panel use theme-based styling (no `dark:`; works with StyleLab themes).
- **GraphicCard** — In night and cyberpunk themes, content text inherits the card’s text color so children with hardcoded dark text still display correctly.
- **Docs** — Installation: Next.js/existing React apps install the package only; add `react`/`react-dom` only for new projects without React.
- **Types** — `StyleLabTheme`, `ComponentName`, `ThemeRecommendation` from `lib/types`; component prop types `NavbarItem`, `NavbarProps`, `CardProps`, `CardClassNames`, `DropdownItem`, `DropdownProps`, `DropdownClassNames` re-exported from the package.

---

## Links

- **Live demo & docs:** [https://stylelab.rajarai.com](https://stylelab.rajarai.com)
- **Full component API (this repo):** [docs/COMPONENTS.md](docs/COMPONENTS.md)