# VXUI React

> A pure-CSS, zero-dependency React UI library for building admin dashboards and operator interfaces. v1.3.13.

> ℹ️ This file (`llms.txt`) is included in the npm package. AI tools can read it directly to obtain component documentation and usage guides.

VXUI React provides layout shells, navigation, form controls, data display, overlays, and mobile components. It relies solely on CSS custom properties (design tokens) for theming — no Tailwind, no CSS-in-JS. Radix UI primitives are used for accessibility-critical components (dialogs, menus, tooltips, etc.).

## Installation

```bash
npm install vxui-react
```

Import the base stylesheet once at your app entry:

```tsx
import 'vxui-react/styles';
```

## Core Layout: AppShell

`AppShell` is the top-level layout component. It composes a collapsible sidebar, sticky topbar, and scrollable content area.

```tsx
import { AppShell } from 'vxui-react';

<AppShell
  brand="My App"
  brandIcon={<img src="/logo.svg" alt="" />}
  title="Dashboard"
  navSections={[
    {
      key: 'main',
      items: [
        { key: 'home', label: 'Home', icon: <HomeIcon />, href: '/' },
        { key: 'reports', label: 'Reports', icon: <ChartIcon />, href: '/reports' },
      ],
    },
  ]}
  headerActions={<UserMenu />}
  sidebarWidth={280}
>
  <p>Page content</p>
</AppShell>
```

### AppShell Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| brand | string | "VXUI" | Brand/product name shown in the sidebar header |
| brandCaption | string | — | Secondary subtitle below the brand name |
| brandIcon | ReactNode | — | Logo element rendered in the sidebar header |
| title | string | — | Page title shown in the topbar |
| description | string | — | Page subtitle shown below the topbar title |
| breadcrumb | ReactNode | — | Breadcrumb element rendered in the topbar |
| navSections | AppShellNavSection[] | — | Structured nav tree grouped by sections. Preferred over navItems |
| navItems | AppShellNavItem[] | — | Flat nav list, auto-wrapped in a single unnamed section |
| sidebarCollapsed | boolean | false | Collapse sidebar to icon-only rail mode |
| sidebarWidth | number \| string | 240px | Custom sidebar width. Number = px (e.g. 280), string = CSS value (e.g. "18rem") |
| density | "comfortable" \| "compact" | — | Layout density. "compact" tightens vertical rhythm for high-density UIs |
| headerActions | ReactNode | — | Slot for right-aligned topbar actions |
| sidebarFooter | ReactNode | — | Slot rendered at the bottom of the sidebar |
| mobileNavOpen | boolean | false | Controls whether the mobile nav overlay is visible |
| onSidebarToggle | () => void | — | Called when the collapse/expand button is clicked |
| onMobileNavToggle | () => void | — | Called when the mobile overlay toggle is clicked |
| children | ReactNode | **required** | Main content area rendered inside ShellContent |

### AppShellNavSection

```ts
interface AppShellNavSection {
  key: string;
  label?: string;            // section heading text
  items: AppShellNavItem[];
}
```

### AppShellNavItem

```ts
interface AppShellNavItem {
  key: string;
  label: string;
  icon?: ReactNode;
  href?: string;
  active?: boolean;
  disabled?: boolean;
  defaultOpen?: boolean;     // for expandable items with children
  children?: AppShellNavItem[];  // nest to create expandable sub-menus
  onClick?: (e: React.MouseEvent) => void;
}
```

## Shell (low-level)

`Shell` is the underlying layout primitive used by AppShell. Use it for fully custom layouts.

```tsx
import { Shell, ShellSidebar, ShellContent } from 'vxui-react';

<Shell sidebarWidth={300} density="compact">
  <ShellSidebar>{/* nav content */}</ShellSidebar>
  <ShellContent>{/* page content */}</ShellContent>
</Shell>
```

