Dialogs

Customizable styles for HTML <dialog> elements are designed to use a script and technique adapted from Mark Otto's Fun with the dialog element article. Disabled by default, enable with $enable-dialogs: true; in the configuration.scss document.

Default dialogs (anchor)

Basic dialogs also have the option of being closable by clicking on the page background outside the dialog.

Heading basic

The quick brown fox jumps over the lazy dog followed by five boxing wizards jumping quickly.

The quick brown fox jumps over the lazy dog followed by five boxing wizards jumping quickly.

Paragraph block title

The quick brown fox jumps over the lazy dog followed by the five boxing wizards jumping quickly.

Heading block title

The quick brown fox jumps over the lazy dog followed by the five boxing wizards jumping quickly.

Example HTML

The [aria-controls] associates the buttons with the dialogs for accessibility and isn't required for the dialog to work correctly. The .backdrop utility is added functionality 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:

Snowy forest with sun filtering through trees
Winter forest

With dialog header:

Winter forest

Snowy forest with sun filtering through trees

Image only:

Snowy forest with sun filtering through trees
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="Snowy forest with sun filtering through trees" 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="Snowy forest with sun filtering through trees" 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="Snowy forest with sun filtering through trees" 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>

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;

Dialog header

The quick brown fox jumps over the lazy dog followed by the five boxing wizards jumping quickly.

Dialog header

The quick brown fox jumps over the lazy dog followed by the five boxing wizards jumping quickly.

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;

Dialog header

The quick brown fox jumps over the lazy dog followed by the five boxing wizards jumping quickly.

Dialog header

The quick brown fox jumps over the lazy dog followed by the five boxing wizards jumping quickly.

Dialog header

The quick brown fox jumps over the lazy dog followed by the five boxing wizards jumping quickly.

Dialog header

The quick brown fox jumps over the lazy dog followed by the five boxing wizards jumping quickly.

Dialog header

The quick brown fox jumps over the lazy dog followed by the five boxing wizards jumping quickly.

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

Minifying Mark's script altered it significantly, view his original via the link below.

//  ------------------------------------------------------------
//  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 dialogs core property values are provided as CSS variables that can be used to customize the styles inline. The values are compiled from Sass variables that can be customized in the properties.scss document when compiling.

Property values
$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.125em !default; 
$dialog-top:          1rem !default;
$dialog-width:        calc((100% - 6px) - 2em) !default;
$dialog-max-width:    30rem !default;

The styles are regular CSS that can be customized in the _dialogs.scss document within the [styles/components] directory. The Sass @if rules control compiling depending on settings in the configuration.scss document.

Styles
@if $enable-dialogs {

// Dialog
:where(dialog) {
  color: var(--dialog-txt);
  margin: auto;
  margin-block-start: var(--dialog-top);  
  padding: 0;
  width: var(--dialog-width);
  max-inline-size: var(--dialog-max-width); 
  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;
  top: 0;
  display: flex;
  align-items: center;
  column-gap: 1rem;
  border-block-end: var(--dialog-bd-width) solid var(--dialog-bd-color);
  background-color: var(--dialog-header-bg);
  padding-block: .5rem;
  padding-inline-start: 1rem;
  padding-inline-end: .75rem;
  border-start-start-radius: 0;
}

: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;
}

// 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-dark {
  --dialog-header-txt: #fff;
  --dialog-header-bg: var(--surf-2-dark);
  --dialog-bd-color: var(--surf-3-dark);
}

.dialog-light {
  --dialog-header-txt: #000;
  --dialog-header-bg: var(--surf-2-light);
  --dialog-bd-color: var(--surf-3-light);
}

} // 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]