Componentform0.89 KB gzipZero dependencies

Switch Card

Switch card combining title, description and toggle.

Preview

Live Preview

Installation

Tokens guide →

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

npx bigbullui add switch-card

Or install the full npm package:

npm install bigbullui
import { SwitchCard } from "@/components/ui/switch-card";

<SwitchCard />

Usage guidance

Use Switch Card when a ticket stub surface needs Switch card combining title, description and toggle. Keep props minimal and prefer composition with sibling stubs.

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

Source code

switch-card.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 SwitchCardProps extends React.HTMLAttributes<HTMLLabelElement> {
  title: string;
  description?: string;
  checked?: boolean;
  defaultChecked?: boolean;
  onCheckedChange?: (checked: boolean) => void;
  disabled?: boolean;
}

/** Card-style toggle switch: title + description + switch control. */
export function SwitchCard({ title, description, checked, defaultChecked, onCheckedChange, disabled, className, ...props }: SwitchCardProps) {
  const [internal, setInternal] = React.useState(defaultChecked ?? false);
  const isOn = checked ?? internal;

  return (
    <label
      className={cn(
        "flex cursor-pointer items-center justify-between gap-4 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",
        isOn ? "border-accent/60 bg-accent/5" : "border-border hover:border-foreground/30",
        disabled && "pointer-events-none opacity-50",
        className
      )}
      {...props}
    >
      <span className="min-w-0">
        <span className="block text-sm font-medium">{title}</span>
        {description && <span className="mt-0.5 block text-xs text-muted-foreground">{description}</span>}
      </span>
      <input type="checkbox" checked={isOn} onChange={(e) => { setInternal(e.target.checked); onCheckedChange?.(e.target.checked); }} className="peer sr-only" disabled={disabled} />
      <span
        className={cn(
          "relative h-5 w-9 shrink-0 rounded-full border transition-colors duration-200 motion-reduce:transition-none peer-focus-visible:outline-none peer-focus-visible:ring-2 peer-focus-visible:ring-ring",
          isOn ? "border-accent bg-accent" : "border-border bg-secondary"
        )}
        aria-hidden="true"
      >
        <span
          className={cn(
            "absolute top-0.5 size-3.5 rounded-full bg-background shadow-xs transition-all duration-200 motion-reduce:transition-none",
            isOn ? "left-[18px]" : "left-0.5"
          )}
        />
      </span>
    </label>
  );
}

Props

PropTypeDescription
titlestring
descriptionstring
checkedboolean
defaultCheckedboolean
onCheckedChange(checked: boolean) => void
disabledboolean