caliper uidocs

Drop overlay

Free

Lets a whole area take dropped files: a clear target rises over the content while files are dragged in and names what will happen.

Installs components/ui/drop-overlay.tsxDepends on lucide-reactMotion none in component source
pnpm dlx shadcn@latest add https://caliperui.site/r/drop-overlay.json

Documents

  • board-pack-q3.pdf
  • contract-ostra.docx

Or drag files anywhere onto this panel.

When to use #

Use it for

  • Document lists, chats and media libraries.
  • Anywhere a file button already exists.

Skip it when

  • The only way to upload; dragging is not available to everyone.

Installation #

terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/drop-overlay.json

Adds its 2 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.

Usage #

This is the code behind the preview above.

example.tsx
import { useState } from "react"
import { DropOverlay } from "@/components/ui/drop-overlay"

export function DropOverlayDemo() {
  const [files, setFiles] = useState<string[]>([]);
  return (
    <DropOverlay className="w-full max-w-md rounded-[14px]" onFiles={(added) => setFiles((current) => [...added.map((file) => file.name), ...current])} hint="PDF or images, up to 20 MB each" accept={["image/", ".pdf"]}>
      <div className="grid gap-3 rounded-[14px] p-5 shadow-[inset_0_0_0_1px_var(--border)]">
        <div className="flex items-center justify-between gap-3">
          <p className="text-sm font-medium">Documents</p>
          <label className="cursor-pointer rounded-[9px] px-3 py-1.5 text-sm font-medium shadow-[inset_0_0_0_1px_var(--border)] focus-within:outline-2 focus-within:outline-ring">
            Choose files
            <input type="file" multiple className="sr-only" onChange={(event) => setFiles((current) => [...Array.from(event.target.files ?? []).map((file) => file.name), ...current])} />
          </label>
        </div>
        <ul className="grid gap-1 text-sm text-muted-foreground">
          {(files.length ? files : ["board-pack-q3.pdf", "contract-ostra.docx"]).map((name) => <li key={name}>{name}</li>)}
        </ul>
        <p className="text-xs text-muted-foreground">Or drag files anywhere onto this panel.</p>
      </div>
    </DropOverlay>
  );
}

API reference #

DropOverlay

component
PropTypeDefaultDescription
onFiles *(files: File[]) => void—
acceptreadonly string[]—Accepted types, e.g. ["image/", ".pdf"]; files that do not match are left out.
labelstring"Drop to upload"
hintstring—A second line, e.g. "PDF or images, up to 20 MB each".
classNamestring—
children *ReactNode—

Accessibility #

It exposes status semantics, with aria-hidden kept in step with state. Changes that matter are announced through a live region.

No custom key handling; Tab and the native activation keys do the work.

Motion #

It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.