Socket
Book a DemoInstallSign in
Socket

@bento/dismiss

Package Overview
Dependencies
Maintainers
4
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@bento/dismiss

Dismiss button component for accessible modal dismissal

latest
Source
npmnpm
Version
0.1.1
Version published
Maintainers
4
Created
Source

import { Meta, Story, ArgTypes, Controls, Source, } from '@storybook/addon-docs/blocks';

Dismiss

The @bento/dismiss package provides an accessible, visually hidden dismissal control for overlays and popup content. It ensures users, especially those using screen readers, can reliably dismiss modal dialogs, drawers, and popovers through linear keyboard navigation. This component complements ESC key handling and outside-click behaviors, filling a critical accessibility gap when sighted users have a visible close button but screen reader users navigating linearly encounter no dismissal affordance.

This primitive aligns with React Aria's DismissButton pattern and should be positioned at both the start and end of dismissible overlay content. When a screen reader user navigates forward or backward through an overlay, they will encounter these controls, giving them a clear way to exit the overlay without relying solely on the ESC key or outside-click patterns that may not be discoverable through linear navigation.

Installation

npm install --save @bento/dismiss

Props

The @bento/dismiss package exports the Dismiss component:

import { Dismiss } from '@bento/dismiss';

<Dismiss onDismiss={() => setOpen(false)} />

The following properties are available to be used on the Dismiss component:

PropTypeRequiredDescription
onDismiss() => voidNoCalled when the dismiss button is activated.
ariaLabelstringNoAccessible label for the dismiss button. Should be localized.
childrenReactNodeNoThe content to render inside the container.
slots{ hidden?: ((args: { props: Record<string, unknown>; children: ReactNode; }) => ReactNode) | Record<string, unknown>; } & Record<string, object | Function>NoOptional slot to customize the VisuallyHidden wrapper.
An object that contains the customizations for the slots.
The main way you interact with the slot system as a consumer.
slotstringNoA 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.

For all other properties specified on the Dismiss component, the component will pass them down to the underlying button element. This includes properties such as id, data-* attributes, or additional ARIA attributes that you might need for specialized use cases.

Example

<Source language='tsx' code={ SourceBasic } />

Examples

Basic Usage

The most common pattern for the Dismiss component is to place it at both the start and end of dismissible overlay content. This ensures screen reader users navigating forward encounter a dismissal control at the beginning, and users navigating backward from content below the overlay encounter a dismissal control at the end.

<Source language='tsx' code={ SourceBasic } />

In this example, when a screen reader user navigates into the dialog, they immediately encounter the first dismiss button. If they navigate through all the content and continue forward, they encounter the second dismiss button before exiting the overlay's focus boundary. Both buttons invoke the same onDismiss callback to close the dialog.

Custom Label

The default accessible label is "Dismiss". For localization or context-specific labeling, use the ariaLabel prop to provide a custom label that makes sense in your application's language and terminology.

<Source language='tsx' code={ SourceCustomLabel } />

Providing clear, localized labels helps screen reader users understand what will happen when they activate the dismiss button. Use labels that match the terminology of your application, such as "Close dialog", "Exit menu", or "Dismiss notification".

Usage Guidelines

Understanding when and where to use the Dismiss component is critical for creating accessible overlay patterns. The following guidance is based on React Aria patterns and WCAG accessibility standards.

Required Usage

The Dismiss component is required in the following contexts:

Modal Dialogs and Overlays

When creating modal dialogs that block interaction with the rest of the page, place a Dismiss control at the start and end of the dialog content. This provides screen reader users with a reliable dismissal mechanism regardless of whether they navigate forward or backward through the content. While sighted users can click a visible close button, screen reader users navigating linearly need an equivalent affordance at both boundaries.

Modal Drawers and Sheets

Side panels and bottom sheets that use modal behavior should follow the same pattern as modal dialogs. Position dismiss controls at the boundaries of the drawer content to ensure users can exit through linear navigation.

The Dismiss component is recommended but not strictly required in these contexts:

Dismissible Popovers and Menus

When popovers or dropdown menus can be dismissed but lack a visible close button, include start and end dismiss controls. This ensures screen reader users can exit the popup even when ESC key handling might not be discoverable.

Coachmarks and Tour Steps

Product tours and onboarding flows that can be dismissed should include dismiss controls. If the tour requires explicit action buttons to proceed, consider whether a generic dismiss is appropriate or whether users should be guided through specific actions.

Combobox and Select Popovers

While combobox popovers typically manage focus automatically, adding dismiss controls can help screen reader users who want to exit the popup without making a selection.

Do not use the Dismiss component in these contexts:

Tooltips

Tooltips are non-interactive content that appear on hover or focus and do not trap focus. They do not need dismiss controls as users can simply navigate away from the trigger element.

Toasts and Notifications

Toast notifications and growl-style messages typically do not trap focus and should not use the Dismiss component. If dismissal is needed, provide a visible close button instead.

