refactor(toggles): explicit composition for radio, checkbox & switch (#6614)

* refactor(toggles): explicit composition for radio, checkbox & switch

Migrate Radio, Checkbox and Switch from child-partitioning to an explicit,
React Server Components-safe compound composition.

- Add `<X.Button>` (React Aria *Button) as the clickable label wrapping the
  control + `Label`; remove the child-type inspection that broke under RSC.
- Drop `<X.Content>`; `Description`/`FieldError` become siblings of `X.Button`
  and are exposed via `aria-describedby` for a cleaner accessible name.
- Keep a context-based control-only fallback for Checkbox/Switch so label-less
  usages (e.g. table selection) are unchanged.
- Fix Label context bleed inside toggle buttons; retire
  partition-toggle-field-children.
- Update styles, stories, EN/CN demos, component docs and migration guides.

BREAKING CHANGE: Radio, Checkbox and Switch no longer expose `*.Content`.
Wrap the control and `Label` in `*.Button`, and place `Description`/`FieldError`
as siblings of `*.Button`. Control-only checkboxes and switches are unaffected.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(releases): add v3.2.0 release notes

Calendar week/day views + multiple selection demos, React Aria 1.18 notes,
rolled-in v3.1.1 patch fixes, and the Radio/Checkbox/Switch *.Content -> *.Button
breaking-change migration guide (EN + CN). Updates releases index, meta, home
release badge, and dictionaries.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(docs): rotating release badges on home hero

Replace the single hardcoded release badge with a curated, per-locale
releaseBadges list rendered by a Motion-powered carousel: popLayout spring
swap, auto-advancing accent progress segments (transform-only scaleX with a
soft gradient fade), pause on hover/focus, reduced-motion fallback, and a
plain-badge fallback for a single entry.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(releases): point v3.2.0 contributors to PR #6616

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(releases): fix v3.2.0 release PR link to #6616

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(docs): remove release badge progress indicators

Drop the carousel segment bars under the home hero release badge.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(releases): add tooltip delay CSS vars to v3.2.0 notes

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor(toggles): rename Button to Content and simplify label composition (#6618)

Rename Checkbox/Radio/Switch.Button to .Content, remove toggle-specific
Label auto-wiring, and update docs, demos, styles, and v3.2.0 release notes.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Junior <jrgarciadev@gmail.com>
This commit is contained in:
Diego Gonzalez
2026-06-06 12:49:28 -04:00
committed by GitHub
parent 71647a9040
commit e1a1196ca9
215 changed files with 6350 additions and 5510 deletions
@@ -1,7 +1,6 @@
---
title: Button 按钮
description: 可点击的按钮组件,支持多种变体与状态。
icon: updated
links:
rac: Button
source: button/button.tsx
@@ -1,8 +1,9 @@
---
title: Switch 开关
description: 用于布尔状态的开关组件。
icon: updated
links:
rac: Switch
rac: SwitchField
source: switch/switch.tsx
styles: switch.css
storybook: Components/Controls/Switch
@@ -26,19 +27,20 @@ import { Switch, SwitchGroup, Label } from '@heroui/react';
引入 Switch 组件,并通过点语法访问各部分。
```tsx
import { Switch, Label, Description } from '@heroui/react';
import { Switch, Description, FieldError } from '@heroui/react';
export default () => (
<Switch>
<Switch.Control>
<Switch.Thumb>
<Switch.Icon/> {/* Optional */}
</Switch.Thumb>
</Switch.Control>
<Switch.Content>
<Label />
<Description /> {/* Optional */}
<Switch.Control>
<Switch.Thumb>
<Switch.Icon /> {/* 可选 */}
</Switch.Thumb>
</Switch.Control>
Label {/* 纯文本 —— 可点击的标签,同时作为无障碍名称 */}
</Switch.Content>
<Description /> {/* 可选 — 字段级帮助文本 */}
<FieldError /> {/* 可选 — 校验错误信息 */}
</Switch>
);
```
@@ -51,16 +53,20 @@ import { Switch, SwitchGroup, Label } from '@heroui/react';
export default () => (
<SwitchGroup>
<Switch>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label>Option 1</Label>
<Switch.Content>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Option 1
</Switch.Content>
</Switch>
<Switch>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label>Option 2</Label>
<Switch.Content>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Option 2
</Switch.Content>
</Switch>
</SwitchGroup>
);
@@ -173,7 +179,7 @@ function CustomSwitch() {
className={`size-[27px] bg-white shadow-sm ${isSelected ? "translate-x-5 shadow-lg" : ""}`}
/>
</Switch.Control>
<Label>Custom Switch</Label>
Custom Switch
</>
)}
</Switch>
@@ -190,16 +196,20 @@ function CustomSwitchGroup() {
return (
<SwitchGroup className="gap-8" orientation="horizontal">
<Switch>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label>Option 1</Label>
<Switch.Content>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Option 1
</Switch.Content>
</Switch>
<Switch>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label>Option 2</Label>
<Switch.Content>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Option 2
</Switch.Content>
</Switch>
</SwitchGroup>
);
@@ -226,7 +236,7 @@ function CustomSwitchGroup() {
}
.switch__content {
@apply flex flex-col gap-1;
@apply items-center gap-3;
}
.switch__icon {
@@ -243,8 +253,8 @@ HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态
Switch 组件使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/switch.css)):
- `.switch` - Switch 根容器
- `.switch__content` - 可选内容容器
- `.switch` - Switch 根容器(字段)
- `.switch__content` - 包裹控件与标签文本的可点击 label
- `.switch__control` - Switch 轨道
- `.switch__thumb` - 可移动的滑块
- `.switch__icon` - 滑块内可选图标
@@ -266,16 +276,16 @@ SwitchGroup 组件使用以下 CSS 类([查看源码样式](https://github.com
该 Switch 同时支持 CSS 伪类与 data 属性,以提供更灵活的状态控制:
- **已选中**`[data-selected="true"]`(滑块位置与背景色变化)
- **悬停**`:hover` 或 `[data-hovered="true"]`
- **聚焦**`:focus-visible` 或 `[data-focus-visible="true"]`显示焦点环)
- **禁用**`:disabled` 或 `[aria-disabled="true"]`(降低透明度,禁用指针事件
- **悬停**`:hover` 或 `[data-hovered="true"]`(作用于 `Switch.Control` / 按钮)
- **聚焦**`:focus-visible` 或 `[data-focus-visible="true"]`在按钮上显示轨道焦点环)
- **禁用**`[data-disabled="true"]`(降低透明度,包括帮助文本
- **按压**`:active` 或 `[data-pressed="true"]`
## API 参考
### Switch Props
继承自 [React Aria Switch](https://react-spectrum.adobe.com/react-aria/Switch.html)。
继承自 [React Aria SwitchField](https://react-spectrum.adobe.com/react-aria/Switch.html)。
| Prop | 类型 | 默认值 | 描述 |
|------|------|--------|------|
@@ -283,27 +293,43 @@ SwitchGroup 组件使用以下 CSS 类([查看源码样式](https://github.com
| `isSelected` | `boolean` | `false` | Switch 是否打开。 |
| `defaultSelected` | `boolean` | `false` | 默认是否打开(非受控)。 |
| `isDisabled` | `boolean` | `false` | Switch 是否禁用。 |
| `isInvalid` | `boolean` | `false` | Switch 是否无效。 |
| `isReadOnly` | `boolean` | `false` | Switch 是否只读。 |
| `isRequired` | `boolean` | `false` | Switch 是否必须打开。 |
| `validate` | `(value: boolean) => ValidationError \| true \| null \| undefined` | - | 自定义校验函数。 |
| `validationBehavior` | `'native' \| 'aria'` | `'native'` | 使用原生 HTML 校验或 ARIA 校验。 |
| `name` | `string` | - | 输入元素名称,用于提交 HTML 表单。 |
| `value` | `string` | - | 输入元素值,用于提交 HTML 表单。 |
| `onChange` | `(isSelected: boolean) => void` | - | Switch 值变化时的事件处理函数。 |
| `onPress` | `(e: PressEvent) => void` | - | Switch 被按下时的事件处理函数。 |
| `children` | `React.ReactNode \| (values: SwitchRenderProps) => React.ReactNode` | - | Switch 内容或渲染 prop。 |
| `render` | `DOMRenderFunction<keyof React.JSX.IntrinsicElements, SwitchRenderProps>` | - | 使用自定义渲染函数覆盖默认 DOM 元素。|
| `children` | `React.ReactNode \| (values: SwitchFieldRenderProps) => React.ReactNode` | - | Switch 内容或字段级渲染 prop。 |
| `render` | `DOMRenderFunction<keyof React.JSX.IntrinsicElements, SwitchFieldRenderProps>` | - | 使用自定义渲染函数覆盖默认 DOM 元素。|
### SwitchRenderProps
### Switch.Content Props
使用渲染 prop 模式时,会提供以下值:
包裹控件与标签文本的可点击 `<label>`。请把 `Switch.Control` 与 `Label` 放在它内部;`Description`/`FieldError` 作为 `Switch.Content` 的兄弟节点。对于没有标签的 switch,省略 `Label` 并在 `Switch` 上传入 `aria-label`。
| Prop | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| `children` | `React.ReactNode \| (values: SwitchButtonRenderProps) => React.ReactNode` | - | 按钮内容(控件 + 标签),或按钮级渲染 prop |
| `className` | `string \| (values: SwitchButtonRenderProps) => string` | - | 应用到可点击 label 的类名 |
### SwitchFieldRenderProps
在根 `Switch` 上使用渲染 prop 时,提供以下字段级值:
| Prop | 类型 | 描述 |
|------|------|------|
| `isSelected` | `boolean` | Switch 当前是否打开。 |
| `isHovered` | `boolean` | Switch 是否处于悬停状态。 |
| `isPressed` | `boolean` | Switch 当前是否被按压。 |
| `isFocused` | `boolean` | Switch 是否聚焦。 |
| `isFocusVisible` | `boolean` | Switch 是否处于键盘聚焦状态。 |
| `isDisabled` | `boolean` | Switch 是否禁用。 |
| `isReadOnly` | `boolean` | Switch 是否只读。 |
| `state` | `-` | Switch 的状态。 |
| `isInvalid` | `boolean` | Switch 是否无效。 |
| `isRequired` | `boolean` | Switch 是否必填。 |
| `state` | `ToggleState` | Switch 的状态。 |
### SwitchButtonRenderProps
`Switch.Control` 使用按钮级渲染 prop`isHovered`、`isPressed`、`isFocusVisible` 等)。将函数作为 `Switch.Control` 的子元素即可访问。
### SwitchGroup Props
@@ -1,7 +1,6 @@
---
title: Badge 徽标
description: 展示相对其他元素定位的小型指示器,常用于未读数、状态点与标签等场景。
icon: updated
links:
source: badge/badge.tsx
styles: badge.css
@@ -1,7 +1,6 @@
---
title: Chip 标签
description: 用于展示标签、状态与分类等信息的小型徽标。
icon: updated
links:
source: chip/chip.tsx
styles: chip.css
@@ -1,6 +1,7 @@
---
title: Calendar 日历
description: 基于 React Aria Calendar 的可组合日期选择器,包含月份网格、导航与年份选择器支持。
icon: updated
links:
rac: Calendar
source: calendar/calendar.tsx
@@ -1,6 +1,7 @@
---
title: CheckboxGroup 复选框组
description: 用于管理多项复选框选择的 CheckboxGroup 组件。
icon: updated
links:
rac: CheckboxGroup
source: checkbox-group/checkbox-group.tsx
@@ -32,13 +33,13 @@ export default () => (
<Label />
<Description /> {/* Optional */}
<Checkbox value="option1">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label />
<Description /> {/* Optional */}
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Label {/* 纯文本 —— 可点击的标签 */}
</Checkbox.Content>
<Description /> {/* 可选:单个复选框的帮助文本 */}
</Checkbox>
<FieldError /> {/* Optional */}
</CheckboxGroup>
@@ -110,11 +111,11 @@ function CustomCheckboxGroup() {
return (
<CheckboxGroup className="gap-4" name="custom">
<Checkbox value="option1">
<Checkbox.Control className="border-2 border-purple-500 data-[selected=true]:bg-purple-500">
<Checkbox.Indicator className="text-white" />
</Checkbox.Control>
<Checkbox.Content>
<Label>Option 1</Label>
<Checkbox.Control className="border-2 border-purple-500 data-[selected=true]:bg-purple-500">
<Checkbox.Indicator className="text-white" />
</Checkbox.Control>
Option 1
</Checkbox.Content>
</Checkbox>
</CheckboxGroup>
@@ -1,8 +1,9 @@
---
title: Checkbox 复选框
description: 复选框允许用户从多个独立选项中选择多项,或将单个独立选项标记为已选。
icon: updated
links:
rac: Checkbox
rac: CheckboxField
source: checkbox/checkbox.tsx
styles: checkbox.css
storybook: Components/Forms/Checkbox
@@ -12,7 +13,7 @@ links:
## 引入
```tsx
import { Checkbox, Label } from '@heroui/react';
import { Checkbox } from '@heroui/react';
```
### 用法
@@ -26,17 +27,18 @@ import { Checkbox, Label } from '@heroui/react';
引入 Checkbox 后,可通过点语法访问各个部分。
```tsx
import { Checkbox, Label, Description } from '@heroui/react';
import { Checkbox, Description, FieldError } from '@heroui/react';
export default () => (
<Checkbox>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label />
<Description /> {/* Optional */}
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Label {/* 纯文本 —— 可点击的标签,同时作为无障碍名称 */}
</Checkbox.Content>
<Description /> {/* 可选 — 字段级帮助文本 */}
<FieldError /> {/* 可选 — 校验错误信息 */}
</Checkbox>
);
```
@@ -65,10 +67,10 @@ export default () => (
name="checkbox-indeterminate"
/>
### 标签
### 外部标签
<ComponentPreview
name="checkbox-with-label"
name="checkbox-external-label"
/>
### 带说明
@@ -136,11 +138,11 @@ import { Checkbox, Label } from '@heroui/react';
function CustomCheckbox() {
return (
<Checkbox name="custom">
<Checkbox.Control className="border-2 border-purple-500 data-[selected=true]:bg-purple-500">
<Checkbox.Indicator className="text-white" />
</Checkbox.Control>
<Checkbox.Content>
<Label>Custom Checkbox</Label>
<Checkbox.Control className="border-2 border-purple-500 data-[selected=true]:bg-purple-500">
<Checkbox.Indicator className="text-white" />
</Checkbox.Control>
Custom Checkbox
</Checkbox.Content>
</Checkbox>
);
@@ -182,7 +184,7 @@ function CustomCheckbox() {
}
.checkbox__content {
@apply flex flex-col gap-1;
@apply items-center gap-3;
}
}
```
@@ -194,9 +196,9 @@ HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态
Checkbox 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/checkbox.css)):
- `.checkbox` - Checkbox 根容器
- `.checkbox__content` - 包裹控件与标签文本的可点击 label
- `.checkbox__control` - Checkbox 控件方框
- `.checkbox__indicator` - Checkbox 勾选指示器
- `.checkbox__content` - 可选内容容器
### 交互状态
@@ -205,16 +207,16 @@ Checkbox 同时支持 CSS 伪类与 data 属性,以获得更好的灵活性:
- **选中**`[data-selected="true"]` 或 `[aria-checked="true"]`(显示勾选与背景色变化)
- **不定**`[data-indeterminate="true"]`(以横线表示不定状态)
- **无效**`[data-invalid="true"]` 或 `[aria-invalid="true"]`(以危险色显示错误状态)
- **悬停**`:hover` 或 `[data-hovered="true"]`
- **焦点**`:focus-visible` 或 `[data-focus-visible="true"]`(显示焦点环)
- **禁用**`:disabled` 或 `[aria-disabled="true"]`(降低透明度并禁用指针事件)
- **悬停**`:hover` 或 `[data-hovered="true"]`(交互状态在 `Checkbox.Control` / 按钮上)
- **焦点**`:focus-visible` 或 `[data-focus-visible="true"]`(显示焦点环,作用于按钮
- **禁用**`[data-disabled="true"]`(降低透明度并禁用指针事件)
- **按下**`:active` 或 `[data-pressed="true"]`
## API 参考
### Checkbox Props
继承自 [React Aria Checkbox](https://react-spectrum.adobe.com/react-aria/Checkbox.html)。
继承自 [React Aria CheckboxField](https://react-spectrum.adobe.com/react-aria/Checkbox.html)。
| Prop | 类型 | 默认值 | 描述 |
|------|------|--------|------|
@@ -224,24 +226,38 @@ Checkbox 同时支持 CSS 伪类与 data 属性,以获得更好的灵活性:
| `isDisabled` | `boolean` | `false` | Checkbox 是否禁用 |
| `isInvalid` | `boolean` | `false` | Checkbox 是否无效 |
| `isReadOnly` | `boolean` | `false` | Checkbox 是否只读 |
| `isRequired` | `boolean` | `false` | Checkbox 是否必须选中 |
| `validate` | `(value: boolean) => ValidationError \| true \| null \| undefined` | - | 自定义校验函数 |
| `validationBehavior` | `'native' \| 'aria'` | `'native'` | 使用原生 HTML 校验或 ARIA 校验 |
| `variant` | `"primary" \| "secondary"` | `"primary"` | 组件的视觉变体。`primary` 为默认带阴影样式。`secondary` 为弱强调、无阴影变体,适合用于 surface 上。 |
| `name` | `string` | - | input 元素的 name,用于提交 HTML 表单 |
| `value` | `string` | - | input 元素的 value,用于提交 HTML 表单 |
| `onChange` | `(isSelected: boolean) => void` | - | Checkbox 值变化时调用 |
| `children` | `React.ReactNode \| (values: CheckboxRenderProps) => React.ReactNode` | - | Checkbox 内容或渲染 prop |
| `render` | `DOMRenderFunction<keyof React.JSX.IntrinsicElements, CheckboxRenderProps>` | - | 通过自定义渲染函数覆盖默认的 DOM 元素。 |
| `children` | `React.ReactNode \| (values: CheckboxFieldRenderProps) => React.ReactNode` | - | Checkbox 内容或字段级渲染 prop |
| `render` | `DOMRenderFunction<keyof React.JSX.IntrinsicElements, CheckboxFieldRenderProps>` | - | 通过自定义渲染函数覆盖默认的 DOM 元素。 |
### CheckboxRenderProps
### Checkbox.Content Props
使用渲染 prop 模式时,会提供以下值:
包裹控件与标签文本的可点击 `<label>`。请把 `Checkbox.Control` 与 `Label` 放在它内部;`Description`/`FieldError` 作为 `Checkbox.Content` 的兄弟节点。对于没有标签的 checkbox,省略 `Label` 并在 `Checkbox` 上传入 `aria-label`。
| Prop | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| `children` | `React.ReactNode \| (values: CheckboxButtonRenderProps) => React.ReactNode` | - | 按钮内容(控件 + 标签),或按钮级渲染 prop |
| `className` | `string \| (values: CheckboxButtonRenderProps) => string` | - | 应用到可点击 label 的类名 |
### CheckboxFieldRenderProps
在根 `Checkbox` 上使用渲染 prop 时,提供以下字段级值:
| Prop | 类型 | 描述 |
|------|------|------|
| `isSelected` | `boolean` | Checkbox 当前是否选中 |
| `isIndeterminate` | `boolean` | Checkbox 是否处于不定状态 |
| `isHovered` | `boolean` | Checkbox 是否处于悬停状态 |
| `isPressed` | `boolean` | Checkbox 当前是否处于按下状态 |
| `isFocused` | `boolean` | Checkbox 是否聚焦 |
| `isFocusVisible` | `boolean` | Checkbox 是否处于键盘可见焦点状态 |
| `isDisabled` | `boolean` | Checkbox 是否禁用 |
| `isReadOnly` | `boolean` | Checkbox 是否只读 |
| `isInvalid` | `boolean` | Checkbox 是否无效 |
| `isRequired` | `boolean` | Checkbox 是否必填 |
### CheckboxButtonRenderProps
`Checkbox.Control` 与 `Checkbox.Indicator` 使用按钮级渲染 prop`isHovered`、`isPressed`、`isFocusVisible` 等)。将函数作为 `Checkbox.Control` 或 `Checkbox.Indicator` 的子元素即可访问。
@@ -1,8 +1,9 @@
---
title: RadioGroup 单选框组
description: 用于从列表中选择单个选项的单选组。
icon: updated
links:
rac: RadioGroup
rac: RadioField
source: radio-group/radio-group.tsx
styles: radio-group.css
storybook: Components/Forms/RadioGroup
@@ -20,7 +21,6 @@ import { RadioGroup, Radio } from '@heroui/react';
name="radio-group-basic"
/>
### 组件结构
导入 RadioGroup 组件后,可通过点号访问各个子部分。
@@ -33,17 +33,18 @@ export default () => (
<Label />
<Description />
<Radio value="option1">
<Radio.Control>
<Radio.Indicator>
<span>✓</span> {/* Custom indicator (optional) */}
</Radio.Indicator>
</Radio.Control>
<Radio.Content>
<Label />
<Description />
<Radio.Content> {/* 可点击区域:control + label */}
<Radio.Control>
<Radio.Indicator>
<span>✓</span> {/* Custom indicator (optional) */}
</Radio.Indicator>
</Radio.Control>
Label {/* 纯文本 —— 可点击的标签 */}
</Radio.Content>
<Description /> {/* 兄弟节点 — 位于按钮外部(通过 aria-describedby 关联) */}
<FieldError /> {/* 可选 — 单选项校验错误信息 */}
</Radio>
<FieldError />
<FieldError /> {/* 可选 — 组级校验 */}
</RadioGroup>
)
```
@@ -111,7 +112,6 @@ RadioGroup 支持两种视觉变体:
name="radio-group-delivery-and-payment"
/>
<RelatedShowcases component="RadioGroup" />
<RelatedComponents component="radiogroup" />
@@ -238,8 +238,8 @@ RadioGroup 使用以下 CSS 类([查看源码样式](https://github.com/heroui
| `value` | `string` | - | 单选项的值 |
| `isDisabled` | `boolean` | `false` | 是否禁用该单选项 |
| `name` | `string` | - | 单选项名称,用于提交 HTML 表单 |
| `children` | `React.ReactNode \| (values: RadioRenderProps) => React.ReactNode` | - | 单选内容或渲染 prop |
| `render` | `DOMRenderFunction<keyof React.JSX.IntrinsicElements, RadioRenderProps>` | - | 使用自定义渲染函数覆盖默认 DOM 元素。|
| `children` | `React.ReactNode \| (values: RadioFieldRenderProps) => React.ReactNode` | - | 单选内容或字段级渲染 prop |
| `render` | `DOMRenderFunction<keyof React.JSX.IntrinsicElements, RadioFieldRenderProps>` | - | 使用自定义渲染函数覆盖默认 DOM 元素。|
### Radio.Control Props
@@ -255,27 +255,28 @@ RadioGroup 使用以下 CSS 类([查看源码样式](https://github.com/heroui
| Prop | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| `children` | `React.ReactNode \| (values: RadioRenderProps) => React.ReactNode` | - | 可选内容或接收当前单选状态的渲染 prop。 |
| `children` | `React.ReactNode \| (values: RadioButtonRenderProps) => React.ReactNode` | - | 可选内容或接收当前单选按钮状态的渲染 prop。 |
### Radio.Content Props
继承 `React.HTMLAttributes<HTMLDivElement>`
单选项的可点击区域(包裹隐藏 input 的 `<label>`)。请将 `Radio.Control` 与 `Label` 放在其中。`className` 支持接收 `RadioButtonRenderProps` 的渲染函数
| Prop | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| `children` | `React.ReactNode` | - | 内容包裹层内要渲染的内容(通常为 Label 与 Description |
| `children` | `React.ReactNode \| (values: RadioButtonRenderProps) => React.ReactNode` | - | 可点击内容(通常为 Radio.Control 与 Label |
### RadioRenderProps
### RadioFieldRenderProps
使用渲染 prop 模式时,会提供以下值:
在根级 `Radio` 上使用渲染 prop 时,会提供以下字段级值:
| Prop | 类型 | 描述 |
|------|------|------|
| `isSelected` | `boolean` | 单选项是否已选中 |
| `isHovered` | `boolean` | 是否悬停 |
| `isPressed` | `boolean` | 是否按下 |
| `isFocused` | `boolean` | 是否聚焦 |
| `isFocusVisible` | `boolean` | 是否为键盘可见焦点 |
| `isDisabled` | `boolean` | 是否禁用 |
| `isReadOnly` | `boolean` | 是否只读 |
| `isInvalid` | `boolean` | 是否无效 |
| `isRequired` | `boolean` | 是否必填 |
### RadioButtonRenderProps
`Radio.Control` 和 `Radio.Indicator` 使用按钮级渲染 prop`isHovered`、`isPressed`、`isFocusVisible` 等)。将函数作为 `Radio.Control` 子节点或传给 `Radio.Indicator` 即可访问它们。
@@ -1,7 +1,6 @@
---
title: TextField 文本输入框
description: 便于组合的文本字段,包含标签、说明与内联校验。
icon: updated
links:
rac: TextField
source: textfield/textfield.tsx
@@ -1,7 +1,6 @@
---
title: Autocomplete 自动完成
description: 自动完成将选择与过滤结合,让用户可以搜索并从选项列表中选择。
icon: updated
links:
rac: Select
source: autocomplete/autocomplete.tsx
@@ -1,7 +1,6 @@
---
title: Typography 排版
description: 面向标题、正文与行内代码的语义化排版原语,基于 React Aria Components 的 Text 构建。
icon: new
links:
source: typography/typography.tsx
styles: typography.css
@@ -35,27 +35,27 @@ export default function App() {
<CheckboxGroup name="interests">
<Label>Select interests</Label>
<Checkbox value="coding">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>Coding</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Coding
</Checkbox.Content>
</Checkbox>
<Checkbox value="design">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>Design</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Design
</Checkbox.Content>
</Checkbox>
<Checkbox value="writing">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>Writing</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Writing
</Checkbox.Content>
</Checkbox>
</CheckboxGroup>
@@ -78,7 +78,7 @@ export default function App() {
### 3. Checkbox 结构
**v2** 简单的 Checkbox 组件,子节点直接作为标签
**v3** 复合 Checkbox 组件,由 `Checkbox.Control`、`Checkbox.Indicator` 与 `Checkbox.Content` 组成
**v3** 复合 Checkbox 组件,由 `Checkbox.Content`、`Checkbox.Control` 与 `Checkbox.Indicator` 组成
### 4. 事件处理函数
@@ -126,27 +126,27 @@ export default function App() {
>
<Label>Select interests</Label>
<Checkbox value="coding">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>Coding</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Coding
</Checkbox.Content>
</Checkbox>
<Checkbox value="design">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>Design</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Design
</Checkbox.Content>
</Checkbox>
<Checkbox value="writing">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>Writing</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Writing
</Checkbox.Content>
</Checkbox>
</CheckboxGroup>
@@ -157,7 +157,7 @@ export default function App() {
## 总结
- 继续使用 `CheckboxGroup`;用 `Label` 子组件取代 `label` prop
- 将 Checkbox 子项更新为复合组件结构(`Checkbox.Control`、`Checkbox.Indicator`、`Checkbox.Content`
- 将 Checkbox 子项更新为复合组件结构(`Checkbox.Content`、`Checkbox.Control`、`Checkbox.Indicator`
- 将 `onValueChange` 改为 `onChange`
- 添加 `name` prop 以便集成到表单中
- 基于 React Aria Components 构建,更易实现无障碍
@@ -27,11 +27,11 @@ import { Checkbox, Label } from "@heroui/react";
export default function App() {
return (
<Checkbox defaultSelected id="option">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="option">Option</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Option
</Checkbox.Content>
</Checkbox>
);
@@ -43,12 +43,12 @@ export default function App() {
### 1. 组件结构
**v2** 简单组件,`children` 即标签
**v3** 复合组件:`Checkbox.Control`、`Checkbox.Indicator`、`Checkbox.Content`
**v3** 复合组件:`Checkbox.Content`、`Checkbox.Control`、`Checkbox.Indicator`
### 2. 标签处理
**v2** 标签直接作为 `children` 传入 `Checkbox`
**v3** 标签放在 `Checkbox.Content` 内,并使用 `Label` 组件
**v3** 标签放在 `Checkbox.Content`(可点击的标签)内;`Description` / `FieldError` 是 `Checkbox.Content` 的同级节点
### 3. Prop 变更
@@ -95,11 +95,11 @@ export default function App() {
const [isSelected, setIsSelected] = useState(false);
<Checkbox id="subscribe" isSelected={isSelected} onChange={setIsSelected}>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="subscribe">Subscribe</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Subscribe
</Checkbox.Content>
</Checkbox>
```
@@ -123,13 +123,13 @@ export default function App() {
import { Checkbox, Label, Description } from "@heroui/react";
<Checkbox id="option" defaultSelected>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="option">Option</Label>
<Description>Description text</Description>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Option
</Checkbox.Content>
<Description>Description text</Description>
</Checkbox>
```
</Tab>
@@ -157,27 +157,27 @@ export default function App() {
<Label>Select cities</Label>
<Description>Choose all that apply</Description>
<Checkbox value="buenos-aires">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>Buenos Aires</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Buenos Aires
</Checkbox.Content>
</Checkbox>
<Checkbox value="sydney">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>Sydney</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Sydney
</Checkbox.Content>
</Checkbox>
<Checkbox value="san-francisco">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>San Francisco</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
San Francisco
</Checkbox.Content>
</Checkbox>
</CheckboxGroup>
@@ -201,21 +201,21 @@ export default function App() {
```tsx
{/* Colors - use Tailwind classes */}
<Checkbox defaultSelected id="primary">
<Checkbox.Control className="data-[selected=true]:bg-primary data-[selected=true]:border-primary">
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="primary">Primary</Label>
<Checkbox.Control className="data-[selected=true]:bg-primary data-[selected=true]:border-primary">
<Checkbox.Indicator />
</Checkbox.Control>
Primary
</Checkbox.Content>
</Checkbox>
{/* Sizes - use Tailwind classes */}
<Checkbox defaultSelected id="medium">
<Checkbox.Control className="size-5">
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="medium">Medium</Label>
<Checkbox.Control className="size-5">
<Checkbox.Indicator />
</Checkbox.Control>
Medium
</Checkbox.Content>
</Checkbox>
```
@@ -235,13 +235,13 @@ export default function App() {
<Tab value="v3">
```tsx
<Checkbox defaultSelected id="option">
<Checkbox.Control>
<Checkbox.Indicator>
{({isSelected}) => isSelected ? <HeartIcon /> : null}
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="option">Option</Label>
<Checkbox.Control>
<Checkbox.Indicator>
{({isSelected}) => isSelected ? <HeartIcon /> : null}
</Checkbox.Indicator>
</Checkbox.Control>
Option
</Checkbox.Content>
</Checkbox>
```
@@ -255,21 +255,21 @@ v3 引入 `variant` prop,可选 `"primary"`(默认)与 `"secondary"`
```tsx
{/* Primary variant (default) */}
<Checkbox variant="primary" id="primary-option">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="primary-option">Primary</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Primary
</Checkbox.Content>
</Checkbox>
{/* Secondary variant - lower emphasis, suitable for Surface components */}
<Checkbox variant="secondary" id="secondary-option">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="secondary-option">Secondary</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Secondary
</Checkbox.Content>
</Checkbox>
```
@@ -285,11 +285,11 @@ v3 引入 `variant` prop,可选 `"primary"`(默认)与 `"secondary"`
<Tab value="v3">
```tsx
<Checkbox isIndeterminate id="option">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="option">Option</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Option
</Checkbox.Content>
</Checkbox>
```
@@ -304,13 +304,13 @@ v3 Checkbox 支持渲染 prop,用于暴露状态信息:
<Checkbox id="terms">
{({isSelected, isIndeterminate, isHovered, isPressed, isFocused, isDisabled}) => (
<>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="terms">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
{isSelected ? "Terms accepted" : "Accept terms"}
</Label>
</Checkbox.Content>
</>
)}
@@ -321,15 +321,15 @@ v3 Checkbox 支持渲染 prop,用于暴露状态信息:
```tsx
<Checkbox id="custom">
<Checkbox.Control>
<Checkbox.Indicator>
{({isSelected, isIndeterminate}) =>
isIndeterminate ? <MinusIcon /> : isSelected ? <CheckIcon /> : null
}
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="custom">Custom indicator</Label>
<Checkbox.Control>
<Checkbox.Indicator>
{({isSelected, isIndeterminate}) =>
isIndeterminate ? <MinusIcon /> : isSelected ? <CheckIcon /> : null
}
</Checkbox.Indicator>
</Checkbox.Control>
Custom indicator
</Checkbox.Content>
</Checkbox>
```
@@ -364,13 +364,13 @@ v3 Checkbox 支持渲染 prop,用于暴露状态信息:
```tsx
<Checkbox className="custom-base" id="option">
<Checkbox.Control className="custom-control">
<Checkbox.Indicator className="custom-indicator" />
</Checkbox.Control>
<Checkbox.Content className="custom-content">
<Label htmlFor="option" className="custom-label">
<Checkbox.Content>
<Checkbox.Control className="custom-control">
<Checkbox.Indicator className="custom-indicator" />
</Checkbox.Control>
Option
</Label>
</Checkbox.Content>
</Checkbox>
```
@@ -381,17 +381,18 @@ v3 Checkbox 的结构如下:
```
Checkbox (Root)
├── Checkbox.Control
── Checkbox.Indicator
└── Checkbox.Content (optional)
── Label (required if using Content)
── Description (optional)
├── Checkbox.Content (the clickable label)
── Checkbox.Control
│ │ └── Checkbox.Indicator
── Label
── Description (optional, sibling)
└── FieldError (optional, sibling)
```
## 总结
1. **组件结构**:必须使用复合组件(`Control`、`Indicator`、`Content`)。
2. **标签处理**:标签须使用 `Label`,并放在 `Checkbox.Content` 内。
1. **组件结构**:必须使用复合组件(`Button`、`Control`、`Indicator`)。
2. **标签处理**:标签放在 `Checkbox.Content` 内。
3. **`onValueChange` → `onChange`**:事件处理函数 prop 已重命名。
4. **颜色已移除**:请在 `Checkbox.Control` 上使用 Tailwind CSS 类。
5. **尺寸已移除**:请在 `Checkbox.Control` 上使用 Tailwind CSS 类。
@@ -35,28 +35,28 @@ export default function App() {
<RadioGroup name="city">
<Label>Select city</Label>
<Radio value="london">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>London</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
London
</Radio.Content>
</Radio>
<Radio value="tokyo">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Tokyo</Label>
<Description>Capital of Japan</Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Tokyo
</Radio.Content>
<Description>Capital of Japan</Description>
</Radio>
<Radio value="paris">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Paris</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Paris
</Radio.Content>
</Radio>
</RadioGroup>
@@ -74,12 +74,12 @@ export default function App() {
### 2. Radio 结构
**v2** 简单的 Radio 组件,子节点直接作为标签
**v3** 复合 Radio 组件,由 `Radio.Control`、`Radio.Indicator` 与 `Radio.Content` 组成
**v3** 复合 Radio 组件,由 `Radio.Content`、`Radio.Control` 与 `Radio.Indicator` 组成
### 3. 描述处理
**v2** 在单个 `Radio` 组件上使用 `description` prop
**v3** 在 `Radio.Content` 内部使用 `Description` 组件
**v3** 将 `Description` 组件作为 `Radio.Content` 的兄弟节点使用
### 4. 事件处理函数
@@ -118,22 +118,22 @@ export default function App() {
<RadioGroup name="plan">
<Label>Select plan</Label>
<Radio value="basic">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Basic</Label>
<Description>Basic features</Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Basic
</Radio.Content>
<Description>Basic features</Description>
</Radio>
<Radio value="pro">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Pro</Label>
<Description>All features</Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Pro
</Radio.Content>
<Description>All features</Description>
</Radio>
</RadioGroup>
```
@@ -160,11 +160,11 @@ export default function App() {
<RadioGroup variant="primary" name="plan">
<Label>Select plan</Label>
<Radio value="basic">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Basic</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Basic
</Radio.Content>
</Radio>
</RadioGroup>
@@ -173,11 +173,11 @@ export default function App() {
<RadioGroup variant="secondary" name="plan">
<Label>Select plan</Label>
<Radio value="basic">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Basic</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Basic
</Radio.Content>
</Radio>
</RadioGroup>
@@ -194,19 +194,19 @@ import { RadioGroup, Radio, Label } from "@heroui/react";
<RadioGroup isReadOnly defaultValue="basic" name="plan">
<Label>Select plan</Label>
<Radio value="basic">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Basic</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Basic
</Radio.Content>
</Radio>
<Radio value="pro">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Pro</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Pro
</Radio.Content>
</Radio>
</RadioGroup>
@@ -238,19 +238,19 @@ import { RadioGroup, Radio, Label } from "@heroui/react";
<RadioGroup name="city" value={value} onChange={setValue}>
<Label>Select city</Label>
<Radio value="london">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>London</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
London
</Radio.Content>
</Radio>
<Radio value="tokyo">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Tokyo</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Tokyo
</Radio.Content>
</Radio>
</RadioGroup>
@@ -262,7 +262,7 @@ import { RadioGroup, Radio, Label } from "@heroui/react";
- 用 `Label` 子组件取代 `label` prop
- 将 Radio 子项更新为复合组件结构
- 用 `Radio.Content` 内部的 `Description` 组件取代 Radio 的 `description` prop
- 将 `Description` 组件作为 `Radio.Content` 的兄弟节点,取代 Radio 的 `description` prop
- 将 `onValueChange` 改为 `onChange`
- 添加 `name` prop 以便集成到表单中
- 新增 `variant` prop`"primary"`(默认)或 `"secondary"`,用于较弱的视觉强调
@@ -34,21 +34,21 @@ export default function App() {
<RadioGroup>
<Label>Select city</Label>
<Radio value="london">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>London</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
London
</Radio.Content>
</Radio>
<Radio value="tokyo">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Tokyo</Label>
<Description>Capital of Japan</Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Tokyo
</Radio.Content>
<Description>Capital of Japan</Description>
</Radio>
</RadioGroup>
);
@@ -60,7 +60,7 @@ export default function App() {
### 1. 组件结构
**v2** 简单的 Radio`children` 作为标签文案
**v3** 复合组件:`Radio.Control`、`Radio.Indicator`、`Radio.Content`
**v3** 复合组件:`Radio.Content`、`Radio.Control`、`Radio.Indicator`
### 2. Prop 变更
@@ -68,7 +68,7 @@ export default function App() {
| --- | --- | --- |
| `onValueChange` | `onChange` | 事件处理函数已重命名 |
| `label`(在 RadioGroup 上) | — | 使用 `Label` 组件 |
| `description`(在 Radio 上) | — | 在 `Radio.Content` 内使用 `Description` 组件 |
| `description`(在 Radio 上) | — | 将 `Description` 组件作为 `Radio.Content` 的兄弟节点使用 |
| `size` | — | 已移除(请改用 Tailwind CSS |
| `color` | — | 已移除(请改用 Tailwind CSS |
| `classNames` | — | 在各子组件上使用 `className` prop |
@@ -108,13 +108,13 @@ export default function App() {
<RadioGroup>
<Label>Select city</Label>
<Radio value="london">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>London</Label>
<Description>Capital of England</Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
London
</Radio.Content>
<Description>Capital of England</Description>
</Radio>
</RadioGroup>
@@ -122,11 +122,11 @@ export default function App() {
<RadioGroup isInvalid>
<Label>Select city</Label>
<Radio value="london">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>London</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
London
</Radio.Content>
</Radio>
<FieldError>Please select an option</FieldError>
@@ -162,19 +162,19 @@ export default function App() {
<RadioGroup value={selected} onChange={setSelected}>
<Label>Select city</Label>
<Radio value="london">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>London</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
London
</Radio.Content>
</Radio>
<Radio value="tokyo">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Tokyo</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Tokyo
</Radio.Content>
</Radio>
</RadioGroup>
@@ -198,19 +198,19 @@ export default function App() {
<RadioGroup orientation="horizontal">
<Label>Select city</Label>
<Radio value="london">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>London</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
London
</Radio.Content>
</Radio>
<Radio value="tokyo">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Tokyo</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Tokyo
</Radio.Content>
</Radio>
</RadioGroup>
@@ -227,11 +227,11 @@ v3 在 `RadioGroup` 上新增 `variant` prop,可选 `"primary"`(默认)与
<RadioGroup variant="primary">
<Label>Select city</Label>
<Radio value="london">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>London</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
London
</Radio.Content>
</Radio>
</RadioGroup>
@@ -240,11 +240,11 @@ v3 在 `RadioGroup` 上新增 `variant` prop,可选 `"primary"`(默认)与
<RadioGroup variant="secondary">
<Label>Select city</Label>
<Radio value="london">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>London</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
London
</Radio.Content>
</Radio>
</RadioGroup>
@@ -258,19 +258,19 @@ v3 支持在 `RadioGroup` 上使用 `isReadOnly`:在保持组合可聚焦的
<RadioGroup isReadOnly defaultValue="london">
<Label>Select city</Label>
<Radio value="london">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>London</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
London
</Radio.Content>
</Radio>
<Radio value="tokyo">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Tokyo</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Tokyo
</Radio.Content>
</Radio>
</RadioGroup>
@@ -284,19 +284,19 @@ v3 的 Radio 结构如下:
RadioGroup (Root)
├── Label (optional)
├── Radio
│ ├── Radio.Control
│ │ ── Radio.Indicator
│ └── Radio.Content
── Label
└── Description (optional)
│ ├── Radio.Content (the clickable label)
│ │ ── Radio.Control
│ │ │ └── Radio.Indicator
── Label
│ └── Description (optional, sibling)
└── FieldError (optional)
```
## 总结
1. **组件结构**:必须使用复合组件(`Radio.Control`、`Radio.Indicator`、`Radio.Content`)。
1. **组件结构**:必须使用复合组件(`Radio.Content`、`Radio.Control`、`Radio.Indicator`)。
2. **标签**:已移除 `label` prop,请使用 `Label` 组件。
3. **描述**:已移除 `description` prop,请在 `Radio.Content` 内使用 `Description` 组件
3. **描述**:已移除 `description` prop,请将 `Description` 组件作为 `Radio.Content` 的兄弟节点使用
4. **事件处理函数**`onValueChange` → `onChange`。
5. **样式相关 prop 已移除**`size`、`color` 等请改用 Tailwind CSS。
6. **`classNames` 已移除**:在各子组件上使用 `className` prop。
@@ -27,11 +27,11 @@ import { Switch, Label } from "@heroui/react";
export default function App() {
return (
<Switch>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Label>Enable notifications</Label>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Enable notifications
</Switch.Content>
</Switch>
);
@@ -43,7 +43,7 @@ export default function App() {
### 1. 组件结构
**v2** children 作为标签的简单 Switch
**v3** 复合组件(`Switch.Control`、`Switch.Thumb`、`Switch.Content`)与 `Label` 组件
**v3** 复合组件(`Switch.Content`、`Switch.Control`、`Switch.Thumb`)与 `Label` 组件
### 2. Prop 变更
@@ -62,7 +62,7 @@ export default function App() {
### 3. 新组件
- `SwitchGroup` — 用于将多个 Switch 成组
- `Switch.Content` — 标签与描述的容器
- `Switch.Content` — 可点击的标签,包裹控件与 `Label`
- `Switch.Icon` — 拇指(Thumb)内的图标
## 迁移示例
@@ -88,11 +88,11 @@ export default function App() {
const [isSelected, setIsSelected] = useState(true);
<Switch isSelected={isSelected} onChange={setIsSelected}>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Label>Airplane mode</Label>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Airplane mode
</Switch.Content>
</Switch>
```
@@ -110,9 +110,11 @@ export default function App() {
<Tab value="v3">
```tsx
<Switch defaultSelected aria-label="Automatic updates">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
</Switch.Content>
</Switch>
```
</Tab>
@@ -129,15 +131,15 @@ export default function App() {
<Tab value="v3">
```tsx
<Switch>
<Switch.Control>
<Switch.Thumb>
<Switch.Icon>
<CheckIcon />
</Switch.Icon>
</Switch.Thumb>
</Switch.Control>
<Switch.Content>
<Label>Enable notifications</Label>
<Switch.Control>
<Switch.Thumb>
<Switch.Icon>
<CheckIcon />
</Switch.Icon>
</Switch.Thumb>
</Switch.Control>
Enable notifications
</Switch.Content>
</Switch>
```
@@ -160,13 +162,13 @@ export default function App() {
<Tab value="v3">
```tsx
<Switch>
<Switch.Control className="flex items-center gap-2">
<SunIcon />
<Switch.Thumb />
<MoonIcon />
</Switch.Control>
<Switch.Content>
<Label>Dark mode</Label>
<Switch.Control className="flex items-center gap-2">
<SunIcon />
<Switch.Thumb />
<MoonIcon />
</Switch.Control>
Dark mode
</Switch.Content>
</Switch>
```
@@ -188,13 +190,13 @@ export default function App() {
import { Switch, Label, Description } from "@heroui/react";
<Switch>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Label>Enable notifications</Label>
<Description>You will receive notifications for all activity</Description>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Enable notifications
</Switch.Content>
<Description>You will receive notifications for all activity</Description>
</Switch>
```
</Tab>
@@ -223,54 +225,54 @@ export default function App() {
{/* Sizes */}
<div className="flex gap-4">
<Switch size="sm">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Label>Small</Label>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Small
</Switch.Content>
</Switch>
<Switch size="md">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Label>Medium</Label>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Medium
</Switch.Content>
</Switch>
<Switch size="lg">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Label>Large</Label>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Large
</Switch.Content>
</Switch>
</div>
{/* Colors - Use Tailwind CSS classes */}
<Switch>
<Switch.Control className="data-[selected=true]:bg-accent">
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Label>Primary</Label>
<Switch.Control className="data-[selected=true]:bg-accent">
<Switch.Thumb />
</Switch.Control>
Primary
</Switch.Content>
</Switch>
<Switch>
<Switch.Control className="data-[selected=true]:bg-success">
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Label>Success</Label>
<Switch.Control className="data-[selected=true]:bg-success">
<Switch.Thumb />
</Switch.Control>
Success
</Switch.Content>
</Switch>
<Switch>
<Switch.Control className="data-[selected=true]:bg-danger">
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Label>Danger</Label>
<Switch.Control className="data-[selected=true]:bg-danger">
<Switch.Thumb />
</Switch.Control>
Danger
</Switch.Content>
</Switch>
```
@@ -295,19 +297,19 @@ export default function App() {
<SwitchGroup>
<Switch name="notifications">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Label>Allow Notifications</Label>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Allow Notifications
</Switch.Content>
</Switch>
<Switch name="marketing">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Label>Marketing emails</Label>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Marketing emails
</Switch.Content>
</Switch>
</SwitchGroup>
@@ -321,12 +323,13 @@ v3 `Switch` 的结构如下:
```
Switch (Root)
├── Switch.Control
── Switch.Thumb
└── Switch.Icon (optional)
└── Switch.Content (optional)
── Label (optional)
── Description (optional)
├── Switch.Content (the clickable label)
── Switch.Control
└── Switch.Thumb
│ │ └── Switch.Icon (optional)
── Label
── Description (optional, sibling)
└── FieldError (optional, sibling)
```
分组时:
@@ -334,17 +337,17 @@ Switch (Root)
```
SwitchGroup
├── Switch
│ ├── Switch.Control
│ │ ── Switch.Thumb
│ └── Switch.Content
── Label
└── Description (optional)
│ ├── Switch.Content
│ │ ── Switch.Control
│ │ │ └── Switch.Thumb
── Label
│ └── Description (optional)
└── Switch
├── Switch.Control
── Switch.Thumb
└── Switch.Content
── Label
└── Description (optional)
├── Switch.Content
── Switch.Control
│ │ └── Switch.Thumb
── Label
└── Description (optional)
```
## 说明
@@ -357,7 +360,7 @@ SwitchGroup
### 标签
- **v2** children 作为标签
- **v3** 在 `Switch.Content` 内使用 `Label`;可并列添加 `Description`
- **v3** `Label` 放在 `Switch.Content`(可点击的标签)内;`Description` / `FieldError` 作为 `Switch.Content` 的同级元素
### 图标
@@ -366,11 +369,11 @@ SwitchGroup
## 总结
1. **组件结构**:必须使用复合组件(`Switch.Control`、`Switch.Thumb`、`Switch.Content`
2. **内容容器**:用 `Switch.Content` 包裹 `Label` `Description`,以保证布局正确
1. **组件结构**:必须使用复合组件(`Switch.Content`、`Switch.Control`、`Switch.Thumb`
2. **可点击的标签**:用 `Switch.Content` 包裹 `Switch.Control` 与 `Label`;将 `Description` / `FieldError` 作为 `Switch.Content` 的同级元素
3. **标签**:不再用 children 作为标签 — 请在 `Switch.Content` 内使用 `Label`
4. **事件处理函数**`onValueChange` → `onChange`
5. **样式相关 prop 已移除**`color` — 请使用 Tailwind CSS
6. **图标相关 prop 已移除**`thumbIcon`、`startContent`、`endContent` — 请用子组件或直接自定义
7. **`classNames` 已移除**:请在各子组件上使用 `className` prop
8. **新组件**`SwitchGroup` 用于成组;`Switch.Content` 作为标签 / 描述容器
8. **新组件**`SwitchGroup` 用于成组;`Switch.Content` 作为可点击的标签,包裹控件与 `Label`
@@ -9,6 +9,16 @@ description: HeroUI v3 的所有更新与变更,包含新功能、修复以及
## 最新版本
### v3.2.0
**2026 年 6 月 6 日**
基于 React Aria 1.18 的 Calendar 周/日视图、重做的年份选择器与范围日历演示,新增 Tooltip 延迟主题变量,并纳入随之发布的补丁修复。破坏性变更:`Radio`、`Checkbox`、`Switch` 改为显式的 `*.Content` 组合(控件嵌套进 `*.Content`,标签为纯文本、不嵌套 `<Label>`,帮助文本变为兄弟节点)。
[阅读完整发布说明 →](/docs/react/releases/v3-2-0)
---
### v3.1.0
**2026 年 5 月 25 日**
@@ -7,6 +7,7 @@
"---概述---",
"index",
"---版本列表---",
"v3-2-0",
"v3-1-0",
"v3-0-5",
"v3-0-4",
@@ -0,0 +1,260 @@
---
title: v3.2.0
description: 基于 React Aria 1.18 的 Calendar 周/日视图与年份选择器,以及 Radio、Checkbox、Switch 的破坏性组合方式变更。
github:
pull: 6616
---
<div className="flex items-center gap-3 mb-6">
<span className="text-sm text-muted">2026 年 6 月 6 日</span>
</div>
Calendar 新增周视图与日视图、重做的年份选择器,以及基于 React Aria 1.18 的范围日历演示。[Tooltip](/docs/components/tooltip) 新增用于全局显示与隐藏延迟的主题变量。Radio、Checkbox、Switch 迁移到 React Aria 的 `*Field` + `*Button` 组合方式。本次发布同时纳入了随 React Aria 1.18 一起发布的补丁修复(`Table.SortableColumnHeader`、Toast 与 Fieldset 修复、滚动与 RTL 样式修复)。
⚠️ **破坏性变更**`Radio`、`Checkbox`、`Switch` 改为显式的 `*.Content` 组合 —— `*.Control` 嵌套进 `*.Content`,标签变为 `*.Content` 内的纯文本(不嵌套 `<Label>`),`Description`/`FieldError` 变为 `*.Content` 的兄弟节点。详见[破坏性变更](#-breaking-changes)。
## 安装
升级到最新版本:
<Tabs items={["npm", "pnpm", "yarn", "bun"]}>
<Tab value="npm">
```bash
npm i @heroui/styles@latest @heroui/react@latest
```
</Tab>
<Tab value="pnpm">
```bash
pnpm add @heroui/styles@latest @heroui/react@latest
```
</Tab>
<Tab value="yarn">
```bash
yarn add @heroui/styles@latest @heroui/react@latest
```
</Tab>
<Tab value="bun">
```bash
bun add @heroui/styles@latest @heroui/react@latest
```
</Tab>
</Tabs>
<Callout type="info">
**正在使用 AI 助手?** 对它说「Hey Cursor,把 HeroUI 升级到最新版本」。它会对比版本并应用必要变更。了解更多请参阅 [HeroUI MCP 服务器](/docs/ui-for-agents/mcp-server)。
</Callout>
## 新增内容
### Calendar
`Calendar` 与 `RangeCalendar` 新增周视图与日视图,以及来自 React Aria 1.18 的新日历属性。
- **周视图 / 日视图**:通过 `visibleDuration` 渲染多周或单日布局
- **多选**:在单个 `Calendar` 中选择多个日期
- **React Aria 1.18 属性**`weeksInMonth` 与用于范围选择的 `isDateUnavailable(date, anchorDate)`
- **内部实现**:月份标题使用 React Aria 的 `CalendarHeading`,年份选择器基于 React Aria calendar hooks 重建
**周视图:**
<ComponentPreview name="calendar-week-view" />
**日视图:**
<ComponentPreview name="calendar-day-view" />
**多选:**
<ComponentPreview name="calendar-multiple-selection" />
**多选:**
<ComponentPreview name="calendar-multiple-selection" />
### Table.SortableColumnHeader
`Table.SortableColumnHeader` 用于渲染 sortable 列标题和可选的升降序指示器。放在 `Table.Column` render prop 中,并传入 `sortDirection` ([#6588](https://github.com/heroui-inc/heroui/pull/6588))。
```tsx
<Table.Column allowsSorting>
{({sortDirection}) => (
<Table.SortableColumnHeader sortDirection={sortDirection}>
Name
</Table.SortableColumnHeader>
)}
</Table.Column>
```
- **默认指示器**:存在排序方向时显示 chevron
- **自定义指示器**:传入 `indicator`,或用 `showIndicator={false}` 隐藏
- **样式插槽**`.table__sortable-column-header` + `.table__sortable-column-indicator`
<ComponentPreview name="table-sorting" />
### Tooltip 延迟主题变量
[Tooltip](/docs/components/tooltip) 现在会从主题 CSS 变量读取默认的显示与隐藏延迟 ([#6617](https://github.com/heroui-inc/heroui/pull/6617))
- `--tooltip-delay` — 显示 Tooltip 前的延迟(默认:`1500ms`
- `--tooltip-close-delay` — 隐藏 Tooltip 前的延迟(默认:`500ms`
全局覆盖示例:
```css
:root {
--tooltip-delay: 700ms;
--tooltip-close-delay: 0ms;
}
```
单个 Tooltip 上的 `delay` 和 `closeDelay` 属性仍会覆盖这些值。
<Callout type="warning">
**行为变更:** 使用默认 HeroUI 主题时,Tooltip 延迟现在默认为 `1500ms` / `500ms`,而不是之前的 React Aria 默认值 `700ms` / `0ms`。如需保留旧行为,请显式设置 CSS 变量或 props。
</Callout>
## 组件修复
- **Toast**Toast 队列会串行化 ViewTransition 更新,避免在 `toast.promise()` 关闭 loading toast 并打开成功/失败反馈时出现被跳过的过渡和 AbortError ([#6511](https://github.com/heroui-inc/heroui/pull/6511))。
- **Fieldset**`disabled` 会传递到 React Aria 的 Button、CheckboxGroup、Link、RadioGroup、Slider、ToggleButton 和 ToggleButtonGroup 上下文 ([#6596](https://github.com/heroui-inc/heroui/pull/6596))。
## 样式修复
- **Modal / AlertDialog**`scroll-inside` 对话框通过 `max-h-full min-h-0` 限制高度,使内容区域滚动而非溢出 ([#6597](https://github.com/heroui-inc/heroui/pull/6597))。
- **ScrollShadow**:渐隐遮罩通过 `--scroll-shadow-scrollbar-size` 为可见的原生滚动条预留空间 ([#6598](https://github.com/heroui-inc/heroui/pull/6598))。
- **Table RTL**:列分隔线与拖拽手柄在 RTL 下使用逻辑属性 `end-0` 定位 ([#6606](https://github.com/heroui-inc/heroui/pull/6606))。
## 依赖
- **React Aria Components**`1.17.0` → `1.18.0` ([#6586](https://github.com/heroui-inc/heroui/pull/6586))。1.18 引入了 toggles 所采用的 `*Field` + `*Button` 组合方式、`CalendarHeading`,以及 `isDateUnavailable(date, anchorDate)`。
- **@internationalized/date**`3.12.1` → `3.12.2`
- **React Aria / Stately 辅助包**`@react-aria/*`、`@react-stately/*` 与 `@react-types/shared` 补丁更新
## ⚠️ Breaking Changes
### Radio、Checkbox 与 Switch:显式 `*.Content` 组合
这些组件现在在底层使用 React Aria 的 `*Field` + `*Button` 组合方式。`X.Content` 现在是**可点击的 label**React Aria 的 `*Button`)。共有三点变化:
- **`X.Control` 移进 `X.Content`** —— 它们以前是兄弟节点。
- **标签是 `X.Content` 内部的纯文本** —— `X.Content` 渲染的是 `<label>` 元素,所以不要嵌套 `<Label>` 组件(嵌套的 `<label>` 是无效 HTML)。若要使用独立的 `Label`,请把它放在**外部**,并用 `htmlFor` + 组件 `id` 关联。
- **`Description`/`FieldError` 移到外部**,作为 `X.Content` 的兄弟节点,这样它们会通过 `aria-describedby` 朗读,而不会被并入无障碍名称。
**Checkbox**
```tsx
// v3.1
<Checkbox>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>Accept terms</Label>
<Description>You agree to our terms</Description>
</Checkbox.Content>
</Checkbox>
// v3.2
<Checkbox>
<Checkbox.Content>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Accept terms
</Checkbox.Content>
<Description>You agree to our terms</Description>
</Checkbox>
```
**Radio**
```tsx
// v3.1
<Radio value="a">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Option A</Label>
</Radio.Content>
</Radio>
// v3.2
<Radio value="a">
<Radio.Content>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Option A
</Radio.Content>
</Radio>
```
**Switch**
```tsx
// v3.1
<Switch>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Label>Enable notifications</Label>
</Switch.Content>
</Switch>
// v3.2
<Switch>
<Switch.Content>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Enable notifications
</Switch.Content>
</Switch>
```
**迁移对照**
| v3.1 | v3.2 |
|------|------|
| `X.Control` 与 `X.Content` 为兄弟节点 | `X.Control` 嵌套进 `X.Content` |
| `X.Content` 是包裹 `Label` + 帮助文本的布局 `<div>` | `X.Content` 是包裹 `X.Control` + 标签文本的可点击 `<label>` |
| 通过 `X.Content` 内的 `<Label>` 提供标签 | 标签是 `X.Content` 内的**纯文本**(不嵌套 `<Label>` |
| `Description` / `FieldError` 位于 `X.Content` 内 | `Description` / `FieldError` 作为 `X.Content` 的兄弟节点 |
**外部标签** —— 若要使用独立的 `Label`,请把它放在组件外部,并用 `htmlFor` + 组件 `id` 关联:
```tsx
<div className="flex items-center gap-3">
<Checkbox id="terms">
<Checkbox.Content>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
<Label htmlFor="terms">Accept terms</Label>
</div>
```
**仅含控件的 Checkbox 和 Switch**(没有标签,例如表格行选择或图标开关)仍需用 `X.Content` 作为可点击包装。请把 `Checkbox.Control` / `Switch.Control` 放进 `X.Content`,省略标签,并在根组件上传入 `aria-label`。
各组件完整迁移指南:[Checkbox](/docs/react/migration/checkbox)、[Checkbox Group](/docs/react/migration/checkbox-group)、[Radio](/docs/react/migration/radio)、[Radio Group](/docs/react/migration/radio-group)、[Switch](/docs/react/migration/switch)。
## 链接
- [Calendar 文档](/docs/react/components/calendar)
- [Tooltip 文档](/docs/react/components/tooltip)
- [Checkbox 文档](/docs/react/components/checkbox)
- [Radio Group 文档](/docs/react/components/radio-group)
- [Switch 文档](/docs/react/components/switch)
- [组件文档](/docs/react/components)
- [GitHub 仓库](https://github.com/heroui-inc/heroui)
- [GitHub PR #6616](https://github.com/heroui-inc/heroui/pull/6616)
## 贡献者
感谢所有为本次发布做出贡献的人!
<PRContributors />
@@ -1,7 +1,6 @@
---
title: Button
description: A clickable button component with multiple variants and states
icon: updated
links:
rac: Button
source: button/button.tsx
@@ -1,8 +1,9 @@
---
title: Switch
description: A toggle switch component for boolean states
icon: updated
links:
rac: Switch
rac: SwitchField
source: switch/switch.tsx
styles: switch.css
storybook: Components/Controls/Switch
@@ -26,19 +27,20 @@ import { Switch, SwitchGroup, Label } from '@heroui/react';
Import the Switch component and access all parts using dot notation.
```tsx
import { Switch, Label, Description } from '@heroui/react';
import { Switch, Description, FieldError } from '@heroui/react';
export default () => (
<Switch>
<Switch.Control>
<Switch.Thumb>
<Switch.Icon/> {/* Optional */}
</Switch.Thumb>
</Switch.Control>
<Switch.Content>
<Label />
<Description /> {/* Optional */}
<Switch.Control>
<Switch.Thumb>
<Switch.Icon /> {/* Optional */}
</Switch.Thumb>
</Switch.Control>
Label {/* plain text — the clickable label + accessible name */}
</Switch.Content>
<Description /> {/* Optional — field-level help text */}
<FieldError /> {/* Optional — validation message */}
</Switch>
);
```
@@ -51,16 +53,20 @@ import { Switch, SwitchGroup, Label } from '@heroui/react';
export default () => (
<SwitchGroup>
<Switch>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label>Option 1</Label>
<Switch.Content>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Option 1
</Switch.Content>
</Switch>
<Switch>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label>Option 2</Label>
<Switch.Content>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Option 2
</Switch.Content>
</Switch>
</SwitchGroup>
);
@@ -173,7 +179,7 @@ function CustomSwitch() {
className={`size-[27px] bg-white shadow-sm ${isSelected ? "translate-x-5 shadow-lg" : ""}`}
/>
</Switch.Control>
<Label>Custom Switch</Label>
Custom Switch
</>
)}
</Switch>
@@ -190,16 +196,20 @@ function CustomSwitchGroup() {
return (
<SwitchGroup className="gap-8" orientation="horizontal">
<Switch>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label>Option 1</Label>
<Switch.Content>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Option 1
</Switch.Content>
</Switch>
<Switch>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label>Option 2</Label>
<Switch.Content>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Option 2
</Switch.Content>
</Switch>
</SwitchGroup>
);
@@ -226,7 +236,7 @@ To customize the Switch component classes, you can use the `@layer components` d
}
.switch__content {
@apply flex flex-col gap-1;
@apply items-center gap-3;
}
.switch__icon {
@@ -243,8 +253,8 @@ HeroUI follows the [BEM](https://getbem.com/) methodology to ensure component va
The Switch component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/switch.css)):
- `.switch` - Base switch container
- `.switch__content` - Optional content container
- `.switch` - Base switch container (the field)
- `.switch__content` - Clickable label wrapping the control and label text
- `.switch__control` - Switch control track
- `.switch__thumb` - Switch thumb that moves
- `.switch__icon` - Optional icon inside the thumb
@@ -266,16 +276,16 @@ The SwitchGroup component uses these CSS classes ([View source styles](https://g
The switch supports both CSS pseudo-classes and data attributes for flexibility:
- **Selected**: `[data-selected="true"]` (thumb position and background color change)
- **Hover**: `:hover` or `[data-hovered="true"]`
- **Focus**: `:focus-visible` or `[data-focus-visible="true"]` (shows focus ring)
- **Disabled**: `:disabled` or `[aria-disabled="true"]` (reduced opacity, no pointer events)
- **Hover**: `:hover` or `[data-hovered="true"]` on `Switch.Control` (button)
- **Focus**: `:focus-visible` or `[data-focus-visible="true"]` on the button (shows focus ring on track)
- **Disabled**: `[data-disabled="true"]` on the field (reduced opacity, including help text)
- **Pressed**: `:active` or `[data-pressed="true"]`
## API Reference
### Switch Props
Inherits from [React Aria Switch](https://react-spectrum.adobe.com/react-aria/Switch.html).
Inherits from [React Aria SwitchField](https://react-spectrum.adobe.com/react-aria/Switch.html).
| Prop | Type | Default | Description |
|------|------|---------|-------------|
@@ -283,27 +293,43 @@ Inherits from [React Aria Switch](https://react-spectrum.adobe.com/react-aria/Sw
| `isSelected` | `boolean` | `false` | Whether the switch is on |
| `defaultSelected` | `boolean` | `false` | Whether the switch is on by default (uncontrolled) |
| `isDisabled` | `boolean` | `false` | Whether the switch is disabled |
| `isInvalid` | `boolean` | `false` | Whether the switch is invalid |
| `isReadOnly` | `boolean` | `false` | Whether the switch is read only |
| `isRequired` | `boolean` | `false` | Whether the switch must be selected |
| `validate` | `(value: boolean) => ValidationError \| true \| null \| undefined` | - | Custom validation function |
| `validationBehavior` | `'native' \| 'aria'` | `'native'` | Whether to use native HTML form validation or ARIA |
| `name` | `string` | - | The name of the input element, used when submitting an HTML form |
| `value` | `string` | - | The value of the input element, used when submitting an HTML form |
| `onChange` | `(isSelected: boolean) => void` | - | Handler called when the switch value changes |
| `onPress` | `(e: PressEvent) => void` | - | Handler called when the switch is pressed |
| `children` | `React.ReactNode \| (values: SwitchRenderProps) => React.ReactNode` | - | Switch content or render prop |
| `render` | `DOMRenderFunction<keyof React.JSX.IntrinsicElements, SwitchRenderProps>` | - | Overrides the default DOM element with a custom render function.|
| `children` | `React.ReactNode \| (values: SwitchFieldRenderProps) => React.ReactNode` | - | Switch content or field render prop |
| `render` | `DOMRenderFunction<keyof React.JSX.IntrinsicElements, SwitchFieldRenderProps>` | - | Overrides the default DOM element with a custom render function.|
### SwitchRenderProps
### Switch.Content Props
When using the render prop pattern, these values are provided:
The clickable `<label>` that wraps the control and label text. Put `Switch.Control` and the `Label` inside it; keep `Description`/`FieldError` as siblings of `Switch.Content`. For a switch with no label, omit the `Label` and pass an `aria-label` on `Switch`.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `children` | `React.ReactNode \| (values: SwitchButtonRenderProps) => React.ReactNode` | - | Button content (control + label), or a button render prop |
| `className` | `string \| (values: SwitchButtonRenderProps) => string` | - | Classes applied to the clickable label |
### SwitchFieldRenderProps
When using a render prop on the root `Switch`, these field-level values are provided:
| Prop | Type | Description |
|------|------|-------------|
| `isSelected` | `boolean` | Whether the switch is currently on |
| `isHovered` | `boolean` | Whether the switch is hovered |
| `isPressed` | `boolean` | Whether the switch is currently pressed |
| `isFocused` | `boolean` | Whether the switch is focused |
| `isFocusVisible` | `boolean` | Whether the switch is keyboard focused |
| `isDisabled` | `boolean` | Whether the switch is disabled |
| `isReadOnly` | `boolean` | Whether the switch is read only |
| `state` | `-` | State of the switch. |
| `isInvalid` | `boolean` | Whether the switch is invalid |
| `isRequired` | `boolean` | Whether the switch is required |
| `state` | `ToggleState` | State of the switch |
### SwitchButtonRenderProps
`Switch.Control` uses button-level render props (`isHovered`, `isPressed`, `isFocusVisible`, etc.). Pass a function as `Switch.Control` children to access them.
### SwitchGroup Props
@@ -1,6 +1,5 @@
---
title: Badge
icon: updated
description: Displays a small indicator positioned relative to another element, commonly used for notification counts, status dots, and labels
links:
source: badge/badge.tsx
@@ -1,7 +1,6 @@
---
title: Chip
description: Small informational badges for displaying labels, statuses, and categories
icon: updated
links:
source: chip/chip.tsx
styles: chip.css
@@ -1,6 +1,7 @@
---
title: Calendar
description: Composable date picker with month grid, navigation, and year picker support built on React Aria Calendar
icon: updated
links:
rac: Calendar
source: calendar/calendar.tsx
@@ -1,6 +1,7 @@
---
title: CheckboxGroup
description: A checkbox group component for managing multiple checkbox selections
icon: updated
links:
rac: CheckboxGroup
source: checkbox-group/checkbox-group.tsx
@@ -32,13 +33,13 @@ export default () => (
<Label />
<Description /> {/* Optional */}
<Checkbox value="option1">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label />
<Description /> {/* Optional */}
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Label {/* plain text — the clickable label */}
</Checkbox.Content>
<Description /> {/* Optional per-checkbox help text */}
</Checkbox>
<FieldError /> {/* Optional */}
</CheckboxGroup>
@@ -110,11 +111,11 @@ function CustomCheckboxGroup() {
return (
<CheckboxGroup className="gap-4" name="custom">
<Checkbox value="option1">
<Checkbox.Control className="border-2 border-purple-500 data-[selected=true]:bg-purple-500">
<Checkbox.Indicator className="text-white" />
</Checkbox.Control>
<Checkbox.Content>
<Label>Option 1</Label>
<Checkbox.Control className="border-2 border-purple-500 data-[selected=true]:bg-purple-500">
<Checkbox.Indicator className="text-white" />
</Checkbox.Control>
Option 1
</Checkbox.Content>
</Checkbox>
</CheckboxGroup>
@@ -1,8 +1,9 @@
---
title: Checkbox
description: Checkboxes allow users to select multiple items from a list of individual items, or to mark one individual item as selected.
icon: updated
links:
rac: Checkbox
rac: CheckboxField
source: checkbox/checkbox.tsx
styles: checkbox.css
storybook: Components/Forms/Checkbox
@@ -12,7 +13,7 @@ links:
## Import
```tsx
import { Checkbox, Label } from '@heroui/react';
import { Checkbox } from '@heroui/react';
```
### Usage
@@ -26,17 +27,18 @@ import { Checkbox, Label } from '@heroui/react';
Import the Checkbox component and access all parts using dot notation.
```tsx
import { Checkbox, Label, Description } from '@heroui/react';
import { Checkbox, Description, FieldError } from '@heroui/react';
export default () => (
<Checkbox>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label />
<Description /> {/* Optional */}
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Label {/* plain text — the clickable label + accessible name */}
</Checkbox.Content>
<Description /> {/* Optional — field-level help text */}
<FieldError /> {/* Optional — validation message */}
</Checkbox>
);
```
@@ -65,10 +67,10 @@ export default () => (
name="checkbox-indeterminate"
/>
### With Label
### External Label
<ComponentPreview
name="checkbox-with-label"
name="checkbox-external-label"
/>
### With Description
@@ -131,16 +133,16 @@ The Checkbox component supports two visual variants:
You can customize individual Checkbox components:
```tsx
import { Checkbox, Label } from '@heroui/react';
import { Checkbox } from '@heroui/react';
function CustomCheckbox() {
return (
<Checkbox name="custom">
<Checkbox.Control className="border-2 border-purple-500 data-[selected=true]:bg-purple-500">
<Checkbox.Indicator className="text-white" />
</Checkbox.Control>
<Checkbox.Content>
<Label>Custom Checkbox</Label>
<Checkbox.Control className="border-2 border-purple-500 data-[selected=true]:bg-purple-500">
<Checkbox.Indicator className="text-white" />
</Checkbox.Control>
Custom Checkbox
</Checkbox.Content>
</Checkbox>
);
@@ -182,7 +184,7 @@ To customize the Checkbox component classes, you can use the `@layer components`
}
.checkbox__content {
@apply flex flex-col gap-1;
@apply items-center gap-3;
}
}
```
@@ -193,10 +195,10 @@ HeroUI follows the [BEM](https://getbem.com/) methodology to ensure component va
The Checkbox component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/checkbox.css)):
- `.checkbox` - Base checkbox container
- `.checkbox` - Base checkbox container (the field)
- `.checkbox__content` - Clickable label wrapping the control and label text
- `.checkbox__control` - Checkbox control box
- `.checkbox__indicator` - Checkbox checkmark indicator
- `.checkbox__content` - Optional content container
### Interactive States
@@ -205,16 +207,16 @@ The checkbox supports both CSS pseudo-classes and data attributes for flexibilit
- **Selected**: `[data-selected="true"]` or `[aria-checked="true"]` (shows checkmark and background color change)
- **Indeterminate**: `[data-indeterminate="true"]` (shows indeterminate state with dash)
- **Invalid**: `[data-invalid="true"]` or `[aria-invalid="true"]` (shows error state with danger colors)
- **Hover**: `:hover` or `[data-hovered="true"]`
- **Focus**: `:focus-visible` or `[data-focus-visible="true"]` (shows focus ring)
- **Disabled**: `:disabled` or `[aria-disabled="true"]` (reduced opacity, no pointer events)
- **Hover**: `:hover` or `[data-hovered="true"]` on `Checkbox.Control` (button)
- **Focus**: `:focus-visible` or `[data-focus-visible="true"]` on the button (shows focus ring on control)
- **Disabled**: `[data-disabled="true"]` on the field (reduced opacity, including help text)
- **Pressed**: `:active` or `[data-pressed="true"]`
## API Reference
### Checkbox Props
Inherits from [React Aria Checkbox](https://react-spectrum.adobe.com/react-aria/Checkbox.html).
Inherits from [React Aria CheckboxField](https://react-spectrum.adobe.com/react-aria/Checkbox.html).
| Prop | Type | Default | Description |
|------|------|---------|-------------|
@@ -224,24 +226,38 @@ Inherits from [React Aria Checkbox](https://react-spectrum.adobe.com/react-aria/
| `isDisabled` | `boolean` | `false` | Whether the checkbox is disabled |
| `isInvalid` | `boolean` | `false` | Whether the checkbox is invalid |
| `isReadOnly` | `boolean` | `false` | Whether the checkbox is read only |
| `isRequired` | `boolean` | `false` | Whether the checkbox must be selected |
| `validate` | `(value: boolean) => ValidationError \| true \| null \| undefined` | - | Custom validation function |
| `validationBehavior` | `'native' \| 'aria'` | `'native'` | Whether to use native HTML form validation or ARIA |
| `variant` | `"primary" \| "secondary"` | `"primary"` | Visual variant of the component. `primary` is the default style with shadow. `secondary` is a lower emphasis variant without shadow, suitable for use in surfaces. |
| `name` | `string` | - | The name of the input element, used when submitting an HTML form |
| `value` | `string` | - | The value of the input element, used when submitting an HTML form |
| `onChange` | `(isSelected: boolean) => void` | - | Handler called when the checkbox value changes |
| `children` | `React.ReactNode \| (values: CheckboxRenderProps) => React.ReactNode` | - | Checkbox content or render prop |
| `render` | `DOMRenderFunction<keyof React.JSX.IntrinsicElements, CheckboxRenderProps>` | - | Overrides the default DOM element with a custom render function.|
| `children` | `React.ReactNode \| (values: CheckboxFieldRenderProps) => React.ReactNode` | - | Checkbox content or field render prop |
| `render` | `DOMRenderFunction<keyof React.JSX.IntrinsicElements, CheckboxFieldRenderProps>` | - | Overrides the default DOM element with a custom render function.|
### CheckboxRenderProps
### Checkbox.Content Props
When using the render prop pattern, these values are provided:
The clickable `<label>` that wraps the control and label text. Put `Checkbox.Control` and the `Label` inside it; keep `Description`/`FieldError` as siblings of `Checkbox.Content`. For a checkbox with no label, omit the `Label` and pass an `aria-label` on `Checkbox`.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `children` | `React.ReactNode \| (values: CheckboxButtonRenderProps) => React.ReactNode` | - | Button content (control + label), or a button render prop |
| `className` | `string \| (values: CheckboxButtonRenderProps) => string` | - | Classes applied to the clickable label |
### CheckboxFieldRenderProps
When using a render prop on the root `Checkbox`, these field-level values are provided:
| Prop | Type | Description |
|------|------|-------------|
| `isSelected` | `boolean` | Whether the checkbox is currently checked |
| `isIndeterminate` | `boolean` | Whether the checkbox is in an indeterminate state |
| `isHovered` | `boolean` | Whether the checkbox is hovered |
| `isPressed` | `boolean` | Whether the checkbox is currently pressed |
| `isFocused` | `boolean` | Whether the checkbox is focused |
| `isFocusVisible` | `boolean` | Whether the checkbox is keyboard focused |
| `isDisabled` | `boolean` | Whether the checkbox is disabled |
| `isReadOnly` | `boolean` | Whether the checkbox is read only |
| `isInvalid` | `boolean` | Whether the checkbox is invalid |
| `isRequired` | `boolean` | Whether the checkbox is required |
### CheckboxButtonRenderProps
`Checkbox.Control` and `Checkbox.Indicator` use button-level render props (`isHovered`, `isPressed`, `isFocusVisible`, etc.). Pass a function as `Checkbox.Control` children or to `Checkbox.Indicator` to access them.
@@ -1,8 +1,9 @@
---
title: RadioGroup
description: Radio group for selecting a single option from a list
icon: updated
links:
rac: RadioGroup
rac: RadioField
source: radio-group/radio-group.tsx
styles: radio-group.css
storybook: Components/Forms/RadioGroup
@@ -20,7 +21,6 @@ import { RadioGroup, Radio } from '@heroui/react';
name="radio-group-basic"
/>
### Anatomy
Import the RadioGroup component and access all parts using dot notation.
@@ -33,17 +33,18 @@ export default () => (
<Label />
<Description />
<Radio value="option1">
<Radio.Control>
<Radio.Indicator>
<span>✓</span> {/* Custom indicator (optional) */}
</Radio.Indicator>
</Radio.Control>
<Radio.Content>
<Label />
<Description />
<Radio.Content> {/* The clickable area: control + label */}
<Radio.Control>
<Radio.Indicator>
<span>✓</span> {/* Custom indicator (optional) */}
</Radio.Indicator>
</Radio.Control>
Label {/* plain text — the clickable label */}
</Radio.Content>
<Description /> {/* Sibling — stays outside the button (announced via aria-describedby) */}
<FieldError /> {/* Optional — per-radio validation message */}
</Radio>
<FieldError />
<FieldError /> {/* Optional — group-level validation */}
</RadioGroup>
)
```
@@ -111,7 +112,6 @@ When used inside a [Surface](/docs/components/surface) component, use `variant="
name="radio-group-delivery-and-payment"
/>
<RelatedShowcases component="RadioGroup" />
<RelatedComponents component="radiogroup" />
@@ -194,6 +194,7 @@ The RadioGroup component uses these CSS classes ([View source styles](https://gi
#### Base Classes
- `.radio-group` - Base radio group container
- `.radio` - Individual radio item
- `.radio__content` - Radio button (RAC label wrapper for control + label text)
- `.radio__control` - Radio control (circular button)
- `.radio__indicator` - Radio indicator (inner dot)
- `.radio__content` - Radio content wrapper
@@ -238,8 +239,8 @@ The radio supports both CSS pseudo-classes and data attributes for flexibility:
| `value` | `string` | - | The value of the radio button |
| `isDisabled` | `boolean` | `false` | Whether the radio button is disabled |
| `name` | `string` | - | The name of the radio button, used when submitting an HTML form |
| `children` | `React.ReactNode \| (values: RadioRenderProps) => React.ReactNode` | - | Radio content or render prop |
| `render` | `DOMRenderFunction<keyof React.JSX.IntrinsicElements, RadioRenderProps>` | - | Overrides the default DOM element with a custom render function.|
| `children` | `React.ReactNode \| (values: RadioFieldRenderProps) => React.ReactNode` | - | Radio content or field render prop |
| `render` | `DOMRenderFunction<keyof React.JSX.IntrinsicElements, RadioFieldRenderProps>` | - | Overrides the default DOM element with a custom render function.|
### Radio.Control Props
@@ -255,27 +256,28 @@ Extends `React.HTMLAttributes<HTMLSpanElement>`.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `children` | `React.ReactNode \| (values: RadioRenderProps) => React.ReactNode` | - | Optional content or render prop that receives the current radio state. |
| `children` | `React.ReactNode \| (values: RadioButtonRenderProps) => React.ReactNode` | - | Optional content or render prop that receives the current radio button state. |
### Radio.Content Props
Extends `React.HTMLAttributes<HTMLDivElement>`.
The clickable area of the radio (the `<label>` wrapping the hidden input). Place `Radio.Control` and the `Label` inside it. `className` accepts a render function that receives `RadioButtonRenderProps`.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `children` | `React.ReactNode` | - | The content to render inside the content wrapper (typically Label and Description) |
| `children` | `React.ReactNode \| (values: RadioButtonRenderProps) => React.ReactNode` | - | The clickable content (typically Radio.Control and Label) |
### RadioRenderProps
### RadioFieldRenderProps
When using the render prop pattern, these values are provided:
When using a render prop on the root `Radio`, these field-level values are provided:
| Prop | Type | Description |
|------|------|-------------|
| `isSelected` | `boolean` | Whether the radio is currently selected |
| `isHovered` | `boolean` | Whether the radio is hovered |
| `isPressed` | `boolean` | Whether the radio is currently pressed |
| `isFocused` | `boolean` | Whether the radio is focused |
| `isFocusVisible` | `boolean` | Whether the radio is keyboard focused |
| `isDisabled` | `boolean` | Whether the radio is disabled |
| `isReadOnly` | `boolean` | Whether the radio is read only |
| `isInvalid` | `boolean` | Whether the radio is in an invalid state |
| `isRequired` | `boolean` | Whether the radio is required |
### RadioButtonRenderProps
`Radio.Control` and `Radio.Indicator` use button-level render props (`isHovered`, `isPressed`, `isFocusVisible`, etc.). Pass a function as `Radio.Control` children or to `Radio.Indicator` to access them.
@@ -1,7 +1,6 @@
---
title: TextField
description: Composition-friendly text fields with labels, descriptions, and inline validation
icon: updated
links:
rac: TextField
source: textfield/textfield.tsx
@@ -1,7 +1,6 @@
---
title: Autocomplete
description: An autocomplete combines a select with filtering, allowing users to search and select from a list of options
icon: updated
links:
rac: Select
source: autocomplete/autocomplete.tsx
@@ -1,7 +1,6 @@
---
title: Typography
description: A semantic typography primitive for headings, body copy, and inline code built on React Aria Components Text.
icon: new
links:
source: typography/typography.tsx
styles: typography.css
@@ -35,27 +35,27 @@ export default function App() {
<CheckboxGroup name="interests">
<Label>Select interests</Label>
<Checkbox value="coding">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>Coding</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Coding
</Checkbox.Content>
</Checkbox>
<Checkbox value="design">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>Design</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Design
</Checkbox.Content>
</Checkbox>
<Checkbox value="writing">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>Writing</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Writing
</Checkbox.Content>
</Checkbox>
</CheckboxGroup>
@@ -78,7 +78,7 @@ export default function App() {
### 3. Checkbox Structure
**v2:** Simple Checkbox components with children as labels
**v3:** Compound Checkbox components with `Checkbox.Control`, `Checkbox.Indicator`, and `Checkbox.Content`
**v3:** Compound Checkbox components with `Checkbox.Content`, `Checkbox.Control`, and `Checkbox.Indicator`
### 4. Event Handler
@@ -126,27 +126,27 @@ export default function App() {
>
<Label>Select interests</Label>
<Checkbox value="coding">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>Coding</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Coding
</Checkbox.Content>
</Checkbox>
<Checkbox value="design">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>Design</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Design
</Checkbox.Content>
</Checkbox>
<Checkbox value="writing">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>Writing</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Writing
</Checkbox.Content>
</Checkbox>
</CheckboxGroup>
@@ -157,7 +157,7 @@ export default function App() {
## Summary
- Keep using `CheckboxGroup`; replace `label` prop with `Label` component as a child
- Update Checkbox children to use compound component structure (`Checkbox.Control`, `Checkbox.Indicator`, `Checkbox.Content`)
- Update Checkbox children to use compound component structure (`Checkbox.Content`, `Checkbox.Control`, `Checkbox.Indicator`)
- Change `onValueChange` to `onChange`
- Add `name` prop for form integration
- Built on React Aria Components for accessibility
@@ -27,11 +27,11 @@ import { Checkbox, Label } from "@heroui/react";
export default function App() {
return (
<Checkbox defaultSelected id="option">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="option">Option</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Option
</Checkbox.Content>
</Checkbox>
);
@@ -43,12 +43,12 @@ export default function App() {
### 1. Component Structure
**v2:** Simple component with children as label
**v3:** Compound components: `Checkbox.Control`, `Checkbox.Indicator`, `Checkbox.Content`
**v3:** Compound components: `Checkbox.Content`, `Checkbox.Control`, `Checkbox.Indicator`
### 2. Label Handling
**v2:** Label passed as children directly to `Checkbox`
**v3:** Label must be wrapped in `Checkbox.Content` using the `Label` component
**v3:** Label goes inside `Checkbox.Content` (the clickable label); `Description`/`FieldError` are siblings of `Checkbox.Content`
### 3. Prop Changes
@@ -95,11 +95,11 @@ For migrating checkbox groups, see the [CheckboxGroup migration guide](/docs/rea
const [isSelected, setIsSelected] = useState(false);
<Checkbox id="subscribe" isSelected={isSelected} onChange={setIsSelected}>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="subscribe">Subscribe</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Subscribe
</Checkbox.Content>
</Checkbox>
```
@@ -123,13 +123,13 @@ For migrating checkbox groups, see the [CheckboxGroup migration guide](/docs/rea
import { Checkbox, Label, Description } from "@heroui/react";
<Checkbox id="option" defaultSelected>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="option">Option</Label>
<Description>Description text</Description>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Option
</Checkbox.Content>
<Description>Description text</Description>
</Checkbox>
```
</Tab>
@@ -157,27 +157,27 @@ For migrating checkbox groups, see the [CheckboxGroup migration guide](/docs/rea
<Label>Select cities</Label>
<Description>Choose all that apply</Description>
<Checkbox value="buenos-aires">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>Buenos Aires</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Buenos Aires
</Checkbox.Content>
</Checkbox>
<Checkbox value="sydney">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>Sydney</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Sydney
</Checkbox.Content>
</Checkbox>
<Checkbox value="san-francisco">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>San Francisco</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
San Francisco
</Checkbox.Content>
</Checkbox>
</CheckboxGroup>
@@ -201,21 +201,21 @@ For migrating checkbox groups, see the [CheckboxGroup migration guide](/docs/rea
```tsx
{/* Colors - use Tailwind classes */}
<Checkbox defaultSelected id="primary">
<Checkbox.Control className="data-[selected=true]:bg-primary data-[selected=true]:border-primary">
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="primary">Primary</Label>
<Checkbox.Control className="data-[selected=true]:bg-primary data-[selected=true]:border-primary">
<Checkbox.Indicator />
</Checkbox.Control>
Primary
</Checkbox.Content>
</Checkbox>
{/* Sizes - use Tailwind classes */}
<Checkbox defaultSelected id="medium">
<Checkbox.Control className="size-5">
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="medium">Medium</Label>
<Checkbox.Control className="size-5">
<Checkbox.Indicator />
</Checkbox.Control>
Medium
</Checkbox.Content>
</Checkbox>
```
@@ -235,13 +235,13 @@ For migrating checkbox groups, see the [CheckboxGroup migration guide](/docs/rea
<Tab value="v3">
```tsx
<Checkbox defaultSelected id="option">
<Checkbox.Control>
<Checkbox.Indicator>
{({isSelected}) => isSelected ? <HeartIcon /> : null}
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="option">Option</Label>
<Checkbox.Control>
<Checkbox.Indicator>
{({isSelected}) => isSelected ? <HeartIcon /> : null}
</Checkbox.Indicator>
</Checkbox.Control>
Option
</Checkbox.Content>
</Checkbox>
```
@@ -255,21 +255,21 @@ v3 introduces a `variant` prop with `"primary"` (default) and `"secondary"` opti
```tsx
{/* Primary variant (default) */}
<Checkbox variant="primary" id="primary-option">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="primary-option">Primary</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Primary
</Checkbox.Content>
</Checkbox>
{/* Secondary variant - lower emphasis, suitable for Surface components */}
<Checkbox variant="secondary" id="secondary-option">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="secondary-option">Secondary</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Secondary
</Checkbox.Content>
</Checkbox>
```
@@ -285,11 +285,11 @@ v3 introduces a `variant` prop with `"primary"` (default) and `"secondary"` opti
<Tab value="v3">
```tsx
<Checkbox isIndeterminate id="option">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="option">Option</Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Option
</Checkbox.Content>
</Checkbox>
```
@@ -304,13 +304,13 @@ v3 Checkbox supports a render prop pattern that provides state information:
<Checkbox id="terms">
{({isSelected, isIndeterminate, isHovered, isPressed, isFocused, isDisabled}) => (
<>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="terms">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
{isSelected ? "Terms accepted" : "Accept terms"}
</Label>
</Checkbox.Content>
</>
)}
@@ -321,15 +321,15 @@ v3 Checkbox supports a render prop pattern that provides state information:
```tsx
<Checkbox id="custom">
<Checkbox.Control>
<Checkbox.Indicator>
{({isSelected, isIndeterminate}) =>
isIndeterminate ? <MinusIcon /> : isSelected ? <CheckIcon /> : null
}
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="custom">Custom indicator</Label>
<Checkbox.Control>
<Checkbox.Indicator>
{({isSelected, isIndeterminate}) =>
isIndeterminate ? <MinusIcon /> : isSelected ? <CheckIcon /> : null
}
</Checkbox.Indicator>
</Checkbox.Control>
Custom indicator
</Checkbox.Content>
</Checkbox>
```
@@ -363,13 +363,13 @@ Available render props:
```tsx
<Checkbox className="custom-base" id="option">
<Checkbox.Control className="custom-control">
<Checkbox.Indicator className="custom-indicator" />
</Checkbox.Control>
<Checkbox.Content className="custom-content">
<Label htmlFor="option" className="custom-label">
<Checkbox.Content>
<Checkbox.Control className="custom-control">
<Checkbox.Indicator className="custom-indicator" />
</Checkbox.Control>
Option
</Label>
</Checkbox.Content>
</Checkbox>
```
@@ -380,17 +380,18 @@ The v3 Checkbox follows this structure:
```
Checkbox (Root)
├── Checkbox.Control
── Checkbox.Indicator
└── Checkbox.Content (optional)
── Label (required if using Content)
── Description (optional)
├── Checkbox.Content (the clickable label)
── Checkbox.Control
│ │ └── Checkbox.Indicator
── Label
── Description (optional, sibling)
└── FieldError (optional, sibling)
```
## Summary
1. **Component Structure**: Must use compound components (`Control`, `Indicator`, `Content`)
2. **Label Handling**: Labels must use `Label` component inside `Checkbox.Content`
1. **Component Structure**: Must use compound components (`Button`, `Control`, `Indicator`)
2. **Label Handling**: Labels go inside `Checkbox.Content`
3. **onValueChange → onChange**: Event handler prop renamed
4. **Color Removed**: Use Tailwind CSS classes on `Checkbox.Control`
5. **Size Removed**: Use Tailwind CSS classes on `Checkbox.Control`
@@ -35,28 +35,28 @@ export default function App() {
<RadioGroup name="city">
<Label>Select city</Label>
<Radio value="london">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>London</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
London
</Radio.Content>
</Radio>
<Radio value="tokyo">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Tokyo</Label>
<Description>Capital of Japan</Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Tokyo
</Radio.Content>
<Description>Capital of Japan</Description>
</Radio>
<Radio value="paris">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Paris</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Paris
</Radio.Content>
</Radio>
</RadioGroup>
@@ -74,12 +74,12 @@ export default function App() {
### 2. Radio Structure
**v2:** Simple Radio components with children as labels
**v3:** Compound Radio components with `Radio.Control`, `Radio.Indicator`, and `Radio.Content`
**v3:** Compound Radio components with `Radio.Content`, `Radio.Control`, and `Radio.Indicator`
### 3. Description Handling
**v2:** Used `description` prop on individual `Radio` components
**v3:** Use `Description` component inside `Radio.Content`
**v3:** Use `Description` component as a sibling of `Radio.Content`
### 4. Event Handler
@@ -118,22 +118,22 @@ export default function App() {
<RadioGroup name="plan">
<Label>Select plan</Label>
<Radio value="basic">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Basic</Label>
<Description>Basic features</Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Basic
</Radio.Content>
<Description>Basic features</Description>
</Radio>
<Radio value="pro">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Pro</Label>
<Description>All features</Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Pro
</Radio.Content>
<Description>All features</Description>
</Radio>
</RadioGroup>
```
@@ -160,11 +160,11 @@ export default function App() {
<RadioGroup variant="primary" name="plan">
<Label>Select plan</Label>
<Radio value="basic">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Basic</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Basic
</Radio.Content>
</Radio>
</RadioGroup>
@@ -173,11 +173,11 @@ export default function App() {
<RadioGroup variant="secondary" name="plan">
<Label>Select plan</Label>
<Radio value="basic">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Basic</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Basic
</Radio.Content>
</Radio>
</RadioGroup>
@@ -194,19 +194,19 @@ import { RadioGroup, Radio, Label } from "@heroui/react";
<RadioGroup isReadOnly defaultValue="basic" name="plan">
<Label>Select plan</Label>
<Radio value="basic">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Basic</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Basic
</Radio.Content>
</Radio>
<Radio value="pro">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Pro</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Pro
</Radio.Content>
</Radio>
</RadioGroup>
@@ -238,19 +238,19 @@ import { RadioGroup, Radio, Label } from "@heroui/react";
<RadioGroup name="city" value={value} onChange={setValue}>
<Label>Select city</Label>
<Radio value="london">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>London</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
London
</Radio.Content>
</Radio>
<Radio value="tokyo">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Tokyo</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Tokyo
</Radio.Content>
</Radio>
</RadioGroup>
@@ -262,7 +262,7 @@ import { RadioGroup, Radio, Label } from "@heroui/react";
- Replace `label` prop with `Label` component as child
- Update Radio children to use compound component structure
- Replace `description` prop on Radio with `Description` component inside `Radio.Content`
- Replace `description` prop on Radio with `Description` component as a sibling of `Radio.Content`
- Change `onValueChange` to `onChange`
- Add `name` prop for form integration
- New `variant` prop: `"primary"` (default) or `"secondary"` for lower emphasis styling
@@ -34,21 +34,21 @@ export default function App() {
<RadioGroup>
<Label>Select city</Label>
<Radio value="london">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>London</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
London
</Radio.Content>
</Radio>
<Radio value="tokyo">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Tokyo</Label>
<Description>Capital of Japan</Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Tokyo
</Radio.Content>
<Description>Capital of Japan</Description>
</Radio>
</RadioGroup>
);
@@ -60,7 +60,7 @@ export default function App() {
### 1. Component Structure
**v2:** Simple Radio with children as label
**v3:** Compound components: `Radio.Control`, `Radio.Indicator`, `Radio.Content`
**v3:** Compound components: `Radio.Content`, `Radio.Control`, `Radio.Indicator`
### 2. Prop Changes
@@ -68,7 +68,7 @@ export default function App() {
|---------|-------------|-------|
| `onValueChange` | `onChange` | Renamed event handler |
| `label` (on RadioGroup) | — | Use `Label` component |
| `description` (on Radio) | — | Use `Description` component inside `Radio.Content` |
| `description` (on Radio) | — | Use `Description` component as a sibling of `Radio.Content` |
| `size` | — | Removed (use Tailwind CSS) |
| `color` | — | Removed (use Tailwind CSS) |
| `classNames` | — | Use `className` props on individual components |
@@ -108,13 +108,13 @@ export default function App() {
<RadioGroup>
<Label>Select city</Label>
<Radio value="london">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>London</Label>
<Description>Capital of England</Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
London
</Radio.Content>
<Description>Capital of England</Description>
</Radio>
</RadioGroup>
@@ -122,11 +122,11 @@ export default function App() {
<RadioGroup isInvalid>
<Label>Select city</Label>
<Radio value="london">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>London</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
London
</Radio.Content>
</Radio>
<FieldError>Please select an option</FieldError>
@@ -162,19 +162,19 @@ export default function App() {
<RadioGroup value={selected} onChange={setSelected}>
<Label>Select city</Label>
<Radio value="london">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>London</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
London
</Radio.Content>
</Radio>
<Radio value="tokyo">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Tokyo</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Tokyo
</Radio.Content>
</Radio>
</RadioGroup>
@@ -198,19 +198,19 @@ export default function App() {
<RadioGroup orientation="horizontal">
<Label>Select city</Label>
<Radio value="london">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>London</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
London
</Radio.Content>
</Radio>
<Radio value="tokyo">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Tokyo</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Tokyo
</Radio.Content>
</Radio>
</RadioGroup>
@@ -227,11 +227,11 @@ v3 introduces a `variant` prop on `RadioGroup` with `"primary"` (default) and `"
<RadioGroup variant="primary">
<Label>Select city</Label>
<Radio value="london">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>London</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
London
</Radio.Content>
</Radio>
</RadioGroup>
@@ -240,11 +240,11 @@ v3 introduces a `variant` prop on `RadioGroup` with `"primary"` (default) and `"
<RadioGroup variant="secondary">
<Label>Select city</Label>
<Radio value="london">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>London</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
London
</Radio.Content>
</Radio>
</RadioGroup>
@@ -258,19 +258,19 @@ v3 supports `isReadOnly` on `RadioGroup`, which prevents value changes while kee
<RadioGroup isReadOnly defaultValue="london">
<Label>Select city</Label>
<Radio value="london">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>London</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
London
</Radio.Content>
</Radio>
<Radio value="tokyo">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Tokyo</Label>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Tokyo
</Radio.Content>
</Radio>
</RadioGroup>
@@ -284,19 +284,19 @@ The v3 Radio follows this structure:
RadioGroup (Root)
├── Label (optional)
├── Radio
│ ├── Radio.Control
│ │ ── Radio.Indicator
│ └── Radio.Content
── Label
└── Description (optional)
│ ├── Radio.Content (the clickable label)
│ │ ── Radio.Control
│ │ │ └── Radio.Indicator
── Label
│ └── Description (optional, sibling)
└── FieldError (optional)
```
## Summary
1. **Component Structure**: Must use compound components (`Radio.Control`, `Radio.Indicator`, `Radio.Content`)
1. **Component Structure**: Must use compound components (`Radio.Content`, `Radio.Control`, `Radio.Indicator`)
2. **Label Handling**: `label` prop removed - use `Label` component
3. **Description Handling**: `description` prop removed - use `Description` component inside `Radio.Content`
3. **Description Handling**: `description` prop removed - use `Description` component as a sibling of `Radio.Content`
4. **Event Handler**: `onValueChange` → `onChange`
5. **Styling Props Removed**: `size`, `color` - use Tailwind CSS
6. **ClassNames Removed**: Use `className` props on individual components
@@ -27,11 +27,11 @@ import { Switch, Label } from "@heroui/react";
export default function App() {
return (
<Switch>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Label>Enable notifications</Label>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Enable notifications
</Switch.Content>
</Switch>
);
@@ -43,7 +43,7 @@ export default function App() {
### 1. Component Structure
**v2:** Simple Switch with children as label
**v3:** Compound components (`Switch.Control`, `Switch.Thumb`, `Switch.Content`) with `Label` component
**v3:** Compound components (`Switch.Content`, `Switch.Control`, `Switch.Thumb`) with `Label` component
### 2. Prop Changes
@@ -62,7 +62,7 @@ export default function App() {
### 3. New Components
- `SwitchGroup` - For grouping multiple switches
- `Switch.Content` - Container for label and description
- `Switch.Content` - The clickable label wrapping the control and `Label`
- `Switch.Icon` - For icons inside the thumb
## Migration Examples
@@ -88,11 +88,11 @@ export default function App() {
const [isSelected, setIsSelected] = useState(true);
<Switch isSelected={isSelected} onChange={setIsSelected}>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Label>Airplane mode</Label>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Airplane mode
</Switch.Content>
</Switch>
```
@@ -110,9 +110,11 @@ export default function App() {
<Tab value="v3">
```tsx
<Switch defaultSelected aria-label="Automatic updates">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
</Switch.Content>
</Switch>
```
</Tab>
@@ -129,15 +131,15 @@ export default function App() {
<Tab value="v3">
```tsx
<Switch>
<Switch.Control>
<Switch.Thumb>
<Switch.Icon>
<CheckIcon />
</Switch.Icon>
</Switch.Thumb>
</Switch.Control>
<Switch.Content>
<Label>Enable notifications</Label>
<Switch.Control>
<Switch.Thumb>
<Switch.Icon>
<CheckIcon />
</Switch.Icon>
</Switch.Thumb>
</Switch.Control>
Enable notifications
</Switch.Content>
</Switch>
```
@@ -160,13 +162,13 @@ export default function App() {
<Tab value="v3">
```tsx
<Switch>
<Switch.Control className="flex items-center gap-2">
<SunIcon />
<Switch.Thumb />
<MoonIcon />
</Switch.Control>
<Switch.Content>
<Label>Dark mode</Label>
<Switch.Control className="flex items-center gap-2">
<SunIcon />
<Switch.Thumb />
<MoonIcon />
</Switch.Control>
Dark mode
</Switch.Content>
</Switch>
```
@@ -188,13 +190,13 @@ export default function App() {
import { Switch, Label, Description } from "@heroui/react";
<Switch>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Label>Enable notifications</Label>
<Description>You will receive notifications for all activity</Description>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Enable notifications
</Switch.Content>
<Description>You will receive notifications for all activity</Description>
</Switch>
```
</Tab>
@@ -223,54 +225,54 @@ export default function App() {
{/* Sizes */}
<div className="flex gap-4">
<Switch size="sm">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Label>Small</Label>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Small
</Switch.Content>
</Switch>
<Switch size="md">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Label>Medium</Label>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Medium
</Switch.Content>
</Switch>
<Switch size="lg">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Label>Large</Label>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Large
</Switch.Content>
</Switch>
</div>
{/* Colors - Use Tailwind CSS classes */}
<Switch>
<Switch.Control className="data-[selected=true]:bg-accent">
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Label>Primary</Label>
<Switch.Control className="data-[selected=true]:bg-accent">
<Switch.Thumb />
</Switch.Control>
Primary
</Switch.Content>
</Switch>
<Switch>
<Switch.Control className="data-[selected=true]:bg-success">
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Label>Success</Label>
<Switch.Control className="data-[selected=true]:bg-success">
<Switch.Thumb />
</Switch.Control>
Success
</Switch.Content>
</Switch>
<Switch>
<Switch.Control className="data-[selected=true]:bg-danger">
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Label>Danger</Label>
<Switch.Control className="data-[selected=true]:bg-danger">
<Switch.Thumb />
</Switch.Control>
Danger
</Switch.Content>
</Switch>
```
@@ -295,19 +297,19 @@ export default function App() {
<SwitchGroup>
<Switch name="notifications">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Label>Allow Notifications</Label>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Allow Notifications
</Switch.Content>
</Switch>
<Switch name="marketing">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Label>Marketing emails</Label>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Marketing emails
</Switch.Content>
</Switch>
</SwitchGroup>
@@ -321,12 +323,13 @@ The v3 Switch follows this structure:
```
Switch (Root)
├── Switch.Control
── Switch.Thumb
└── Switch.Icon (optional)
└── Switch.Content (optional)
── Label (optional)
── Description (optional)
├── Switch.Content (the clickable label)
── Switch.Control
└── Switch.Thumb
│ │ └── Switch.Icon (optional)
── Label
── Description (optional, sibling)
└── FieldError (optional, sibling)
```
For groups:
@@ -334,17 +337,17 @@ For groups:
```
SwitchGroup
├── Switch
│ ├── Switch.Control
│ │ ── Switch.Thumb
│ └── Switch.Content
── Label
└── Description (optional)
│ ├── Switch.Content
│ │ ── Switch.Control
│ │ │ └── Switch.Thumb
── Label
│ └── Description (optional)
└── Switch
├── Switch.Control
── Switch.Thumb
└── Switch.Content
── Label
└── Description (optional)
├── Switch.Content
── Switch.Control
│ │ └── Switch.Thumb
── Label
└── Description (optional)
```
## Important Notes
@@ -357,7 +360,7 @@ SwitchGroup
### Label Handling
- **v2:** Children were used as label
- **v3:** Use `Label` component inside `Switch.Content`; optionally add `Description` alongside it
- **v3:** Label goes inside `Switch.Content` (the clickable label); `Description`/`FieldError` are siblings of `Switch.Content`
### Icons
@@ -366,11 +369,11 @@ SwitchGroup
## Summary
1. **Component Structure**: Must use compound components (`Switch.Control`, `Switch.Thumb`, `Switch.Content`)
2. **Content Container**: Use `Switch.Content` to wrap `Label` and `Description` for proper layout
1. **Component Structure**: Must use compound components (`Switch.Content`, `Switch.Control`, `Switch.Thumb`)
2. **Clickable Label**: Use `Switch.Content` to wrap `Switch.Control` and `Label`; place `Description`/`FieldError` as siblings of `Switch.Content`
3. **Label Handling**: Children no longer used as label - use `Label` component inside `Switch.Content`
4. **Event Handler**: `onValueChange` → `onChange`
5. **Styling Props Removed**: `color` - use Tailwind CSS
6. **Icon Props Removed**: `thumbIcon`, `startContent`, `endContent` - use components or customize directly
7. **ClassNames Removed**: Use `className` props on individual components
8. **New Components**: `SwitchGroup` for grouping switches, `Switch.Content` for label/description container
8. **New Components**: `SwitchGroup` for grouping switches, `Switch.Content` as the clickable label wrapping the control and `Label`
@@ -9,6 +9,16 @@ description: All updates and changes to HeroUI v3, including new features, fixes
## Latest Release
### v3.2.0
**June 6, 2026**
Calendar week and day views, a reworked year picker, and range demos on React Aria 1.18, plus tooltip delay theme variables and the patch fixes shipped with it. Breaking: `Radio`, `Checkbox`, and `Switch` move to an explicit `*.Content` composition (control nests inside `*.Content`; the label is plain text, no nested `<Label>`; help text becomes a sibling).
[Read full release notes →](/docs/react/releases/v3-2-0)
---
### v3.1.0
**May 25, 2026**
@@ -7,6 +7,7 @@
"---Overview---",
"index",
"---Releases---",
"v3-2-0",
"v3-1-0",
"v3-0-5",
"v3-0-4",
@@ -0,0 +1,260 @@
---
title: v3.2.0
description: Calendar week/day views and year picker on React Aria 1.18, plus breaking Radio, Checkbox, and Switch composition changes.
github:
pull: 6616
---
<div className="flex items-center gap-3 mb-6">
<span className="text-sm text-muted">June 6, 2026</span>
</div>
Calendar gains week and day views, a reworked year picker, and range demos built on React Aria 1.18. [Tooltip](/docs/components/tooltip) adds theme variables for global show and hide delays. The toggles (`Radio`, `Checkbox`, `Switch`) move to React Aria's `*Field` + `*Button` composition. This release also rolls in the patch fixes shipped alongside React Aria 1.18 (`Table.SortableColumnHeader`, toast and fieldset fixes, scroll and RTL style fixes).
⚠️ **Breaking changes**: `Radio`, `Checkbox`, and `Switch` move to an explicit `*.Content` composition — `*.Control` nests inside `*.Content`, the label is plain text inside `*.Content` (no nested `<Label>`), and `Description`/`FieldError` become siblings of `*.Content`. See [Breaking Changes](#-breaking-changes).
## Installation
Update to the latest version:
<Tabs items={["npm", "pnpm", "yarn", "bun"]}>
<Tab value="npm">
```bash
npm i @heroui/styles@latest @heroui/react@latest
```
</Tab>
<Tab value="pnpm">
```bash
pnpm add @heroui/styles@latest @heroui/react@latest
```
</Tab>
<Tab value="yarn">
```bash
yarn add @heroui/styles@latest @heroui/react@latest
```
</Tab>
<Tab value="bun">
```bash
bun add @heroui/styles@latest @heroui/react@latest
```
</Tab>
</Tabs>
<Callout type="info">
**Using AI assistants?** Prompt "Hey Cursor, update HeroUI to the latest version" and your AI assistant will compare versions and apply the necessary changes. Learn more about the [HeroUI MCP Server](/docs/ui-for-agents/mcp-server).
</Callout>
## What's New
### Calendar
`Calendar` and `RangeCalendar` gain week and day views, plus new React Aria 1.18 calendar props.
- **Week / day views**: `visibleDuration` renders multi-week or single-day layouts
- **Multiple selection**: select several dates in a single `Calendar`
- **React Aria 1.18 props**: `weeksInMonth` and `isDateUnavailable(date, anchorDate)` for ranges
- **Internals**: month labels use React Aria's `CalendarHeading`, and the year picker is rebuilt on React Aria calendar hooks
**Week view:**
<ComponentPreview name="calendar-week-view" />
**Day view:**
<ComponentPreview name="calendar-day-view" />
**Multiple selection:**
<ComponentPreview name="calendar-multiple-selection" />
**Multiple selection:**
<ComponentPreview name="calendar-multiple-selection" />
### Table.SortableColumnHeader
`Table.SortableColumnHeader` renders a sortable column label with an optional ascending/descending indicator. Use it inside a `Table.Column` render prop and forward `sortDirection` ([#6588](https://github.com/heroui-inc/heroui/pull/6588)).
```tsx
<Table.Column allowsSorting>
{({sortDirection}) => (
<Table.SortableColumnHeader sortDirection={sortDirection}>
Name
</Table.SortableColumnHeader>
)}
</Table.Column>
```
- **Default indicator**: Chevron appears when a sort direction exists
- **Custom indicator**: Pass `indicator`, or hide it with `showIndicator={false}`
- **Style slots**: `.table__sortable-column-header` + `.table__sortable-column-indicator`
<ComponentPreview name="table-sorting" />
### Tooltip delay theme variables
[Tooltip](/docs/components/tooltip) reads default show and hide delays from theme CSS variables ([#6617](https://github.com/heroui-inc/heroui/pull/6617)):
- `--tooltip-delay` — delay before showing a tooltip (default: `1500ms`)
- `--tooltip-close-delay` — delay before hiding a tooltip (default: `500ms`)
Override them globally:
```css
:root {
--tooltip-delay: 700ms;
--tooltip-close-delay: 0ms;
}
```
Individual `delay` and `closeDelay` props still override these values on specific tooltips.
<Callout type="warning">
**Behavior change:** With the default HeroUI theme, tooltip delays now default to `1500ms` / `500ms` instead of the previous React Aria defaults of `700ms` / `0ms`. Set the CSS variables or props explicitly to preserve the old timing.
</Callout>
## Component Fixes
- **Toast**: ViewTransition updates are serialized in the toast queue, preventing skipped transitions and AbortError rejections when `toast.promise()` swaps the loading toast for success/error feedback ([#6511](https://github.com/heroui-inc/heroui/pull/6511)).
- **Fieldset**: `disabled` propagates through React Aria Button, CheckboxGroup, Link, RadioGroup, Slider, ToggleButton, and ToggleButtonGroup contexts ([#6596](https://github.com/heroui-inc/heroui/pull/6596)).
## Style Fixes
- **Modal / AlertDialog**: `scroll-inside` dialogs cap height with `max-h-full min-h-0` so the body scrolls instead of overflowing ([#6597](https://github.com/heroui-inc/heroui/pull/6597)).
- **ScrollShadow**: Fade masks reserve space for visible native scrollbars via `--scroll-shadow-scrollbar-size` ([#6598](https://github.com/heroui-inc/heroui/pull/6598)).
- **Table RTL**: Column separators and resize handles use logical `end-0` positioning in RTL ([#6606](https://github.com/heroui-inc/heroui/pull/6606)).
## Dependencies
- **React Aria Components**: `1.17.0` → `1.18.0` ([#6586](https://github.com/heroui-inc/heroui/pull/6586)). 1.18 adds the `*Field` + `*Button` composition adopted by the toggles, `CalendarHeading`, and `isDateUnavailable(date, anchorDate)`.
- **@internationalized/date**: `3.12.1` → `3.12.2`
- **React Aria / Stately helpers**: `@react-aria/*`, `@react-stately/*`, and `@react-types/shared` patch updates
## ⚠️ Breaking Changes
### Radio, Checkbox & Switch: explicit `*.Content` composition
These toggles now use React Aria's `*Field` + `*Button` composition under the hood. `X.Content` is now the **clickable label** (React Aria's `*Button`). Three things change:
- **`X.Control` moves inside `X.Content`** — they used to be siblings.
- **The label is plain text inside `X.Content`** — `X.Content` renders the `<label>` element, so don't nest a `<Label>` component (nested `<label>` elements are invalid HTML). For a standalone `Label`, place it **outside** and link it with `htmlFor` + the toggle `id`.
- **`Description`/`FieldError` move out** as siblings of `X.Content`, so they're exposed via `aria-describedby` instead of being folded into the accessible name.
**Checkbox**
```tsx
// v3.1
<Checkbox>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>Accept terms</Label>
<Description>You agree to our terms</Description>
</Checkbox.Content>
</Checkbox>
// v3.2
<Checkbox>
<Checkbox.Content>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
Accept terms
</Checkbox.Content>
<Description>You agree to our terms</Description>
</Checkbox>
```
**Radio**
```tsx
// v3.1
<Radio value="a">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Option A</Label>
</Radio.Content>
</Radio>
// v3.2
<Radio value="a">
<Radio.Content>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
Option A
</Radio.Content>
</Radio>
```
**Switch**
```tsx
// v3.1
<Switch>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Label>Enable notifications</Label>
</Switch.Content>
</Switch>
// v3.2
<Switch>
<Switch.Content>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
Enable notifications
</Switch.Content>
</Switch>
```
**Migration summary**
| v3.1 | v3.2 |
|------|------|
| `X.Control` and `X.Content` are siblings | `X.Control` nests inside `X.Content` |
| `X.Content` is a layout `<div>` wrapping `Label` + help text | `X.Content` is the clickable `<label>` wrapping `X.Control` + label text |
| Label provided via `<Label>` inside `X.Content` | Label is **plain text** inside `X.Content` (no nested `<Label>`) |
| `Description` / `FieldError` inside `X.Content` | `Description` / `FieldError` as siblings of `X.Content` |
**External label** — to use a standalone `Label`, place it outside the toggle and link it with `htmlFor` + the toggle `id`:
```tsx
<div className="flex items-center gap-3">
<Checkbox id="terms">
<Checkbox.Content>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
<Label htmlFor="terms">Accept terms</Label>
</div>
```
**Control-only checkboxes and switches** (no label, e.g. table row selection or icon switches) still need `X.Content` as the clickable wrapper. Wrap `Checkbox.Control` / `Switch.Control` in `X.Content`, omit the label, and pass an `aria-label` on the root.
Full per-component guides: [Checkbox](/docs/react/migration/checkbox), [Checkbox Group](/docs/react/migration/checkbox-group), [Radio](/docs/react/migration/radio), [Radio Group](/docs/react/migration/radio-group), and [Switch](/docs/react/migration/switch).
## Links
- [Calendar Docs](/docs/react/components/calendar)
- [Tooltip Docs](/docs/react/components/tooltip)
- [Checkbox Docs](/docs/react/components/checkbox)
- [Radio Group Docs](/docs/react/components/radio-group)
- [Switch Docs](/docs/react/components/switch)
- [Component Docs](/docs/react/components)
- [GitHub Repository](https://github.com/heroui-inc/heroui)
- [GitHub PR #6616](https://github.com/heroui-inc/heroui/pull/6616)
## Contributors
Thanks to everyone who contributed to this release!
<PRContributors />
@@ -0,0 +1,106 @@
"use client";
import {Rocket} from "@gravity-ui/icons";
import LinkRoot from "fumadocs-core/link";
import {AnimatePresence, animate, motion, useMotionValue, useReducedMotion} from "motion/react";
import {useEffect, useState} from "react";
interface ReleaseBadge {
label: string;
href: string;
}
interface ReleaseBadgesProps {
badges: ReleaseBadge[];
/** Autoplay interval in milliseconds. */
intervalMs?: number;
}
// Soft, fluid settle for the badge slide (Apple-style spring, minimal bounce).
const slideSpring = {bounce: 0.18, type: "spring", visualDuration: 0.4} as const;
// Quick ease-out fade so opacity never lingers behind the movement.
const fadeOut = {duration: 0.22, ease: [0.215, 0.61, 0.355, 1]} as const;
function BadgePill({badge}: {badge: ReleaseBadge}) {
return (
<LinkRoot
className="flex items-center gap-1 rounded-full bg-accent-soft px-2 py-1 text-xs text-accent-soft-foreground transition-colors hover:bg-accent-soft-hover"
href={badge.href}
>
<Rocket className="size-3 shrink-0 text-accent-soft-foreground" />
<span className="max-w-60 truncate sm:max-w-full">{badge.label}</span>
</LinkRoot>
);
}
export function ReleaseBadges({badges, intervalMs = 5000}: ReleaseBadgesProps) {
const reduceMotion = useReducedMotion() ?? false;
const [index, setIndex] = useState(0);
const [paused, setPaused] = useState(false);
const progress = useMotionValue(0);
const count = badges.length;
// Autoplay: drive a single progress value (transform-only fill) and advance
// on completion. Stopping retains the value, so hover/focus pause + resume
// and manual jumps stay in sync and interruptible.
useEffect(() => {
if (count <= 1 || paused) return;
if (reduceMotion) {
const id = setInterval(() => setIndex((i) => (i + 1) % count), intervalMs);
return () => clearInterval(id);
}
const remaining = (1 - progress.get()) * intervalMs;
const controls = animate(progress, 1, {
duration: remaining / 1000,
ease: "linear",
onComplete: () => {
progress.set(0);
setIndex((i) => (i + 1) % count);
},
});
return () => controls.stop();
}, [index, paused, reduceMotion, count, intervalMs, progress]);
if (count === 0) return null;
// Single highlight: render the plain badge (no carousel chrome).
if (count === 1) {
const [only] = badges;
return only ? <BadgePill badge={only} /> : null;
}
const activeIndex = index % count;
const active = badges[activeIndex];
if (!active) return null;
return (
<div
className="flex h-6 items-center justify-center"
onBlurCapture={() => setPaused(false)}
onFocusCapture={() => setPaused(true)}
onMouseEnter={() => setPaused(true)}
onMouseLeave={() => setPaused(false)}
>
<AnimatePresence initial={false} mode="popLayout">
<motion.div
key={activeIndex}
animate={{opacity: 1, y: 0}}
exit={reduceMotion ? {opacity: 0} : {opacity: 0, y: -10}}
initial={reduceMotion ? {opacity: 0} : {opacity: 0, y: 10}}
style={{willChange: "transform, opacity"}}
transition={
reduceMotion ? {duration: 0} : {default: slideSpring, opacity: fadeOut, y: slideSpring}
}
>
<BadgePill badge={active} />
</motion.div>
</AnimatePresence>
</div>
);
}
+2 -8
View File
@@ -1,4 +1,3 @@
import {Rocket} from "@gravity-ui/icons";
import {buttonVariants} from "@heroui/react";
import LinkRoot from "fumadocs-core/link";
import {notFound} from "next/navigation";
@@ -11,6 +10,7 @@ import {i18n} from "@/lib/i18n";
import {DemoShowcase} from "./components/demo-showcase";
import {ProBanner} from "./components/pro-banner";
import {ReleaseBadges} from "./components/release-badges";
export const dynamic = "force-static";
export const revalidate = false;
@@ -31,13 +31,7 @@ export default async function HomePage({params}: {params: Promise<{lang: string}
<main className="flex min-h-[calc(100vh-4rem)] flex-col">
<section className="z-10 flex min-h-0 flex-1 flex-col items-center px-4 pt-12 text-center">
<div className="mx-auto flex max-w-2xl flex-col items-center justify-center">
<LinkRoot
className="flex items-center gap-1 rounded-full bg-accent-soft px-2 py-1 text-xs text-accent-soft-foreground transition-colors hover:bg-accent-soft-hover"
href="/docs/native/releases/create-heroui-native-app"
>
<Rocket className="size-3 text-accent-soft-foreground" />
<span className="max-w-60 truncate sm:max-w-full">{home.releaseBadge}</span>
</LinkRoot>
<ReleaseBadges badges={home.releaseBadges} />
<h1 className="mt-2 text-3xl font-bold tracking-tight text-foreground sm:text-4xl lg:mt-4 lg:text-5xl">
{home.titleMain} <div className="text-muted/70">{home.titleMuted}</div>
</h1>
@@ -122,9 +122,11 @@ export function ShuffleButton({enableKeyboardShortcut = true}: ShuffleButtonProp
variant="secondary"
onChange={setIsDontShowAgainChecked}
>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
<Label htmlFor="dont-show-again">{dict.dontShowAgain}</Label>
</div>
@@ -12,16 +12,20 @@ export function UIComponentsDemo() {
<div className="flex w-full items-center justify-center gap-8">
{/* Checkbox - Selected State */}
<Checkbox defaultSelected aria-label={t.checkboxAriaLabel}>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
{/* Switch - On State */}
<Switch defaultSelected aria-label={t.switchAriaLabel}>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
</Switch.Content>
</Switch>
{/* Radio Buttons - Unselected and Selected */}
@@ -33,14 +37,18 @@ export function UIComponentsDemo() {
orientation="horizontal"
>
<Radio value="option1">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
</Radio>
<Radio value="option2">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
</Radio>
</RadioGroup>
@@ -230,9 +230,11 @@ export function DesignThemeSelector({
</Description>
</div>
<Switch isSelected={vibrant} onChange={handleVibrantToggle}>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.Content>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
</Switch.Content>
</Switch>
</div>
</div>
+15 -15
View File
@@ -6,31 +6,31 @@ export function Basic() {
<Label></Label>
<Description></Description>
<Checkbox value="coding">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Description></Description>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
<Description></Description>
</Checkbox>
<Checkbox value="design">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Description></Description>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
<Description></Description>
</Checkbox>
<Checkbox value="writing">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Description></Description>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
<Description></Description>
</Checkbox>
</CheckboxGroup>
);
@@ -10,27 +10,27 @@ export function Controlled() {
<CheckboxGroup className="min-w-[320px]" name="skills" value={selected} onChange={setSelected}>
<Label></Label>
<Checkbox value="coding">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
<Checkbox value="design">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
<Checkbox value="writing">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
<Label className="my-4 text-sm text-muted">{selected.join(", ") || "无"}</Label>
@@ -8,31 +8,31 @@ export function CustomRenderFunction() {
<Label></Label>
<Description></Description>
<Checkbox value="coding">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Description></Description>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
<Description></Description>
</Checkbox>
<Checkbox value="design">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Description></Description>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
<Description></Description>
</Checkbox>
<Checkbox value="writing">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Description></Description>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
<Description></Description>
</Checkbox>
</CheckboxGroup>
);
@@ -6,22 +6,22 @@ export function Disabled() {
<Label></Label>
<Description></Description>
<Checkbox value="feature1">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Description></Description>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
<Description></Description>
</Checkbox>
<Checkbox value="feature2">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Description></Description>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
<Description></Description>
</Checkbox>
</CheckboxGroup>
);
@@ -32,22 +32,19 @@ export function FeaturesAndAddOns() {
<Description></Description>
<div className="flex flex-col gap-2">
{addOns.map((addon) => (
<Checkbox
key={addon.value}
value={addon.value}
variant="secondary"
className={clsx(
"group relative flex-col gap-4 rounded-3xl bg-surface px-5 py-4 transition-all",
"data-[selected=true]:bg-accent/10",
)}
>
<Checkbox.Control className="absolute top-3 right-4 size-5 rounded-full before:rounded-full">
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content className="flex flex-row items-start justify-start gap-4">
<addon.icon className="size-5 text-accent" />
<Checkbox key={addon.value} value={addon.value} variant="secondary">
<Checkbox.Content
className={clsx(
"group relative flex w-full flex-row items-start justify-start gap-4 rounded-3xl bg-surface px-5 py-4 transition-all",
"data-[selected=true]:bg-accent/10",
)}
>
<Checkbox.Control className="absolute top-3 right-4 size-5 rounded-full before:rounded-full">
<Checkbox.Indicator />
</Checkbox.Control>
<addon.icon className="size-5 text-accent-soft-foreground" />
<div className="flex flex-col gap-1">
<Label>{addon.title}</Label>
<span>{addon.title}</span>
<Description>{addon.description}</Description>
</div>
</Checkbox.Content>
@@ -1,6 +1,6 @@
"use client";
import {Checkbox, CheckboxGroup, Label} from "@heroui/react";
import {Checkbox, CheckboxGroup} from "@heroui/react";
import {useState} from "react";
export function Indeterminate() {
@@ -17,37 +17,37 @@ export function Indeterminate() {
setSelected(isSelected ? allOptions : []);
}}
>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
<div className="ml-6 flex flex-col gap-2">
<CheckboxGroup value={selected} onChange={setSelected}>
<Checkbox value="coding">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
<Checkbox value="design">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
<Checkbox value="writing">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
</CheckboxGroup>
@@ -7,31 +7,31 @@ export function OnSurface() {
<Label></Label>
<Description></Description>
<Checkbox value="coding">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Description></Description>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
<Description></Description>
</Checkbox>
<Checkbox value="design">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Description></Description>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
<Description></Description>
</Checkbox>
<Checkbox value="writing">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Description></Description>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
<Description></Description>
</Checkbox>
</CheckboxGroup>
</Surface>
@@ -18,27 +18,27 @@ export function Validation() {
<CheckboxGroup isRequired name="preferences">
<Label></Label>
<Checkbox value="email">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
<Checkbox value="sms">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
<Checkbox value="push">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
<FieldError></FieldError>
@@ -8,52 +8,52 @@ export function WithCustomIndicator() {
<Label></Label>
<Description></Description>
<Checkbox value="notifications">
<Checkbox.Control>
<Checkbox.Indicator>
{({isSelected}) =>
isSelected ? (
<svg
aria-hidden="true"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeWidth={2}
viewBox="0 0 24 24"
>
<path d="M6 18L18 6M6 6l12 12" />
</svg>
) : null
}
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Description></Description>
<Checkbox.Control>
<Checkbox.Indicator>
{({isSelected}) =>
isSelected ? (
<svg
aria-hidden="true"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeWidth={2}
viewBox="0 0 24 24"
>
<path d="M6 18L18 6M6 6l12 12" />
</svg>
) : null
}
</Checkbox.Indicator>
</Checkbox.Control>
</Checkbox.Content>
<Description></Description>
</Checkbox>
<Checkbox value="newsletter">
<Checkbox.Control>
<Checkbox.Indicator>
{({isSelected}) =>
isSelected ? (
<svg
aria-hidden="true"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeWidth={2}
viewBox="0 0 24 24"
>
<path d="M6 18L18 6M6 6l12 12" />
</svg>
) : null
}
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Description></Description>
<Checkbox.Control>
<Checkbox.Indicator>
{({isSelected}) =>
isSelected ? (
<svg
aria-hidden="true"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeWidth={2}
viewBox="0 0 24 24"
>
<path d="M6 18L18 6M6 6l12 12" />
</svg>
) : null
}
</Checkbox.Indicator>
</Checkbox.Control>
</Checkbox.Content>
<Description></Description>
</Checkbox>
</CheckboxGroup>
);
+6 -6
View File
@@ -1,13 +1,13 @@
import {Checkbox, Label} from "@heroui/react";
import {Checkbox} from "@heroui/react";
export function Basic() {
return (
<Checkbox id="basic-terms">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox name="basic-terms">
<Checkbox.Content>
<Label htmlFor="basic-terms"></Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
);
@@ -1,6 +1,6 @@
"use client";
import {Checkbox, Label} from "@heroui/react";
import {Checkbox} from "@heroui/react";
import {useState} from "react";
export function Controlled() {
@@ -8,16 +8,14 @@ export function Controlled() {
return (
<div className="flex flex-col gap-3">
<div className="flex items-center gap-3">
<Checkbox id="email-notifications" isSelected={isSelected} onChange={setIsSelected}>
<Checkbox id="email-notifications" isSelected={isSelected} onChange={setIsSelected}>
<Checkbox.Content>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="email-notifications"></Label>
</Checkbox.Content>
</Checkbox>
</div>
</Checkbox.Content>
</Checkbox>
<p className="text-sm text-muted">
<span className="font-medium">{isSelected ? "已勾选" : "未勾选"}</span>
</p>
@@ -1,72 +1,72 @@
"use client";
import {Checkbox, Label} from "@heroui/react";
import {Checkbox} from "@heroui/react";
export function CustomIndicator() {
return (
<div className="flex gap-4">
<Checkbox defaultSelected name="heart">
<Checkbox.Control>
<Checkbox.Indicator>
{({isSelected}) =>
isSelected ? (
<svg fill="currentColor" viewBox="0 0 24 24">
<path
d="M12.62 20.81c-.34.12-.9.12-1.24 0C8.48 19.82 2 15.69 2 8.69 2 5.6 4.49 3.1 7.56 3.1c1.82 0 3.43.88 4.44 2.24a5.53 5.53 0 0 1 4.44-2.24C19.51 3.1 22 5.6 22 8.69c0 7-6.48 11.13-9.38 12.12Z"
fill="currentColor"
/>
</svg>
) : null
}
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Checkbox.Control>
<Checkbox.Indicator>
{({isSelected}) =>
isSelected ? (
<svg fill="currentColor" viewBox="0 0 24 24">
<path
d="M12.62 20.81c-.34.12-.9.12-1.24 0C8.48 19.82 2 15.69 2 8.69 2 5.6 4.49 3.1 7.56 3.1c1.82 0 3.43.88 4.44 2.24a5.53 5.53 0 0 1 4.44-2.24C19.51 3.1 22 5.6 22 8.69c0 7-6.48 11.13-9.38 12.12Z"
fill="currentColor"
/>
</svg>
) : null
}
</Checkbox.Indicator>
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
<Checkbox defaultSelected name="plus">
<Checkbox.Control>
<Checkbox.Indicator>
{({isSelected}) =>
isSelected ? (
<svg fill="none" viewBox="0 0 24 24">
<path
d="M6 12H18"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="3"
/>
<path
d="M12 18V6"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="3"
/>
</svg>
) : null
}
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Checkbox.Control>
<Checkbox.Indicator>
{({isSelected}) =>
isSelected ? (
<svg fill="none" viewBox="0 0 24 24">
<path
d="M6 12H18"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="3"
/>
<path
d="M12 18V6"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="3"
/>
</svg>
) : null
}
</Checkbox.Indicator>
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
<Checkbox isIndeterminate name="indeterminate">
<Checkbox.Control>
<Checkbox.Indicator>
{({isIndeterminate}) =>
isIndeterminate ? (
<svg stroke="currentColor" strokeWidth={3} viewBox="0 0 24 24">
<line x1="21" x2="3" y1="12" y2="12" />
</svg>
) : null
}
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Checkbox.Control>
<Checkbox.Indicator>
{({isIndeterminate}) =>
isIndeterminate ? (
<svg stroke="currentColor" strokeWidth={3} viewBox="0 0 24 24">
<line x1="21" x2="3" y1="12" y2="12" />
</svg>
) : null
}
</Checkbox.Indicator>
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
</div>
@@ -5,10 +5,12 @@ import {Checkbox, Label} from "@heroui/react";
export function CustomRenderFunction() {
return (
<div className="flex items-center gap-3">
<Checkbox id="basic-terms" render={(props) => <label {...props} data-custom="bar" />}>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox id="basic-terms" render={(props) => <div {...props} data-custom="bar" />}>
<Checkbox.Content>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
<Label htmlFor="basic-terms"></Label>
</div>
@@ -1,13 +1,13 @@
import {Checkbox, Label} from "@heroui/react";
import {Checkbox} from "@heroui/react";
export function CustomStyles() {
return (
<Checkbox id="custom">
<Checkbox.Control className="border-2 border-purple-500 data-[selected=true]:border-purple-500 data-[selected=true]:bg-purple-500">
<Checkbox.Indicator className="text-white" />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="custom"></Label>
<Checkbox.Control className="border-2 border-purple-500 data-[selected=true]:border-purple-500 data-[selected=true]:bg-purple-500">
<Checkbox.Indicator className="text-white" />
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
);
@@ -1,13 +1,13 @@
import {Checkbox, Label} from "@heroui/react";
import {Checkbox} from "@heroui/react";
export function DefaultSelected() {
return (
<Checkbox defaultSelected id="default-notifications">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="default-notifications"></Label>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
);
+6 -6
View File
@@ -1,15 +1,15 @@
import {Checkbox, Description, Label} from "@heroui/react";
import {Checkbox, Description} from "@heroui/react";
export function Disabled() {
return (
<Checkbox isDisabled id="feature">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="feature"></Label>
<Description></Description>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
<Description></Description>
</Checkbox>
);
}
@@ -0,0 +1,16 @@
import {Checkbox, Label} from "@heroui/react";
export function ExternalLabel() {
return (
<div className="flex items-center gap-3">
<Checkbox id="label-marketing">
<Checkbox.Content>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
<Label htmlFor="label-marketing"></Label>
</div>
);
}
+14 -20
View File
@@ -1,6 +1,6 @@
"use client";
import {Button, Checkbox, Label} from "@heroui/react";
import {Button, Checkbox} from "@heroui/react";
import React from "react";
export function Form() {
@@ -9,7 +9,7 @@ export function Form() {
const formData = new FormData(e.target as HTMLFormElement);
alert(
`表单提交,内容如下\n${Array.from(formData.entries())
`表单提交数据\n${Array.from(formData.entries())
.map(([key, value]) => `${key}: ${value}`)
.join("\n")}`,
);
@@ -18,36 +18,30 @@ export function Form() {
return (
<form className="flex flex-col gap-4" onSubmit={handleSubmit}>
<div className="flex flex-col gap-3">
<div className="flex items-center gap-3">
<Checkbox id="form-notifications" name="notifications" value="on">
<Checkbox name="notifications" value="on">
<Checkbox.Content>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox>
<Checkbox.Content>
<Label htmlFor="form-notifications"></Label>
</Checkbox.Content>
</div>
<div className="flex items-center gap-3">
<Checkbox defaultSelected id="form-newsletter" name="newsletter" value="on">
</Checkbox>
<Checkbox defaultSelected name="newsletter" value="on">
<Checkbox.Content>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox>
<Checkbox.Content>
<Label htmlFor="form-newsletter"></Label>
</Checkbox.Content>
</div>
<div className="flex items-center gap-3">
<Checkbox id="form-marketing" name="marketing" value="on">
</Checkbox>
<Checkbox name="marketing" value="on">
<Checkbox.Content>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox>
<Checkbox.Content>
<Label htmlFor="form-marketing"></Label>
</Checkbox.Content>
</div>
</Checkbox>
</div>
<Button className="mt-4" size="sm" type="submit" variant="primary">
@@ -9,31 +9,31 @@ export function FullRounded() {
className="[&_[data-slot='checkbox-default-indicator--checkmark']]:size-2"
name="small-rounded"
>
<Checkbox.Control className="size-3 rounded-full before:rounded-full">
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Checkbox.Control className="size-3 rounded-full before:rounded-full">
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
</div>
<div className="flex flex-col gap-3">
<Checkbox name="default-rounded">
<Checkbox.Control className="size-4 rounded-full before:rounded-full">
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Checkbox.Control className="size-4 rounded-full before:rounded-full">
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
</div>
<div className="flex flex-col gap-3">
<Checkbox name="large-rounded">
<Checkbox.Control className="size-5 rounded-full before:rounded-full">
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Checkbox.Control className="size-5 rounded-full before:rounded-full">
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
</div>
@@ -42,11 +42,11 @@ export function FullRounded() {
className="[&_[data-slot='checkbox-default-indicator--checkmark']]:size-4"
name="xl-rounded"
>
<Checkbox.Control className="size-6 rounded-full before:rounded-full">
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label></Label>
<Checkbox.Control className="size-6 rounded-full before:rounded-full">
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
</Checkbox>
</div>
@@ -1,6 +1,6 @@
"use client";
import {Checkbox, Description, Label} from "@heroui/react";
import {Checkbox, Description} from "@heroui/react";
import {useState} from "react";
export function Indeterminate() {
@@ -17,13 +17,13 @@ export function Indeterminate() {
setIsIndeterminate(false);
}}
>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="select-all"></Label>
<Description>线</Description>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
<Description>线</Description>
</Checkbox>
);
}
+1 -1
View File
@@ -12,4 +12,4 @@ export {Invalid} from "./invalid";
export {RenderProps} from "./render-props";
export {Variants} from "./variants";
export {WithDescription} from "./with-description";
export {WithLabel} from "./with-label";
export {ExternalLabel} from "./external-label";
+7 -7
View File
@@ -1,15 +1,15 @@
import {Checkbox, Description, Label} from "@heroui/react";
import {Checkbox, FieldError} from "@heroui/react";
export function Invalid() {
return (
<Checkbox isInvalid id="agreement" name="agreement">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox isInvalid isRequired name="agreement">
<Checkbox.Content>
<Label htmlFor="agreement"></Label>
<Description></Description>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
<FieldError></FieldError>
</Checkbox>
);
}
@@ -1,19 +1,19 @@
"use client";
import {Checkbox, Description, Label} from "@heroui/react";
import {Checkbox, Description} from "@heroui/react";
export function RenderProps() {
return (
<Checkbox id="render-props-terms">
{({isSelected}) => (
<>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="render-props-terms">{isSelected ? "已同意条款" : "接受条款"}</Label>
<Description>{isSelected ? "感谢您的确认" : "请先阅读并接受条款"}</Description>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
{isSelected ? "已同意条款" : "接受条款"}
</Checkbox.Content>
<Description>{isSelected ? "感谢您的确认" : "请先阅读并接受条款"}</Description>
</>
)}
</Checkbox>
+11 -11
View File
@@ -1,4 +1,4 @@
import {Checkbox, Description, Label} from "@heroui/react";
import {Checkbox, Description} from "@heroui/react";
export function Variants() {
return (
@@ -6,25 +6,25 @@ export function Variants() {
<div className="flex flex-col gap-2">
<p className="text-sm font-medium text-muted"></p>
<Checkbox id="primary" name="primary" variant="primary">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="primary"></Label>
<Description></Description>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
<Description></Description>
</Checkbox>
</div>
<div className="flex flex-col gap-2">
<p className="text-sm font-medium text-muted"></p>
<Checkbox id="secondary" name="secondary" variant="secondary">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="secondary"></Label>
<Description></Description>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
<Description></Description>
</Checkbox>
</div>
</div>
@@ -1,15 +1,15 @@
import {Checkbox, Description, Label} from "@heroui/react";
import {Checkbox, Description} from "@heroui/react";
export function WithDescription() {
return (
<Checkbox id="description-notifications">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox name="description-notifications">
<Checkbox.Content>
<Label htmlFor="description-notifications"></Label>
<Description>@你时收到通知</Description>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox.Content>
<Description></Description>
</Checkbox>
);
}
@@ -1,14 +0,0 @@
import {Checkbox, Label} from "@heroui/react";
export function WithLabel() {
return (
<Checkbox id="label-marketing">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="label-marketing"></Label>
</Checkbox.Content>
</Checkbox>
);
}
+1 -1
View File
@@ -4,7 +4,7 @@ import {Calendar, DateField, DatePicker, Label} from "@heroui/react";
export function Basic() {
return (
<DatePicker className="w-64" name="date">
<DatePicker className="w-72" name="date">
<Label></Label>
<DateField.Group fullWidth>
<DateField.Input>{(segment) => <DateField.Segment segment={segment} />}</DateField.Input>
@@ -10,7 +10,7 @@ export function Controlled() {
const [value, setValue] = useState<DateValue | null>(today(getLocalTimeZone()));
return (
<div className="flex w-64 flex-col gap-4">
<div className="flex w-72 flex-col gap-4">
<DatePicker name="date" value={value} onChange={setValue}>
<Label></Label>
<DateField.Group fullWidth>
@@ -5,7 +5,7 @@ import {Calendar, DateField, DatePicker, Label} from "@heroui/react";
export function CustomRenderFunction() {
return (
<DatePicker
className="w-64"
className="w-72"
name="date"
render={(props) => <div {...props} data-custom="date-picker" />}
>
@@ -5,7 +5,7 @@ import {getLocalTimeZone, today} from "@internationalized/date";
export function Disabled() {
return (
<DatePicker isDisabled className="w-64" name="date" value={today(getLocalTimeZone())}>
<DatePicker isDisabled className="w-72" name="date" value={today(getLocalTimeZone())}>
<Label></Label>
<DateField.Group fullWidth>
<DateField.Input>{(segment) => <DateField.Segment segment={segment} />}</DateField.Input>
@@ -37,7 +37,7 @@ export function FormExample() {
};
return (
<Form className="flex w-64 flex-col gap-3" onSubmit={handleSubmit}>
<Form className="flex w-72 flex-col gap-3" onSubmit={handleSubmit}>
<DatePicker
isRequired
isInvalid={isInvalid}
@@ -50,7 +50,7 @@ export function FormatOptions() {
<div className="flex flex-col gap-4">
<DatePicker
key={granularity}
className="w-fit min-w-64"
className="w-fit min-w-72"
defaultValue={defaultValue}
granularity={granularity}
hideTimeZone={hideTimeZone}
@@ -173,16 +173,20 @@ export function FormatOptions() {
<div className="flex min-w-80 flex-col gap-2">
<Switch isSelected={hideTimeZone} onChange={setHideTimeZone}>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label className="text-sm"></Label>
<Switch.Content>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
</Switch.Content>
</Switch>
<Switch isSelected={shouldForceLeadingZeros} onChange={setShouldForceLeadingZeros}>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label className="text-sm"></Label>
<Switch.Content>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
</Switch.Content>
</Switch>
</div>
</div>
@@ -8,7 +8,7 @@ export function InternationalCalendar() {
return (
<I18nProvider locale="hi-IN-u-ca-indian">
<DatePicker
className="w-64"
className="w-72"
defaultValue={today(getLocalTimeZone())}
name="international-date"
>
@@ -5,7 +5,7 @@ import {Icon} from "@iconify/react";
export function WithCustomIndicator() {
return (
<DatePicker className="w-64" name="date">
<DatePicker className="w-72" name="date">
<Label></Label>
<DateField.Group fullWidth>
<DateField.Input>{(segment) => <DateField.Segment segment={segment} />}</DateField.Input>
@@ -14,7 +14,7 @@ export function WithValidation() {
return (
<DatePicker
isRequired
className="w-64"
className="w-72"
isInvalid={isInvalid}
minValue={currentDate}
name="date"
@@ -4,7 +4,7 @@ import {DateField, DateRangePicker, Label, RangeCalendar} from "@heroui/react";
export function Basic() {
return (
<DateRangePicker className="w-72" endName="endDate" startName="startDate">
<DateRangePicker className="w-80" endName="endDate" startName="startDate">
<Label></Label>
<DateField.Group fullWidth>
<DateField.Input slot="start">
@@ -16,7 +16,7 @@ export function Controlled() {
const [value, setValue] = useState<DateRange | null>({end: start.add({days: 4}), start});
return (
<div className="flex w-72 flex-col gap-4">
<div className="flex w-80 flex-col gap-4">
<DateRangePicker endName="endDate" startName="startDate" value={value} onChange={setValue}>
<Label></Label>
<DateField.Group fullWidth>
@@ -5,7 +5,7 @@ import {DateField, DateRangePicker, Label, RangeCalendar} from "@heroui/react";
export function CustomRenderFunction() {
return (
<DateRangePicker
className="w-72"
className="w-80"
endName="endDate"
render={(props) => <div data-custom="foo" {...props} />}
startName="startDate"
@@ -9,7 +9,7 @@ export function Disabled() {
return (
<DateRangePicker
isDisabled
className="w-72"
className="w-80"
endName="endDate"
startName="startDate"
value={{end: start.add({days: 4}), start}}
@@ -41,7 +41,7 @@ export function FormExample() {
};
return (
<Form className="flex w-72 flex-col gap-3" onSubmit={handleSubmit}>
<Form className="flex w-80 flex-col gap-3" onSubmit={handleSubmit}>
<DateRangePicker
isRequired
endName="tripEndDate"
@@ -85,7 +85,7 @@ export function FormatOptions() {
<div className="flex w-full flex-col gap-4">
<DateRangePicker
key={granularity}
className="w-max min-w-72"
className="w-max min-w-80"
defaultValue={defaultValue}
endName="endDate"
granularity={granularity}
@@ -259,16 +259,20 @@ export function FormatOptions() {
<div className="flex min-w-[529px] flex-col gap-2">
<Switch isSelected={hideTimeZone} onChange={setHideTimeZone}>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label className="text-sm"></Label>
<Switch.Content>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
</Switch.Content>
</Switch>
<Switch isSelected={shouldForceLeadingZeros} onChange={setShouldForceLeadingZeros}>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label className="text-sm"></Label>
<Switch.Content>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
</Switch.Content>
</Switch>
</div>
</div>
@@ -16,7 +16,7 @@ export function InputContainer() {
return (
<DateRangePicker
shouldForceLeadingZeros
className="w-full max-w-2xs min-w-72"
className="w-full max-w-2xs min-w-80"
defaultValue={defaultValue}
granularity="second"
hourCycle={12}
@@ -10,7 +10,7 @@ export function InternationalCalendar() {
return (
<I18nProvider locale="hi-IN-u-ca-indian">
<DateRangePicker
className="w-72"
className="w-80"
defaultValue={{end: start.add({days: 7}), start}}
endName="endDate"
startName="startDate"
@@ -5,7 +5,7 @@ import {Icon} from "@iconify/react";
export function WithCustomIndicator() {
return (
<DateRangePicker className="w-72" endName="endDate" startName="startDate">
<DateRangePicker className="w-80" endName="endDate" startName="startDate">
<Label></Label>
<DateField.Group fullWidth>
<DateField.Input slot="start">
@@ -20,7 +20,7 @@ export function WithValidation() {
return (
<DateRangePicker
isRequired
className="w-72"
className="w-80"
endName="endDate"
isInvalid={isInvalid}
minValue={currentDate}
+3 -3
View File
@@ -560,9 +560,9 @@ export const demos: Record<string, DemoItem> = {
component: CheckboxDemos.Indeterminate,
file: "cn/checkbox/indeterminate.tsx",
},
"checkbox-with-label": {
component: CheckboxDemos.WithLabel,
file: "cn/checkbox/with-label.tsx",
"checkbox-external-label": {
component: CheckboxDemos.ExternalLabel,
file: "cn/checkbox/external-label.tsx",
},
"checkbox-with-description": {
component: CheckboxDemos.WithDescription,
@@ -1,6 +1,6 @@
"use client";
import {Button, Label, Modal, Radio, RadioGroup} from "@heroui/react";
import {Button, Modal, Radio, RadioGroup} from "@heroui/react";
import {useState} from "react";
export function ScrollComparison() {
@@ -14,16 +14,20 @@ export function ScrollComparison() {
onChange={(value) => setScroll(value as "inside" | "outside")}
>
<Radio value="inside">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Label></Label>
<Radio.Content>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
</Radio>
<Radio value="outside">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Label></Label>
<Radio.Content>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
</Radio>
</RadioGroup>
+15 -15
View File
@@ -6,31 +6,31 @@ export function Basic() {
<Label></Label>
<Description></Description>
<Radio value="basic">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description> 100 </Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
<Description> 100 </Description>
</Radio>
<Radio value="premium">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description> 200 </Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
<Description> 200 </Description>
</Radio>
<Radio value="business">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description></Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
<Description></Description>
</Radio>
</RadioGroup>
);
@@ -11,31 +11,31 @@ export function Controlled() {
<RadioGroup name="plan-controlled" value={value} onChange={setValue}>
<Label></Label>
<Radio value="starter">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description></Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
<Description></Description>
</Radio>
<Radio value="pro">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description></Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
<Description></Description>
</Radio>
<Radio value="teams">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description> 10 访</Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
<Description> 10 访</Description>
</Radio>
</RadioGroup>
<p className="text-sm text-muted">
@@ -8,43 +8,43 @@ export function CustomIndicator() {
<Label></Label>
<Description></Description>
<Radio value="basic">
<Radio.Control>
<Radio.Indicator>
{({isSelected}) =>
isSelected ? <span className="text-xs leading-none text-background"></span> : null
}
</Radio.Indicator>
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description> 100 </Description>
<Radio.Control>
<Radio.Indicator>
{({isSelected}) =>
isSelected ? <span className="text-xs leading-none text-background"></span> : null
}
</Radio.Indicator>
</Radio.Control>
</Radio.Content>
<Description> 100 </Description>
</Radio>
<Radio value="premium">
<Radio.Control>
<Radio.Indicator>
{({isSelected}) =>
isSelected ? <span className="text-xs leading-none text-background"></span> : null
}
</Radio.Indicator>
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description> 200 </Description>
<Radio.Control>
<Radio.Indicator>
{({isSelected}) =>
isSelected ? <span className="text-xs leading-none text-background"></span> : null
}
</Radio.Indicator>
</Radio.Control>
</Radio.Content>
<Description> 200 </Description>
</Radio>
<Radio value="business">
<Radio.Control>
<Radio.Indicator>
{({isSelected}) =>
isSelected ? <span className="text-xs leading-none text-background"></span> : null
}
</Radio.Indicator>
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description></Description>
<Radio.Control>
<Radio.Indicator>
{({isSelected}) =>
isSelected ? <span className="text-xs leading-none text-background"></span> : null
}
</Radio.Indicator>
</Radio.Control>
</Radio.Content>
<Description></Description>
</Radio>
</RadioGroup>
);
@@ -6,37 +6,37 @@ export function CustomRenderFunction() {
return (
<RadioGroup
defaultValue="premium"
name="plan"
name="plan-custom-render"
render={(props) => <div {...props} data-custom="foo" />}
>
<Label></Label>
<Description></Description>
<Radio value="basic">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description> 100 </Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
<Description> 100 </Description>
</Radio>
<Radio value="premium">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description> 200 </Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
<Description> 200 </Description>
</Radio>
<Radio value="business">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description></Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
<Description></Description>
</Radio>
</RadioGroup>
);
@@ -63,20 +63,18 @@ export function DeliveryAndPayment() {
<Label></Label>
<div className="grid gap-x-4 md:grid-cols-3">
{deliveryOptions.map((option) => (
<Radio
key={option.value}
value={option.value}
className={clsx(
"group relative flex-col gap-4 rounded-xl border border-transparent bg-surface px-5 py-4 transition-all data-[selected=true]:border-accent data-[selected=true]:bg-accent/10",
"data-[focus-visible=true]:border-accent data-[focus-visible=true]:bg-accent/10",
)}
>
<Radio.Control className="absolute top-3 right-4 size-5">
<Radio.Indicator />
</Radio.Control>
<Radio.Content className="flex flex-col gap-6">
<Radio key={option.value} value={option.value}>
<Radio.Content
className={clsx(
"group relative flex w-full flex-col gap-6 rounded-xl border border-transparent bg-surface px-5 py-4 transition-all data-[selected=true]:border-accent data-[selected=true]:bg-accent/10",
"data-[focus-visible=true]:border-accent data-[focus-visible=true]:bg-accent/10",
)}
>
<Radio.Control className="absolute top-3 right-4 size-5">
<Radio.Indicator />
</Radio.Control>
<div className="flex flex-col gap-1">
<Label>{option.title}</Label>
<span>{option.title}</span>
<Description>{option.description}</Description>
</div>
<span className="text-sm font-semibold">{option.price}</span>
@@ -93,21 +91,19 @@ export function DeliveryAndPayment() {
</div>
<div className="grid gap-x-4 md:grid-cols-2">
{paymentOptions.map((option) => (
<Radio
key={option.value}
value={option.value}
className={clsx(
"group relative flex-col gap-4 rounded-xl border border-transparent bg-surface px-5 py-4 transition-all",
"data-[selected=true]:border-accent data-[selected=true]:bg-accent/10",
)}
>
<Radio.Control className="absolute top-3 right-4 size-5">
<Radio.Indicator />
</Radio.Control>
<Radio.Content className="flex flex-row items-start justify-start gap-4">
<Radio key={option.value} value={option.value}>
<Radio.Content
className={clsx(
"group relative flex w-full flex-row items-start justify-start gap-4 rounded-xl border border-transparent bg-surface px-5 py-4 transition-all",
"data-[selected=true]:border-accent data-[selected=true]:bg-accent/10",
)}
>
<Radio.Control className="absolute top-3 right-4 size-5">
<Radio.Indicator />
</Radio.Control>
<Icon className="size-6" icon={option.icon} />
<div className="flex flex-col gap-1">
<Label>{option.title}</Label>
<span>{option.title}</span>
<Description>{option.description}</Description>
</div>
</Radio.Content>
+15 -15
View File
@@ -6,31 +6,31 @@ export function Disabled() {
<Label></Label>
<Description></Description>
<Radio value="starter">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description></Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
<Description></Description>
</Radio>
<Radio value="pro">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description></Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
<Description></Description>
</Radio>
<Radio value="teams">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description> 10 访</Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
<Description> 10 访</Description>
</Radio>
</RadioGroup>
);
@@ -6,31 +6,31 @@ export function Horizontal() {
<Label></Label>
<RadioGroup defaultValue="pro" name="plan-orientation" orientation="horizontal">
<Radio value="starter">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description></Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
<Description></Description>
</Radio>
<Radio value="pro">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description></Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
<Description></Description>
</Radio>
<Radio value="teams">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description> 10 </Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
<Description> 10 </Description>
</Radio>
</RadioGroup>
</div>
@@ -3,35 +3,35 @@ import {Description, Label, Radio, RadioGroup, Surface} from "@heroui/react";
export function OnSurface() {
return (
<Surface className="w-full rounded-3xl p-6">
<RadioGroup defaultValue="premium" name="plan" variant="secondary">
<RadioGroup defaultValue="premium" name="plan-on-surface" variant="secondary">
<Label></Label>
<Description></Description>
<Radio value="basic">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description> 100 </Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
<Description> 100 </Description>
</Radio>
<Radio value="premium">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description> 200 </Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
<Description> 200 </Description>
</Radio>
<Radio value="business">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description></Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
<Description></Description>
</Radio>
</RadioGroup>
</Surface>
@@ -15,31 +15,31 @@ export function Uncontrolled() {
>
<Label></Label>
<Radio value="starter">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description></Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
<Description></Description>
</Radio>
<Radio value="pro">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description></Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
<Description></Description>
</Radio>
<Radio value="teams">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description> 10 访</Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
<Description> 10 访</Description>
</Radio>
</RadioGroup>
<p className="text-sm text-muted">
@@ -21,31 +21,31 @@ export function Validation() {
<RadioGroup isRequired name="plan-validation">
<Label></Label>
<Radio value="starter">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description></Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
<Description></Description>
</Radio>
<Radio value="pro">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description></Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
<Description></Description>
</Radio>
<Radio value="teams">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label></Label>
<Description> 10 访</Description>
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
</Radio.Content>
<Description> 10 访</Description>
</Radio>
<FieldError></FieldError>
</RadioGroup>

Some files were not shown because too many files have changed in this diff Show More