Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/skills/apsara/references/tokens.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ Use for padding, margin, and gap. Values are in `px`.
| `--rs-space-8` | 28 | | `--rs-space-17` | 120 |
| `--rs-space-9` | 32 | | | |

> Layout components (`Flex`, `Grid`, `Box`) accept numeric `gap`/`padding` props that map to this scale (e.g. `gap={4}` → `--rs-space-4` = 12px).
> Layout components (`Flex`, `Grid`, `Box`) accept numeric `gap`/`padding` props that map to this scale (e.g. `gap={4}` → `--rs-space-4` = 12px). `gap={0}` removes the gap.

---

Expand Down
2 changes: 1 addition & 1 deletion apps/www/src/components/docs/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export default function DocsNavbar({
/>
</Fragment>
))}
<Breadcrumb.Item current>{title}</Breadcrumb.Item>
<Breadcrumb.Item active>{title}</Breadcrumb.Item>
</Breadcrumb>
)}
</div>
Expand Down
1 change: 1 addition & 0 deletions apps/www/src/components/docs/search.module.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.searchContainer {
width: 512px;
border-radius: var(--rs-radius-5);
overflow: hidden;
}
Expand Down
2 changes: 1 addition & 1 deletion apps/www/src/components/docs/search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ export default function DocsSearch({ pageTree }: { pageTree: Root }) {
>
<SearchIcon />
</Command.DialogTrigger>
<Command.DialogContent width={512} className={styles.searchContainer}>
<Command.DialogContent className={styles.searchContainer}>
<Command
items={itemValues}
mode='none'
Expand Down
65 changes: 62 additions & 3 deletions apps/www/src/content/docs/(overview)/upgrading.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ it to the viewer's zone.
`FilterChip`'s `calendarProps` and `DataTable`'s `filterProps.calendar` take
CalendarPreview props now. `slotProps.popover` and `showCalendarIcon` work as
before. `slotProps.input` takes `CalendarPreview.Input` props, which have no
`value` or `defaultValue`. `onErrorChange` still reports `'Invalid date'`, but
the error clears only when the typed text is valid or empty, or a date is
committed. Closing the popup does not clear it.
`value` or `defaultValue`. The typed-date error is still `'Invalid date'`, but
it clears only when the typed text is valid or empty, or a date is committed.
Closing the popup does not clear it. The callback that reports the error has a
new name. See [One callback for typed dates](#one-callback-for-typed-dates).

| Removed | Replacement |
|---|---|
Expand Down Expand Up @@ -116,6 +117,64 @@ These filters used to match by mistake, and now match differently:
`'2026/1/1T10:30'`, used to be read. It now matches only `neq`, and the
timeline does not draw it. Store such dates as ISO 8601 (`2026-01-01T10:30`).

## Unreleased: one-off props

Some props that only one component had are removed or renamed to the names
other components use. Defaults do not change, so only code that passes an old
prop needs a change. TypeScript reports each old prop.

| Component | Before | After |
|---|---|---|
| `Command.DialogContent` | `width={640}` | `style={{ width: 640 }}` or a `className` |
| `Search`, `DataView.Search` | `width={240}` | A sized parent. `Search` fills its parent's width. |
| `Skeleton` | `borderRadius` | `radius`: `none`, `small`, `medium`, `large` or `full` |
| `Breadcrumb.Item` | `current` | `active`, as on `Sidebar.Item` |
| `DatePicker` | `onErrorChange` | `onValidityChange` |
| `FilterChip` | `calendarProps.onErrorChange` | `calendarProps.slotProps.input.onValidityChange` |

```tsx
// Before
<Command.DialogContent width={640}>…</Command.DialogContent>
<Search width={240} />
<Skeleton borderRadius="50%" />
<Breadcrumb.Item current>Profile</Breadcrumb.Item>

// After
<Command.DialogContent style={{ width: 640 }}>…</Command.DialogContent>
<div style={{ width: 240 }}>
<Search />
</div>
<Skeleton radius="full" />
<Breadcrumb.Item active>Profile</Breadcrumb.Item>
```

`Skeleton` keeps its default corner radius. For a value outside the radius
scale, set `style={{ borderRadius }}`.

The active `Breadcrumb.Item` has `data-active="true"` in place of
`data-current="true"`. Update CSS that targets the old attribute.

### One callback for typed dates

`DatePicker` and `CalendarPreview.Input` report the validity of typed text with
`onValidityChange`. `FilterChip` takes it on `calendarProps.slotProps.input`,
which is its `CalendarPreview.Input`. The callback receives
`{ valid, reason, message }`.
`message` is `undefined` while the text is valid, so read `message` where you
used the old error string:

```tsx
// Before
<DatePicker onErrorChange={setError} />

// After
<DatePicker onValidityChange={({ message }) => setError(message)} />
```

`DatePicker` reports the reason `unparseable` or `out-of-bounds`, with the
message `'Invalid date'`. The callback also fires when the reason changes and
the text stays invalid.

## 2.0: `Theme` is rewritten

`Theme` used to put its tokens on `<html>` from an effect, so it could not
Expand Down
18 changes: 9 additions & 9 deletions apps/www/src/content/docs/components/breadcrumb/demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const getCode = (props: ComponentPropsType) => {
<Breadcrumb.Separator/>
<Breadcrumb.Item href="#">Products</Breadcrumb.Item>
<Breadcrumb.Separator/>
<Breadcrumb.Item href="#" current>Shoes</Breadcrumb.Item>
<Breadcrumb.Item href="#" active>Shoes</Breadcrumb.Item>
</Breadcrumb>`;
};

Expand All @@ -36,7 +36,7 @@ export const sizeDemo = {
<Breadcrumb.Separator/>
<Breadcrumb.Item href="#">Products</Breadcrumb.Item>
<Breadcrumb.Separator/>
<Breadcrumb.Item href="#" current>Shoes</Breadcrumb.Item>
<Breadcrumb.Item href="#" active>Shoes</Breadcrumb.Item>
</Breadcrumb>`
},
{
Expand All @@ -47,7 +47,7 @@ export const sizeDemo = {
<Breadcrumb.Separator/>
<Breadcrumb.Item href="#">Products</Breadcrumb.Item>
<Breadcrumb.Separator/>
<Breadcrumb.Item href="#" current>Shoes</Breadcrumb.Item>
<Breadcrumb.Item href="#" active>Shoes</Breadcrumb.Item>
</Breadcrumb>`
}
]
Expand All @@ -62,14 +62,14 @@ export const separatorDemo = {
<Breadcrumb.Separator>|</Breadcrumb.Separator>
<Breadcrumb.Item href="#">Products</Breadcrumb.Item>
<Breadcrumb.Separator>|</Breadcrumb.Separator>
<Breadcrumb.Item href="#" current>Shoes</Breadcrumb.Item>
<Breadcrumb.Item href="#" active>Shoes</Breadcrumb.Item>
</Breadcrumb>
<Breadcrumb>
<Breadcrumb.Item href="#">Home</Breadcrumb.Item>
<Breadcrumb.Separator>-</Breadcrumb.Separator>
<Breadcrumb.Item href="#">Products</Breadcrumb.Item>
<Breadcrumb.Separator>-</Breadcrumb.Separator>
<Breadcrumb.Item href="#" current>Shoes</Breadcrumb.Item>
<Breadcrumb.Item href="#" active>Shoes</Breadcrumb.Item>
</Breadcrumb>
</Flex>`
};
Expand All @@ -82,7 +82,7 @@ export const ellipsisDemo = {
<Breadcrumb.Separator/>
<Breadcrumb.Ellipsis/>
<Breadcrumb.Separator/>
<Breadcrumb.Item href="#" current>Shoes</Breadcrumb.Item>
<Breadcrumb.Item href="#" active>Shoes</Breadcrumb.Item>
</Breadcrumb>`
};

Expand Down Expand Up @@ -115,7 +115,7 @@ export const dropdownLinksDemo = {
{ children: 'Books', onClick: () => {console.log('Books')}}
]}>Categories</Breadcrumb.Item>
<Breadcrumb.Separator/>
<Breadcrumb.Item href="#" current>Current</Breadcrumb.Item>
<Breadcrumb.Item href="#" active>Current</Breadcrumb.Item>
</Breadcrumb>`
};

Expand All @@ -127,7 +127,7 @@ export const asDemo = {
<Breadcrumb.Separator/>
<Breadcrumb.Item href="#" render={<NextLink />}>Playground</Breadcrumb.Item>
<Breadcrumb.Separator/>
<Breadcrumb.Item href="#" current>Docs</Breadcrumb.Item>
<Breadcrumb.Item href="#" active>Docs</Breadcrumb.Item>
</Breadcrumb>`
};

Expand All @@ -139,7 +139,7 @@ export const disabledDemo = {
<Breadcrumb.Separator/>
<Breadcrumb.Item disabled>Loading…</Breadcrumb.Item>
<Breadcrumb.Separator/>
<Breadcrumb.Item href="#" current>Products</Breadcrumb.Item>
<Breadcrumb.Item href="#" active>Products</Breadcrumb.Item>
</Breadcrumb>`
};

Expand Down
8 changes: 4 additions & 4 deletions apps/www/src/content/docs/components/breadcrumb/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -100,19 +100,19 @@ Groups all parts of the breadcrumb navigation.

### Item

Renders an individual breadcrumb link. Use the `current` prop on the item that represents the current page so it is styled and exposed to assistive tech (e.g. `aria-current="page"`). Use the `disabled` prop for non-clickable, visually muted items (e.g. loading or no access).
Renders an individual breadcrumb link. Use the `active` prop on the item that represents the current page so it is styled and exposed to assistive tech (e.g. `aria-current="page"`). Use the `disabled` prop for non-clickable, visually muted items (e.g. loading or no access).

Item elements expose data attributes for CSS state targeting so you can style current and disabled states without relying on internal class names:
Item elements expose data attributes for CSS state targeting so you can style active and disabled states without relying on internal class names:

| Attribute | When present |
|-----------|----------------|
| `data-current="true"` | Item is the current page (`current` prop) |
| `data-active="true"` | Item is the current page (`active` prop) |
| `data-disabled="true"` | Item is disabled (`disabled` prop) |

Example:

```css
[data-current="true"] {
[data-active="true"] {
color: var(--my-brand);
}
[data-disabled="true"] {
Expand Down
2 changes: 1 addition & 1 deletion apps/www/src/content/docs/components/breadcrumb/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export interface BreadcrumbItem {
* Whether the item is the current page
* @defaultValue false
*/
current?: boolean;
active?: boolean;

/**
* When true, the item is non-clickable and visually muted (e.g. loading or no access).
Expand Down
2 changes: 1 addition & 1 deletion apps/www/src/content/docs/components/calendar/demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ function DatePickerFieldExample() {
style={{ maxWidth: 240 }}
>
<DatePicker
onErrorChange={setError}
onValidityChange={({ message }) => setError(message)}
slotProps={{ input: { size: "medium" } }}
/>
</Field>
Expand Down
2 changes: 2 additions & 0 deletions apps/www/src/content/docs/components/calendar/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ Selects a date range across two inputs. The grid uses a state machine that branc

Single-date selection with a typable input. The popover opens on focus; pressing Enter, blurring, or clicking outside commits the value and fires `onSelect`.

`onValidityChange` reports typed text that is not a date (`reason: 'unparseable'`) or is outside `startMonth` and `endMonth` (`reason: 'out-of-bounds'`), with the message `'Invalid date'`. It reports `{ valid: true }` when the text is valid again, or when the picker commits or closes. DatePicker shows no error and sets no `aria-invalid`. To show the error, pass `message` to the `error` prop of `Field`, as in the With Field example. `Field` also sets `aria-invalid` on the input.

<Demo data={datePickerDemo} />

### Controlled
Expand Down
15 changes: 6 additions & 9 deletions apps/www/src/content/docs/components/calendar/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,15 +249,12 @@ export interface DatePickerProps {
/** Callback function when date is selected */
onSelect?: (date: Date) => void;

/**
* Fires when the typed-input validation state changes: with a message when
* the typed text stops parsing as a valid in-bounds date, and with
* `undefined` when it becomes valid again (or the picker commits/closes).
* DatePicker renders no error UI of its own — not even `aria-invalid`.
* Lift this into `Field`'s `error` prop (or your form library) to display
* it; `Field` also wires `aria-invalid` onto the input.
*/
onErrorChange?: (error: string | undefined) => void;
/** Called when the typed date becomes invalid or valid again. */
onValidityChange?: (validity: {
valid: boolean;
reason?: 'unparseable' | 'out-of-bounds' | 'unavailable' | 'out-of-order';
message?: string;
}) => void;

/** @deprecated Use `slotProps.calendar` instead. */
calendarProps?: CalendarProps;
Expand Down
6 changes: 6 additions & 0 deletions apps/www/src/content/docs/components/command/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,12 @@ Visual divider between groups. The separator is hidden automatically while the u

Renders the portal, viewport, and popup for the command palette. There is no backdrop overlay. Unlike `Dialog.Content`, there is no close button; users dismiss the palette via `Escape` or by clicking outside it.

The popup is 540px wide. To change the width, pass `className` or `style`, as on `Dialog.Content`:

```tsx
<Command.DialogContent style={{ width: 640 }}>
```

<auto-type-table path="./props.ts" name="CommandDialogContentProps" />

### Slots
Expand Down
4 changes: 2 additions & 2 deletions apps/www/src/content/docs/components/command/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,8 @@ export interface CommandDialogContentProps {
/** Additional CSS class names applied to the dialog popup. */
className?: string;

/** Explicit width for the dialog popup. */
width?: string | number;
/** Inline styles applied to the dialog popup. */
style?: React.CSSProperties;

/**
* Corner radius for this palette only. Overrides the theme's `radius`.
Expand Down
4 changes: 1 addition & 3 deletions apps/www/src/content/docs/components/filter-chip/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export interface FilterChipProps {
yearRange?: { from: number; to: number };
defaultMonth?: Date;
today?: Date;
/** Props for the date input (`CalendarPreview.Input`) and its popup (`CalendarPreview.Content`). `input.disabled` and `input.readOnly` also apply to the calendar. */
/** Props for the date input (`CalendarPreview.Input`) and its popup (`CalendarPreview.Content`). `input.disabled` and `input.readOnly` also apply to the calendar. `input.onValidityChange` reports typed-date errors. */
slotProps?: {
input?: Omit<CalendarPreviewInputProps, 'field'>;
popover?: Omit<PopoverContentProps, 'children'>;
Expand All @@ -77,8 +77,6 @@ export interface FilterChipProps {
* @default false
*/
showCalendarIcon?: boolean;
/** Called with a message when the typed date is invalid, and with `undefined` when it is valid again. */
onErrorChange?: (error: string | undefined) => void;
};

/** Additional CSS class names */
Expand Down
2 changes: 1 addition & 1 deletion apps/www/src/content/docs/components/flex/demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const playground = {
controls: {
gap: {
type: 'select',
options: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17],
options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17],
initialValue: 3
},
wrap: {
Expand Down
3 changes: 2 additions & 1 deletion apps/www/src/content/docs/components/flex/props.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
export interface FlexProps {
/**
* Sets the gutter space between rows and columns.
* Each step maps directly to `--rs-space-1` through `--rs-space-17`.
* `0` removes the gap. Steps 1 to 17 map to `--rs-space-1` through `--rs-space-17`.
*/
gap?:
| 0
| 1
| 2
| 3
Expand Down
2 changes: 1 addition & 1 deletion apps/www/src/content/docs/components/grid/demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const playground = {
controls: {
gap: {
type: 'select',
options: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17],
options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17],
initialValue: 3
},
rows: {
Expand Down
5 changes: 4 additions & 1 deletion apps/www/src/content/docs/components/grid/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ export interface GridProps {

/**
* Sets the gap between grid items.
* Each step maps directly to `--rs-space-1` through `--rs-space-17`.
* `0` removes the gap. Steps 1 to 17 map to `--rs-space-1` through `--rs-space-17`.
*/
gap?:
| 0
| 1
| 2
| 3
Expand All @@ -65,6 +66,7 @@ export interface GridProps {
* Sets the gap between grid columns. Uses the same scale as `gap`.
*/
columnGap?:
| 0
| 1
| 2
| 3
Expand All @@ -87,6 +89,7 @@ export interface GridProps {
* Sets the gap between grid rows. Uses the same scale as `gap`.
*/
rowGap?:
| 0
| 1
| 2
| 3
Expand Down
10 changes: 10 additions & 0 deletions apps/www/src/content/docs/components/search/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ Two sizes. `large` is the default; `small` fits a toolbar or a table header.

<Demo data={sizeDemo} />

### Width

Search fills the width of its parent. To set a width, put it in a sized element:

```tsx
<div style={{ width: 240 }}>
<Search />
</div>
```

### Leading icon

Pass a node to `leadingIcon` to replace the search icon, or `null` to hide it.
Expand Down
2 changes: 1 addition & 1 deletion apps/www/src/content/docs/components/skeleton/demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export const providerDemo = {
duration={2}
>
<Flex gap={4}>
<Skeleton width="48px" height="48px" borderRadius="50%" />
<Skeleton width="48px" height="48px" radius="full" />
<Flex direction="column" gap={2} style={{ flex: 1 }}>
<Skeleton width="200px" />
<Skeleton width="150px" />
Expand Down
2 changes: 1 addition & 1 deletion apps/www/src/content/docs/components/skeleton/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ Create a card-like loading state by combining multiple skeletons.
<Skeleton width="75%" /> {/* Inherits provider props, overrides width */}
<Skeleton
height="48px"
borderRadius="50%"
radius="full"
/> {/* Overrides specific props */}
</Skeleton.Provider>
```
Expand Down
Loading
Loading