Dialogs
Customizable styles for HTML <dialog>
elements, requires dialog.js
provided with the assets and $enable-dialogs: true;
in the _configuration.scss
document.
Default dialogs (anchor)
The dialogs have been designed to use a script and technique adapted from Mark Otto's Fun with the dialog element article that provides the opening functionality and the light dismiss ability to click outside the dialog to close it.
Example HTML
The [aria-controls]
isn't required for functionality and is probably not necessary when the button and dialog are together in the content flow as demonstrated. The .backdrop
utility provides the light dismiss ability that allows closing the dialog by clicking on the page background, but also isn't required for the dialog to function.
<button data-dialog="#dialog-1" aria-controls="dialog-1">Basic with heading</button>
<dialog id="dialog-1" class="dialog-basic">
<h2>Heading basic</h2>
<p>The quick brown fox jumps over the lazy dog followed by five boxing wizards jumping quickly.</p>
<button class="close-dialog">Close</button>
<div class="close-dialog backdrop"></div>
</dialog>
<button data-dialog="#dialog-2" aria-controls="dialog-2">Basic without heading</button>
<dialog id="dialog-2" class="dialog-basic">
<p>The quick brown fox jumps over the lazy dog followed by five boxing wizards jumping quickly.</p>
<button class="close-dialog">Close</button>
<div class="close-dialog backdrop"></div>
</dialog>
<button data-dialog="#dialog-3" aria-controls="dialog-3">Paragraph block title</button>
<dialog id="dialog-3">
<header>
<p>Paragraph block title</p>
<button class="close-dialog"><span class="vis-hidden">Close</span></button>
</header>
<div class="dialog-body">
<p>The quick brown fox jumps over the lazy dog followed by the five boxing wizards jumping quickly.</p>
</div>
<div class="close-dialog backdrop"></div>
</dialog>
<button data-dialog="#dialog-4" aria-controls="dialog-4">Heading block title</button>
<dialog id="dialog-4">
<header>
<h2>Heading block title</h2>
<button class="close-dialog"><span class="vis-hidden">Close</span></button>
</header>
<div class="dialog-body">
<p>The quick brown fox jumps over the lazy dog followed by the five boxing wizards jumping quickly.</p>
</div>
<div class="close-dialog backdrop"></div>
</dialog>
Image dialogs (anchor)
The .dialog-img
class is added to the element to display images within dialogs. The .dialog-img-btn
utility can be added to the button to use an image for the button (as below) or a standard button like those above can be used.
With figure and caption:
With dialog header:
Image only:
Example HTML
// With figure and caption
<button data-dialog="#winter-forest" aria-controls="winter-forest" class="dialog-img-btn">
<img src="/img/docs/winter-forest-300.webp" alt="" title="View image" width="300">
</button>
<dialog id="winter-forest" class="dialog-img">
<button class="dialog-img-close close-dialog"><span class="vis-hidden">Close</span></button>
<figure>
<img src="/img/docs/winter-forest-1024.webp" alt="Snowy forest with sun filtering through trees" loading="lazy" width="1024">
<figcaption>Winter forest</figcaption>
</figure>
<div class="close-dialog backdrop"></div>
</dialog>
// With dialog header
<button data-dialog="#winter-forest2" aria-controls="winter-forest2" class="dialog-img-btn">
<img src="/img/docs/winter-forest-300.webp" alt="" title="View image" width="300">
</button>
<dialog id="winter-forest2" class="dialog-img">
<header>
<p>Winter forest</p>
<button class="close-dialog"><span class="vis-hidden">Close</span></button>
</header>
<img src="/img/docs/winter-forest-1024.webp" alt="Snowy forest with sun filtering through trees" loading="lazy" width="1024">
<div class="close-dialog backdrop"></div>
</dialog>
// Image only
<button data-dialog="#winter-forest3" aria-controls="winter-forest3" class="dialog-img-btn">
<img src="/img/docs/winter-forest-300.webp" alt="" title="View image" width="300">
</button>
<dialog id="winter-forest3" class="dialog-img">
<button class="dialog-img-close close-dialog"><span class="vis-hidden">Close</span></button>
<img src="/img/docs/winter-forest-1024.webp" alt="Snowy forest with sun filtering through trees" loading="lazy" width="1024">
<div class="close-dialog backdrop"></div>
</dialog>
Offcanvas dialogs (anchor)
The .dialog-offcanvas-start
and .dialog-offcanvas-start
utilities modify the default dialog margins and include opacity and positioning transition styles with $enable-dialog-animate: true
.
Example HTML
<button data-dialog="#dialog-offcanvas-start" aria-controls="dialog-offcanvas-start">Offcanvas start</button>
<dialog id="dialog-offcanvas-start" class="dialog-basic dialog-offcanvas-start">
<h2>Offcanvas start</h2>
<p>The quick brown fox jumps over the lazy dog followed by five boxing wizards jumping quickly.</p>
<button class="close-dialog">Close</button>
<div class="close-dialog backdrop"></div>
</dialog>
<button data-dialog="#dialog-offcanvas-end" aria-controls="dialog-offcanvas-end">Offcanvas end</button>
<dialog id="dialog-offcanvas-end" class="dialog-basic dialog-offcanvas-end">
<h2>Offcanvas end</h2>
<p>The quick brown fox jumps over the lazy dog followed by five boxing wizards jumping quickly.</p>
<button class="close-dialog">Close</button>
<div class="close-dialog backdrop"></div>
</dialog>
Dialog transitions (anchor)
The transition animation style is an optional extra that can be enabled with $enable-dialog-animate: true;
in the _configuration.scss
document. The styles respect a user's prefers-reduced-motion
preferences and the default transition timing can be customized in the _properties.scss
document or with variables inline.
Example HTML
The 'No transition' example uses the --dialog-transition
variable to remove the default .25s transition included with the $enable-dialog-animate: true;
styles to demonstrate the default behaviour of dialogs, with the custom example it demonstrates how they can also be customized individually inline using variables if enabled.
<button data-dialog="#default-transition" aria-controls="default-transition">No transition</button>
<dialog id="default-transition" class="dialog-basic" style="--dialog-transition: 0s;">
<h2>Heading basic</h2>
<p>The quick brown fox jumps over the lazy dog followed by five boxing wizards jumping quickly.</p>
<button class="close-dialog">Close</button>
<div class="close-dialog backdrop"></div>
</dialog>
<button data-dialog="#transition-enabled" aria-controls="transition-enabled">Enabled (default .25s)</button>
<dialog id="transition-enabled" class="dialog-basic">
<h2>Heading basic</h2>
<p>The quick brown fox jumps over the lazy dog followed by five boxing wizards jumping quickly.</p>
<button class="close-dialog">Close</button>
<div class="close-dialog backdrop"></div>
</dialog>
<button data-dialog="#transition-custom" aria-controls="transition-custom">Enabled (custom 1.5s)</button>
<dialog id="transition-custom" class="dialog-basic" style="--dialog-transition: 1.5s;">
<h2>Heading basic</h2>
<p>The quick brown fox jumps over the lazy dog followed by five boxing wizards jumping quickly.</p>
<button class="close-dialog">Close</button>
<div class="close-dialog backdrop"></div>
</dialog>
Colors (anchor)
The color modifier classes are used with the block style dialog demonstrated above, they only apply color to the dialogs but if required the button color modifiers (if enabled) can be used to apply colors to the buttons.
With $enable-theme-colors: true;
Example HTML
<button data-dialog="#dialog-light" aria-controls="dialog-light">Light</button>
<dialog id="dialog-light" class="dialog-light">
<header>
<p>Dialog header</p>
<button class="close-dialog"><span class="vis-hidden">Close</span></button>
</header>
<div class="dialog-body">
<p>The quick brown fox jumps over the lazy dog followed by the five boxing wizards jumping quickly.</p>
</div>
<div class="close-dialog backdrop"></div>
</dialog>
<button data-dialog="#dialog-dark" aria-controls="dialog-dark">Dark</button>
<dialog id="dialog-dark" class="dialog-dark">
<header>
<p>Dialog header</p>
<button class="close-dialog"><span class="vis-hidden">Close</span></button>
</header>
<div class="dialog-body">
<p>The quick brown fox jumps over the lazy dog followed by the five boxing wizards jumping quickly.</p>
</div>
<div class="close-dialog backdrop"></div>
</dialog>
With $enable-primary-colors: true;
Example HTML
<button data-dialog="#dialog-blue" aria-controls="dialog-blue">Blue</button>
<dialog id="dialog-blue" class="dialog-blue">
<header>
<p>Dialog header</p>
<button class="close-dialog"><span class="vis-hidden">Close</span></button>
</header>
<div class="dialog-body">
<p>The quick brown fox jumps over the lazy dog followed by the five boxing wizards jumping quickly.</p>
</div>
<div class="close-dialog backdrop"></div>
</dialog>
<button data-dialog="#dialog-red" aria-controls="dialog-red">Red</button>
<dialog id="dialog-red" class="dialog-red">
<header>
<p>Dialog header</p>
<button class="close-dialog"><span class="vis-hidden">Close</span></button>
</header>
<div class="dialog-body">
<p>The quick brown fox jumps over the lazy dog followed by the five boxing wizards jumping quickly.</p>
</div>
<div class="close-dialog backdrop"></div>
</dialog>
<button data-dialog="#dialog-green" aria-controls="dialog-green">Green</button>
<dialog id="dialog-green" class="dialog-green">
<header>
<p>Dialog header</p>
<button class="close-dialog"><span class="vis-hidden">Close</span></button>
</header>
<div class="dialog-body">
<p>The quick brown fox jumps over the lazy dog followed by the five boxing wizards jumping quickly.</p>
</div>
<div class="close-dialog backdrop"></div>
</dialog>
<button data-dialog="#dialog-orange" aria-controls="dialog-orange">Orange</button>
<dialog id="dialog-orange" class="dialog-orange">
<header>
<p>Dialog header</p>
<button class="close-dialog"><span class="vis-hidden">Close</span></button>
</header>
<div class="dialog-body">
<p>The quick brown fox jumps over the lazy dog followed by the five boxing wizards jumping quickly.</p>
</div>
<div class="close-dialog backdrop"></div>
</dialog>
<button data-dialog="#dialog-cyan" aria-controls="dialog-cyan">Cyan</button>
<dialog id="dialog-cyan" class="dialog-cyan">
<header>
<p>Dialog header</p>
<button class="close-dialog"><span class="vis-hidden">Close</span></button>
</header>
<div class="dialog-body">
<p>The quick brown fox jumps over the lazy dog followed by the five boxing wizards jumping quickly.</p>
</div>
<div class="close-dialog backdrop"></div>
</dialog>
Script (anchor)
The script is available in the [assets/js]
directory minified and uncompressed as below.
View script
// ------------------------------------------------------------
// Adapted from Mark Otto's 'Fun with the dialog element'
// https://markdotto.com/2022/03/16/dialog-element/
// ------------------------------------------------------------
let toggler = document.querySelectorAll("[data-dialog]"),
closers = document.querySelectorAll(".close-dialog");
toggler &&
(toggler.forEach(function (e) {
let l = e.getAttribute("data-dialog"),
t = document.querySelectorAll(l);
e.addEventListener("click", (e) => {
t.forEach(function (e) {
e.showModal();
});
});
}),
closers.forEach(function (e) {
e.addEventListener("click", (l) => {
e.closest("dialog").close();
});
}));
Customize (anchor)
The default dialogs property values can be customized in the _properties.scss
document. The styles can be customized in the _dialogs.scss
document in the [styles/components]
directory, they're written as standard CSS with limited Sass functionality included for compiling purposes.
Properties
$dialog-txt: var(--text) !default;
$dialog-bg: var(--body-bg) !default;
$dialog-header-txt: var(--text) !default;
$dialog-header-bg: var(--surf-2) !default;
$dialog-body-bg: var(--body-bg) !default;
$dialog-body-py: 0.75rem !default;
$dialog-body-px: 1rem !default;
$dialog-bd-color: var(--surf-3) !default;
$dialog-bd-width: 1px !default;
$dialog-radius: 0.188rem !default;
$dialog-top: 1rem !default;
$dialog-width: calc((100% - 6px) - 2em) !default;
$dialog-max-width: 30rem !default;
$dialog-transition: 0.25s !default;
$dialog-offcanvas: -30rem !default;
Styles
// ----------------------------------------------------------
// Dialogs
// ----------------------------------------------------------
@use "../../configuration" as *;
@use "../../properties" as *;
@use "icons" as *;
@if $enable-dialogs {
// Dialog
:where(dialog) {
color: var(--dialog-txt);
inline-size: var(--dialog-width);
max-inline-size: var(--dialog-max-width);
margin: auto;
margin-block-start: var(--dialog-top);
padding: 0;
border-width: var(--dialog-bd-width);
border-color: var(--dialog-bd-color);
border-radius: var(--dialog-radius);
background-color: var(--dialog-bg);
overscroll-behavior: contain;
}
// Dialog header
:where(dialog :is(header, .dialog-header)) {
@if $enable-icon-mixins or $enable-icon-styles {
--ico: var(--dialog-header-txt);
}
color: var(--dialog-header-txt);
position: sticky;
align-self: start;
inset-block-start: 0;
display: flex;
align-items: center;
column-gap: 1rem;
padding-block: .5rem;
padding-inline-start: 1rem;
padding-inline-end: .75rem;
border-block-end: var(--dialog-bd-width) solid var(--dialog-bd-color);
border-start-start-radius: 0;
background-color: var(--dialog-header-bg);
}
:where(dialog :is(header, .dialog-header) *) {
@if $enable-icon-mixins or $enable-icon-styles {
--ico: var(--dialog-header-txt);
}
color: var(--dialog-header-txt);
}
:where(dialog :is(header, .dialog-header) *:is(h1, h2, h3, h4, h5, h6, p)) {
--mb: 0;
}
// Heading sizes block-padding varies with font-family selection.
:where(dialog :is(header, .dialog-header) *:is(h1, h2, h3, h4, h5, h6)) {
padding-block-start: .2rem;
padding-block-end: .4rem;
}
// Dialog body wrapper
:where(.dialog-body) {
padding-block: var(--dialog-body-py);
padding-inline: var(--dialog-body-px);
background-color: var(--dialog-body-bg);
}
:where(.dialog-body:has(pre)) {
padding-block: 0;
padding-inline: 0;
background-color: var(--dialog-body-bg);
}
:where(.dialog-body pre) {
border: none;
}
:where(.dialog-body :last-child) {
--mb: 0;
}
:where(.dialog-basic) {
padding-block: var(--dialog-body-py);
padding-inline: var(--dialog-body-px);
}
:where(.dialog-basic:has(h1:first-child, h2:first-child, h3:first-child, h4:first-child, h4:first-child, h4:first-child)) {
padding-block-end: 1rem;
}
:where(.dialog-basic *:has(+ .backdrop)) {
--mb: 0;
}
// Image dialog with image button
// Image button
:where(.dialog-img-btn) {
--fs: var(--fs-xxs);
--btn-py: 0;
--btn-px: 0;
}
:where(.dialog-img-btn:hover) {
cursor: zoom-in;
}
// Image dialog
:where(.dialog-img) {
--dialog-body-py: 0;
--dialog-body-px: 0;
--dialog-max-width: fit-content;
}
:where(.dialog-img img) {
margin-inline: auto;
object-fit: cover;
}
:where(.dialog-img figure) {
margin-block-end: 0;
}
:where(.dialog-img figcaption) {
padding-block: .5rem;
padding-inline: 1rem;
}
// Offcanvas
.dialog-offcanvas-start, .dialog-offcanvas-end {
--dialog-radius: 0;
margin: 0;
block-size: 100dvh;
max-block-size: 100%;
border: none;
}
.dialog-offcanvas-start {
margin-inline-end: auto;
border-inline-end: var(--dialog-bd-width) solid var(--dialog-bd-color);
}
.dialog-offcanvas-end {
margin-inline-start: auto;
border-inline-start: var(--dialog-bd-width) solid var(--dialog-bd-color);
}
// Dialog animation (if enabled)
@if $enable-dialog-animate {
@media screen and (prefers-reduced-motion: no-preference) {
dialog {
transition: var(--dialog-transition);
opacity: 0;
}
}
dialog[open] {
opacity: 1;
}
@starting-style {
dialog[open] {
opacity: 0;
}
.dialog-offcanvas-start {
margin-inline-start: var(--dialog-offcanvas);
}
.dialog-offcanvas-end {
margin-inline-end: var(--dialog-offcanvas);
}
}
}
// Dialog close buttons
// Essential icons
:where(dialog :is(header, .dialog-header) button.close-dialog):before,
:where(.dialog-img-close):before {
--ico: var(--dialog-header-txt);
@if $enable-close-svg {
--svg: var(--close);
}
@else {
--svg: #{$close};
}
@if $enable-icon-mixins or $enable-icon-styles {
}
@else {
--ico-va: -.115em;
}
@extend %icon-mask;
}
// Header close button style
:where(dialog :is(header, .dialog-header) button.close-dialog) {
--btn-bg: transparent;
--btn-hover: transparent;
--btn-bd-color: transparent;
--btn-py: .25rem;
--btn-px: .5rem;
margin-inline-start: auto;
}
:where(dialog :is(header, .dialog-header) button.close-dialog:is(:focus, :active)) {
outline: none;
box-shadow: none;
}
// Image dialog close button style (doesn't apply to header, .dialog-header close button if used)
.dialog-img > button.close-dialog {
--btn-radius: 0;
position: absolute;
inset-block-start: 0;
inset-inline-end: 0;
border-end-start-radius: inherit;
border-block-start: none;
border-inline-end: none;
}
// Backdrop
::backdrop {
background: #00000099;
}
// Adding <div class="close-dialog backdrop"></div> as last child inside
// the dialog allows clicking the page background to close the dialog.
:where(.backdrop) {
position: fixed;
inset: 0;
z-index: -1;
display: block;
content: "";
}
// Colors
@if $enable-theme-colors {
.dialog-light {
--dialog-header-txt: #000;
--dialog-header-bg: var(--surf-2-light);
--dialog-bd-color: var(--surf-3-light);
}
.dialog-dark {
--dialog-header-txt: #fff;
--dialog-header-bg: var(--surf-2-dark);
--dialog-bd-color: var(--surf-3-dark);
}
} // END [if/theme-colors]
@if $enable-primary-colors {
.dialog-blue {
--dialog-header-txt: #fff;
--dialog-header-bg: var(--blue);
--dialog-bd-color: var(--blue);
}
.dialog-red {
--dialog-header-txt: #fff;
--dialog-header-bg: var(--red);
--dialog-bd-color: var(--red);
}
.dialog-green {
--dialog-header-txt: #fff;
--dialog-header-bg: var(--green);
--dialog-bd-color: var(--green);
}
.dialog-orange {
--dialog-header-txt: #000;
--dialog-header-bg: var(--orange);
--dialog-bd-color: var(--orange);
}
.dialog-cyan {
--dialog-header-txt: #000;
--dialog-header-bg: var(--cyan);
--dialog-bd-color: var(--cyan);
}
} // END [if/primary-colors]
} // END [if/dialogs]