Dialog

Overlay primitive that coordinates native dialog behavior with shared overlay stack rules.

Default

Without modal a dialog is non-modal, like native show(): the rest of the page stays usable and the header close button is its exit. Label becomes the header title beside that button. Buttons in the actions slot sit right-aligned in the footer, secondary first and primary last; native command="close" closes the dialog.

Send Fellowship memo?

Frodo and Sam will receive the updated route. Sméagol is keeping the meeting notes.

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-dialog/ui-dialog.html"
>

<ui-button
  id="open-confirmation"
>Review Fellowship memo</ui-button>
<ui-dialog
  id="confirmation-dialog"
  for="open-confirmation"
  label="Send Fellowship memo?"
>
  <p>Frodo and Sam will receive the updated route. Sméagol is keeping the meeting notes.</p>
  <ui-button
    slot="actions"
    commandfor="confirmation-dialog"
    command="close"
  >Keep draft</ui-button>
  <ui-button
    slot="actions"
    variant="solid"
    commandfor="confirmation-dialog"
    command="close"
  >Send memo</ui-button>
</ui-dialog>
<script setup>
  import { UiButton, UiDialog } from "@threadlabs/looma";
</script>

<template>
  <UiButton
    id="open-confirmation"
  >Review Fellowship memo</UiButton>
  <UiDialog
    id="confirmation-dialog"
    for="open-confirmation"
    label="Send Fellowship memo?"
  >
    <p>Frodo and Sam will receive the updated route. Sméagol is keeping the meeting notes.</p>
    <template #actions>
      <UiButton
        commandfor="confirmation-dialog"
        command="close"
      >Keep draft</UiButton>
      <UiButton
        variant="solid"
        commandfor="confirmation-dialog"
        command="close"
      >Send memo</UiButton>
    </template>
  </UiDialog>
</template>
import { UiButton, UiDialog } from "@threadlabs/looma";

export default function Example() {
  return <>
    <UiButton
      id="open-confirmation"
    >Review Fellowship memo</UiButton>
    <UiDialog
      id="confirmation-dialog"
      htmlFor="open-confirmation"
      label="Send Fellowship memo?"
      slots={{
        actions: <>
          <UiButton
            commandfor="confirmation-dialog"
            command="close"
          >Keep draft</UiButton>
          <UiButton
            variant="solid"
            commandfor="confirmation-dialog"
            command="close"
          >Send memo</UiButton>
        </>,
      }}
    >
      <p>Frodo and Sam will receive the updated route. Sméagol is keeping the meeting notes.</p>
    </UiDialog>
  </>;
}
/** @jsxImportSource preact */
import { UiButton, UiDialog } from "@threadlabs/looma";

export default function Example() {
  return <>
    <UiButton
      id="open-confirmation"
    >Review Fellowship memo</UiButton>
    <UiDialog
      id="confirmation-dialog"
      htmlFor="open-confirmation"
      label="Send Fellowship memo?"
      slots={{
        actions: <>
          <UiButton
            commandfor="confirmation-dialog"
            command="close"
          >Keep draft</UiButton>
          <UiButton
            variant="solid"
            commandfor="confirmation-dialog"
            command="close"
          >Send memo</UiButton>
        </>,
      }}
    >
      <p>Frodo and Sam will receive the updated route. Sméagol is keeping the meeting notes.</p>
    </UiDialog>
  </>;
}
<script>
  import { UiButton, UiDialog } from "@threadlabs/looma";
</script>

{#snippet actions()}
  <UiButton
    commandfor="confirmation-dialog"
    command="close"
  >Keep draft</UiButton>
  <UiButton
    variant="solid"
    commandfor="confirmation-dialog"
    command="close"
  >Send memo</UiButton>
{/snippet}

<UiButton
  id="open-confirmation"
>Review Fellowship memo</UiButton>
<UiDialog
  id="confirmation-dialog"
  for="open-confirmation"
  label="Send Fellowship memo?"
  slots={{ actions }}
>
  <p>Frodo and Sam will receive the updated route. Sméagol is keeping the meeting notes.</p>
</UiDialog>
import { Component } from "@angular/core";
import { UiButton, UiDialog } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiButton, UiDialog],
  template: `
    <ng-template
      ui-button
      [hnAttributes]="{ id: 'open-confirmation' }"
    >Review Fellowship memo</ng-template>
    <dialog
      ui-dialog
      id="confirmation-dialog"
      [for]="'open-confirmation'"
      [label]="'Send Fellowship memo?'"
    >
      <p>Frodo and Sam will receive the updated route. Sméagol is keeping the meeting notes.</p>
      <ng-container ngProjectAs="[slot='actions']">
        <ng-template
          ui-button
          [hnAttributes]="{ commandfor: 'confirmation-dialog', command: 'close' }"
        >Keep draft</ng-template>
        <ng-template
          ui-button
          [variant]="'solid'"
          [hnAttributes]="{ commandfor: 'confirmation-dialog', command: 'close' }"
        >Send memo</ng-template>
      </ng-container>
    </dialog>
  `,
})
export default class Example {}

modal

Modal opens the dialog in the top layer over a backdrop and makes the rest of the page inert until it closes.

Delete project?

This permanently removes the project and its history.

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-dialog/ui-dialog.html"
>

<ui-button
  id="open-modal-dialog"
>Open modal dialog</ui-button>
<ui-dialog
  id="modal-dialog"
  for="open-modal-dialog"
  modal
  label="Delete project?"
>
  <p>This permanently removes the project and its history.</p>
  <ui-button
    slot="actions"
    commandfor="modal-dialog"
    command="close"
  >Cancel</ui-button>
  <ui-button
    slot="actions"
    variant="danger"
    commandfor="modal-dialog"
    command="close"
  >Delete</ui-button>
</ui-dialog>
<script setup>
  import { UiButton, UiDialog } from "@threadlabs/looma";
</script>

<template>
  <UiButton
    id="open-modal-dialog"
  >Open modal dialog</UiButton>
  <UiDialog
    id="modal-dialog"
    for="open-modal-dialog"
    :modal="true"
    label="Delete project?"
  >
    <p>This permanently removes the project and its history.</p>
    <template #actions>
      <UiButton
        commandfor="modal-dialog"
        command="close"
      >Cancel</UiButton>
      <UiButton
        variant="danger"
        commandfor="modal-dialog"
        command="close"
      >Delete</UiButton>
    </template>
  </UiDialog>
</template>
import { UiButton, UiDialog } from "@threadlabs/looma";

export default function Example() {
  return <>
    <UiButton
      id="open-modal-dialog"
    >Open modal dialog</UiButton>
    <UiDialog
      id="modal-dialog"
      htmlFor="open-modal-dialog"
      modal
      label="Delete project?"
      slots={{
        actions: <>
          <UiButton
            commandfor="modal-dialog"
            command="close"
          >Cancel</UiButton>
          <UiButton
            variant="danger"
            commandfor="modal-dialog"
            command="close"
          >Delete</UiButton>
        </>,
      }}
    >
      <p>This permanently removes the project and its history.</p>
    </UiDialog>
  </>;
}
/** @jsxImportSource preact */
import { UiButton, UiDialog } from "@threadlabs/looma";

export default function Example() {
  return <>
    <UiButton
      id="open-modal-dialog"
    >Open modal dialog</UiButton>
    <UiDialog
      id="modal-dialog"
      htmlFor="open-modal-dialog"
      modal
      label="Delete project?"
      slots={{
        actions: <>
          <UiButton
            commandfor="modal-dialog"
            command="close"
          >Cancel</UiButton>
          <UiButton
            variant="danger"
            commandfor="modal-dialog"
            command="close"
          >Delete</UiButton>
        </>,
      }}
    >
      <p>This permanently removes the project and its history.</p>
    </UiDialog>
  </>;
}
<script>
  import { UiButton, UiDialog } from "@threadlabs/looma";
</script>

{#snippet actions()}
  <UiButton
    commandfor="modal-dialog"
    command="close"
  >Cancel</UiButton>
  <UiButton
    variant="danger"
    commandfor="modal-dialog"
    command="close"
  >Delete</UiButton>
{/snippet}

<UiButton
  id="open-modal-dialog"
>Open modal dialog</UiButton>
<UiDialog
  id="modal-dialog"
  for="open-modal-dialog"
  modal
  label="Delete project?"
  slots={{ actions }}
>
  <p>This permanently removes the project and its history.</p>
</UiDialog>
import { Component } from "@angular/core";
import { UiButton, UiDialog } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiButton, UiDialog],
  template: `
    <ng-template
      ui-button
      [hnAttributes]="{ id: 'open-modal-dialog' }"
    >Open modal dialog</ng-template>
    <dialog
      ui-dialog
      id="modal-dialog"
      [for]="'open-modal-dialog'"
      [modal]="true"
      [label]="'Delete project?'"
    >
      <p>This permanently removes the project and its history.</p>
      <ng-container ngProjectAs="[slot='actions']">
        <ng-template
          ui-button
          [hnAttributes]="{ commandfor: 'modal-dialog', command: 'close' }"
        >Cancel</ng-template>
        <ng-template
          ui-button
          [variant]="'danger'"
          [hnAttributes]="{ commandfor: 'modal-dialog', command: 'close' }"
        >Delete</ng-template>
      </ng-container>
    </dialog>
  `,
})
export default class Example {}

Outside dismissal

A modal dialog closes on Escape by default; closedby="any" also closes it after a press outside.

Keyboard shortcuts

Press Escape or click the backdrop to close this dialog.

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-dialog/ui-dialog.html"
>

<ui-button
  id="open-dismissible-dialog"
>Open dismissible dialog</ui-button>
<ui-dialog
  id="dismissible-dialog"
  for="open-dismissible-dialog"
  modal
  closedby="any"
  label="Keyboard shortcuts"
>
  <p>Press <kbd>Escape</kbd> or click the backdrop to close this dialog.</p>
</ui-dialog>
<script setup>
  import { UiButton, UiDialog } from "@threadlabs/looma";
</script>

<template>
  <UiButton
    id="open-dismissible-dialog"
  >Open dismissible dialog</UiButton>
  <UiDialog
    id="dismissible-dialog"
    for="open-dismissible-dialog"
    :modal="true"
    closedby="any"
    label="Keyboard shortcuts"
  >
    <p>Press <kbd>Escape</kbd> or click the backdrop to close this dialog.</p>
  </UiDialog>
</template>
import { UiButton, UiDialog } from "@threadlabs/looma";

export default function Example() {
  return <>
    <UiButton
      id="open-dismissible-dialog"
    >Open dismissible dialog</UiButton>
    <UiDialog
      id="dismissible-dialog"
      htmlFor="open-dismissible-dialog"
      modal
      closedby="any"
      label="Keyboard shortcuts"
    >
      <p>Press <kbd>Escape</kbd> or click the backdrop to close this dialog.</p>
    </UiDialog>
  </>;
}
/** @jsxImportSource preact */
import { UiButton, UiDialog } from "@threadlabs/looma";

export default function Example() {
  return <>
    <UiButton
      id="open-dismissible-dialog"
    >Open dismissible dialog</UiButton>
    <UiDialog
      id="dismissible-dialog"
      htmlFor="open-dismissible-dialog"
      modal
      closedby="any"
      label="Keyboard shortcuts"
    >
      <p>Press <kbd>Escape</kbd> or click the backdrop to close this dialog.</p>
    </UiDialog>
  </>;
}
<script>
  import { UiButton, UiDialog } from "@threadlabs/looma";
</script>

<UiButton
  id="open-dismissible-dialog"
>Open dismissible dialog</UiButton>
<UiDialog
  id="dismissible-dialog"
  for="open-dismissible-dialog"
  modal
  closedby="any"
  label="Keyboard shortcuts"
>
  <p>Press <kbd>Escape</kbd> or click the backdrop to close this dialog.</p>
