@salemcobalt/ui 0.2.0-rc.21 → 0.2.0-rc.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @salemcobalt/ui
2
2
 
3
- React 19 components for the Salem Cobalt design system: `Button`, `IconButton`, `Icon` and a shipped icon set, `BroomIcon`, `Badge`, `Card`, `Stack`, `Inline`, `Heading`, `Text`, `Kbd`, `Dialog`, `FullScreenFlow`, `Tooltip`, `Menu`, `Popover`, `PriorityNav`, `Toolbar`, `Banner`, `Spinner`, `Progress`, `Tabs`, `Input`, `Textarea`, `Select`, `FileInput` and `FilePreviewDialog`, `Checkbox`, `Switch`, `Table` (with row selection), `SelectionBar`, `Toast`, `Stat`, `Chip`, `Disclosure`, `EmptyState`, `Skeleton`, `Score`, `ReasonList`, `WordDiff`, `Quote`. Requires `@salemcobalt/tokens`.
3
+ React 19 components for the Salem Cobalt design system: `Button`, `IconButton`, `Icon` and a shipped icon set, `Avatar` and `AvatarButton`, `BroomIcon`, `Badge`, `Card`, `Stack`, `Inline`, `Heading`, `Text`, `Kbd`, `Dialog`, `FullScreenFlow`, `Tooltip`, `Menu`, `Popover`, `PriorityNav`, `Toolbar`, `Banner`, `Spinner`, `Progress`, `Tabs`, `Input`, `Textarea`, `Select`, `FileInput` and `FilePreviewDialog`, `Checkbox`, `Switch`, `Table` (with row selection), `SelectionBar`, `Toast`, `Stat`, `Chip`, `Disclosure`, `EmptyState`, `Skeleton`, `Score`, `ReasonList`, `WordDiff`, `Quote`. Requires `@salemcobalt/tokens`.
4
4
 
