Luminous Card

A card wrapper component that adds a subtle cursor-tracking glow to enhance visual feedback in interactive UIs.

⚙️

Customizable Dashboard

Organize widgets, reports, and charts the way you need them to keep all your insights at a glance.

luminous-card-preview.jsx

import LuminousCard from "@/components/interactions/luminous-card/luminous-card";

const LuminousCardPreview = () => {
  return (
    <LuminousCard
      color={{
        light: "rgba(132, 250, 175, 0.15)",
        dark: "rgba(132, 250, 175, 0.25)",
      }}
      style={cardStyle}
    >
      {/* Add your card content here */}
      <span style={iconStyle}>⚙️</span>
      <h2>Customizable Dashboard</h2>
      <p>Organize widgets, reports, and charts the way you need them to keep all your insights at a glance.</p>
    </LuminousCard>
  )
};

const cardStyle = {
  width: "320px",
  background: "var(--layer-secondary)",
  border: "1px solid var(--border-secondary)",
  borderRadius: "8px",
  padding: "24px",
};

const iconStyle = {
  display: "inline-block",
  marginBottom: "20px",
  fontSize: "24px",
}

export default LuminousCardPreview;

Installation

Run the command from your project root directory (the folder that contains package.json).

Terminal / Console

npx mosaicui-cli@latest interactions/luminous-card

1. Copy the component file

Create a new file called luminous-card.jsx in your reusable components folder (for example /src/components/) and paste the following code into it.

luminous-card.jsx

import { memo, useCallback, useRef } from "react";
import styles from "./luminous-card.module.css";

const LuminousCard = (props) => {
  const {
    color = "rgba(255, 255, 255, 0.25)",
    className,
    tagName = "div",
    children,
    ...restProps
  } = props;

  const Component = tagName || "div";

  const cardWrapperRef = useRef();

  const cardMouseMoveHandler = useCallback((e) => {
    const cardWrapperNode = cardWrapperRef.current;
    const { x: cardWrapperX, y: cardWrapperY } = cardWrapperNode.getBoundingClientRect();
    const x = Math.max(0, e.clientX - cardWrapperX);
    const y = Math.max(0, e.clientY - cardWrapperY);
    let lightThemeColor = color;
    let darkThemeColor = color;
    if (typeof color === "object") {
      lightThemeColor = color.light;
      darkThemeColor = color.dark;
    }
    Object.entries({
      "x": `${x}px`,
      "y": `${y}px`,
      "light-color": lightThemeColor,
      "dark-color": darkThemeColor,
    }).forEach(([key, value]) => {
      cardWrapperNode.style.setProperty(`--${key}`, value);
    });
  }, [color]);

  return (
    <Component
      {...restProps}
      ref={cardWrapperRef}
      onMouseMove={cardMouseMoveHandler}
      className={[
        className,
        styles["luminous-card"],
      ].join(" ")}
    >
      {children}
    </Component>
  );
};

export default memo(LuminousCard);

2. Copy the CSS module file

In the same folder, create a file called luminous-card.module.css and paste the following CSS.

luminous-card.module.css

.luminous-card {
  --x: 0px;
  --y: 0px;
  --luminous-color: var(--light-color);

  @media (prefers-color-scheme: dark) {
    --luminous-color: var(--dark-color);
  }

  position: relative;
  overflow: hidden;

  &:before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 2;
    background: transparent;
    opacity: 0;
    pointer-events: none;
    transition: 
      background 250ms ease-in-out,
      opacity 250ms ease-in-out;
  }

  &:hover {
    &:before {
      opacity: 0.85;
      background:
        radial-gradient(
          circle at var(--x) var(--y),
          var(--luminous-color),
          transparent 85%
        );
    }
  }
}

Props

Configure the component with the following props:

PropTypeRequiredDefaultDescription
childrenReact.ReactNodeNoContent rendered inside the component.
tagNamestringNo"div"HTML tag used as the wrapper element for the component.
colorstring | { light: string; dark: string }No"rgba(255, 255, 255, 0.25)"Color used for the lighting / radial gradient effect. Accepts a single color string or an object with light and dark values for theme-specific colors.
classNamestringNoAdditional CSS classes applied to the root element.
styleReact.CSSPropertiesNoInline styles applied to the root element.