---
name: squad-ui
description: Master skill for @thesqd/squad-ui — install, use any component, run CLI tools, invoke sub-skills (auth, file upload), access docs, and configure styling in any Next.js project
---

# Squad UI — Master Skill

You are an expert in the `@thesqd/squad-ui` component library. This skill gives you complete knowledge of every component, CLI command, sub-skill, design token, and integration pattern. Use it to answer questions, build UIs, set up features, and guide developers through anything Squad UI related.

---

## Dynamic Discovery (IMPORTANT)

This skill includes a static reference of components below, but the library is actively growing. **Always fetch the latest data** when you need to:

### Get the current component list
```bash
curl -s https://raw.githubusercontent.com/sis-thesqd/squad-ui/main/registry.json
```
This returns JSON with every component's name, description, category, file path, and dependencies. Use this as the **source of truth** for what components exist.

### Get documentation for any component
```bash
curl -s https://raw.githubusercontent.com/sis-thesqd/squad-ui/main/docs/{component-name}.md
```
Replace `{component-name}` with the registry key (e.g., `button`, `select`, `date-picker`). This gives you full props, types, and usage examples.

### Get skill instructions
```bash
curl -s https://raw.githubusercontent.com/sis-thesqd/squad-ui/main/docs/skills/{skill-name}.md
```
Available skills: `auth-setup`, `file-upload-setup`, `squad-ui`

### When to fetch
- **Before using a component** you're unsure about — fetch its doc
- **When the user asks "what components are available"** — fetch registry.json
- **When setting up auth or file upload** — fetch the sub-skill doc
- **If a component in the static list below doesn't match** what you find in the project — fetch the registry to get the latest

The static reference below is a snapshot for quick offline use. When in doubt, fetch.

---

## Quick Reference

- **Package:** `@thesqd/squad-ui`
- **Source:** `github:sis-thesqd/squad-ui`
- **Install:** `npm install github:sis-thesqd/squad-ui`
- **Styles:** `@import "@thesqd/squad-ui/styles.css";`
- **Peer deps:** `react`, `react-dom`, `lucide-react`, `@supabase/supabase-js` (optional, for auth)
- **Framework:** Next.js App Router (primary target), works with any React 18/19 project
- **CSS:** Tailwind 4.1 — all styling is inline Tailwind classes, no separate CSS files per component
- **Font:** Albra Grotesk (bundled, with CDN fallback)
- **Brand color:** Indigo `#513de5`

---

## Installation & Setup

### Step 1: Install

```bash
npm install github:sis-thesqd/squad-ui
```

The package auto-builds via its `prepare` script (runs `tsup`). Requires Node 18+.

If install hangs, try: `npm install github:sis-thesqd/squad-ui --ignore-scripts` then `cd node_modules/@thesqd/squad-ui && npm run build`.

### Step 2: Import Styles (CRITICAL ORDER)

In the project's `globals.css`, add Squad UI styles **BEFORE** the Tailwind import. This is required because Squad UI's CSS contains a Google Fonts `@import` which must precede all other rules.

```css
@import "@thesqd/squad-ui/styles.css";   /* MUST be first */
@import "tailwindcss";
@source "../../../node_modules/@thesqd/squad-ui/dist";
```

**Common error:** `@import rules must precede all rules` — means the squad-ui import is after `@import "tailwindcss"`. Move it to line 1.

### Step 3: Use Components

```tsx
import { Button, Badge, Input, Card } from "@thesqd/squad-ui";
```

All components are named exports from the package root.

---

## CLI Tool

The package includes a CLI accessible via `npx @thesqd/squad-ui` or `npx squad-ui`:

| Command | Description |
|---------|-------------|
| `npx @thesqd/squad-ui list` | List all components grouped by category |
| `npx @thesqd/squad-ui info <name>` | Show component details (category, file, deps, import) |
| `npx @thesqd/squad-ui docs <name>` | Print full markdown documentation for a component |
| `npx @thesqd/squad-ui init` | Show setup instructions |

**Examples:**
```bash
npx @thesqd/squad-ui list
npx @thesqd/squad-ui info button
npx @thesqd/squad-ui docs select
npx @thesqd/squad-ui docs auth-setup   # skill docs too
```

---

## Sub-Skills

