Design System / 05.4

Overlays

Surfaces above the page: a blurred-scrim modal, slide-in drawers, numbered hotspots with glass tooltips, and the opt-in cursor.

Modal

Full-viewport overlay: 3px backdrop blur behind a white squared panel, fading in on the entry curve and out faster. Scroll locks, Escape and scrim clicks close, and focus returns where it came from.

open / onClose
boolean / () => void
Controlled; unmount is internal so the exit can play after open flips false.
label
string?
Accessible name for the dialog.
className
string?
Restyles the panel. No portal — mount outside any transform/filter ancestor.

Drawer

The modal’s sliding sibling. From the right it is full-width on phones and 30vw on desktop; from the top it is a shallow 28vh band. Both exit faster than they enter.

open / onClose
boolean / () => void
Controlled; Escape and scrim clicks close, scroll locks while open.
side
"right" | "top"
Default "right" (400ms in / 300ms out); top runs 600ms in / 400ms out.
label
string?
Accessible name for the panel.

Hotspot map

Numbered 1.5px-outline dots pinned to percentage positions over imagery; hover or tap opens a glass tooltip that drops from the dot. One open at a time.

Lorem ipsum
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Sed do eiusmod tempor incididunt ut labore et dolore.
Ut enim ad minim veniam, quis nostrud exercitation.
hotspots
{ x, y, label, content }[]
x/y are percentages of the container; dots carry aria-expanded.
children
ReactNode
The image or media block the dots sit over.
className
string?
Container defaults to light ink so dots read on photography; override for very light images.

Glass tooltip

The hotspot’s bubble, exported standalone. Frosted glass is sanctioned only over photography and dark surfaces — never on paper.

Lorem ipsum dolor sit amet, consectetur.
children
ReactNode
Tooltip content.
className
string?
Position and size it in context.

Custom cursor

An opt-in, site-level follower: a difference-blend dot that lerps after the pointer, scales up over interactive elements, and can trail a slow-following label chip. It never activates for coarse pointers or reduced motion — and it is deliberately not mounted on this docs site.

// Site-level, render once (e.g. in the root layout):
<CustomCursor />

// Any ancestor can label the chip:
<a data-cursor-label="Lorem" href="/ipsum">Dolor sit</a>
className
string?
Restyles the dot.
data-cursor-label
attribute
Set on any hovered ancestor to feed the label chip (lerp .07 vs the dot's .25).

Requires the package stylesheet: it toggles html.has-custom-cursor to hide the native cursor while active.