
Security News
CISA Kills Off RSS Feeds for KEVs and Cyber Alerts
CISA is discontinuing official RSS support for KEV and cybersecurity alerts, shifting updates to email and social media, disrupting automation workflows.
svelte-accessible-dialog
Advanced tools
An accessible dialog component for Svelte apps. Demo.
npm install svelte-accessible-dialog
<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>
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>
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>
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>
<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>
<DialogOverlay style="z-index: 10">
<DialogContent style="border: 2px solid #000">
<p>I am a dialog</p>
</DialogContent>
</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" . |
Must be a child of DialogOverlay
. Element props will be spread onto the underlying div
.
WAI-ARIA: https://www.w3.org/TR/wai-aria-practices-1.2/#dialog_modal
key | action |
---|---|
Escape | Dismisses the dialog |
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.
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.
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.
<DialogContent aria-label="Cookie notice">
<p>We use cookies to improve your website experience</p>
<button>Not interested</button>
<button>Ok, thanks</button>
</DialogContent>
<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>
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
.
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 use Jest and svelte-testing-library.
git clone git@github.com:reecelucas/svelte-accessible-dialog.git
cd svelte-accessible-dialog
yarn
yarn test
FAQs
An accessible dialog component for Svelte apps
We found that svelte-accessible-dialog demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
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.
Security News
CISA is discontinuing official RSS support for KEV and cybersecurity alerts, shifting updates to email and social media, disrupting automation workflows.
Security News
The MCP community is launching an official registry to standardize AI tool discovery and let agents dynamically find and install MCP servers.
Research
Security News
Socket uncovers an npm Trojan stealing crypto wallets and BullX credentials via obfuscated code and Telegram exfiltration.