Mantine Led

Logo

@gfazioli/mantine-led

A highly customizable LED indicator component for React applications built with Mantine. Provides visual feedback for status indicators with support for colors, sizes, animations, and controlled states.

Installation

yarn add @gfazioli/mantine-led

After installation import package styles at the root of your application:

import '@gfazioli/mantine-led/styles.css';

You can import styles within a layer @layer mantine-led by importing @gfazioli/mantine-led/styles.layer.css file.

import '@gfazioli/mantine-led/styles.layer.css';

Usage

Explore all available props and see the component in action:

Variant
Shape
Color
Off color
Size
Radius
Intensity
Animation duration
Animation count
Animation delay
Label position
import { Led } from '@gfazioli/mantine-led';

function Demo() {
  return (
    <Led/>
  );
}

Controlled

The Led component is fully controlled through the value prop. Here's an example of controlling the LED state:

import { useState } from 'react';
import { Button, Group, Stack } from '@mantine/core';
import { Led } from '@gfazioli/mantine-led';

function Demo() {
  const [value, { open, close, toggle }] = useDisclosure(false);

  return (
    <Stack align="center">
      <Led value={value} size="lg" variant="3d" />
      <Group>
        <Button onClick={open} variant="light" color="green">
          Turn On
        </Button>
        <Button onClick={close} variant="light" color="red">
          Turn Off
        </Button>
        <Button onClick={toggle} variant="light">
          Toggle
        </Button>
      </Group>
    </Stack>
  );
}

Variants

The Led component supports four visual variants: flat (default), 3d (realistic with depth and specular highlight), neon (glowing neon sign style), and dot (ultra-minimal):

flat

3d

neon

dot

import { Group, Stack, Text } from '@mantine/core';
import { Led } from '@gfazioli/mantine-led';

function Demo() {
  return (
    <Group>
      <Stack align="center" gap="xs">
        <Led variant="flat" size="xl" />
        <Text size="xs">Flat</Text>
      </Stack>
      <Stack align="center" gap="xs">
        <Led variant="3d" size="xl" />
        <Text size="xs">3D</Text>
      </Stack>
      <Stack align="center" gap="xs">
        <Led variant="neon" size="xl" />
        <Text size="xs">Neon</Text>
      </Stack>
      <Stack align="center" gap="xs">
        <Led variant="dot" size="xl" />
        <Text size="xs">Dot</Text>
      </Stack>
    </Group>
  );
}

Colors

The Led component supports all Mantine theme colors. Use the color prop to set the LED color:

import { Group } from '@mantine/core';
import { Led } from '@gfazioli/mantine-led';

function Demo() {
  return (
    <Group>
      <Led color="red" />
      <Led color="green" />
      <Led color="blue" />
      <Led color="yellow" />
      <Led color="orange" />
      <Led color="cyan" />
      <Led color="pink" />
      <Led color="violet" />
    </Group>
  );
}

Off Color

Use the offColor prop to customize the LED appearance when turned off. By default, the off state uses a dimmed version of the main color.

Off (red)

On (green)

Off 3D (gray)

On 3D (blue)

import { Group, Stack, Text } from '@mantine/core';
import { Led } from '@gfazioli/mantine-led';

function Demo() {
  return (
    <Group>
      <Stack align="center" gap="xs">
        <Led value={false} offColor="red" color="green" size="lg" />
        <Text size="xs">Off (red)</Text>
      </Stack>
      <Stack align="center" gap="xs">
        <Led value offColor="red" color="green" size="lg" />
        <Text size="xs">On (green)</Text>
      </Stack>
      <Stack align="center" gap="xs">
        <Led value={false} offColor="gray" color="blue" size="lg" variant="3d" />
        <Text size="xs">Off 3D (gray)</Text>
      </Stack>
      <Stack align="center" gap="xs">
        <Led value offColor="gray" color="blue" size="lg" variant="3d" />
        <Text size="xs">On 3D (blue)</Text>
      </Stack>
    </Group>
  );
}

Gradient

Use the gradient prop to apply a linear gradient to the LED when on. Accepts a MantineGradient object with from, to, and optional deg properties:

Fire

Ice

3D Gradient

Neon Gradient

import { Group, Stack, Text } from '@mantine/core';
import { Led } from '@gfazioli/mantine-led';

function Demo() {
  return (
    <Group>
      <Stack align="center" gap="xs">
        <Led gradient={{ from: 'red', to: 'orange', deg: 45 }} size="xl" />
        <Text size="xs">Fire</Text>
      </Stack>
      <Stack align="center" gap="xs">
        <Led gradient={{ from: 'blue', to: 'cyan', deg: 90 }} size="xl" />
        <Text size="xs">Ice</Text>
      </Stack>
      <Stack align="center" gap="xs">
        <Led gradient={{ from: 'grape', to: 'pink', deg: 135 }} size="xl" variant="3d" />
        <Text size="xs">3D Gradient</Text>
      </Stack>
      <Stack align="center" gap="xs">
        <Led gradient={{ from: 'green', to: 'lime' }} size="xl" variant="neon" />
        <Text size="xs">Neon Gradient</Text>
      </Stack>
    </Group>
  );
}

Shapes

The shape prop controls the LED form factor. Available shapes are circle (default), square, and rectangle:

Circle

Square

Rectangle

Circle 3D

Square 3D

Rectangle 3D

import { Group, Stack, Text } from '@mantine/core';
import { Led } from '@gfazioli/mantine-led';

function Demo() {
  return (
    <Group>
      <Stack align="center" gap="xs">
        <Led shape="circle" size="lg" />
        <Text size="xs">Circle</Text>
      </Stack>
      <Stack align="center" gap="xs">
        <Led shape="square" size="lg" />
        <Text size="xs">Square</Text>
      </Stack>
      <Stack align="center" gap="xs">
        <Led shape="rectangle" size="lg" />
        <Text size="xs">Rectangle</Text>
      </Stack>
      <Stack align="center" gap="xs">
        <Led shape="circle" size="lg" variant="3d" />
        <Text size="xs">Circle 3D</Text>
      </Stack>
      <Stack align="center" gap="xs">
        <Led shape="square" size="lg" variant="3d" />
        <Text size="xs">Square 3D</Text>
      </Stack>
      <Stack align="center" gap="xs">
        <Led shape="rectangle" size="lg" variant="3d" />
        <Text size="xs">Rectangle 3D</Text>
      </Stack>
    </Group>
  );
}

Animations

Add visual feedback with built-in animations. The component supports multiple animation types:

  • pulse: Smooth pulsing effect
  • flash: Quick flashing
  • breathe: Slow breathing effect
  • blink: Regular blinking
  • glow: Glowing effect with increased intensity

Pulse

Flash

Breathe

Blink

Glow

import { Group, Stack, Text } from '@mantine/core';
import { Led } from '@gfazioli/mantine-led';

function Demo() {
  return (
    <Group>
      <Stack align="center" gap="xs">
        <Led animate animationType="pulse" size="lg" />
        <Text size="xs">Pulse</Text>
      </Stack>
      <Stack align="center" gap="xs">
        <Led animate animationType="flash" size="lg" color="red" />
        <Text size="xs">Flash</Text>
      </Stack>
      <Stack align="center" gap="xs">
        <Led animate animationType="breathe" size="lg" color="blue" />
        <Text size="xs">Breathe</Text>
      </Stack>
      <Stack align="center" gap="xs">
        <Led animate animationType="blink" size="lg" color="yellow" />
        <Text size="xs">Blink</Text>
      </Stack>
      <Stack align="center" gap="xs">
        <Led animate animationType="glow" size="lg" color="cyan" />
        <Text size="xs">Glow</Text>
      </Stack>
    </Group>
  );
}

Animation Count

Use the animationCount prop to limit the number of animation iterations. By default, animations loop infinitely. When a count is set, the animation stops after the specified number of cycles:

3 flashes

5 pulses

Infinite

import { useState } from 'react';
import { Button, Group, Stack, Text } from '@mantine/core';
import { Led } from '@gfazioli/mantine-led';

function Demo() {
  const [key, setKey] = useState(0);

  return (
    <Stack align="center" gap="md">
      <Group>
        <Stack align="center" gap="xs">
          <Led animate animationType="flash" animationCount={3} size="lg" color="red" key={`a-${key}`} />
          <Text size="xs">3 flashes</Text>
        </Stack>
        <Stack align="center" gap="xs">
          <Led animate animationType="pulse" animationCount={5} size="lg" color="blue" key={`b-${key}`} />
          <Text size="xs">5 pulses</Text>
        </Stack>
        <Stack align="center" gap="xs">
          <Led animate animationType="glow" size="lg" color="green" />
          <Text size="xs">Infinite</Text>
        </Stack>
      </Group>
      <Button variant="light" size="xs" onClick={() => setKey((k) => k + 1)}>
        Replay finite animations
      </Button>
    </Stack>
  );
}

Interactive

Provide an onChange callback to make the LED clickable and toggleable. The LED becomes keyboard-accessible with role="switch" and responds to click, Enter, and Space:

Click the LEDs to toggle them

Power
Network
import { useState } from 'react';
import { Group, Stack, Text } from '@mantine/core';
import { Led } from '@gfazioli/mantine-led';

function Demo() {
  const [value1, setValue1] = useState(true);
  const [value2, setValue2] = useState(false);

  return (
    <Stack align="center" gap="md">
      <Text size="sm" c="dimmed">Click the LEDs to toggle them</Text>
      <Group>
        <Led
          value={value1}
          onChange={setValue1}
          size="xl"
          variant="3d"
          label="Power"
        />
        <Led
          value={value2}
          onChange={setValue2}
          size="xl"
          variant="3d"
          color="blue"
          label="Network"
        />
      </Group>
    </Stack>
  );
}

Tooltip

Use the tooltip prop to display a tooltip on hover. This is useful for compact dashboards where LEDs are small and need contextual information. Use tooltipProps to customize the underlying Mantine Tooltip component — you can control position, withArrow, color, multiline, and any other Tooltip prop:

With tooltipProps

import { Group, Stack, Text } from '@mantine/core';
import { Led } from '@gfazioli/mantine-led';

function Demo() {
  return (
    <Stack gap="lg" align="center">
      <Group>
        <Led tooltip="Server is online" color="green" size="lg" />
        <Led tooltip="Database connected" color="blue" size="lg" variant="3d" />
        <Led tooltip="Service unavailable" value={false} color="red" size="lg" />
      </Group>

      <Text size="sm" c="dimmed">With tooltipProps</Text>
      <Group>
        <Led
          tooltip="Uptime: 99.9%"
          tooltipProps={{ position: 'bottom', withArrow: true, color: 'green' }}
          color="green"
          size="lg"
          variant="3d"
        />
        <Led
          tooltip="Warning: high latency"
          tooltipProps={{ position: 'right', withArrow: true, color: 'yellow' }}
          color="yellow"
          size="lg"
          variant="3d"
        />
        <Led
          tooltip="Offline since 2h"
          tooltipProps={{ position: 'bottom', withArrow: true, color: 'red', multiline: true, w: 150 }}
          value={false}
          color="red"
          size="lg"
          variant="3d"
        />
      </Group>
    </Stack>
  );
}

Labels

Add descriptive labels to your LED indicators with full customization support:

Power
Standby
Online
Active
Custom Label
import { Badge, Group, Stack } from '@mantine/core';
import { Led } from '@gfazioli/mantine-led';

function Demo() {
  return (
    <Stack gap="lg">
      <Group>
        <Led label="Power" />
        <Led value={false} label="Standby" color="gray" />
      </Group>

      <Group>
        <Led label="Online" labelPosition="left" color="green" />
        <Led label="Active" labelPosition="right" color="blue" />
      </Group>

      <Group>
        <Led
          label={<Badge size="sm" variant="light">Custom Label</Badge>}
          color="violet"
        />
      </Group>
    </Stack>
  );
}

The label prop accepts any React node, allowing you to use custom components like badges, icons, or formatted text. Use labelPosition to control whether the label appears on the left or right side of the LED.

Led.Group

Use Led.Group to render multiple LEDs with shared props. Pass value as a number to activate the first N LEDs, or as a boolean array for individual control:

Signal strength: 3/5

import { useState } from 'react';
import { Group, Slider, Stack, Text } from '@mantine/core';
import { Led } from '@gfazioli/mantine-led';

function Demo() {
  const [level, setLevel] = useState(3);

  return (
    <Stack gap="lg" align="center">
      <Text size="sm" fw={500}>Signal strength: {level}/5</Text>
      <Led.Group value={level} count={5} color="green" size="md" variant="3d" />
      <Led.Group value={level} count={5} color="cyan" size="sm" shape="rectangle" />
      <Led.Group
        value={[true, false, true, true, false]}
        color="red"
        size="md"
        animate
        animationType="pulse"
      />
      <Slider
        value={level}
        onChange={setLevel}
        min={0}
        max={5}
        step={1}
        w={200}
        label={(v) => `${v}/5`}
      />
    </Stack>
  );
}

Color Scale

Use colorScale to map different colors progressively across LEDs — perfect for VU meters and level indicators:

VU Meter: 7/10

import { useState } from 'react';
import { Slider, Stack, Text } from '@mantine/core';
import { Led } from '@gfazioli/mantine-led';

function Demo() {
  const [level, setLevel] = useState(7);

  return (
    <Stack align="center" gap="md">
      <Text size="sm" fw={500}>VU Meter: {level}/10</Text>
      <Led.Group
        value={level}
        count={10}
        colorScale={['green', 'green', 'green', 'yellow', 'yellow', 'orange', 'red', 'red']}
        size="md"
        shape="rectangle"
        variant="flat"
      />
      <Slider value={level} onChange={setLevel} min={0} max={10} step={1} w={250} />
    </Stack>
  );
}

Cascade Animation

Use animationDelay on Led.Group to create staggered cascade effects. Each LED starts its animation with a progressive delay:

Cascade animation (staggered delay)

import { Stack, Text } from '@mantine/core';
import { Led } from '@gfazioli/mantine-led';

function Demo() {
  return (
    <Stack align="center" gap="md">
      <Text size="sm" fw={500}>Cascade animation (staggered delay)</Text>
      <Led.Group
        value={5}
        count={5}
        animate
        animationType="pulse"
        animationDelay={0.2}
        size="lg"
        color="cyan"
        variant="3d"
      />
      <Led.Group
        value={8}
        count={8}
        animate
        animationType="glow"
        animationDelay={0.15}
        size="md"
        color="violet"
        variant="neon"
      />
    </Stack>
  );
}

Led.Matrix

Use Led.Matrix to render a 2D grid of LEDs. Pass a boolean[][] array to control individual LED states:

Heart (dot)

Heart (flat)

4x4 all on

import { Stack, Text } from '@mantine/core';
import { Led } from '@gfazioli/mantine-led';

const heartPattern = [
  [false, true, false, true, false],
  [true, true, true, true, true],
  [true, true, true, true, true],
  [false, true, true, true, false],
  [false, false, true, false, false],
];

function Demo() {
  return (
    <Group>
      <Stack align="center" gap="xs">
        <Led.Matrix value={heartPattern} color="red" size="sm" />
        <Text size="xs">Heart (dot)</Text>
      </Stack>
      <Stack align="center" gap="xs">
        <Led.Matrix value={heartPattern} color="red" size="sm" variant="flat" shape="circle" />
        <Text size="xs">Heart (flat)</Text>
      </Stack>
      <Stack align="center" gap="xs">
        <Led.Matrix rows={4} cols={4} color="green" size="xs" />
        <Text size="xs">4x4 all on</Text>
      </Stack>
    </Group>
  );
}

Led.SevenSegment

Use Led.SevenSegment to display numbers and characters using a classic seven-segment display. Supports digits 0-9, hex characters A-F, and special characters like :, ., -:

Number

Clock

Text

import { Group, Stack, Text } from '@mantine/core';
import { Led } from '@gfazioli/mantine-led';

function Demo() {
  return (
    <Group>
      <Stack align="center" gap="xs">
        <Led.SevenSegment value={42} color="red" size="lg" />
        <Text size="xs">Number</Text>
      </Stack>
      <Stack align="center" gap="xs">
        <Led.SevenSegment value="12:30" color="green" size="md" />
        <Text size="xs">Clock</Text>
      </Stack>
      <Stack align="center" gap="xs">
        <Led.SevenSegment value="HE-LP" color="cyan" size="sm" />
        <Text size="xs">Text</Text>
      </Stack>
    </Group>
  );
}

Live Clock

A real-time clock using Led.SevenSegment with hours, minutes, and seconds:

Live clock

import { useEffect, useState } from 'react';
import { Paper, Stack, Text } from '@mantine/core';
import { Led } from '@gfazioli/mantine-led';

function formatTime(date: Date): string {
  const h = String(date.getHours()).padStart(2, '0');
  const m = String(date.getMinutes()).padStart(2, '0');
  const s = String(date.getSeconds()).padStart(2, '0');
  return `${h}:${m}:${s}`;
}

function Demo() {
  const [time, setTime] = useState('00:00:00');

  useEffect(() => {
    setTime(formatTime(new Date()));
    const interval = setInterval(() => setTime(formatTime(new Date())), 1000);
    return () => clearInterval(interval);
  }, []);

  return (
    <Paper p="xl" radius="md" withBorder>
      <Stack align="center" gap="xs">
        <Led.SevenSegment value={time} color="red" size="xl" />
        <Text size="xs" c="dimmed">Live clock</Text>
      </Stack>
    </Paper>
  );
}

Styles API

Led supports Styles API, you can add styles to any inner element of the component with classNames prop. Follow Styles API documentation to learn more.

Example Label

Component Styles API

Hover over selectors to highlight corresponding elements

/*
 * Hover over selectors to apply outline styles
 *
 */

Accessibility

The Led component follows WAI-ARIA best practices:

  • Non-interactive LEDs use role="status" with optional aria-label from string labels
  • Interactive LEDs (with onChange) use role="switch" with aria-checked and keyboard support (Enter, Space)
  • Use the description prop to add aria-description for screen readers
  • Animations are automatically disabled when the user has prefers-reduced-motion: reduce enabled

Use Cases

System Status Panel

Monitor multiple system components with organized LED indicators:

System Status Panel

Network

Internet
LAN
VPN

Services

Database
API Server
Cache

Resources

CPU Load
Memory
Disk Space

Security

Firewall
SSL Cert
Auth Service
import { Paper, SimpleGrid, Stack, Text, Title } from '@mantine/core';
import { Led } from '@gfazioli/mantine-led';