### Shell Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| collapsed | boolean | false | Collapse sidebar to rail mode |
| mobileNavOpen | boolean | false | Show mobile nav overlay |
| density | "comfortable" \| "compact" | — | Layout density |
| sidebarWidth | number \| string | 240px | Custom sidebar width |
| className | string | — | Additional CSS class |
| children | ReactNode | **required** | Layout children |

## Dialog

Modal dialog component built on Radix UI. Supports size presets, placement options, and fullscreen mode for mobile devices.

```tsx
import { Dialog, DialogClose } from 'vxui-react';
import { Button } from 'vxui-react';

<Dialog
  trigger={<Button>Open Dialog</Button>}
  title="Dialog Title"
  description="Optional description text."
  size="md"         // 'sm' | 'md' | 'lg' | 'xl' | 'full'
  placement="center" // 'center' | 'top' | 'bottom' | etc.
  fullscreen={false} // Enable fullscreen on mobile devices
  scrollable={true}
  closable={true}
>
  <p>Dialog content goes here.</p>
  {{/* footer */}}
  <DialogClose asChild><Button variant="outline">Close</Button></DialogClose>
</Dialog>
```

### Dialog Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| trigger | ReactNode | **required** | Element that opens the dialog |
| title | string | **required** | Dialog title |
| description | string | — | Optional description below the title |
| children | ReactNode | **required** | Dialog body content |
| footer | ReactNode | — | Footer content (e.g., action buttons) |
| size | 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full' | 'md' | Width preset |
| placement | 'center' \| 'top' \| 'right' \| 'bottom' \| 'left' \| 'top-left' \| 'top-right' \| 'bottom-left' \| 'bottom-right' \| 'top-half' \| 'right-half' \| 'bottom-half' \| 'left-half' | 'center' | Position on screen (desktop only) |
| padding | 'none' \| 'sm' \| 'md' \| 'lg' | 'md' | Inner padding preset |
| scrollable | boolean | true | Enable body scroll when content overflows |
| closable | boolean | true | Show the close (×) button |
| fullscreen | boolean | false | When true, the dialog occupies the full viewport (ideal for mobile) |
| className | string | — | Additional CSS class |
| defaultOpen | boolean | false | Whether the dialog is open by default |
| open | boolean | — | Controlled open state |
| onOpenChange | (open: boolean) => void | — | Open state change handler |
| onConfirm | () => void | — | Callback when confirm button is clicked; enables confirm/cancel footer |
| onCancel | () => void | — | Callback when cancel button is clicked |
| confirmLabel | string | "Confirm" | Label for the confirm button |
| cancelLabel | string | "Cancel" | Label for the cancel button |
| confirmVariant | 'solid' \| 'danger' | 'solid' | Visual variant of the confirm button |

## Component Index

### Layout & Navigation
- **AppShell** — Full-featured app shell with sidebar, topbar, and content area
- **Shell / ShellSidebar / ShellContent** — Low-level layout primitives
- **NavigationMenu** — Horizontal navigation bar with dropdown menus
- **Breadcrumb** — Breadcrumb trail with separator support
- **Pagination** — Page navigation with first/last/prev/next controls
- **Tabs** — Horizontal tab strip with panel content
- **Menubar** — Desktop-style menu bar
- **Stepper** — Multi-step wizard indicator

### Buttons & Actions
- **Button** — Primary action button with variants (default, outline, ghost, destructive) and sizes (sm, md, lg)
- **Toggle** — Two-state toggle button
- **SegmentedControl** — Mutually exclusive option group
- **DropdownMenu** — Trigger + popover menu list (Radix UI)
- **ContextMenu** — Right-click context menu (Radix UI)

### Forms & Inputs
- **Input** — Text input with label, error, and description support
- **Textarea** — Multi-line text input
- **Checkbox** — Single checkbox or checkbox group
- **Radio** — Radio button group
- **Switch** — Toggle switch
- **Slider** — Range slider
- **NumberInput** — Numeric input with increment/decrement controls
- **TagInput** — Multi-tag token input
- **Rating** — Star rating input
- **DatePicker** — Calendar-based date picker
- **ColorPicker** — Color swatch/hex picker
- **Select** — Searchable single-select dropdown with custom overlay behavior
- **MultiSelect** — Searchable multi-select with tag display and "+N more" badge
- **PinInput** — PIN / OTP code input with auto-advance, paste support, and mask mode
- **TimePicker** — Time selection with hours, minutes, and optional seconds columns
- **Form** — Form wrapper with validation helpers

