Skip to content

Spotlight Card

A card with a soft light that follows the pointer. The light is off for touch and for visitors who ask for less motion, and the card keeps its own focus ring.

Motionv0.1.0experimentalWCAG 2.2 AAView spec

Optional, and kept apart.

Nothing here is included in a project unless you add it, and the plain component it builds on is unchanged. This effect is decoration: it is hidden from screen readers, stands still for visitors who prefer reduced motion, and can be paused. Use Still on a preview to see that version.

Weekly summary

Move the pointer over this card and a soft light follows it.

import { SpotlightCard } from "@rdloom/react";

export default function SpotlightCardBasicExample() {
  return (
    <div className="flex w-full justify-center">
      <SpotlightCard className="w-full max-w-sm">
        <h3 className="text-base font-medium">Weekly summary</h3>
        <p className="mt-1 text-sm text-[var(--rd-color-text-muted)]">Move the pointer over this card and a soft light follows it.</p>
      </SpotlightCard>
    </div>
  );
}

Installation

npx rdloom add spotlight-card

Copies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes.

It also adds rdloom-motion.css next to your tokens and imports it from the tokens file, once: no setup, and no inline styles. The plain components you already use are untouched.

Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/spotlight-card.json

Works, but without upgrade tracking, and you import the motion CSS yourself.

Usage

import { SpotlightCard } from "@rdloom/react";

<div className="flex w-full justify-center">
  <SpotlightCard className="w-full max-w-sm">
    <h3 className="text-base font-medium">Weekly summary</h3>
    <p className="mt-1 text-sm text-[var(--rd-color-text-muted)]">Move the pointer over this card and a soft light follows it.</p>
  </SpotlightCard>
</div>

Grid

Invoices

Send and track what is owed.

Customers

Everyone you bill, in one list.

Reports

Monthly totals you can export.

import { SpotlightCard } from "@rdloom/react";

const items = [
  { title: "Invoices", text: "Send and track what is owed." },
  { title: "Customers", text: "Everyone you bill, in one list." },
  { title: "Reports", text: "Monthly totals you can export." },
];

export default function SpotlightCardGridExample() {
  return (
    <div className="flex w-full justify-center">
      <div className="grid w-full max-w-2xl gap-3 sm:grid-cols-3">
        {items.map((item) => (
          <SpotlightCard key={item.title} size={180}>
            <h3 className="text-sm font-medium">{item.title}</h3>
            <p className="mt-1 text-sm text-[var(--rd-color-text-muted)]">{item.text}</p>
          </SpotlightCard>
        ))}
      </div>
    </div>
  );
}

Billing

Update your plan or payment method.

Open billing
import { SpotlightCard } from "@rdloom/react";

export default function SpotlightCardWithLinkExample() {
  return (
    <div className="flex w-full justify-center">
      <SpotlightCard className="w-full max-w-sm">
        <h3 className="text-base font-medium">Billing</h3>
        <p className="mt-1 text-sm text-[var(--rd-color-text-muted)]">Update your plan or payment method.</p>
        <a
          href="#billing"
          className="mt-3 inline-block rounded-[var(--rd-radius-control)] text-sm font-medium text-[var(--rd-color-action-primary)] underline outline-none focus-visible:ring-2 focus-visible:ring-[var(--rd-color-focus-ring)]"
        >
          Open billing
        </a>
      </SpotlightCard>
    </div>
  );
}

API Reference

Defined by the spec. Components also accept the props of the React Aria component they wrap.

PropTypeDefault
childrenrequired

The card content.

nodenone
size

Radius of the light in pixels.

number240
color

Colour of the light, any CSS colour. Defaults to the primary action colour.

stringnone
isPaused

Turns the light off.

booleanfalse

Accessibility

Role group, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.

Keyboard

Not interactive.

Screen readers announce

  • Read as ordinary content: the light is not announced

What your code must do

  • The light is decoration: aria-hidden, behind the content and never blocks the pointer
  • Off for touch pointers, for reduced motion, inside rdm-still and with isPaused
  • Content and its contrast do not depend on the light: the light is a faint tint under the text
  • Any focusable child keeps its own visible focus ring: the card never replaces or hides it
  • In forced-colors (high contrast) mode the light is removed

Guidelines

Use it when

  • Feature or pricing cards
  • A grid of tiles that should feel responsive to the pointer

Avoid it when

  • Cards that must be perfectly flat in a dense table

Don't

  • Putting text colours that only pass contrast over the brightest part of the light
  • Hiding the focus ring of a link inside the card

Design tokens

The semantic tokens this component uses. Change them once and every component follows; see Design tokens.

  • --rd-color-surface-default
  • --rd-color-border-default
  • --rd-color-action-primary
  • --rd-radius-overlay