Skip to content

Instantly share code, notes, and snippets.

@mattdanielbrown
Created January 12, 2025 01:20
Show Gist options
  • Save mattdanielbrown/2428e24f41ec21d0a0b8a59ade0a02a8 to your computer and use it in GitHub Desktop.
Save mattdanielbrown/2428e24f41ec21d0a0b8a59ade0a02a8 to your computer and use it in GitHub Desktop.
<dialog> types
<dialog id="dialogDefault">
<form method="dialog">
<p>Hi, I'm the default dialog.</p>
<button type="submit" value="ok">OK</button>
</form>
</dialog>
<dialog id="dialogError" role="alertdialog" class="classic error">
<header>⚠ Error!</header>
<form method="dialog">
<p>This is an error message.</p>
<footer>
<button type="submit" value="close">Close</button>
</footer>
</form>
</dialog>
<dialog id="dialogWait" class="classic wait">
<header>🕑 Please wait</header>
<form method="dialog">
<progress indeterminate></progress>
</form>
</dialog>
<dialog id="dialogNotify" class="classic notify">
<header>ℹ Something happened!</header>
<form method="dialog">
<p>Something happened that I need to tell you.</p>
<footer>
<button type="submit" value="ok">OK</button>
</footer>
</form>
</dialog>
<dialog id="dialogConfirm" class="classic">
<header>☑ Please Confirm</header>
<form method="dialog">
<p>The web is awesome right?</p>
<footer>
<button class="light" type="submit" value="yes">Yes</button>
<button class="error" type="submit" value="no">No</button>
</footer>
</form>
</dialog>
<dialog id="dialogTransition" class="classic animated">
<header>➥ I'm animated!</header>
<form method="dialog">
<p>I don't use any keyframes either, I'm all CSS transitions.</p>
<footer>
<button class="light" type="submit" value="rad">Rad</button>
</footer>
</form>
</dialog>
<dialog id="dialogDismiss" class="classic">
<header>Close me by clicking the backdrop</header>
<form method="dialog">
<p>Some Javascript is listening for clicks and closing this dialog if it notices the backdrop was clicked.</p>
<p>Note: Wanting light dismiss for a dialog tends to mean the UX you're after is a <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/popover">popover</a>.</p>
</form>
</dialog>
<dialog id="dialogCustom" class="custom classic animated">
<header>
<span>✏️ New User</span>
<button onclick="dialogCustom.close('X')">
<svg aria-hidden="true" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
</svg>
</button>
</header>
<form method="dialog">
<div>
<label for="uname">User Name</label>
<input type="text">
</div>
<footer>
<button class="light" type="submit" value="create">Create</button>
<button class="error" type="submit" value="cancel">Cancel</button>
</footer>
</form>
</dialog>
<button onclick="dialogDefault.showModal()">Default</button>
<button onclick="dialogError.showModal()" class="error">Error</button>
<button onclick="dialogWait.showModal(); fakeWait()" class="wait">Wait</button>
<button onclick="dialogNotify.showModal()" class="light">Notify</button>
<button onclick="dialogConfirm.showModal()" class="light">Confirm</button>
<button onclick="dialogTransition.showModal()" class="dark">Transitions</button>
<button onclick="dialogDismiss.showModal()" class="dark">Light Dismiss</button>
<button onclick="dialogCustom.showModal()" class="custom">Custom</button>
function fakeWait() {
setTimeout(() => {
dialogWait.close('wait completed')
}, 2000)
}
// light dismiss
dialogDismiss.addEventListener('click', ({target:dialog}) => {
if (dialog.nodeName === 'DIALOG') {
dialog.close('dismiss')
}
})
// watch for closed dialogs
// log the close value to know what button user's clicked
document.querySelectorAll('dialog').forEach(dialog => {
dialog.onclose = event => {
console.log(event.target.returnValue)
}
})
/* disables scrolling the page behind a dialog */
html:has(dialog[open]) {
overflow: hidden;
}
@layer dialogs {
dialog.classic {
--_theme-color: CanvasText;
--_on-theme-color: Canvas;
border-color: var(--_theme-color);
accent-color: var(--_theme-color);
padding: 0;
inline-size: clamp(25ch, 60ch, 80vw);
border-radius: 10px;
> header {
display: flex;
align-items: center;
background: var(--_theme-color);
color: var(--_on-theme-color);
padding: .75rem 1rem;
font-weight: bold;
font-size: 1.25rem;
}
> form {
padding: 1.25rem 1rem 1rem;
> p {
margin-block-start: 0;
line-height: 1.5;
&:last-child {
margin-block-end: 0;
}
}
}
footer {
display: flex;
gap: 1ch;
place-content: center end;
margin-block-start: 2rem;
padding-block-start: 1rem;
border-block-start: 1px solid #aaa3;
}
}
dialog.error {
--_theme-color: red;
--_on-theme-color: white;
&::backdrop {
background-color: hsl(from var(--_theme-color) h s 20% / 10%)
}
}
dialog.wait {
--_theme-color: Highlight;
--_on-theme-color: HighlightText;
progress {
inline-size: 100%;
}
}
/* enable transitions */
dialog.animated {
&, &::backdrop {
transition:
display .3s allow-discrete,
overlay .3s allow-discrete,
opacity .3s,
transform .3s;
opacity: 0;
transition-timing-function: ease-in-out;
}
/* ON STAGE */
&[open] {
opacity: 1;
&::backdrop {
opacity: 0.8;
}
}
/* OFF STAGE */
@starting-style {
&[open],
&[open]::backdrop {
opacity: 0;
}
&[open] {
transform: translateY(10px);
}
}
&::backdrop {
background-color: black;
}
}
dialog.custom {
--_theme-color: oklch(98% .02 200 / 50%);
--_on-theme-color: oklch(30% .02 200);
max-inline-size: 40ch;
background: oklch(90% .02 200 / 25%);
> header {
display: flex;
justify-content: space-between;
> button {
background: none;
box-shadow: none;
padding: 0;
height: 2ch;
width: 2ch;
color: var(--_on-theme-color);
}
}
> form > div {
display: grid;
gap: .5ch;
}
input {
padding: 1ch 1lh;
border-radius: 5px;
background: oklch(90% .02 200 / 25%);
color: var(--_on-theme-color);
border: 1px solid oklch(90% .02 200 / 35%);
}
&::backdrop {
background: radial-gradient(circle at center, hsl(220 50% 50% / 50%), hsl(220 50% 50% / 0%));
backdrop-filter: blur(25px);
}
&[open]::backdrop {
opacity: 1;
}
}
}
@layer buttons {
button {
font-size: 1.25rem;
padding-block: 1ch;
padding-inline: 2ch;
border-radius: 5px;
border: 1px solid #0000;
box-shadow: 0 2px 3px #0003;
outline-offset: 5px;
dialog footer & {
font-size: 1rem;
}
@media (prefers-color-scheme: dark) {
box-shadow: 0 2px 3px #000a;
}
}
button.error {
background: red;
color: white;
border-color: red;
}
button.wait {
background: Highlight;
color: HighlightText;
border-color: Highlight;
}
button.light {
background: white;
color: #222;
border-color: #eee;
}
button.dark {
background: hsl(200 5% 15%);
color: #fff;
border-color: hsl(200 5% 20%);
}
dialog.custom {
header button {
padding: 0;
inline-size: 2ch;
block-size: 2ch;
}
button {
box-shadow: 0 2px 3px #0003;
}
}
}
html {
block-size: 100%;
color-scheme: dark light;
}
body {
min-block-size: 100%;
font-family: system-ui, sans-serif;
display: flex;
flex-flow: row wrap;
place-items: center;
place-content: center;
gap: 1rem;
margin: 0;
}
p {
text-wrap: pretty;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment