如何基于Radix UI Radio Groups实现Hyper UI风格选中样式?
Radix Radio Groups 改 Hyper UI 堆叠网格单选样式的适配方案
核心问题:你当前的结构大概率是把文本塞进了单选控件内部,导致未选中时不显示;另外没把选中状态绑定到外层容器,所以边框样式不生效。直接给你改好的代码和关键调整点:
改造后的完整代码
"use client"; import { useState } from "react"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { Check } from "lucide-react"; type PlanOption = { value: string; title: string; desc: string; price: string; }; export default function StackedGridRadio() { const [selected, setSelected] = useState("basic"); const options: PlanOption[] = [ { value: "basic", title: "基础版", desc: "个人用户首选", price: "¥99/月" }, { value: "pro", title: "专业版", desc: "小型团队协作", price: "¥199/月" }, { value: "enterprise", title: "企业版", desc: "大型组织定制", price: "¥499/月" }, ]; return ( <RadioGroup defaultValue="basic" onValueChange={setSelected} className="grid grid-cols-1 md:grid-cols-3 gap-4" > {options.map(opt => ( <div key={opt.value} className={`relative rounded-lg border-2 p-4 transition-all cursor-pointer ${selected === opt.value ? 'border-blue-600 bg-blue-50' : 'border-gray-200 hover:border-gray-300'}`} > {/* 隐藏原生Radio控件,只保留交互逻辑 */} <RadioGroupItem value={opt.value} id={`opt-${opt.value}`} className="absolute opacity-0" /> {/* 选中状态的对勾图标 */} <Check className={`absolute top-4 right-4 h-5 w-5 text-blue-600 ${selected === opt.value ? 'opacity-100' : 'opacity-0'}`} /> {/* 文本内容,绑定label确保点击选中 */} <label htmlFor={`opt-${opt.value}`} className="block"> <h3 className="text-lg font-semibold">{opt.title}</h3> <p className="text-sm text-gray-500 mt-1">{opt.desc}</p> <p className="text-xl font-bold mt-2">{opt.price}</p> </label> </div> ))} </RadioGroup> ); }
关键调整说明
- 结构拆分:把每个选项做成「外层容器 + 隐藏的Radio控件 + 文本 + 选中图标」,文本不再嵌套在Radio内部,确保始终显示。
- 状态绑定:用
useState绑定RadioGroup的选中值,直接给外层容器加选中样式(边框、背景色),比依赖Radix的data-state更直观,也避免DOM查询的麻烦。 - 交互优化:外层容器加
cursor-pointer,label绑定Radio的id,点击文本或容器任何区域都能触发选中;hover时改变边框颜色,提升反馈。 - 样式替换:把选中状态的边框换成Hyper UI常用的蓝色系,背景加浅蓝高亮,和原设计对齐。
内容的提问来源于stack exchange,提问作者dot321
相关产品推荐
相关产品推荐