</UiDialog>
import { Component } from "@angular/core";
import { UiButton, UiDialog } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiButton, UiDialog],
  template: `
    <ng-template
      ui-button
      [hnAttributes]="{ id: 'open-dismissible-dialog' }"
    >Open dismissible dialog</ng-template>
    <dialog
      ui-dialog
      id="dismissible-dialog"
      [for]="'open-dismissible-dialog'"
      [modal]="true"
      [closedby]="'any'"
      [label]="'Keyboard shortcuts'"
    >
      <p>Press <kbd>Escape</kbd> or click the backdrop to close this dialog.</p>
    </dialog>
  `,
})
export default class Example {}

Long content

The header and actions stay pinned while only the body scrolls, so the title, close button, and primary action are always reachable.

Terms of service

Section 1. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.

Section 2. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.

Section 3. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.

Section 4. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.

Section 5. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.

Section 6. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.

Section 7. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.

Section 8. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.

Section 9. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.

Section 10. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.

Section 11. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.

Section 12. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.

Section 13. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.

Section 14. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.

Section 15. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.

Section 16. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-dialog/ui-dialog.html"
>

<ui-button
  id="open-long-dialog"
>Open long dialog</ui-button>
<ui-dialog
  id="long-dialog"
  for="open-long-dialog"
  modal
  closedby="any"
  label="Terms of service"
>
  <p>Section 1. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 2. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 3. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 4. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 5. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 6. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 7. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 8. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 9. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 10. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 11. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 12. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 13. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 14. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 15. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 16. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <ui-button
    slot="actions"
    commandfor="long-dialog"
    command="close"
  >Decline</ui-button>
  <ui-button
    slot="actions"
    variant="solid"
    commandfor="long-dialog"
    command="close"
  >Accept</ui-button>
</ui-dialog>
<script setup>
  import { UiButton, UiDialog } from "@threadlabs/looma";
</script>

<template>
  <UiButton
    id="open-long-dialog"
  >Open long dialog</UiButton>
  <UiDialog
    id="long-dialog"
    for="open-long-dialog"
    :modal="true"
    closedby="any"
    label="Terms of service"
  >
    <p>Section 1. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
    <p>Section 2. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
    <p>Section 3. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
    <p>Section 4. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
    <p>Section 5. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
    <p>Section 6. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
    <p>Section 7. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
    <p>Section 8. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
    <p>Section 9. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
    <p>Section 10. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
    <p>Section 11. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
    <p>Section 12. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
    <p>Section 13. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
    <p>Section 14. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
    <p>Section 15. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
    <p>Section 16. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
    <template #actions>
      <UiButton
        commandfor="long-dialog"
        command="close"
      >Decline</UiButton>
      <UiButton
        variant="solid"
        commandfor="long-dialog"
        command="close"
      >Accept</UiButton>
    </template>
  </UiDialog>
</template>
import { UiButton, UiDialog } from "@threadlabs/looma";

export default function Example() {
  return <>
    <UiButton
      id="open-long-dialog"
    >Open long dialog</UiButton>
    <UiDialog
      id="long-dialog"
      htmlFor="open-long-dialog"
      modal
      closedby="any"
      label="Terms of service"
      slots={{
        actions: <>
          <UiButton
            commandfor="long-dialog"
            command="close"
          >Decline</UiButton>
          <UiButton
            variant="solid"
            commandfor="long-dialog"
            command="close"
          >Accept</UiButton>
        </>,
      }}
    >
      <p>Section 1. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 2. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 3. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 4. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 5. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 6. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 7. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 8. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 9. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 10. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 11. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 12. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 13. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 14. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 15. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 16. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
    </UiDialog>
  </>;
}
/** @jsxImportSource preact */
import { UiButton, UiDialog } from "@threadlabs/looma";

export default function Example() {
  return <>
    <UiButton
      id="open-long-dialog"
    >Open long dialog</UiButton>
    <UiDialog
      id="long-dialog"
      htmlFor="open-long-dialog"
      modal
      closedby="any"
      label="Terms of service"
      slots={{
        actions: <>
          <UiButton
            commandfor="long-dialog"
            command="close"
          >Decline</UiButton>
          <UiButton
            variant="solid"
            commandfor="long-dialog"
            command="close"
          >Accept</UiButton>
        </>,
      }}
    >
      <p>Section 1. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 2. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 3. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 4. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 5. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 6. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 7. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 8. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 9. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 10. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 11. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 12. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 13. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 14. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 15. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 16. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
    </UiDialog>
  </>;
}
<script>
  import { UiButton, UiDialog } from "@threadlabs/looma";
</script>

{#snippet actions()}
  <UiButton
    commandfor="long-dialog"
    command="close"
  >Decline</UiButton>
  <UiButton
    variant="solid"
    commandfor="long-dialog"
    command="close"
  >Accept</UiButton>
{/snippet}

<UiButton
  id="open-long-dialog"
>Open long dialog</UiButton>
<UiDialog
  id="long-dialog"
  for="open-long-dialog"
  modal
  closedby="any"
  label="Terms of service"
  slots={{ actions }}
>
  <p>Section 1. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 2. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 3. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 4. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 5. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 6. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 7. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 8. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 9. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 10. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 11. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 12. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 13. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 14. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 15. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
  <p>Section 16. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
</UiDialog>
import { Component } from "@angular/core";
import { UiButton, UiDialog } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiButton, UiDialog],
  template: `
    <ng-template
      ui-button
      [hnAttributes]="{ id: 'open-long-dialog' }"
    >Open long dialog</ng-template>
    <dialog
      ui-dialog
      id="long-dialog"
      [for]="'open-long-dialog'"
      [modal]="true"
      [closedby]="'any'"
      [label]="'Terms of service'"
    >
      <p>Section 1. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 2. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 3. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 4. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 5. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 6. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 7. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 8. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 9. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 10. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 11. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 12. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 13. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 14. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 15. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <p>Section 16. These terms describe how the service may be used, what data is stored, and how either party can end the agreement.</p>
      <ng-container ngProjectAs="[slot='actions']">
        <ng-template
          ui-button
          [hnAttributes]="{ commandfor: 'long-dialog', command: 'close' }"
        >Decline</ng-template>
        <ng-template
          ui-button
          [variant]="'solid'"
          [hnAttributes]="{ commandfor: 'long-dialog', command: 'close' }"
        >Accept</ng-template>
      </ng-container>
    </dialog>
  `,
})
export default class Example {}

Alert confirmation

Alert dialogs are always modal, have no header close button, and cannot close from outside. Keep an explicit, focused action available.

Delete item?

This action cannot be undone.

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-dialog/ui-dialog.html"
>

<ui-button
  id="open-alert-dialog"
>Delete item</ui-button>
<ui-dialog
  id="alert-dialog"
  for="open-alert-dialog"
  alert
  closedby="none"
  label="Delete item?"
>
  <p
    id="alert-description"
  >This action cannot be undone.</p>
  <ui-button
    slot="actions"
    commandfor="alert-dialog"
    command="close"
    autofocus
  >Cancel</ui-button>
  <ui-button
    slot="actions"
    variant="danger"
    commandfor="alert-dialog"
    command="close"
  >Delete</ui-button>
</ui-dialog>
<script setup>
  import { UiButton, UiDialog } from "@threadlabs/looma";
</script>

<template>
  <UiButton
    id="open-alert-dialog"
  >Delete item</UiButton>
  <UiDialog
    id="alert-dialog"
    for="open-alert-dialog"
    :alert="true"
    closedby="none"
    label="Delete item?"
  >
    <p
      id="alert-description"
    >This action cannot be undone.</p>
    <template #actions>
      <UiButton
        commandfor="alert-dialog"
        command="close"
        :autofocus="true"
      >Cancel</UiButton>
      <UiButton
        variant="danger"
        commandfor="alert-dialog"
        command="close"
      >Delete</UiButton>
    </template>
  </UiDialog>
</template>
import { UiButton, UiDialog } from "@threadlabs/looma";