This master skill can delegate to specialized sub-skills for complex setup tasks. When a user requests these features, fetch and follow the sub-skill instructions exactly.

### Auth Setup Sub-Skill

**Triggers:** "set up auth", "add authentication", "add login", "add Google OAuth"

**What to do:** Fetch the skill file and follow it step by step:
```bash
curl -s https://raw.githubusercontent.com/sis-thesqd/squad-ui/main/docs/skills/auth-setup.md
```

Or read from local: `~/.claude/skills/squad-ui-auth-setup/SKILL.md`

**What it creates:**
- `.env.local` with Supabase credentials (fetched via MCP)
- `lib/supabase.ts` — Supabase client
- `providers.tsx` — AuthProvider wrapper
- `/login` page with Google OAuth via LoginCard
- Protected home page at `/`
- `/auth/error` page
- CSS import order fix in `globals.css`

**Key components:** `AuthProvider`, `useAuth`, `AuthGate`, `LoginCard`

**Default Supabase project:** `wttgwoxlezqoyzmesekt`
**Default allowed domain:** `churchmediasquad.com`

### File Upload Sub-Skill

**Triggers:** "add file upload", "set up uploads", "add S3 upload"

**What to do:** Fetch the skill file and follow it step by step:
```bash
curl -s https://raw.githubusercontent.com/sis-thesqd/squad-ui/main/docs/skills/file-upload-setup.md
```

Or read from local: `~/.claude/skills/squad-ui-file-upload/SKILL.md`

**What it creates:**
- `lib/s3.ts` — S3 client utility
- `/api/upload/presign` — presigned PUT URL (5min expiry)
- `/api/upload/complete` — returns public URL
- `/api/upload/delete` — removes from S3
- S3 credentials in `.env.local`

**Two modes:**
- `mode="endpoint"` — S3/Wasabi via presigned URLs (requires API routes)
- `mode="supabase"` — Direct Supabase Storage upload (zero server code)

**Key component:** `FileUpload`

---

## All Components (44 total)

### General

| Component | Import | Description |
|-----------|--------|-------------|
| **Avatar** | `Avatar` | User avatar with image, initials fallback from name, or generic person icon. Sizes: `xs`, `sm`, `md`, `lg`, `xl`. Props: `src`, `name`, `initials`, `size`, `fallback`. |
| **Button** | `Button` | Polymorphic button/link. Colors: `primary`, `secondary`, `outline`, `link-color`, `link-gray`, `tertiary`, `destructive`, plus 15+ more. Sizes: `sm`, `md`, `lg`, `xl`. Props: `iconLeading`, `iconTrailing`, `loading`, `disabled`, `href` (renders as `<a>`). |
| **Badge** | `Badge` | Inline label. Types: `pill-color`, `pill-outline`, `badge-color`, `badge-modern`. Colors: `brand`, `gray`, `success`, `warning`, `danger`, `info`, `coral`, `purple`, `violet`, `amber`, `pink`. Sizes: `sm`, `md`, `lg`. Props: `dot`, `icon`, `close`. |
| **Card** | `Card` | Container. Padding: `none`, `small`, `base`, `large`. BorderRadius: `sm`, `md`, `lg`. Background: `default`, `secondary`. Border: `default`, `none`. Clickable via `href`. |
| **FeaturedIcon** | `FeaturedIcon` | Icon container. Themes: `light`, `dark`, `modern`, `outline`. Colors: `brand`, `gray`, `error`, `warning`, `success`. Sizes: `xs`, `sm`, `md`, `lg`. Props: `icon` (Lucide icon component). |
| **Switch** | `Switch` | Toggle. Sizes: `sm`, `md`, `lg`. Props: `label`, `description`, `trackContent`, `thumbIcon`, `disabled`. |
| **ThemeToggle** | `ThemeToggle` | Preset Switch for dark mode. Auto-toggles `data-theme` on `<html>`. |
| **Alert** | `Alert` | Notification banner. Intents: `brand`, `success`, `warning`, `danger`, `info`. Props: `title`, `icon`, `children`, `onClose`. Set `icon={false}` to hide icon. |
| **Toast** | `Toaster, toast` | Toast notifications (wraps Sonner). Place `<Toaster />` once in layout. Call `toast("msg")`, `toast.success()`, `toast.error()`, `toast.warning()`, `toast.info()`. Supports `description`, `action`, `duration`, `promise`. |
| **Kbd** | `Kbd, KbdGroup` | Keyboard shortcut display. `Kbd` for single keys, `KbdGroup` to group key combinations. Styled to look like physical keys. |
| **TagGroup** | `TagGroup` | Tag display. Props: `items` (array of `{id, label, href, icon}`), `variant` (`"fill"`, `"outline"`, `"modern"`), `color` (11 options), `onTagClick`. Tags with `href` render as links. |
| **DescriptionList** | `DescriptionList` | Term/details pairs in responsive grid. Compound pattern: `DescriptionList.Term` + `DescriptionList.Details`. Mobile stacks, desktop shows 2-column grid. |

