Sheet

A sheet slides in from the edge of the screen to display supplementary content or actions without leaving the current page.

<div class="flex justify-center">
    <dui-button variant="ButtonVariant.Outline" commandfor="--sheet-intro" command="show-modal">
        Open
    </dui-button>
</div>
<dui-sheet id="--sheet-intro">
    <dui-sheet-header>
        <dui-sheet-title>Edit profile</dui-sheet-title>
        <dui-sheet-description>Make changes to your profile here. Click save when you're done.
        </dui-sheet-description>
    </dui-sheet-header>
    <dui-field-group class="grid gap-6 px-4">
        <dui-field>
            <dui-label for="--sheet-intro-name">Name</dui-label>
            <dui-input id="--sheet-intro-name" name="name" defaultValue="Ibn Battuta"/>
        </dui-field>
        <dui-field>
            <dui-label for="--sheet-intro-username">Username</dui-label>
            <dui-input id="--sheet-intro-username" name="username" defaultValue="@@ibnbattuta"/>
        </dui-field>
    </dui-field-group>
    <dui-sheet-footer>
        <dui-button variant="ButtonVariant.Outline" commandfor="--sheet-intro" command="close">
            Cancel
        </dui-button>
        <dui-button commandfor="--sheet-intro" command="close">
            Save Changes
        </dui-button>
    </dui-sheet-footer>
</dui-sheet>

Usage

<dui-sheet> renders a standard HTML dialog element styled to slide in from the edge of the screen. Use the Invoker Commands API to open and close it by adding commandfor and command attributes to a <dui-button> element.

<dui-button commandfor="--custom-sheet" command="show-modal">
    Sheet Trigger
</dui-button>
<dui-sheet id="--custom-sheet">
    <dui-sheet-header>
        <dui-sheet-title>...</dui-sheet-title>
        <dui-sheet-description>...</dui-sheet-description>
    </dui-sheet-header>
    <!-- Sheet content goes here -->
    <dui-sheet-footer>
        <dui-button variant="ButtonVariant.Outline" commandfor="--custom-sheet" command="close">
            Close
        </dui-button>
    </dui-sheet-footer>
</dui-sheet>

Because the sheet renders a native dialog element, the same JavaScript API and events documented for the Dialog component apply here too. For more information, you can review the following MDN documentation:

Examples

Sides

Use the side attribute to control which edge of the screen the sheet slides in from. It accepts SheetSide.Top, SheetSide.Right (the default), SheetSide.Bottom, and SheetSide.Left.

<div class="flex justify-center gap-2">
    <dui-button variant="ButtonVariant.Outline" commandfor="--sheet-sides-top" command="show-modal">
        Top
    </dui-button>
    <dui-button variant="ButtonVariant.Outline" commandfor="--sheet-sides-right" command="show-modal">
        Right
    </dui-button>
    <dui-button variant="ButtonVariant.Outline" commandfor="--sheet-sides-bottom" command="show-modal">
        Bottom
    </dui-button>
    <dui-button variant="ButtonVariant.Outline" commandfor="--sheet-sides-left" command="show-modal">
        Left
    </dui-button>
</div>
<dui-sheet id="--sheet-sides-top" side="SheetSide.Top" class="data-[side=top]:h-[50vh]">
    <div class="p-4">
        Open on the top
    </div>
</dui-sheet>
<dui-sheet id="--sheet-sides-right" side="SheetSide.Right">
    <div class="p-4">
        Open on the right
    </div>
</dui-sheet>
<dui-sheet id="--sheet-sides-bottom" side="SheetSide.Bottom" class="data-[side=bottom]:h-[50vh]">
    <div class="p-4">
        Open on the bottom
    </div>
</dui-sheet>
<dui-sheet id="--sheet-sides-left" side="SheetSide.Left">
    <div class="p-4">
        Open on the left
    </div>
</dui-sheet>

Dismissing the sheet

Control how the sheet can be dismissed using the closedby attribute. The default behavior allows closing via the Esc key or a close button. Set closedby="any" to also allow clicking the backdrop (light dismiss), or closedby="none" to require explicit dismissal via a close button only.

<div class="flex justify-center gap-2">
    <dui-button variant="ButtonVariant.Outline" commandfor="--sheet-dismiss-default" command="show-modal">
        Default Dismiss
    </dui-button>
    <dui-button variant="ButtonVariant.Outline" commandfor="--sheet-dismiss-light" command="show-modal">
        Light Dismiss
    </dui-button>
    <dui-button variant="ButtonVariant.Outline" commandfor="--sheet-dismiss-manual" command="show-modal">
        Manual Dismiss
    </dui-button>
