Xzero UI
Pro library

Black holePro

A delete transition that pulls the chosen items into a tiny singularity while the rest of the list bends around it; undo spins them back out.

$ pnpm dlx shadcn@4.23.0 add @xzero-ui-pro/black-hole
Live · keyboard ready

When to use

When not to use

Installation

Register both Xzero UI namespaces in your project's components.json:

{
  "registries": {
    "@xzero-ui": "https://mcp.xzero.co.uk/r/{name}.json",
    "@xzero-ui-pro": "https://mcp.xzero.co.uk/r/pro/{name}.json"
  }
}
npx shadcn@4.23.0 add @xzero-ui-pro/black-hole

Install xzero-foundation once, and import @/components/xzero/foundation.css in your app root. No Xzero token is used by this snapshot. For sample portraits, photos, fonts, or logos, download https://mcp.xzero.co.uk/install-assets.mjs and run node xzero-install-assets.mjs <receiving-project>.

Usage

import { useLayoutEffect, useRef, useState } from "react";
import { BlackHole } from "@/components/xzero/black-hole/black-hole";
import type { BlackHoleHandle } from "@/components/xzero/black-hole/black-hole";

export function Inbox({ initial }: { initial: Mail[] }) {
  const hole = useRef<BlackHoleHandle>(null);
  const rows = useRef(new Map<string, HTMLLIElement>());
  const [items, setItems] = useState(initial);
  const [selected, setSelected] = useState<string[]>([]);

  async function remove() {
    await hole.current?.swallow(selected.map(id => rows.current.get(id)!));
    setItems(current => current.filter(item => !selected.includes(item.id)));
    setSelected([]);
  }

  return (
    <BlackHole ref={hole}>
      <ul>
        {items.map(item => (
          <li key={item.id} ref={el => { if (el) rows.current.set(item.id, el); }}>{item.subject}</li>
        ))}
      </ul>
    </BlackHole>
  );
}

API reference

BlackHole

Wraps a list or grid. Its ref exposes swallow and release, which run a GPU transition: the chosen items spiral into a tiny singularity while the rest of the content is gravitationally lensed around it, then the hole collapses with a soft shockwave.

PropTypeDefaultDescription
children (required)ReactNode–The content that holds the items you remove. It is painted to a texture for the length of the effect.
refRef<BlackHoleHandle>–Gives access to swallow and release.
classNamestring–Extra class on the root.
styleCSSProperties–Inline styles on the root.

BlackHoleHandle

Imperative methods on the ref. Calls queue, so a release requested during a swallow waits its turn.

PropTypeDefaultDescription
swallow(targets: Element[], options?: BlackHoleOptions) => Promise<void>–Pulls the elements in. They stay hidden afterwards; remove them from your data when the promise resolves so the list closes the gap.
release(targets: Element[], options?: BlackHoleOptions) => Promise<void>–The reverse. Call it in a layout effect right after the items are back in the DOM: they are hidden at once, the list makes room, then they spiral out into place.

BlackHoleOptions

PropTypeDefaultDescription
origin{ x: number; y: number }–Where the hole opens, in viewport coordinates. Defaults to the centre of the targets. Pass the delete button's centre to pull items toward it.
durationnumber1.3Length of the effect in seconds. The reverse runs a little faster.

Keyboard interactions

KeysAction
None of its ownThe component has no controls. Wire Delete or Backspace to your own selection, as the demo does.

Accessibility

Motion

Responsive behavior

Performance

Notes for AI

Also in loaders

Guidance for AI tools

Black hole: A delete transition that pulls the chosen items into a tiny singularity while the rest of the list bends around it; undo spins them back out. Follow the declared prop types and do not invent props. Keep keyboard access, reduced motion support, and both light and dark themes intact when adapting it.

Full library index: https://ui.xzero.co.uk/llms.txt

Browse all components