function Demo() {
  return (
    <Paper p="xl" withBorder>
      <Stack gap="lg">
        <Title order={3}>System Status Panel</Title>

        <SimpleGrid cols={2}>
          <Stack gap="md">
            <Text fw={800}>Network</Text>
            <Paper withBorder p="md">
              <Stack gap="xs">
                <Led
                  label="Internet"
                  color="green"
                  labelPosition="left"
                  justify="space-between"
                  variant="3d"
                  tooltip="Connected — 142ms latency"
                />
                <Led
                  label="LAN"
                  color="green"
                  labelPosition="left"
                  justify="space-between"
                  variant="3d"
                  tooltip="192.168.1.0/24"
                />
                <Led
                  value={false}
                  label="VPN"
                  offColor="red"
                  color="green"
                  labelPosition="left"
                  justify="space-between"
                  variant="3d"
                  tooltip="Disconnected"
                />
              </Stack>
            </Paper>
          </Stack>

          <Stack gap="md">
            <Text fw={800}>Services</Text>
            <Paper withBorder p="md">
              <Stack gap="xs">
                <Led
                  label="Database"
                  color="green"
                  labelPosition="left"
                  animate
                  animationType="blink"
                  animationCount={3}
                  justify="space-between"
                  variant="3d"
                  tooltip="Replication in progress..."
                />
                <Led
                  label="API Server"
                  color="green"
                  labelPosition="left"
                  justify="space-between"
                  variant="3d"
                  tooltip="Uptime: 14d 3h"
                />
                <Led
                  label="Cache"
                  color="yellow"
                  labelPosition="left"
                  justify="space-between"
                  variant="3d"
                  tooltip="Hit rate: 78% — consider scaling"
                  tooltipProps={{ color: 'yellow' }}
                />
              </Stack>
            </Paper>
          </Stack>

          <Stack gap="md">
            <Text fw={800}>Resources</Text>
            <Paper withBorder p="md">
              <Stack gap="xs">
                <Led
                  label="CPU Load"
                  color="green"
                  labelPosition="left"
                  justify="space-between"
                  variant="3d"
                  tooltip="12% average"
                />
                <Led
                  label="Memory"
                  color="yellow"
                  labelPosition="left"
                  justify="space-between"
                  variant="3d"
                  tooltip="6.2 / 8 GB used"
                  tooltipProps={{ color: 'yellow' }}
                />
                <Led
                  label="Disk Space"
                  color="red"
                  labelPosition="left"
                  animate
                  animationType="flash"
                  animationCount={5}
                  justify="space-between"
                  variant="3d"
                  tooltip="92% full — action required"
                  tooltipProps={{ color: 'red' }}
                />
              </Stack>
            </Paper>
          </Stack>

          <Stack gap="md">
            <Text fw={800}>Security</Text>
            <Paper withBorder p="md">
              <Stack gap="xs">
                <Led
                  label="Firewall"
                  color="green"
                  labelPosition="left"
                  justify="space-between"
                  variant="3d"
                  tooltip="All rules active"
                />
                <Led
                  label="SSL Cert"
                  color="green"
                  labelPosition="left"
                  justify="space-between"
                  value={false}
                  offColor="red"
                  variant="3d"
                  tooltip="Expired 2 days ago!"
                  tooltipProps={{ color: 'red' }}
                />
                <Led
                  label="Auth Service"
                  color="green"
                  labelPosition="left"
                  justify="space-between"
                  variant="3d"
                  tooltip="OAuth 2.0 — 0 failed attempts"
                />
              </Stack>
            </Paper>
          </Stack>
        </SimpleGrid>
      </Stack>
    </Paper>
  );
}

DEFCON Alert System

A fun example showing how to build a DEFCON (Defense Readiness Condition) alert system with animated LEDs:

Defense Readiness Condition

DEFCON 5

Normal readiness

DEFCON 4

Increased intelligence watch

DEFCON 3

Increase in force readiness

DEFCON 2

Further increase in force readiness

DEFCON 1

Maximum force readiness

import { useState } from 'react';
import { Button, Group, Paper, Stack, Text, Title } from '@mantine/core';
import { Led } from '@gfazioli/mantine-led';

const DEFCON_LEVELS = [
  { level: 5, color: 'blue', label: 'DEFCON 5', description: 'Normal readiness' },
  { level: 4, color: 'green', label: 'DEFCON 4', description: 'Increased intelligence watch' },
  { level: 3, color: 'yellow', label: 'DEFCON 3', description: 'Increase in force readiness' },
  { level: 2, color: 'orange', label: 'DEFCON 2', description: 'Further increase in force readiness' },
  { level: 1, color: 'red', label: 'DEFCON 1', description: 'Maximum force readiness' },
];

function Demo() {
  const [currentLevel, setCurrentLevel] = useState(5);

  return (
    <Paper p="xl" withBorder>
      <Stack gap="lg">
        <Title order={3} ta="center">Defense Readiness Condition</Title>

        <Stack gap="md">
          {DEFCON_LEVELS.map(({ level, color, label, description }) => (
            <Group key={level} justify="space-between">
              <Group gap="md">
                <Led
                  value={currentLevel === level}
                  color={color}
                  offColor="gray"
                  size="lg"
                  variant="3d"
                  animate={currentLevel === level && level <= 2}
                  animationType={level === 1 ? 'flash' : 'pulse'}
                  animationCount={level === 1 ? 6 : undefined}
                  tooltip={`Level ${level}: ${description}`}
                />
                <div>
                  <Text fw={currentLevel === level ? 700 : 400} size="sm">
                    {label}
                  </Text>
                  <Text size="xs" c="dimmed">
                    {description}
                  </Text>
                </div>
              </Group>
            </Group>
          ))}
        </Stack>

        <Group justify="center">
          <Button.Group>
            {[5, 4, 3, 2, 1].map((level) => (
              <Button
                key={level}
                onClick={() => setCurrentLevel(level)}
                variant={currentLevel === level ? 'filled' : 'default'}
                size="xs"
              >
                {level}
              </Button>
            ))}
          </Button.Group>
        </Group>
      </Stack>
    </Paper>
  );
}

