> ## Documentation Index
> Fetch the complete documentation index at: https://sdk.ekanos.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Components

> Complete list of all exported UI components.

## Form controls

### Button

```ts theme={null}
import { Button } from '@ekanos/ui/button';
import type { ButtonProps } from '@ekanos/ui/button';

// Variants: default, destructive, outline, success, secondary, ghost, link
// Sizes: default, xs, sm, lg, icon, icon-xs, icon-sm, icon-lg
<Button variant="default" size="lg">Click me</Button>
<Button disabled>Disabled</Button>
```

### Input

```ts theme={null}
import { Input } from '@ekanos/ui/input';

<Input type="text" placeholder="Enter text..." />
<Input type="email" disabled />
```

### Textarea

```ts theme={null}
import { Textarea } from '@ekanos/ui/textarea';

<Textarea placeholder="Enter a long message..." rows={4} />
```

### Select

```ts theme={null}
import {
  Select,
  SelectTrigger,
  SelectValue,
  SelectContent,
  SelectItem,
  SelectGroup,
  SelectLabel,
} from '@ekanos/ui/select';

<Select defaultValue="option1">
  <SelectTrigger>
    <SelectValue />
  </SelectTrigger>
  <SelectContent>
    <SelectGroup>
      <SelectLabel>Options</SelectLabel>
      <SelectItem value="option1">Option 1</SelectItem>
      <SelectItem value="option2">Option 2</SelectItem>
    </SelectGroup>
  </SelectContent>
</Select>
```

### Switch

```ts theme={null}
import { Switch } from '@ekanos/ui/switch';

<Switch defaultChecked />
<Switch disabled />
```

### Label

```ts theme={null}
import { Input } from '@ekanos/ui/input';
import { Label } from '@ekanos/ui/label';

<Label htmlFor="input-id">Your name</Label>
<Input id="input-id" />
```

### Form (with react-hook-form)

```ts theme={null}
import {
  Form,
  FormField,
  FormItem,
  FormLabel,
  FormControl,
  FormDescription,
  FormMessage,
} from '@ekanos/ui/form';
import { Input } from '@ekanos/ui/input';
import { useForm } from 'react-hook-form';

export function MyForm() {
  const form = useForm({ defaultValues: { email: '' } });
  const onSubmit = (values: { email: string }) => console.log(values);

  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit(onSubmit)}>
        <FormField
          control={form.control}
          name="email"
          render={({ field }) => (
            <FormItem>
              <FormLabel>Email</FormLabel>
              <FormControl>
                <Input {...field} type="email" />
              </FormControl>
              <FormDescription>We'll never share your email.</FormDescription>
              <FormMessage />
            </FormItem>
          )}
        />
      </form>
    </Form>
  );
}
```

## Layout & containers

### Card

```ts theme={null}
import {
  Card,
  CardHeader,
  CardTitle,
  CardDescription,
  CardContent,
  CardFooter,
} from '@ekanos/ui/card';

<Card>
  <CardHeader>
    <CardTitle>Card title</CardTitle>
    <CardDescription>Card description</CardDescription>
  </CardHeader>
  <CardContent>Content here</CardContent>
  <CardFooter>Footer content</CardFooter>
</Card>
```

### Dialog

```ts theme={null}
import {
  Dialog,
  DialogTrigger,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogDescription,
  DialogFooter,
} from '@ekanos/ui/dialog';

<Dialog>
  <DialogTrigger render={<Button />}>Open Dialog</DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Dialog title</DialogTitle>
      <DialogDescription>Dialog description</DialogDescription>
    </DialogHeader>
    <DialogFooter>
      <Button>Save</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>
```

## Status & feedback

### Badge

```ts theme={null}
import { Badge } from '@ekanos/ui/badge';

// Variants: default, secondary, destructive, outline, ghost, link, success, warning, info
<Badge>New</Badge>
<Badge variant="outline">Draft</Badge>
```

### StatusBadge

One neutral outlined pill with a colored dot, for record states ("Active", "Failed", "Pending"). The label carries the meaning; the dot confirms it, so the pill reads the same to a color-blind viewer.

```tsx theme={null}
import { StatusBadge } from '@ekanos/ui/status-badge';

// tone: 'success' | 'warning' | 'destructive' | 'info' | 'neutral' (default)
<StatusBadge tone="success">Active</StatusBadge>
<StatusBadge tone="destructive">Failed</StatusBadge>
<StatusBadge>Draft</StatusBadge>
```

It accepts every `Badge` prop except `variant`, and exports the `StatusTone` type.

### Stat

A headline figure: a small uppercase label, the figure in the heading face with its change beside it, and one muted line of context.

