NASA Proceeds to Mars

    svelte-accessible-dialog

    2.1.3 • Public • Published

    svelte-accessible-dialog

    An accessible dialog component for Svelte apps. Demo.

    Coverage Status Build Status npm bundle size npm GitHub

    Installation

    npm install svelte-accessible-dialog

    Usage

    Basic

    <script>
      import { DialogOverlay, DialogContent } from 'svelte-accessible-dialog';
    
      let isOpen;
    
      const open = () => {
        isOpen = true;
      };
    
      const close = () => {
        isOpen = false;
      };
    </script>
    
    <button on:click={open}>Open Dialog</button>
    
    <DialogOverlay {isOpen} onDismiss={close}>
      <DialogContent aria-label="Announcement">
        <button on:click={close}>Close</button>
        <p>I am a dialog</p>
      </DialogContent>
    </DialogOverlay>

    Setting Initial Focus

    By default, the first focusable element will receive focus when the dialog opens, but you can provide an element to focus instead.

    <script>
      import { DialogOverlay, DialogContent } from 'svelte-accessible-dialog';
    
      let isOpen;
      let initialFocusElement;
    
      const open = () => {
        isOpen = true;
      };
    
      const close = () => {
        isOpen = false;
      };
    </script>
    
    <button on:click={open}>Open Dialog</button>
    
    <DialogOverlay {isOpen} {initialFocusElement} onDismiss={close}>
      <DialogContent aria-label="Announcement">
        <button on:click={close}>Close</button>
        <label>
          Name: <input type="text" bind:this={initialFocusElement} />
        </label>
        <p>I am a dialog</p>
      </DialogContent>
    </DialogOverlay>

    Setting Return Focus

    By default, the element that invoked the dialog will receive focus when the dialog closes, but you can provide an element to focus instead.

    See the WAI-ARIA authoring practices for more detail about when you might want to do this.

    <script>
      import { DialogOverlay, DialogContent } from 'svelte-accessible-dialog';
    
      let isOpen;
      let returnFocusElement;
    
      const open = () => {
        isOpen = true;
      };
    
      const close = () => {
        isOpen = false;
      };
    </script>
    
    <button on:click={open}>Open Dialog</button>
    <button bind:this={returnFocusElement}>I focus on close</button>
    
    <DialogOverlay {isOpen} {returnFocusElement} onDismiss={close}>
      <DialogContent aria-label="Announcement">
        <button on:click={close}>Close</button>
        <p>I am a dialog</p>
      </DialogContent>
    </DialogOverlay>

    Legacy Support for aria-modal

    DialogContent has the aria-modal attribute. This indicates to screen readers that only content contained within the dialog should be accessible to the user. Modern screen readers respect this attribute, but you can enable a legacy workaround if you require deeper support.

    See the WAI-ARIA authoring practices for more detail.

    <script>
      import { DialogOverlay, DialogContent } from 'svelte-accessible-dialog';
    
      let isOpen;
    
      const open = () => {
        isOpen = true;
      };
    
      const close = () => {
        isOpen = false;
      };
    </script>
    
    <button on:click={open}>Open Dialog</button>
    
    <DialogOverlay {isOpen} ariaModalLegacy={true} onDismiss={close}>
      <DialogContent aria-label="Announcement">
        <button on:click={close}>Close</button>
        <p>I am a dialog</p>
      </DialogContent>
    </DialogOverlay>

    Styling

    :global

    <style>
      :global([data-svelte-dialog-overlay].overlay) {
        z-index: 10;
      }
    
      :global([data-svelte-dialog-content].content) {
        border: 2px solid #000;
      }
    </style>
    
    <DialogOverlay class="overlay">
      <DialogContent class="content">
        <p>I am a dialog</p>
      </DialogContent>
    </DialogOverlay>

    Inline Styles

    <DialogOverlay style="z-index: 10">
      <DialogContent style="border: 2px solid #000">
        <p>I am a dialog</p>
      </DialogContent>
    </DialogOverlay>

    Props

    DialogOverlay

    Must render DialogContent. Any props not listed below will be spread onto the underlying div.

    Prop Type Required Description
    isOpen Boolean Yes Controls whether the dialog is open or not.
    onDismiss () => void Yes This function is called whenever the user hits "Escape" or clicks outside the dialog. The dialog must be closed on onDismiss.
    initialFocusElement HTMLElement No The element that will receive focus when the dialog is open. Defaults to the first focusable element.
    returnFocusElement HTMLElement No The element that will receive focus when the dialog closes. Defaults to the element that invoked the dialog.
    ariaModalLegacy Boolean No Enables a fallback for the aria-modal attribute. When true, all content outside of the active dialog will have the aria-hidden and inert attributes set to "true".

    DialogContent

    Must be a child of DialogOverlay. Element props will be spread onto the underlying div.

    Accessibility

    WAI-ARIA: https://www.w3.org/TR/wai-aria-practices-1.2/#dialog_modal

    Keyboard Accessibility

    key action
    Escape Dismisses the dialog

    Tabbable Elements

    It's recommended to have at least one tabbable element in the DialogContent. Ideally, the first element in the dialog should be a close button. If no tabbable elements are found, the dialog content element itself will receive focus.

    Hiding Page Content from Screen Readers

    Until fairly recently, keeping a screen reader within an active dialog was difficult. A focus trap prevents focus from leaving a dialog, but does nothing to stop a wandering virtual cursor. A common solution to this problem was to set the aria-hidden and inert attributes on all elements outside of the active dialog.

    ARIA 1.1 introduced the aria-modal attribute. aria-modal indicates to screen readers that only content contained within a dialog with aria-modal="true" should be accessible to the user. Modern screen readers respect this attribute, so svelte-accessible-dialog does not implement the legacy workaround by default.

    If support for aria-modal is inadequate for your app, you can pass ariaModalLegacy={true} to DialogOverlay to enable the legacy workaround.

    Labelling

    A dialog needs to be properly labelled to provide context for users that rely on assistive technology. If a dialog is announced to the user without a label, it can be confusing and difficult to navigate.

    There are two general approaches to labelling: aria-label and aria-labelledby. If the text is visible on the screen, you should provide the label's HTML element with a unique id attribute. That id is then given to an aria-labelledby attribute on the DialogContent. With this context, the screen reader will announce whatever text is nested inside that ID'd markup as the title for the Dialog.

    If a design doesn't include a visible label on the screen, you need to provide an aria-label prop on the DialogContent instead.

    aria-label

    <DialogContent aria-label="Cookie notice">
      <p>We use cookies to improve your website experience</p>
      <button>Not interested</button>
      <button>Ok, thanks</button>
    </DialogContent>

    aria-labelledby

    <DialogContent aria-labelledby="cookie-dialog-title">
      <h2 id="cookie-dialog-title">Cookie Notice</h2>
      <p>We use cookies to improve your website experience</p>
      <button>Not interested</button>
      <button>Ok, thanks</button>
    </DialogContent>

    Z-index

    DialogOverlay does not set a z-index. It depends on DOM order to be on top of the page content (it's inserted at the end of the document when it's opened). If you're fighting z-index wars, make sure to add a z-index to DialogOverlay.

    Configuring webpack

    If you're using webpack with svelte-loader, make sure to add "svelte" to resolve.mainFields in your webpack config. This ensures that webpack imports the uncompiled components (src/index.js) rather than the compiled version (dist/index.mjs), which is more efficient.

    If you're using Rollup with rollup-plugin-svelte, this will happen automatically.

    Tests

    Tests use Jest and svelte-testing-library.

    git clone git@github.com:reecelucas/svelte-accessible-dialog.git
    cd svelte-accessible-dialog
    yarn
    yarn test

    LICENSE

    MIT

    Install

    npm i svelte-accessible-dialog

    DownloadsWeekly Downloads

    643

    Version

    2.1.3

    License

    MIT

    Unpacked Size

    103 kB

    Total Files

    13

    Last publish

    Collaborators

    • reecelucas