Back to buttons
"use client";

/**
 * @author: @dorianbaffier
 * @description: Bell Button
 * @version: 1.0.0
 * @date: 2026-10-14
 * @license: MIT
 * @website: https://kokonutui.com
 * @github: https://github.com/kokonut-labs/kokonutui
 */

import {
  type PointerEvent,
  useCallback,
  useEffect,
  useRef,
  useState,
} from "react";
import { cn } from "@/lib/utils";

// The bell and its clapper are two pendulums on the same pivot. Nothing
// couples them but collisions: the swinging bell catches the clapper with
// its lip, flings it across, and it strikes the other side. Angles in deg,
// time in seconds.
const STEP = 1 / 240;
const BELL_STIFFNESS = 380;
const BELL_DAMPING = 5;
const CLAPPER_STIFFNESS = 150;
const CLAPPER_DAMPING = 4;
// How far the clapper can swing inside the bell before it hits the lip.
const LIP = 13;
const RESTITUTION = 0.45;
// Share of a hit that goes into the bell, which is much heavier.
const RECOIL = 0.18;
// Impacts harder than this ring out (deg/s, relative).
const STRIKE_SPEED = 260;
const RING_KICK = 600;
const NUDGE_KICK = 160;
const MUTE_KICK = 300;
const REST = 0.05;
const PIVOT = { x: 12, y: 2.5 };
const EASE_OUT = "cubic-bezier(0.23, 1, 0.32, 1)";

function haptic(ms: number) {
  if (typeof navigator !== "undefined" && "vibrate" in navigator) {
    navigator.vibrate(ms);
  }
}

/** A number whose digits roll to their new value, like a counter. */
function RollingNumber({ value }: { value: number }) {
  const text = value.toLocaleString("en-US");
  return (
    <span aria-hidden="true" className="inline-flex tabular-nums">
      {text.split("").map((char, i) => {
        // Keyed from the right, so the ones stay the ones when a digit is added.
        const key = text.length - i;
        if (char < "0" || char > "9") {
          return <span key={key}>{char}</span>;
        }
        const digit = Number(char);
        return (
          <span className="relative inline-block h-5 overflow-hidden" key={key}>
            <span
              className="flex flex-col transition-transform duration-300 ease-[cubic-bezier(0.23,1,0.32,1)] motion-reduce:transition-none"
              style={{ transform: `translateY(${-digit * 10}%)` }}
            >
              {"0123456789".split("").map((d) => (
                <span className="h-5 leading-5" key={d}>
                  {d}
                </span>
              ))}
            </span>
          </span>
        );
      })}
    </span>
  );
}

export interface BellButtonProps {
  /** `default` shows the label and count, `compact` the label, `icon` the bell alone. */
  variant?: "default" | "compact" | "icon";
  /** Controlled state. */
  subscribed?: boolean;
  defaultSubscribed?: boolean;
  onSubscribedChange?: (subscribed: boolean) => void;
  /** Subscribers, not counting this one. */
  count?: number;
  label?: string;
  subscribedLabel?: string;
  className?: string;
}

