npm package: @legalaidalberta/luic
Description: Legal Aid Alberta's React component library. Built with React, Tailwind CSS v4, react-select, and Zod validation.
Latest version: 0.1.0

# @luic/ui

Legal Aid Alberta's React component library. Built with React, Tailwind CSS v4, react-select, and Zod validation.

## Installation

```bash
pnpm add @luic/ui
```

## Quick Start

```tsx
import { TextInput, createTextInputSchema } from '@luic/ui';
import '@luic/ui/dist/styles.css';

const schema = createTextInputSchema({ required: true, minLength: 2 });

function MyForm() {
  const [name, setName] = useState('');
  const [error, setError] = useState<string>();

  const handleBlur = () => {
    const result = schema.safeParse(name);
    setError(result.success ? undefined : result.error.issues[0].message);
  };

  return (
    <TextInput
      label="Full Name"
      value={name}
      onChange={(e) => setName(e.target.value)}
      onBlur={handleBlur}
      error={error}
      required
    />
  );
}
```

## Components

### Selection Controls

| Component | Description | When to Use | When Not to Use |
|-----------|-------------|-------------|-----------------|
| **Select** | Single-selection dropdown (react-select) | Choosing one option from a list of 5+; searchable, clearable by default | Binary choices (Toggle), 2-4 visible options (Radio) |
| **MultiSelect** | Multi-selection dropdown (react-select) | Choosing multiple options from a list; tags, filters, categories | Single selection (Select), boolean agreement (Checkbox) |
| **TagInput** | Creatable multi-select for free-text tags | Tags, keywords, labels where users can create new values | Selecting from fixed options only (MultiSelect) |
| **SearchInput** | Text search or async typeahead | Simple search fields (simple mode) or server-side search (async mode) | Selecting from a static list (Select) |

### Text Inputs

| Component | Description | When to Use | When Not to Use |
|-----------|-------------|-------------|-----------------|
| **TextInput** | Single-line text entry with optional addons | Names, emails, short text, any single-line field | Multi-line text (Textarea), passwords (PasswordInput), URLs (URLInput) |
| **PasswordInput** | Password entry with toggle and strength meter | Password fields, PIN entry, secret keys | General text (TextInput), search (SearchInput) |
| **Textarea** | Multi-line text with character counter | Comments, descriptions, messages | Single-line input (TextInput), rich text editing |
| **URLInput** | URL entry with format validation | Website URLs, API endpoints, link fields | General text (TextInput), email addresses |

### Numeric Inputs

| Component | Description | When to Use | When Not to Use |
|-----------|-------------|-------------|-----------------|
| **NumericInput** | Number entry with +/- buttons and clamping | Quantities, ages, counts, integers or decimals | Currency (CurrencyInput), percentages (PercentageInput), visual ranges (Slider) |
| **CurrencyInput** | Money entry with currency symbol | Prices, payments, budgets, monetary values | Plain numbers (NumericInput), percentages (PercentageInput) |
| **PercentageInput** | Percentage entry (0-100) with % symbol | Discount rates, completion %, probability | Plain numbers (NumericInput), visual ranges (Slider) |
| **Slider** | Range slider for visual value selection | Volume, brightness, approximate values | Precise number entry (NumericInput), percentage fields (PercentageInput) |

### Date & Time

| Component | Description | When to Use | When Not to Use |
|-----------|-------------|-------------|-----------------|
| **DatePicker** | Date selection via native input | Birthdates, deadlines, scheduling | Time selection (TimePicker) |
| **TimePicker** | Time selection via native input | Appointment times, schedules | Date selection (DatePicker), durations (NumericInput) |

### Boolean & Choice Controls

| Component | Description | When to Use | When Not to Use |
|-----------|-------------|-------------|-----------------|
| **Toggle** | On/off switch | Feature flags, settings, boolean preferences | Agreement checkboxes (Checkbox), multi-state options (Select) |
| **Checkbox** | Single checkbox with label | Terms agreement, opt-in/out, boolean fields | On/off switches (Toggle), single selection (Radio) |
| **Radio** | Radio button for group single-selection | 2-5 visible choices: gender, plan selection | Large lists (Select), multiple selections (MultiSelect), on/off (Toggle) |

