Diwa Design System

Flyout

A full-height overlay panel that slides in from the edge of the viewport. Follows the controlled component pattern — the consumer manages open state and responds to the dismiss event.

When to use

Do

  • Use for contextual details, settings, or forms that augment the current view without full navigation.
  • Use the footer slot for primary and secondary actions so they stay visible above the fold.
  • Use position="start" for navigation drawers or filter panels that originate from the left.
  • Always provide a meaningful heading so screen readers announce the dialog purpose.

Don't

  • Don't use for critical blocking confirmations — use a modal dialog instead.
  • Don't nest multiple flyouts — open only one at a time.
  • Don't use for content that requires full-page context or complex multi-step flows.
  • Don't forget to return focus to the trigger element when the flyout closes.

Controlled pattern

The flyout follows the controlled component pattern. The consumer owns the open state and must update it in response to the dismiss event. The flyout never mutates open internally — it only emits dismiss.

Vanilla JS

<!-- Trigger -->
<diwa-button id="open-btn">Open flyout</diwa-button>

<!-- Flyout -->
<diwa-flyout id="my-flyout" heading="Settings">
  <p>Flyout body content.</p>
  <div slot="footer">
    <diwa-button id="save-btn">Save</diwa-button>
  </div>
</diwa-flyout>

<script>
  const flyout = document.getElementById('my-flyout');
  const openBtn = document.getElementById('open-btn');

  openBtn.addEventListener('click', () => { flyout.open = true; });
  flyout.addEventListener('dismiss', () => { flyout.open = false; });
</script>

React

import { useState } from 'react';

function SettingsPage() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <>
      <diwa-button onclick={() => setIsOpen(true)}>Open flyout</diwa-button>

      <diwa-flyout
        open={isOpen}
        heading="Settings"
        ondismiss={() => setIsOpen(false)}
      >
        <p>Flyout body content.</p>
        <div slot="footer">
          <diwa-button onclick={() => setIsOpen(false)}>Save</diwa-button>
        </div>
      </diwa-flyout>
    </>
  );
}

Slots

SlotDescription
defaultScrollable body content. Fills the available space between the header and footer.
headerExtra content placed in the header row after the heading text and before the dismiss button.
footerSticky footer content (typically action buttons). The footer area is automatically hidden when this slot is empty.

Custom width

Override the panel width using the --diwa-flyout-width CSS custom property. The panel has a hard minimum of 320px and a maximum of 100vw.

/* Override the default 480px width via the CSS custom property */
diwa-flyout {
  --diwa-flyout-width: 600px;
}

Body scroll lock

When the flyout opens, document.body.style.overflow is set to hidden to prevent background content from scrolling beneath the backdrop. It is restored when the flyout closes or the element is removed from the DOM (disconnectedCallback). If your application manages body scroll independently, you may need to coordinate with this behaviour.