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;