Traffic Light

An automatic traffic light that cycles through red, yellow, and green with realistic timing. Demonstrates offColor, variant="3d", animate, and tooltip:

red

import { useEffect, useState } from 'react';
import { Button, Group, Paper, Stack, Text } from '@mantine/core';
import { Led } from '@gfazioli/mantine-led';

type TrafficState = 'red' | 'yellow' | 'green';

const DURATIONS: Record<TrafficState, number> = { red: 4000, green: 3000, yellow: 1500 };
const NEXT: Record<TrafficState, TrafficState> = { red: 'green', green: 'yellow', yellow: 'red' };

function Demo() {
  const [state, setState] = useState<TrafficState>('red');
  const [running, setRunning] = useState(true);

  useEffect(() => {
    if (!running) return;
    const timer = setTimeout(() => setState(NEXT[state]), DURATIONS[state]);
    return () => clearTimeout(timer);
  }, [state, running]);

  return (
    <Group justify="center" gap="xl">
      <Paper p="lg" withBorder radius="xl" bg="dark.8" w={80}>
        <Stack align="center" gap="md">
          <Led
            value={state === 'red'}
            color="red"
            offColor="dark"
            size="xl"
            variant="3d"
            animate={state === 'red'}
            animationType="glow"
            tooltip="Stop"
          />
          <Led
            value={state === 'yellow'}
            color="yellow"
            offColor="dark"
            size="xl"
            variant="3d"
            animate={state === 'yellow'}
            animationType="pulse"
            animationDuration={0.5}
            tooltip="Caution"
          />
          <Led
            value={state === 'green'}
            color="green"
            offColor="dark"
            size="xl"
            variant="3d"
            tooltip="Go"
          />
        </Stack>
      </Paper>
      <Stack gap="xs">
        <Text fw={700} size="lg" tt="uppercase">{state}</Text>
        <Button
          variant="light"
          size="xs"
          onClick={() => setRunning((r) => !r)}
        >
          {running ? 'Pause' : 'Resume'}
        </Button>
        <Button
          variant="subtle"
          size="xs"
          onClick={() => setState(NEXT[state])}
        >
          Skip
        </Button>
      </Stack>
    </Group>
  );
}

Password Strength Meter

A real-time password strength indicator using Led.Group with colorScale and shape="rectangle":

Enter a password

import { useState } from 'react';
import { Stack, Text, TextInput } from '@mantine/core';
import { Led } from '@gfazioli/mantine-led';

function getStrength(password: string): number {
  let score = 0;
  if (password.length >= 4) score++;
  if (password.length >= 8) score++;
  if (/[A-Z]/.test(password)) score++;
  if (/[0-9]/.test(password)) score++;
  if (/[^a-zA-Z0-9]/.test(password)) score++;
  return score;
}

const LABELS = ['Very weak', 'Weak', 'Fair', 'Strong', 'Very strong'];

function Demo() {
  const [password, setPassword] = useState('');
  const strength = getStrength(password);

  return (
    <Stack gap="md" w={300} mx="auto">
      <TextInput
        label="Password"
        placeholder="Type a password..."
        value={password}
        onChange={(e) => setPassword(e.currentTarget.value)}
      />
      <Led.Group
        value={strength}
        count={5}
        colorScale={['red', 'orange', 'yellow', 'lime', 'green']}
        size="sm"
        shape="rectangle"
        gap="xs"
      />
      <Text size="xs" c="dimmed" ta="center">
        {password.length > 0 ? LABELS[strength - 1] || 'Too short' : 'Enter a password'}
      </Text>
    </Stack>
  );
}