```tsx theme={null}
import {
  Stat,
  StatChange,
  StatDetail,
  StatFigure,
  StatLabel,
  StatValue,
} from '@ekanos/ui/stat';

<Stat>
  <StatLabel>Cash on hand</StatLabel>
  <StatFigure>
    <StatValue>$48,210.55</StatValue>
    <StatChange tone="positive">+8.2%</StatChange>
  </StatFigure>
  <StatDetail>Across 3 accounts</StatDetail>
</Stat>;
```

`StatChange`'s `tone` (`'positive' | 'negative' | 'neutral'`) is the reading, not the direction: expenses falling is `positive`. A trend icon pairs with the color so the tone never rides on color alone; pass `icon` (a Font Awesome class) to use a different glyph.

### Alert

```ts theme={null}
import { Alert, AlertTitle, AlertDescription } from '@ekanos/ui/alert';

// Variants: default, destructive, success, warning, info
<Alert>
  <AlertTitle>Heads up!</AlertTitle>
  <AlertDescription>You can add components to your project.</AlertDescription>
</Alert>
```

### Skeleton

```ts theme={null}
import { Skeleton } from '@ekanos/ui/skeleton';

<Skeleton className="h-12 w-12 rounded-full" />
<Skeleton className="h-4 w-full" />
```

### Spinner

```ts theme={null}
import { Spinner } from '@ekanos/ui/spinner';

<Spinner className="size-4 text-primary" />
<Spinner />  // Defaults: size-6, text-muted-foreground
```

## Overlays & menus

### Dropdown Menu

```ts theme={null}
import {
  DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
} from '@ekanos/ui/dropdown-menu';

<DropdownMenu>
  <DropdownMenuTrigger render={<Button variant="ghost" />}>Menu</DropdownMenuTrigger>
  <DropdownMenuContent>
    <DropdownMenuLabel>Actions</DropdownMenuLabel>
    <DropdownMenuItem>New</DropdownMenuItem>
    <DropdownMenuSeparator />
    <DropdownMenuItem>Delete</DropdownMenuItem>
  </DropdownMenuContent>
</DropdownMenu>
```

### Tooltip

```ts theme={null}
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@ekanos/ui/tooltip';

<TooltipProvider>
  <Tooltip>
    <TooltipTrigger>Hover me</TooltipTrigger>
    <TooltipContent>Tooltip text</TooltipContent>
  </Tooltip>
</TooltipProvider>
```

### Separator

```ts theme={null}
import { Separator } from '@ekanos/ui/separator';

<div>Content above</div>
<Separator className="my-4" />
<div>Content below</div>
```

## Page structure

`@ekanos/ui/page` ships the page shape the host's own pages use, so a partner page does not hand-roll chrome. A page composes the parts in this order:

```tsx theme={null}
import {
  PageBody,
  PageRail,
  PageTitleSection,
  PageToolbar,
  PageToolbarActions,
} from '@ekanos/ui/page';

export function RecordsPage() {
  return (
    <>
      <PageRail />
      <PageBody>
        <PageTitleSection
          title="Records"
          subtitle="Everything synced from Acme."
          actions={<Button>New record</Button>}
        />
        <PageToolbar>
          <Input placeholder="Search" className="flex-1" />
          <PageToolbarActions>
            {/* filters, range, view toggle */}
          </PageToolbarActions>
        </PageToolbar>
        {/* content */}
      </PageBody>
    </>
  );
}
```

| Export | Role |
| - | - |
| `PageRail` | The sticky header bar that navigates: sidebar trigger, route tabs. On a page with a `PageTitleSection` it renders unnamed (`<PageRail />`). Props include `title`, `subtitle`, `leading`, `tabs`, `subTabs`, `titleActions`, `actions`, `sidebarTrigger`. |
| `PageTitleSection` | The page's heading (the document's `h1`), one-sentence `subtitle`, and the page's main `actions`. Also `back`, `tabs`, `headingLevel`, `divider`. |
| `PageToolbar` / `PageToolbarActions` | The row that acts on the page's content (search, filters); `PageToolbarActions` pushes its children hard right. |
| `PageRailTabs` / `PageRailTab` | Route tabs for a page whose sections are separate URLs (`PageRailTab` takes `href` and `active`). Passed to `PageRail`'s `tabs`. |
| `PageRailSubTabs` / `PageRailSubTab` | Second-level route navigation, passed to `PageRail`'s `subTabs`. |
| `PageBackButton` | Icon-only back arrow for a nested page; goes in `PageTitleSection`'s `back` slot. Requires a `label`. |
| `PageRangeSwitch` | A time-window switch (`options` with `value`, `label`, `href`; plain links, so the range lives in the URL). Goes in `PageToolbarActions`. |
| `PageViewModeToggle` | A rows/cards style switch (`options`, `value`, `onChange`). Goes in `PageToolbarActions`. |
| `PageBody` | Padded flex column for the page content. `fullBleed` removes the padding. |

