Skip to content
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions .agents/skills/add-new-component/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ Key rules:
- `displayName` set for React DevTools (e.g., `'Component.Trigger'`)
- `cx()` from `class-variance-authority` to merge CSS module class with user's `className`
- Spread `...props` last so consumers can override defaults
- **`data-slot` on every rendered element** — a stable, kebab-case, component-prefixed identifier (`data-slot='component-trigger'`, `data-slot='component-panel-label'`). Subparts extend the prefix; a repeated part reuses one name. Slot names are public API covered by semver — renaming one is a breaking change. Put the attribute before the `...props` spread so consumers can override. Skip `Popover.Content`/`Menu.Content` call sites (extra props land on the positioner, not the popup).

### Object.assign Composition

Expand Down Expand Up @@ -270,6 +271,7 @@ import styles from '../<name>.module.css';
4. **Keyboard navigation** — Tab, Enter, Space, Arrow keys as applicable
5. **Disabled state** — `aria-disabled`, no toggle on click
6. **Sub-components** — className, ref forwarding for each sub-component
7. **data-slot contract** — a `__tests__/data-slots.test.tsx` asserting every slot renders, using `expectSlots` from `~/test-utils/data-slots` (pass `document.body` for portaled parts; also assert conditional slots disappear when their part is absent)

### Testing Tips

