Created
February 26, 2025 20:35
-
-
Save meduzen/327aa7bdf581ab92a1441a1cc35b3a4f to your computer and use it in GitHub Desktop.
`<dialog>` component using Svelte 3 and plain CSS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| :where(dialog) { | |
| position: fixed; | |
| margin: auto; | |
| size: fit-content; | |
| overscroll-behavior-y: contain; | |
| } | |
| :where(dialog:not([open])) { | |
| display: none; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* Hey reader! Do not forget to replace custom media queries if you don't use `postcss-preset-env`. */ | |
| @import url('../config/media-queries.css'); | |
| @import url('../media-queries/motion.css'); | |
| @import url('../media-queries/ui.css'); | |
| @keyframes dialog-background-light { | |
| to { background: oklch(1 0 0 / .7); } | |
| } | |
| @keyframes dialog-background-dark { | |
| to { background: oklch(0 0 0 / .6); } | |
| } | |
| .dialog { | |
| /* helps the dialog to stay in the middle while closing and transitioning */ | |
| inset: 0; | |
| inline-size: 100%; | |
| max-inline-size: min(70rem, 100vw - 2rem); | |
| padding: 0; | |
| container: dialog / inline-size; | |
| background: var(--bg); | |
| border: 0; | |
| border-radius: 1rem; | |
| @media (--light) { | |
| box-shadow: 0 1rem 7rem oklch(0 0 0 / .6); | |
| } | |
| @media (--dark) { | |
| /* @todo: change this color */ | |
| border: .1rem solid #333; | |
| } | |
| @media (--motion) { | |
| transition: | |
| opacity .2s ease-in-out, | |
| transform .2s ease-in-out, | |
| visibility .2s ease-in-out; | |
| &::backdrop { | |
| background: transparent; | |
| animation: dialog-background-light .5s ease-out forwards; | |
| @media (--dark) { | |
| animation-name: dialog-background-dark; | |
| } | |
| } | |
| &:not([open]) { | |
| display: block; | |
| visibility: hidden; | |
| opacity: 0; | |
| transform: scale(.95) translateY(-2rem); | |
| transform-origin: 50% 50%; | |
| transition: | |
| opacity .2s ease-in-out, | |
| transform .2s ease-in-out, | |
| visibility .2s ease-in-out .2s; | |
| } | |
| } | |
| & > * { | |
| padding-inline: 2rem; | |
| } | |
| } | |
| .dialog__header { | |
| padding-block: 1rem; | |
| display: grid; | |
| border-block-end: .1rem solid #eee; | |
| /* border-block-end: .2rem solid var(--accent); */ | |
| @container (inline-size >= 20em) { | |
| grid-template-columns: 1fr auto; | |
| gap: 2rem; | |
| } | |
| } | |
| .dialog__title { | |
| margin: 0; | |
| } | |
| .dialog__close { | |
| position: relative; | |
| top: .3rem; | |
| /* limit element height to its child button height */ | |
| align-self: baseline; | |
| font-size: 0; | |
| /* Put close button before title. */ | |
| @container (inline-size < 20em) { | |
| order: -1; | |
| left: -.7rem; | |
| margin-block-start: 1em; | |
| } | |
| } | |
| .dialog__closeBtn { | |
| line-height: 0; | |
| } | |
| .dialog__closeBtnIcon { | |
| @media (--phone) { | |
| size: 3.2rem; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!-- @todo: Take inspiration from https://github.com/meduzen/react-learning-udemy-schwarzmuller/blob/main/src/components/Dialog.jsx#L21-L32 if a callback system is needed later on. --> | |
| <!-- @todo: Walk through the WCAG dialog pattern: https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/. --> | |
| <script> | |
| /** | |
| * Dialog title (currently a h2) | |
| * @type {string} | |
| */ | |
| export let title | |
| /** | |
| * The text of the button closing the dialog. | |
| * @type {string} | |
| */ | |
| export let closeLabel = 'Close' | |
| export const show = () => dialog.showModal() | |
| /** | |
| * @type {HTMLDialogElement} | |
| */ | |
| let dialog | |
| </script> | |
| <!-- Unoptimized SVGs, should be redrawn. --> | |
| <svg style="display: none;"> | |
| <!-- Heroicons (https://icones.js.org/collection/heroicons) --> | |
| <symbol id="cross-path" viewBox="0 0 20 20"> | |
| <path fill="currentColor" d="M6.28 5.22a.75.75 0 0 0-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 1 0 1.06 1.06L10 11.06l3.72 3.72a.75.75 0 1 0 1.06-1.06L11.06 10l3.72-3.72a.75.75 0 0 0-1.06-1.06L10 8.94L6.28 5.22Z"></path> | |
| </symbol> | |
| </svg> | |
| <!-- HTML --> | |
| <dialog class="dialog" bind:this={dialog} id={crypto.randomUUID()}> | |
| <!-- header --> | |
| <div class="dialog__header"> | |
| <h2 class="dialog__title">{title}</h2> | |
| <form class="dialog__close" method="dialog"> | |
| <button | |
| class="dialog__closeBtn" | |
| value="0" | |
| title={closeLabel} | |
| aria-label={closeLabel} | |
| > | |
| <svg class="dialog__closeBtnIcon" width="20" height="20"> | |
| <use xlink:href="#cross-path"/> | |
| </svg> | |
| </button> | |
| </form> | |
| </div> | |
| <!-- content --> | |
| <div class="dialog__content"> | |
| <slot/> | |
| </div> | |
| </dialog> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <script> | |
| import Dialog from './Dialog.svelte'; | |
| /** | |
| * @type {Dialog} | |
| */ | |
| let dialog | |
| const toggleButton = document.getElementById('toggle-settings') | |
| toggleButton.addEventListener('click', () => dialog.show()) | |
| </script> | |
| <Dialog | |
| title="Settings" | |
| closeLabel="Close settings" | |
| bind:this={dialog} | |
| > | |
| <p>Some content.</p> | |
| </Dialog> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment