Brightness Control
An interactive brightness control component with animated transitions, providing a user-friendly interface for adjusting brightness levels from 0-6.
Preview
Brightness Control
Installation
Install dependencies
# Install Shadcn Button component
npx shadcn@latest add button
# Install Framer Motion
npm install framer-motion
# Install Lucide React for icons
npm install lucide-react
Create the component file
mkdir -p components/shsfui/controls && touch components/shsfui/controls/brightness-control.tsx
Add the component code
Open the newly created file and paste the following code:
"use client";
import * as React from "react";
import { useState } from "react";
import {
MinusIcon,
PlusIcon,
SunIcon,
SunDimIcon,
MoonIcon,
} from "lucide-react";
import { motion, AnimatePresence } from "framer-motion";
import { Button } from "@/components/ui/button";
import { cn } from "@/utils/cn";
type BrightnessControlProps = {
className?: string;
initialBrightness?: number;
minBrightness?: number;
maxBrightness?: number;
onChange?: (brightness: number) => void;
};
const BrightnessControl = React.forwardRef<
HTMLDivElement,
BrightnessControlProps
>((props, ref) => {
const {
className,
initialBrightness = 3,
minBrightness = 0,
maxBrightness = 6,
onChange,
...restProps
} = props;
const [brightness, setBrightness] = useState(initialBrightness);
const handleBrightnessChange = (newBrightness: number) => {
setBrightness(newBrightness);
onChange?.(newBrightness);
};
const decreaseBrightness = () => {
if (brightness > minBrightness) {
handleBrightnessChange(brightness - 1);
}
};
const increaseBrightness = () => {
if (brightness < maxBrightness) {
handleBrightnessChange(brightness + 1);
}
};
const getBrightnessIcon = () => {
if (brightness === minBrightness) return MoonIcon;
if (brightness < maxBrightness / 2) return SunDimIcon;
return SunIcon;
};
const dynamicOpacity =
brightness === minBrightness
? 0.3
: 0.4 + (brightness / maxBrightness) * 0.6;
const numberVariants = {
initial: { opacity: 0, y: 20 },
animate: { opacity: dynamicOpacity, y: 0 },
exit: { opacity: 0, y: -20 },
};
const buttonVariants = {
tap: { scale: 0.9 },
hover: { scale: 1.05 },
};
const Icon = getBrightnessIcon();
return (
<div
ref={ref}
className={cn("inline-flex items-center", className)}
role="group"
aria-labelledby="brightness-control"
{...restProps}
>
<span id="brightness-control" className="sr-only">
Brightness Control
</span>
<motion.div variants={buttonVariants} whileHover="hover" whileTap="tap">
<Button
className="rounded-full"
variant="outline"
size="icon"
aria-label="Decrease brightness"
onClick={decreaseBrightness}
disabled={brightness === minBrightness}
type="button"
>
<MinusIcon size={16} aria-hidden="true" />
</Button>
</motion.div>
<div
className="flex items-center px-4 text-sm font-medium"
aria-live="polite"
>
<div className="flex items-center">
<motion.div
key={`icon-${brightness}`}
initial={{ opacity: dynamicOpacity }}
animate={{ opacity: dynamicOpacity }}
transition={{ duration: 0.2 }}
className="mr-2"
>
<Icon size={16} aria-hidden="true" />
</motion.div>
<div className="relative h-6 w-4 overflow-hidden">
<AnimatePresence mode="popLayout" initial={false}>
<motion.span
key={brightness}
className="absolute inset-0 flex items-center justify-center"
variants={numberVariants}
initial="initial"
animate="animate"
exit="exit"
transition={{ duration: 0.2 }}
aria-hidden="true"
>
{brightness}
</motion.span>
</AnimatePresence>
</div>
</div>
</div>
<motion.div variants={buttonVariants} whileHover="hover" whileTap="tap">
<Button
className="rounded-full"
variant="outline"
size="icon"
aria-label="Increase brightness"
onClick={increaseBrightness}
disabled={brightness === maxBrightness}
type="button"
>
<PlusIcon size={16} aria-hidden="true" />
</Button>
</motion.div>
</div>
);
});
BrightnessControl.displayName = "BrightnessControl";
export BrightnessControl;