npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bento/overlay

v0.1.1

Published

Overlay primitive for creating layered UI experiences like modals, drawers, and popups

Downloads

108

Readme

Overlay

The @bento/overlay package provides the Overlay component, a foundational primitive for creating layered UI experiences where content appears above the main application view. Overlay manages state and coordinates React Aria hooks, passing props via slots to children for flexible composition.

Purpose

Overlay serves as a coordinator that brings together focused primitives like Portal, FocusLock, ScrollLock, and Dismiss. It handles state management and React Aria integration while delegating rendering and behavior to composed primitives.

Use Cases

Overlay addresses several key scenarios:

  • Modal dialogs: Displaying content that requires immediate user attention
  • Drawers and sheets: Side panels or bottom sheets common in mobile interfaces
  • Loading states: Fullscreen loading indicators that block interaction
  • Image viewers: Lightbox-style image viewing experiences
  • Onboarding flows: Step-by-step tutorials that overlay the application

Installation

npm install --save @bento/overlay @bento/portal @bento/focus-lock @bento/scroll-lock @bento/dismiss

Props

The @bento/overlay package exports the Overlay component:

| Prop | Type | Required | Description | |------|------|----------|------------| | slots | Record<string, object \| Function> & { trigger?: TriggerSlotValue; } | No | Slot overrides for the overlay. The trigger slot is typed to expose React Aria press handlers so consumers are guided toward using pressable-aware primitives (e.g. @bento /button). | | open | boolean | No | Whether the overlay is open (controlled). | | defaultOpen | boolean | No | Default open state (uncontrolled). | | onOpenChange | (open: boolean) => void | No | Callback fired when open state changes. | | type | "dialog" \| "menu" \| "listbox" \| "tree" \| "grid" | No | The type of overlay trigger. | | isDismissable | boolean | No | Whether the overlay can be dismissed by clicking outside or pressing ESC. | | children | ReactNode | No | Content to render inside the overlay. Consumers provide Portal, ScrollLock, FocusLock, and content via slots. | | slot | string | No | A named part of a component that can be customized. This is implemented by the consuming component. The exposed slot names of a component are available in the components documentation. |

Examples

Basic Usage

The simplest overlay usage demonstrates controlled state management:

Complete Modal

A complete modal dialog combining all overlay primitives. This example demonstrates best practices for accessible modal patterns using Portal for DOM rendering, ScrollLock for background scroll prevention, FocusLock for focus containment, and Dismiss for screen reader accessibility:

Drawer

Drawers use the same overlay primitives as modals but with different positioning. This example shows a drawer sliding in from the right side:

Uncontrolled with Trigger

An uncontrolled overlay that manages its own state internally. The trigger button uses the trigger slot to receive toggle handlers from the Overlay component:

Non-Modal Popover

A lightweight popover without ScrollLock or backdrop, suitable for non-blocking UI elements:

Composition

Overlay follows Bento's composition model by managing state and passing handlers via slots to children. It does not render DOM elements itself but coordinates the behavior of composed primitives.

Slot System

Overlay passes props to children through the slot system. Children with slot assignments automatically receive the appropriate props:

| Slot | Purpose | Props Provided | Required | |------|---------|---------------|----------| | trigger | Element that opens/closes the overlay | triggerProps from React Aria (requires a pressable component) | No | | backdrop | Visual backdrop behind content | overlayProps from React Aria | No | | content | Main overlay content container | overlayProps from React Aria | Yes |

Note: The trigger slot must be filled with a component that consumes React Aria press events (for example @bento/button or any primitive built on @bento/pressable). Passing a raw DOM element (e.g. Container as="button") leaves the onPress handler on the native element, which React ignores and will log warnings.

Component Structure

The recommended overlay structure follows this hierarchy:

Overlay (state management, coordination)
├─ Trigger (slot="trigger", optional)
└─ Portal (DOM rendering location, when open)
   ├─ ScrollLock (background scroll prevention)
   ├─ Backdrop (slot="backdrop", optional, for modal overlays)
   └─ FocusLock (focus trapping and restoration)
      └─ Content (slot="content", user-provided content)
         ├─ Dismiss (accessible dismiss at start)
         ├─ User content (Text, buttons, etc.)
         └─ Dismiss (accessible dismiss at end)

Controlled vs Uncontrolled

Overlay supports both controlled and uncontrolled state management using React Aria's useOverlayTriggerState hook.

Controlled - Parent component manages state:

const [open, setOpen] = useState(false);
<Overlay open={open} onOpenChange={setOpen}>
  {/* content */}