5
5
  ```ts
6
6
  import '@salemcobalt/tokens/css'; // custom properties, light + dark
@@ -23,42 +23,43 @@ import {
23
23
 
24
24
  Styles ship as one stylesheet inside `@layer sc`, so an app's unlayered CSS always wins. Every value in it is a token custom property; theme with `data-theme="light|dark"` on `<html>` and density with `data-density="compact"` on any ancestor. Interactive behaviour (Dialog, Tooltip and Menu focus and keyboard handling, `render` composition) comes from Base UI; see `docs/adr/0001-headless-primitives.md`.
25
25
 
26
- | Component | Notes |
27
- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
28
- | `Button` | `variant` primary · secondary · ghost · danger; `size` sm · md · lg; `iconStart`/`iconEnd`; `loading` (keeps width, `aria-busy`); `fullWidth`. `shortcut={<Kbd>N</Kbd>}` shows a key hint after the label, tinted to the variant and hidden from the name (add `aria-keyshortcuts="n"`); `iconOnly` collapses to the icon in a square button without remounting (label kept as the name, shortcut hidden). The hint is not shown where there is no fine pointer (`not all and (any-pointer: fine)`: phones, tablets without a trackpad). |
29
- | `IconButton` | Square Button for one icon. Required `label` (the accessible name); `variant` ghost (default) · secondary · primary · danger; `size`; `tooltip` (`true` shows `label`, or a string); `tooltipSide`; `round`; `loading`; `render={<a href />}` or a Next.js `Link` makes it a link. `dot` neutral · accent · success · warning · danger · info puts a status dot top-left with a ring in `--sc-icon-button-dot-ring` (default `surface-default`; set it to the header background); decorative, so say the status in `label`. |
30
- | `Icon`, `*Icon` | 49 outline icons on a 16px grid in `currentColor`, one component each (`PlayIcon`, `ChevronDownIcon` …, listed in `iconNames`); `size` xs 12 · sm 14 · md 16 · lg 20 · xl 24 · em; decorative unless `title` is given. `Icon` wraps your own paths. |
31
- | `Badge` | `tone` neutral · accent · success · warning · danger · info; `variant` soft · solid · outline; `size` sm · md; `dot`. Text only. |
32
- | `Card` | `variant` outlined · elevated · filled · plain (a quiet row: no surface, a hover wash without border or shadow, selected is `accent.wash` with a 1px `accent.border`); `padding` none · sm · md · lg; `interactive`; `selected` (visual only: pair with `aria-pressed`/`aria-selected`/`aria-current`; also turns a Score inside solid); `as` or `render` to make it an `a`/`button`/`li`. |
33
- | `Stack`, `Inline` | `gap` from the spacing scale (`none` … `4xl`); `align`; `justify`; `as`. `Inline` wraps unless `wrap={false}`. |
34
- | `Kbd` | One key per `Kbd`; `Kbd.Group label="Command K"` groups a shortcut with a `+` (or `separator="space"`). |
35
- | `Dialog` | `Root` (`open`, `defaultOpen`, `onOpenChange`, `dismissible`, `onRequestClose(reason)`: Esc, backdrop and every `Close` ask instead of closing; close by setting `open`), `Trigger`, `Content` (`size` sm · md · lg, every size clamps to the viewport minus a 16px gutter; `variant="sheet"` becomes a bottom sheet below 640px with a scrolling `Body`; `variant="fullscreen"` covers the viewport, inset 16px from 900px, with a scrolling `Body`), `Title`, `Description`, `Body`, `Actions`, `Close`. Menus, selects and tooltips opened inside render above it. |
36
- | `FullScreenFlow` | A multi-step takeover on `Dialog variant="fullscreen"` (docs/adr/0006). `Root` (`open` default `true`, required `onRequestClose(reason)` for Esc and the ×, `onNext`/`onBack` for Enter and the arrow keys outside controls, `decoration`), `Header` (`title` names the dialog, `logo`, `compactTitle` below 760px, `status`, `closeLabel`; children: `Progress`), `Progress` (`chapters` `{ label, steps }[]`, `step` counted from 1, `label`; current chapter `aria-current="step"`), `Body`, `Rail` (from 1000px, `footer`) › `RailGroup` (`label`) › `RailItem` (`current`, `done`, `disabled`, `adornment`), `Viewport` (`direction` forward · back; one keyed `Step`), `Step` (`title` an `<h2>` that takes focus, `eyebrow`, `description`, `announcement` for the live region), `Footer` (`back`, `hint`, children; 44px buttons). The app owns the step state; the flow owns motion, focus and announcements. |
37
- | `Toolbar` | `Root` (required `aria-label`, `wrap`, `gap`), `Group` (`aria-label`, `gap`), `Separator`, `Spacer`, `Button` (`render={<IconButton … />}` or `render={<Menu.Trigger render={…} />}` so the control joins the roving focus: Tab enters, arrows move, Tab leaves), `Link` (an anchor in the same navigation; `render={<IconButton label render={<a href />} />}` for an icon link). |
38
- | `Banner` | `tone` info (default) · success · warning · danger · neutral; `icon` (or `false`); `action` slot; `dismissible` + `onDismiss` (`open` to control); warning and danger are `role="alert"`, the rest `role="status"`. |
39
- | `Tabs` | `Root` (`value`, `defaultValue`, `onValueChange`), `List` (required `aria-label`, `fill`), `Tab` (`value`, `icon`, `count`, `disabled`), `Panel` (`value`, `keepMounted`). Arrow keys move, Enter/Space activates. `List variant="segmented"`: a quiet segmented control (sunken track, the active tab raised on `surface-default`, no accent, plain muted counts); `line` stays the default. |
40
- | `Input`, `Textarea` | Labelled native controls on Base UI's Field: required `label` (`hideLabel` keeps it for assistive tech), `description`, `error` (sets invalid and announces), `invalid`, `required`, `disabled`, `name`, `size` sm · md · lg (Input), `iconStart`/`iconEnd` (Input), `rows`/`resize`/`variant="code"` (Textarea; monospace, tab-size 2, no spellcheck, add native `wrap="off"` for sideways scroll), `onValueChange(string)` beside native `onChange`. Every native attribute passes through: `type="date" \| "time" \| "number"` keep the same height, `list` + a `<datalist>` gives suggestions. |
41
- | `Select` | Same field props; `options` as `{ value, label, disabled? }` or `{ label, options }` groups; `value`/`defaultValue`/`onValueChange` (`null` for none, shows `placeholder`; no sentinel option needed); `clearable` adds an × and Backspace/Delete on the trigger that call `onValueChange(null)`; `clearLabel`; `fullWidth`. Submits through `name` like a native select. |
42
- | `FileInput` | Same field props (`label`, `hideLabel`, `description`, `error`, `invalid`, `required`, `disabled`, `name`, `size`) on a drop zone built on FilePond (docs/adr/0004): `accept` (`".pdf,.docx"`, MIME types, `image/*`, as the native attribute), `acceptLabel`, `maxFileSize` (bytes or `"10MB"`), `multiple` + `maxFiles`, `files`/`defaultFiles`/`onFilesChange(File[])`, `idleLabel`, `browseLabel`. No upload server: plain `File`s go to the callback, and with `name` to the form's `FormData` (Server Actions included). Rejected files never join the list; the reason shows in the error slot and is announced. Without `multiple`, a new file replaces the current one only once it passes. FilePond loads on mount, in its own chunk. `preview` (default `true`): the name of a PDF, plain text, Markdown or image file is a button that opens a `FilePreviewDialog` before upload; DOCX and other types keep a plain name. |
43
- | `FilePreviewDialog` | Shows a local `File` before upload in a `lg` Dialog (`file`, `open`, `onOpenChange`, `finalFocus`): a PDF in the browser's viewer (an object URL re-typed `application/pdf`; at 48rem and below a link opens it in a new tab), plain text and Markdown as text, images in an `<img>`; anything else gets a note and is never framed. The object URL lives only while it is open. |
44
- | `Checkbox`, `Switch` | Required `label`, `description`, `checked`/`defaultChecked`/`onCheckedChange(boolean)`, `name`/`value`, `disabled`, `required`; Checkbox adds `indeterminate`; Switch adds `labelPosition` start · end. |
45
- | `Tooltip` | `content`; `children` is the trigger (an `IconButton`, `Button` or focusable `Badge`); `side` top · bottom · left · right; `align`; `delay` (300ms); `open`/`onOpenChange`; `disabled`. `Tooltip.Provider` around a row skips the delay between siblings. |
46
- | `Chip` | A pill: `tone` neutral · accent · success · warning · danger · info; `size` sm · md; `icon`; `onClick` + `selected` make it a toggle button (`aria-pressed`); `onRemove` adds a labelled × (Backspace/Delete on the chip removes too); `removeLabel`; `disabled`. |
47
- | `Disclosure` | A native `<details>` collapse: required `summary`, `summaryOpen`, `open`/`defaultOpen`/`onOpenChange`, `previewLines` (fades the first N lines while closed, summary moves below), `summaryPosition`; share `name` for an exclusive group. |
48
- | `EmptyState` | Centred placeholder: required `title`, `description`, `icon` (24px in a muted circle), children are the actions, `size` sm · md, `as` div · section · li. |
49
- | `Skeleton` | Loading placeholder, `aria-hidden`: `variant` text (default, one line) · rect · circle, `width`, `height`, `lines` (text, last line shorter), `still`. Put `aria-busy` on the region or a labelled `Spinner` beside it. |
50
- | `Menu` | `Root` (`open`, `onOpenChange`, `modal`), `Trigger` (secondary Button with a chevron, or `render={<IconButton label="More" />}`), `Content` (`side`, `align`), `Item` (`icon`, `trailing`, `tone="danger"`, `disabled`), `LinkItem` (`href`), `CheckboxItem` (`checked`, `onCheckedChange`), `RadioGroup` (`value`, `onValueChange`, `label`) + `RadioItem`, `Group` (`label`), `Separator`. Arrow keys, Home/End, type-ahead, Esc and focus return come from Base UI. |
51
- | `Table` selection | `useRowSelection(ids)` (ids in visible order; ids that disappear leave the selection): `selected`, `selectedIds`, `count`, `anchor`, `headerState` none · some · all, `isSelected`, `toggle`, `toggleRange`, `selectAll`, `clear`; a pure reducer underneath (`selection.*` events). `Table.Root selection` + `Table.SelectionHeader` (`label`, mixed when some) + `Table.SelectionCell label="Select …"` in a `Table.Row selectionId`. Shift+click (checkbox or row) and Shift+Space extend from the anchor; Cmd/Ctrl+click on a row toggles without reaching its `onClick`; Cmd/Ctrl+A in the table selects all, Esc clears. |
52
- | `SelectionBar` | Floating bulk-action bar on Toolbar, centred at the bottom of the viewport (content width up to 48rem, gutters and the safe area respected, full width between the gutters under 640px, fade and slide-up, none under reduced motion): `count` (hidden at 0), `onClear`, children are `Toolbar.Button`s, `aria-label` ("Bulk actions"), `countLabel`, `clearLabel`. Mount it at the end of the scrolling content: while open it leaves a spacer as tall as itself so the last rows scroll clear. A polite status region announces the count; focus returns to where it came from when the bar leaves. |
53
- | `Toast` | `Toast.Provider` (`timeout` 6000, `limit` 3) near the root, `useToast().add({ title, description, tone, action: { label, onClick }, timeout, priority })` below it; Base UI Toast: polite region, paused on hover and focus, F6 to reach it, bottom-end at `--sc-z-toast`. |
54
- | `PriorityNav` | Priority+ primary navigation (docs/adr/0005): required `aria-label`; `items` as `{ href, label, id?, current?, badge?, badgeLabel? }` in priority order; links that fit stay inline and the rest go into a "More" disclosure (APG disclosure navigation: a `button` with `aria-expanded`, not a menu; Esc, click outside and focus leaving close it). The `current` link is never collapsed. `moreLabel`, `moreCountLabel(n)` (announced only: "More, 3 links"), `renderLink={(props) => <Link {...props} />}` for a router. Server-renders every link on one clipped line (scrolls sideways without JS), measures widths once (again after web fonts load or when keys/string labels change) and re-renders only when the inline count changes. Takes the free space in a flex row (`flex: 1 1 0%`); in a grid use a `minmax(0, 1fr)` track. The open panel sits in the top layer (Popover API), anchored to the toggle with CSS anchor positioning (a JS fallback elsewhere) and capped to the viewport. `data-overflowing` + `onOverflowChange(boolean)` let a header make room, with `overflowHysteresis` (px) so it cannot oscillate. Under 400px wide (container query) More shows only an ellipsis. `badge` (a count, or `'dot'`; `0` shows nothing) draws an accent pill or dot after the label, decorative, with `badgeLabel` ("2 suggestions to review") appended to the link's name; while the item is collapsed, More shows the collapsed items' summed count (a dot when they only have dots) and adds their `badgeLabel`s to its name ("More, 3 links, 2 suggestions to review"). Icon-only, the badge sits on More's corner. No motion. |
55
- | `Popover` | Non-modal panel on Base UI Popover: `Root` (`open`, `defaultOpen`, `onOpenChange`, `modal` default false), `Trigger` (secondary Button, or `render={<IconButton label dot />}`), `Content` (`side`, `align`, `sideOffset`, `arrow`; Menu's surface, `--sc-z-dropdown`), `Title` (names the dialog), `Description`, `Close` (the × with no children, or wraps a Button). Esc and focus return come from Base UI. |
56
- | `BroomIcon` | Animated 24px-grid icon for "a sweep is running": `sweeping`, `size` (Icon sizes, default `lg`, or `display` 72px with a 1.75px stroke), `title`. Inline SVG; strokes stay 1.25px at any size; holds still with the dust shown under `prefers-reduced-motion`. Inside an IconButton it takes the next icon size up. |
57
- | `Score` | A number on a tinted tile or in a ring: required `value` (a number, or `null` for not scored yet: a muted dash on the neutral tile or in an empty ring, no tone, no arc, read as "Fit score: not scored"), `max` (100), `tone` neutral · accent (default) · success · warning · danger (no thresholds inside: map value to tone in the app), `variant` tile · ring (64px, decorative SVG, arc animates only on change, none under reduced motion), `size` sm 40 · md 48 (tile), `caption`, `label` ("Fit score"), `valueText`, `selected`. Assistive tech reads one visually hidden sentence, "Fit score 94 out of 100"; the visible number is `aria-hidden`, no `aria-label`. A tile turns solid when `selected` or inside `.sc-card[data-selected]`, `[aria-pressed="true"]` or `[aria-selected="true"]`. |
58
- | `ReasonList` | `ReasonList` (`ul`) + `ReasonList.Item` (`li`): required `tone` for (filled square) · against (bar) · gap (hollow circle), so the marker shape carries the meaning as well as the colour; `size` sm · md; `divider` (dashed rule above). Name it with a heading via `aria-labelledby`; `<strong>` inside picks out key words. |
59
- | `WordDiff` | Prose word diff: required `parts` (`{ kind: 'same' \| 'added' \| 'removed', text }[]`), `as` p · div · span, `size` sm · md, `addedLabel`/`removedLabel` ("added"/"removed"). Real `<del>`/`<ins>` with visually hidden "[removed: …]"/"[added: …]"; strike-through and underline carry the change (3:1 on the `diff.*` tints); whitespace kept exactly (`pre-wrap`), edge whitespace left outside the mark. |
60
- | `Quote` | Someone's words echoed back with who said them: required `label` ("You") and `children`. A `figure` with the label as `figcaption` and the words in a `blockquote`, on `accent.wash` with an `accent.border`. |
61
- | `Heading`, `Text` | `Heading size="overline"`: 12px uppercase mono with `tracking-wider`, muted unless `tone` is set; keep a real `level`. `Text family="mono"` for data next to prose (row meta, dates). |
26
+ | Component | Notes |
27
+ | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
28
+ | `Button` | `variant` primary · secondary · ghost · danger; `size` sm · md · lg; `iconStart`/`iconEnd`; `loading` (keeps width, `aria-busy`); `fullWidth`. `shortcut={<Kbd>N</Kbd>}` shows a key hint after the label, tinted to the variant and hidden from the name (add `aria-keyshortcuts="n"`); `iconOnly` collapses to the icon in a square button without remounting (label kept as the name, shortcut hidden). The hint is not shown where there is no fine pointer (`not all and (any-pointer: fine)`: phones, tablets without a trackpad). |
29
+ | `IconButton` | Square Button for one icon. Required `label` (the accessible name); `variant` ghost (default) · secondary · primary · danger; `size`; `tooltip` (`true` shows `label`, or a string); `tooltipSide`; `round`; `loading`; `render={<a href />}` or a Next.js `Link` makes it a link. `dot` neutral · accent · success · warning · danger · info puts a status dot top-left with a ring in `--sc-icon-button-dot-ring` (default `surface-default`; set it to the header background); decorative, so say the status in `label`. |
30
+ | `Icon`, `*Icon` | 49 outline icons on a 16px grid in `currentColor`, one component each (`PlayIcon`, `ChevronDownIcon` …, listed in `iconNames`); `size` xs 12 · sm 14 · md 16 · lg 20 · xl 24 · em; decorative unless `title` is given. `Icon` wraps your own paths. |
31
+ | `Badge` | `tone` neutral · accent · success · warning · danger · info; `variant` soft · solid · outline; `size` sm · md; `dot`. Text only. |
32
+ | `Avatar`, `AvatarButton` | A person's picture, else initials on `color.avatar.fill` inside a 1px `color.avatar.ring` (the ring is the boundary: ≥ 3:1 on the fill and every surface; the tint alone is not). `name` (a name or an email; initials from the first and last words, or the email's local part; `avatarInitials` exported), `initials` override (up to 2 characters), `src` (initials until it loads and if it fails, on Base UI Avatar), `size` xs 24 · sm 32 · md 40 (default) · lg 48 (control heights, density-aware). Decorative unless `label` (`role="img"`). `AvatarButton`: required `label`, the round button for an account menu (`<Menu.Trigger render={<AvatarButton label="Account: Robin" name="Robin" size="sm" />} />`) or a link via `render`; ring 2px on hover and while open, system focus ring, 44px hit area on touch. |
33
+ | `Card` | `variant` outlined · elevated · filled · plain (a quiet row: no surface, a hover wash without border or shadow, selected is `accent.wash` with a 1px `accent.border`); `padding` none · sm · md · lg; `interactive`; `selected` (visual only: pair with `aria-pressed`/`aria-selected`/`aria-current`; also turns a Score inside solid); `as` or `render` to make it an `a`/`button`/`li`. |
34
+ | `Stack`, `Inline` | `gap` from the spacing scale (`none` … `4xl`); `align`; `justify`; `as`. `Inline` wraps unless `wrap={false}`. |
35
+ | `Kbd` | One key per `Kbd`; `Kbd.Group label="Command K"` groups a shortcut with a `+` (or `separator="space"`). |
36
+ | `Dialog` | `Root` (`open`, `defaultOpen`, `onOpenChange`, `dismissible`, `onRequestClose(reason)`: Esc, backdrop and every `Close` ask instead of closing; close by setting `open`), `Trigger`, `Content` (`size` sm · md · lg, every size clamps to the viewport minus a 16px gutter; `variant="sheet"` becomes a bottom sheet below 640px with a scrolling `Body`; `variant="fullscreen"` covers the viewport, inset 16px from 900px, with a scrolling `Body`), `Title`, `Description`, `Body`, `Actions`, `Close`. Menus, selects and tooltips opened inside render above it. |
37
+ | `FullScreenFlow` | A multi-step takeover on `Dialog variant="fullscreen"` (docs/adr/0006). `Root` (`open` default `true`, required `onRequestClose(reason)` for Esc and the ×, `onNext`/`onBack` for Enter and the arrow keys outside controls, `decoration`), `Header` (`title` names the dialog, `logo`, `compactTitle` below 760px, `status`, `closeLabel`; children: `Progress`), `Progress` (`chapters` `{ label, steps }[]`, `step` counted from 1, `label`; current chapter `aria-current="step"`), `Body`, `Rail` (from 1000px, `footer`) › `RailGroup` (`label`) › `RailItem` (`current`, `done`, `disabled`, `adornment`), `Viewport` (`direction` forward · back; one keyed `Step`), `Step` (`title` an `<h2>` that takes focus, `eyebrow`, `description`, `announcement` for the live region), `Footer` (`back`, `hint`, children; 44px buttons). The app owns the step state; the flow owns motion, focus and announcements. |
38
+ | `Toolbar` | `Root` (required `aria-label`, `wrap`, `gap`), `Group` (`aria-label`, `gap`), `Separator`, `Spacer`, `Button` (`render={<IconButton … />}` or `render={<Menu.Trigger render={…} />}` so the control joins the roving focus: Tab enters, arrows move, Tab leaves), `Link` (an anchor in the same navigation; `render={<IconButton label render={<a href />} />}` for an icon link). |
39
+ | `Banner` | `tone` info (default) · success · warning · danger · neutral; `icon` (or `false`); `action` slot; `dismissible` + `onDismiss` (`open` to control); warning and danger are `role="alert"`, the rest `role="status"`. |
40
+ | `Tabs` | `Root` (`value`, `defaultValue`, `onValueChange`), `List` (required `aria-label`, `fill`), `Tab` (`value`, `icon`, `count`, `disabled`), `Panel` (`value`, `keepMounted`). Arrow keys move, Enter/Space activates. `List variant="segmented"`: a quiet segmented control (sunken track, the active tab raised on `surface-default`, no accent, plain muted counts); `line` stays the default. |
41
+ | `Input`, `Textarea` | Labelled native controls on Base UI's Field: required `label` (`hideLabel` keeps it for assistive tech), `description`, `error` (sets invalid and announces), `invalid`, `required`, `disabled`, `name`, `size` sm · md · lg (Input), `iconStart`/`iconEnd` (Input), `rows`/`resize`/`variant="code"` (Textarea; monospace, tab-size 2, no spellcheck, add native `wrap="off"` for sideways scroll), `onValueChange(string)` beside native `onChange`. Every native attribute passes through: `type="date" \| "time" \| "number"` keep the same height, `list` + a `<datalist>` gives suggestions. |
42
+ | `Select` | Same field props; `options` as `{ value, label, disabled? }` or `{ label, options }` groups; `value`/`defaultValue`/`onValueChange` (`null` for none, shows `placeholder`; no sentinel option needed); `clearable` adds an × and Backspace/Delete on the trigger that call `onValueChange(null)`; `clearLabel`; `fullWidth`. Submits through `name` like a native select. |
43
+ | `FileInput` | Same field props (`label`, `hideLabel`, `description`, `error`, `invalid`, `required`, `disabled`, `name`, `size`) on a drop zone built on FilePond (docs/adr/0004): `accept` (`".pdf,.docx"`, MIME types, `image/*`, as the native attribute), `acceptLabel`, `maxFileSize` (bytes or `"10MB"`), `multiple` + `maxFiles`, `files`/`defaultFiles`/`onFilesChange(File[])`, `idleLabel`, `browseLabel`. No upload server: plain `File`s go to the callback, and with `name` to the form's `FormData` (Server Actions included). Rejected files never join the list; the reason shows in the error slot and is announced. Without `multiple`, a new file replaces the current one only once it passes. FilePond loads on mount, in its own chunk. `preview` (default `true`): the name of a PDF, plain text, Markdown or image file is a button that opens a `FilePreviewDialog` before upload; DOCX and other types keep a plain name. |
44
+ | `FilePreviewDialog` | Shows a local `File` before upload in a `lg` Dialog (`file`, `open`, `onOpenChange`, `finalFocus`): a PDF in the browser's viewer (an object URL re-typed `application/pdf`; at 48rem and below a link opens it in a new tab), plain text and Markdown as text, images in an `<img>`; anything else gets a note and is never framed. The object URL lives only while it is open. |
45
+ | `Checkbox`, `Switch` | Required `label`, `description`, `checked`/`defaultChecked`/`onCheckedChange(boolean)`, `name`/`value`, `disabled`, `required`; Checkbox adds `indeterminate`; Switch adds `labelPosition` start · end. |
46
+ | `Tooltip` | `content`; `children` is the trigger (an `IconButton`, `Button` or focusable `Badge`); `side` top · bottom · left · right; `align`; `delay` (300ms); `open`/`onOpenChange`; `disabled`. `Tooltip.Provider` around a row skips the delay between siblings. |
47
+ | `Chip` | A pill: `tone` neutral · accent · success · warning · danger · info; `size` sm · md; `icon`; `onClick` + `selected` make it a toggle button (`aria-pressed`); `onRemove` adds a labelled × (Backspace/Delete on the chip removes too); `removeLabel`; `disabled`. |
48
+ | `Disclosure` | A native `<details>` collapse: required `summary`, `summaryOpen`, `open`/`defaultOpen`/`onOpenChange`, `previewLines` (fades the first N lines while closed, summary moves below), `summaryPosition`; share `name` for an exclusive group. |
49
+ | `EmptyState` | Centred placeholder: required `title`, `description`, `icon` (24px in a muted circle), children are the actions, `size` sm · md, `as` div · section · li. |
50
+ | `Skeleton` | Loading placeholder, `aria-hidden`: `variant` text (default, one line) · rect · circle, `width`, `height`, `lines` (text, last line shorter), `still`. Put `aria-busy` on the region or a labelled `Spinner` beside it. |
51
+ | `Menu` | `Root` (`open`, `onOpenChange`, `modal`), `Trigger` (secondary Button with a chevron, or `render={<IconButton label="More" />}`), `Content` (`side`, `align`), `Item` (`icon`, `trailing`, `tone="danger"`, `disabled`), `LinkItem` (`href`), `CheckboxItem` (`checked`, `onCheckedChange`), `RadioGroup` (`value`, `onValueChange`, `label`) + `RadioItem`, `Group` (`label`), `Separator`. Arrow keys, Home/End, type-ahead, Esc and focus return come from Base UI. |
52
+ | `Table` selection | `useRowSelection(ids)` (ids in visible order; ids that disappear leave the selection): `selected`, `selectedIds`, `count`, `anchor`, `headerState` none · some · all, `isSelected`, `toggle`, `toggleRange`, `selectAll`, `clear`; a pure reducer underneath (`selection.*` events). `Table.Root selection` + `Table.SelectionHeader` (`label`, mixed when some) + `Table.SelectionCell label="Select …"` in a `Table.Row selectionId`. Shift+click (checkbox or row) and Shift+Space extend from the anchor; Cmd/Ctrl+click on a row toggles without reaching its `onClick`; Cmd/Ctrl+A in the table selects all, Esc clears. |
53
+ | `SelectionBar` | Floating bulk-action bar on Toolbar, centred at the bottom of the viewport (content width up to 48rem, gutters and the safe area respected, full width between the gutters under 640px, fade and slide-up, none under reduced motion): `count` (hidden at 0), `onClear`, children are `Toolbar.Button`s, `aria-label` ("Bulk actions"), `countLabel`, `clearLabel`. Mount it at the end of the scrolling content: while open it leaves a spacer as tall as itself so the last rows scroll clear. A polite status region announces the count; focus returns to where it came from when the bar leaves. |
54
+ | `Toast` | `Toast.Provider` (`timeout` 6000, `limit` 3) near the root, `useToast().add({ title, description, tone, action: { label, onClick }, timeout, priority })` below it; Base UI Toast: polite region, paused on hover and focus, F6 to reach it, bottom-end at `--sc-z-toast`. |
55
+ | `PriorityNav` | Priority+ primary navigation (docs/adr/0005): required `aria-label`; `items` as `{ href, label, id?, current?, badge?, badgeLabel? }` in priority order; links that fit stay inline and the rest go into a "More" disclosure (APG disclosure navigation: a `button` with `aria-expanded`, not a menu; Esc, click outside and focus leaving close it). The `current` link is never collapsed. `moreLabel`, `moreCountLabel(n)` (announced only: "More, 3 links"), `renderLink={(props) => <Link {...props} />}` for a router. Server-renders every link on one clipped line (scrolls sideways without JS), measures widths once (again after web fonts load or when keys/string labels change) and re-renders only when the inline count changes. Takes the free space in a flex row (`flex: 1 1 0%`); in a grid use a `minmax(0, 1fr)` track. The open panel sits in the top layer (Popover API), anchored to the toggle with CSS anchor positioning (a JS fallback elsewhere) and capped to the viewport. `data-overflowing` + `onOverflowChange(boolean)` let a header make room, with `overflowHysteresis` (px) so it cannot oscillate. Under 400px wide (container query) More shows only an ellipsis. `badge` (a count, or `'dot'`; `0` shows nothing) draws an accent pill or dot after the label, decorative, with `badgeLabel` ("2 suggestions to review") appended to the link's name; while the item is collapsed, More shows the collapsed items' summed count (a dot when they only have dots) and adds their `badgeLabel`s to its name ("More, 3 links, 2 suggestions to review"). Icon-only, the badge sits on More's corner. No motion. |
56
+ | `Popover` | Non-modal panel on Base UI Popover: `Root` (`open`, `defaultOpen`, `onOpenChange`, `modal` default false), `Trigger` (secondary Button, or `render={<IconButton label dot />}`), `Content` (`side`, `align`, `sideOffset`, `arrow`; Menu's surface, `--sc-z-dropdown`), `Title` (names the dialog), `Description`, `Close` (the × with no children, or wraps a Button). Esc and focus return come from Base UI. |
57
+ | `BroomIcon` | Animated 24px-grid icon for "a sweep is running": `sweeping`, `size` (Icon sizes, default `lg`, or `display` 72px with a 1.75px stroke), `title`. Inline SVG; strokes stay 1.25px at any size; holds still with the dust shown under `prefers-reduced-motion`. Inside an IconButton it takes the next icon size up. |
58
+ | `Score` | A number on a tinted tile or in a ring: required `value` (a number, or `null` for not scored yet: a muted dash on the neutral tile or in an empty ring, no tone, no arc, read as "Fit score: not scored"), `max` (100), `tone` neutral · accent (default) · success · warning · danger (no thresholds inside: map value to tone in the app), `variant` tile · ring (64px, decorative SVG, arc animates only on change, none under reduced motion), `size` sm 40 · md 48 (tile), `caption`, `label` ("Fit score"), `valueText`, `selected`. Assistive tech reads one visually hidden sentence, "Fit score 94 out of 100"; the visible number is `aria-hidden`, no `aria-label`. A tile turns solid when `selected` or inside `.sc-card[data-selected]`, `[aria-pressed="true"]` or `[aria-selected="true"]`. |
59
+ | `ReasonList` | `ReasonList` (`ul`) + `ReasonList.Item` (`li`): required `tone` for (filled square) · against (bar) · gap (hollow circle), so the marker shape carries the meaning as well as the colour; `size` sm · md; `divider` (dashed rule above). Name it with a heading via `aria-labelledby`; `<strong>` inside picks out key words. |
60
+ | `WordDiff` | Prose word diff: required `parts` (`{ kind: 'same' \| 'added' \| 'removed', text }[]`), `as` p · div · span, `size` sm · md, `addedLabel`/`removedLabel` ("added"/"removed"). Real `<del>`/`<ins>` with visually hidden "[removed: …]"/"[added: …]"; strike-through and underline carry the change (3:1 on the `diff.*` tints); whitespace kept exactly (`pre-wrap`), edge whitespace left outside the mark. |
61
+ | `Quote` | Someone's words echoed back with who said them: required `label` ("You") and `children`. A `figure` with the label as `figcaption` and the words in a `blockquote`, on `accent.wash` with an `accent.border`. |
62
+ | `Heading`, `Text` | `Heading size="overline"`: 12px uppercase mono with `tracking-wider`, muted unless `tone` is set; keep a real `level`. `Text family="mono"` for data next to prose (row meta, dates). |
62
63
 
63
64
  ```tsx
64
65
  <Dialog.Root>
package/dist/index.d.ts CHANGED
@@ -3299,7 +3299,44 @@ interface QuoteProps extends ComponentPropsWithRef<'figure'> {
3299
3299
  */
3300
3300
  declare function Quote({ label, className, children, ...rest }: QuoteProps): react.JSX.Element;
3301
3301
 
3302
+ type AvatarSize = 'xs' | 'sm' | 'md' | 'lg';
3303
+ /**
3304
+ * One or two capital letters for a person: the first letters of the first and last words of a name, or, for an email
3305
+ * address, of its local part split on `.`, `_`, `+`, `-` and digits. Empty when there is nothing to take a letter from.
3306
+ */
3307
+ declare function avatarInitials(name: string | null | undefined): string;
3308
+ interface AvatarLookProps {
3309
+ /** The person's name, or their email address when there is no name. The initials come from it. (On AvatarButton it is not the form `name` attribute.) */
3310
+ name?: string | null | undefined;
3311
+ /** Overrides the initials taken from `name`. Shown as given, up to two characters. */
3312
+ initials?: string | undefined;
3313
+ /** A picture. The initials show until it loads, and stay if it fails. */
3314
+ src?: string | undefined;
3315
+ /** `xs` 24px (beside text), `sm` 32, `md` 40 (default) and `lg` 48 follow the control heights, compact density included. */
3316
+ size?: AvatarSize | undefined;
3317
+ }
3318
+ interface AvatarProps extends AvatarLookProps, Omit<ComponentPropsWithRef<'span'>, 'children'> {
3319
+ /** Gives the avatar an accessible name (`role="img"`), e.g. the person's name where it is not written next to it. Without it the avatar is decorative and hidden from assistive tech. */
3320
+ label?: string | undefined;
3321
+ }
3322
+ /**
3323
+ * A person: their picture, else their initials on a brand tint inside a 1px ring (the ring is what keeps the circle
3324
+ * visible on every surface). Decorative unless `label` is given. For a clickable avatar use `AvatarButton`.
3325
+ */
3326
+ declare function Avatar({ name, initials, src, size, label, className, ...rest }: AvatarProps): react.JSX.Element;
3327
+ interface AvatarButtonProps extends AvatarLookProps, Omit<useRender.ComponentProps<'button'>, 'children' | 'aria-label' | 'name'> {
3328
+ /** Required. The button's accessible name, e.g. `Account: Robin Test`; the avatar inside is decorative. */
3329
+ label: string;
3330
+ }
3331
+ /**
3332
+ * A round button that is an Avatar, for an account menu or a profile link. The ring thickens on hover and while its
3333
+ * popup is open; the focus ring is the system's; on touch the hit area is at least 44px. Compose it as a trigger:
3334
+ * `<Menu.Trigger render={<AvatarButton label="Account: Robin Test" name="Robin Test" size="sm" />} />`, or make it a
3335
+ * link with `render={<a href="/profile" />}`.
3336
+ */
3337
+ declare function AvatarButton({ label, name, initials, src, size, type, className, render, ref, ...rest }: AvatarButtonProps): react.ReactElement<unknown, string | react.JSXElementConstructor<any>>;
3338
+
3302
3339
  declare const version = "0.1.1";
3303
3340
 
3304
- export { ArchiveIcon, ArrowDownIcon, ArrowLeftIcon, ArrowRightIcon, ArrowUpIcon, Badge, Banner, BellIcon, BriefcaseIcon, BroomIcon, Button, CalendarIcon, Card, CheckIcon, Checkbox, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, Chip, CircleCheckIcon, CircleXIcon, ClockIcon, CopyIcon, Dialog, Disclosure, DocumentIcon, DownloadIcon, EditIcon, EmptyState, ExternalLinkIcon, EyeIcon, FileInput, FilePreviewDialog, FilterIcon, FlagIcon, FullScreenFlow, Heading, Icon, IconButton, InboxIcon, InfoIcon, Inline, Input, Kbd, LinkIcon, MailIcon, MapPinIcon, Menu, MenuIcon, MinusIcon, MoonIcon, MoreHorizontalIcon, MoreVerticalIcon, PauseIcon, PlayIcon, PlusIcon, Popover, PriorityNav, Progress$1 as Progress, Quote, ReasonList, RefreshIcon, Score, SearchIcon, Select, SelectionBar, SettingsIcon, Skeleton, Spinner, Stack, StarIcon, Stat, SunIcon, Switch, Table, Tabs, Text, Textarea, ThumbsDownIcon, ThumbsUpIcon, Toast, Toolbar, Tooltip, TrashIcon, UndoIcon, UserIcon, WarningIcon, WordDiff, XIcon, iconNames, useRowSelection, useToast, version };
3305
- export type { BadgeProps, BadgeSize, BadgeTone, BadgeVariant, BannerProps, BannerTone, BroomIconProps, ButtonProps, ButtonSize, ButtonVariant, CardElement, CardPadding, CardProps, CardVariant, CheckboxProps, ChipProps, ChipSize, ChipTone, ControlSize, DialogActionsProps, DialogBodyProps, DialogCloseProps, DialogCloseRequestReason, DialogContentProps, DialogDescriptionProps, DialogRootProps, DialogSize, DialogTitleProps, DialogVariant, DisclosureProps, EmptyStateElement, EmptyStateProps, EmptyStateSize, FieldProps, FileInputProps, FilePreviewDialogProps, FlowChapter, FlowCloseRequestReason, FlowDirection, FullScreenFlowBodyProps, FullScreenFlowFooterProps, FullScreenFlowHeaderProps, FullScreenFlowProgressProps, FullScreenFlowRailGroupProps, FullScreenFlowRailItemProps, FullScreenFlowRailProps, FullScreenFlowRootProps, FullScreenFlowStepProps, FullScreenFlowViewportProps, HeadingElement, HeadingLevel, HeadingProps, HeadingSize, IconButtonDotTone, IconButtonProps, IconGlyphProps, IconName, IconProps, IconSize, InlineAlign, InlineElement, InlineJustify, InlineProps, InputProps, KbdGroupProps, KbdProps, MenuAlign, MenuCheckboxItemProps, MenuContentProps, MenuGroupProps, MenuItemProps, MenuItemTone, MenuLinkItemProps, MenuRadioGroupProps, MenuRadioItemProps, MenuRootProps, MenuSeparatorProps, MenuSide, MenuTriggerProps, PopoverAlign, PopoverCloseProps, PopoverContentProps, PopoverDescriptionProps, PopoverRootProps, PopoverSide, PopoverTitleProps, PopoverTriggerProps, PriorityNavItem, PriorityNavLinkProps, PriorityNavProps, ProgressProps, ProgressSize, ProgressTone, ProgressVariant, QuoteProps, ReasonListItemProps, ReasonListProps, ReasonListSize, ReasonTone, RowSelection, RowSelectionHeaderState, ScoreProps, ScoreSize, ScoreTone, ScoreVariant, SelectGroup, SelectOption, SelectProps, SelectionBarProps, SkeletonProps, SkeletonVariant, SpacingSize, SpinnerProps, StackAlign, StackElement, StackJustify, StackProps, StatDeltaTone, StatElement, StatProps, StatSize, StatTrend, StatVariant, SwitchProps, TableAlign, TableBodyProps, TableCaptionProps, TableCellProps, TableDensity, TableFootProps, TableHeadProps, TableHeaderProps, TableRootProps, TableRowProps, TableSelectionCellProps, TableSelectionHeaderProps, TableSortDirection, TabsListProps, TabsListVariant, TabsPanelProps, TabsRootProps, TabsTabProps, TextAlign, TextElement, TextFamily, TextProps, TextSize, TextTone, TextWeight, TextareaProps, TextareaVariant, ToastApi, ToastOptions, ToastProviderProps, ToastTone, ToolbarButtonProps, ToolbarGroupProps, ToolbarLinkProps, ToolbarRootProps, ToolbarSeparatorProps, ToolbarSpacerProps, TooltipAlign, TooltipProps, TooltipSide, WordDiffElement, WordDiffKind, WordDiffPart, WordDiffProps, WordDiffSize };
3341
+ export { ArchiveIcon, ArrowDownIcon, ArrowLeftIcon, ArrowRightIcon, ArrowUpIcon, Avatar, AvatarButton, Badge, Banner, BellIcon, BriefcaseIcon, BroomIcon, Button, CalendarIcon, Card, CheckIcon, Checkbox, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, Chip, CircleCheckIcon, CircleXIcon, ClockIcon, CopyIcon, Dialog, Disclosure, DocumentIcon, DownloadIcon, EditIcon, EmptyState, ExternalLinkIcon, EyeIcon, FileInput, FilePreviewDialog, FilterIcon, FlagIcon, FullScreenFlow, Heading, Icon, IconButton, InboxIcon, InfoIcon, Inline, Input, Kbd, LinkIcon, MailIcon, MapPinIcon, Menu, MenuIcon, MinusIcon, MoonIcon, MoreHorizontalIcon, MoreVerticalIcon, PauseIcon, PlayIcon, PlusIcon, Popover, PriorityNav, Progress$1 as Progress, Quote, ReasonList, RefreshIcon, Score, SearchIcon, Select, SelectionBar, SettingsIcon, Skeleton, Spinner, Stack, StarIcon, Stat, SunIcon, Switch, Table, Tabs, Text, Textarea, ThumbsDownIcon, ThumbsUpIcon, Toast, Toolbar, Tooltip, TrashIcon, UndoIcon, UserIcon, WarningIcon, WordDiff, XIcon, avatarInitials, iconNames, useRowSelection, useToast, version };
3342
+ export type { AvatarButtonProps, AvatarProps, AvatarSize, BadgeProps, BadgeSize, BadgeTone, BadgeVariant, BannerProps, BannerTone, BroomIconProps, ButtonProps, ButtonSize, ButtonVariant, CardElement, CardPadding, CardProps, CardVariant, CheckboxProps, ChipProps, ChipSize, ChipTone, ControlSize, DialogActionsProps, DialogBodyProps, DialogCloseProps, DialogCloseRequestReason, DialogContentProps, DialogDescriptionProps, DialogRootProps, DialogSize, DialogTitleProps, DialogVariant, DisclosureProps, EmptyStateElement, EmptyStateProps, EmptyStateSize, FieldProps, FileInputProps, FilePreviewDialogProps, FlowChapter, FlowCloseRequestReason, FlowDirection, FullScreenFlowBodyProps, FullScreenFlowFooterProps, FullScreenFlowHeaderProps, FullScreenFlowProgressProps, FullScreenFlowRailGroupProps, FullScreenFlowRailItemProps, FullScreenFlowRailProps, FullScreenFlowRootProps, FullScreenFlowStepProps, FullScreenFlowViewportProps, HeadingElement, HeadingLevel, HeadingProps, HeadingSize, IconButtonDotTone, IconButtonProps, IconGlyphProps, IconName, IconProps, IconSize, InlineAlign, InlineElement, InlineJustify, InlineProps, InputProps, KbdGroupProps, KbdProps, MenuAlign, MenuCheckboxItemProps, MenuContentProps, MenuGroupProps, MenuItemProps, MenuItemTone, MenuLinkItemProps, MenuRadioGroupProps, MenuRadioItemProps, MenuRootProps, MenuSeparatorProps, MenuSide, MenuTriggerProps, PopoverAlign, PopoverCloseProps, PopoverContentProps, PopoverDescriptionProps, PopoverRootProps, PopoverSide, PopoverTitleProps, PopoverTriggerProps, PriorityNavItem, PriorityNavLinkProps, PriorityNavProps, ProgressProps, ProgressSize, ProgressTone, ProgressVariant, QuoteProps, ReasonListItemProps, ReasonListProps, ReasonListSize, ReasonTone, RowSelection, RowSelectionHeaderState, ScoreProps, ScoreSize, ScoreTone, ScoreVariant, SelectGroup, SelectOption, SelectProps, SelectionBarProps, SkeletonProps, SkeletonVariant, SpacingSize, SpinnerProps, StackAlign, StackElement, StackJustify, StackProps, StatDeltaTone, StatElement, StatProps, StatSize, StatTrend, StatVariant, SwitchProps, TableAlign, TableBodyProps, TableCaptionProps, TableCellProps, TableDensity, TableFootProps, TableHeadProps, TableHeaderProps, TableRootProps, TableRowProps, TableSelectionCellProps, TableSelectionHeaderProps, TableSortDirection, TabsListProps, TabsListVariant, TabsPanelProps, TabsRootProps, TabsTabProps, TextAlign, TextElement, TextFamily, TextProps, TextSize, TextTone, TextWeight, TextareaProps, TextareaVariant, ToastApi, ToastOptions, ToastProviderProps, ToastTone, ToolbarButtonProps, ToolbarGroupProps, ToolbarLinkProps, ToolbarRootProps, ToolbarSeparatorProps, ToolbarSpacerProps, TooltipAlign, TooltipProps, TooltipSide, WordDiffElement, WordDiffKind, WordDiffPart, WordDiffProps, WordDiffSize };
package/dist/index.js CHANGED
@@ -3640,6 +3640,66 @@ function Quote({ label, className, children, ...rest }) {
3640
3640
  ] });
