diff --git a/.changeset/mosaic-item-size.md b/.changeset/mosaic-item-size.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/mosaic-item-size.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/swingset/src/stories/item.mdx b/packages/swingset/src/stories/item.mdx index dd5aea2f3f8..2aea12d0cac 100644 --- a/packages/swingset/src/stories/item.mdx +++ b/packages/swingset/src/stories/item.mdx @@ -4,6 +4,8 @@ import * as ItemStories from './item.stories'; Item is a flexible row for lists of accounts, organizations, and settings in Mosaic. It's composed from parts via dot syntax (`Item.Root`, `Item.Media`, `Item.Content`, `Item.Title`, …). `Item.Root` renders as a `
` by default; pass it a `render` prop to make a row an interactive link or button, which adds hover and cursor affordances. +Set `size` once on `Item.Root` and the row scales as a unit: it fixes the row's height and gap, and `Item.Media` picks the matching column width up from context rather than taking a size of its own. + ## Example +### Sizes + + + ### Group {children}}> - + {org.name[0]} @@ -44,27 +53,38 @@ import { Item } from '@clerk/ui/mosaic/components/item'; Member - + ; ``` +Media sizes itself from the row, so give it a child that fills its column — an `Avatar.Root` with `size='fit'`, or an icon at `width='100%'`. An action row that has no secondary text uses `Item.Label` in place of `Item.Title`: + +```tsx + }> + + + + + Sign out of all accounts + +; +``` + ## Parts -| Part | Class | Description | -| -------------------- | ------------------------ | -------------------------------------------------------------------- | -| `Item.Root` | `cl-item` | Root row. Renders a `
`, or a custom element via `render`. | -| `Item.Media` | `cl-item-media` | Leading (or trailing) media: icon, image, or avatar. | -| `Item.Content` | `cl-item-content` | Vertical stack that grows to fill the row between media and actions. | -| `Item.Title` | `cl-item-title` | Primary label. | -| `Item.Description` | `cl-item-description` | Secondary text. Renders a `

`. | -| `Item.Actions` | `cl-item-actions` | Trailing controls (buttons, badges). | -| `Item.Header` | `cl-item-header` | Header row above a group: a label with optional actions. | -| `Item.HeaderTitle` | `cl-item-header-title` | Label text within an `Item.Header`. | -| `Item.HeaderActions` | `cl-item-header-actions` | Trailing controls within an `Item.Header`. | -| `Item.Group` | `cl-item-group` | Vertical wrapper around a set of rows (layout only, no role). | -| `Item.Separator` | `cl-item-separator` | Thin divider (`


`) between rows. | +| Part | Class | Description | +| ------------------ | --------------------- | -------------------------------------------------------------------------- | +| `Item.Root` | `cl-item` | Root row. Renders a `
`, or a custom element via `render`. | +| `Item.Media` | `cl-item-media` | Square leading column: icon, image, or avatar. Sized by the root's `size`. | +| `Item.Content` | `cl-item-content` | Vertical stack that grows to fill the row between media and actions. | +| `Item.Title` | `cl-item-title` | Primary label. Truncates to a single line. | +| `Item.Description` | `cl-item-description` | Secondary text beneath the title. Truncates to a single line. | +| `Item.Label` | `cl-item-label` | Sole label on an action row, in place of a title. Dimmed until hovered. | +| `Item.Actions` | `cl-item-actions` | Trailing controls (buttons, badges). | +| `Item.Group` | `cl-item-group` | Vertical wrapper around a set of rows (layout only, no role). | +| `Item.Separator` | `cl-item-separator` | Thin divider (`
`) between rows. | Every part accepts a `render` prop for element polymorphism and forwards a ref. @@ -72,18 +92,23 @@ Every part accepts a `render` prop for element polymorphism and forwards a ref. The root reflects its state as `data-*` attributes on `.cl-item`, so consumers can scope overrides without touching StyleX's hashed atoms: -| Prop | Attribute | Values | Default | -| --------- | ------------------ | ----------------------------------- | -------- | -| `variant` | `data-variant` | `entity` \| `action` | `entity` | -| `render` | `data-interactive` | present when a `render` is provided | — | +| Prop | Attribute | Values | Default | +| -------- | ------------------ | ----------------------------------- | ------- | +| `size` | `data-size` | `xs` \| `md` | `md` | +| `render` | `data-interactive` | present when a `render` is provided | — | -`variant` sets the row's vertical density (`entity` is standard, `action` is denser) and, on interactive rows, promotes the title color. +`size` fixes the row's height and gap. `Item.Media` reflects the same value as `data-size` and takes its width from it, so the two stay in step without being set twice: ```css /* Re-theme interactive rows */ .cl-item[data-interactive] { background-color: var(--cl-color-card); } + +/* Widen the media column on compact rows */ +.cl-item-media[data-size='xs'] { + width: 1.5rem; +} ``` -`Item.Media` sizes to its child and centers it; its height follows the row. Bring your own icon, avatar, or image at whatever dimensions you need, then size the slot by sizing that child. Colors, radii, and spacing all resolve from the Mosaic tokens (`--cl-color-*`, `--cl-radius-*`, `--cl-spacing`). +`Item.Media` is a square that centers its child. Because the column is sized by the row, give it a child that fills it — an `Avatar.Root` with `size='fit'`, or an icon at `width='100%'` — rather than a fixed pixel size that won't track `size`. Colors, radii, and spacing all resolve from the Mosaic tokens (`--cl-color-*`, `--cl-radius-*`, `--cl-spacing`). diff --git a/packages/swingset/src/stories/item.stories.tsx b/packages/swingset/src/stories/item.stories.tsx index 7dd6c1fc288..4c63b08424d 100644 --- a/packages/swingset/src/stories/item.stories.tsx +++ b/packages/swingset/src/stories/item.stories.tsx @@ -35,36 +35,6 @@ function CheckMarkIcon(props: React.ComponentPropsWithoutRef<'svg'>) { ); } -function EllipsisIcon(props: React.ComponentPropsWithoutRef<'svg'>) { - return ( - - - - - - - ); -} - function PlusIcon(props: React.ComponentPropsWithoutRef<'svg'>) { return ( Member - + ); @@ -158,6 +133,31 @@ export function Interactive() { ); } +export function Sizes() { + return ( +
+ {(['md', 'xs'] as const).map(size => ( + + + + T + + + + Test Organization + + + ))} +
+ ); +} + export function Group() { return (
@@ -173,25 +173,52 @@ export function Group() { Cameron Walker + cameron@clerk.com - + - - cameron@clerk.com - - - + + + cameron.walker@gmail.com + + + + + + Clerk - 24 members • Basic ( Clerk - 24 members • Basic ( DesignCloud - 12 members • Pro ( - + - Add account + Add account - - - (