Frosted Capsule CTAReact + Multi-Shadow

Scaling Capsule Tactile Button

Interactive capsule button engineered with a frosted translucent glass cavity tray, multi-tier elevation drop shadows, specular bevel insets, and an embedded circular apex emblem badge.

preview.tsx
Interactions:0
Hover or click to interact

Customizer

PRESETS & VARIATIONS

All Variants Preview

Hover over buttons to test frosted glass capsule elevation & multi-tier shadow stacks

Figma Dark Obsidianvariant="dark"
1:1 Figma Master design with frosted translucent cavity tray
UI Pirate Magmavariant="orange"
Signature magma orange glowing capsule with apex emblem
Titanium Lightvariant="light"
Clean ceramic light pill with high-contrast ladder apex
Cyberpunk Neonvariant="cyberpunk"
Ultraviolet neon tray with cybernetic specular insets

Installation & Setup

This component is self-contained and powered by Framer Motion for spring physics and Tailwind CSS for styling. Install the required peer dependency:

npm install framer-motion clsx tailwind-merge
1. Copy Component Code

Paste into components/ScalingCapsuleButton.tsx.

2. Import in Your Page

Drop into any Next.js (App/Pages) or React project.

3. Customize Props

Pass custom labels, variants, sizes, and click handlers.

Code & Integration

ScalingCapsuleButton.tsx• Production Ready
"use client";

import React, { useState } from "react";
import { motion } from "framer-motion";

export type ScalingCapsuleVariant = "dark" | "orange" | "light" | "cyberpunk";

export interface ScalingCapsuleButtonProps {
  label?: string;
  variant?: ScalingCapsuleVariant;
  size?: "xs" | "sm" | "md" | "lg" | "xl";
  icon?: React.ReactNode;
  onClick?: () => void;
  className?: string;
  disabled?: boolean;
}

export const ApexEmblemIcon: React.FC<{ className?: string }> = ({
  className = "w-[16px] h-[16px]",
}) => (
  <svg
    viewBox="0 0 24 24"
    fill="currentColor"
    xmlns="http://www.w3.org/2000/svg"
    className={className}
  >
    <path d="M12 2.2L6.2 13.5H17.8L12 2.2ZM12 6.8L14.4 11.5H9.6L12 6.8Z" />
    <polygon points="6.2,13.5 3,21.5 6.8,21.5 8.2,18 7.2,15.5 8.5,13.5" />
    <polygon points="17.8,13.5 21,21.5 17.2,21.5 15.8,18 16.8,15.5 15.5,13.5" />
    <rect x="7.4" y="14.2" width="9.2" height="1.1" rx="0.3" />
    <rect x="6.8" y="16.0" width="10.4" height="1.1" rx="0.3" />
    <rect x="6.0" y="17.8" width="12.0" height="1.1" rx="0.3" />
    <rect x="5.2" y="19.6" width="13.6" height="1.1" rx="0.3" />
  </svg>
);

