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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@solid-aria/dialog

v0.1.4

Published

Primitives for building accessible dialog component.

Downloads

4,459

Readme

@solid-aria/dialog

pnpm turborepo size version stage

Dialog is an overlay shown above other content in an application.

  • createDialog - Provides the behavior and accessibility implementation for a dialog component.

Installation

npm install @solid-aria/dialog
# or
yarn add @solid-aria/dialog
# or
pnpm add @solid-aria/dialog

createDialog

Provides the behavior and accessibility implementation for a dialog component.

Features

The HTML <dialog> element can be used to build dialogs. However, it is not yet widely supported across browsers, and building fully accessible custom dialogs from scratch is very difficult and error prone. createDialog, combined with createModal, helps achieve accessible dialogs that can be styled as needed.

  • Exposed to assistive technology as a dialog or alertdialog with ARIA
  • Handles labelling the dialog by its title element
  • Handles focusing the dialog on mount, unless a child element is already focused
  • Contains focus inside the dialog when combined with <FocusScope>
  • Hides content behind the dialog from screen readers when combined with createModal
  • Prevents scrolling the page behind the dialog when combined with createPreventScroll
  • Handles closing the dialog when interacting outside and pressing the Escape key, when combined with createOverlay

How to use it

This example shows how to build a typical modal dialog. It has a partially transparent backdrop above the rest of the page, prevents scrolling the body using createPreventScroll, and hides content outside the dialog with createModal.

The modal can be closed by clicking or interacting outside the dialog if the isDismissable prop is set to true, or by pressing the Escape key. This is handled by createOverlay.

Focus is contained within the dialog while it is open using a <FocusScope>. In addition, the first focusable element is automatically focused when the dialog opens, and focus is restored back to the button that opened it when it closes.

The application is contained in an OverlayProvider, which is used to hide the content from screen readers with aria-hidden while a modal is open. In addition, each modal must be contained in an OverlayContainer, which uses a SolidJS Portal to render the modal at the end of the document body. If a nested modal is opened, then the first modal will also be set to aria-hidden, so that only the top-most modal is accessible.

import { createButton } from "@solid-aria/button";
import { AriaDialogProps, createDialog } from "@solid-aria/dialog";
import { FocusScope } from "@solid-aria/focus";
import {
  AriaModalProps,
  AriaOverlayProps,
  createModal,
  createOverlay,
  createOverlayTriggerState,
  createPreventScroll,
  OverlayContainer,
  OverlayProvider
} from "@solid-aria/overlays";

import { JSX, Show } from "solid-js";

interface ModalDialogProps extends AriaDialogProps, AriaModalProps, AriaOverlayProps {
  title?: JSX.Element;
  children?: JSX.Element;
}

function ModalDialog(props: ModalDialogProps) {
  // Handle interacting outside the dialog and pressing
  // the Escape key to close the modal.
  let ref: HTMLDivElement | undefined;
  const { overlayProps, underlayProps } = createOverlay(props, () => ref);

  // Prevent scrolling while the modal is open, and hide content
  // outside the modal from screen readers.
  createPreventScroll();
  const { modalProps } = createModal();

  // Get props for the dialog and its title
  const { dialogProps, titleProps } = createDialog(props, () => ref);

  return (
    <div
      style={{
        position: "fixed",
        "z-index": 100,
        top: 0,
        left: 0,
        bottom: 0,
        right: 0,
        background: "rgba(0, 0, 0, 0.5)",
        display: "flex",
        "align-items": "center",
        "justify-content": "center"
      }}
      {...underlayProps}
    >
      <FocusScope contain restoreFocus autofocus>
        <div
          {...overlayProps}
          {...dialogProps}
          {...modalProps}
          ref={ref}
          style={{
            background: "white",
            color: "black",
            padding: "30px"
          }}
        >
          <h3 {...titleProps} style={{ "margin-top": 0 }}>
            {props.title}
          </h3>
          {props.children}
        </div>
      </FocusScope>
    </div>
  );
}

function Example() {
  let openButtonRef: HTMLButtonElement | undefined;
  let closeButtonRef: HTMLButtonElement | undefined;

  const state = createOverlayTriggerState({});

  // createButton ensures that focus management is handled correctly,
  // across all browsers. Focus is restored to the button once the
  // dialog closes.
  const { buttonProps: openButtonProps } = createButton(
    {
      onPress: () => state.open()
    },
    () => openButtonRef
  );

  const { buttonProps: closeButtonProps } = createButton(
    {
      onPress: () => state.close()
    },
    () => closeButtonRef
  );

  return (
    <>
      <button {...openButtonProps} ref={openButtonRef}>
        Open Dialog
      </button>
      <Show when={state.isOpen()}>
        <OverlayContainer>
          <ModalDialog title="Enter your name" isOpen onClose={state.close} isDismissable>
            <form style={{ display: "flex", "flex-direction": "column" }}>
              <label for="first-name">First Name:</label>
              <input id="first-name" />
              <label for="last-name">Last Name:</label>
              <input id="last-name" />
              <button {...closeButtonProps} ref={closeButtonRef} style={{ "margin-top": "10px" }}>
                Submit
              </button>
            </form>
          </ModalDialog>
        </OverlayContainer>
      </Show>
    </>
  );
}

function App() {
  return (
    // Application must be wrapped in an OverlayProvider so that it can be
    // hidden from screen readers when a modal opens.
    <OverlayProvider>
      <Example />
    </OverlayProvider>
  );
}

Changelog

All notable changes are described in the CHANGELOG.md file.