### Forms

| Component | Import | Description |
|-----------|--------|-------------|
| **Input** | `Input` | Text input. Types: `text`, `email`, `phone`, `number`, `decimal`, `search`, `password`, `url`. Props: `label`, `description`, `hint`, `error`, `prefix`, `suffix`, `iconLeading`, `iconTrailing`, `clearable`. |
| **Select** | `Select` | Dropdown. Props: `options` (array of `{label, value}`), `searchable`, `multiple`, `label`, `error`, `placeholder`. Multi-select shows tags. |
| **Checkbox** | `Checkbox` | Props: `label`, `description`, `error`, `indeterminate`, `checked`, `onChange`. |
| **DatePicker** | `DatePicker` | Calendar popover. Also exports: `DateField`, `DateInput`, `DatePickerTrigger`, `Calendar`. Based on react-aria-components + @internationalized/date. |
| **DateRangePicker** | `DateRangePicker` | Date range with dual inputs (start/end) + RangeCalendar popover. Also exports `DateRangePickerTrigger`. |
| **Slider** | `Slider` | Range slider. Props: `value`, `onChange`, `minValue`, `maxValue`, `step`, `label`, `description`, `errorMessage`, `disabled`, `showOutput`, `formatOptions`. Supports single and range (two thumbs via array value). |
| **DurationField** | `DurationField` | H:M:S input. Props: `value` ({hours, minutes, seconds}), `onChange`, `showHours`, `showMinutes`, `showSeconds`, `format` (`"hms"` or `"object"`). |
| **TagField** | `TagField` | Tag input. Props: `value`, `onChange`, `maxTags`, `allowDuplicates`, `separators`, `badgeColor`, `badgeType`. Separators: Enter, comma, semicolon. Supports paste splitting. |
| **ColorPicker** | `ColorPicker` | Color input. Props: `value`, `onChange`, `label`, `presets`. |
| **RadioCardGroup** | `RadioCardGroup` | Card selection. Props: `items` (array of `{id, label, description, icon, badge, tags}`), `value`, `onChange`, `multiple`, `error`. |
| **CarouselTileRadioGroup** | `CarouselTileRadioGroup` | Image carousel tiles. Props: `items` (array of `{id, label, imageUrl}`), `value`, `onChange`, `multiple`, `autoPlayInterval`. Uses Embla Carousel. |
| **FileUpload** | `FileUpload` | File upload (FilePond). Props: `mode` (`"endpoint"` or `"supabase"`), `endpoint`, `bucket`, `supabaseClient`, `label`, `description`, `maxFiles`, `maxFileSize`, `acceptedFileTypes`, `allowImagePreview`, `onUpload`. |
| **RepeatableGroup** | `RepeatableGroup` | Self-contained dynamic form rows. Props: `fields` (array of `{key, label, type, options, required}`), `value`, `onChange`, `minInstances`, `maxInstances`, `label`, `description`. Field types: `text`, `email`, `phone`, `number`, `select`, `textarea`, `checkbox`, `toggle`. Zero internal dependencies. |
| **GridList** | `GridList` | Selectable list. Props: `items`, `sections`, `selectionMode` (`"single"`, `"multiple"`, `"none"`), `selectedKeys`, `onSelectionChange`. |
| **RichTextEditor** | `RichTextEditor` | Rich text editor (Tiptap, bundled). Props: `value`, `onChange` (HTML string), `label`, `description`, `placeholder`, `errorMessage`, `disabled`, `maxLength`, `minHeight`, `maxHeight`, `toolbarPosition` (`"top"` or `"bottom"`), `toolbar` (`true`/`false`/config object). Toolbar tools: `bold`, `italic`, `underline`, `strikethrough`, `headings`, `bulletList`, `orderedList`, `blockquote`, `codeBlock`, `horizontalRule`, `align`, `link`, `image`, `history`. All individually toggleable. Keyboard: Cmd+B/I/U/K. |

