Elements Dialogs
Elements

Dialogs


Setup

Dialog styles are included in Manifest CSS or a standalone stylesheet, both referencing theme variables.

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/mnfst@latest/lib/manifest.min.css" />

Default

Dialogs are supported in pure HTML using the <dialog> element as a popover. The <button> that opens the dialog requires the popovertarget="ID" attribute, matching the dialog ID.

<button popovertarget="dialog-default">Open Empty Dialog</button>
<dialog popover id="dialog-default"></dialog>

Light Dismiss

Popovers operate by default as lightboxes, and clicking anywhere outside the dialog or pressing esc will close it, know as "light dismiss". Prevent this with the popover="manual" attribute.

<button popovertarget="dialog-manual">Open Dialog</button>
<dialog popover="manual" id="dialog-manual" class="col center gap-2">
    <p>Click outside, I dare you.<p>
    <button popovertarget="dialog-manual" popoveraction="hide">Close</button>
</dialog>

Manual popovers require internal close buttons, with a popovertarget ID'ing the dialog and popoveraction="hide" to close it.


Layout

Use the <header>, <main>, and/or <footer> elements as direct children of the dialog element for a typical dialog layout.

<button popovertarget="dialog-formatted">Open Formatted Dialog</button>

<dialog popover id="dialog-formatted">

    <header>
        <h2>Dialog Title</h2>
        <button popovertarget="dialog-formatted" aria-label="Close" x-icon="lucide:x"></button>
    </header>

    <main>
        <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.</p>
    </main>

    <footer>
        <button popovertarget="dialog-formatted">Cancel</button>
        <button popovertarget="dialog-formatted" class="brand">Confirm</button>
    </footer>

</dialog>

The layout containers have default styles for padding, and the header will spread its content while the footer aligns it to the end.


Nesting

Dialogs can open or close from each other in a visual stack. Dropdowns are also popovers that can control dialogs and exist within them.

123456789101112131415161718192021222324252627282930313233343536373839404142434445
<button popovertarget="dialog-first">Open First Dialog</button>

<!-- Open dialog from dropdown -->
<button x-dropdown="dropdown-start">Dropdown</button>
<menu id="dropdown-start">
    <button popovertarget="dialog-first">Open First Dialog</button>
</menu>

<!-- First dialog -->
<dialog popover id="dialog-first">
    <header>
        <h3>First Dialog</h3>
        <button popovertarget="dialog-first" aria-label="Close" x-icon="lucide:x"></button>
    </header>
    <main>
        <p>This dialog can open another.</p>
        <p>Dropdowns will close when a dialog is opened.</p>
        <p>Buttons can be used to switch or close dialogs.</p>
    </main>
    <footer>
        <button popovertarget="dialog-second">Open Second Dialog</button>

        <!-- Nested Dropdown -->
        <button x-dropdown="dropdown-internal">Dropdown</button>
        <menu id="dropdown-internal">
            <button popovertarget="dialog-second">Open Second Dialog</button>
        </menu>
        <button popovertarget="dialog-first">Close</button>
    </footer>
</dialog>

<!-- Nested second dialog -->
<dialog popover id="dialog-second">
    <header>
        <h3>Second Dialog</h3>
        <button popovertarget="dialog-second" aria-label="Close" x-icon="lucide:x"></button>
    </header>
    <main>
        <p>This dialog opens above the first one. The Close All button targets the first dialog, automatically closing both.</p>
    </main>
    <footer>
        <button popovertarget="dialog-second" popoveraction="hide">Go Back</button>
        <button popovertarget="dialog-first" popoveraction="hide">Close All</button>
    </footer>
</dialog>

Templating

HTML IDs must identify single elements on a page, and generating multiple dialogs in a template loop requires each dialog be assigned a unique ID. These can be generated with Alpine using template literals like ${i}.