export default function Example() {
  return <>
    <UiButton
      id="open-alert-dialog"
    >Delete item</UiButton>
    <UiDialog
      id="alert-dialog"
      htmlFor="open-alert-dialog"
      alert
      closedby="none"
      label="Delete item?"
      slots={{
        actions: <>
          <UiButton
            commandfor="alert-dialog"
            command="close"
            autoFocus
          >Cancel</UiButton>
          <UiButton
            variant="danger"
            commandfor="alert-dialog"
            command="close"
          >Delete</UiButton>
        </>,
      }}
    >
      <p
        id="alert-description"
      >This action cannot be undone.</p>
    </UiDialog>
  </>;
}
/** @jsxImportSource preact */
import { UiButton, UiDialog } from "@threadlabs/looma";

export default function Example() {
  return <>
    <UiButton
      id="open-alert-dialog"
    >Delete item</UiButton>
    <UiDialog
      id="alert-dialog"
      htmlFor="open-alert-dialog"
      alert
      closedby="none"
      label="Delete item?"
      slots={{
        actions: <>
          <UiButton
            commandfor="alert-dialog"
            command="close"
            autoFocus
          >Cancel</UiButton>
          <UiButton
            variant="danger"
            commandfor="alert-dialog"
            command="close"
          >Delete</UiButton>
        </>,
      }}
    >
      <p
        id="alert-description"
      >This action cannot be undone.</p>
    </UiDialog>
  </>;
}
<script>
  import { UiButton, UiDialog } from "@threadlabs/looma";
</script>

{#snippet actions()}
  <UiButton
    commandfor="alert-dialog"
    command="close"
    autofocus
  >Cancel</UiButton>
  <UiButton
    variant="danger"
    commandfor="alert-dialog"
    command="close"
  >Delete</UiButton>
{/snippet}

<UiButton
  id="open-alert-dialog"
>Delete item</UiButton>
<UiDialog
  id="alert-dialog"
  for="open-alert-dialog"
  alert
  closedby="none"
  label="Delete item?"
  slots={{ actions }}
>
  <p
    id="alert-description"
  >This action cannot be undone.</p>
</UiDialog>
import { Component } from "@angular/core";
import { UiButton, UiDialog } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiButton, UiDialog],
  template: `
    <ng-template
      ui-button
      [hnAttributes]="{ id: 'open-alert-dialog' }"
    >Delete item</ng-template>
    <dialog
      ui-dialog
      id="alert-dialog"
      [for]="'open-alert-dialog'"
      [alert]="true"
      [closedby]="'none'"
      [label]="'Delete item?'"
    >
      <p
        id="alert-description"
      >This action cannot be undone.</p>
      <ng-container ngProjectAs="[slot='actions']">
        <ng-template
          ui-button
          [hnAttributes]="{ commandfor: 'alert-dialog', command: 'close', autofocus: true }"
        >Cancel</ng-template>
        <ng-template
          ui-button
          [variant]="'danger'"
          [hnAttributes]="{ commandfor: 'alert-dialog', command: 'close' }"
        >Delete</ng-template>
      </ng-container>
    </dialog>
  `,
})
export default class Example {}

Sizes

Choose a common width with size. Glassiness and surface separation follow the shared theme; dialog purpose and rendered size determine the material treatment automatically. This dialog is non-modal, so the rest of the page remains interactive.

Reference details

Keep the rest of the page available while reviewing these details.

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-dialog/ui-dialog.html"
>

<ui-button
  id="open-wide-dialog"
>Open wide dialog</ui-button>
<ui-dialog
  id="wide-dialog"
  for="open-wide-dialog"
  size="lg"
  label="Reference details"
>
  <p>Keep the rest of the page available while reviewing these details.</p>
  <ui-button
    slot="actions"
    commandfor="wide-dialog"
    command="close"
  >Done</ui-button>
</ui-dialog>
<script setup>
  import { UiButton, UiDialog } from "@threadlabs/looma";
</script>