Expand Down Expand Up @@ -497,6 +499,7 @@ Checklist:
- [ ] All tests pass
- [ ] Docs site builds and new page is generated
- [ ] `displayName` set on all sub-components
- [ ] Every rendered element has a `data-slot`, with a `data-slots.test.tsx` covering them
- [ ] CSS uses `--rs-*` tokens only
- [ ] Export in `packages/raystack/index.tsx` in alphabetical order
- [ ] Playground example added and registered
74 changes: 74 additions & 0 deletions apps/www/src/content/docs/(overview)/styling.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,80 @@ Apsara components built on [Base UI](https://base-ui.com/) expose data attribute

Common data attributes include `data-open`, `data-closed`, `data-active`, `data-disabled`, `data-state`, `data-starting-style`, and `data-ending-style`.

### With data-slot

Every part a component renders carries a `data-slot` attribute — a stable, public identifier for that element. Use it to style inner parts that have no `className` prop of their own, without depending on Apsara's internal (hashed) class names:

```css
/* Tint the remove button inside every filter chip */
[data-slot="filter-chip-remove"] {
color: var(--rs-color-foreground-danger-primary);
}

/* Make DataView loading rows taller */
[data-slot="data-view-list-loader-row"] {
min-height: 56px;
}

/* Hide the magnifier icon inside Search */
[data-slot="search"] [data-slot="search-input"] {
padding-left: var(--rs-space-3);
}
```

Slot names follow one convention:

- **kebab-case**, prefixed with the component name: `filter-chip-label`, `data-view-list-row`.
- Subparts extend the prefix: `data-view-filter-summary-clear`.
- The same part keeps the same name everywhere it appears (loader cells in `DataView.List` reuse `data-view-list-cell`).

Slot names are covered by semver — renaming or removing one is a breaking change, so selectors written against them are safe to keep. This is different from state attributes like `data-open` above: state attributes tell you *how* an element currently is, `data-slot` tells you *what* it is.

Combine them freely:

```css
/* Only style rows of a clickable list while it is loading */
[data-slot="data-view-list-loader-row"][aria-busy="true"] {
opacity: 0.6;
}
```

Slots also make reliable selectors for tests (`querySelector('[data-slot="search-clear-button"]')`) instead of reaching for hashed CSS-module classes.

#### Styling based on what a component contains

Because every part carries its own slot, a parent can change its own styling based on which children are present — no prop needed to signal it. CSS's `:has()` reaches into descendants:

```css
/* A filter bar that's tighter when it has no chips yet */
[data-slot="data-view-filters"]:has([data-slot="filter-chip"]) {
gap: var(--rs-space-4);
}

/* Style a submit button differently only inside a form's footer */
[data-slot="dialog-footer"] [data-slot="button"] {
width: 100%;
}
```

With Tailwind's arbitrary variants, the same thing reads as a utility class instead of a stylesheet rule:

```tsx
<Flex className="has-[[data-slot=filter-chip]]:gap-4">
<DataView.Filters />
</Flex>
```

```tsx
<Dialog.Footer className="[&_[data-slot=button]]:w-full">
<Button>Save</Button>
</Dialog.Footer>
```

This is the same technique as the state-based selectors above (`data-[state=open]:...`) — `data-slot` just gives you a target that's stable across releases instead of one tied to internal markup.

Coverage: every Apsara component exposes `data-slot` on every element it renders (`data-table` is the one exception — it's being phased out in favor of `DataView`). Each component's docs page lists its slot names.

## Theming with Data Attributes

The `Theme` component sets data attributes on the root `<html>` element. Use these to conditionally style elements based on the active theme:
Expand Down
14 changes: 14 additions & 0 deletions apps/www/src/content/docs/components/accordion/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,20 @@ Contains the collapsible content for an item.

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

### Slots

Every rendered part carries a stable `data-slot` attribute for [styling and testing](/docs/styling#with-data-slot):

| Slot | Element |
|------|---------|
| `accordion` | The root element that wraps all items |
| `accordion-item` | Wrapper around a single header and its panel |
| `accordion-header` | The header row that holds the trigger |
| `accordion-trigger` | The `<button>` that toggles the item |
| `accordion-trigger-icon` | The chevron icon inside the trigger |
| `accordion-content` | The collapsible panel for an item |
| `accordion-content-body` | The padded `<div>` that holds the panel content |

## Examples

### Single vs Multiple
Expand Down
15 changes: 15 additions & 0 deletions apps/www/src/content/docs/components/alert-dialog/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,21 @@ Renders the alert dialog footer section.

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

### Slots

Every rendered part carries a stable `data-slot` attribute for [styling and testing](/docs/styling#with-data-slot):

| Slot | Element |
|------|---------|
| `alert-dialog-backdrop` | The overlay behind the alert dialog |
| `alert-dialog-viewport` | Viewport wrapper that positions the alert dialog |
| `alert-dialog-content` | The alert dialog popup |
| `alert-dialog-header` | The `AlertDialog.Header` container |
| `alert-dialog-title` | The `AlertDialog.Title` element |
| `alert-dialog-body` | The `AlertDialog.Body` container |
| `alert-dialog-description` | The `AlertDialog.Description` element |
| `alert-dialog-footer` | The `AlertDialog.Footer` row |

## Examples

### Controlled
Expand Down
8 changes: 8 additions & 0 deletions apps/www/src/content/docs/components/amount/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ Formats and displays monetary values with locale and currency support.

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

### Slots

Every rendered part carries a stable `data-slot` attribute for [styling and testing](/docs/styling#with-data-slot):

| Slot | Element |
|------|---------|
| `amount` | The root `<span>` element |

## Examples

### Basic
Expand Down
13 changes: 13 additions & 0 deletions apps/www/src/content/docs/components/announcement-bar/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,19 @@ Renders a dismissible banner for announcements and alerts.

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

### Slots

Every rendered part carries a stable `data-slot` attribute for [styling and testing](/docs/styling#with-data-slot):

| Slot | Element |
|------|---------|
| `announcement-bar` | The root `<div>` element |
| `announcement-bar-icon` | Wrapper around the leading icon (when `leadingIcon` is set) |
| `announcement-bar-text` | The main text |
| `announcement-bar-action` | The action `<button>` (when `actionLabel` or `actionIcon` is set) |
| `announcement-bar-action-label` | The action button's text label |
| `announcement-bar-action-icon` | Wrapper around the action icon (when `actionIcon` is set) |

## Accessibility

- The action (when `actionLabel` or `actionIcon` is provided) is rendered
Expand Down
12 changes: 12 additions & 0 deletions apps/www/src/content/docs/components/avatar/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@ Groups multiple avatars with overlap and count indicator.

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

### Slots

Every rendered part carries a stable `data-slot` attribute for [styling and testing](/docs/styling#with-data-slot):

| Slot | Element |
|------|---------|
| `avatar` | The root element that wraps the image and fallback |
| `avatar-image` | The `<img>` element (when the image loads) |
| `avatar-fallback` | The fallback shown before the image loads or on error |
| `avatar-group` | The root `<div>` of an `AvatarGroup` |
| `avatar-group-item` | Wrapper around each avatar in the group, including the overflow avatar |

## Examples

### Variant
Expand Down
10 changes: 10 additions & 0 deletions apps/www/src/content/docs/components/badge/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ Renders a small label for status, count, or category indication.

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

### Slots

Every rendered part carries a stable `data-slot` attribute for [styling and testing](/docs/styling#with-data-slot):

| Slot | Element |
|------|---------|
| `badge` | The root `<span>` element |
| `badge-icon` | Wrapper around `icon` (when set) |
| `badge-screen-reader-text` | Visually hidden text (when `screenReaderText` is set) |

## Examples

### Variant
Expand Down
19 changes: 19 additions & 0 deletions apps/www/src/content/docs/components/breadcrumb/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,25 @@ Renders a collapsed indicator for hidden breadcrumb items.

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

### Slots

Every rendered part carries a stable `data-slot` attribute for [styling and testing](/docs/styling#with-data-slot):

| Slot | Element |
|------|---------|
| `breadcrumb` | The root `<nav>` |
| `breadcrumb-list` | The `<ol>` wrapping items |
| `breadcrumb-item` | The `<li>` wrapping each item |
| `breadcrumb-link` | The link/span rendering the item's content |
| `breadcrumb-item-text` | The item's label text (when a leading/trailing icon is set) |
| `breadcrumb-leading-icon` | Leading icon (when `leadingIcon` is set) |
| `breadcrumb-trailing-icon` | Trailing icon (when `trailingIcon` is set) |
| `breadcrumb-dropdown-trigger` | Trigger button (when `dropdownItems` is set) |
| `breadcrumb-dropdown-icon` | Trigger chevron icon (when `dropdownItems` is set) |
| `breadcrumb-dropdown-item` | An entry in the dropdown menu |
| `breadcrumb-separator` | `Breadcrumb.Separator` |
| `breadcrumb-ellipsis` | `Breadcrumb.Ellipsis` |

## Examples

### Size
Expand Down
12 changes: 12 additions & 0 deletions apps/www/src/content/docs/components/button/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ Renders a clickable button element.

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

### Slots

Every rendered part carries a stable `data-slot` attribute for [styling and testing](/docs/styling#with-data-slot):

| Slot | Element |
|------|---------|
| `button` | The root `<button>` element |
| `button-leading-icon` | Wrapper around `leadingIcon` (when set and not loading) |
| `button-trailing-icon` | Wrapper around `trailingIcon` (when set and not loading) |
| `button-loader` | The loading spinner (when `loading`) |
| `button-loader-text` | Wrapper around `loaderText` (when `loading` with `loaderText`) |

## Examples

### Variant
Expand Down
31 changes: 31 additions & 0 deletions apps/www/src/content/docs/components/calendar/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,37 @@ The DatePicker exposes its slots — `input`, `calendar`, `popover` — through

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

### Slots

Every rendered part carries a stable `data-slot` attribute for [styling and testing](/docs/styling#with-data-slot):

| Slot | Element |
|------|---------|
| `calendar` | The root calendar surface |
| `calendar-nav-previous` | The previous-month navigation button |
| `calendar-nav-next` | The next-month navigation button |
| `calendar-dropdown` | The month/year dropdown trigger (when `captionLayout="dropdown"`) |
| `calendar-dropdown-content` | The dropdown's option list (when the dropdown is open) |
| `calendar-month-grid` | Wrapper around the day grid and its skeleton |
| `calendar-grid-table` | The `<table>` that holds the days |
| `calendar-grid-skeleton` | The loading skeleton shown over the grid |
| `calendar-day` | The `<button>` for a single day |
| `calendar-day-info` | Extra info shown inside a day (when `dateInfo` resolves for that day) |
| `calendar-day-number` | The day number inside a day button |
| `calendar-day-tooltip` | The tooltip shown on hover (when `showTooltip` and a message exists) |
| `date-picker-trigger` | The `<div>` wrapping the DatePicker trigger |
| `date-picker-input` | The DatePicker's text `<input>` |
| `date-picker-error` | The error message (always mounted, toggled via `data-visible`) |
| `date-picker-positioner` | The popover positioner for the DatePicker |
| `date-picker-content` | The DatePicker popover content that holds the calendar |
| `range-picker-trigger` | The `<div>` wrapping the RangePicker trigger |
| `range-picker-trigger-group` | The row that groups the start and end inputs |
| `range-picker-start-input` | The RangePicker's start date `<input>` |
| `range-picker-end-input` | The RangePicker's end date `<input>` |
| `range-picker-positioner` | The popover positioner for the RangePicker |
| `range-picker-content` | The RangePicker popover content that holds the calendar |
| `range-picker-footer` | The footer below the calendar (when `footer` is set) |

## Examples

### Calendar
Expand Down
17 changes: 17 additions & 0 deletions apps/www/src/content/docs/components/callout/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,23 @@ Renders a highlighted message block for tips, warnings, or information.

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

### Slots

Every rendered part carries a stable `data-slot` attribute for [styling and testing](/docs/styling#with-data-slot):

| Slot | Element |
|------|---------|
| `callout-transition` | The outer transition shell `<div>` that plays the enter and exit animation |
| `callout-transition-body` | The `<div>` that animates during the enter/exit transition |
| `callout` | The main callout `<div>` (role `status`, or `alert` for the alert type) |
| `callout-container` | Wrapper `<div>` holding the message and actions |
| `callout-message-container` | Wrapper `<div>` around the icon and message |
| `callout-icon` | Wrapper around the icon (when an icon is set) |
| `callout-message` | Wrapper around the message content |
| `callout-actions` | Wrapper `<div>` for the action and dismiss button |
| `callout-action` | Wrapper around the action (when an action is provided) |
| `callout-dismiss` | The dismiss (close) button (when `dismissible`) |

## Examples

### Type
Expand Down
11 changes: 11 additions & 0 deletions apps/www/src/content/docs/components/checkbox/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,17 @@ Groups multiple checkboxes and manages their shared checked state via a `string[

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

### Slots

Every rendered part carries a stable `data-slot` attribute for [styling and testing](/docs/styling#with-data-slot):

| Slot | Element |
|------|---------|
| `checkbox` | The checkbox control itself |
| `checkbox-indicator` | The indicator inside the control |
| `checkbox-icon` | The check or indeterminate icon (when checked or indeterminate) |
| `checkbox-group` | The `Checkbox.Group` container |

## Examples

### States
Expand Down
12 changes: 12 additions & 0 deletions apps/www/src/content/docs/components/chip/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@ Renders a compact element for tags, labels, or attributes.

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

### Slots

Every rendered part carries a stable `data-slot` attribute for [styling and testing](/docs/styling#with-data-slot):

| Slot | Element |
|------|---------|
| `chip` | The root element (`<button>` when interactive, `<span>` otherwise) |
| `chip-leading-icon` | Wrapper around `leadingIcon` (when set) |
| `chip-trailing-icon` | Wrapper around `trailingIcon` (when set and not dismissible) |
| `chip-dismiss` | The dismiss button (when `isDismissible`) |
| `chip-dismiss-icon` | The dismiss icon (when `isDismissible`) |

## Examples

### Variant
Expand Down
21 changes: 21 additions & 0 deletions apps/www/src/content/docs/components/code-block/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,27 @@ Renders a button to expand or collapse long code blocks.

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

### Slots

Every rendered part carries a stable `data-slot` attribute for [styling and testing](/docs/styling#with-data-slot):

| Slot | Element |
|------|---------|
| `code-block` | The root `<div>` container |
| `code-block-header` | The header row above the code |
| `code-block-label` | The label text in the header |
| `code-block-copy-button` | The copy-to-clipboard button |
| `code-block-content` | Wrapper `<div>` around the code |
| `code-block-code` | Wrapper `<div>` around the highlighted code |
| `code-block-pre` | The `<pre>` element holding the lines |
| `code-block-line` | Each code line `<div>` |
| `code-block-line-number` | The line-number `<span>` (when line numbers are shown) |
| `code-block-line-content` | The `<span>` holding a line's tokens |
| `code-block-token` | Each syntax token `<span>` |
| `code-block-collapse-trigger` | The show/hide button (when `maxLines` is exceeded) |
| `code-block-language-select-trigger` | The language select trigger button |
| `code-block-language-select-value` | The selected language value inside the trigger |

## Examples

### Basic Usage
Expand Down
Loading
Loading