export const ScalingCapsuleButton: React.FC<ScalingCapsuleButtonProps> = ({
  label = "Scaling Workshop",
  variant = "dark",
  size = "md",
  icon,
  onClick,
  className = "",
  disabled = false,
}) => {
  const [isHovered, setIsHovered] = useState(false);
  const [isPressed, setIsPressed] = useState(false);

  const sizeConfig = {
    sm: {
      outerPadding: "p-[5px]",
      outerRadius: "rounded-[40px]",
      innerRadius: "rounded-[20px]",
      innerPadding: "pl-[22px] pr-[1px] pt-[1px] pb-[2px]",
      fontSize: "text-[13px]",
      outerCircleSize: "w-[38px] h-[38px]",
      innerCircleSize: "w-[22px] h-[22px]",
      iconSize: "w-[14px] h-[14px]",
      gap: "gap-[12px]",
      liftY: -3,
    },
    md: {
      outerPadding: "p-[6px]",
      outerRadius: "rounded-[50px]",
      innerRadius: "rounded-[24px]",
      innerPadding: "pl-[30px] pr-[1px] pt-[1px] pb-[3px]",
      fontSize: "text-[15px]",
      outerCircleSize: "w-[45px] h-[45px]",
      innerCircleSize: "w-[26px] h-[26px]",
      iconSize: "w-[16px] h-[16px]",
      gap: "gap-[15px]",
      liftY: -4,
    },
    lg: {
      outerPadding: "p-[8px]",
      outerRadius: "rounded-[60px]",
      innerRadius: "rounded-[28px]",
      innerPadding: "pl-[36px] pr-[2px] pt-[2px] pb-[4px]",
      fontSize: "text-[18px]",
      outerCircleSize: "w-[54px] h-[54px]",
      innerCircleSize: "w-[32px] h-[32px]",
      iconSize: "w-[20px] h-[20px]",
      gap: "gap-[18px]",
      liftY: -5,
    },
  }[size];

  const themeStyles = {
    dark: {
      outerBg: "bg-[rgba(209,213,236,0.14)]",
      outerShadow: "shadow-[0px_1.5px_0px_0px_white,inset_0px_0px_2px_0px_rgba(0,0,0,0.4)]",
      capBg: "bg-[#343434]",
      textColor: "text-[#F8F8F8]",
      textShadow: "0px 1px 1px rgba(0,0,0,0.5)",
      outerCircleBg: "bg-gradient-to-b from-[#1b1b1b] to-[#343434]",
      innerCircleBg: "bg-black",
      capBevel:
        "shadow-[inset_0px_2px_0px_0px_rgba(255,255,255,0.25),inset_0px_-5px_0px_0px_rgba(160,160,160,0.2),inset_0px_1px_0px_0px_rgba(255,255,255,0.8),inset_0px_-4px_0px_0px_black]",
      restingShadow:
        "drop-shadow(0px 3px 3px rgba(0,0,0,0.14)) drop-shadow(0px 6.65px 5.32px rgba(0,0,0,0.13)) drop-shadow(0px 12.5px 10px rgba(0,0,0,0.14)) drop-shadow(0px 22.3px 17.9px rgba(0,0,0,0.14)) drop-shadow(0px 41.8px 33.4px rgba(0,0,0,0.15)) drop-shadow(0px 100px 80px rgba(0,0,0,0.15))",
      hoverShadow:
        "drop-shadow(0px 4px 6px rgba(0,0,0,0.2)) drop-shadow(0px 14px 18px rgba(0,0,0,0.22)) drop-shadow(0px 36px 32px rgba(0,0,0,0.25))",
      iconColor: "text-white",
    },
    orange: {
      outerBg: "bg-[rgba(255,91,4,0.15)]",
      outerShadow: "shadow-[0px_1.5px_0px_0px_rgba(255,255,255,0.7),inset_0px_0px_3px_0px_rgba(255,91,4,0.4)]",
      capBg: "bg-[#FF5B04]",
      textColor: "text-white",
      textShadow: "0px 1px 2px rgba(0,0,0,0.4)",
      outerCircleBg: "bg-gradient-to-b from-[#CC4400] to-[#FF5B04]",
      innerCircleBg: "bg-[#B33B00]",
      capBevel:
        "shadow-[inset_0px_2px_0px_0px_rgba(255,255,255,0.45),inset_0px_-5px_0px_0px_rgba(180,45,0,0.4),inset_0px_1px_0px_0px_rgba(255,255,255,0.9),inset_0px_-4px_0px_0px_rgba(120,30,0,0.8)]",
      restingShadow:
        "drop-shadow(0px 4px 6px rgba(255,91,4,0.25)) drop-shadow(0px 14px 18px rgba(255,91,4,0.2))",
      hoverShadow:
        "drop-shadow(0px 6px 12px rgba(255,91,4,0.35)) drop-shadow(0px 22px 28px rgba(255,91,4,0.3))",
      iconColor: "text-white",
    },
    light: {
      outerBg: "bg-[rgba(0,0,0,0.06)]",
      outerShadow: "shadow-[0px_1.5px_0px_0px_white,inset_0px_0px_2px_0px_rgba(0,0,0,0.15)]",
      capBg: "bg-[#FAFAFA]",
      textColor: "text-gray-900",
      textShadow: "0px 1px 0px rgba(255,255,255,0.8)",
      outerCircleBg: "bg-gradient-to-b from-[#E2E2E2] to-[#FAFAFA]",
      innerCircleBg: "bg-[#222222]",
      capBevel:
        "shadow-[inset_0px_2px_0px_0px_rgba(255,255,255,0.9),inset_0px_-4px_0px_0px_rgba(0,0,0,0.1),inset_0px_1px_0px_0px_white,inset_0px_-2px_0px_0px_rgba(0,0,0,0.15)]",
      restingShadow:
        "drop-shadow(0px 3px 4px rgba(0,0,0,0.08)) drop-shadow(0px 10px 12px rgba(0,0,0,0.06))",
      hoverShadow:
        "drop-shadow(0px 6px 10px rgba(0,0,0,0.12)) drop-shadow(0px 18px 20px rgba(0,0,0,0.09))",
      iconColor: "text-white",
    },
    cyberpunk: {
      outerBg: "bg-[rgba(0,229,190,0.12)]",
      outerShadow: "shadow-[0px_1.5px_0px_0px_rgba(0,229,190,0.5),inset_0px_0px_4px_0px_rgba(0,229,190,0.35)]",
      capBg: "bg-[#111827]",
      textColor: "text-[#E0F2FE]",
      textShadow: "0px 0px 8px rgba(0,229,190,0.5)",
      outerCircleBg: "bg-gradient-to-b from-[#090E1A] to-[#111827]",
      innerCircleBg: "bg-black",
      capBevel:
        "shadow-[inset_0px_2px_0px_0px_rgba(0,229,190,0.4),inset_0px_-5px_0px_0px_rgba(0,0,0,0.6),inset_0px_1px_0px_0px_rgba(0,229,190,0.8),inset_0px_-4px_0px_0px_black]",
      restingShadow:
        "drop-shadow(0px 4px 6px rgba(0,0,0,0.4)) drop-shadow(0px 10px 16px rgba(0,229,190,0.15))",
      hoverShadow:
        "drop-shadow(0px 6px 12px rgba(0,0,0,0.5)) drop-shadow(0px 18px 24px rgba(0,229,190,0.3))",
      iconColor: "text-[#00E5BE]",
    },
  }[variant];

  return (
    <div
      className={`relative inline-flex items-center justify-center select-none ${sizeConfig.outerPadding} ${sizeConfig.outerRadius} ${themeStyles.outerBg} ${themeStyles.outerShadow} transition-all duration-300 ${className}`}
      onMouseEnter={() => setIsHovered(true)}
      onMouseLeave={() => {
        setIsHovered(false);
        setIsPressed(false);
      }}
    >
      <motion.button
        type="button"
        disabled={disabled}
        onClick={onClick}
        onMouseDown={() => setIsPressed(true)}
        onMouseUp={() => setIsPressed(false)}
        onTouchStart={() => setIsPressed(true)}
        onTouchEnd={() => setIsPressed(false)}
        animate={{
          y: isPressed ? 1.5 : isHovered ? sizeConfig.liftY : 0,
          scale: isPressed ? 0.985 : 1,
        }}
        transition={{
          type: "spring",
          stiffness: 400,
          damping: 24,
          mass: 0.7,
        }}
        className={`relative flex items-center ${sizeConfig.gap} ${sizeConfig.innerPadding} ${sizeConfig.innerRadius} ${themeStyles.capBg} cursor-pointer focus:outline-none overflow-hidden`}
        style={{
          filter: isHovered ? themeStyles.hoverShadow : themeStyles.restingShadow,
          willChange: "transform",
        }}
      >
        <span
          className={`font-semibold ${sizeConfig.fontSize} ${themeStyles.textColor} whitespace-nowrap tracking-[-0.2px] leading-none select-none`}
          style={{
            textShadow: themeStyles.textShadow,
          }}
        >
          {label}
        </span>

        <div
          className={`relative shrink-0 ${sizeConfig.outerCircleSize} rounded-full ${themeStyles.outerCircleBg} flex items-center justify-center`}
        >
          <div
            className={`shrink-0 ${sizeConfig.innerCircleSize} rounded-full ${themeStyles.innerCircleBg} flex items-center justify-center`}
          >
            <div className={themeStyles.iconColor}>
              {icon || <ApexEmblemIcon className={sizeConfig.iconSize} />}
            </div>
          </div>
        </div>

        <div
          className={`absolute inset-0 pointer-events-none ${sizeConfig.innerRadius} ${themeStyles.capBevel}`}
        />
      </motion.button>
    </div>
  );
};

export default ScalingCapsuleButton;

Component API Reference

PropTypeDefaultDescription
labelstring"Scaling Workshop"Text displayed on the button cap
variant"dark" | "orange" | "light" | "cyberpunk""dark"Visual colorway theme style
size"xs" | "sm" | "md" | "lg" | "xl""md"Physical scaling size scale
iconReact.ReactNode<ApexEmblemIcon />Custom icon or emblem component
onClick() => voidundefinedClick callback event handler
disabledbooleanfalseDisables interaction and hover states
classNamestring""Additional custom Tailwind CSS classes

Theme Presets

Obsidian Core

Matte #343434 cap with 26px black circle badge

variant: darkReady to Use

UI Pirate Brand

High energy brand orange with deep cavity glow

variant: orangeReady to Use

Porcelain Minimalist

Clean light ceramic cap for bright SaaS products

variant: lightReady to Use

Cyberpunk Apex

Deep obsidian cap with cyan rim and glowing icon

variant: cyberpunkReady to Use

Personalized Help

Struggling with tactile capsule buttons or design systems? Let's talk about your product.

UI Pirate is a product design & development agency trusted by 50+ SaaS founders and enterprise teams across the US, UK & beyond. Tell us what you need.