# UI Components
**Source:** `src/lib/client/ui/`
**Import alias:** `$ui/*` (maps to `src/lib/client/ui/`)
**Lint rule:** `scripts/lint/ui.ts` (`deno task lint:ui`)
Profilarr ships a small in-house component library that wraps the raw HTML
primitives the app needs: buttons, inputs, selects, textareas, dialogs, and
tables. Every page in `src/routes/` is expected to consume these wrappers
instead of raw elements. Compliance is enforced by a custom Deno lint script
(`no-raw-ui`) that parses every `.svelte` file with the Svelte 5 compiler and
flags banned tags.
## Table of Contents
- [The `no-raw-ui` Lint Rule](#the-no-raw-ui-lint-rule)
- [Component Catalog](#component-catalog)
- [Inputs](#inputs)
- [Buttons and Toggles](#buttons-and-toggles)
- [Layout and Containers](#layout-and-containers)
- [Modals and Overlays](#modals-and-overlays)
- [Data Display](#data-display)
- [Navigation](#navigation)
- [Feedback and Info](#feedback-and-info)
- [Filters](#filters)
- [Actions Bar](#actions-bar)
- [Arr-Specific](#arr-specific)
- [Semantic Tokens and Theming (Planned)](#semantic-tokens-and-theming-planned)
## The `no-raw-ui` Lint Rule
`scripts/lint/ui.ts` is a script that enforces the "wrappers
only" convention. It walks the Svelte 5 AST produced by
`npm:svelte/compiler`, reports each banned tag with a suggested replacement,
and exits non-zero on any violation.
```bash
deno task lint:ui # run the no-raw-ui rule alone
```
### Exemptions
**``** is always allowed. Roughly 140 hidden inputs
exist in the codebase as SvelteKit form-action plumbing; they are not visible
UI and wrapping them would add noise. The exemption only applies when `type`
is a static string literal equal to `"hidden"`. Dynamic `type={foo}`
expressions are flagged.
**Escape-hatch comment.** For the rare case where a wrapper is genuinely
wrong (or doesn't exist yet), an HTML comment placed immediately before the
element disables the rule for that line:
```svelte
```
The directive format is strict:
```
```
A **malformed** directive (missing `--`, empty reason, wrong rule name) is
itself reported as a violation, so you cannot accidentally "pass" the linter
by writing a broken directive.
## Component Catalog
Components are organized by functional category. Each major component (the
ones that directly replace banned HTML, plus the most heavily used wrappers)
is documented with three representative call sites. Minor components get a
one-line description.
### Inputs
#### FormInput
**`$ui/form/FormInput.svelte`** wraps `` (and, with `textarea={true}`,
`