### Overlays & Popovers
- **Dialog** — Modal dialog (Radix UI)
- **Sheet** — Slide-in panel (drawer) from any edge
- **Popover** — Floating content anchored to a trigger (Radix UI)
- **Tooltip** — Short contextual hover label (Radix UI)
- **HoverCard** — Rich hover preview card (Radix UI)

### Feedback & Status
- **Toast** — Ephemeral notification (info / success / warning / danger tones)
- **Notification** — Global notification stack with provider/hook pattern, auto-dismiss, and placement options
- **Alert** — Inline alert banner (info / success / warning / danger)
- **Progress** — Linear progress bar (determinate and indeterminate)
- **Spinner** — Loading spinner
- **Skeleton** — Placeholder loading skeleton

### Data Display
- **Descriptions** — Key-value pair display in horizontal/vertical table layout
- **Table** — Sortable, filterable data table with mobile card layout, sticky filter bar, and fullscreen search
- **Badge** — Inline status label with tone variants
- **Avatar** — User avatar with fallback initials
- **Card** — Content container card
- **Accordion** — Collapsible content sections (Radix UI)
- **Calendar** — Month calendar view
- **Image** — Lazy-loading image with placeholder, fallback, radius presets, and click-to-preview lightbox
- **Carousel** — Horizontal content carousel
- **TreeView** — Hierarchical tree list
- **Timeline** — Vertical timeline list
- **ScrollArea** — Custom-styled scrollable container (Radix UI)
- **Resizable** — Resizable panel layout

### Utility
- **CodeBlock** — Syntax-highlighted code block (Prism.js)
- **CommandPalette** — Global keyboard command search
- **Result** — Status result page with icon, title, description, and actions (success / error / warning / info / forbidden / not-found / server-error)
- **EmptyState** — Empty content placeholder with action
- **FileUpload** — Drag-and-drop file upload area
- **LanguageSwitcher** — Locale toggle button
- **ThemeProvider** — Theme context provider with light/dark mode, custom token overrides, and localStorage persistence
- **Responsive** — Conditional rendering based on viewport breakpoint (desktop / tablet / mobile)
- **Article** — Structured article layout with header, body, sections, props table, pager, and stats grid
- **Separator** — Horizontal or vertical rule
- **Heading / Text / Label** — Typography primitives

### Mobile-specific (vxui-react/mobile)
- **MobileApp** — Root mobile layout
- **MobileShell** — Mobile screen wrapper
- **MobileDrawer** — Bottom-sheet style drawer
- **BottomNav** — Mobile tab bar
- **MobileList** — List with dividers optimized for touch
- **ActionSheet** — iOS-style action sheet

## Theming (CSS Custom Properties)

Override any token in your own stylesheet or via a `<style>` block:

```css
:root {
  --vx-primary: #7c3aed;        /* primary action color */
  --vx-primary-strong: #6d28d9; /* hover/pressed state */
  --vx-primary-soft: rgba(124, 58, 237, 0.1);

  --vx-secondary: #64748b;      /* secondary/muted color */
  --vx-success: #10b981;
  --vx-warning: #f59e0b;
  --vx-danger: #ef4444;

  --vx-bg: #f8fafc;             /* page background */
  --vx-bg-accent: #f1f5f9;      /* subtle accent background */
  --vx-surface: #ffffff;        /* card/panel surfaces */
  --vx-border: #e2e8f0;         /* default border */
  --vx-border-strong: #cbd5e1;

  --vx-text: #0f172a;           /* primary text */
  --vx-text-secondary: #64748b;
  --vx-text-muted: #94a3b8;

  --vx-radius-sm: 8px;
  --vx-radius: 10px;
  --vx-radius-lg: 14px;

  --vx-sidebar-width: 240px;        /* sidebar width (default) */
  --vx-sidebar-width-collapsed: 72px;
  --vx-header-height: 92px;
}
```