export default function BellButton({
  variant = "default",
  subscribed: subscribedProp,
  defaultSubscribed = false,
  onSubscribedChange,
  count = 1284,
  label = "Notify me",
  subscribedLabel = "Subscribed",
  className,
}: BellButtonProps) {
  const [internal, setInternal] = useState(defaultSubscribed);
  const subscribed = subscribedProp ?? internal;

  const bellRef = useRef<SVGGElement>(null);
  const clapperRef = useRef<SVGPathElement>(null);
  const leftWaveRef = useRef<SVGPathElement>(null);
  const rightWaveRef = useRef<SVGPathElement>(null);

  const sim = useRef({
    bell: 0,
    bellSpeed: 0,
    clapper: 0,
    clapperSpeed: 0,
    // Muffled: the clapper is held against the bell and can't strike.
    muffled: false,
    raf: 0,
    last: 0,
    carry: 0,
    lastNudge: 0,
    reduceMotion: false,
  });

  const draw = useCallback(() => {
    const s = sim.current;
    bellRef.current?.setAttribute(
      "transform",
      `rotate(${s.bell.toFixed(2)} ${PIVOT.x} ${PIVOT.y})`
    );
    clapperRef.current?.setAttribute(
      "transform",
      `rotate(${s.clapper.toFixed(2)} ${PIVOT.x} ${PIVOT.y})`
    );
  }, []);

  // Two short arcs flare out on the side that was struck.
  const flare = useCallback((side: 1 | -1, strength: number) => {
    const wave = side > 0 ? rightWaveRef.current : leftWaveRef.current;
    if (!wave?.animate) {
      return;
    }
    const peak = Math.min(1, 0.45 + strength * 0.55);
    wave.animate(
      [
        { opacity: peak, transform: "translateX(0px)" },
        { opacity: 0, transform: `translateX(${side * 3}px)` },
      ],
      { duration: 420, easing: EASE_OUT }
    );
  }, []);

  // Advances both pendulums one step and resolves a clapper hit.
  const step = useCallback(() => {
    const s = sim.current;
    s.bellSpeed +=
      (-s.bell * BELL_STIFFNESS - s.bellSpeed * BELL_DAMPING) * STEP;
    s.bell += s.bellSpeed * STEP;
    if (s.muffled) {
      s.clapper = s.bell;
      s.clapperSpeed = s.bellSpeed;
      return;
    }
    s.clapperSpeed +=
      (-s.clapper * CLAPPER_STIFFNESS - s.clapperSpeed * CLAPPER_DAMPING) *
      STEP;
    s.clapper += s.clapperSpeed * STEP;
    const relative = s.clapper - s.bell;
    if (Math.abs(relative) <= LIP) {
      return;
    }
    const side = relative > 0 ? 1 : -1;
    const impact = (s.clapperSpeed - s.bellSpeed) * side;
    s.clapper = s.bell + side * LIP;
    if (impact <= 0) {
      return;
    }
    s.clapperSpeed -= side * impact * (1 + RESTITUTION);
    s.bellSpeed += side * impact * RECOIL;
    if (impact > STRIKE_SPEED) {
      // The clapper hits the bell's right wall when it swings right.
      flare(side as 1 | -1, impact / 1600);
      haptic(6);
    }
  }, [flare]);

  const tick = useCallback(
    (now: number) => {
      const s = sim.current;
      s.raf = 0;
      const elapsed = s.last ? Math.min((now - s.last) / 1000, 0.05) : STEP;
      s.last = now;
      s.carry += elapsed;
      while (s.carry >= STEP) {
        step();
        s.carry -= STEP;
      }
      draw();
      const energy =
        Math.abs(s.bell) +
        Math.abs(s.bellSpeed) * 0.02 +
        Math.abs(s.clapper) +
        Math.abs(s.clapperSpeed) * 0.02;
      if (energy < REST) {
        s.bell = 0;
        s.bellSpeed = 0;
        s.clapper = 0;
        s.clapperSpeed = 0;
        s.last = 0;
        draw();
        return;
      }
      s.raf = requestAnimationFrame(tick);
    },
    [draw, step]
  );

  const kick = useCallback(
    (speed: number) => {
      const s = sim.current;
      if (s.reduceMotion) {
        return;
      }
      s.bellSpeed += speed;
      if (!s.raf) {
        s.raf = requestAnimationFrame(tick);
      }
    },
    [tick]
  );

  useEffect(() => {
    const s = sim.current;
    const query = window.matchMedia("(prefers-reduced-motion: reduce)");
    s.reduceMotion = query.matches;
    const onChange = () => {
      s.reduceMotion = query.matches;
    };
    query.addEventListener("change", onChange);
    return () => {
      query.removeEventListener("change", onChange);
      cancelAnimationFrame(s.raf);
      s.raf = 0;
    };
  }, []);

  const handleClick = () => {
    const next = !subscribed;
    if (subscribedProp === undefined) {
      setInternal(next);
    }
    onSubscribedChange?.(next);
    const s = sim.current;
    if (next) {
      s.muffled = false;
      // Swing the bell away from the clapper so the first hit rings.
      kick(s.bellSpeed >= 0 ? RING_KICK : -RING_KICK);
      if (s.reduceMotion) {
        flare(1, 0.6);
        flare(-1, 0.6);
      }
      haptic(10);
    } else {
      // Off: a dull shake, the clapper held still against the bell.
      s.muffled = true;
      kick(-MUTE_KICK);
    }
  };

  const handlePointerEnter = (event: PointerEvent) => {
    // A brush past the bell sets it swaying a little. Mouse only: on touch,
    // enter fires right before the tap.
    const s = sim.current;
    if (event.pointerType !== "mouse" || s.muffled) {
      return;
    }
    if (event.timeStamp - s.lastNudge < 700) {
      return;
    }
    s.lastNudge = event.timeStamp;
    kick(s.bellSpeed >= 0 ? NUDGE_KICK : -NUDGE_KICK);
  };

  // Starting unsubscribed means the bell is muted.
  useEffect(() => {
    sim.current.muffled = !subscribed;
  }, [subscribed]);

  return (
    <button
      aria-label={variant === "icon" ? label : undefined}
      aria-pressed={subscribed}
      className={cn(
        "inline-flex h-11 select-none items-center gap-2.5 rounded-full border font-medium text-sm outline-none transition-colors duration-200 ease-out focus-visible:ring-2 focus-visible:ring-zinc-400 focus-visible:ring-offset-2 dark:ring-offset-zinc-950",
        variant === "default" && "pr-1.5 pl-4",
        variant === "compact" && "px-4",
        variant === "icon" && "w-11 justify-center",
        subscribed
          ? "border-zinc-900 bg-zinc-900 text-white hover:bg-zinc-800 active:bg-zinc-700 dark:border-zinc-100 dark:bg-zinc-100 dark:text-zinc-900 dark:active:bg-zinc-300 dark:hover:bg-zinc-200"
          : "border-zinc-200 bg-white text-zinc-900 hover:bg-zinc-50 active:bg-zinc-100 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-100 dark:active:bg-zinc-800 dark:hover:bg-zinc-900",
        className
      )}
      onClick={handleClick}
      onPointerEnter={handlePointerEnter}
      type="button"
    >
      <svg
        aria-hidden="true"
        className="size-5 overflow-visible"
        fill="none"
        stroke="currentColor"
        strokeLinecap="round"
        strokeLinejoin="round"
        strokeWidth={2}
        viewBox="0 0 24 24"
      >
        <path
          className="opacity-0"
          d="M21.5 4.5c1.2 1.6 1.8 3.4 1.8 5.5"
          ref={rightWaveRef}
        />
        <path
          className="opacity-0"
          d="M2.5 4.5C1.3 6.1.7 7.9.7 10"
          ref={leftWaveRef}
        />
        <path d="M10.27 21a2 2 0 0 0 3.46 0" ref={clapperRef} />
        <g ref={bellRef}>
          <path
            className={cn(
              "transition-[fill] duration-200 ease-out",
              subscribed ? "fill-current" : "fill-transparent"
            )}
            d="M3.26 15.33A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.67C19.41 13.96 18 12.5 18 8A6 6 0 0 0 6 8c0 4.5-1.41 5.96-2.74 7.33"
          />
          {/* The mute slash, drawn across when off. */}
          <path
            className="transition-[stroke-dashoffset] duration-300 ease-[cubic-bezier(0.23,1,0.32,1)] motion-reduce:transition-none"
            d="M3 3l18 18"
            strokeDasharray="26"
            strokeDashoffset={subscribed ? 26 : 0}
          />
        </g>
      </svg>

      {/* aria-pressed carries the state, so the name stays the label: a
          screen reader hears "Notify me, pressed", not "Subscribed, pressed". */}
      {variant === "icon" ? null : <span className="sr-only">{label}</span>}
      {variant === "icon" ? null : (
        <span aria-hidden="true" className="relative grid">
          {[
            { key: "off", on: !subscribed, text: label },
            { key: "on", on: subscribed, text: subscribedLabel },
          ].map((line) => (
            <span
              className={cn(
                "col-start-1 row-start-1 whitespace-nowrap transition-[opacity,filter] duration-200 ease-out",
                line.on ? "opacity-100 blur-0" : "opacity-0 blur-[2px]"
              )}
              key={line.key}
            >
              {line.text}
            </span>
          ))}
        </span>
      )}

      {variant === "default" ? (
        <span
          className={cn(
            "rounded-full px-2.5 py-1 text-xs transition-colors duration-200 ease-out",
            subscribed
              ? "bg-white/15 text-white/80 dark:bg-zinc-900/10 dark:text-zinc-900/70"
              : "bg-zinc-100 text-zinc-500 dark:bg-zinc-900 dark:text-zinc-400"
          )}
        >
          <RollingNumber value={count + (subscribed ? 1 : 0)} />
          <span className="sr-only">
            {(count + (subscribed ? 1 : 0)).toLocaleString("en-US")} subscribers
          </span>
        </span>
      ) : null}
    </button>
  );
}

Bell Button

By Kokonut UI

Try the interaction, copy the React source files, or download the complete example.

About Bell Button

Bell Button is an animated React and Tailwind CSS button by Kokonut UI. Try the interaction, copy the React source, or download the example with creator credit.

Effects and categories
Hover effects, Animated
Using this example
Copy a TSX file or download the original source and standalone demo. Test keyboard focus, motion and contrast in your own project.

Usage, Tailwind and licensing questions

Read the source files

import-examples/bell-button.tsx

import BellButton from "@/components/kokonutui/bell-button";
export default function ImportedDemo(){return <>
    <div className="flex flex-wrap items-center justify-center gap-4">
        <BellButton />
        <BellButton label="Follow" subscribedLabel="Following" variant="compact" defaultSubscribed />
        <BellButton label="Notifications" variant="icon" />
    </div>
</>}

components/kokonutui/bell-button.tsx

"use client";

/**
 * @author: @dorianbaffier
 * @description: Bell Button
 * @version: 1.0.0
 * @date: 2026-10-14
 * @license: MIT
 * @website: https://kokonutui.com
 * @github: https://github.com/kokonut-labs/kokonutui
 */

import {
  type PointerEvent,
  useCallback,
  useEffect,
  useRef,
  useState,
} from "react";
import { cn } from "@/lib/utils";

// The bell and its clapper are two pendulums on the same pivot. Nothing
// couples them but collisions: the swinging bell catches the clapper with
// its lip, flings it across, and it strikes the other side. Angles in deg,
// time in seconds.
const STEP = 1 / 240;
const BELL_STIFFNESS = 380;
const BELL_DAMPING = 5;
const CLAPPER_STIFFNESS = 150;
const CLAPPER_DAMPING = 4;
// How far the clapper can swing inside the bell before it hits the lip.
const LIP = 13;
const RESTITUTION = 0.45;
// Share of a hit that goes into the bell, which is much heavier.
const RECOIL = 0.18;
// Impacts harder than this ring out (deg/s, relative).
const STRIKE_SPEED = 260;
const RING_KICK = 600;
const NUDGE_KICK = 160;
const MUTE_KICK = 300;
const REST = 0.05;
const PIVOT = { x: 12, y: 2.5 };
const EASE_OUT = "cubic-bezier(0.23, 1, 0.32, 1)";

function haptic(ms: number) {
  if (typeof navigator !== "undefined" && "vibrate" in navigator) {
    navigator.vibrate(ms);
  }
}

/** A number whose digits roll to their new value, like a counter. */
function RollingNumber({ value }: { value: number }) {
  const text = value.toLocaleString("en-US");
  return (
    <span aria-hidden="true" className="inline-flex tabular-nums">
      {text.split("").map((char, i) => {
        // Keyed from the right, so the ones stay the ones when a digit is added.
        const key = text.length - i;
        if (char < "0" || char > "9") {
          return <span key={key}>{char}</span>;
        }
        const digit = Number(char);
        return (
          <span className="relative inline-block h-5 overflow-hidden" key={key}>
            <span
              className="flex flex-col transition-transform duration-300 ease-[cubic-bezier(0.23,1,0.32,1)] motion-reduce:transition-none"
              style={{ transform: `translateY(${-digit * 10}%)` }}
            >
              {"0123456789".split("").map((d) => (
                <span className="h-5 leading-5" key={d}>
                  {d}
                </span>
              ))}
            </span>
          </span>
        );
      })}
    </span>
  );
}

export interface BellButtonProps {
  /** `default` shows the label and count, `compact` the label, `icon` the bell alone. */
  variant?: "default" | "compact" | "icon";
  /** Controlled state. */
  subscribed?: boolean;
  defaultSubscribed?: boolean;
  onSubscribedChange?: (subscribed: boolean) => void;
  /** Subscribers, not counting this one. */
  count?: number;
  label?: string;
  subscribedLabel?: string;
  className?: string;
}

export default function BellButton({
  variant = "default",
  subscribed: subscribedProp,
  defaultSubscribed = false,
  onSubscribedChange,
  count = 1284,
  label = "Notify me",
  subscribedLabel = "Subscribed",
  className,
}: BellButtonProps) {
  const [internal, setInternal] = useState(defaultSubscribed);
  const subscribed = subscribedProp ?? internal;

  const bellRef = useRef<SVGGElement>(null);
  const clapperRef = useRef<SVGPathElement>(null);
  const leftWaveRef = useRef<SVGPathElement>(null);
  const rightWaveRef = useRef<SVGPathElement>(null);

  const sim = useRef({
    bell: 0,
    bellSpeed: 0,
    clapper: 0,
    clapperSpeed: 0,
    // Muffled: the clapper is held against the bell and can't strike.
    muffled: false,
    raf: 0,
    last: 0,
    carry: 0,
    lastNudge: 0,
    reduceMotion: false,
  });

  const draw = useCallback(() => {
    const s = sim.current;
    bellRef.current?.setAttribute(
      "transform",
      `rotate(${s.bell.toFixed(2)} ${PIVOT.x} ${PIVOT.y})`
    );
    clapperRef.current?.setAttribute(
      "transform",
      `rotate(${s.clapper.toFixed(2)} ${PIVOT.x} ${PIVOT.y})`
    );
  }, []);

  // Two short arcs flare out on the side that was struck.
  const flare = useCallback((side: 1 | -1, strength: number) => {
    const wave = side > 0 ? rightWaveRef.current : leftWaveRef.current;
    if (!wave?.animate) {
      return;
    }
    const peak = Math.min(1, 0.45 + strength * 0.55);
    wave.animate(
      [
        { opacity: peak, transform: "translateX(0px)" },
        { opacity: 0, transform: `translateX(${side * 3}px)` },
      ],
      { duration: 420, easing: EASE_OUT }
    );
  }, []);

  // Advances both pendulums one step and resolves a clapper hit.
  const step = useCallback(() => {
    const s = sim.current;
    s.bellSpeed +=
      (-s.bell * BELL_STIFFNESS - s.bellSpeed * BELL_DAMPING) * STEP;
    s.bell += s.bellSpeed * STEP;
    if (s.muffled) {
      s.clapper = s.bell;
      s.clapperSpeed = s.bellSpeed;
      return;
    }
    s.clapperSpeed +=
      (-s.clapper * CLAPPER_STIFFNESS - s.clapperSpeed * CLAPPER_DAMPING) *
      STEP;
    s.clapper += s.clapperSpeed * STEP;
    const relative = s.clapper - s.bell;
    if (Math.abs(relative) <= LIP) {
      return;
    }
    const side = relative > 0 ? 1 : -1;
    const impact = (s.clapperSpeed - s.bellSpeed) * side;
    s.clapper = s.bell + side * LIP;
    if (impact <= 0) {
      return;
    }
    s.clapperSpeed -= side * impact * (1 + RESTITUTION);
    s.bellSpeed += side * impact * RECOIL;
    if (impact > STRIKE_SPEED) {
      // The clapper hits the bell's right wall when it swings right.
      flare(side as 1 | -1, impact / 1600);
      haptic(6);
    }
  }, [flare]);

  const tick = useCallback(
    (now: number) => {
      const s = sim.current;
      s.raf = 0;
      const elapsed = s.last ? Math.min((now - s.last) / 1000, 0.05) : STEP;
      s.last = now;
      s.carry += elapsed;
      while (s.carry >= STEP) {
        step();
        s.carry -= STEP;
      }
      draw();
      const energy =
        Math.abs(s.bell) +
        Math.abs(s.bellSpeed) * 0.02 +
        Math.abs(s.clapper) +
        Math.abs(s.clapperSpeed) * 0.02;
      if (energy < REST) {
        s.bell = 0;
        s.bellSpeed = 0;
        s.clapper = 0;
        s.clapperSpeed = 0;
        s.last = 0;
        draw();
        return;
      }
      s.raf = requestAnimationFrame(tick);
    },
    [draw, step]
  );

  const kick = useCallback(
    (speed: number) => {
      const s = sim.current;
      if (s.reduceMotion) {
        return;
      }
      s.bellSpeed += speed;
      if (!s.raf) {
        s.raf = requestAnimationFrame(tick);
      }
    },
    [tick]
  );

  useEffect(() => {
    const s = sim.current;
    const query = window.matchMedia("(prefers-reduced-motion: reduce)");
    s.reduceMotion = query.matches;
    const onChange = () => {
      s.reduceMotion = query.matches;
    };
    query.addEventListener("change", onChange);
    return () => {
      query.removeEventListener("change", onChange);
      cancelAnimationFrame(s.raf);
      s.raf = 0;
    };
  }, []);

  const handleClick = () => {
    const next = !subscribed;
    if (subscribedProp === undefined) {
      setInternal(next);
    }
    onSubscribedChange?.(next);
    const s = sim.current;
    if (next) {
      s.muffled = false;
      // Swing the bell away from the clapper so the first hit rings.
      kick(s.bellSpeed >= 0 ? RING_KICK : -RING_KICK);
      if (s.reduceMotion) {
        flare(1, 0.6);
        flare(-1, 0.6);
      }
      haptic(10);
    } else {
      // Off: a dull shake, the clapper held still against the bell.
      s.muffled = true;
      kick(-MUTE_KICK);
    }
  };

  const handlePointerEnter = (event: PointerEvent) => {
    // A brush past the bell sets it swaying a little. Mouse only: on touch,
    // enter fires right before the tap.
    const s = sim.current;
    if (event.pointerType !== "mouse" || s.muffled) {
      return;
    }
    if (event.timeStamp - s.lastNudge < 700) {
      return;
    }
    s.lastNudge = event.timeStamp;
    kick(s.bellSpeed >= 0 ? NUDGE_KICK : -NUDGE_KICK);
  };

  // Starting unsubscribed means the bell is muted.
  useEffect(() => {
    sim.current.muffled = !subscribed;
  }, [subscribed]);

  return (
    <button
      aria-label={variant === "icon" ? label : undefined}
      aria-pressed={subscribed}
      className={cn(
        "inline-flex h-11 select-none items-center gap-2.5 rounded-full border font-medium text-sm outline-none transition-colors duration-200 ease-out focus-visible:ring-2 focus-visible:ring-zinc-400 focus-visible:ring-offset-2 dark:ring-offset-zinc-950",
        variant === "default" && "pr-1.5 pl-4",
        variant === "compact" && "px-4",
        variant === "icon" && "w-11 justify-center",
        subscribed
          ? "border-zinc-900 bg-zinc-900 text-white hover:bg-zinc-800 active:bg-zinc-700 dark:border-zinc-100 dark:bg-zinc-100 dark:text-zinc-900 dark:active:bg-zinc-300 dark:hover:bg-zinc-200"
          : "border-zinc-200 bg-white text-zinc-900 hover:bg-zinc-50 active:bg-zinc-100 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-100 dark:active:bg-zinc-800 dark:hover:bg-zinc-900",
        className
      )}
      onClick={handleClick}
      onPointerEnter={handlePointerEnter}
      type="button"
    >
      <svg
        aria-hidden="true"
        className="size-5 overflow-visible"
        fill="none"
        stroke="currentColor"
        strokeLinecap="round"
        strokeLinejoin="round"
        strokeWidth={2}
        viewBox="0 0 24 24"
      >
        <path
          className="opacity-0"
          d="M21.5 4.5c1.2 1.6 1.8 3.4 1.8 5.5"
          ref={rightWaveRef}
        />
        <path
          className="opacity-0"
          d="M2.5 4.5C1.3 6.1.7 7.9.7 10"
          ref={leftWaveRef}
        />
        <path d="M10.27 21a2 2 0 0 0 3.46 0" ref={clapperRef} />
        <g ref={bellRef}>
          <path
            className={cn(
              "transition-[fill] duration-200 ease-out",
              subscribed ? "fill-current" : "fill-transparent"
            )}
            d="M3.26 15.33A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.67C19.41 13.96 18 12.5 18 8A6 6 0 0 0 6 8c0 4.5-1.41 5.96-2.74 7.33"
          />
          {/* The mute slash, drawn across when off. */}
          <path
            className="transition-[stroke-dashoffset] duration-300 ease-[cubic-bezier(0.23,1,0.32,1)] motion-reduce:transition-none"
            d="M3 3l18 18"
            strokeDasharray="26"
            strokeDashoffset={subscribed ? 26 : 0}
          />
        </g>
      </svg>

      {/* aria-pressed carries the state, so the name stays the label: a
          screen reader hears "Notify me, pressed", not "Subscribed, pressed". */}
      {variant === "icon" ? null : <span className="sr-only">{label}</span>}
      {variant === "icon" ? null : (
        <span aria-hidden="true" className="relative grid">
          {[
            { key: "off", on: !subscribed, text: label },
            { key: "on", on: subscribed, text: subscribedLabel },
          ].map((line) => (
            <span
              className={cn(
                "col-start-1 row-start-1 whitespace-nowrap transition-[opacity,filter] duration-200 ease-out",
                line.on ? "opacity-100 blur-0" : "opacity-0 blur-[2px]"
              )}
              key={line.key}
            >
              {line.text}
            </span>
          ))}
        </span>
      )}

      {variant === "default" ? (
        <span
          className={cn(
            "rounded-full px-2.5 py-1 text-xs transition-colors duration-200 ease-out",
            subscribed
              ? "bg-white/15 text-white/80 dark:bg-zinc-900/10 dark:text-zinc-900/70"
              : "bg-zinc-100 text-zinc-500 dark:bg-zinc-900 dark:text-zinc-400"
          )}
        >
          <RollingNumber value={count + (subscribed ? 1 : 0)} />
          <span className="sr-only">
            {(count + (subscribed ? 1 : 0)).toLocaleString("en-US")} subscribers
          </span>
        </span>
      ) : null}
    </button>
  );
}

