# @azmr/ui

> Release history for @azmr/ui.

Source: https://platform.azmara.io/docs/changelog/ui

## 0.12.1

### Patch Changes

- 1baf44e: `StateView` `titleAs` accepts `h1`, for a state that is the whole page (a 404 page, a root error page) with no other heading.

## 0.12.0

### Minor Changes

- d31c392: Add loading states (PAZM-122). `Spinner` gains `size` (`xs` to `xl`, or `inherit` for 1em; default unchanged), `tone` (Progress's tones; default still the text colour) and `delay` (milliseconds before it fades in, in CSS, so it works in Server Components). New `@azmr/ui/loading` (Server Components): `Loading`, a spinner with a visible, announced message (beside, below or screen-reader only) as `inline`, `section` (Suspense fallbacks) or `page` (`loading.tsx`, 200 ms delay by default), with an optional `logo` that a ring spins around while the logo stays upright, or that pulses. New `@azmr/ui/loading/client`: `LoadingOverlay` keeps refetching content on screen, dimmed and `aria-busy`, with a spinner and message over it; `LoadingBar` is a GitHub-style bar fixed to the top of the page that creeps towards 90% while loading, then fills and fades, or shows a known `value`; `useLoadingState(loading, { delay, minDuration })` gives the same show-late, stay-long timing for your own indicator. `progressTones` moved to its own module (still exported from `@azmr/ui/progress`).
- 54c7ca1: Add route-level states (PAZM-82). New `@azmr/ui/state-view` (Server Component): `StateView`, one layout (icon, title, description, action) for a region's `loading`, `empty` and `error` states, as a section or a full page; loading is a polite status built on `Loading` (logo and delay included, so it doubles as the page loader), error is an alert, and the title's heading level is configurable. New `@azmr/ui/error-boundary`: `ErrorBoundary` with an `onError(error, info)` reporting hook for any service, `reset` and `resetKeys` for recovery, a custom `fallback` or `fallbackProps`, and `rethrow` to let framework errors through (pass Next.js's `unstable_rethrow` so `redirect()` and `notFound()` keep working). The default fallback never shows the error's message. Add the `inbox` icon to `@azmr/ui/icons/lucide`.

## 0.11.0

### Minor Changes

- caeef37: Forms now work with controls from other libraries, and rows can be changed from code.

  - `Field` accepts a function child for any control that isn't a Base UI one, such as a Radix (shadcn) Select trigger, a native `<select>` or a third-party date picker. It gets `{ id, aria-labelledby, aria-describedby, aria-invalid, aria-required, aria-errormessage }` to spread on the control, so the label points at it and the description and error describe it. New type: `FieldControlProps`.
  - `FormField`'s `render` gets the same props as `control`. Spread them on the element that takes focus. The element `render` returns no longer gets `aria-required` or `aria-errormessage` cloned onto it, because `control` carries them.
  - `FormFieldArray` takes `fieldArray`, your own `useFieldArray` result, so other parts of the form can `replace()` or `append()` rows. Its `row.name()` is typed: `row.name("qty")` is `` `lines.${number}.qty` ``, ready for `FormField`'s `name`, and an unknown field fails to compile. In an untyped form it stays a `string`. New type: `FormFieldArrayRow`.
  - `NumberField` and `MoneyField` take `groupClassName` for the border, height and radius around the input, and mark their parts with `data-slot` (`number-field`, `number-field-group`, `number-field-input`).

## 0.10.0

### Minor Changes

- 724dcff: Add `@azmr/ui/field-array`: `FieldArray`, a repeating group of fields (invoice lines, contacts, addresses) that works with any form state. Rows are added, removed and reordered by pointer drag (mouse, touch, pen; Escape cancels), by the arrow keys, Home and End on each row's handle, or with optional move buttons. `min` and `max` disable the remove and add buttons without taking them out of the tab order; `empty` shows when there are no rows, `header` adds column headings for a table-like layout and `footer` renders totals from them. Focus moves to a new row's first field, to the neighbour of a removed row and back to a moved row's handle, and every change is announced in a polite live region; all text is replaceable through `messages`. Add `FormFieldArray` to `@azmr/ui/rhf`, which runs `useFieldArray`, shows the array's own error under its legend and feeds `footer` the current values without re-rendering the rows. Fix `FormField` replacing the control's own `ref` when it registers it: both refs now get the element.

## 0.9.0

### Minor Changes

- 1322095: Per-component class lists replace the stylesheet's `@source` scan, so an app only generates CSS for the components it uses.

  `tokens.css` (and so `styles.css`) no longer contains `@source "./**/*.js"`. That rule made Tailwind scan every file in the package, so an app that used Button and Input still generated the CSS for toast, the drawer, the accordion and every other component: in app.azmara.io, 523 of its 1,289 utility classes, about 10 kB gzip of render-blocking CSS and roughly 150 ms of first paint.

  Instead, each entry point ships a class list, generated at build time with Tailwind's own scanner: `@azmr/ui/classes/<entry>.css` holds exactly the classes that entry's JavaScript uses (shared chunks included), and `@azmr/ui/classes.css` holds them all.

  **Migration (required):** add the class lists to your global CSS, after the stylesheet. Either list the entries you use:

  ```css
  @import "tailwindcss";
  @import "@azmr/ui/styles.css";
  @import "@azmr/ui/classes/button.css";
  @import "@azmr/ui/classes/input.css";
  ```

  or keep generating everything, as before, with one line:

  ```css
  @import "@azmr/ui/classes.css";
  ```

  Without either, components render unstyled. A Button and Input app goes from about 13 kB to about 6 kB of CSS (gzip). The build checks that `classes.css` generates exactly what the old scan did, and that a Button and Input app stays within a 6.5 kB budget.

## 0.8.0

### Minor Changes

- 480388b: Add the debounce contract to `Input`: `debounceMs` delays `onValueChange`, `onCommit` fires on blur or Enter (not while an input method is composing) after sending any waiting value, `data-pending` marks a waiting value, a waiting value is dropped on unmount, and an unchanged value is never reported or committed twice. The native `onChange` stays immediate, so `register()` is unaffected. `@azmr/ui/hooks` now exports `useDebouncedCallback` (stable, with `flush`, `cancel` and `isPending`) and `useDebouncedValue`.
- b5585b6: Add the form adapters. `@azmr/ui/rhf` (React Hook Form 7, optional peer): `Form` (a `noValidate` form with `FormProvider` and `handleSubmit`), `FormField` (a `Field` that registers its control with `register()`, so there is no `Controller`, shows the field's error and required state, and switches to `useController` with `controlled` or `render` for controls without an input ref) and `FormErrorSummary`. `@azmr/ui/zod` (Zod 4, optional peer, server-safe): `zodResolver` (async refinements, parsed output, nested and array paths, `root` for object-level issues, `criteriaMode: "all"`) and `fieldsFromSchema`/`fieldFromSchema`, which describe a schema's fields (label, control type, required, limits, enum options, metadata) for generated forms. `ErrorSummary` now lists errors in the order their fields appear on the page, with form-level errors first.
- 4deeaee: Add `@azmr/ui/number-field`: `NumberField` and `MoneyField` on Base UI's NumberField. They format with `Intl.NumberFormat` for the locale (`LocaleProvider`, en-NZ and NZD without one), report a real `number` or `null` and never a formatted string, clamp to `min`/`max`, support `step`, `allowNegative`, `decimalScale`, `grouping`, `format`, an optional stepper, paste parsing and a wheel guard, and follow the `debounceMs`/`onCommit` contract. `MoneyField` rounds to the currency's minor unit. `register()` works without a `Controller`: `ref` is the named hidden number input, `onChange`/`onBlur` get real DOM events on it, and `numberOrNull` is a ready `setValueAs`. Both fit inside an `InputGroup`.

## 0.7.0

### Minor Changes

- aaace08: Add `@azmr/ui/password-input`: `PasswordInput` with a show/hide toggle (a button with `aria-pressed` and a fixed "Show password" name), an optional strength meter (`strength`, with a small length-first default scorer or your own, such as zxcvbn-ts) and an optional requirement checklist (`requirements`, with ready-made `passwordRequirements`) whose changes are announced politely. `autoComplete` defaults to `current-password`, paste is never blocked, and the password is hidden again when its form submits so password managers offer to save it. The `ref` is the real `<input>`, so `register()` works without a `Controller`.

## 0.6.0

### Minor Changes

- e085ee5: Add `@azmr/ui/form`: `Fieldset` (legend, description and group error for grouped controls), `FormSection` (titled sections for long forms, with one or two columns and an optional heading-beside-fields layout that responds to the section's own width) and `ErrorSummary` (the GOV.UK error summary: after a failed submit it takes focus and lists each error as a link that moves focus to its field). `ErrorSummary` reads React Hook Form's `formState.errors`, Base UI Form's `errors` or a plain record, so it needs no `Controller`.
- 16ab0f1: Add `@azmr/ui/input-group`: `InputGroup`, `InputGroupInput`, `InputGroupAddon` and `InputGroupButton` put text, icons and buttons inside an input's border (a currency or unit, a search icon, a clear or copy button). The border and focus ring wrap the whole group and the height follows the density tokens. Text addons join the input's accessible description and icon addons are hidden from screen readers. Buttons are `type="button"` and sit in the tab order after the input. Inside a `Field`, `aria-required` and `aria-errormessage` reach the input, and `InputGroupInput` forwards the real `<input>` ref so `register()` works without a `Controller`.

## 0.5.0

### Minor Changes

- 690ba1f: Add `Field` (`@azmr/ui/field`) and `Textarea` (`@azmr/ui/input`). `Field` wraps one control with a label, description, error and required marker on Base UI's Field, and wires `id`, `aria-labelledby`, `aria-describedby`, `aria-invalid`, `aria-errormessage` and `aria-required`; errors are announced through a polite live region. Pass `errors.<name>?.message` from React Hook Form as `error`. `FieldRoot`, `FieldLabel`, `FieldDescription` and `FieldError` compose custom layouts. `Textarea` forwards the real `<textarea>` ref and has an `autoResize` option.

## 0.4.1

### Patch Changes

- ea59e9b: Package metadata: `homepage` now points at each package's page on platform.azmara.io (docs.azmara.io never resolved), `bugs` links to the GitHub issues, and README doc links follow. `@azmr/ui` gets a description that matches the design system it now is.

## 0.4.0

### Minor Changes

- d756ee0: Make the server-safe entries truly server-safe. lucide-react 1.x's icons are client components, so `@azmr/ui/display`, `@azmr/ui/stepper`, `@azmr/ui/breadcrumb`, `@azmr/ui/pagination` and `@azmr/ui/icons/lucide` sent Lucide's runtime (about 44 kB before compression) to every page that used them, even from a Server Component.

  - Built-in icons are now glyphs: plain SVG rendered on the server from the `lucide` package's shape data, with exactly the markup lucide-react renders (attributes, `lucide lucide-<name>` classes and alias classes, `aria-hidden`), tested against lucide-react for every icon. The client components (menu, modal, toast, accordion, split button) use them too.
  - `lucideIcons` and the named `Icon` from `@azmr/ui/icons/lucide` now hold these glyphs. They take the same props as lucide-react's icons (`size`, `color`, `strokeWidth`, `absoluteStrokeWidth`), but their TypeScript type is `Glyph` rather than `LucideIcon`.
  - `lucide` becomes a dependency (shape data only, no React). `lucide-react` stays a dependency and is upgraded to 1.59.0.
  - The build now fails if a server-safe entry imports `lucide-react`, and the consumer check builds a Next.js page from every server-safe entry and fails if its client manifest gains anything from `@azmr/ui` or Lucide.

## 0.3.0

### Minor Changes

- d7d6b2b: Add `@azmr/ui/motion`, an optional module for the `motion` library. `MotionProvider` sets up `LazyMotion` (the animation features load on demand, `dom` by default or `max` for layout animation, drag and reorder) and `MotionConfig reducedMotion="user"`, and is strict by default so the full `motion` component cannot be pulled into your bundle by accident. The module re-exports `m`, `AnimatePresence`, `Reorder`, `animate`, `useAnimate`, `useInView`, `useMotionValue`, `useSpring`, `useTransform` and `useDragControls`, and adds `springs` (`gentle`, `snappy`, `bouncy`).

  `motion` is an optional peer dependency (`^14.0.0`, tested against 14.2.0): installing `@azmr/ui` does not install it, and nothing outside this module imports it. The wrapper is about 0.5 kB.

## 0.2.0

### Minor Changes

- 7edd869: Add the button family. `@azmr/ui/button` gains `IconButton` (a required `label` is its accessible name, type-enforced) and `ButtonGroup` (a named, joined group). New entries: `@azmr/ui/toggle` (`Toggle`, `ToggleGroup`: single or multiple selection, roving keyboard) and `@azmr/ui/split-button` (`SplitButton`: a primary action plus a menu of related ones).

- 3efa36a: Add `Button`, `Input` and `Spinner`, built on Base UI primitives, the design tokens and CVA variants.

  - `Button`: variants `default | secondary | outline | ghost | destructive | link`, sizes `sm | md | lg | icon`, `loading` / `loadingText` / `startIcon` / `endIcon`, `type="button"` by default, `render` for links. A loading button ignores activation but stays focusable. Icon-only buttons are type-checked to require `aria-label` or `aria-labelledby`.
  - `Input`: `ref` is the real `<input>`, so `react-hook-form`'s `register()` works with no `Controller`; native `onChange` and typed `onValueChange`; density-token height and padding; full-strength focus ring.
  - `Spinner`: decorative by default or a labelled status; stays animated under reduced motion.
  - New subpaths `@azmr/ui/button`, `@azmr/ui/input`, `@azmr/ui/spinner` (client entries) and root exports for all three, plus `buttonVariants`.
  - `@base-ui/react` and `class-variance-authority` become dependencies. Tree-shaking is budget-tested: importing only `useSignal` from the root costs 0.1 kB.

- 4b117dc: Add the `@azmr/ui/styles.css` design tokens and Tailwind v4 theme mapping.

  - shadcn-compatible token names (`--background`, `--primary`, `--ring`, `--radius`, `--sidebar-*`) plus `--success`, `--warning`, `--info`, `--chart-1..8`, `--motion-*`, `--elevation-*`, `--z-*` and `--density-*`.
  - OKLCH colours with `light-dark()`: follows the system by default, or force a mode with `.dark` / `.light` / `data-theme`. No JavaScript, no flash.
  - Density via `data-density="compact|spacious"`; reduced motion collapses the motion tokens and adds a safety net (spare essential motion with `data-motion="essential"`).
  - Verified by tests: WCAG AA contrast for every token pair in both modes, 3:1 for the focus ring and chart colours, chart colours distinguishable under protan/deutan/tritan simulation, and the real Tailwind v4 compiler generating every utility.
  - `package.json` now exports `./styles.css` and marks only CSS as side-effectful (`sideEffects: ["**/*.css"]`).

- 0fb248b: Add `@azmr/ui/disclosure`: `Accordion`, `Collapsible` and `Tabs` (horizontal and vertical), built on Base UI with token styling, heading-wrapped accordion triggers, full keyboard maps (including accordion arrow navigation, which Base UI 1.9 does not implement) and height animation that follows the motion tokens. Add `useUrlTab` to `@azmr/ui/hooks`, which keeps the selected tab in a search parameter and ignores any value not in the allowed list.

- fe55409: Add `Progress`, `Avatar`, `AvatarGroup` and `Chip`.

  - `@azmr/ui/progress`: linear and circular, determinate and indeterminate (`value={null}`), with `min`/`max`, tones, `showValue` and a required accessible name. An indeterminate indicator is partial and pulses, and keeps pulsing under reduced motion (a still full bar would read as complete). Tone colours are tested for 3:1 against the track and the page in both themes; the `warning` tone uses `--warning-text`.
  - `@azmr/ui/avatar`: an image with initials as the fallback when it is missing or fails to load, sizes and shapes, a required name, `alt` or `decorative`, `getInitials` (whole characters, never half a surrogate pair), and `AvatarGroup` with a labelled `+N` overflow.
  - `Chip` (in `@azmr/ui/display`, a Server Component): a removable Badge with an accessible remove button.

- fed8637: Add the display components (`@azmr/ui/display`), all Server Components: `Card` (with Header, Title, Description, Content, Footer), `Badge` (default, secondary, outline and the status variants), `Alert` (info, success, warning, destructive; assertive `role="alert"` for problems and polite `role="status"` otherwise, a default icon per variant, optional labelled dismiss button), `Skeleton` (decorative, pulses, stilled by the reduced-motion rule) and `EmptyState`. The status colours are checked for contrast on the blended tint surface in light and dark, over both the page and a card, from the component class names; this found and fixed a destructive badge at 4.26:1.

- 6a86517: Add `@azmr/ui/floating`: `Tooltip` (with `TooltipProvider` and the shared `TOOLTIP_DELAY_MS`), `Popover` and `HoverCard`. Collision-aware, portalled above overlays, capped to the available space, and animated with the motion tokens. The tooltip adds `role="tooltip"` and `aria-describedby` on its trigger while open, which Base UI 1.9 omits.

- 2799969: Foundation for the `@azmr/ui` redesign (see `docs/superpowers/specs/2026-10-15-azmr-ui-design.md`).

  - **`@azmr/core` is now a peer dependency** (`>=0.0.2 <1.0.0`) instead of an exact-pinned dependency. Two copies of core give separate scheduler/tracking state and incompatible `Signal` types, so the app's own copy must be the one `@azmr/ui` uses. If you already followed the README and installed both packages, nothing changes; otherwise add `@azmr/core` to your dependencies.
  - **New entry points:** `@azmr/ui/hooks` (client) and `@azmr/ui/lib` (server-safe, exports `cn`). `cn()` is also exported from the package root.
  - **`"use client"` is now emitted** on the client entries (`@azmr/ui` and `@azmr/ui/hooks`), so they can be imported from Next.js App Router Server Components' client boundaries without wrapping. `@azmr/ui/lib` has no directive and can be imported from Server Components.
  - **`useSignal` no longer resubscribes on every render.** `subscribe` and `getSnapshot` are memoised on the signal; passing a different signal resubscribes once. Also verified against `computed()` signals.
  - The build now verifies its own output (`scripts/verify-dist.mjs`): directives per entry, type declarations present, and the `exports` map matching the build entries.

- eb89693: Add the icon system.

  - `@azmr/ui/icons`: `Icon` renders any icon component (Lucide, Tabler or your own SVG) with size tokens (`xs`..`xl` or a number), `strokeWidth`, `spin` and accessibility (decorative by default; `aria-label` / `aria-labelledby` make it a labelled image). It is a Server Component. `createIconSet()` builds a typed `<Icon name>` for a fixed set, and `registerIcons()` registers custom SVGs by name. An unknown name renders nothing and warns once in development.
  - `@azmr/ui/icons/lucide`: a curated set of about 60 Lucide icons by name, with Lucide's own kebab-case names.
  - `@azmr/ui/icons/mui-map`: migration data mapping over 300 `@mui/icons-material` icons (variants and the legacy `Outline` spelling resolved) to Lucide and Tabler, validated in tests against the real packages, with the deliberately unmapped common names listed.
  - `Spinner` is now a Server Component (no `"use client"`), so it can be used in `loading.tsx` and Suspense fallbacks.
  - `lucide-react` becomes a dependency. `react-icons` is deliberately not bundled.
  - The build gains a `serverComponents` entry kind: `verify-dist` now allows stateless React (no hooks, no context) in server entries and follows imports through nested shared chunks.

- 04f06eb: Add the layout components (`@azmr/ui/layout`), all Server Components: `Stack` (responsive direction, gap on the 4px scale or the density token, align, justify, wrap, dividers between children), `Grid` and `GridItem` (CSS grid with a responsive 1 to 12 column count, spans per breakpoint, and `minItemWidth` for an auto-fit grid), `Container`, `Separator` (a real `hr`, a labelled vertical separator, or decorative) and `AspectRatio`. They replace MUI `Box`, `Stack`, `Grid` and `Container`. `@azmr/ui/layout/mui-map` is data and helpers for the migration codemod (spacing to gap, breakpoints, Container `maxWidth`, Grid item props), returning `undefined` or `unsupported` for what has no equivalent. Inputs that reach a style attribute (`minItemWidth`, `ratio`) are validated. The responsive class tables are generated and checked for drift so Tailwind always sees the class names.

- 6825a0f: Add `@azmr/ui/menu`: `DropdownMenu` and `ContextMenu` with items, link, checkbox and radio items, groups, separators, shortcut hints and submenus. Menus sit on the popover layer so they appear above dialogs. Full WAI-ARIA keyboard map (open keys, arrows with wrap, Home and End, typeahead, submenu keys, Escape one level at a time).

- d2275af: Add `@azmr/ui/modal`: `Dialog` (sizes, stackable), `AlertDialog` (no outside dismissal, focus starts on Cancel) and `Drawer` (four edges, swipe to dismiss). All trap focus, make the page inert, lock scroll and return focus. Add the `--scrim` colour token (and the `bg-scrim` utility) for the dimming layer behind modal surfaces.

- 259c350: Add the motion system. `@azmr/ui/styles.css` now includes CSS presets (fade, scale, four slides, collapse, stagger, shimmer, check draw, a page cross-fade) driven by the motion tokens, with a reduced-motion rule for each; new tokens `--motion-stagger-step` and `--motion-duration-ambient`. New entries: `@azmr/ui/presets` (server-safe `Collapse`, which animates `grid-template-rows` and is inert while closed, `Stagger`, `AnimatedCheck`, and the typed `motionPresets` map) and `@azmr/ui/transition` (`Transition`, a feature-detecting wrapper for React's `ViewTransition` that is a no-op until your React has it, and `CountUp`). `useReducedMotion` is exported from `@azmr/ui/hooks`. `Skeleton` gains `shimmer`.

- 538a678: Add a shadcn-compatible registry (namespace `@azmr`, served at `https://platform.azmara.io/r/{name}.json`). `npx shadcn@latest add @azmr/button` copies `button.tsx` and `spinner.tsx` into the app, installs `@base-ui/react` and `class-variance-authority`, and adds a `tokens` item (density, motion, elevation and a few colours, never overriding the app's own palette) to `globals.css`. The registry source is generated from the package source with only import paths rewritten, and an unmapped relative import fails the build. The L2 primitive wrappers now re-export Base UI under its own names; the public API is unchanged. Registry files and fixtures are not published to npm.

- d707f33: Add `ScrollArea` (`@azmr/ui/scroll-area`), built on Base UI: a scrollable region with token-styled scrollbars that fade with the motion tokens (and stop animating under reduced motion). The viewport is a keyboard-focusable `role="region"`, so `aria-label` or `aria-labelledby` is required by the types. `orientation` (vertical, horizontal, both), `scrollbar` (`hover` or `always`), `keepMounted`, `viewportClassName` and `viewportRef`. Adds the `primitives/scroll-area` L2 wrapper.

- 63884e3: Add four server-safe components, each its own entry: `@azmr/ui/table` (a semantic `Table` with caption, header, footer, sticky header, striping, a named scroll region and `numeric` cells), `@azmr/ui/breadcrumb` (`Breadcrumb` parts and a data-driven `Breadcrumbs` with `maxItems` collapsing), `@azmr/ui/pagination` (`Pagination` as real links or buttons, plus the pure `getPageRange` the data grid can reuse) and `@azmr/ui/stepper` (a status `Stepper`, horizontal or vertical, with every state also said in text). `buttonVariants` now lives in a pure module so server components can use it.

- 630d373: Status colours in buttons and in `createTheme`.

  - `Button` gains `success`, `warning` and `info` variants (`destructive` was already the "danger" button).
  - `createTheme` now accepts `secondary`, `success`, `warning`, `info` and `destructive` alongside `primary`. Each is derived to be AA in both modes. Labels stay at least 4.5:1 on a solid button's **hover** surface, checked with the same gamma-space blend a browser uses (this also tightens `primary`: a brand colour that only just passed at rest could fall below 4.5:1 on hover). `success`, `info` and `destructive` are also readable as text on the page and on cards, and `warning` gets a readable `--warning-text`. Errors name the option that was wrong.
  - The default light `success` colour is slightly darker: its button label measured 4.46:1 on hover.
  - Hover contrast of every solid Button variant is now tested over the page and a card in both modes.

- f534058: Theme, density and locale support.

  - **`@azmr/ui/theme`** (server-safe, no React): `createTheme({ primary, radius, font, density })` turns a brand colour into token overrides with guaranteed WCAG AA (primary pair >= 4.5:1, focus ring >= 3:1, both modes); cookie helpers (`readCookie`, `serializeCookie`, `parseThemePreference`, `parseDensity`); `htmlAttributes` / `htmlAttributes.fromCookies(header)` to render `data-theme` and `data-density` on the server with no inline script; `createLocale` (en-NZ / NZD / Monday week start by default, cached `Intl` formatters, date-only strings never shift across time zones); `themeInitScript()` as an optional inline fallback for static sites.
  - **`@azmr/ui/react`** (client): `ThemeProvider` / `useTheme` (system tracking, cookie persistence, density) and `LocaleProvider` / `useLocale`.
  - `styles.css` gains a `--font-sans-stack` token mapped to Tailwind's `font-sans`.
  - Build and conventions: the verifier now fails if a server-safe entry can reach React (including through shared chunks), and a new `server-safe-no-react` convention enforces the same at source level.

- c835cbf: Add `@azmr/ui/toast`: a `Toaster` and an imperative `toast` API (`toast.success`, `error`, `info`, `warning`, `loading`, `promise`, `update`, `dismiss`). Toasts stack, dedupe by id, pause while hovered or focused, can be swiped away, take an action such as Undo, and are announced politely (errors urgently). Six positions, on the `--z-toast` layer.

- 12aef93: Add `@azmr/ui/tokens.css`: the design tokens, themes, density, motion and Tailwind mapping without the global base rules. `styles.css` is now `tokens.css` plus those rules (the `body` colours and the default border and outline colour), so existing imports render exactly as before. Use `tokens.css` in an app that already has global styles, such as one migrating from another UI library, or a docs site.

- 1cffcfd: Add `Typography`, `Heading`, `Text`, `Link`, `Code` and `Kbd` (`@azmr/ui/typography`), all Server Components.

  - `Typography`: a type scale (`h1` to `h6`, `lead`, `body`, `small`, `caption`, `overline`) where the variant picks the semantic element and the look, from colour tokens only. `as` overrides the element without changing the look; `align`, `noWrap` and `gutterBottom` match the MUI props.
  - `Heading` separates the semantic `level` from the visual `size`. `Text` has `size`, `tone` (muted, destructive, success, warning, info) and `weight`.
  - `Link` is underlined by default (not colour alone), has a full-strength focus ring, and `external` adds `target`, `rel="noopener noreferrer"` and a screen-reader hint. `as` renders a router link.
  - `@azmr/ui/typography/mui-map`: data mapping MUI Typography variants and props to ours, flagging the approximate ones, for the migration codemod.

### Patch Changes

- a5a2134: Fix `exports` condition order (`types` before `import`) and declare
  `"sideEffects": false` on `@azmr/core`, `@azmr/ui`, and `@azmr/security`
  (both the `.` and `./browser` conditions on `security`).

  The `types`-first order previously worked only because TypeScript falls
  back to the sibling `index.d.ts` when the condition order doesn't matter to
  it — bundlers with stricter `exports` resolution (e.g. Next.js's
  Turbopack/webpack) are not guaranteed to be as forgiving. `sideEffects:
  false` lets bundlers tree-shake unused named exports out of consuming
  bundles; verified with an esbuild bundle of a single named import from
  `@azmr/security/browser` — no unrelated exports leak in.

  No public API change.

- 343204f: Fix scoped themes when the bundler lowers `light-dark()` (Next.js does, for older browser targets). Lowered tokens are resolved where they are declared and inherited, so a nested `data-theme="light"` inside a dark page, or `.dark` on a panel, had no effect on colours; only the root worked. `.light`, `.dark` and `data-theme` scopes now re-declare every colour token explicitly (a test keeps them identical to the `:root` values), so nesting works however the CSS is processed. Adds about 0.6 kB gzip to the stylesheet (budget now 4 kB). Also exports the `ButtonOwnProps` type and aligns the workspace on a single React version.

- ef12296: `useSignal` is now built on `useSyncExternalStore` instead of
  `useState` + `useEffect` + `Signal.subscribe`, the exact pattern
  `useSyncExternalStore` exists to replace. Under concurrent rendering, an
  update that lands between render and commit could previously produce a
  torn UI; `useSyncExternalStore` closes that window by re-checking the
  snapshot immediately after subscribing and forcing a synchronous re-render
  if it changed.

  `getSnapshot`/`getServerSnapshot` both read via `Signal.peek()` (which never
  registers a dependency), and the hook no longer depends on
  `Signal.subscribe()` firing immediately with the current value on
  subscribe — it deliberately doesn't; the snapshot is read independently, as
  `useSyncExternalStore`'s contract expects.

  New render tests (via `react-dom/client` + `React.act`, no new
  dependency — the package's existing `react`/`react-dom` devDependencies
  cover it) verify the hook actually renders correctly: initial value shown
  without waiting on subscribe, updates after mount reflected, and rapid
  sequential `set()` calls before the next flush don't leave a stale value.
  The previous test file had zero real render tests. A `test-setup.ts` sets
  `IS_REACT_ACT_ENVIRONMENT` for React 19's `act`.

  No public API change.

- 1cffcfd: Add a `--warning-text` token (and `text-warning-text`): a warning colour that is readable as text on the page and on cards in light and dark (the yellow `--warning` and the near-black `--warning-foreground` are surface colours, and the latter was unreadable as text on the dark theme). `Text tone="warning"` now uses it. Contrast of all four semantic tone colours as text on the background and card is now tested in both modes.

## 0.1.0

### Minor Changes

- d70fccb: Upgrade workspace dependencies across the monorepo (pnpm 10 -> 11 tooling
  migration): React 18.3 -> 19.2 (`@azmr/ui`), Zod 3.24 -> 4.4 (`@azmr/security`),
  better-sqlite3 11.7 -> 12.11 (`@azmr/db`), plus vitest, tsup, and `@types/node`
  bumped to latest across all packages. `typescript` stays pinned at `^5.7.3`
  (TypeScript 7 currently breaks `tsup`'s `.d.ts` bundling via
  `rollup-plugin-dts`).

### Patch Changes

- 5bf56bb: Fix package metadata: mojibake in the description field (encoding
  mismatch, was showing garbled text on npm) and a missing LICENSE file
  in the published tarball despite being listed in `files`.

  `@azmr/ai` got the same fixes plus missing `homepage`/`repository`/
  `author`/`keywords`, but isn't included in this changeset - it's on
  changesets' ignore list and doesn't take version bumps this way.

- Updated dependencies \[5bf56bb]

- Updated dependencies \[d70fccb]
  - @azmr/core\@0.0.2

All notable changes to this project will be documented in this file.

## \[0.0.1]

### Added

- Initial release of React UI components wired to reactive Signals
- XSS-safe Grid component
- useSignal hook for automatic re-renders
- Zero boilerplate reactive UI development
