# @ahrowe/ui

React 19 component library. `npm install @ahrowe/ui`, import everything from `@ahrowe/ui`, and
import `@ahrowe/ui/style.css` once in the app entry.

**Before using a component, read its doc: `node_modules/@ahrowe/ui/docs/<Doc>`**, where `<Doc>` is
the first column of the table below. The doc has the import line, runnable examples, key props,
enums and slot names. Do not infer props from a component's name.

## Rules for every component

- **`AhroweProvider` is required.** Wrap the app in it once (AhroweProvider.md). It injects the
  CSS variables every component paints with, so without it they render unstyled; it renders the
  element Modal, Tooltip, Popover, Toast and Menu portal into; and it shows toasts. Two themes
  ship, `defaultTheme` (dark) and `lightTheme`; a custom theme is
  `{ id, variables: { '--primary-color': '#6200ea', … } }` passed through `themes`, selected with
  `currentThemeId`. `useTheme()` switches at runtime and remembers the choice; `useTheme('system')`
  follows the operating system's light or dark setting. The variable list is in ThemeProvider.md.
  It already is a `ThemeProvider`, a `ConfigProvider`, a `ScrollbarProvider` and the toast host,
  so do not add those or a `<div id="bodyEnd" />` beside it. A section with its own theme or defaults nests another one.
- **Form components** (Input, Textarea, NumberInput, Dropdown, MultiSelect, InputDropdown, Checkbox,
  Switch, RadioGroup, DatePicker, TimeInput, OtpInput, IconPicker, TemplateEditor) take `formValidator`, which then owns
  the value: `new FormValidator('', [Validators.required()])`. Whole forms use
  `useFormValidatorGroup`. See FormValidator.md and FormValidatorGroup.md. Without a `formValidator`, a
  field shows an error while `isValid` is false or `errorMessage` is non-empty: pass the message only
  while there is an error (`errorMessage={rejected ? 'Wrong code' : undefined}`).