<template>
  <UiButton
    id="open-wide-dialog"
  >Open wide dialog</UiButton>
  <UiDialog
    id="wide-dialog"
    for="open-wide-dialog"
    size="lg"
    label="Reference details"
  >
    <p>Keep the rest of the page available while reviewing these details.</p>
    <template #actions>
      <UiButton
        commandfor="wide-dialog"
        command="close"
      >Done</UiButton>
    </template>
  </UiDialog>
</template>
import { UiButton, UiDialog } from "@threadlabs/looma";

export default function Example() {
  return <>
    <UiButton
      id="open-wide-dialog"
    >Open wide dialog</UiButton>
    <UiDialog
      id="wide-dialog"
      htmlFor="open-wide-dialog"
      size="lg"
      label="Reference details"
      slots={{
        actions: <UiButton
          commandfor="wide-dialog"
          command="close"
        >Done</UiButton>,
      }}
    >
      <p>Keep the rest of the page available while reviewing these details.</p>
    </UiDialog>
  </>;
}
/** @jsxImportSource preact */
import { UiButton, UiDialog } from "@threadlabs/looma";

export default function Example() {
  return <>
    <UiButton
      id="open-wide-dialog"
    >Open wide dialog</UiButton>
    <UiDialog
      id="wide-dialog"
      htmlFor="open-wide-dialog"
      size="lg"
      label="Reference details"
      slots={{
        actions: <UiButton
          commandfor="wide-dialog"
          command="close"
        >Done</UiButton>,
      }}
    >
      <p>Keep the rest of the page available while reviewing these details.</p>
    </UiDialog>
  </>;
}
<script>
  import { UiButton, UiDialog } from "@threadlabs/looma";
</script>

{#snippet actions()}
  <UiButton
    commandfor="wide-dialog"
    command="close"
  >Done</UiButton>
{/snippet}

<UiButton
  id="open-wide-dialog"
>Open wide dialog</UiButton>
<UiDialog
  id="wide-dialog"
  for="open-wide-dialog"
  size="lg"
  label="Reference details"
  slots={{ actions }}
>
  <p>Keep the rest of the page available while reviewing these details.</p>
</UiDialog>
import { Component } from "@angular/core";
import { UiButton, UiDialog } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiButton, UiDialog],
  template: `
    <ng-template
      ui-button
      [hnAttributes]="{ id: 'open-wide-dialog' }"
    >Open wide dialog</ng-template>
    <dialog
      ui-dialog
      id="wide-dialog"
      [for]="'open-wide-dialog'"
      [size]="'lg'"
      [label]="'Reference details'"
    >
      <p>Keep the rest of the page available while reviewing these details.</p>
      <ng-container ngProjectAs="[slot='actions']">
        <ng-template
          ui-button
          [hnAttributes]="{ commandfor: 'wide-dialog', command: 'close' }"
        >Done</ng-template>
      </ng-container>
    </dialog>
  `,
})
export default class Example {}

API

Package: @threadlabs/looma · Native root: dialog

Attributes

NameDescriptionPropertyTypeDefaultAllowed values
alertUse alert dialog semantics and require an explicit action instead of the header close button. An alert dialog is always modal.alertbooleanfalse—
closedbyWhich user close requests work: any includes outside presses, closerequest includes Escape, and none requires an action. Unset follows native dialog: closerequest when modal, none when not.closedbykeyword—any, closerequest, none
forID of the control that opens the dialog.forstring—
labelVisible title in the dialog header; also its accessible name.labelstring——
modalOpen in the top layer with a backdrop and make the rest of the page inert until it closes, like native showModal(). Without it the dialog is non-modal, like native show().modalbooleanfalse—
modelessDeprecated: does nothing, since dialogs are non-modal unless modal is set. Accepted so markup written for 0.15 keeps working; modal wins when both are set.modelessbooleanfalse—
openWhether the dialog is open initially or when set externally.openbooleanfalse—
sizeChoose a compact, standard, wide, or viewport-filling dialog. Shared surface separation adapts to dialog purpose and rendered size.sizekeywordmdsm, md, lg, fullscreen

Framework props