</div>
<dui-sheet id="--sheet-dismiss-default">
    <dui-sheet-header>
        <dui-sheet-title>Default Dismiss</dui-sheet-title>
    </dui-sheet-header>
    <div class="p-4">
        <p>This sheet can be closed by pressing the
            <dui-kbd>Esc</dui-kbd>
            key or clicking the close button below.
        </p>
    </div>
    <dui-sheet-footer>
        <dui-button variant="ButtonVariant.Outline" commandfor="--sheet-dismiss-default" command="close">
            Close
        </dui-button>
    </dui-sheet-footer>
</dui-sheet>
<dui-sheet id="--sheet-dismiss-light" closedby="any">
    <dui-sheet-header>
        <dui-sheet-title>Light Dismiss</dui-sheet-title>
    </dui-sheet-header>
    <div class="p-4">
        <p>This sheet can be closed by pressing the
            <dui-kbd>Esc</dui-kbd>
            key, clicking on the backdrop, or clicking the close button below.
        </p>
    </div>
    <dui-sheet-footer>
        <dui-button variant="ButtonVariant.Outline" commandfor="--sheet-dismiss-light" command="close">
            Close
        </dui-button>
    </dui-sheet-footer>
</dui-sheet>
<dui-sheet id="--sheet-dismiss-manual" closedby="none">
    <dui-sheet-header>
        <dui-sheet-title>Manual Dismiss</dui-sheet-title>
    </dui-sheet-header>
    <div class="p-4">
        <p>This sheet can only be closed by clicking the close button below.</p>
    </div>
    <dui-sheet-footer>
        <dui-button variant="ButtonVariant.Outline" commandfor="--sheet-dismiss-manual" command="close">
            Close
        </dui-button>
    </dui-sheet-footer>
</dui-sheet>

Without a close button

By default the sheet renders a close button in the top-right corner. Set show-close-button="false" to hide it.

<div class="flex justify-center">
    <dui-button variant="ButtonVariant.Outline" commandfor="--sheet-no-close-button" command="show-modal">
        Open
    </dui-button>
</div>
<dui-sheet id="--sheet-no-close-button" show-close-button="false">
    <dui-sheet-header>
        <dui-sheet-title>No Close Button</dui-sheet-title>
        <dui-sheet-description>This sheet doesn't have a close button in the top-right corner. Press Esc to close.</dui-sheet-description>
    </dui-sheet-header>
</dui-sheet>

Get form content depending on return value

Add submit buttons to a form with method="dialog" and set a value on each button. Check returnValue to determine which button was clicked, then use FormData in the close event handler to read the submitted values.

If you want to return a result from the sheet, we strongly recommend using the dialog() helper which allows you to use sheets in an async/await style

<dui-stack align="StackAlign.Start" class="min-w-md">
    <dui-button variant="ButtonVariant.Outline" commandfor="--sheet-return-form-value-sheet" command="show-modal">
        Open Sheet with Form
    </dui-button>
    <label class="text-sm font-bold">Output:</label>
    <div class="font-mono w-full h-40 overflow-y-auto bg-gray-50" id="--sheet-return-form-value-output">-</div>
</dui-stack>
<dui-sheet id="--sheet-return-form-value-sheet">
    <form method="dialog"  class="flex flex-col h-full gap-4">
        <dui-sheet-header>
            <dui-sheet-title>Edit profile</dui-sheet-title>
            <dui-sheet-description>Make changes to your profile here. Click save when you're done.
            </dui-sheet-description>
        </dui-sheet-header>
        <dui-field-group class="grid gap-6 px-4">
            <dui-field>
                <dui-label for="--sheet-return-form-value-name">Name</dui-label>
                <dui-input id="--sheet-return-form-value-name" name="name" defaultValue="Ibn Battuta" required/>
            </dui-field>
            <dui-field>
                <dui-label for="--sheet-return-form-value-username">Username</dui-label>
                <dui-input id="--sheet-return-form-value-username" name="username" defaultValue="@@ibnbattuta"
                           required/>
            </dui-field>
        </dui-field-group>
        <dui-sheet-footer>
            <dui-button variant="ButtonVariant.Outline" type="button" commandfor="--sheet-return-form-value-sheet"
                        command="close">
                Cancel
            </dui-button>
            <dui-button type="submit" value="confirm">
                Save Changes
            </dui-button>
        </dui-sheet-footer>
    </form>
</dui-sheet>
<script>
    (function() {
        const sheet = document.getElementById("--sheet-return-form-value-sheet");
        const output = document.getElementById("--sheet-return-form-value-output");

        sheet.addEventListener("close", () => {
            const cancelled = sheet.returnValue === "" || sheet.returnValue === "cancel";
            if (cancelled) {
                output.innerHTML = "Cancelled";
                return;
            }

            const form = sheet.querySelector("form");
            const data = Object.fromEntries(new FormData(form));
            output.innerHTML = JSON.stringify(data, null, 2);
        });
        sheet.addEventListener("toggle", (e) => {
            // Reset the return value every time the dialog opens to prevent a previous
            // returnValue from being returned when pressing the Esc key
            if (e.newState === "open") {
                sheet.returnValue = "";
            }
        });
    })();