### Overlays

| Component | Import | Description |
|-----------|--------|-------------|
| **Modal** | `Modal` | Dialog. Props: `isOpen`, `onClose`, `title`, `description`, `size` (`xs`, `sm`, `md`, `lg`, `xl`, `2xl`, `3xl`, `4xl`, `5xl`, `fullscreen`), `children`, `footer`. |
| **Drawer** | `Drawer` | Slide-in panel. Props: `isOpen`, `onClose`, `side` (`left`, `right`, `top`, `bottom`), `size` (`sm`, `md`, `lg`, `xl`, `full`), `title`, `description`, `footer`, `showClose`. Pure CSS animations. |
| **Popover** | `Popover` | Floating panel. Props: `trigger`, `children` (ReactNode or render prop `{close}`), `placement` (16 options), `offset`, `arrow`, `isDismissable`. Also exports `PopoverContent` helper with `title`, `description`, `footer`, `showClose`. |
| **Tooltip** | `Tooltip, TooltipTrigger` | Tooltip with title + optional description. Props: `title`, `description`, `arrow`, `delay`, `placement` (16 options). Wrap non-focusable elements in `TooltipTrigger`. React Aria powered. |
| **CommandMenu** | `CommandMenu, CommandMenuSearch, CommandMenuList, CommandMenuSection, CommandMenuItem, CommandMenuSeparator` | Spotlight-style command palette. Compound component with search, sections, keyboard nav (arrow keys + enter), shortcuts display. No external deps. |
| **Menu** | `Menu, MenuContent, MenuItem, MenuSeparator, ButtonGroup` | Dropdown menu. `Menu` wraps trigger + `MenuContent`. `MenuItem` has `label`, `icon`, `description`, `destructive`, `disabled`, `onClick`. `ButtonGroup` splits primary action + menu dropdown. |
| **Dropdown** | `Dropdown, DropdownContent, DropdownItem, DropdownSection, DropdownSeparator` | React Aria dropdown. Items support `icon`, `description`, `shortcut`, `destructive`, `isDisabled`. Sections with `header`. Selection modes: `none`, `single`. |

### Layout

| Component | Import | Description |
|-----------|--------|-------------|
| **Sidebar** | `SidebarProvider, Sidebar, SidebarInset, SidebarNav, SidebarTrigger, SidebarSeparator` | Collapsible sidebar. `useSidebar()` hook gives `{open, toggle, dock}`. `SidebarNav` takes `items` array with sections, nested items, badges, icons. Intents: `brand`, `neutral`. |

### Navigation

| Component | Import | Description |
|-----------|--------|-------------|
| **Navbar** | `Navbar` | Responsive nav bar. Props: `logo`, `items` (array of `{id, label, href, icon, current}`), `trailing`, `sticky`, `intent` (`"default"`, `"floating"`), `layout` (`"right"`, `"center"`, `"split"`). Mobile hamburger menu built-in. |
| **Breadcrumbs** | `Breadcrumbs` | Trail. Props: `items` (array of `{label, href, icon}`), `separator` (`"chevron"` or `"slash"`). Last item renders as current page (no link). |
| **Steps** | `Steps` | Progress wizard. Props: `steps` (array of `{label, description?, icon?}`), `currentStep` (0-indexed), `orientation` (`"horizontal"`, `"vertical"`), `size` (`"sm"`, `"md"`), `variant` (`"numbered"`, `"dot"`). States: complete (green check), current (brand ring/dot), upcoming (gray). |
| **Tabs** | `Tabs` | Tab bar. Props: `items` (array of `{id, label, icon, badge, disabled}`), `value`, `onChange`, `variant` (`"underline"`, `"pill"`, `"enclosed"`). |
| **Tree** | `Tree` | Hierarchical tree. Props: `items` (array of `TreeItemData` with `{id, label, icon?, children?, href?, disabled?}`), `defaultExpandedKeys`, `selectionMode`. Items with `href` render as smooth-scroll links. Also exports: `TreeItem`, `TreeContent`, `TreeIndicator`, `TreeLabel`. |