</Overlay>

Uncontrolled - Overlay manages state internally:

<Overlay defaultOpen={false}>
  <Button slot="trigger">Open</Button>
  {/* content */}
</Overlay>

React Aria Integration

Overlay integrates React Aria's overlay hooks to provide accessible behavior:

  • useOverlayTriggerState: Manages controlled/uncontrolled state
  • useOverlayTrigger: Provides trigger and overlay props
  • useModalOverlay: For modal semantics (via composed children)
  • useOverlay: For dismissal behavior (via composed children)

The component passes React Aria hook return values to children via slots, allowing consumers to manually compose primitives while maintaining proper accessibility.

Accessibility

Overlay is designed with accessibility as a foundation:

State Management

  • Controlled and uncontrolled state patterns via React Aria
  • Proper trigger-overlay relationship through ARIA attributes
  • State communicated to children via slots

Focus Management

  • Delegated to FocusLock primitive when composed
  • Focus trapping for modal patterns
  • Focus restoration on close
  • Initial focus control

Keyboard Support

  • ESC key handling via React Aria overlayProps
  • Tab navigation contained within FocusLock
  • Trigger activation via Space/Enter

Screen Reader Support

  • Proper ARIA attributes from React Aria hooks
  • Dismiss components for linear navigation
  • Modal semantics when appropriate

Data Attributes

Overlay applies the following data attribute to help with styling and testing:

| Attribute | Description | Example Values | |-----------|-------------|----------------| | data-state | Current open state | "open" / "closed" |

These attributes are passed via the Box context to children, allowing styling based on overlay state:

.my-overlay [slot="content"][data-state="open"] {
  animation: fadeIn 200ms ease-in;
}

.my-overlay [slot="backdrop"][data-state="open"] {
  animation: fadeIn 200ms ease-in;
}

Related Packages

Overlay is designed to work with these Bento primitives:

  • @bento/portal: Renders content outside normal DOM hierarchy
  • @bento/focus-lock: Traps focus within overlay boundaries
  • @bento/scroll-lock: Prevents background scrolling
  • @bento/dismiss: Accessible dismiss controls for screen readers
  • @bento/container: Base primitive for polymorphic rendering
  • @bento/button: Accessible button component for triggers and actions
  • @bento/text: Typography component for overlay content

Server-Side Rendering

Overlay is SSR-safe and works correctly in server-side rendering environments. The component only manages state and does not attempt to access browser APIs. Ensure composed primitives like Portal are properly configured for SSR.

Browser Support

Overlay supports all modern browsers through React Aria's cross-browser compatibility layer. The component handles platform-specific behavior transparently.

Customization

Slots

Overlay is created using the @bento/slots package via withSlots('BentoOverlay', ...), which means it supports the standard slot and slots props for integration with parent components.

Overlay defines three slot assignments that children can use to receive overlay-specific props:

  • trigger: Receives trigger props from useOverlayTrigger
  • backdrop: Receives overlay props for backdrop interactions
  • content: Receives overlay props for content container

See the @bento/slots package for more information on how to use the slot and slots properties.

Styling

Since Overlay doesn't render DOM elements itself, styling is applied to composed children. Use the className or style props on Portal children, or target elements using data attributes:

<Overlay open={open} onOpenChange={setOpen}>
  <Container slot="backdrop" className="my-backdrop" />
  <Container slot="content" className="my-content">
    {/* content */}
  </Container>
</Overlay>

CSS targeting:

.my-backdrop[data-state="open"] {
  animation: fadeIn 200ms;
}

.my-content[data-state="open"] {
  animation: slideUp 300ms;
}

Best Practices

Always provide a way to close: Ensure users can dismiss overlays via backdrop click, ESC key, close button, or Dismiss controls. Use appropriate patterns: Modal overlays should use FocusLock and ScrollLock. Non-modal popovers may skip these primitives for lightweight interactions. Position carefully: Use Portal to avoid clipping issues. Consider viewport boundaries and scrolling when positioning overlay content. Manage focus: Always use FocusLock with restoreFocus for modal patterns to ensure focus returns to the trigger. Screen reader accessibility: Include Dismiss controls at start and end of modal content for users navigating linearly. Semantic HTML: Use appropriate heading levels and ARIA labels within overlay content.

Performance

Overlay is lightweight and only manages state and context. Performance characteristics depend on composed children. Use conditional rendering to avoid mounting Portal, FocusLock, and other primitives when the overlay is closed.