Componentform0.81 KB gzipZero dependencies

Radio Tile

Radio tile with icon, description and sliding dot.

Preview

Live Preview

Installation

Tokens guide →

Add to your project via CLI (zero dependencies, code you own):

npx bigbullui add radio-tile

Or install the full npm package:

npm install bigbullui
import { RadioTile } from "@/components/ui/radio-tile";

<RadioTile />

Usage guidance

Use Radio Tile when a ticket stub surface needs Radio tile with icon, description and sliding dot. Keep props minimal and prefer composition with sibling stubs.

Avoid Radio Tile for long form flows or dense data grids where a dedicated layout block fits better.

Source code

radio-tile.tsx

Zero dependencies (only React + utils). Copy and paste directly into your project:

"use client";

import * as React from "react";
import { cn } from "./lib/utils";

export interface RadioTileProps extends React.HTMLAttributes<HTMLLabelElement> {
  title: string;
  description?: string;
  icon?: React.ReactNode;
  value: string;
  checked?: boolean;
  onCheckedChange?: (value: string) => void;
  name?: string;
}

/** Radio tile card: icon + title + description + radio indicator. */
export function RadioTile({ title, description, icon, value, checked, onCheckedChange, name = "radio-tile", className, ...props }: RadioTileProps) {
  return (
    <label
      className={cn(
        "group flex cursor-pointer items-start gap-3 rounded-lg border p-3.5 select-none",
        "transition-all duration-200 motion-reduce:transition-none",
        "focus-within:outline-none focus-within:ring-2 focus-within:ring-ring",
        checked ? "border-accent bg-accent/5" : "border-border hover:border-foreground/30",
        className
      )}
      {...props}
    >
      <input
        type="radio"
        name={name}
        value={value}
        checked={checked}
        onChange={() => onCheckedChange?.(value)}
        className="peer sr-only"
      />
      {icon && (
        <span className={cn("shrink-0 transition-colors duration-150 motion-reduce:transition-none", checked ? "text-accent" : "text-muted-foreground")}>
          {icon}
        </span>
      )}
      <span className="min-w-0 flex-1">
        <span className={cn("block text-sm font-medium transition-colors motion-reduce:transition-none", checked && "text-accent")}>{title}</span>
        {description && <span className="mt-0.5 block text-xs text-muted-foreground">{description}</span>}
      </span>
      <span
        className={cn(
          "mt-0.5 flex size-4 shrink-0 items-center justify-center rounded-full border-2 transition-colors duration-150 motion-reduce:transition-none",
          checked ? "border-accent" : "border-border"
        )}
        aria-hidden="true"
      >
        {checked && <span className="size-2 rounded-full bg-accent" />}
      </span>
    </label>
  );
}

Props

PropTypeDescription
titlestring
descriptionstring
iconReact.ReactNode
valuestring
checkedboolean
onCheckedChange(value: string) => void
namestring