### Blocks

| Component | Import | Description |
|-----------|--------|-------------|
| **LoginCard** | `LoginCard` | Self-contained login form (zero internal deps). Props: `variant` (`"password"`, `"magic-link"`), `logo`, `heading`, `subheading`, `onSubmit`, `onGoogleSignIn`, `onMagicLink`, `showRememberMe`, `showForgotPassword`, `showSignUp`, `footer`, `errorMessage`, `loading`. |
| **PanelEditor** | `PanelEditor` | Brochure editor. Props: `panels` (array of `PanelDefinition`), `foldType` (`"none"`, `"bi"`, `"tri"`), `colorScheme`, `onPanelChange`, `progress`. |

### Progress / Loading

| Component | Import | Description |
|-----------|--------|-------------|
| **Loader** | `Loader` | Spinner/dots. Props: `size` (`sm`, `md`, `lg`), `variant` (`"spinner"`, `"dots"`), `color`. |
| **ProgressBar** | `ProgressBar` | Determinate bar. Props: `value` (0-100), `intent` (`"brand"`, `"success"`, `"warning"`, `"danger"`), `label`, `showValue`, `indeterminate`. |
| **Meter** | `Meter` | Gauge/meter. Props: `value`, `min`, `max`, `format` (`"percent"`, `"fraction"`, `"value"`), `intent`, `label`. |

### Auth

| Component | Import | Description |
|-----------|--------|-------------|
| **AuthProvider** | `AuthProvider` | Context provider. Props: `supabaseClient`, `allowedDomain`, `verifyEmployee`, `children`. Always use `supabaseClient` prop (pre-initialized), NOT `supabaseUrl`/`supabaseAnonKey`. |
| **useAuth** | `useAuth` | Hook. Returns: `{user, loading, error, signInWithGoogle, signInWithEmail, signOut, supabase}`. `user` is `SquadUser` with `{id, email, displayName, avatarUrl, employee}`. |
| **AuthGate** | `AuthGate` | Renders children only when authenticated. Props: `loadingComponent`, `loginComponent`. Shows built-in login screen by default. |

---

## Documentation Access

Every component has a markdown doc file. To read docs for any component:

1. **From the repo:** `docs/{component-name}.md` (e.g., `docs/button.md`, `docs/select.md`)
2. **From GitHub raw:**
   ```
   https://raw.githubusercontent.com/sis-thesqd/squad-ui/main/docs/{component-name}.md
   ```
3. **Via CLI:** `npx @thesqd/squad-ui docs {component-name}`

**Available doc files:**
`alert`, `auth`, `auth-setup`, `avatar`, `badge`, `breadcrumbs`, `button`, `card`, `carousel-tile-radio-group`, `checkbox`, `color-picker`, `command-menu`, `date-picker`, `date-range-picker`, `description-list`, `drawer`, `dropdown`, `duration-field`, `featured-icon`, `file-upload`, `grid-list`, `input`, `kbd`, `login-card`, `menu`, `modal`, `navbar`, `panel-editor`, `popover`, `progress`, `radio-card-group`, `repeatable-group`, `rich-text-editor`, `select`, `sidebar`, `slider`, `steps`, `switch`, `tabs`, `tag-field`, `tag-group`, `toast`, `tooltip`, `tree`

**Skill docs:** `docs/skills/auth-setup.md`, `docs/skills/file-upload-setup.md`

When you need detailed props/examples for a specific component, fetch its doc file:
```bash
curl -s https://raw.githubusercontent.com/sis-thesqd/squad-ui/main/docs/button.md
```

---

## Design Tokens (CSS Custom Properties)

Squad UI uses CSS custom properties defined in `src/styles/theme.css`. Use these in custom styling:

### Colors
| Token | Value | Usage |
|-------|-------|-------|
| `--sqd-color-bg-fill` | `#ffffff` | Default background |
| `--sqd-color-bg-fill-hover` | `#fafafa` | Hover background |
| `--sqd-color-bg-fill-brand` | `#303030` | Brand fill (dark) |
| `--sqd-color-bg-fill-critical` | `#c70a24` | Destructive fill |
| `--sqd-color-text` | `#303030` | Primary text |
| `--sqd-color-text-secondary` | `#616161` | Secondary text |
| `--sqd-color-text-disabled` | `#b5b5b5` | Disabled text |
| `--sqd-color-text-critical` | `#8e0b21` | Error text |
| `--sqd-color-border` | `#e3e3e3` | Default border |
| `--sqd-color-border-hover` | `#cccccc` | Hover border |
| `--sqd-color-border-focus` | `rgba(81,61,229,0.25)` | Focus ring |

### Spacing & Radius
| Token | Value |
|-------|-------|
| `--sqd-space-100` | `0.25rem` |
| `--sqd-space-200` | `0.5rem` |
| `--sqd-space-300` | `0.75rem` |
| `--sqd-space-400` | `1rem` |
| `--sqd-space-600` | `1.5rem` |
| `--sqd-border-radius-200` | `0.5rem` |
| `--sqd-border-radius-300` | `0.75rem` |
| `--sqd-border-radius-400` | `1rem` |

### Shadows
| Token | Usage |
|-------|-------|
| `--sqd-shadow-button` | Default button shadow |
| `--sqd-shadow-button-hover` | Button hover shadow |
| `--sqd-shadow-button-primary` | Primary button shadow |
| `--sqd-shadow-button-primary-hover` | Primary hover shadow |
| `--sqd-focus-ring` | Focus outline ring |

### Font
| Token | Value |
|-------|-------|
| `--sqd-font-family-sans` | `"Albra Grotesk", -apple-system, sans-serif` |
| `--sqd-font-size-350` | `0.875rem` |
| `--sqd-font-weight-medium` | `500` |
| `--sqd-font-weight-semibold` | `600` |

---

## Common Patterns

### Protecting a Page (requires auth skill)
```tsx
"use client";
import { useEffect } from "react";
import { useRouter } from "next/navigation";
import { useAuth } from "@thesqd/squad-ui";

export default function ProtectedPage() {
  const { user, loading } = useAuth();
  const router = useRouter();

  useEffect(() => {
    if (!loading && !user) router.replace("/login");
  }, [user, loading, router]);

  if (loading || !user) return null;

  return <div>Welcome, {user.displayName}</div>;
}
```

### Using AuthGate (simpler alternative)
```tsx
import { AuthGate } from "@thesqd/squad-ui";

<AuthGate loginPath="/login" fallback={<Loader />}>
  <ProtectedContent />
</AuthGate>
```

### Basic Form Layout
```tsx
import { Input, Select, Button, Checkbox } from "@thesqd/squad-ui";

<form>
  <Input label="Name" required />
  <Input label="Email" type="email" required />
  <Select label="Role" options={[
    { label: "Admin", value: "admin" },
    { label: "Viewer", value: "viewer" },
  ]} />
  <Checkbox label="I agree to the terms" />
  <Button color="primary" type="submit">Submit</Button>
</form>
```

### Sidebar Layout
```tsx
import {
  SidebarProvider, Sidebar, SidebarInset, SidebarNav, SidebarTrigger
} from "@thesqd/squad-ui";
import { Home, Settings, Users } from "lucide-react";

<SidebarProvider>
  <Sidebar>
    <SidebarNav items={[
      { type: "section", label: "Menu", items: [
        { id: "home", label: "Home", icon: Home, href: "/" },
        { id: "users", label: "Users", icon: Users, href: "/users" },
        { id: "settings", label: "Settings", icon: Settings, href: "/settings" },
      ]},
    ]} />
  </Sidebar>
  <SidebarInset>
    <SidebarTrigger />
    {/* page content */}
  </SidebarInset>
</SidebarProvider>
```

### File Upload (S3)
```tsx
import { FileUpload } from "@thesqd/squad-ui";

<FileUpload
  mode="endpoint"
  endpoint="/api/upload"
  label="Upload Files"
  maxFileSize="10MB"
  maxFiles={5}
  acceptedFileTypes={["image/*", "application/pdf"]}
  allowImagePreview
  onUpload={(files) => console.log(files)}
/>
```

### File Upload (Supabase Storage)
```tsx
import { FileUpload, useAuth } from "@thesqd/squad-ui";

const { supabase } = useAuth();

<FileUpload
  mode="supabase"
  bucket="uploads"
  path="user-files"
  supabaseClient={supabase}
  label="Upload Files"
  maxFileSize="10MB"
  onUpload={(files) => console.log(files)}
/>
```