</script>

JavaScript API

Since <dui-sheet> renders a native dialog element, you can control it directly via JavaScript using the showModal(), show(), and close().

<dui-group justify="GroupJustify.Center">
    <dui-button variant="ButtonVariant.Outline" id="--sheet-js-api-show-modal-button">
        Show Modal
    </dui-button>
    <dui-button variant="ButtonVariant.Outline" id="--sheet-js-api-show-button">
        Show
    </dui-button>
</dui-group>
<dui-sheet id="--sheet-js-api-sheet">
    <dui-sheet-header>
        <dui-sheet-title>JavaScript API</dui-sheet-title>
        <dui-sheet-description>You can open and close this sheet using the dialog JS API</dui-sheet-description>
    </dui-sheet-header>
    <dui-sheet-footer>
        <dui-button variant="ButtonVariant.Outline" id="--sheet-js-api-close-button">
            Close via JS
        </dui-button>
    </dui-sheet-footer>
</dui-sheet>
<script>
    (function() {
        const sheet = document.getElementById("--sheet-js-api-sheet");
        const showModalButton = document.getElementById("--sheet-js-api-show-modal-button");
        const showButton = document.getElementById("--sheet-js-api-show-button");
        const closeButton = document.getElementById("--sheet-js-api-close-button");

        showModalButton.addEventListener("click", () => {
            sheet.showModal();
        });

        showButton.addEventListener("click", () => {
            sheet.show();
        });

        closeButton.addEventListener("click", () => {
            sheet.close();
        });
    })();
</script>

JavaScript events

The sheet fires beforetoggle and toggle events as it opens and closes, a cancel event when the user attempts to close via Esc or request-close, and a close event once closed. Call preventDefault() in beforetoggle to prevent opening, or in cancel to prevent closing.

<dui-stack gap="StackGap.Small" align="StackAlign.Start" class="min-w-md">
    <dui-button variant="ButtonVariant.Outline" commandfor="--sheet-js-events-sheet" command="show-modal">
        Open Sheet
    </dui-button>
    <dui-input type="checkbox"
               id="--sheet-js-events-prevent-toggle-checkbox"
               label="PreventDefault() in beforetoggle event"
               description="Will prevent the sheet from opening"/>
    <label class="text-sm font-bold">Events:</label>
    <div class="font-mono w-full h-40 overflow-y-auto bg-gray-50" id="--sheet-js-events-output">
    </div>
</dui-stack>
<dui-sheet id="--sheet-js-events-sheet">
    <dui-sheet-header>
        <dui-sheet-title>Events</dui-sheet-title>
        <dui-sheet-description>This demonstrates the events on the sheet - or rather, the underlying dialog
        </dui-sheet-description>
    </dui-sheet-header>
    <div class="p-4">
        <dui-input type="checkbox"
                   id="--sheet-js-events-prevent-cancel-checkbox"
                   label="PreventDefault() in cancel event"
                   description="Will prevent the Request Close button from closing the sheet"/>
    </div>
    <dui-sheet-footer>
        <dui-button variant="ButtonVariant.Outline" commandfor="--sheet-js-events-sheet" command="close">
            Close
        </dui-button>
        <dui-button variant="ButtonVariant.Outline" commandfor="--sheet-js-events-sheet" command="request-close">
            Request Close
        </dui-button>
    </dui-sheet-footer>
</dui-sheet>
<script>
    (function () {
        let counter = 0;
        const sheet = document.getElementById('--sheet-js-events-sheet');
        const output = document.getElementById('--sheet-js-events-output');
        const preventToggleCheckbox = document.getElementById('--sheet-js-events-prevent-toggle-checkbox');
        const preventCancelCheckbox = document.getElementById('--sheet-js-events-prevent-cancel-checkbox');

        sheet.addEventListener("beforetoggle", (e) => {
            if (preventToggleCheckbox.checked === true) {
                output.innerText = `${++counter}: beforetoggle event prevented!\n` + output.innerText;
                e.preventDefault();
                return;
            }
            output.innerText = `${++counter}: beforetoggle event: ${e.oldState} -> ${e.newState}\n` + output.innerText;
        });
        sheet.addEventListener("toggle", (e) => {
            output.innerText = `${++counter}: toggle event: ${e.oldState} -> ${e.newState}\n` + output.innerText;
        });
        sheet.addEventListener("cancel", (e) => {
            if (preventCancelCheckbox.checked === true) {
                output.innerText = `${++counter}: cancel event prevented!\n` + output.innerText;
                e.preventDefault();
                return;
            }
            output.innerText = `${++counter}: cancel event\n` + output.innerText;
        });
        sheet.addEventListener("close", (e) => {
            output.innerText = `${++counter}: close event\n` + output.innerText;
        });
    })();
</script>

On this page