NameTypeDefaultAllowed values
alertbooleanfalse—
closedby"any" | "closerequest" | "none"—any, closerequest, none
forstring—
labelstring——
modalbooleanfalse—
modelessbooleanfalse—
openbooleanfalse—
size"sm" | "md" | "lg" | "fullscreen"mdsm, md, lg, fullscreen

Events

NameDetailDescription
closeobject
  • open: boolean (required)
  • reason: keyword (required)
  • trigger: keyword (required)
Emitted when the component transitions to a closed state.
openobject
  • open: boolean (required)
  • reason: keyword (required)
  • trigger: keyword (required)
Emitted when the component transitions to an open state.

Slots

NamePurpose
defaultDefault child content.
actionsNamed content for the actions region.

Design tokens

Set shared theme properties on a theme boundary. Component hooks apply only to their owning component.

Component tokens

TokenDeclared valuesFallback
--ui-dialog-border—var(--ui-overlay-border, var(--ui-border-strong)) | color-mix(in oklab, var(--ui-overlay-border, var(--ui-border-strong)) calc((1 - var(--_ui-material-active)) * 100%), transparent)
--ui-dialog-duration—var(--ui-motion-base)
--ui-dialog-max-width—var(--_ui-dialog-size, 440px)
--ui-dialog-radius—var(--ui-overlay-radius, var(--ui-radius-dialog)) | 0
--ui-dialog-shadow—var(--ui-overlay-shadow, var(--ui-elevation-dialog, var(--ui-shadow-xl))) | var( --ui-overlay-shadow, 0 calc(var(--_ui-material-separation) * var(--_ui-material-scale, 1) * 8px) calc( var(--_ui-material-separation) * var(--_ui-material-scale, 1) * 24px ) color-mix(in oklab, var(--ui-text-primary) 15%, transparent), var(--ui-material-glow, 0 0 transparent), inset 0 0 0 calc(var(--_ui-material-active) * var(--ui-border-width)) var(--ui-material-edge, var(--ui-border-strong)), var(--ui-elevation-dialog, var(--ui-shadow-xl)) )
--ui-dialog-surface—var(--ui-overlay-surface, var(--ui-surface-elevated)) | var(--_ui-material-underpaint, var(--ui-material-content, var(--ui-overlay-surface, var(--ui-surface-elevated))))
--ui-dialog-viewport-gap—var(--ui-space-4)

Shared tokens

TokenFallback
--ui-border-strong—
--ui-border-width—
--ui-elevation-dialogvar(--ui-shadow-xl)
--ui-font-semibold—
--ui-font-size-lg—
--ui-glassiness0
--ui-line-height-tight—
--ui-material-contentvar(--ui-overlay-surface, var(--ui-surface-elevated))
--ui-material-edgevar(--ui-border-strong)
--ui-material-glassvar(--ui-surface-elevated)
--ui-material-glow0 0 transparent
--ui-material-highlightvar(--ui-surface-highlight, linear-gradient(transparent, transparent))
--ui-motion-base—
--ui-motion-ease—
--ui-overlay-backdrop—
--ui-overlay-bordervar(--ui-border-strong)
--ui-overlay-radiusvar(--ui-radius-dialog)
--ui-overlay-shadowvar(--ui-elevation-dialog, var(--ui-shadow-xl)) | 0 calc(var(--_ui-material-separation) * var(--_ui-material-scale, 1) * 8px) calc( var(--_ui-material-separation) * var(--_ui-material-scale, 1) * 24px ) color-mix(in oklab, var(--ui-text-primary) 15%, transparent), var(--ui-material-glow, 0 0 transparent), inset 0 0 0 calc(var(--_ui-material-active) * var(--ui-border-width)) var(--ui-material-edge, var(--ui-border-strong)), var(--ui-elevation-dialog, var(--ui-shadow-xl))
--ui-overlay-surfacevar(--ui-surface-elevated)
--ui-radius-dialog—
--ui-shadow-xl—
--ui-space-1—
--ui-space-2—
--ui-space-3—
--ui-space-4—
--ui-space-5—
--ui-surface-elevated—
--ui-surface-highlightlinear-gradient(transparent, transparent)
--ui-surface-separation0
--ui-text-primary—