- **Days and times are strings**, never a `Date`: DatePicker and Calendar's `date` hold
  `'YYYY-MM-DD'`, TimeInput `'HH:MM'`. Calendar events are moments, so they stay `Date`s.
  `dateToDay` and `dayToDate` convert a day in the browser's time zone, `dayTimeToDate(day, time)`
  and `dateToTime` a day and a time together. Never use `toISOString().slice(0, 10)`, which gives
  the UTC day. DatePicker.md shows how to turn a day into a moment in a given time zone.
  Month and weekday names, the week start and text sorting follow `locale` on `AhroweProvider`
  (`'de-AT'`, default the browser's); `useLocale()` returns it for your own `Intl` calls.
- **Styling.** `className` and `style` hit the root element; `classNames.<slot>` and `styles.<slot>`
  hit named inner elements. Slot names are on each doc's **Slots** line. Every native attribute of
  the root element passes through (`id`, `aria-*`, `data-*`, `on*`). Space your own layout with the
  theme's `--spacing-s`, `--spacing-m` and `--spacing-l` (4, 8 and 16px by default), not fixed pixels.
- **Text a component renders itself** (close buttons, empty messages, page labels) is set with its
  `labels` prop. App-wide defaults for any prop go through `AhroweProvider`'s `defaultProps`
  (ConfigProvider.md).
- **Variants are enums**, not strings: `ButtonStyleType.Filled`, `ButtonSize.Small`. Each doc lists
  them under **Enums**. A few small props take string literals instead, spelled out in their doc's
  props table, such as Avatar's `status`.
- **Colours are one vocabulary.** Every `color` prop takes a `ThemeColor` (`Primary`, `Neutral`,
  `Success`, `Info`, `Warn`, `Error`) or any CSS colour, and a separate `styleType` sets how much
  the component stands out, so any style works in any colour (ThemeColor.md). Pick the style by
  emphasis, the same way everywhere: `Filled` for the one main action of a region (at most one),
  `Light` for status and selection, `Outlined` for secondary actions, `Plain` for tertiary ones.
- **`icon` props** take a FontAwesome `IconDefinition` or any React element.
- **Links.** An `href` (Button, ActionIcon, ListItem, Breadcrumb, Menu, CommandPalette) renders a
  real `<a>`. With a client-side router, set `navigate` on a `ConfigProvider` inside the router
  once, or every plain click loads the page (ConfigProvider.md).
- **`ref`** is a plain prop, there is no `forwardRef`. It lands on the element you would reach for:
  Input, Textarea, NumberInput and SearchInput hand out their inner `<input>`, ChatComposer its
  `<textarea>`. Timer, Flip, Tilt, Ripple, VirtualList, ChatMessageList, RoomDrawer and RoomViewer
  expose an imperative handle instead; its type is exported next to the props.
- Every module is `'use client'`; nothing renders on the server. The exception is
  `@ahrowe/ui/floor-plan`, the plan model (FloorPlan.md) without React, for server code that reads
  or validates a plan. No right-to-left support.
  `prefers-reduced-motion` is honoured. Built for Chrome and Edge 111, Firefox 114, Safari 16.4
  and newer, with no polyfills. The package is 0.x: a minor can break, a patch cannot.
- **Upgrading.** Every breaking change has a `## Migration from …` section, an old-to-new table,
  in the doc of the component it changed. When TypeScript reports a name as missing after an
  update, search the docs for that name. `CHANGELOG.md` beside `docs/` lists every change by
  version and names the section to read.

## Components

<!-- components:start -->

| Doc | Exports | When to use |
|-----|---------|-------------|
| Accordion.md | Accordion, AccordionStyleType, ThemeColor | Collapsible sections for progressive disclosure — FAQs, settings panels, long-form content that users expand on demand |
| ActionButtons.md | ActionButtons | Standardised Submit / Cancel button pair for forms, dialogs, and drawer footers |
| ActionIcon.md | ActionIcon | Clickable icon button without a label — toolbar actions, table row actions, close buttons, icon-only controls |
| AhroweProvider.md | AhroweProvider | The one wrapper an app needs, at its root: it applies the theme, sets default props for every component, shows toasts and renders the element that Modal, Tooltip, Popover, Menu and Toast portal into |
| Alert.md | Alert, ThemeColor | A persistent inline status message, used for form-level errors, page/section banners, empty-config warnings, and "here's what changed" notices |
| AnimatedLogo.md | AnimatedLogo | A self-drawing SVG mark for splash/loading screens, empty states, or anywhere a brand or icon mark should feel alive rather than static |
| AnimatedText.md | AnimatedText, AnimatedTextVariant, AnimatedTextUnit | Decorative text entrance or emphasis effect, for hero headlines, empty-state messages, "premium"/"new" callouts, loading/decoding states |
| Avatar.md | Avatar, AvatarGroup | User or entity image — profile pictures, comment authors, member lists |
| Badge.md | Badge, BadgeStyleType, BadgePosition, ThemeColor | Small count or status indicator — unread counts, notification dots, "new" markers |
| BodyEnd.md | BodyEnd | Renders children through a React portal, to the end of the app |
| Breadcrumb.md | Breadcrumb | A trail showing the current page's position within a hierarchy, with links back to each ancestor, such as a file browser path, a category/subcategory drill-down, or a multi-level settings page |
| Button.md | Button, ButtonStyleType, ButtonSize, ButtonType, ThemeColor | Primary interactive action trigger |
| ButtonGroup.md | ButtonGroup, ButtonGroupOrientation | Joins a row (or column) of buttons into a single seamless control, merging their borders and rounding only the outer corners |
| Calendar.md | Calendar, CalendarView | Showing and editing your own events over time, in month, week, day and agenda views: bookings, shifts, appointments |
| Card.md | Card, CardStyleType, CardHeader, CardMedia, CardContent, CardActions, ThemeColor | Surface container for grouping related content — list items, profile cards, dashboard tiles, product listings |
| Carousel.md | Carousel | A slide track for stepping through a set of items one at a time, with arrows, dots, and drag/swipe support, plus optional autoplay |
| ChatComposer.md | ChatComposer | The field a chat message is written in, with its send button: under a ChatMessageList, which shows a whole chat put together |
| ChatMessage.md | ChatMessage, ChatMessageStyleType, ChatMessageStatus | One message in a conversation: a messenger bubble, a support agent's reply, an AI assistant's answer |
| ChatMessageList.md | ChatMessageList | The scrolling conversation of a chat: a messenger, a support widget, an AI assistant |
| Checkbox.md | Checkbox | Boolean toggle input — settings, multi-select lists, accept terms, feature flags |
| Chip.md | Chip, ChipContainer, ChipStyleType, ThemeColor | Compact tag, label, or filter token — selected items, applied filters, category badges, user avatars with remove |
| ColorPicker.md | ColorPicker | Hex colour selection — theme editors, annotation tools, customisation panels |
| CommandPalette.md | CommandPalette | A search-first dialog for running any command or jumping anywhere in the app from the keyboard, usually opened with ⌘K / Ctrl+K |
| ConfigProvider.md | ConfigProvider, useLocale, Presentation | Set global **default props** for library components once, near the root of your app, instead of repeating the same prop on every instance |
| ConfirmModal.md | ConfirmModal | Confirmation dialog before destructive or irreversible actions — delete, archive, send, overwrite |
| CopyButton.md | CopyButton | Icon-only button that copies a value to the clipboard: an invoice number, an ID in a table row, an API key or share link inside an `Input` |
| DatePicker.md | DatePicker, dateToDay, dayToDate, dateToTime, dayTimeToDate | Date selection input — booking forms, birth date fields, date range pickers, deadline selectors |
| Divider.md | Divider, DividerTextPosition, DividerOrientation | A rule that separates sections of content — horizontal by default, filling the width of its parent |
| Drawer.md | Drawer | Inline horizontal panel that collapses and expands by animating its width — side panels, navigation rails, detail/filter panes that slide open beside the main content |
| DropZone.md | DropZone, DropZoneReturnType, DropZoneStatus | Drag-and-drop file upload area, optionally click-to-open the native picker |
| Dropdown.md | Dropdown, Presentation | Single-value selection from a list — country pickers, status selectors, category filters, any select-style input |
| EmptyState.md | EmptyState | Placeholder shown when a list, table, or search result has no items — empty inboxes, no search matches, freshly-created sections |
| ErrorBoundary.md | ErrorBoundary | Wrap any subtree whose render errors shouldn't take down the rest of the app — a widget, a route, a third-party embed, a list row |
| Fab.md | Fab | Floating action button — primary screen action (create, add, compose), optionally with a speed-dial sub-menu |
| Flip.md | Flip | Flip a front/back pair of content 180°, like a physical playing card |
| FloorPlan.md | emptyFloorPlan, addWall, derivePlan | The shared data model behind `RoomDrawer` and `RoomViewer` |
| FormValidator.md | FormValidator, Validators, useFormValidator | Track and validate a single form field's value, dirty/touched state, and errors |
| FormValidatorGroup.md | useFormValidatorGroup, FormValidatorGroup, FormValidator, Validators | Manage a whole form — multiple fields, a single submit handler, validate-on-submit, dirty/touched tracking across the form, and mapping server-side errors back onto the right fields |
| Hooks.md | useHotkeys, formatHotkey, useMediaQuery, useCoarsePointer, useVisualViewportSize, useScrollEdges, useSwipeDismiss, useOverlay, useClipboard, tabbablesIn | Standalone React hooks the library uses internally and publishes for the same jobs in your own code: keyboard shortcuts, media queries, the mobile keyboard's viewport, scroll-edge detection, swipe-to-dismiss, copying to the clipboard, and the Escape / focus-trap / scroll-lock behaviour behind a custom overlay |
| IconPicker.md | IconPicker | Pick one item from a visual icon/image grid — category selectors, emoji-style pickers, avatar selectors, anything where each option is best represented by an icon or image rather than text alone |
| IdleManager.md | IdleManager | Show something only once the user has stopped interacting — a screen saver, a session-timeout warning, an "are you still there?" prompt, an idle dashboard view |
| Image.md | Image | An `<img>` that holds its place while it loads: a skeleton the image's size, or its thumbnail, then the image, and a fallback if it fails |
| Input.md | Input, InputType | Text input with floating label, validation state, and type variants |
| InputDropdown.md | InputDropdown | Combobox-style input — the user can type to filter and also pick from a dropdown list |
| InteractableDiv.md | InteractableDiv | Accessible `<div>` that behaves like a button — click and keyboard (Enter/Space) events, with proper ARIA semantics |
| KanbanBoard.md | KanbanBoard, KanbanColumn | Drag-and-drop board with columns — task management, workflow stages, status boards (e.g. Todo / In Progress / Done) |
| KlipyPicker.md | KlipyPicker | GIF picker backed by the Klipy API — chat reactions, message composers, content authoring tools |
| Lightbox.md | Lightbox | A full-screen viewer for a set of images, opened from thumbnails: swipe, the arrows or the arrow keys move between them, and each one zooms and pans like a `PanZoom` |
| ListItem.md | ListItem | One row of a list that stands for a record: a customer, an invoice, a delivery note |
| Loader.md | Loader, LoaderType, ThemeColor | A loading indicator for something in progress with no known end: a fetch, a save, a connection |
| LoadingOverlay.md | LoadingOverlay, Loader, LoaderType | Covering content while it reloads: a table refreshing, a form saving, a panel fetching its next page |
| Menu.md | Menu, menuHotkeys | A list of actions hanging off a trigger — a row's "more" button, a toolbar overflow, a context menu on a card |
| Modal.md | Modal | Full overlay dialog — forms, detail views, confirmations, wizards that need to block the rest of the UI |
| MultiSelect.md | MultiSelect | Pick several options from a list |
| NumberInput.md | NumberInput | Numeric input with formatting — prices, quantities, percentages, phone numbers with separators |
| OptionPicker.md | OptionPicker | Segmented control for picking exactly one option from a small set — view modes, sort directions, simple tab-like switches |
| OtpInput.md | OtpInput, OtpInputCharset | A one-time-passcode / verification-code input, rendered as one box per character |
| Overscroll.md | Overscroll | Mobile-style collapsing header that shrinks as the user scrolls — profile pages, detail screens, anywhere a large hero area should yield space to content on scroll |
| Pagination.md | Pagination | Move through server-paged results a page at a time — a results list, an admin table, a report |
| PanZoom.md | PanZoom | A zoomable viewport onto one image, SVG or any other content, inline on the page: zoom with buttons, a pinch, Ctrl + wheel or a double tap, then drag to look around |
| Popover.md | Popover | Any panel anchored to a trigger: profile cards, quick forms, action menus, pickers, filter dropdowns |
| ProgressBar.md | ProgressBar | Visual progress indicator — file uploads, multi-step completion, storage usage, stacked stats (e.g. budget breakdown) |
| RadioGroup.md | RadioGroup, RadioGroupOrientation | Single-choice selection from a small, always-visible set of options — plan pickers, shipping methods, yes/no/maybe, settings with 2–5 mutually exclusive choices |
| Rating.md | Rating | A star rating input or display, for review scores, feedback prompts, and quality indicators |
| RevealLens.md | RevealLens | A two-layer reveal/magnifier effect — an `overlay` covers a `background` by default, and moving the pointer over the component uncovers the `background` in an area around the cursor |
| Ripple.md | Ripple | Material-style ink ripple effect on any interactive element — buttons, list items, cards, tiles |
| RoomDrawer.md | RoomDrawer | An interactive floor plan editor |
| RoomViewer.md | RoomViewer | Shows a floor plan someone can look at and click, without letting them change it — a building directory, a booking screen, a dashboard beside a room list |
| ScrollFade.md | ScrollFade, ScrollFadeAxis | A scrollable box whose content dissolves at the edge it is scrolling past, instead of being cut off mid-line against it |
| ScrollbarProvider.md | ScrollbarProvider | Applies custom scrollbar styling to a scrollable region — use to keep scrollbar appearance consistent with the theme |
| SearchInput.md | SearchInput | Search field with built-in debouncing and an optional result count line — list filters, search-as-you-type pages |
| SectionHeader.md | SectionHeader, SectionHeaderSize | A reusable heading block for the top of a page, panel, card, or any section — a title with an optional subtitle, a leading icon or thumbnail, and a trailing actions area |
| Skeleton.md | Skeleton, SkeletonVariant, SkeletonAnimation | Content-shaped loading placeholder, shown while real content is being fetched — more polished than a Loader for content-heavy pages (lists, cards, profile headers) |
| Slider.md | Slider | Let the user pick a number from a continuous or stepped range by dragging a thumb: volume, brightness, price ranges, zoom, quality presets |
| SplitButton.md | SplitButton | A primary action button with a small attached dropdown for closely related secondary actions, e.g. "Save ▾" opening "Save as draft" / "Save and close" |
| Stepper.md | Stepper, StepperOrientation, StepperStyleType, StepperCollapse, StepperLabelPlacement, ThemeColor | Step progress indicator for multi-step forms, onboarding flows, and checkout wizards |
| Sticky.md | Sticky | Pin a piece of content to the top of the viewport once the page scrolls past it — section headers, toolbars, filter bars, table headers |
| Switch.md | Switch | Sliding boolean toggle — settings panels, feature flags, preference lists |
| TabHeader.md | TabHeader | A horizontal row of tabs to switch between sibling views or sections — settings panels, dashboards, detail pages |
| TemplateEditor.md | TemplateEditor, TemplateEditorPalettePosition, parseTemplate | Authoring an email or message template that mixes prose with placeholders resolved at send time, e.g. `Hello {{receiver.firstName}},` |
| Textarea.md | Textarea | Multiline text input — comments, descriptions, notes, code snippets, long-form content |
| ThemeColor.md | ThemeColor | The `color` prop of every component that takes one: the theme's named colours, or any CSS colour of your own |
| ThemeProvider.md | ThemeProvider, useTheme | Applies a theme to its subtree, injecting its CSS custom properties as inline styles so every component inside can use them |
| Tilt.md | Tilt, TiltGroup | Wrap content that should tilt toward the cursor in 3D |
| TimeInput.md | TimeInput | Time-of-day selection — appointment times, opening hours, shift starts, reminders |
| Timeline.md | Timeline, TimelineAlignment, ThemeColor | A vertical list of dated events, connected by a line, for audit logs, activity feeds, order status history, and deploy logs |
| Timer.md | Timer | A countdown timer with a depleting ring, add/remove-time and pause/cancel controls, and an imperative ref API |
| Toast.md | showToast, ThemeColor | Non-blocking notifications — save confirmations, error alerts, info messages, warnings |
| Tooltip.md | Tooltip | Contextual help, info icons, field explanations, error pointers |
| Tree.md | Tree, flattenTree | A hierarchical list the user can expand, collapse and pick from: categories, nested locations, folder structures, org charts |
| VirtualList.md | VirtualList | Efficiently render large lists or tables — thousands of rows rendered with a virtualised scroll window so only visible rows are in the DOM |
| WeatherIcon.md | WeatherIcon, WeatherType | An animated weather symbol for a forecast, a dashboard widget or a day selector, one per condition a forecast reports |
| Wizard.md | Wizard | Animated step container for multi-step forms and flows |

<!-- components:end -->