Dark mode tokens are applied automatically when `<html>` has `class="dark"` or `data-theme="dark"`.

## Key Design Principles

1. **No global side-effects** — CSS is scoped to `.vx-*` class names. VXUI will not override your existing styles.
2. **Token-first theming** — All visual decisions (color, radius, spacing) are CSS variables. Override any of them to rebrand in minutes.
3. **Accessible by default** — Overlay and interactive components are built on Radix UI primitives, providing keyboard navigation and ARIA semantics out of the box.
4. **Mobile-first responsive** — Sidebar collapses to an overlay on narrow viewports. Mobile-specific components live in a separate import path.
5. **No runtime CSS injection** — Styles ship as a single static stylesheet. No flash-of-unstyled-content from runtime injection.

## Descriptions

Display key-value pairs in a structured table layout. Supports horizontal (label left, value right) and vertical (label top, value bottom) modes.

```tsx
import { Descriptions } from 'vxui-react';

<Descriptions
  title="User Info"
  layout="horizontal"
  column={3}
  size="md"
  bordered
  items={[
    { label: 'Name', children: 'Alice' },
    { label: 'Email', children: 'alice@example.com' },
    { label: 'Role', children: 'Admin', span: 1 },
  ]}
/>
```

### Descriptions Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| items | DescriptionsItem[] | **required** | List of description items |
| layout | 'horizontal' \| 'vertical' | 'horizontal' | Layout direction |
| column | number | 3 (h) / 1 (v) | Number of columns per row |
| size | 'sm' \| 'md' \| 'lg' | 'md' | Size preset controlling spacing and font-size |
| bordered | boolean | true | Show border around the descriptions |
| title | ReactNode | — | Title shown above the descriptions |
| extra | ReactNode | — | Extra content rendered next to the title |

## Image

Lazy-loading image component with placeholder, error fallback, radius presets, and optional click-to-preview lightbox.

```tsx
import { Image } from 'vxui-react';

<Image
  src="/photo.jpg"
  alt="Landscape"
  width={400}
  height={300}
  radius="md"
  fit="cover"
  preview
  caption="A beautiful landscape"
/>
```

### Image Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| src | string | **required** | Image source URL |
| alt | string | '' | Alt text |
| width | string \| number | — | Fixed width (CSS value or px number) |
| height | string \| number | — | Fixed height (CSS value or px number) |
| radius | 'none' \| 'sm' \| 'md' \| 'lg' \| 'full' | 'md' | Border radius preset |
| fit | 'cover' \| 'contain' \| 'fill' \| 'none' | 'cover' | CSS object-fit value |
| lazy | boolean | true | Enable lazy loading via IntersectionObserver |
| placeholder | ReactNode | — | Content shown while loading |
| fallback | ReactNode | — | Content shown on error |
| preview | boolean | false | Enable click-to-preview lightbox |
| caption | ReactNode | — | Caption text below the image |

## Notification

Global notification system using provider/hook pattern. Notifications stack in a fixed position with auto-dismiss and manual dismiss support.

```tsx
import { NotificationProvider, useNotification } from 'vxui-react';

function App() {
  return (
    <NotificationProvider placement="top-right" maxCount={5}>
      <MyApp />
    </NotificationProvider>
  );
}

function MyApp() {
  const { push, dismiss, clear } = useNotification();
  return (
    <button onClick={() => push({ title: 'Saved', description: 'Changes saved.', tone: 'success', duration: 3000 })}>
      Save
    </button>
  );
}
```

### NotificationProvider Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| children | ReactNode | **required** | App content |
| placement | 'top-right' \| 'top-left' \| 'top-center' \| 'bottom-right' \| 'bottom-left' \| 'bottom-center' | 'top-right' | Placement of the notification stack |
| maxCount | number | 5 | Maximum number of visible notifications |

