# iOS Round Toggle Button

> Control Center style round toggle with icon and caption; switches to the tint color when pressed.

- Author: Atelier UI
- License: MIT
- Page: https://chromotion.dev/en/components/ios-round-toggle-button
- Site: Atelier UI (https://chromotion.dev)

## Install

```bash
npx shadcn@latest add https://chromotion.dev/r/ios-round-toggle-button.json
```

Stack: React + TypeScript + Tailwind CSS v4, shadcn/ui conventions. `cn` comes from `@/lib/utils`.

## Dependencies

```bash
npm install lucide-react
```

## Usage

```tsx title="components/ios-round-toggle-button-demo.tsx"
import { Bluetooth, Moon, Plane, Wifi } from "lucide-react"

import { IOSRoundToggleButton } from "@/components/ios-round-toggle-button"

export default function IOSRoundToggleButtonDemo() {
  return (
    <div className="flex flex-wrap items-start justify-center gap-6 rounded-3xl bg-gradient-to-br from-slate-200 to-slate-300 p-10 dark:from-slate-800 dark:to-slate-900">
      <IOSRoundToggleButton icon={<Plane />} label="Airplane" tint="#ff9500" />
      <IOSRoundToggleButton icon={<Wifi />} label="Wi-Fi" defaultPressed />
      <IOSRoundToggleButton icon={<Bluetooth />} label="Bluetooth" defaultPressed />
      <IOSRoundToggleButton icon={<Moon />} label="Focus" tint="#5856d6" />
    </div>
  )
}
```

## Source

```tsx title="components/ios-round-toggle-button.tsx"
"use client"

import * as React from "react"

import { cn } from "@/lib/utils"

export interface IOSRoundToggleButtonProps
  extends Omit<React.ComponentProps<"button">, "onChange" | "children" | "defaultValue"> {
  icon: React.ReactNode
  /** Caption under the circle; also used as the accessible name */
  label: string
  /** Color of the "on" state */
  tint?: string
  pressed?: boolean
  defaultPressed?: boolean
  onPressedChange?: (pressed: boolean) => void
}

export function IOSRoundToggleButton({
  icon,
  label,
  tint = "#007aff",
  pressed,
  defaultPressed = false,
  onPressedChange,
  onClick,
  className,
  style,
  ...props
}: IOSRoundToggleButtonProps) {
  const [inner, setInner] = React.useState(defaultPressed)
  const on = pressed ?? inner

  return (
    <span className="inline-flex flex-col items-center gap-1.5">
      <button
        type="button"
        aria-pressed={on}
        aria-label={label}
        onClick={(event) => {
          onClick?.(event)
          const next = !on
          if (pressed === undefined) setInner(next)
          onPressedChange?.(next)
        }}
        style={on ? { backgroundColor: tint, ...style } : style}
        className={cn(
          "flex size-16 items-center justify-center rounded-full backdrop-blur-xl transition-[transform,background-color,color] duration-200 select-none active:scale-90",
          "focus-visible:ring-[3px] focus-visible:ring-[#007aff]/40 focus-visible:outline-none [&_svg]:size-7",
          on ? "text-white" : "bg-black/[0.08] text-neutral-900 dark:bg-white/15 dark:text-white",
          className
        )}
        {...props}
      >
        {icon}
      </button>
      <span className="text-[12px] font-medium text-neutral-600 dark:text-neutral-300">{label}</span>
    </span>
  )
}
```