3641
3641
  }
3642
3642
 
3643
+ // src/components/Avatar/Avatar.tsx
3644
+ import { Avatar as BaseAvatar } from "@base-ui/react/avatar";
3645
+ import { useRender as useRender3 } from "@base-ui/react/use-render";
3646
+ import { jsx as jsx39, jsxs as jsxs33 } from "react/jsx-runtime";
3647
+ function avatarInitials(name) {
3648
+ const value = name?.trim() ?? "";
3649
+ const words = (value.includes("@") ? (value.split("@")[0] ?? "").split(/[._+\-\d]+/) : value.split(/\s+/)).filter(Boolean);
3650
+ const first = (w) => w ? Array.from(w)[0] ?? "" : "";
3651
+ const letters = words.length > 1 ? first(words[0]) + first(words[words.length - 1]) : first(words[0]);
3652
+ return letters.toUpperCase();
3653
+ }
3654
+ function Letters({ name, initials }) {
3655
+ const letters = initials?.trim() ? Array.from(initials.trim()).slice(0, 2).join("") : avatarInitials(name);
3656
+ return letters ? /* @__PURE__ */ jsx39("span", { className: "sc-avatar__initials", children: letters }) : /* @__PURE__ */ jsx39(UserIcon, { className: "sc-avatar__icon", size: "em" });
3657
+ }
3658
+ function Avatar({ name, initials, src, size = "md", label, className, ...rest }) {
3659
+ return /* @__PURE__ */ jsxs33(
3660
+ BaseAvatar.Root,
3661
+ {
3662
+ className: cx("sc-avatar", className),
3663
+ "data-size": size,
3664
+ role: label ? "img" : void 0,
3665
+ "aria-label": label,
3666
+ "aria-hidden": label ? void 0 : true,
3667
+ ...rest,
3668
+ children: [
3669
+ src ? /* @__PURE__ */ jsx39(BaseAvatar.Image, { className: "sc-avatar__image", src, alt: "" }) : null,
3670
+ /* @__PURE__ */ jsx39(BaseAvatar.Fallback, { className: "sc-avatar__fallback", children: /* @__PURE__ */ jsx39(Letters, { name, initials }) })
3671
+ ]
3672
+ }
3673
+ );
3674
+ }
3675
+ function AvatarButton({
3676
+ label,
3677
+ name,
3678
+ initials,
3679
+ src,
3680
+ size = "md",
3681
+ type,
3682
+ className,
3683
+ render,
3684
+ ref,
3685
+ ...rest
3686
+ }) {
3687
+ return useRender3({
3688
+ render,
3689
+ ref,
3690
+ defaultTagName: "button",
3691
+ props: {
3692
+ // `type` only means something on a real button; a rendered link keeps its own attributes.
3693
+ type: render ? type : type ?? "button",
3694
+ className: cx("sc-avatar-button", className),
3695
+ "data-size": size,
3696
+ "aria-label": label,
3697
+ ...rest,
3698
+ children: /* @__PURE__ */ jsx39(Avatar, { name, initials, src, size })
3699
+ }
3700
+ });
3701
+ }
3702
+
3643
3703
  // src/index.ts
3644
3704
  var version = "0.1.1";
3645
3705
  export {
@@ -3648,6 +3708,8 @@ export {
3648
3708
  ArrowLeftIcon,
3649
3709
  ArrowRightIcon,
3650
3710
  ArrowUpIcon,
3711
+ Avatar,
3712
+ AvatarButton,
3651
3713
  Badge,
3652
3714
  Banner,
3653
3715
  BellIcon,
@@ -3733,6 +3795,7 @@ export {
3733
3795
  WarningIcon,
3734
3796
  WordDiff,
3735
3797
  XIcon,
3798
+ avatarInitials,
3736
3799
  iconNames,
3737
3800
  useRowSelection,
3738
3801
  useToast,