# Dynamic Island

> A springy Dynamic Island interaction with ring and timer states.

A shadcn/ui block from the [@uicapsule](https://uicapsule.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/uicapsule/dynamic-island).

- Page, with a live preview: https://shadcn.exchange/r/uicapsule/dynamic-island
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/uicapsule/dynamic-island-light-b1be80533dae5822.png
- Registry item: https://uicapsule.com/r/dynamic-island.json
- Categories: navigation, skeuomorphism
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://uicapsule.com/r/dynamic-island.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `lucide-react`, `motion`

## Files

### `/dynamic-island.tsx`

```tsx
"use client";

import { useState } from "react";
import { AnimatePresence, motion } from "motion/react";
import type { Transition } from "motion/react";

import { ExpandedWidget, expandedWidgetOptions } from "./expanded-widgets";
import type { ExpandedWidgetView } from "./expanded-widgets";
import { Ring } from "./ring";
import { Timer } from "./timer";

type BaseView = "idle" | "ring" | "timer";
type View = BaseView | ExpandedWidgetView;
type VariantKey = View | `${View}-${View}`;
interface AnimationVariant {
  scale?: number;
  scaleX?: number;
  y?: number;
  bounce?: number;
}

const baseViews: { view: BaseView; label: string }[] = [
  { label: "idle", view: "idle" },
  { label: "ring", view: "ring" },
  { label: "timer", view: "timer" },
];
const views: { view: View; label: string }[] = [...baseViews, ...expandedWidgetOptions];

const variants = {
  exit: (transition: AnimationVariant = {}) => ({
    ...transition,
    filter: "blur(5px)",
    opacity: [1, 0],
  }),
};

const ANIMATION_VARIANTS = new Map<VariantKey, AnimationVariant>([
  ["ring-idle", { bounce: 0.5, scale: 0.9, scaleX: 0.9 }],
  ["timer-ring", { bounce: 0.35, scale: 0.7, y: -7.5 }],
  ["ring-timer", { bounce: 0.35, scale: 1.4, y: 7.5 }],
  ["timer-idle", { bounce: 0.3, scale: 0.7, y: -7.5 }],
]);

// Fallback for pairs the table below does not name (e.g. expanded ↔ expanded).
const DEFAULT_BOUNCE = 0.4;

const BOUNCE_VARIANTS = new Map<VariantKey, number>([
  ["idle", 0.5],
  ["ring-idle", 0.5],
  ["timer-ring", 0.35],
  ["ring-timer", 0.35],
  ["timer-idle", 0.3],
  ["idle-timer", 0.3],
  ["idle-ring", 0.5],
]);

const viewContent = (view: View) => {
  switch (view) {
    case "ring": {
      return <Ring />;
    }
    case "timer": {
      return <Timer />;
    }
    case "idle": {
      return <div className="h-7" />;
    }
    default: {
      return <ExpandedWidget view={view} />;
    }
  }
};

const DynamicIsland = () => {
  const [view, setView] = useState<View>("idle");
  const [variantKey, setVariantKey] = useState<VariantKey>("idle");

  const content = viewContent(view);
  const spring: Transition = {
    bounce: BOUNCE_VARIANTS.get(variantKey) ?? DEFAULT_BOUNCE,
    type: "spring",
  };

  return (
    <div className="h-[260px]">
      <div className="relative flex h-full w-full flex-col justify-between">
        <motion.div
          layout
          transition={spring}
          style={{ borderRadius: 32 }}
          className="mx-auto w-fit min-w-[100px] overflow-hidden rounded-full bg-black"
        >
          <motion.div
            transition={spring}
            initial={{
              filter: "blur(5px)",
              opacity: 0,
              originX: 0.5,
              originY: 0.5,
              scale: 0.9,
            }}
            animate={{
              filter: "blur(0px)",
              opacity: 1,
              originX: 0.5,
              originY: 0.5,
              scale: 1,
              transition: {
                delay: 0.05,
              },
            }}
            key={view}
          >
            {content}
          </motion.div>
        </motion.div>

        <div className="pointer-events-none absolute top-0 left-1/2 flex h-[200px] w-[300px] -translate-x-1/2 items-start justify-center">
          <AnimatePresence mode="popLayout" custom={ANIMATION_VARIANTS.get(variantKey)}>
            <motion.div initial={{ opacity: 0 }} exit="exit" variants={variants} key={view}>
              {content}
            </motion.div>
          </AnimatePresence>
        </div>
        <div className="mx-auto flex max-w-[720px] flex-wrap justify-center gap-2 px-3">
          {views.map(({ view: v, label }) => (
            <button
              type="button"
              className="h-8 rounded-full bg-white px-3 py-1.5 text-xs font-medium text-gray-900 capitalize shadow-sm ring-1 ring-gray-300/50 ring-inset hover:bg-gray-50 aria-pressed:bg-black aria-pressed:text-white aria-pressed:ring-white/20 aria-pressed:hover:bg-black"
              aria-pressed={view === v}
              onClick={() => {
                setView(v);
                setVariantKey(`${view}-${v}`);
              }}
              key={v}
            >
              {label}
            </button>
          ))}
        </div>
      </div>
    </div>
  );
};

export default DynamicIsland;
```

### `/expanded-widgets.tsx`

```tsx
"use client";

import type { CSSProperties, ReactElement, ReactNode } from "react";
import {
  FastForward,
  Headphones,
  Info,
  Mic,
  Phone,
  PhoneOff,
  Plane,
  Play,
  Rewind,
  ScreenShare,
  Video,
} from "lucide-react";
import { motion } from "motion/react";

export type ExpandedWidgetView = "music" | "call" | "memo" | "recording" | "incoming" | "flight";

export interface ExpandedWidgetOption {
  view: ExpandedWidgetView;
  label: string;
}

export const expandedWidgetOptions: ExpandedWidgetOption[] = [
  { label: "music", view: "music" },
  { label: "call", view: "call" },
  { label: "memo", view: "memo" },
  { label: "record", view: "recording" },
  { label: "incoming", view: "incoming" },
  { label: "flight", view: "flight" },
];

// Tokens from the Figma widget pack (Dynamic island pack → variable defs):
// bg/black-solid, text/disabled, bg/overlay-alpha-secondary, red/500,
// text/error-primary, green/500, border/tertiary, pink/500, fuchsia/500.
const ISLAND_BG = "#0a0a0a";
const TEXT_MUTED = "#a3a3a3";
const DARK_BUTTON = "rgba(26,26,26,0.75)";
const RED = "#ef4444";
const RED_DEEP = "#dc2626";
const GREEN = "#16b364";
const RING = "#e5e5e5";
const PINK = "#ec4899";

// Label-1 (16/22, -0.18) and Label-3 (12/16, -0.12) from the pack's type ramp.
const TITLE_CLASS = "truncate text-[16px] leading-[22px] tracking-[-0.18px]";
const SUBTITLE_CLASS = "truncate text-xs leading-4 tracking-[-0.12px]";

// Every expanded widget sits on the same 376px card; only the corner radius and
// the internal flow (`layout`) differ between them.
const WIDGET_SHELL_CLASS = "flex w-[376px] max-w-[calc(100vw-40px)] p-6 text-white";

const WidgetShell = ({ children, layout }: { children: ReactNode; layout: string }) => (
  <div className={`${WIDGET_SHELL_CLASS} ${layout}`} style={{ backgroundColor: ISLAND_BG }}>
    {children}
  </div>
);

// Stop control shared by the recording widgets, straight from the pack:
// 44px circle with a 1px border/tertiary ring and a rounded red square inside.
const StopButton = ({ label }: { label: string }) => (
  <button
    aria-label={label}
    className="grid size-11 shrink-0 place-items-center rounded-full border transition-transform hover:scale-105 active:scale-95"
    style={{ borderColor: RING }}
    type="button"
  >
    <span className="size-[18px] rounded-[4px]" style={{ backgroundColor: RED }} />
  </button>
);

interface RoundActionProps {
  children: ReactNode;
  label: string;
  tone: "light" | "dark" | "danger";
}

const ROUND_ACTION_STYLES: Record<RoundActionProps["tone"], CSSProperties> = {
  danger: { backgroundColor: RED_DEEP, color: "#ffffff" },
  dark: { backgroundColor: DARK_BUTTON, color: "#ffffff" },
  light: { backgroundColor: "#ffffff", color: "#000000" },
};

const RoundAction = ({ children, label, tone }: RoundActionProps) => (
  <button
    aria-label={label}
    className="grid size-11 place-items-center rounded-full transition-transform hover:scale-105 active:scale-95"
    style={ROUND_ACTION_STYLES[tone]}
    type="button"
  >
    {children}
  </button>
);

const AlbumArt = () => (
  <div className="size-12 shrink-0 rounded-full bg-[conic-gradient(from_30deg,#38bdf8,#facc15,#f97316,#22c55e,#38bdf8)] p-1">
    <div className="size-full rounded-full bg-[radial-gradient(circle_at_35%_35%,#f8fafc_0_10%,#0f172a_11%_42%,#14b8a6_43%_64%,#111827_65%)]" />
  </div>
);

const Avatar = () => (
  <div className="size-12 shrink-0 rounded-full bg-[radial-gradient(circle_at_42%_32%,#f4d4b8_0_16%,#2f241f_17%_38%,#f8fafc_39%_56%,#171717_57%)]" />
);

// Five-bar equalizer fading pink/500 → fuchsia/500 like the pack's glyph.
const EQ_BARS = [
  { color: "#db2777", delay: 0, height: 10, id: "eq-0" },
  { color: "#ec4899", delay: 0.15, height: 18, id: "eq-1" },
  { color: "#ec4899", delay: 0.3, height: 13, id: "eq-2" },
  { color: "#d946ef", delay: 0.1, height: 20, id: "eq-3" },
  { color: "#c026d3", delay: 0.25, height: 12, id: "eq-4" },
];

const Equalizer = () => (
  <div aria-hidden className="flex h-[22px] items-center gap-[3px]">
    {EQ_BARS.map((bar) => (
      <motion.span
        key={bar.id}
        className="w-[3px] rounded-full"
        style={{ backgroundColor: bar.color, height: bar.height }}
        animate={{ scaleY: [1, 0.55, 0.85, 1] }}
        transition={{ delay: bar.delay, duration: 1.1, ease: "easeInOut", repeat: Infinity }}
      />
    ))}
  </div>
);

const MusicExpanded = () => (
  <WidgetShell layout="flex-col gap-3 rounded-[32px]">
    <div className="flex items-center gap-2">
      <AlbumArt />
      <div className="min-w-0 flex-1">
        <p className={TITLE_CLASS}>Selfless</p>
        <p className={SUBTITLE_CLASS} style={{ color: TEXT_MUTED }}>
          The New Abnormal
        </p>
      </div>
      <Equalizer />
    </div>
    <div className="flex items-center gap-2">
      <span className="text-xs tracking-[-0.12px] tabular-nums" style={{ color: TEXT_MUTED }}>
        0:44
      </span>
      <div
        className="h-2 flex-1 overflow-hidden rounded-full"
        style={{ backgroundColor: DARK_BUTTON }}
      >
        <div className="h-full w-[34.5%] rounded-full" style={{ backgroundColor: PINK }} />
      </div>
      <span className="text-xs tracking-[-0.12px] tabular-nums" style={{ color: TEXT_MUTED }}>
        -3:00
      </span>
    </div>
    <div className="flex items-center justify-center gap-6">
      <Rewind aria-hidden="true" className="size-6" fill="currentColor" strokeWidth={0} />
      <Play aria-hidden="true" className="size-8" fill="currentColor" strokeWidth={0} />
      <FastForward aria-hidden="true" className="size-6" fill="currentColor" strokeWidth={0} />
    </div>
  </WidgetShell>
);

const OngoingCallExpanded = () => (
  <WidgetShell layout="flex-col gap-3 rounded-[32px]">
    <div className="flex items-center gap-2">
      <Avatar />
      <div className="min-w-0 flex-1">
        <p className={TITLE_CLASS}>Mike Wheeler</p>
        <p className={SUBTITLE_CLASS} style={{ color: TEXT_MUTED }}>
          FaceTime Audio
        </p>
      </div>
      <Info aria-hidden="true" className="size-[22px]" strokeWidth={1.5} />
    </div>
    <div className="flex items-center justify-between">
      <RoundAction label="Audio route" tone="light">
        <Headphones aria-hidden="true" className="size-[22px]" strokeWidth={1.8} />
      </RoundAction>
      <RoundAction label="Mute microphone" tone="light">
        <Mic aria-hidden="true" className="size-[22px]" strokeWidth={1.8} />
      </RoundAction>
      <RoundAction label="Video" tone="dark">
        <Video aria-hidden="true" className="size-[22px]" strokeWidth={1.8} />
      </RoundAction>
      <RoundAction label="Share screen" tone="dark">
        <ScreenShare aria-hidden="true" className="size-[22px]" strokeWidth={1.8} />
      </RoundAction>
      <RoundAction label="End call" tone="danger">
        <PhoneOff aria-hidden="true" className="size-[22px]" strokeWidth={1.8} />
      </RoundAction>
    </div>
  </WidgetShell>
);

// Waveform heights straight from the mock; the last bar fades and hands off to
// a dot trail (red/50 at 40%) for the unrecorded remainder.
const MEMO_BARS = [25, 20, 16, 20, 25, 20, 14, 10, 16, 22, 27, 15, 19].map((height, index) => ({
  height,
  id: `memo-${index}`,
}));
const MEMO_DOTS = Array.from({ length: 11 }, (_, index) => `dot-${index}`);

const VoiceMemoExpanded = () => (
  <WidgetShell layout="items-center gap-3 rounded-full">
    <div aria-hidden className="flex h-11 shrink-0 items-center gap-[2px]">
      {MEMO_BARS.map(({ id, height }, index) => (
        <motion.span
          key={id}
          className="w-[3px] shrink-0 rounded-[4px]"
          style={{ backgroundColor: RED, height }}
          animate={{ scaleY: [1, 0.6, 0.9, 1] }}
          transition={{
            delay: (index % 5) * 0.09,
            duration: 0.9 + (index % 4) * 0.12,
            ease: "easeInOut",
            repeat: Infinity,
          }}
        />
      ))}
      <span
        className="h-3 w-[3px] shrink-0 rounded-[4px] opacity-40"
        style={{ backgroundColor: RED }}
      />
      {MEMO_DOTS.map((id) => (
        <span key={id} className="size-[3px] shrink-0 rounded-[4px] bg-[#fef2f2] opacity-40" />
      ))}
    </div>
    <span
      className="min-w-0 flex-1 text-right text-[17px] tabular-nums"
      style={{ color: RED_DEEP }}
    >
      05:00
    </span>
    <StopButton label="Stop recording" />
  </WidgetShell>
);

const ScreenRecordingExpanded = () => (
  <WidgetShell layout="items-center gap-3 rounded-full">
    <span className={`flex-1 ${TITLE_CLASS}`}>Screen Recording</span>
    <div className="flex shrink-0 items-center gap-1">
      <motion.span
        aria-hidden
        className="size-2 rounded-full"
        style={{ backgroundColor: RED }}
        animate={{ opacity: [1, 0.25, 1] }}
        transition={{ duration: 1.6, ease: "easeInOut", repeat: Infinity }}
      />
      <span className="text-[17px] tabular-nums" style={{ color: RED_DEEP }}>
        05:00
      </span>
    </div>
    <StopButton label="Stop screen recording" />
  </WidgetShell>
);

const IncomingCallExpanded = () => (
  <WidgetShell layout="items-center gap-2 rounded-full">
    <Avatar />
    <div className="min-w-0 flex-1">
      <p className={`${TITLE_CLASS} font-semibold`}>Mike Wheeler</p>
      <p className={SUBTITLE_CLASS} style={{ color: TEXT_MUTED }}>
        Mobile
      </p>
    </div>
    <button
      aria-label="Decline call"
      className="grid size-11 shrink-0 place-items-center rounded-full transition-transform hover:scale-105 active:scale-95"
      style={{ backgroundColor: RED_DEEP }}
      type="button"
    >
      <PhoneOff aria-hidden="true" className="size-[22px]" strokeWidth={1.8} />
    </button>
    <button
      aria-label="Accept call"
      className="grid size-11 shrink-0 place-items-center rounded-full transition-transform hover:scale-105 active:scale-95"
      style={{ backgroundColor: GREEN }}
      type="button"
    >
      <motion.span
        className="grid place-items-center"
        animate={{ rotate: [0, -14, 12, -9, 7, 0] }}
        transition={{ duration: 0.9, ease: "easeInOut", repeat: Infinity, repeatDelay: 1.4 }}
      >
        <Phone aria-hidden="true" className="size-[22px]" strokeWidth={1.8} />
      </motion.span>
    </button>
  </WidgetShell>
);

const FlightExpanded = () => (
  <WidgetShell layout="flex-col gap-4 rounded-[32px]">
    <div className="flex items-center gap-2">
      <div
        className="grid size-12 shrink-0 place-items-center rounded-2xl"
        style={{ backgroundColor: DARK_BUTTON }}
      >
        <Plane aria-hidden="true" className="size-6 text-[#a78bfa]" fill="currentColor" />
      </div>
      <div className="min-w-0 flex-1">
        <p className={TITLE_CLASS}>Departure</p>
        <p className={SUBTITLE_CLASS} style={{ color: TEXT_MUTED }}>
          On time
        </p>
      </div>
      <span className="text-[28px] leading-none font-light tabular-nums">TL104</span>
    </div>
    <div className="flex items-center gap-2">
      <Plane aria-hidden="true" className="size-5" style={{ color: GREEN }} fill="currentColor" />
      <div
        className="h-2 flex-1 overflow-hidden rounded-full"
        style={{ backgroundColor: DARK_BUTTON }}
      >
        <div className="h-full w-[72%] rounded-full" style={{ backgroundColor: GREEN }} />
      </div>
      <span className="text-xs tracking-[-0.12px]" style={{ color: TEXT_MUTED }}>
        LAX
      </span>
    </div>
    <button
      className="h-12 w-full rounded-full bg-white text-[15px] font-medium text-black transition-transform hover:scale-[1.02] active:scale-[0.98]"
      type="button"
    >
      Show Boarding Pass
    </button>
  </WidgetShell>
);

const EXPANDED_WIDGETS: Record<ExpandedWidgetView, () => ReactElement> = {
  call: OngoingCallExpanded,
  flight: FlightExpanded,
  incoming: IncomingCallExpanded,
  memo: VoiceMemoExpanded,
  music: MusicExpanded,
  recording: ScreenRecordingExpanded,
};

interface ExpandedWidgetProps {
  view: ExpandedWidgetView;
}

export const ExpandedWidget = ({ view }: ExpandedWidgetProps) => {
  const Widget = EXPANDED_WIDGETS[view];
  return <Widget />;
};
```

### `/preview.tsx`

```tsx
"use client";

import DynamicIsland from "./dynamic-island";

const Preview = () => (
  <main className="flex h-dvh items-center justify-center overflow-hidden bg-[radial-gradient(circle_at_50%_30%,#334155_0%,#0f172a_42%,#020617_100%)] p-5">
    <div className="w-full max-w-[760px]">
      <DynamicIsland />
    </div>
  </main>
);

export default Preview;
```

### `/ring.tsx`

```tsx
import { useState, useEffect } from "react";
import { motion, AnimatePresence } from "motion/react";

export const Ring = () => {
  const [isSilent, setIsSilent] = useState(false);
  const [firstTime, setFirstTime] = useState(true);

  useEffect(() => {
    const id = setTimeout(
      () => {
        setFirstTime(false);
        setIsSilent(!isSilent);
      },
      firstTime ? 1000 : 2000,
    );

    return () => clearTimeout(id);
  }, [isSilent, firstTime]);

  return (
    <motion.div
      initial={false}
      layout
      className="relative flex h-7 items-center justify-between px-2.5"
      style={{ width: isSilent ? 148 : 128 }}
      transition={{ bounce: 0.5, type: "spring" }}
    >
      <AnimatePresence>
        {isSilent ? (
          <motion.div
            initial={{ filter: "blur(4px)", opacity: 0, scaleX: 0 }}
            animate={{
              filter: "blur(0px)",
              opacity: 1,
              scaleX: 1,
            }}
            exit={{ filter: "blur(4px)", opacity: 0, scaleX: 0 }}
            transition={{ bounce: 0.35, type: "spring" }}
            className="absolute left-[5px] h-[18px] w-10 origin-left rounded-full bg-[#FD4F30]"
          />
        ) : null}
      </AnimatePresence>
      <motion.div
        initial={false}
        className="relative h-[12.75px] w-[11.25px]"
        animate={{
          rotate: isSilent ? [0, -15, 5, -2, 0] : [0, 20, -15, 12.5, -10, 10, -7.5, 7.5, -5, 5, 0],
          x: isSilent ? 9 : 0,
        }}
      >
        <svg
          width="11.25"
          height="12.75"
          viewBox="0 0 15 17"
          fill="none"
          xmlns="http://www.w3.org/2000/svg"
        >
          <path
            d="M1.17969 13.3125H13.5625C14.2969 13.3125 14.7422 12.9375 14.7422 12.3672C14.7422 11.5859 13.9453 10.8828 13.2734 10.1875C12.7578 9.64844 12.6172 8.53906 12.5547 7.64062C12.5 4.64062 11.7031 2.57812 9.625 1.82812C9.32812 0.804688 8.52344 0 7.36719 0C6.21875 0 5.40625 0.804688 5.11719 1.82812C3.03906 2.57812 2.24219 4.64062 2.1875 7.64062C2.125 8.53906 1.98438 9.64844 1.46875 10.1875C0.789062 10.8828 0 11.5859 0 12.3672C0 12.9375 0.4375 13.3125 1.17969 13.3125ZM7.36719 16.4453C8.69531 16.4453 9.66406 15.4766 9.76562 14.3828H4.97656C5.07812 15.4766 6.04688 16.4453 7.36719 16.4453Z"
            fill="white"
          />
        </svg>
        <div className="absolute inset-0">
          <div className="h-5 translate-x-[5.25px] -translate-y-[5px] rotate-[-40deg] overflow-hidden">
            <motion.div
              animate={{ scaleY: isSilent ? 1 : 0 }}
              transition={{
                delay: isSilent ? 0.15 : 0,
                duration: isSilent ? 0.125 : 0.05,
                ease: "easeInOut",
              }}
              className="h-4 w-fit origin-top rounded-full"
            >
              <div className="flex h-full w-[3px] items-center justify-center rounded-full bg-[#FD4F30]">
                <div className="h-full w-[0.75px] rounded-full bg-white" />
              </div>
            </motion.div>
          </div>
        </div>
      </motion.div>
      <div className="ml-auto flex items-center">
        {isSilent ? (
          <span className="text-xs font-medium text-[#FD4F30]">Silent</span>
        ) : (
          <span className="text-xs font-medium text-white">Ring</span>
        )}
      </div>
    </motion.div>
  );
};
```

### `/timer.tsx`

```tsx
import { AnimatePresence, motion } from "motion/react";
import { useEffect, useState } from "react";

const Counter = ({ paused }: { paused: boolean }) => {
  const [count, setCount] = useState(60);

  useEffect(() => {
    if (paused) {
      return;
    }

    const id = setInterval(() => {
      setCount((c) => {
        if (c === 0) {
          return 60;
        }
        return c - 1;
      });
    }, 1000);

    return () => {
      clearInterval(id);
    };
  }, [paused]);

  const digits = [
    { place: "tens", value: Math.floor(count / 10) },
    { place: "ones", value: count % 10 },
  ];

  return (
    <div className="relative w-[64px] overflow-hidden text-3xl font-light whitespace-nowrap">
      0:
      <AnimatePresence initial={false} mode="popLayout">
        {digits.map((digit) => (
          <motion.div
            className="inline-block tabular-nums"
            key={`${digit.place}-${digit.value}`}
            initial={{ filter: "blur(2px)", opacity: 0, y: "12px" }}
            animate={{ filter: "blur(0px)", opacity: 1, y: "0" }}
            exit={{ filter: "blur(2px)", opacity: 0, y: "-12px" }}
            transition={{ bounce: 0.35, type: "spring" }}
          >
            {digit.value}
          </motion.div>
        ))}
      </AnimatePresence>
    </div>
  );
};

export const Timer = () => {
  const [isPaused, setIsPaused] = useState(false);

  return (
    <div className="flex w-[284px] items-center gap-2 py-3 pr-5 pl-3.5">
      <motion.button
        type="button"
        aria-label="Pause timer"
        onClick={() => setIsPaused((p) => !p)}
        whileTap={{ scale: 0.9 }}
        className="flex h-10 w-10 items-center justify-center rounded-full bg-[#5A3C07] transition-colors hover:bg-[#694608]"
      >
        <AnimatePresence initial={false} mode="wait">
          {isPaused ? (
            <motion.svg
              key="play"
              initial={{ filter: "blur(4px)", opacity: 0, scale: 0.5 }}
              animate={{ filter: "blur(0px)", opacity: 1, scale: 1 }}
              exit={{ filter: "blur(4px)", opacity: 0, scale: 0.5 }}
              transition={{ duration: 0.1 }}
              viewBox="0 0 12 14"
              fill="none"
              xmlns="http://www.w3.org/2000/svg"
              className="h-4 w-4 fill-current text-[#FDB000]"
            >
              <path d="M0.9375 13.2422C1.25 13.2422 1.51562 13.1172 1.82812 12.9375L10.9375 7.67188C11.5859 7.28906 11.8125 7.03906 11.8125 6.625C11.8125 6.21094 11.5859 5.96094 10.9375 5.58594L1.82812 0.3125C1.51562 0.132812 1.25 0.015625 0.9375 0.015625C0.359375 0.015625 0 0.453125 0 1.13281V12.1172C0 12.7969 0.359375 13.2422 0.9375 13.2422Z" />
            </motion.svg>
          ) : (
            <motion.svg
              key="pause"
              initial={{ filter: "blur(4px)", opacity: 0, scale: 0.5 }}
              animate={{ filter: "blur(0px)", opacity: 1, scale: 1 }}
              exit={{ filter: "blur(4px)", opacity: 0, scale: 0.5 }}
              transition={{ duration: 0.1 }}
              viewBox="0 0 10 13"
              fill="none"
              xmlns="http://www.w3.org/2000/svg"
              className="h-4 w-4 fill-current text-[#FDB000]"
            >
              <path d="M1.03906 12.7266H2.82031C3.5 12.7266 3.85938 12.3672 3.85938 11.6797V1.03906C3.85938 0.328125 3.5 0 2.82031 0H1.03906C0.359375 0 0 0.359375 0 1.03906V11.6797C0 12.3672 0.359375 12.7266 1.03906 12.7266ZM6.71875 12.7266H8.49219C9.17969 12.7266 9.53125 12.3672 9.53125 11.6797V1.03906C9.53125 0.328125 9.17969 0 8.49219 0H6.71875C6.03125 0 5.67188 0.359375 5.67188 1.03906V11.6797C5.67188 12.3672 6.03125 12.7266 6.71875 12.7266Z" />
            </motion.svg>
          )}
        </AnimatePresence>
      </motion.button>
      <button
        type="button"
        aria-label="Exit"
        className="flex h-10 w-10 items-center justify-center rounded-full bg-[#3C3D3C] text-white transition-colors hover:bg-[#4A4B4A]"
      >
        <svg
          xmlns="http://www.w3.org/2000/svg"
          fill="none"
          viewBox="0 0 24 24"
          strokeWidth={2}
          stroke="currentColor"
          className="h-6 w-6"
        >
          <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
        </svg>
      </button>
      <div className="ml-auto flex items-baseline gap-1.5 pr-0.5 text-[#F7A815]">
        <span className="text-sm leading-none font-medium text-inherit">Timer</span>
        <Counter paused={isPaused} />
      </div>
    </div>
  );
};
```
