For the complete documentation index, see llms.txt. Markdown variants are available by appending .md to documentation URLs. Homepage markdown is available at /llms.md/content.md. An agent skill is available at /.well-known/agent-skills/site-skill.md.

Radio Group

The Radio Group component from this design system, built on Base UI.

Installation

$ pnpm dlx shadcn@latest add @cobalt/radio-group

Sizes

default is 16px and sm is 14px. Arrow keys move the selection inside a group.

Source

"use client";

import { Radio as RadioPrimitive } from "@base-ui/react/radio";
import { RadioGroup as RadioGroupPrimitive } from "@base-ui/react/radio-group";
import { cn } from "cn";

/**
 * Set `data-pointer-open` on the group (or an ancestor) to hide the focus ring
 * on its items. A popover opened with the mouse focuses an item from script and
 * Chrome then reports `:focus-visible` if a key was pressed earlier. Remove the
 * attribute on the first key press so keyboard focus stays visible.
 */
function RadioGroup({ className, ...props }: RadioGroupPrimitive.Props) {
  return (
    <RadioGroupPrimitive
      data-slot="radio-group"
      className={cn("grid w-full gap-2", className)}
      {...props}
    />
  );
}

function RadioGroupItem({
  className,
  size = "default",
  ...props
}: RadioPrimitive.Root.Props & { size?: "default" | "sm" }) {
  return (
    <RadioPrimitive.Root
      data-slot="radio-group-item"
      data-size={size}
      className={cn(
        "group/radio-group-item peer relative flex size-4 shrink-0 cursor-pointer items-center justify-center rounded-full outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background in-data-[pointer-open]:focus-visible:ring-0 in-data-[pointer-open]:focus-visible:ring-offset-0 data-disabled:cursor-not-allowed data-disabled:opacity-50 aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=sm]:size-3.5",
        className
      )}
      {...props}
    >
      <span className="absolute inset-0 rounded-full border border-[var(--border-strong,var(--input))] bg-popover shadow-[shadow:var(--shadow-control,0_1px_2px_0_rgb(0_0_0/0.05))] transition-opacity duration-200 ease-[ease] motion-reduce:transition-none group-data-checked/radio-group-item:opacity-0 group-data-[size=sm]/radio-group-item:border-[0.875px]" />
      <span className="absolute inset-0 rounded-full bg-primary bg-[image:var(--gradient-button-primary-default,linear-gradient(180deg,oklch(from_var(--primary)_calc(l_+_0.08)_c_h),var(--primary)))] opacity-0 shadow-[inset_0_0_0_1px_oklch(from_var(--primary)_calc(l_+_0.08)_c_h),inset_0_2px_0_0_rgb(255_255_255/0.25)] transition-opacity duration-200 ease-[ease] motion-reduce:transition-none group-data-checked/radio-group-item:opacity-100 group-data-[size=sm]/radio-group-item:shadow-[inset_0_0_0_0.875px_oklch(from_var(--primary)_calc(l_+_0.08)_c_h),inset_0_1.75px_0_0_rgb(255_255_255/0.25)]" />
      <RadioPrimitive.Indicator
        keepMounted
        data-slot="radio-group-indicator"
        className="absolute top-1/2 left-1/2 size-1.5 -translate-x-1/2 -translate-y-1/2 scale-0 rounded-full bg-primary-foreground opacity-0 transition-[scale,opacity] duration-200 ease-[ease] data-checked:scale-100 data-checked:opacity-100 motion-reduce:transition-none group-data-[size=sm]/radio-group-item:size-[5px]"
      />
    </RadioPrimitive.Root>
  );
}

export { RadioGroup, RadioGroupItem };