{"id":14962690,"url":"https://github.com/reecelucas/svelte-accessible-dialog","last_synced_at":"2025-10-25T00:30:40.727Z","repository":{"id":39151845,"uuid":"257909715","full_name":"reecelucas/svelte-accessible-dialog","owner":"reecelucas","description":"An accessible dialog component for Svelte apps","archived":false,"fork":false,"pushed_at":"2024-02-03T17:38:10.000Z","size":1861,"stargazers_count":26,"open_issues_count":12,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-01-31T04:31:54.880Z","etag":null,"topics":["a11y","accessibility","alert","dialog","modal","svelte","svelte3","sveltejs"],"latest_commit_sha":null,"homepage":"","language":"Svelte","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/reecelucas.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-04-22T13:28:36.000Z","updated_at":"2023-01-31T18:06:34.000Z","dependencies_parsed_at":"2024-09-22T15:01:06.046Z","dependency_job_id":"4f136952-f292-4d1e-a894-9ad88754cb58","html_url":"https://github.com/reecelucas/svelte-accessible-dialog","commit_stats":{"total_commits":34,"total_committers":4,"mean_commits":8.5,"dds":0.4117647058823529,"last_synced_commit":"ec76ab51acbd6cdac213fdc7fe0a8b7ea1bc0c8f"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reecelucas%2Fsvelte-accessible-dialog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reecelucas%2Fsvelte-accessible-dialog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reecelucas%2Fsvelte-accessible-dialog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reecelucas%2Fsvelte-accessible-dialog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/reecelucas","download_url":"https://codeload.github.com/reecelucas/svelte-accessible-dialog/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238046827,"owners_count":19407623,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["a11y","accessibility","alert","dialog","modal","svelte","svelte3","sveltejs"],"created_at":"2024-09-24T13:30:22.717Z","updated_at":"2025-10-25T00:30:40.277Z","avatar_url":"https://github.com/reecelucas.png","language":"Svelte","funding_links":[],"categories":[],"sub_categories":[],"readme":"# svelte-accessible-dialog\n\nAn accessible dialog component for Svelte apps. [Demo](https://svelte.dev/repl/6c6729de07b04cba8ea3fd413c013137).\n\n[![Coverage Status](https://coveralls.io/repos/github/reecelucas/svelte-accessible-dialog/badge.svg?branch=master)](https://coveralls.io/github/reecelucas/svelte-accessible-dialog?branch=master)\n[![Build Status](https://travis-ci.org/reecelucas/svelte-accessible-dialog.svg?branch=master)](https://travis-ci.org/reecelucas/svelte-accessible-dialog)\n![npm bundle size](https://img.shields.io/bundlephobia/minzip/svelte-accessible-dialog.svg)\n![npm](https://img.shields.io/npm/v/svelte-accessible-dialog.svg)\n![GitHub](https://img.shields.io/github/license/reecelucas/svelte-accessible-dialog.svg)\n\n* [Installation](#installation)\n* [Usage](#usage)\n* [Styling](#styling)\n* [Props](#props)\n* [Accessibility](#accessibility)\n* [Configuring webpack](#configuring-webpack)\n* [Tests](#tests)\n* [LICENSE](#license)\n\n## Installation\n\n```bash\nnpm install svelte-accessible-dialog\n```\n\n## Usage\n\n### Basic\n\n```html\n\u003cscript\u003e\n  import { DialogOverlay, DialogContent } from 'svelte-accessible-dialog';\n\n  let isOpen;\n\n  const open = () =\u003e {\n    isOpen = true;\n  };\n\n  const close = () =\u003e {\n    isOpen = false;\n  };\n\u003c/script\u003e\n\n\u003cbutton on:click={open}\u003eOpen Dialog\u003c/button\u003e\n\n\u003cDialogOverlay {isOpen} onDismiss={close}\u003e\n  \u003cDialogContent aria-label=\"Announcement\"\u003e\n    \u003cbutton on:click={close}\u003eClose\u003c/button\u003e\n    \u003cp\u003eI am a dialog\u003c/p\u003e\n  \u003c/DialogContent\u003e\n\u003c/DialogOverlay\u003e\n```\n\n### Setting Initial Focus\n\nBy default, the first focusable element will receive focus when the dialog opens, but you can provide an element to focus instead.\n\n```html\n\u003cscript\u003e\n  import { DialogOverlay, DialogContent } from 'svelte-accessible-dialog';\n\n  let isOpen;\n  let initialFocusElement;\n\n  const open = () =\u003e {\n    isOpen = true;\n  };\n\n  const close = () =\u003e {\n    isOpen = false;\n  };\n\u003c/script\u003e\n\n\u003cbutton on:click={open}\u003eOpen Dialog\u003c/button\u003e\n\n\u003cDialogOverlay {isOpen} {initialFocusElement} onDismiss={close}\u003e\n  \u003cDialogContent aria-label=\"Announcement\"\u003e\n    \u003cbutton on:click={close}\u003eClose\u003c/button\u003e\n    \u003clabel\u003e\n      Name: \u003cinput type=\"text\" bind:this={initialFocusElement} /\u003e\n    \u003c/label\u003e\n    \u003cp\u003eI am a dialog\u003c/p\u003e\n  \u003c/DialogContent\u003e\n\u003c/DialogOverlay\u003e\n```\n\n### Setting Return Focus\n\nBy default, the element that invoked the dialog will receive focus when the dialog closes, but you can provide an element to focus instead.\n\nSee the [WAI-ARIA authoring practices](https://www.w3.org/TR/wai-aria-practices-1.2#keyboard-interaction-7) for more detail about when you might want to do this.\n\n```html\n\u003cscript\u003e\n  import { DialogOverlay, DialogContent } from 'svelte-accessible-dialog';\n\n  let isOpen;\n  let returnFocusElement;\n\n  const open = () =\u003e {\n    isOpen = true;\n  };\n\n  const close = () =\u003e {\n    isOpen = false;\n  };\n\u003c/script\u003e\n\n\u003cbutton on:click={open}\u003eOpen Dialog\u003c/button\u003e\n\u003cbutton bind:this={returnFocusElement}\u003eI focus on close\u003c/button\u003e\n\n\u003cDialogOverlay {isOpen} {returnFocusElement} onDismiss={close}\u003e\n  \u003cDialogContent aria-label=\"Announcement\"\u003e\n    \u003cbutton on:click={close}\u003eClose\u003c/button\u003e\n    \u003cp\u003eI am a dialog\u003c/p\u003e\n  \u003c/DialogContent\u003e\n\u003c/DialogOverlay\u003e\n```\n\n### Legacy Support for aria-modal\n\n`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](#hiding-page-content-from-screen-readers) if you require deeper support.\n\nSee the [WAI-ARIA authoring practices](https://www.w3.org/TR/wai-aria-practices-1.2#dialog_roles_states_props) for more detail.\n\n```html\n\u003cscript\u003e\n  import { DialogOverlay, DialogContent } from 'svelte-accessible-dialog';\n\n  let isOpen;\n\n  const open = () =\u003e {\n    isOpen = true;\n  };\n\n  const close = () =\u003e {\n    isOpen = false;\n  };\n\u003c/script\u003e\n\n\u003cbutton on:click={open}\u003eOpen Dialog\u003c/button\u003e\n\n\u003cDialogOverlay {isOpen} ariaModalLegacy={true} onDismiss={close}\u003e\n  \u003cDialogContent aria-label=\"Announcement\"\u003e\n    \u003cbutton on:click={close}\u003eClose\u003c/button\u003e\n    \u003cp\u003eI am a dialog\u003c/p\u003e\n  \u003c/DialogContent\u003e\n\u003c/DialogOverlay\u003e\n```\n\n## Styling\n\n### :global\n\n```html\n\u003cstyle\u003e\n  :global([data-svelte-dialog-overlay].overlay) {\n    z-index: 10;\n  }\n\n  :global([data-svelte-dialog-content].content) {\n    border: 2px solid #000;\n  }\n\u003c/style\u003e\n\n\u003cDialogOverlay class=\"overlay\"\u003e\n  \u003cDialogContent class=\"content\"\u003e\n    \u003cp\u003eI am a dialog\u003c/p\u003e\n  \u003c/DialogContent\u003e\n\u003c/DialogOverlay\u003e\n```\n\n### Inline Styles\n\n```html\n\u003cDialogOverlay style=\"z-index: 10\"\u003e\n  \u003cDialogContent style=\"border: 2px solid #000\"\u003e\n    \u003cp\u003eI am a dialog\u003c/p\u003e\n  \u003c/DialogContent\u003e\n\u003c/DialogOverlay\u003e\n```\n\n## Props\n\n### DialogOverlay\n\nMust render `DialogContent`. Any props not listed below will be spread onto the underlying `div`.\n\n| Prop                  | Type        | Required | Description |\n|-----------------------|-------------|----------|-------------|\n| `isOpen`              | Boolean     | Yes      |   Controls whether the dialog is open or not. |\n| `onDismiss`           | () =\u003e void  | Yes      |   This function is called whenever the user hits \"Escape\" or clicks outside the dialog. The dialog must be closed on `onDismiss`.            |\n| `initialFocusElement` | HTMLElement | No       |    The element that will receive focus when the dialog is open. Defaults to the first focusable element.             |\n| `returnFocusElement`  | HTMLElement | No       |  The element that will receive focus when the dialog closes. Defaults to the element that invoked the dialog.            |\n| `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\"`.        |\n\n### DialogContent\n\nMust be a child of `DialogOverlay`. Element props will be spread onto the underlying `div`.\n\n## Accessibility\n\nWAI-ARIA: \u003chttps://www.w3.org/TR/wai-aria-practices-1.2/#dialog_modal\u003e\n\n### Keyboard Accessibility\n\n| key       | action               |\n|-----------|----------------------|\n| `Escape`  | Dismisses the dialog |\n\n### Tabbable Elements\n\nIt'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.\n\n### Hiding Page Content from Screen Readers\n\nUntil 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.\n\n[ARIA 1.1](https://www.w3.org/TR/wai-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.\n\nIf support for `aria-modal` is inadequate for your app, you can pass `ariaModalLegacy={true}` to `DialogOverlay` to enable the legacy workaround.\n\n### Labelling\n\nA 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.\n\nThere 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.\n\nIf a design doesn't include a visible label on the screen, you need to provide an `aria-label` prop on the `DialogContent` instead.\n\n#### aria-label\n\n```html\n\u003cDialogContent aria-label=\"Cookie notice\"\u003e\n  \u003cp\u003eWe use cookies to improve your website experience\u003c/p\u003e\n  \u003cbutton\u003eNot interested\u003c/button\u003e\n  \u003cbutton\u003eOk, thanks\u003c/button\u003e\n\u003c/DialogContent\u003e\n```\n\n#### aria-labelledby\n\n```html\n\u003cDialogContent aria-labelledby=\"cookie-dialog-title\"\u003e\n  \u003ch2 id=\"cookie-dialog-title\"\u003eCookie Notice\u003c/h2\u003e\n  \u003cp\u003eWe use cookies to improve your website experience\u003c/p\u003e\n  \u003cbutton\u003eNot interested\u003c/button\u003e\n  \u003cbutton\u003eOk, thanks\u003c/button\u003e\n\u003c/DialogContent\u003e\n```\n\n## Z-index\n\n`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`.\n\n## Configuring webpack\n\nIf you're using webpack with [svelte-loader](https://github.com/sveltejs/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.\n\nIf you're using Rollup with [rollup-plugin-svelte](https://github.com/sveltejs/rollup-plugin-svelte), this will happen automatically.\n\n## Tests\n\nTests use [Jest](https://jestjs.io/) and [svelte-testing-library](https://testing-library.com/docs/svelte-testing-library/intro).\n\n```bash\ngit clone git@github.com:reecelucas/svelte-accessible-dialog.git\ncd svelte-accessible-dialog\nyarn\nyarn test\n```\n\n## LICENSE\n\n[MIT](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freecelucas%2Fsvelte-accessible-dialog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Freecelucas%2Fsvelte-accessible-dialog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freecelucas%2Fsvelte-accessible-dialog/lists"}