RadioGroup
Một lựa chọn trong số vài phương án mà người dùng so sánh cạnh nhau. Mỗi phương án là vòng tròn 16px, label đứng sau và description tùy chọn nằm dưới label; phương án đã chọn tô bằng accent và hiện một chấm on-accent.
Xem trước
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
<div className="flex flex-col gap-sm">
<span id="preview-plan-heading" className="text-sm font-medium text-text-primary">Gói</span>
<RadioGroup aria-labelledby="preview-plan-heading" defaultValue="pro">
<RadioGroupItem value="free" label="Miễn phí" description="Tối đa 3 dự án." />
<RadioGroupItem value="pro" label="Pro" description="Không giới hạn dự án và lịch sử phiên bản." />
<RadioGroupItem value="team" label="Nhóm" description="Pro, thêm thư viện dùng chung và vai trò." />
</RadioGroup>
</div>Cách dùng
Khi nào dùng
- Chọn đúng một trong hai đến năm phương án mà người dùng cần thấy và so sánh cùng lúc, như một gói.
- Phương án cần một dòng thứ hai để nói chúng nghĩa là gì.
Khi nào không dùng
- Hơn năm phương án, hoặc phương án người dùng đã biết, như một quốc gia. Dùng Select.
- Bật hoặc tắt một cài đặt. Dùng Switch.
- Chọn nhiều phương án. Dùng Checkbox.
Biến thể
orientation
Đặt trên RadioGroup. vertical cho phương án có description hoặc label dài; horizontal cho hai hoặc ba phương án ngắn. Phím mũi tên đi theo hướng này.
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
<div className="flex flex-col gap-sm">
<span id="preview-plan-heading-orientation-vertical" className="text-sm font-medium text-text-primary">Gói</span>
<RadioGroup aria-labelledby="preview-plan-heading-orientation-vertical" defaultValue="pro">
<RadioGroupItem value="free" label="Miễn phí" description="Tối đa 3 dự án." />
<RadioGroupItem value="pro" label="Pro" description="Không giới hạn dự án và lịch sử phiên bản." />
<RadioGroupItem value="team" label="Nhóm" description="Pro, thêm thư viện dùng chung và vai trò." />
</RadioGroup>
</div>
<div className="flex flex-col gap-sm">
<span id="preview-plan-heading-orientation-horizontal" className="text-sm font-medium text-text-primary">Gói</span>
<RadioGroup aria-labelledby="preview-plan-heading-orientation-horizontal" defaultValue="pro" orientation="horizontal">
<RadioGroupItem value="free" label="Miễn phí" description="Tối đa 3 dự án." />
<RadioGroupItem value="pro" label="Pro" description="Không giới hạn dự án và lịch sử phiên bản." />
<RadioGroupItem value="team" label="Nhóm" description="Pro, thêm thư viện dùng chung và vai trò." />
</RadioGroup>
</div>variant
Đặt trên RadioGroup. default là vòng tròn thường kèm label. card vẽ mỗi lựa chọn thành một thẻ bao label và mô tả, cho vài lựa chọn cần một câu giải thích, chẳng hạn các gói. Thẻ được chọn nổi lên và viền dày hơn.
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
<div className="flex flex-col gap-sm">
<span id="preview-plan-heading-variant-default" className="text-sm font-medium text-text-primary">Gói</span>
<RadioGroup aria-labelledby="preview-plan-heading-variant-default" defaultValue="pro">
<RadioGroupItem value="free" label="Miễn phí" description="Tối đa 3 dự án." />
<RadioGroupItem value="pro" label="Pro" description="Không giới hạn dự án và lịch sử phiên bản." />
<RadioGroupItem value="team" label="Nhóm" description="Pro, thêm thư viện dùng chung và vai trò." />
</RadioGroup>
</div>
<div className="flex flex-col gap-sm">
<span id="preview-plan-heading-variant-card" className="text-sm font-medium text-text-primary">Gói</span>
<RadioGroup aria-labelledby="preview-plan-heading-variant-card" defaultValue="pro">
<RadioGroupItem value="free" label="Miễn phí" description="Tối đa 3 dự án." />
<RadioGroupItem value="pro" label="Pro" description="Không giới hạn dự án và lịch sử phiên bản." />
<RadioGroupItem value="team" label="Nhóm" description="Pro, thêm thư viện dùng chung và vai trò." />
</RadioGroup>
</div>Ví dụ
Mặc định
Một tiêu đề nhìn thấy đặt tên nhóm qua aria-labelledby.
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
<div className="flex flex-col gap-sm">
<span id="plan-heading" className="text-sm font-medium text-text-primary">Gói</span>
<RadioGroup aria-labelledby="plan-heading" defaultValue="pro">
<RadioGroupItem value="free" label="Miễn phí" description="Tối đa 3 dự án." />
<RadioGroupItem value="pro" label="Pro" description="Không giới hạn dự án và lịch sử phiên bản." />
<RadioGroupItem value="team" label="Nhóm" description="Pro, thêm thư viện dùng chung và vai trò." />
</RadioGroup>
</div>Phương án ngắn trên một hàng
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
<RadioGroup aria-label="Chủ đề" orientation="horizontal" defaultValue="system">
<RadioGroupItem value="system" label="Hệ thống" />
<RadioGroupItem value="light" label="Sáng" />
<RadioGroupItem value="dark" label="Tối" />
</RadioGroup>Một phương án disabled
Phím mũi tên bỏ qua phương án đó.
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
<RadioGroup aria-label="Khu vực" defaultValue="eu">
<RadioGroupItem value="eu" label="Châu Âu" />
<RadioGroupItem value="us" label="Hoa Kỳ" />
<RadioGroupItem value="ap" label="Châu Á Thái Bình Dương" description="Sẽ có vào cuối năm nay." disabled />
</RadioGroup>Dạng thẻ
Mỗi gói cần một câu giải thích, nên mỗi lựa chọn là một thẻ.
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
<RadioGroup aria-label="Gói" defaultValue="pro" variant="card" className="w-full max-w-sm">
<RadioGroupItem value="free" label="Miễn phí" description="Three projects, community support." />
<RadioGroupItem value="pro" label="Pro" description="Unlimited projects and priority support." />
</RadioGroup>Nên và không nên
Nên
- Dùng cho hai đến năm phương án mà người dùng cần thấy và so sánh cùng lúc.
- Đặt một tiêu đề nhìn thấy phía trên nhóm và trỏ aria-labelledby vào tiêu đề đó.
- Chọn một mặc định khi có một phương án là lựa chọn thường dùng.
- Thêm description khi riêng label không nói phương án nghĩa là gì.
Không nên
- Đừng dùng cho hơn năm phương án; dùng Select.
- Đừng dùng để bật hoặc tắt một cài đặt; dùng Switch.
- Đừng dùng để chọn nhiều phương án; dùng Checkbox.
Props
| Tên | Kiểu | Giá trị | Mặc định | Mô tả |
|---|---|---|---|---|
| orientation | enum | vertical | horizontal | vertical | Đặt trên RadioGroup. vertical cho phương án có description hoặc label dài; horizontal cho hai hoặc ba phương án ngắn. Phím mũi tên đi theo hướng này. |
| variant | enum | default | card | default | Đặt trên RadioGroup. default là vòng tròn thường kèm label. card vẽ mỗi lựa chọn thành một thẻ bao label và mô tả, cho vài lựa chọn cần một câu giải thích, chẳng hạn các gói. Thẻ được chọn nổi lên và viền dày hơn. |
| defaultValue | string | - | - | Đặt trên RadioGroup. Giá trị được chọn ở lần render đầu, khi bạn không kiểm soát nó. Chọn một mặc định hợp lý khi đã có. |
| value | string | - | - | Đặt trên RadioGroup. Giá trị đã chọn, khi bạn kiểm soát nó. Đi cùng onValueChange. |
| onValueChange | function | - | - | Đặt trên RadioGroup. Được gọi với value của phương án vừa chọn. |
| disabled | boolean | - | false | Trên RadioGroup thì tắt mọi phương án và làm mờ label; trên một RadioGroupItem thì chỉ phương án đó. |
| required | boolean | - | false | Đặt trên RadioGroup. Phải chọn một phương án thì mới gửi được form. |
| name | string | - | - | Đặt trên RadioGroup. Tên trong form; form gửi giá trị đã chọn dưới tên này. |
| label | node | - | - | Đặt trên RadioGroupItem. Tên nhìn thấy, sau vòng tròn. Nhấn vào tên thì chọn phương án. |
| description | node | - | - | Đặt trên RadioGroupItem. Dòng thứ hai dưới label: phương án này nghĩa là gì. Cần label. |
Slot
| Tên | Bắt buộc | Mô tả |
|---|---|---|
| items | có | Hai đến năm RadioGroupItem, mỗi item có value và label. |
Cấu tạo
| Phần | Mô tả |
|---|---|
| root | RadioGroup: các phương án trong một cột (gap-md), hoặc trong một hàng xuống dòng (gap-x-lg, gap-y-md). |
| item | RadioGroupItem: vòng tròn 16px, viền nhạt trên nền nổi với bóng nhẹ. Khi được chọn, accent tô kín tới mép. |
| indicator | Chấm trong vòng tròn đã chọn: 6px, màu icon on-accent (::after của indicator ở currentColor). |
| hitArea | ::before trong suốt, căn giữa vòng tròn, không nhỏ hơn size-target-min (24px với chuột, 44px với cảm ứng). |
| row | Khi có label: hàng giữ vòng tròn, rồi Label và description, gap-sm. Dùng chung với Checkbox và Switch. |
| label | Khi có label: một Label sau vòng tròn, thẳng hàng với vòng tròn ở dòng đầu. |
| description | Khi có description: chữ secondary 14px dưới label, gap-xs. |
| tile | Với variant card: thẻ bao quanh hàng, radius-element, padding spacing-md và viền ring-border. Nhấn vào bất kỳ đâu trên thẻ để chọn. Khi được chọn: nền nổi, shadow-low và vòng 2px accent. |
Khả năng truy cập
Role: radiogroup
| Phím | Hành động |
|---|---|
| Tab | Đưa focus vào nhóm, tới phương án đã chọn (hoặc phương án đầu), rồi đưa focus ra khỏi nhóm. Cả nhóm là một tab stop. |
| Arrow keys | Chuyển tới phương án kế hoặc phương án trước và chọn nó, quay từ phương án cuối về phương án đầu. |
| Space | Chọn phương án đang focus khi chưa chọn phương án nào. |
- Đặt tên nhóm: aria-labelledby với id của tiêu đề nhìn thấy phía trên, hoặc aria-label. Type bắt buộc một trong hai.
- Đặt tên mỗi phương án bằng prop label; nhấn vào label thì chọn phương án. description liên kết bằng aria-describedby và được đọc sau tên.
- Nhóm disabled làm mờ mọi label; phím mũi tên bỏ qua phương án disabled.
- Trong form, một input ẩn mang name và giá trị đã chọn.
- Phương án đã chọn khác các phương án còn lại ở cả chấm lẫn phần tô, nên lựa chọn không bao giờ chỉ dựa vào màu.
Cài đặt
npx werewolf-ui add radio-groupChạy npx werewolf-ui init một lần trong app trước khi thêm component. Init cài token, thêm CSS import và luật cho agent. Add cài component này cùng các dependency và giữ nguyên theme của bạn. Xem Cài đặt để biết yêu cầu và cách cài qua registry.