These primitives use route links from `next/link`, so render them inside a Next app (the harness provides one).

## Utilities

### Icon (Font Awesome)

```ts theme={null}
import { Icon, resolveIcon, renderIcon } from '@ekanos/ui/icon';

<Icon name="fa-solid fa-house" />
<Icon name="fa-solid fa-star" className="h-5 w-5 text-primary" />

// Resolve a name to a component that accepts { className, style }
const Gear = resolveIcon('fa-solid fa-gear');
<Gear className="h-4 w-4" />

// Render directly; returns null for an empty name
renderIcon('fa-regular fa-bell', 'h-4 w-4');
```

`name` is a Font Awesome class string (`fa-<style> fa-<glyph>`, e.g. `fa-light fa-house`). Legacy Lucide component names (e.g. `AlertCircle`) are still translated to Font Awesome at render time during the migration, but must not be written; an unknown name falls back to `fa-light fa-shapes`.

See [Fonts & Icons](/ui/fonts-and-icons) for Font Awesome setup.

### If (conditional rendering)

```ts theme={null}
import { If } from '@ekanos/ui/if';

<If condition={isLoaded} fallback={<p>Loading…</p>}>
  <div>Content is shown</div>
</If>

// Render function receives the condition narrowed to its truthy type
<If condition={user}>{(u) => <p>{u.name}</p>}</If>
```

### Trans (i18n with next-intl)

```ts theme={null}
import { Trans } from '@ekanos/ui/trans';

<Trans
  i18nKey="myNamespace:greeting"
  defaults="Hello {{name}}"
  values={{ name: 'Alice' }}
/>
```

A client component that must render under a `NextIntlClientProvider` (it throws without one). A key resolves to the host's message when it exists; otherwise `defaults` (interpolated), then `children`, then the key itself.

### Utils

```ts theme={null}
import { cn, isRouteActive, checkIfRouteIsActive } from '@ekanos/ui/utils';

// Class merging (clsx + tailwind-merge)
const classes = cn('px-4', 'px-8'); // Results in 'px-8'

// Route checking (for nav highlighting): isRouteActive(path, currentPath, end?)
if (isRouteActive(href, pathname)) {
  // Highlight nav item
}
```

## AI surfaces

### PromptInput

```tsx theme={null}
import {
  PromptInput,
  PromptInputBody,
  PromptInputFooter,
  PromptInputSubmit,
  PromptInputTextarea,
} from '@ekanos/ui/ai-prompt-input';

<PromptInput onSubmit={(message) => send(message.text)}>
  <PromptInputBody>
    <PromptInputTextarea />
  </PromptInputBody>
  <PromptInputFooter>
    <PromptInputSubmit status={status} />
  </PromptInputFooter>
</PromptInput>;
```

`onSubmit` receives `{ text, files }` and the form event. The entry also exports attachment, action-menu, select, hover-card, tab, and command parts (`PromptInputActionMenu`, `PromptInputSelect`, `PromptInputCommand`, …). Its `ChatStatus` and `FileUIPart` types come from the `ai` package, which is imported for types only.

## Styling imports

### Stylesheets

```ts theme={null}
// Pre-compiled stylesheet (no Tailwind)
import '@ekanos/ui/styles.css';
```

```css theme={null}
/* Tailwind v4 preset — in your CSS entry, after @import 'tailwindcss' */
@import '@ekanos/ui/theme.css'; /* token map, token values, base rules, dark variant, @source */

/* The pieces theme.css already includes, if you want them separately */
@import '@ekanos/ui/tokens.css'; /* custom properties only, no Tailwind at-rules */
@import '@ekanos/ui/base.css'; /* base rules: border colour, icon placeholders, .ekanos-root */
```

See [Styling](/ui/styling).

## TypeScript

Every entry ships type declarations. A few prop types are exported by name, for example:

```ts theme={null}
import type { ButtonProps } from '@ekanos/ui/button';
import type { BadgeProps } from '@ekanos/ui/badge';
import type { PromptInputProps } from '@ekanos/ui/ai-prompt-input';
```

For components without a named props type, use `React.ComponentProps<typeof Input>`. Props extend the underlying element's props, so standard attributes work:

```ts theme={null}
<Button type="submit" className="custom" />
<Input aria-label="Search" disabled />
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.