Radio Button Group
Allows single selection from multiple options.
solid
outline
import { RadioButtonGroup } from '~/components/ui/radio-button-group'
export const Demo = (props: RadioButtonGroup.RootProps) => {
  const options = [{ value: 'S' }, { value: 'M' }, { value: 'L' }, { value: 'XL' }]
  return (
    <RadioButtonGroup.Root defaultValue="M" {...props}>
      {options.map((option, id) => (
        <RadioButtonGroup.Item key={id} value={option.value} disabled={option.value === 'L'} px="0">
          <RadioButtonGroup.ItemControl />
          <RadioButtonGroup.ItemText>{option.value}</RadioButtonGroup.ItemText>
          <RadioButtonGroup.ItemHiddenInput />
        </RadioButtonGroup.Item>
      ))}
    </RadioButtonGroup.Root>
  )
}
import { For } from 'solid-js'
import { RadioButtonGroup } from '~/components/ui/radio-button-group'
export const Demo = (props: RadioButtonGroup.RootProps) => {
  const options = [{ value: 'S' }, { value: 'M' }, { value: 'L' }, { value: 'XL' }]
  return (
    <RadioButtonGroup.Root defaultValue="M" {...props}>
      <For each={options}>
        {(option) => (
          <RadioButtonGroup.Item value={option.value} disabled={option.value === 'L'} px="0">
            <RadioButtonGroup.ItemControl />
            <RadioButtonGroup.ItemText>{option.value}</RadioButtonGroup.ItemText>
            <RadioButtonGroup.ItemHiddenInput />
          </RadioButtonGroup.Item>
        )}
      </For>
    </RadioButtonGroup.Root>
  )
}
Usage
import { RadioButtonGroup } from '~/components/ui/radio-button-group'Installation
npx @park-ui/cli components add radio-button-group1
Add Styled Primitive
Copy the code snippet below into ~/components/ui/styled/radio-button-group.tsx
'use client'
import type { Assign } from '@ark-ui/react'
import { RadioGroup } from '@ark-ui/react/radio-group'
import { type RadioButtonGroupVariantProps, radioButtonGroup } from 'styled-system/recipes'
import type { HTMLStyledProps } from 'styled-system/types'
import { createStyleContext } from './utils/create-style-context'
const { withProvider, withContext } = createStyleContext(radioButtonGroup)
export interface RootProps
  extends Assign<HTMLStyledProps<'div'>, RadioGroup.RootProps>,
    RadioButtonGroupVariantProps {}
export const Root = withProvider<HTMLDivElement, RootProps>(RadioGroup.Root, 'root')
export const Indicator = withContext<
  HTMLDivElement,
  Assign<HTMLStyledProps<'div'>, RadioGroup.IndicatorProps>
>(RadioGroup.Indicator, 'indicator')
export const ItemControl = withContext<
  HTMLDivElement,
  Assign<HTMLStyledProps<'div'>, RadioGroup.ItemControlProps>
>(RadioGroup.ItemControl, 'itemControl')
export const Item = withContext<
  HTMLLabelElement,
  Assign<HTMLStyledProps<'label'>, RadioGroup.ItemProps>
>(RadioGroup.Item, 'item')
export const ItemText = withContext<
  HTMLSpanElement,
  Assign<HTMLStyledProps<'span'>, RadioGroup.ItemTextProps>
>(RadioGroup.ItemText, 'itemText')
export const Label = withContext<
  HTMLLabelElement,
  Assign<HTMLStyledProps<'label'>, RadioGroup.LabelProps>
>(RadioGroup.Label, 'label')
export {
  RadioGroupContext as Context,
  RadioGroupItemHiddenInput as ItemHiddenInput,
} from '@ark-ui/react/radio-group'
import { type Assign, RadioGroup } from '@ark-ui/solid'
import type { ComponentProps } from 'solid-js'
import { type RadioButtonGroupVariantProps, radioButtonGroup } from 'styled-system/recipes'
import type { HTMLStyledProps } from 'styled-system/types'
import { createStyleContext } from './utils/create-style-context'
const { withProvider, withContext } = createStyleContext(radioButtonGroup)
export type RootProps = ComponentProps<typeof Root>
export const Root = withProvider<
  Assign<Assign<HTMLStyledProps<'div'>, RadioGroup.RootProps>, RadioButtonGroupVariantProps>