12345678910111213141516171819202122232425
<template x-for="i in 3" :key="i">

    <!-- Multiple elements need to be wrapped in a container, since template tags only recognize their first child. -->
    <div>

        <!-- Button template -->
        <button :popovertarget="`dialog-template-${i}`" x-text="`Dialog ${i}`"></button>
        
        <!-- Dialog template -->
        <dialog popover :id="`dialog-template-${i}`">
            <header>
                <h3 x-text="`Template Dialog ${i}`"></h3>
                <button :popovertarget="`dialog-template-${i}`" aria-label="Close" x-icon="lucide:x"></button>
            </header>
            <main>
                <p x-text="`This is dialog number ${i} generated from a template.`"></p>
            </main>
            <footer>
                <button :popovertarget="`dialog-template-${i}`">Close</button>
            </footer>
        </dialog>

    </div>

</template>

Utility Class

The dialog utility class will apply a dialog element's styles to other elements.

1234567891011121314151617181920212223
<!-- Alpine can be used to open/close a fake dialog if it's not a popover -->
<div x-data="{ reveal: false }">

    <!-- Open button -->
    <button @click="reveal = true">Open Fake Dialog</button>

    <!-- Fake dialog -->
    <div class="dialog" x-show="reveal">
        <header>
            <span class="h3">Fake it to make it</span>
        </header>
        <main>
            <p>If a fake dialog is not a popover like this one, it does not have native lightbox behaviour and requires a custom close button to dismiss.</p>
        </main>
        <footer>

            <!-- Close button -->
            <button @click="reveal = false">Close</button>

        </footer>
    </div>

</div>

Styles

Theme

Default dialogs use the following theme variables:

Variable Purpose
--color-content-stark Dialog text color
--color-popover-surface Dialog background color
--radius Dialog border radius (doubled for dialogs)
--spacing Dialog layout gaps and padding

Backdrop

Dialog backdrops (the light dismiss area) have arbitrary background colors with transparency. They can be styled with custom CSS in light and dark modes.

/* Light theme backdrop */
dialog[popover]::backdrop {
    background-color: rgba(255, 0, 0, 0.2);
}

/* Dark theme backdrop */
.dark dialog[popover]::backdrop {
    background-color: rgba(0, 0, 255, 0.2);
}

Transitions

Default open/close transitions for all popovers—including dialogs—are defined in reset styles. Override them with custom CSS.

123456789101112131415161718192021222324
[popover] {
    display: none;
    transition: opacity .15s ease-in, scale .15s ease-in, display .15s ease-in;
    transition-behavior: allow-discrete;

    &:popover-open {
        display: flex
    }

    /* Opening state */
    @starting-style {
        scale: .9;
        opacity: 0
    }

    /* Closing state */
    &:not(:popover-open) {
        display: none !important;
        scale: 1;
        opacity: 0;
        transition-duration: .15s;
        transition-timing-function: ease-out
    }
}

Tailwind CSS

If using Tailwind, individual dialogs can be customized with utility classes. Dialogs will automatically adjust their size and positioning based on content.

<button popovertarget="dialog-tailwind-preview">Custom Size & Position</button>
<dialog popover id="dialog-tailwind-preview" class="w-96 h-80 mt-20">
    <header>
        <h3>Tailwind Dialog</h3>
        <button popovertarget="dialog-tailwind-preview" aria-label="Close" x-icon="lucide:x"></button>
    </header>
    <main>
        <p>This dialog uses Tailwind utility classes for custom sizing and positioning.</p>
    </main>
    <footer>
        <button popovertarget="dialog-tailwind-preview">Close</button>
    </footer>
</dialog>

Customization

Modify base dialog styles with custom CSS for the dialog[popover] selector.

dialog[popover], .dialog {
    background-color: #f0f8ff;
    border: 2px solid #3b82f6;
    border-radius: 16px;
    box-shadow: 0 25px 50px -12px rgba(59, 130, 246, 0.25);

    &::backdrop {
        background-color: rgba(59, 130, 246, 0.1);
    }

    & :where(header, main, footer) {
        padding: 2rem;
    }
}

Powered by Manifest