Glass Button
lore-glass/glass-buttonFreeComponent
An iOS-style liquid-glass button: a circular icon disc or text capsule on a frosted glass surface, with a velocity-eased press gel that squeezes the button and clears the tint while held.
Live preview
live · rendered by the registry
Rendered by lore-glass on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://loreglasses.com/r/glass-button.jsonSource
1"use client";23import {4 GlassSurface,5 type GlassSurfaceHandle,6} from "@/components/ui/glass-surface";7import {8 glassEase,9 MotionValue,10 PRESS_DURATION,11 prefersReducedMotion,12 RELEASE_DURATION,13 tween,14} from "@/components/ui/glass-motion";15import { cn } from "@/lib/utils";16import { type ComponentProps, useCallback, useEffect, useRef } from "react";1718const PRESS_SCALE = 0.92;19const PRESS_LIFT = -0.2;2021interface GlassButtonProps extends ComponentProps<"button"> {22 size?: number;23 tint?: number;24 tintColor?: string;25 variant?: "icon" | "capsule";26}2728function GlassButton({29 size = 44,30 tint = 0.2,31 tintColor,32 variant = "icon",33 className,34 style,35 children,36 type = "button",37 onPointerDown,38 onPointerUp,39 onPointerCancel,40 onPointerLeave,41 onKeyDown,42 onKeyUp,43 ...props44}: GlassButtonProps) {45 const innerRef = useRef<HTMLSpanElement | null>(null);46 const surfaceHandle = useRef<GlassSurfaceHandle | null>(null);47 const scale = useRef(new MotionValue(1));48 const lift = useRef(new MotionValue(0));49 const pressed = useRef(false);50 const frame = useRef(0);5152 const apply = useCallback(() => {53 frame.current = 0;54 const inner = innerRef.current;55 if (inner) {56 const s = scale.current.get();57 inner.style.transform = s === 1 ? "" : `scale(${s})`;58 }59 surfaceHandle.current?.setTintLift(lift.current.get());60 }, []);6162 const schedule = useCallback(() => {63 if (frame.current === 0) {64 frame.current = requestAnimationFrame(apply);65 }66 }, [apply]);6768 useEffect(() => {69 const subs = [scale.current.on(schedule), lift.current.on(schedule)];70 return () => {71 for (const off of subs) {72 off();73 }74 cancelAnimationFrame(frame.current);75 };76 }, [schedule]);7778 const press = useCallback(() => {79 if (pressed.current) {80 return;81 }82 pressed.current = true;83 const d = prefersReducedMotion() ? 0 : PRESS_DURATION;84 tween(scale.current, PRESS_SCALE, d, glassEase);85 tween(lift.current, PRESS_LIFT, d, glassEase);86 }, []);8788 const release = useCallback(() => {89 if (!pressed.current) {90 return;91 }92 pressed.current = false;93 const d = prefersReducedMotion() ? 0 : RELEASE_DURATION;94 tween(scale.current, 1, d, glassEase);95 tween(lift.current, 0, d, glassEase);96 }, []);9798 return (99 <button100 className={cn(101// … truncated
What it pulls in
Other registry items
Files it writes
More from lore-glass
All 10 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Glassglass | lore-glass | Components | Free | no deps · 4 files | |
| Glass Dialogglass-dialog | lore-glass | Components | Free | 1 dep | |
| Glass Dockglass-dock | lore-glass | Components | Free | 1 dep | |
| Glass Inputglass-input | lore-glass | Components | Free | 1 dep | |
| Glass Popoverglass-popover | lore-glass | Components | Free | 1 dep | |
| Glass Sliderglass-slider | lore-glass | Components | Free | no deps | |
| Glass Switchglass-switch | lore-glass | Components | Free | no deps | |
| Glass Tabsglass-tabs | lore-glass | Components | Free | 1 dep |
