Button

Frosted Button

A translucent Button for use over art: a blurred, faint background-tinted fill with a border and inner glow that follow currentColor. Needs a backdrop behind it for the blur to read.

Free, MIT licenseOpen in Browse
import { cn } from "cn"

import { Button } from "@/components/ui/button"

/**
 * A translucent button for use over artwork or photos: a 15% wash of the
 * background color (25% on hover) with a backdrop blur, and a border and
 * inner glow drawn in the text color. The blur only shows when something
 * sits behind the button.
 *
 * It is the default `Button` with its fill replaced, so `size`, the focus
 * ring and the disabled style stay the same. Its text uses the `foreground`
 * color, and the border and glow follow any text color you set.
 */
function FrostedButton({
  className,
  ...props
}: Omit<
  React.ComponentProps<typeof Button>,
  "variant" | "className" | "size"
> & {
  /**
   * The stock `Button` size: `default` 36px tall, `xs` 24px, `sm` 32px and
   * `lg` 40px, or the square `icon` 36px, `icon-xs` 24px, `icon-sm` 32px and
   * `icon-lg` 40px.
   */
  size?: React.ComponentProps<typeof Button>["size"]
  /**
   * Extra classes, as a string only: the stock `Button` does not accept a
   * `className` function.
   */
  className?: string
}) {
  return (
    <Button
      data-slot="frosted-button"
      className={cn(
        "relative border-current/25 bg-background/15 text-foreground shadow-md backdrop-blur-md hover:bg-background/25",
        "after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit] after:opacity-20 after:shadow-[inset_0_0_2px_1px_currentColor]",
        className
      )}
      {...props}
    />
  )
}

export { FrostedButton }

Installation

pnpm dlx shadcn add @01orbital/frosted-button
npx shadcn add @01orbital/frosted-button
yarn shadcn add @01orbital/frosted-button
bunx --bun shadcn add @01orbital/frosted-button

Free under the MIT license. Add the @01orbital registry to components.json once (install guide), then run the command.

It lands at components/button/frosted-button.tsx, as source you own and edit.

The example also uses conic-glow. Install it too:

pnpm dlx shadcn add @01orbital/conic-glow
npx shadcn add @01orbital/conic-glow
yarn shadcn add @01orbital/conic-glow
bunx --bun shadcn add @01orbital/conic-glow

Usage

"use client"

import { useState } from "react"

import { FrostedButton } from "@/components/button/frosted-button"
import { ConicGlow } from "@/components/glow/conic-glow"

// Every word on the page. Change copy here, not in the markup.
const COPY = {
  cta: "Turn the light",
} as const

// Each press aims a different stretch of the color wheel at the button.
const ANGLES = [224, 314, 44, 134] as const

// Frost needs color behind it to show.
export default function FrostedButtonDemo() {
  const [turn, setTurn] = useState(0)
  return (
    <div className="dark grid w-full place-items-center bg-background p-6 text-foreground">
      <div className="relative isolate grid aspect-4/3 w-full max-w-md place-items-center overflow-hidden rounded-3xl bg-background">
        {/* Scaled from its corner so the glow fills a card this small. */}
        <ConicGlow
          rotate={ANGLES[turn]}
          className="-z-10 origin-top-right scale-300"
        />
        <FrostedButton
          size="lg"
          type="button"
          onClick={() => setTurn((turn + 1) % ANGLES.length)}
        >
          {COPY.cta}
        </FrostedButton>
      </div>
    </div>
  )
}

API reference

Prop

Type

It also accepts the props of the Base UI part it wraps, and the HTML attributes of the element it renders.

Dependencies

npm: cn. The CLI installs them for you.

Registry pieces it installs with it:

  • button