---
title: Fluid Action Panel
description: A professional, highly interactive floating utility widget that morphs between a collapsed progress trigger and an expanded glassmorphic settings dashboard. Features page scroll progress tracking, custom theme togglers, and clean copy-link and social sharing shortcuts with responsive touch controls.
---
## Installation
CLI
Manual
```bash
npx lightswind@latest add fluid-action-panel
```
Copy and paste the following code into your project.
```tsx
"use client";
import React, { useState, useEffect, useRef } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { Settings2, X, Sun, Moon, Share2, Link2, Twitter, Compass, ArrowUp, Check, Monitor } from "lucide-react";
import { cn } from "@/components/lib/utils";
export interface FluidActionPanelProps {
position?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
className?: string;
onThemeToggle?: () => void;
isDark?: boolean;
}
export const FluidActionPanel = ({
position = "bottom-right",
className,
onThemeToggle,
isDark = false
}: FluidActionPanelProps) => {
const [isOpen, setIsOpen] = useState(false);
const [scrollProgress, setScrollProgress] = useState(0);
const [copied, setCopied] = useState(false);
const containerRef = useRef(null);
// Track scroll position for reading progress
useEffect(() => {
const handleScroll = () => {
const totalScroll = document.documentElement.scrollHeight - window.innerHeight;
if (totalScroll > 0) {
const progress = (window.scrollY / totalScroll) * 100;
setScrollProgress(Math.min(progress, 100));
}
};
window.addEventListener("scroll", handleScroll);
handleScroll();
return () => window.removeEventListener("scroll", handleScroll);
}, []);
// Close on click outside
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
setIsOpen(false);
}
};
if (isOpen) {
document.addEventListener("mousedown", handleClickOutside);
}
return () => document.removeEventListener("mousedown", handleClickOutside);
}, [isOpen]);
const handleCopyLink = () => {
navigator.clipboard.writeText(window.location.href);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
const handleScrollToTop = () => {
window.scrollTo({ top: 0, behavior: "smooth" });
};
const positionClasses = {
"bottom-right": "bottom-6 right-6",
"bottom-left": "bottom-6 left-6",
"top-right": "top-6 right-6",
"top-left": "top-6 left-6",
};
// SVG circular path metrics
const radius = 26;
const stroke = 2.5;
const normalizedRadius = radius - stroke * 2;
const circumference = normalizedRadius * 2 * Math.PI;
const strokeDashoffset = circumference - (scrollProgress / 100) * circumference;
// Stagger configurations for child elements
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.05,
delayChildren: 0.05
}
}
};
const itemVariants = {
hidden: { opacity: 0, y: 10 },
visible: { opacity: 1, y: 0, transition: { type: "spring" as const, stiffness: 300, damping: 25 } }
};
return (
{/* Toggle Button wrapper (Always rendered at the top-left coordinate space of the layout container) */}
setIsOpen(!isOpen)}
className="w-14 h-14 flex items-center justify-center relative focus:outline-none z-25 group"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
{/* SVG Progress Ring */}
{/* Morphing Icon */}
{!isOpen ? (
) : (
)}
{/* Action Panel Content (Animate in only when open) */}
{isOpen && (
{/* Header Info */}
System Hub
Page Navigator
{/* Progress Indicator Card */}
Progress
{Math.round(scrollProgress)}% Scrolled
{/* Toggle controls grid */}
{/* Theme Controller */}
{/* Link Copy Widget */}
{/* Share hub row */}
Quick Share
Twitter
{/* Footer specs */}
Lightswind UI
v1.2.6
)}
);
};
```
## Usage
```tsx
import { FluidActionPanel } from "@/components/lightswind/fluid-action-panel";
```
```tsx
import { FluidActionPanel } from "@/components/lightswind/fluid-action-panel";
export default function App() {
const [isDark, setIsDark] = useState(false);
return (
{/* Content here */}
setIsDark(!isDark)}
/>
);
}
```