---

## Style Exports

Every component exports its Tailwind class strings for custom composition:

```tsx
import { buttonStyles, cardStyles, inputStyles } from "@thesqd/squad-ui";

// buttonStyles.root, buttonStyles.icon, etc.
```

Available: `alertStyles`, `authStyles`, `avatarStyles`, `badgeStyles`, `breadcrumbsStyles`, `buttonStyles`, `cardStyles`, `carouselTileStyles`, `checkboxStyles`, `colorPickerStyles`, `commandMenuStyles`, `descriptionListStyles`, `drawerStyles`, `dropdownStyles`, `durationFieldStyles`, `featuredIconStyles`, `fileUploadStyles`, `gridListStyles`, `inputStyles`, `kbdStyles`, `loginCardStyles`, `menuStyles`, `modalStyles`, `navbarStyles`, `panelEditorStyles`, `popoverStyles`, `progressStyles`, `radioCardGroupStyles`, `repeatableGroupStyles`, `richTextEditorStyles`, `selectStyles`, `sidebarStyles`, `sliderStyles`, `stepsStyles`, `switchStyles`, `tabsStyles`, `tagFieldStyles`, `tagGroupStyles`, `toastStyles`, `tooltipStyles`, `treeStyles`

---

## Type Exports

All TypeScript types are exported from the package root:

```tsx
import type {
  ButtonProps, ButtonColor, ButtonSize,
  BadgeProps, BadgeType, BadgeColor,
  InputProps, InputType,
  SelectProps, SelectOption,
  ModalProps, ModalSize,
  PopoverProps, PopoverPlacement,
  CardProps, CardPadding,
  AlertProps, AlertIntent,
  TabsProps, TabItem,
  // ... etc
} from "@thesqd/squad-ui";
```

---

## Troubleshooting

### CSS Not Loading / Styles Look Wrong
**Fix:** Ensure this exact order in `globals.css`:
```css
@import "@thesqd/squad-ui/styles.css";  /* MUST be first */
@import "tailwindcss";
@source "../../../node_modules/@thesqd/squad-ui/dist";
```

### Type Error: "ReactNode is not assignable"
**Fix:** Update React types: `npm install -D @types/react@latest @types/react-dom@latest`

### npm install github:sis-thesqd/squad-ui Fails
**Fix:** Ensure Node 18+. If `prepare` script hangs: `npm install github:sis-thesqd/squad-ui --ignore-scripts` then `cd node_modules/@thesqd/squad-ui && npm run build`.

### Tailwind Classes Not Applied to Squad UI Components
**Fix:** Add the `@source` directive after `@import "tailwindcss"`:
```css
@source "../../../node_modules/@thesqd/squad-ui/dist";
```
This tells Tailwind to scan squad-ui's built files for class names.

### "supabase.auth.getSession is not a function"
**Fix:** Use `supabaseClient` prop with a pre-initialized client, NOT `supabaseUrl`/`supabaseAnonKey`.

### FilePond CSS Not Loading
**Fix:** FilePond CSS is bundled with squad-ui. Ensure `@import "@thesqd/squad-ui/styles.css"` is in globals.css before Tailwind.

---

## Architecture Notes

- **Build:** tsup with PostCSS + Tailwind 4.1 plugin. Outputs ESM (`dist/index.js`), CJS (`dist/index.cjs`), types (`dist/index.d.ts`), and CSS (`dist/index.css`).
- **No separate CSS per component.** All styling is inline Tailwind classes. The single `dist/index.css` contains the design tokens, font-face declarations, and keyframe animations.
- **Albra Grotesk font** is bundled as OTF files in dist/ with Supabase CDN fallback.
- **Icons:** All icons use `lucide-react`. Components accept icon components via `icon`, `iconLeading`, `iconTrailing` props.
- **React Aria:** DatePicker/Calendar use `react-aria-components` and `@internationalized/date` (bundled).
- **FilePond:** FileUpload wraps FilePond with 6 plugins (file-validate-size, file-validate-type, image-validate-size, image-preview, file-rename, file-metadata).
- **Embla Carousel:** CarouselTileRadioGroup uses `embla-carousel-react` (bundled).