>(RadioGroup.Root, 'root')
export const Indicator = withContext<Assign<HTMLStyledProps<'div'>, RadioGroup.IndicatorProps>>(
  RadioGroup.Indicator,
  'indicator',
)
export const ItemControl = withContext<Assign<HTMLStyledProps<'div'>, RadioGroup.ItemControlProps>>(
  RadioGroup.ItemControl,
  'itemControl',
)
export const Item = withContext<Assign<HTMLStyledProps<'label'>, RadioGroup.ItemProps>>(
  RadioGroup.Item,
  'item',
)
export const ItemText = withContext<Assign<HTMLStyledProps<'span'>, RadioGroup.ItemTextProps>>(
  RadioGroup.ItemText,
  'itemText',
)
export const Label = withContext<Assign<HTMLStyledProps<'label'>, RadioGroup.LabelProps>>(
  RadioGroup.Label,
  'label',
)
export {
  RadioGroupContext as Context,
  RadioGroupItemHiddenInput as ItemHiddenInput,
} from '@ark-ui/solid'
No snippet found2
Add Re-Export
To improve the developer experience, re-export the styled primitives in~/components/ui/radio-button-group.tsx.
export * as RadioButtonGroup from './styled/radio-button-group'
export * as RadioButtonGroup from './styled/radio-button-group'
3
Integrate Recipe
If you're not using @park-ui/preset, add the following recipe to yourpanda.config.ts:
import { radioGroupAnatomy } from '@ark-ui/anatomy'
import { defineSlotRecipe } from '@pandacss/dev'
export const radioButtonGroup = defineSlotRecipe({
  className: 'radioButtonGroup',
  slots: radioGroupAnatomy.keys(),
  base: {
    root: {
      display: 'flex',
      flexWrap: 'wrap',
    },
    item: {
      alignItems: 'center',
      appearance: 'none',
      borderColor: 'border.default',
      borderRadius: 'l2',
      borderWidth: '1px',
      color: 'fg.default',
      cursor: 'pointer',
      display: 'inline-flex',
      fontWeight: 'semibold',
      justifyContent: 'center',
      outline: 'none',
      position: 'relative',
      transitionDuration: 'normal',
      transitionProperty: 'background, border-color, color, box-shadow',
      transitionTimingFunction: 'default',
      userSelect: 'none',
      verticalAlign: 'middle',
      whiteSpace: 'nowrap',
      _hover: {
        background: 'gray.a2',
      },
      _checked: {
        cursor: 'default',
      },
      _disabled: {
        borderColor: 'border.disabled',
        color: 'fg.disabled',
        cursor: 'not-allowed',
        _hover: {
          background: 'initial',
          borderColor: 'border.disabled',
          color: 'fg.disabled',
        },
      },
    },
    itemText: {
      display: 'inline-flex',
      alignItems: 'center',
    },
  },
  defaultVariants: {
    size: 'md',
    variant: 'solid',
  },
  variants: {
    variant: {
      solid: {
        item: {
          _checked: {
            background: 'colorPalette.default',
            borderColor: 'colorPalette.default',
            color: 'colorPalette.fg',
            _hover: {
              color: 'colorPalette.fg',
              background: 'colorPalette.default',
            },
          },
        },
      },
      outline: {
        item: {
          _checked: {
            borderColor: 'colorPalette.default',
            boxShadow: '0 0 0 1px var(--colors-color-palette-default)',
            _hover: {
              background: 'initial',
            },
          },
        },
      },
    },
    size: {
      sm: {
        root: {
          gap: '2',
        },
        item: {
          h: '9',
          minW: '9',
          textStyle: 'sm',
          px: '3.5',
          '& svg': {
            width: '4.5',
            height: '4.5',
          },
        },
        itemText: {
          gap: '2',
        },
      },
      md: {
        root: {
          gap: '3',
        },
        item: {
          h: '10',
          minW: '10',
          textStyle: 'sm',
          px: '4',
          '& svg': {
            width: '5',
            height: '5',
          },
        },
        itemText: {
          gap: '2',
        },
      },
      lg: {
        root: {
          gap: '3',
        },
        item: {
          h: '11',
          minW: '11',
          textStyle: 'md',
          px: '4.5',
          '& svg': {
            width: '5',
            height: '5',
          },
        },
        itemText: {
          gap: '2',
        },
      },
      xl: {
        root: {
          gap: '3',
        },
        item: {
          h: '12',
          minW: '12',
          textStyle: 'md',
          px: '5',
          '& svg': {
            width: '5',
            height: '5',
          },
        },
        itemText: {
          gap: '2.5',
        },
      },
    },
  },
})