lib/utils.ts

import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

demo.tsx

export { default as default } from "@/import-examples/bell-button";

CSS

/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
@layer properties;
@layer theme, base, components, utilities;
@layer theme {
  :root, :host {
    --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
      "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
      "Segoe UI Symbol", "Noto Color Emoji";
    --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
      "Courier New", monospace;
    --color-zinc-50: oklch(98.5% 0 none);
    --color-zinc-100: oklch(96.7% 0.001 286.375);
    --color-zinc-200: oklch(92% 0.004 286.32);
    --color-zinc-300: oklch(87.1% 0.006 286.286);
    --color-zinc-400: oklch(70.5% 0.015 286.067);
    --color-zinc-500: oklch(55.2% 0.016 285.938);
    --color-zinc-700: oklch(37% 0.013 285.805);
    --color-zinc-800: oklch(27.4% 0.006 286.033);
    --color-zinc-900: oklch(21% 0.006 285.885);
    --color-zinc-950: oklch(14.1% 0.005 285.823);
    --color-white: #fff;
    --spacing: 0.25rem;
    --text-xs: 0.75rem;
    --text-xs--line-height: calc(1 / 0.75);
    --text-sm: 0.875rem;
    --text-sm--line-height: calc(1.25 / 0.875);
    --font-weight-medium: 500;
    --ease-out: cubic-bezier(0, 0, 0.2, 1);
    --default-transition-duration: 150ms;
    --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
    --default-font-family: var(--font-sans);
    --default-mono-font-family: var(--font-mono);
  }
}
@layer base {
  *, ::after, ::before, ::backdrop, ::file-selector-button {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
    border: 0 solid;
  }
  html, :host {
    line-height: 1.5;
    -webkit-text-size-adjust: 100%;
    tab-size: 4;
    font-family: var(--default-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");
    font-feature-settings: var(--default-font-feature-settings, normal);
    font-variation-settings: var(--default-font-variation-settings, normal);
    -webkit-tap-highlight-color: transparent;
  }
  hr {
    height: 0;
    color: inherit;
    border-top-width: 1px;
  }
  abbr:where([title]) {
    -webkit-text-decoration: underline dotted;
    text-decoration: underline dotted;
  }
  h1, h2, h3, h4, h5, h6 {
    font-size: inherit;
    font-weight: inherit;
  }
  a {
    color: inherit;
    -webkit-text-decoration: inherit;
    text-decoration: inherit;
  }
  b, strong {
    font-weight: bolder;
  }
  code, kbd, samp, pre {
    font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);
    font-feature-settings: var(--default-mono-font-feature-settings, normal);
    font-variation-settings: var(--default-mono-font-variation-settings, normal);
    font-size: 1em;
  }
  small {
    font-size: 80%;
  }
  sub, sup {
    font-size: 75%;
    line-height: 0;
    position: relative;
    vertical-align: baseline;
  }
  sub {
    bottom: -0.25em;
  }
  sup {
    top: -0.5em;
  }
  table {
    text-indent: 0;
    border-color: inherit;
    border-collapse: collapse;
  }
  :-moz-focusring:where(:not(iframe)) {
    outline: auto;
  }
  progress {
    vertical-align: baseline;
  }
  summary {
    display: list-item;
  }
  ol, ul, menu {
    list-style: none;
  }
  img, svg, video, canvas, audio, iframe, embed, object {
    display: block;
    vertical-align: middle;
  }
  img, video {
    max-width: 100%;
    height: auto;
  }
  button, input, select, optgroup, textarea, ::file-selector-button {
    font: inherit;
    font-feature-settings: inherit;
    font-variation-settings: inherit;
    letter-spacing: inherit;
    color: inherit;
    border-radius: 0;
    background-color: transparent;
    opacity: 1;
  }
  :where(select:is([multiple], [size])) optgroup {
    font-weight: bolder;
  }
  :where(select:is([multiple], [size])) optgroup option {
    padding-inline-start: 20px;
  }
  ::file-selector-button {
    margin-inline-end: 4px;
  }
  ::placeholder {
    opacity: 1;
  }
  @supports (not (-webkit-appearance: -apple-pay-button))  or (contain-intrinsic-size: 1px) {
    ::placeholder {
      color: currentcolor;
      @supports (color: color-mix(in lab, red, red)) {
        color: color-mix(in oklab, currentcolor 50%, transparent);
      }
    }
  }
  textarea {
    resize: vertical;
  }
  ::-webkit-search-decoration {
    -webkit-appearance: none;
  }
  ::-webkit-date-and-time-value {
    min-height: 1lh;
    text-align: inherit;
  }
  ::-webkit-datetime-edit {
    display: inline-flex;
  }
  ::-webkit-datetime-edit-fields-wrapper {
    padding: 0;
  }
  ::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field {
    padding-block: 0;
  }
  ::-webkit-calendar-picker-indicator {
    line-height: 1;
  }
  :-moz-ui-invalid {
    box-shadow: none;
  }
  button, input:where([type="button"], [type="reset"], [type="submit"]), ::file-selector-button {
    appearance: button;
  }
  ::-webkit-inner-spin-button, ::-webkit-outer-spin-button {
    height: auto;
  }
  [hidden]:where(:not([hidden="until-found"])) {
    display: none !important;
  }
}
@layer utilities {
  .sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border-width: 0;
  }
  .relative {
    position: relative;
  }
  .col-start-1 {
    grid-column-start: 1;
  }
  .row-start-1 {
    grid-row-start: 1;
  }
  .flex {
    display: flex;
  }
  .grid {
    display: grid;
  }
  .inline-block {
    display: inline-block;
  }
  .inline-flex {
    display: inline-flex;
  }
  .size-5 {
    width: calc(var(--spacing) * 5);
    height: calc(var(--spacing) * 5);
  }
  .h-5 {
    height: calc(var(--spacing) * 5);
  }
  .h-11 {
    height: calc(var(--spacing) * 11);
  }
  .w-11 {
    width: calc(var(--spacing) * 11);
  }
  .transform {
    transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
  }
  .flex-col {
    flex-direction: column;
  }
  .flex-wrap {
    flex-wrap: wrap;
  }
  .items-center {
    align-items: center;
  }
  .justify-center {
    justify-content: center;
  }
  .gap-2\.5 {
    gap: calc(var(--spacing) * 2.5);
  }
  .gap-4 {
    gap: calc(var(--spacing) * 4);
  }
  .overflow-hidden {
    overflow: hidden;
  }
  .overflow-visible {
    overflow: visible;
  }
  .rounded-full {
    border-radius: calc(infinity * 1px);
  }
  .border {
    border-style: var(--tw-border-style);
    border-width: 1px;
  }
  .border-zinc-200 {
    border-color: var(--color-zinc-200);
  }
  .border-zinc-900 {
    border-color: var(--color-zinc-900);
  }
  .bg-white {
    background-color: var(--color-white);
  }
  .bg-white\/15 {
    background-color: color-mix(in srgb, #fff 15%, transparent);
    @supports (color: color-mix(in lab, red, red)) {
      background-color: color-mix(in oklab, var(--color-white) 15%, transparent);
    }
  }
  .bg-zinc-100 {
    background-color: var(--color-zinc-100);
  }
  .bg-zinc-900 {
    background-color: var(--color-zinc-900);
  }
  .fill-current {
    fill: currentcolor;
  }
  .fill-transparent {
    fill: transparent;
  }
  .px-2\.5 {
    padding-inline: calc(var(--spacing) * 2.5);
  }
  .px-4 {
    padding-inline: calc(var(--spacing) * 4);
  }
  .py-1 {
    padding-block: var(--spacing);
  }
  .pr-1\.5 {
    padding-right: calc(var(--spacing) * 1.5);
  }
  .pl-4 {
    padding-left: calc(var(--spacing) * 4);
  }
  .text-sm {
    font-size: var(--text-sm);
    line-height: var(--tw-leading, var(--text-sm--line-height));
  }
  .text-xs {
    font-size: var(--text-xs);
    line-height: var(--tw-leading, var(--text-xs--line-height));
  }
  .leading-5 {
    --tw-leading: calc(var(--spacing) * 5);
    line-height: calc(var(--spacing) * 5);
  }
  .font-medium {
    --tw-font-weight: var(--font-weight-medium);
    font-weight: var(--font-weight-medium);
  }
  .whitespace-nowrap {
    white-space: nowrap;
  }
  .text-white {
    color: var(--color-white);
  }
  .text-white\/80 {
    color: color-mix(in srgb, #fff 80%, transparent);
    @supports (color: color-mix(in lab, red, red)) {
      color: color-mix(in oklab, var(--color-white) 80%, transparent);
    }
  }
  .text-zinc-500 {
    color: var(--color-zinc-500);
  }
  .text-zinc-900 {
    color: var(--color-zinc-900);
  }
  .tabular-nums {
    --tw-numeric-spacing: tabular-nums;
    font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,);
  }
  .opacity-0 {
    opacity: 0%;
  }
  .opacity-100 {
    opacity: 100%;
  }
  .ring {
    --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
    box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
  }
  .blur-\[2px\] {
    --tw-blur: blur(2px);
    filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
  }
  .transition-\[fill\] {
    transition-property: fill;
    transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
    transition-duration: var(--tw-duration, var(--default-transition-duration));
  }
  .transition-\[opacity\,filter\] {
    transition-property: opacity,filter;
    transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
    transition-duration: var(--tw-duration, var(--default-transition-duration));
  }
  .transition-\[stroke-dashoffset\] {
    transition-property: stroke-dashoffset;
    transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
    transition-duration: var(--tw-duration, var(--default-transition-duration));
  }
  .transition-colors {
    transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;
    transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
    transition-duration: var(--tw-duration, var(--default-transition-duration));
  }
  .transition-transform {
    transition-property: transform, translate, scale, rotate;
    transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
    transition-duration: var(--tw-duration, var(--default-transition-duration));
  }
  .duration-200 {
    --tw-duration: 200ms;
    transition-duration: 200ms;
  }
  .duration-300 {
    --tw-duration: 300ms;
    transition-duration: 300ms;
  }
  .ease-\[cubic-bezier\(0\.23\,1\,0\.32\,1\)\] {
    --tw-ease: cubic-bezier(0.23,1,0.32,1);
    transition-timing-function: cubic-bezier(0.23,1,0.32,1);
  }
  .ease-out {
    --tw-ease: var(--ease-out);
    transition-timing-function: var(--ease-out);
  }
  .outline-none {
    --tw-outline-style: none;
    outline-style: none;
  }
  .select-none {
    -webkit-user-select: none;
    user-select: none;
  }
  @media (hover: hover) {
    .hover\:bg-zinc-50:hover {
      background-color: var(--color-zinc-50);
    }
    .hover\:bg-zinc-800:hover {
      background-color: var(--color-zinc-800);
    }
  }
  .focus-visible\:ring-2:focus-visible {
    --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
    box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
  }
  .focus-visible\:ring-zinc-400:focus-visible {
    --tw-ring-color: var(--color-zinc-400);
  }
  .focus-visible\:ring-offset-2:focus-visible {
    --tw-ring-offset-width: 2px;
    --tw-ring-offset-shadow: var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
  }
  .active\:bg-zinc-100:active {
    background-color: var(--color-zinc-100);
  }
  .active\:bg-zinc-700:active {
    background-color: var(--color-zinc-700);
  }
  @media (prefers-reduced-motion: reduce) {
    .motion-reduce\:transition-none {
      transition-property: none;
    }
  }
  .dark\:border-zinc-100:where(.dark, .dark *) {
    border-color: var(--color-zinc-100);
  }
  .dark\:border-zinc-800:where(.dark, .dark *) {
    border-color: var(--color-zinc-800);
  }
  .dark\:bg-zinc-100:where(.dark, .dark *) {
    background-color: var(--color-zinc-100);
  }
  .dark\:bg-zinc-900:where(.dark, .dark *) {
    background-color: var(--color-zinc-900);
  }
  .dark\:bg-zinc-900\/10:where(.dark, .dark *) {
    background-color: color-mix(in srgb, oklch(21% 0.006 285.885) 10%, transparent);
    @supports (color: color-mix(in lab, red, red)) {
      background-color: color-mix(in oklab, var(--color-zinc-900) 10%, transparent);
    }
  }
  .dark\:bg-zinc-950:where(.dark, .dark *) {
    background-color: var(--color-zinc-950);
  }
  .dark\:text-zinc-100:where(.dark, .dark *) {
    color: var(--color-zinc-100);
  }
  .dark\:text-zinc-400:where(.dark, .dark *) {
    color: var(--color-zinc-400);
  }
  .dark\:text-zinc-900:where(.dark, .dark *) {
    color: var(--color-zinc-900);
  }
  .dark\:text-zinc-900\/70:where(.dark, .dark *) {
    color: color-mix(in srgb, oklch(21% 0.006 285.885) 70%, transparent);
    @supports (color: color-mix(in lab, red, red)) {
      color: color-mix(in oklab, var(--color-zinc-900) 70%, transparent);
    }
  }
  .dark\:ring-offset-zinc-950:where(.dark, .dark *) {
    --tw-ring-offset-color: var(--color-zinc-950);
  }
  @media (hover: hover) {
    .dark\:hover\:bg-zinc-200:where(.dark, .dark *):hover {
      background-color: var(--color-zinc-200);
    }
    .dark\:hover\:bg-zinc-900:where(.dark, .dark *):hover {
      background-color: var(--color-zinc-900);
    }
  }
  .dark\:active\:bg-zinc-300:where(.dark, .dark *):active {
    background-color: var(--color-zinc-300);
  }
  .dark\:active\:bg-zinc-800:where(.dark, .dark *):active {
    background-color: var(--color-zinc-800);
  }
}
:root {
  --background: #fff;
  --foreground: #111;
  --primary: #171717;
  --primary-foreground: #fff;
  --secondary: #eee;
  --secondary-foreground: #171717;
  --muted: #eee;
  --muted-foreground: #666;
  --accent: #eee;
  --accent-foreground: #171717;
  --border: #ddd;
  --input: #ddd;
  --ring: #777;
  --destructive: #dc2626;
  --destructive-foreground: #fff;
  --popover: #fff;
  --popover-foreground: #111;
  --success: #15803d;
  --success-foreground: #fff;
  --radius: .625rem;
  --hairline: 1px;
}
.dark {
  --background: #090909;
  --foreground: #fafafa;
  --primary: #fafafa;
  --primary-foreground: #171717;
  --secondary: #262626;
  --secondary-foreground: #fafafa;
  --muted: #262626;
  --muted-foreground: #a3a3a3;
  --accent: #262626;
  --accent-foreground: #fafafa;
  --border: #333;
  --input: #333;
  --popover: #171717;
  --popover-foreground: #fafafa;
  --color-fd-background: #000;
  --color-fd-card: #0a0a0a;
  --color-fd-popover: #0a0a0a;
  --color-fd-border: rgb(255 255 255 / 0.14);
  --color-fd-accent: rgb(255 255 255 / 0.07);
}
:root {
  --sidebar: hsl(0 0% 98%);
  --sidebar-foreground: hsl(240 5.3% 26.1%);
  --sidebar-primary: hsl(240 5.9% 10%);
  --sidebar-primary-foreground: hsl(0 0% 98%);
  --sidebar-accent: hsl(240 4.8% 95.9%);
  --sidebar-accent-foreground: hsl(240 5.9% 10%);
  --sidebar-border: hsl(220 13% 91%);
  --sidebar-ring: hsl(217.2 91.2% 59.8%);
}
.dark {
  --sidebar: hsl(240 5.9% 10%);
  --sidebar-foreground: hsl(240 4.8% 95.9%);
  --sidebar-primary: hsl(224.3 76.3% 48%);
  --sidebar-primary-foreground: hsl(0 0% 100%);
  --sidebar-accent: hsl(240 3.7% 15.9%);
  --sidebar-accent-foreground: hsl(240 4.8% 95.9%);
  --sidebar-border: hsl(240 3.7% 15.9%);
  --sidebar-ring: hsl(217.2 91.2% 59.8%);
}
:root {
  --leaf: #1fa834;
}
.dark {
  --leaf: #3bb44b;
}
@property --tw-rotate-x {
  syntax: "*";
  inherits: false;
}
@property --tw-rotate-y {
  syntax: "*";
  inherits: false;
}
@property --tw-rotate-z {
  syntax: "*";
  inherits: false;
}
@property --tw-skew-x {
  syntax: "*";
  inherits: false;
}
@property --tw-skew-y {
  syntax: "*";
  inherits: false;
}
@property --tw-border-style {
  syntax: "*";
  inherits: false;
  initial-value: solid;
}
@property --tw-leading {
  syntax: "*";
  inherits: false;
}
@property --tw-font-weight {
  syntax: "*";
  inherits: false;
}
@property --tw-ordinal {
  syntax: "*";
  inherits: false;
}
@property --tw-slashed-zero {
  syntax: "*";
  inherits: false;
}
@property --tw-numeric-figure {
  syntax: "*";
  inherits: false;
}
@property --tw-numeric-spacing {
  syntax: "*";
  inherits: false;
}
@property --tw-numeric-fraction {
  syntax: "*";
  inherits: false;
}
@property --tw-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0 0 #0000;
}
@property --tw-shadow-color {
  syntax: "*";
  inherits: false;
}
@property --tw-shadow-alpha {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 100%;
}
@property --tw-inset-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0 0 #0000;
}
@property --tw-inset-shadow-color {
  syntax: "*";
  inherits: false;
}
@property --tw-inset-shadow-alpha {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 100%;
}
@property --tw-ring-color {
  syntax: "*";
  inherits: false;
}
@property --tw-ring-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0 0 #0000;
}
@property --tw-inset-ring-color {
  syntax: "*";
  inherits: false;
}
@property --tw-inset-ring-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0 0 #0000;
}
@property --tw-ring-inset {
  syntax: "*";
  inherits: false;
}
@property --tw-ring-offset-width {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}
@property --tw-ring-offset-color {
  syntax: "*";
  inherits: false;
  initial-value: #fff;
}
@property --tw-ring-offset-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0 0 #0000;
}
@property --tw-blur {
  syntax: "*";
  inherits: false;
}
@property --tw-brightness {
  syntax: "*";
  inherits: false;
}
@property --tw-contrast {
  syntax: "*";
  inherits: false;
}
@property --tw-grayscale {
  syntax: "*";
  inherits: false;
}
@property --tw-hue-rotate {
  syntax: "*";
  inherits: false;
}
@property --tw-invert {
  syntax: "*";
  inherits: false;
}
@property --tw-opacity {
  syntax: "*";
  inherits: false;
}
@property --tw-saturate {
  syntax: "*";
  inherits: false;
}
@property --tw-sepia {
  syntax: "*";
  inherits: false;
}
@property --tw-drop-shadow {
  syntax: "*";
  inherits: false;
}
@property --tw-drop-shadow-color {
  syntax: "*";
  inherits: false;
}
@property --tw-drop-shadow-alpha {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 100%;
}
@property --tw-drop-shadow-size {
  syntax: "*";
  inherits: false;
}
@property --tw-duration {
  syntax: "*";
  inherits: false;
}
@property --tw-ease {
  syntax: "*";
  inherits: false;
}
@layer properties {
  @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
    *, ::before, ::after, ::backdrop {
      --tw-rotate-x: initial;
      --tw-rotate-y: initial;
      --tw-rotate-z: initial;
      --tw-skew-x: initial;
      --tw-skew-y: initial;
      --tw-border-style: solid;
      --tw-leading: initial;
      --tw-font-weight: initial;
      --tw-ordinal: initial;
      --tw-slashed-zero: initial;
      --tw-numeric-figure: initial;
      --tw-numeric-spacing: initial;
      --tw-numeric-fraction: initial;
      --tw-shadow: 0 0 #0000;
      --tw-shadow-color: initial;
      --tw-shadow-alpha: 100%;
      --tw-inset-shadow: 0 0 #0000;
      --tw-inset-shadow-color: initial;
      --tw-inset-shadow-alpha: 100%;
      --tw-ring-color: initial;
      --tw-ring-shadow: 0 0 #0000;
      --tw-inset-ring-color: initial;
      --tw-inset-ring-shadow: 0 0 #0000;
      --tw-ring-inset: initial;
      --tw-ring-offset-width: 0px;
      --tw-ring-offset-color: #fff;
      --tw-ring-offset-shadow: 0 0 #0000;
      --tw-blur: initial;
      --tw-brightness: initial;
      --tw-contrast: initial;
      --tw-grayscale: initial;
      --tw-hue-rotate: initial;
      --tw-invert: initial;
      --tw-opacity: initial;
      --tw-saturate: initial;
      --tw-sepia: initial;
      --tw-drop-shadow: initial;
      --tw-drop-shadow-color: initial;
      --tw-drop-shadow-alpha: 100%;
      --tw-drop-shadow-size: initial;
      --tw-duration: initial;
      --tw-ease: initial;
    }
  }
}