### Specialty Inputs

| Component | Description | When to Use | When Not to Use |
|-----------|-------------|-------------|-----------------|
| **ColorPicker** | Color input with hex display and preview | Theme customization, brand colors, design settings | Selecting from predefined options (Select) |
| **Rating** | Star-based rating (1-5) | Reviews, feedback, satisfaction scores | Numeric scores (NumericInput), likes (Toggle) |
| **FileUpload** | File upload with drag-and-drop | Documents, images, attachments | URL links to files (URLInput) |

### Data Display

| Component | Description | When to Use | When Not to Use |
|-----------|-------------|-------------|-----------------|
| **DataTable** | Simple table with selection and pagination | Data lists, admin tables, simple tabular data | Complex filtering/grouping (AdvancedDataGrid) |
| **AdvancedDataGrid** | Feature-rich grid with filtering, sorting, grouping, search, export, inline editing | Complex data management, admin dashboards, reporting | Simple lists (DataTable), fewer than 10 items |

### Layout

| Component | Description | When to Use | When Not to Use |
|-----------|-------------|-------------|-----------------|
| **Card** | Container for grouping content | Dashboard widgets, content sections, summary panels | Form layouts (FormField), data display (DataTable) |

## Shared Props

All form components support these base props:

| Prop | Type | Description |
|------|------|-------------|
| `label` | `string` | Field label |
| `error` | `string` | Error message |
| `errors` | `string[]` | Multiple validation errors (from Zod) |
| `helperText` | `string` | Helper/hint text below the field |
| `disabled` | `boolean` | Disables the field |
| `required` | `boolean` | Marks the field as required |
| `size` | `'sm' \| 'md' \| 'lg'` | Size variant (default: `'md'`) |
| `colorVariant` | `'default' \| 'primary' \| 'success' \| 'error' \| 'warning'` | Color accent |

## Zod Validation

Every component has a co-located Zod schema. Import directly from the component or from the validation barrel:

```tsx
// From component
import { createTextInputSchema } from '@luic/ui';

// All schemas available
import {
  createTextInputSchema,
  createPasswordSchema,
  createTextareaSchema,
  urlSchema,
  createNumericInputSchema,
  currencySchema,
  percentageSchema,
  createDateSchema,
  timeSchema,
  colorSchema,
  ratingSchema,
  createSelectSchema,
  createMultiSelectSchema,
  createTagsSchema,
  createSearchInputSchema,
  fileSchema,
  toggleSchema,
  checkboxSchema,
  radioSchema,
} from '@luic/ui';
```

### useFormValidation Hook

Framework-agnostic validation hook that works with any form library:

```tsx
import { useFormValidation, createTextInputSchema } from '@luic/ui';

function MyInput() {
  const [value, setValue] = useState('');
  const { errors, getValidationProps } = useFormValidation({
    schema: createTextInputSchema({ required: true, minLength: 3 }),
    mode: 'onBlur', // 'onBlur' | 'onChange' | 'onSubmit'
  });

  return (
    <TextInput
      label="Name"
      value={value}
      onChange={(e) => setValue(e.target.value)}
      errors={errors}
      {...getValidationProps(value)}
    />
  );
}
```

## React Select Customization

For advanced react-select customization, the theme layer is exported:

```tsx
import { LuicSelectBase, getLuicSelectStyles } from '@luic/ui';

// Use LuicSelectBase directly for custom select implementations
// Use getLuicSelectStyles() to get the Luic theme for your own react-select instances
```

## Development

```bash
pnpm install          # Install dependencies
pnpm dev              # Start Vite dev server
pnpm storybook        # Start Storybook
pnpm build            # Build (ES + CJS + UMD + types + CSS)
pnpm test             # Run tests (Vitest + Playwright)
pnpm lint             # Run ESLint
```

## Build Output

```
dist/
  luic.es.js       # ES module
  luic.cjs.js      # CommonJS
  luic.umd.js      # UMD (global: LuicUI)
  styles.css       # Tailwind CSS
  index.d.ts       # TypeScript declarations
```