# iOS Control Center

> Frosted tiles with draggable brightness and volume sliders.

- Author: Atelier UI
- License: MIT
- Page: https://chromotion.dev/en/templates/ios-control-center
- Site: Atelier UI (https://chromotion.dev)

## Install

```bash
npx shadcn@latest add https://chromotion.dev/r/ios-control-center.json
```

Adds the page at `app/ios-control-center/page.tsx` together with the components below. Stack: React + TypeScript + Tailwind CSS v4, shadcn/ui conventions. `cn` comes from `@/lib/utils`.

## Components

- [iOS Kit](https://chromotion.dev/r/ios-kit.md): `@/components/ios-kit`

## Dependencies

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

## Source

```tsx title="app/ios-control-center/page.tsx"
"use client"

import * as React from "react"
import {
  Bluetooth,
  Calculator,
  Camera,
  Flashlight,
  Moon,
  Pause,
  Play,
  Plane,
  Radio,
  ScreenShare,
  SkipBack,
  SkipForward,
  Sun,
  Timer,
  Volume2,
  Wifi,
  RotateCcw,
} from "lucide-react"

import { cn } from "@/lib/utils"
import { IOSDevice } from "@/components/ios-kit"

type Icon = React.ComponentType<{ className?: string }>

const tile = "rounded-[28px] bg-white/20 backdrop-blur-2xl"

function Toggle({
  icon: Icon,
  label,
  on,
  onClick,
  tint,
  off = "bg-black/30 text-white",
  className,
}: {
  icon: Icon
  label: string
  on: boolean
  onClick: () => void
  tint: string
  off?: string
  className?: string
}) {
  return (
    <button
      type="button"
      aria-label={label}
      aria-pressed={on}
      onClick={onClick}
      className={cn(
        "flex items-center justify-center rounded-full transition-[background-color,transform] duration-200 active:scale-90",
        on ? tint : off,
        className
      )}
    >
      <Icon className="size-6" />
    </button>
  )
}

function VerticalSlider({ value, onChange, icon: Icon, label }: { value: number; onChange: (v: number) => void; icon: Icon; label: string }) {
  const update = (event: React.PointerEvent<HTMLDivElement>) => {
    const rect = event.currentTarget.getBoundingClientRect()
    onChange(Math.round(Math.min(100, Math.max(0, ((rect.bottom - event.clientY) / rect.height) * 100))))
  }
  return (
    <div
      role="slider"
      aria-label={label}
      aria-valuemin={0}
      aria-valuemax={100}
      aria-valuenow={value}
      aria-orientation="vertical"
      tabIndex={0}
      onKeyDown={(event) => {
        if (event.key === "ArrowUp" || event.key === "ArrowRight") onChange(Math.min(100, value + 5))
        if (event.key === "ArrowDown" || event.key === "ArrowLeft") onChange(Math.max(0, value - 5))
      }}
      onPointerDown={(event) => {
        event.currentTarget.setPointerCapture(event.pointerId)
        update(event)
      }}
      onPointerMove={(event) => event.buttons === 1 && update(event)}
      className="relative flex h-[160px] w-[72px] touch-none items-end overflow-hidden rounded-[28px] bg-white/20 backdrop-blur-2xl outline-none focus-visible:ring-2 focus-visible:ring-white"
    >
      <div className="w-full bg-white" style={{ height: `${value}%` }} />
      <Icon className="pointer-events-none absolute bottom-4 left-1/2 size-6 -translate-x-1/2 text-black/60 mix-blend-difference" />
    </div>
  )
}

export default function IOSControlCenter() {
  const [airplane, setAirplane] = React.useState(false)
  const [cellular, setCellular] = React.useState(true)
  const [wifi, setWifi] = React.useState(true)
  const [bluetooth, setBluetooth] = React.useState(true)
  const [playing, setPlaying] = React.useState(true)
  const [focus, setFocus] = React.useState(false)
  const [torch, setTorch] = React.useState(false)
  const [lock, setLock] = React.useState(false)
  const [brightness, setBrightness] = React.useState(65)
  const [volume, setVolume] = React.useState(40)

  return (
    <IOSDevice light className="bg-black">
      <div
        aria-hidden
        className="absolute inset-0 scale-110 bg-[radial-gradient(90%_55%_at_20%_10%,#5b7cff_0%,transparent_60%),radial-gradient(90%_60%_at_90%_70%,#d946ef_0%,transparent_60%),linear-gradient(#121634,#221038)] blur-2xl"
      />
      <div className="relative flex min-h-0 flex-1 flex-col gap-3 px-5 pt-[70px] pb-[60px] text-white">
        <div className="grid grid-cols-2 gap-3">
          <div className={cn(tile, "grid grid-cols-2 place-items-center gap-3 p-4")}>
            <Toggle icon={Plane} label="Airplane Mode" on={airplane} onClick={() => setAirplane((v) => !v)} tint="bg-orange-500" className="size-[56px]" />
            <Toggle icon={Radio} label="Cellular Data" on={cellular && !airplane} onClick={() => setCellular((v) => !v)} tint="bg-green-500" className="size-[56px]" />
            <Toggle icon={Wifi} label="Wi-Fi" on={wifi && !airplane} onClick={() => setWifi((v) => !v)} tint="bg-blue-500" className="size-[56px]" />
            <Toggle icon={Bluetooth} label="Bluetooth" on={bluetooth} onClick={() => setBluetooth((v) => !v)} tint="bg-blue-500" className="size-[56px]" />
          </div>

          <div className={cn(tile, "flex flex-col justify-between p-4")}>
            <div>
              <p className="truncate text-[15px] font-semibold">Midnight City</p>
              <p className="truncate text-[13px] text-white/70">M83</p>
            </div>
            <div className="flex items-center justify-between">
              <button type="button" aria-label="Previous" className="active:opacity-50"><SkipBack className="size-6" fill="currentColor" /></button>
              <button type="button" aria-label={playing ? "Pause" : "Play"} onClick={() => setPlaying((v) => !v)} className="active:opacity-50">
                {playing ? <Pause className="size-8" fill="currentColor" /> : <Play className="size-8" fill="currentColor" />}
              </button>
              <button type="button" aria-label="Next" className="active:opacity-50"><SkipForward className="size-6" fill="currentColor" /></button>
            </div>
          </div>
        </div>

        <div className="grid grid-cols-4 gap-3">
          <button
            type="button"
            aria-pressed={focus}
            onClick={() => setFocus((v) => !v)}
            className={cn(tile, "col-span-2 flex items-center gap-3 px-4 py-4 text-left transition-colors", focus && "bg-indigo-500/80")}
          >
            <Moon className="size-6" fill={focus ? "currentColor" : "none"} />
            <span className="text-[15px] font-semibold">{focus ? "Do Not Disturb" : "Focus"}</span>
          </button>
          <VerticalSlider value={brightness} onChange={setBrightness} icon={Sun} label="Brightness" />
          <VerticalSlider value={volume} onChange={setVolume} icon={Volume2} label="Volume" />
        </div>

        <div className="grid grid-cols-3 gap-3">
          <Toggle icon={RotateCcw} label="Orientation lock" on={lock} onClick={() => setLock((v) => !v)} tint="bg-red-500 text-white" off={cn(tile, "text-white")} className="h-[68px] rounded-[26px]" />
          <button type="button" aria-label="Screen Mirroring" className={cn(tile, "flex h-[68px] items-center justify-center rounded-[26px] active:scale-90")}>
            <ScreenShare className="size-6" />
          </button>
          <Toggle icon={Flashlight} label="Flashlight" on={torch} onClick={() => setTorch((v) => !v)} tint="bg-white text-black" off={cn(tile, "text-white")} className="h-[68px] rounded-[26px]" />
          <button type="button" aria-label="Timer" className={cn(tile, "flex h-[68px] items-center justify-center rounded-[26px] active:scale-90")}>
            <Timer className="size-6" />
          </button>
          <button type="button" aria-label="Calculator" className={cn(tile, "flex h-[68px] items-center justify-center rounded-[26px] active:scale-90")}>
            <Calculator className="size-6" />
          </button>
          <button type="button" aria-label="Camera" className={cn(tile, "flex h-[68px] items-center justify-center rounded-[26px] active:scale-90")}>
            <Camera className="size-6" />
          </button>
        </div>
      </div>
    </IOSDevice>
  )
}
```