### useNotification() API

| Method | Signature | Description |
|--------|-----------|-------------|
| push | (notification: NotificationInput) => number | Push a notification, returns its id |
| dismiss | (id: number) => void | Dismiss a notification by id |
| clear | () => void | Dismiss all notifications |

### NotificationInput

```ts
interface NotificationInput {
  title: string;
  description?: string;
  tone?: 'info' | 'success' | 'warning' | 'danger';
  duration?: number;  // auto-dismiss ms; 0 = persistent
  action?: ReactNode; // custom action node
}
```

## PinInput

PIN / OTP code input with auto-advance, paste support, numeric/alphanumeric modes, and optional mask.

```tsx
import { PinInput } from 'vxui-react';

<PinInput
  length={6}
  type="numeric"
  size="md"
  mask={false}
  label="Verification Code"
  onComplete={(value) => console.log('Code:', value)}
/>
```

### PinInput Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| length | number | 6 | Number of input fields |
| value | string | — | Controlled value |
| defaultValue | string | '' | Default value for uncontrolled mode |
| onChange | (value: string) => void | — | Callback on value change |
| onComplete | (value: string) => void | — | Callback when all fields are filled |
| type | 'numeric' \| 'alphanumeric' | 'numeric' | Input type |
| size | 'sm' \| 'md' \| 'lg' | 'md' | Size preset |
| placeholder | string | '○' | Placeholder character in empty fields |
| disabled | boolean | false | Disable all inputs |
| error | string | — | Error message |
| label | string | — | Label above the input group |
| hint | string | — | Hint text below the input group |
| mask | boolean | false | Mask input (show dots) |
| autoFocus | boolean | false | Auto-focus the first input on mount |

## Result

Status result page component for success, error, warning, and other outcomes. Includes default icons per status.

```tsx
import { Result } from 'vxui-react';
import { Button } from 'vxui-react';

<Result
  status="success"
  title="Order Placed"
  description="Your order has been placed successfully."
  actions={<Button>Continue Shopping</Button>}
/>
```

### Result Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| status | 'success' \| 'error' \| 'warning' \| 'info' \| 'forbidden' \| 'not-found' \| 'server-error' | **required** | Pre-built status with default icon and color |
| title | string | **required** | Title text |
| description | string | — | Description text below the title |
| icon | ReactNode | — | Custom icon (overrides the default status icon) |
| actions | ReactNode | — | Action area: buttons, links, etc. |
| extra | ReactNode | — | Extra content area below actions |

## MultiSelect

Searchable multi-select dropdown with tag display, clearable selection, and a "+N more" badge when tags overflow.

```tsx
import { MultiSelect } from 'vxui-react';

<MultiSelect
  options={[
    { value: 'react', label: 'React' },
    { value: 'vue', label: 'Vue' },
    { value: 'angular', label: 'Angular' },
  ]}
  placeholder="Select frameworks"
  maxDisplay={3}
  onChange={(selected) => console.log(selected)}
/>
```

### MultiSelect Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| options | MultiSelectOption[] | **required** | Available options |
| value | string[] | — | Controlled selected values |
| defaultValue | string[] | — | Default values for uncontrolled mode |
| onChange | (value: string[]) => void | — | Callback on selection change |
| placeholder | string | — | Placeholder text |
| searchPlaceholder | string | — | Search input placeholder |
| label | string | — | Label above the select |
| hint | string | — | Hint text |
| error | string | — | Error message |
| disabled | boolean | false | Disable the select |
| clearable | boolean | — | Show clear button |
| emptyText | string | — | Text when no options match |
| maxDisplay | number | — | Max visible tags before "+N more" badge |

## TimePicker

Time selection component with hours and minutes columns, optional seconds, and a dropdown overlay.

```tsx
import { TimePicker } from 'vxui-react';

<TimePicker
  label="Meeting Time"
  seconds={false}
  onChange={(value) => console.log('Time:', value)}
/>
```