Non-Dismissible Content

Legal consent dialogs, age gates, or other content that requires explicit user action should use specific action buttons rather than a generic dismiss control.

Placement Rules

Follow these guidelines for correct placement:

Position the first dismiss control immediately after the opening tag of your overlay content container, before any other focusable elements. Position the second dismiss control immediately before the closing tag of your overlay content container, after all other focusable elements. This ensures linear navigation always encounters a dismiss affordance at the boundaries.

Always maintain a visible close button for sighted users. The Dismiss component is a screen reader affordance, not a replacement for visible UI controls. Sighted keyboard users and mouse users need their own clear way to close overlays.

Use meaningful, localized labels through the ariaLabel prop. The default "Dismiss" may not be appropriate for all contexts or languages. Consider labels like "Close dialog", "Exit menu", or region-specific translations that match your application's terminology.

Accessibility

The Dismiss component is designed with accessibility as its primary purpose. It fills a specific gap in overlay accessibility by providing a dismissal affordance for users navigating with screen readers using linear navigation patterns.

Screen Reader Compatibility

The component uses the @bento/visually-hidden primitive to ensure the button remains completely accessible to assistive technologies while being visually hidden from sighted users. This technique uses CSS to position content off-screen rather than using display: none or visibility: hidden, which would make the content unavailable to screen readers.

Keyboard Navigation

The dismiss button is fully keyboard accessible. Screen reader users can activate it using Enter or Space when focused on the element. The component uses tabIndex={-1} which removes the button from the standard tab order but allows screen readers to navigate to it through virtual cursor navigation. This is the standard pattern for this type of control and aligns with React Aria's DismissButton implementation.

ARIA Labeling

The component applies aria-label to provide context about what the button does. The default label is "Dismiss", but you should customize this through the ariaLabel prop to match your application's terminology and language. Clear, descriptive labels help screen reader users understand the button's purpose without requiring additional context.

Semantic HTML

The component renders as a native HTML button element with type="button" to prevent accidental form submission. Using semantic HTML ensures compatibility with assistive technologies and provides the expected button semantics and keyboard behavior automatically.

Integration with Overlay Patterns

The Dismiss component is designed to work alongside other accessibility features in overlay patterns. It complements ESC key handling from React Aria's overlay hooks, outside-click dismissal through OverlayTrigger, and focus containment from focus management utilities. While these features provide dismissal mechanisms for some users, the Dismiss component specifically addresses the needs of screen reader users navigating linearly through content.

Position the component inside your overlay's focus boundary, within the same container as your overlay content. It should be rendered before and after your overlay's main content to ensure users encounter it when navigating in either direction.

Customization

The Dismiss component is built using the @bento/slots package, allowing you to customize specific parts of the component through slot-based composition. While the component is intentionally visually hidden, understanding its customization options can be useful for specialized use cases or debugging.

Slots

The component is registered as BentoDismiss and introduces the following slots:

  • hidden: Assigned to the @bento/visually-hidden component that wraps the dismiss button.

You can use the slots prop to override the default behavior of the visually hidden wrapper:

<Source language='tsx' code={ SourceSlotCustomization } />

In this example, we override the hidden slot to provide custom styling to the visually hidden wrapper. While this is possible, it is rarely necessary in practice since the component is designed to be visually hidden by default.

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

Styling

While the dismiss button is visually hidden by default and styling is typically not necessary, you can customize the underlying button element using className or style props. These props will be applied to the button element itself, not the visually hidden wrapper.

import { Dismiss } from '@bento/dismiss';

<Dismiss onDismiss={handleDismiss} className="my-dismiss-button" />

When you assign a className to the component, you take full responsibility for styling. The component will pass this className to the button element, allowing you to target it with CSS selectors. However, keep in mind that because the button is wrapped in a visually hidden container, visual styles will not be visible to sighted users.

Data Attributes

The following data attributes are automatically applied to the component:

AttributeDescriptionExample Values
data-hiddenApplied to the visually hidden wrapper, indicating content is accessible to screen readers"true"

These attributes are provided by the underlying @bento/visually-hidden component and can be used for debugging or specialized styling scenarios:

[data-hidden="true"] button[type="button"] {
  /* Target all visually hidden buttons */
}

Default Attributes

The component renders a native button element with the following attributes:

  • type="button": Prevents the button from submitting forms when used inside form elements.
  • tabIndex={-1}: Removes the button from the standard tab order while keeping it accessible to screen readers through virtual cursor navigation.
  • aria-label: Provides an accessible label for screen readers. Defaults to "Dismiss" but can be customized via the ariaLabel prop.
  • style={{ width: 1, height: 1 }}: A defensive fallback that ensures the button has minimal dimensions even when visually hidden, which can help with screen reader detection in certain scenarios.

Keywords

accessibility

FAQs

Package last updated on 18 Dec 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts