Button Radio Group
Installation
Add the following Soul components
The button-radio-group component uses the field-error and label components. Make sure you have added them to your project.
Install the following dependencies
npm install clsx lucide-react
Copy and paste the following code into your project
form/button-radio-group/index.tsx
import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';import { clsx } from 'clsx';import * as React from 'react';import { FieldError } from '@/vibes/soul/form/field-error';import { Label } from '@/vibes/soul/form/label';interface Option { value: string; label: string; disabled?: boolean;}/** * This component supports various CSS variables for theming. Here's a comprehensive list, along * with their default values: * * ```css * :root { * --button-radio-group-focus: hsl(var(--primary)); * --button-radio-group-light-unchecked-border: hsl(var(--contrast-100)); * --button-radio-group-light-unchecked-background: hsl(var(--background)); * --button-radio-group-light-unchecked-text: hsl(var(--foreground)); * --button-radio-group-light-unchecked-border-hover: hsl(var(--contrast-200)); * --button-radio-group-light-unchecked-background-hover: hsl(var(--contrast-100)); * --button-radio-group-light-checked-background: hsl(var(--foreground)); * --button-radio-group-light-checked-text: hsl(var(--background)); * --button-radio-group-light-border-error: hsl(var(--error)); * --button-radio-group-dark-unchecked-border: hsl(var(--contrast-500)); * --button-radio-group-dark-unchecked-background: hsl(var(--background)); * --button-radio-group-dark-unchecked-text: hsl(var(--background)); * --button-radio-group-dark-unchecked-border-hover: hsl(var(--contrast-400)); * --button-radio-group-dark-unchecked-background-hover: hsl(var(--contrast-500)); * --button-radio-group-dark-checked-background: hsl(var(--background)); * --button-radio-group-dark-checked-text: hsl(var(--foreground)); * --button-radio-group-dark-border-error: hsl(var(--error)); * } * ``` */export const ButtonRadioGroup = React.forwardRef< React.ComponentRef<typeof RadioGroupPrimitive.Root>, React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root> & { label?: string; options: Option[]; errors?: string[]; onOptionMouseEnter?: (value: string) => void; colorScheme?: 'light' | 'dark'; }>( ( { label, options, errors, className, onOptionMouseEnter, colorScheme = 'light', ...rest }, ref, ) => { const id = React.useId(); return ( <div className={clsx('button-radio-group space-y-2', className)}> {label !== undefined && label !== '' && ( <Label colorScheme={colorScheme} id={id}> {label} </Label> )} <RadioGroupPrimitive.Root {...rest} aria-labelledby={id} className="flex flex-wrap gap-2" ref={ref} > {options.map((option) => ( <RadioGroupPrimitive.Item aria-label={option.label} className={clsx( 'h-12 whitespace-nowrap rounded-full border px-4 font-body text-sm font-normal leading-normal transition-colors focus-visible:outline-0 focus-visible:ring-2 data-[disabled]:pointer-events-none data-[disabled]:opacity-50', { light: 'border-[var(--button-radio-group-light-unchecked-border,hsl(var(--contrast-100)))] focus-visible:ring-[var(--button-radio-group-light-focus,hsl(var(--primary)))] data-[state=checked]:bg-[var(--button-radio-group-light-checked-background,hsl(var(--foreground)))] data-[state=unchecked]:bg-[var(--button-radio-group-light-unchecked-background,hsl(var(--background)))] data-[state=checked]:text-[var(--button-radio-group-light-checked-text,hsl(var(--background)))] data-[state=unchecked]:text-[var(--button-radio-group-light-unchecked-text,hsl(var(--foreground)))] data-[state=unchecked]:hover:border-[var(--button-radio-group-light-unchecked-border-hover,hsl(var(--contrast-200)))] data-[state=unchecked]:hover:bg-[var(--button-radio-group-light-unchecked-background-hover,hsl(var(--contrast-100)))]', dark: 'border-[var(--button-radio-group-dark-unchecked-border,hsl(var(--contrast-500)))] focus-visible:ring-[var(--button-radio-group-dark-focus,hsl(var(--primary)))] data-[state=checked]:bg-[var(--button-radio-group-dark-checked-background,hsl(var(--background)))] data-[state=unchecked]:bg-[var(--button-radio-group-dark-unchecked-background,hsl(var(--foreground)))] data-[state=checked]:text-[var(--button-radio-group-dark-checked-text,hsl(var(--foreground)))] data-[state=unchecked]:text-[var(--button-radio-group-dark-checked-text,hsl(var(--background)))] data-[state=unchecked]:hover:border-[var(--button-radio-group-dark-unchecked-border-hover,hsl(var(--contrast-400)))] data-[state=unchecked]:hover:bg-[var(--button-radio-group-dark-unchecked-background-hover,hsl(var(--contrast-500)))]', }[colorScheme], { light: errors && errors.length > 0 ? 'data-[state=unchecked]:border-[var(--button-radio-group-light-border-error,hsl(var(--error)))]' : 'data-[state=checked]:border-[var(--button-radio-group-light-checked-background,hsl(var(--foreground)))]', dark: errors && errors.length > 0 ? 'data-[state=unchecked]:border-[var(--button-radio-group-dark-border-error,hsl(var(--error)))]' : 'data-[state=checked]:border-[var(--button-radio-group-dark-checked-background,hsl(var(--foreground)))]', }[colorScheme], )} disabled={option.disabled} id={option.value} key={option.value} onMouseEnter={() => { onOptionMouseEnter?.(option.value); }} value={option.value} > {option.label} </RadioGroupPrimitive.Item> ))} </RadioGroupPrimitive.Root> {errors?.map((error) => <FieldError key={error}>{error}</FieldError>)} </div> ); },);ButtonRadioGroup.displayName = 'ButtonRadioGroup';
Usage
'use client';import { ButtonRadioGroup } from '@/vibes/soul/form/button-radio-group';function Usage() { return ( <ButtonRadioGroup options={[ { label: 'Option 1', value: 'option-1' }, { label: 'Option 2', value: 'option-2' }, { label: 'Option 3', value: 'option-3' }, ]} /> );}
API Reference
This component uses the RadioGroup component from Radix UI. Refer to the RadioGroup documentation for more information.
ButtonRadioGroupProps
Prop | Type | Default |
---|---|---|
className | string | |
label | string | |
options* | Option[] | |
errors | string[] | |
onOptionMouseEnter | (value: string) => void | |
colorScheme | 'light' | 'dark' | 'light' |
Option
Prop | Type | Default |
---|---|---|
value* | string | |
label* | string | |
disabled | boolean |
CSS Variables
This component supports various CSS variables for theming. Here's a comprehensive list.
:root { --button-radio-group-focus: hsl(var(--primary)); --button-radio-group-light-unchecked-border: hsl(var(--contrast-100)); --button-radio-group-light-unchecked-background: hsl(var(--background)); --button-radio-group-light-unchecked-text: hsl(var(--foreground)); --button-radio-group-light-unchecked-border-hover: hsl(var(--contrast-200)); --button-radio-group-light-unchecked-background-hover: hsl(var(--contrast-100)); --button-radio-group-light-checked-background: hsl(var(--foreground)); --button-radio-group-light-checked-text: hsl(var(--background)); --button-radio-group-light-border-error: hsl(var(--error)); --button-radio-group-dark-unchecked-border: hsl(var(--contrast-500)); --button-radio-group-dark-unchecked-background: hsl(var(--background)); --button-radio-group-dark-unchecked-text: hsl(var(--background)); --button-radio-group-dark-unchecked-border-hover: hsl(var(--contrast-400)); --button-radio-group-dark-unchecked-background-hover: hsl(var(--contrast-500)); --button-radio-group-dark-checked-background: hsl(var(--background)); --button-radio-group-dark-checked-text: hsl(var(--foreground)); --button-radio-group-dark-border-error: hsl(var(--error));}