### TimePicker Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| value | string | — | Controlled value (HH:MM or HH:MM:SS) |
| defaultValue | string | — | Default value for uncontrolled mode |
| onChange | (value: string) => void | — | Callback on value change |
| placeholder | string | 'Select time' | Placeholder text |
| label | string | — | Label above the input |
| hint | string | — | Hint text |
| error | string | — | Error message |
| disabled | boolean | false | Disable the input |
| seconds | boolean | false | Show seconds column |

## ThemeProvider

Theme context provider that manages light/dark mode switching, custom CSS token overrides, and persists the selection to localStorage.

```tsx
import { ThemeProvider, useTheme, createTheme } from 'vxui-react';

const themes = {
  light: createTheme('light', { label: 'Light' }),
  dark: createTheme('dark', { label: 'Dark' }),
  brand: createTheme('light', {
    label: 'Brand',
    tokens: { '--vx-primary': '#7c3aed' },
  }),
};

function App() {
  return (
    <ThemeProvider defaultTheme="light" themes={themes}>
      <MyApp />
    </ThemeProvider>
  );
}

function MyApp() {
  const { theme, setTheme, availableThemes } = useTheme();
  return <select value={theme} onChange={(e) => setTheme(e.target.value)}>...</select>;
}
```

### ThemeProvider Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| children | ReactNode | **required** | App content |
| defaultTheme | string | 'light' | Default theme name |
| storageKey | string | 'vxui-react-theme' | localStorage key for persistence |
| themes | ThemeRegistry | { light, dark } | Registry of named themes |

### useTheme() API

| Property | Type | Description |
|----------|------|-------------|
| theme | string | Active theme name |
| setTheme | (name: string) => void | Switch to a named theme |
| availableThemes | string[] | List of registered theme names |

## Responsive

Conditionally render different nodes based on the current viewport breakpoint (desktop ≥1024px, tablet 768–1023px, mobile ≤767px).

```tsx
import { Responsive } from 'vxui-react';

<Responsive
  desktop={<DesktopLayout />}
  tablet={<TabletLayout />}
  mobile={<MobileLayout />}
/>
```

### Responsive Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| desktop | ReactNode | **required** | Rendered on desktop (≥1024px) and tablet when tablet is omitted |
| mobile | ReactNode | **required** | Rendered on phone (≤767px) |
| tablet | ReactNode | — | Rendered on tablet (768–1023px); falls back to desktop |

## Article

Structured article layout component for documentation and long-form content. Includes sub-components for header, title, body, sections, props table, pager navigation, stats grid, and empty states.

```tsx
import { Article, ArticleHeader, ArticleTitle, ArticleBody, Section, SectionHeading } from 'vxui-react';

<Article>
  <ArticleHeader>
    <ArticleTitle>Getting Started</ArticleTitle>
  </ArticleHeader>
  <ArticleBody>
    <Section sectionId="intro">
      <SectionHeading anchor="#intro">Introduction</SectionHeading>
      <p>Welcome to VXUI.</p>
    </Section>
  </ArticleBody>
</Article>
```

### Article Sub-components

| Component | Description |
|-----------|-------------|
| Article | Root `<article>` wrapper |
| ArticleHeader | Header area with title and metadata |
| ArticleTitle | Main `<h1>` title |
| ArticleBody | Body wrapper with content area |
| Section | Content section with optional anchor id |
| SectionHeading | Section `<h2>` heading with optional anchor link |
| Pager | Previous / Next page navigation |
| PropsTable | Component props documentation table |
| StatsGrid | Grid of stat cards with label, value, hint, and icon |
| ArticleEmptyState | Empty state placeholder for articles |

## File Structure (library source)

```
src/
  components/        # All React components
    mobile/          # Mobile-specific components
    pages/           # Full-page template components
  styles/
    base.css         # All CSS (design tokens + component styles)
  i18n/
    index.tsx        # English & Chinese translations
  lib/
    cx.ts            # className utility
    version.ts       # library version
    VXUIProvider.tsx  # top-level provider combining ThemeProvider + i18n
```
