Widget Stack

A stack of small or medium widgets with vertical snap scrolling and page dots.

Widget Stack groups glanceable widgets into one space. Swipe or scroll vertically to move between widgets, use the dots on the right to jump to a page, or focus the stack and use the arrow, Page Up, Page Down, Home, and End keys.

Preview

Photos

Beach memories

Manila

28°

Partly cloudy

Installation

$ npx shadcn@latest add https://spark-ui-olive.vercel.app/r/widget-stack.json

Usage

import { WidgetStack } from "@/components/widget-stack";
import { CalendarDays, CloudSun, MapPin } from "lucide-react";
export default function WidgetStackDemo() {
  return (
    <WidgetStack size="medium" ariaLabel="Daily widgets">
      <article className="relative h-full bg-sky-500 text-white">
        <div
          role="img"
          aria-label="Beach at sunrise"
          className="h-full w-full bg-cover bg-center"
          style={{ backgroundImage: "url('/images/trail-3.jpg')" }}
        />
        <div className="absolute inset-x-0 bottom-0 bg-linear-to-t from-black/65 to-transparent px-4 pb-3 pt-8">
          <p className="text-xs font-medium text-white/75">Photos</p>
          <p className="text-sm font-semibold">Beach memories</p>
        </div>
      </article>

      <article className="flex h-full items-center justify-between bg-linear-to-br from-sky-400 to-blue-700 p-4 text-white">
        <div>
          <p className="flex items-center gap-1 text-xs font-medium text-white/70">
            <MapPin className="size-3" aria-hidden /> Manila
          </p>
          <p className="mt-1 text-5xl font-light tracking-tight">28°</p>
          <p className="text-sm">Partly cloudy</p>
        </div>
        <CloudSun className="size-20 text-yellow-200" strokeWidth={1.25} />
      </article>
    </WidgetStack>
  );
}

Small and Medium Sizes

Both sizes support any number of child widgets and the same vertical stack interaction.

Photos

Good afternoon

UV index is moderate

July

SMTWTFS12345678910111213141516171819202122232425262728293031

Manila

28°

Partly cloudy

82%

Device

Properties

PropTypeDefaultDescription
childrenReactNodeWidget pages rendered in stack order.
size"small" | "medium""small"Square or wide widget proportion.
defaultIndexnumber0Widget page shown first.
onIndexChange(index: number) => voidCalled after scrolling settles on another widget page.
ariaLabelstring"Widget stack"Accessible label for the scrollable stack.
classNamestringAdditional classes for the stack container.

Accessibility

  • The stack is keyboard focusable and supports arrow keys, Page Up, Page Down, Home, and End.
  • Every page and dot announces its position within the stack.
  • Dot controls are native buttons with visible focus states.
  • Smooth scrolling is disabled when the user prefers reduced motion.