# Dialog / Diálogo

> Modal window for a short task or a confirmation.

Page: https://componentes-lovat.vercel.app/c/dialog/  
License: MIT · Design: coss ui (MIT) · dialog.tsx  
Requires: tokens.css, button, text-field  
Context: ambos · Density: media · Tone: sobria

## Files

- Tokens (once): https://componentes-lovat.vercel.app/tokens.css
- Dependency `button`: https://componentes-lovat.vercel.app/r/button.json
- Dependency `text-field`: https://componentes-lovat.vercel.app/r/text-field.json
- HTML: https://componentes-lovat.vercel.app/raw/dialog/dialog.html
- CSS: https://componentes-lovat.vercel.app/raw/dialog/dialog.css
- JS: https://componentes-lovat.vercel.app/raw/dialog/dialog.js
- Everything in one file: https://componentes-lovat.vercel.app/r/dialog.html

Dark theme: add class `dark` to `<html>`.

## When to use

- To confirm something irreversible.
- For a short task.

## When not to use

- To say something worked: use a toast.

## Accessibility

Roles: dialog, alertdialog

| Keys | Action |
|---|---|
| Esc | Closes |
| Tab | Cycles within the dialog |

- <dialog> with showModal(): the rest of the page is inert.
- Focus returns to the opening button.
- Opens with commandfor; dialog.js is the fallback.

## Code

```html
<button class="c-btn c-btn--outline" type="button" commandfor="dlg-profile" command="show-modal">Editar perfil</button>
<button class="c-btn c-btn--destructive" type="button" commandfor="dlg-delete" command="show-modal">Eliminar cuenta</button>

<dialog class="c-dialog" id="dlg-profile" aria-labelledby="dlg-profile-title" aria-describedby="dlg-profile-desc" closedby="any">
  <form class="c-dialog__form" method="dialog">
    <div class="c-dialog__header">
      <h2 class="c-dialog__title" id="dlg-profile-title">Editar perfil</h2>
      <p class="c-dialog__description" id="dlg-profile-desc">Cambia tus datos y guarda cuando termines.</p>
    </div>
    <div class="c-dialog__panel">
      <div class="c-field"><label class="c-label" for="dlg-name">Nombre</label><span class="c-input"><input class="c-input__field" id="dlg-name" value="Ada Lovelace" autofocus></span></div>
    </div>
    <div class="c-dialog__footer">
      <button class="c-btn c-btn--ghost" value="cancel" formnovalidate>Cancelar</button>
      <button class="c-btn c-btn--primary" value="save">Guardar</button>
    </div>
    <button class="c-btn c-btn--ghost c-btn--icon c-dialog__close" type="button" commandfor="dlg-profile" command="close" aria-label="Cerrar"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg></button>
  </form>
</dialog>

<dialog class="c-dialog" id="dlg-delete" role="alertdialog" aria-labelledby="dlg-delete-title" aria-describedby="dlg-delete-desc">
  <form class="c-dialog__form" method="dialog">
    <div class="c-dialog__header">
      <h2 class="c-dialog__title" id="dlg-delete-title">¿Eliminar tu cuenta?</h2>
      <p class="c-dialog__description" id="dlg-delete-desc">Se borran tus proyectos y no se puede deshacer.</p>
    </div>
    <div class="c-dialog__footer">
      <button class="c-btn c-btn--outline" value="cancel" autofocus>Cancelar</button>
      <button class="c-btn c-btn--destructive" value="delete">Eliminar</button>
    </div>
  </form>
</dialog>
```

```css
/* dialog · Componentes (MIRÓ Labs). Diseño de coss ui (MIT, © coss.com, antes Origin UI).
   Generado por tools/build_css.py desde dialog.tw.json. No editar a mano. Necesita tokens.css. */
.c-dialog__close{position:absolute;inset-inline-end:calc(var(--spacing)*2);top:calc(var(--spacing)*2)}
.c-dialog{display:flex}
.c-dialog__header{display:flex}
.c-dialog__footer{display:flex}
.c-dialog{max-height:100%;min-height:calc(var(--spacing)*0);width:100%;max-width:var(--container-lg);min-width:calc(var(--spacing)*0);transform-origin:50%;flex-direction:column}
.c-dialog__header{flex-direction:column}
.c-dialog__footer{flex-direction:column-reverse}
.c-dialog__header{gap:calc(var(--spacing)*2)}
.c-dialog__footer{gap:calc(var(--spacing)*2)}
.c-dialog{border-radius:var(--radius-2xl);border-style:var(--tw-border-style);border-width:1px;background-color:var(--popover)}
.c-dialog__header{padding:calc(var(--spacing)*6)}
.c-dialog__panel{padding:calc(var(--spacing)*6)}
.c-dialog__footer{padding-inline:calc(var(--spacing)*6)}
.c-dialog__panel{padding-top:calc(var(--spacing)*1)}
.c-dialog__footer{padding-top:calc(var(--spacing)*4);padding-bottom:calc(var(--spacing)*6)}
.c-dialog__description{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}
.c-dialog__title{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height));--tw-leading:1;line-height:1;--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}
.c-dialog__description{color:var(--muted-foreground)}
.c-dialog{color:var(--popover-foreground);--tw-shadow-alpha:5%;--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000000d),0 4px 6px -4px var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}
@supports (color:lab(0% 0 0)){
.c-dialog{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,lab(0% 0 0/.05)),0 4px 6px -4px var(--tw-shadow-color,lab(0% 0 0/.05))}
}
.c-dialog{transition-property:scale,opacity,translate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));--tw-duration:.2s;transition-duration:.2s;--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out);will-change:transform;--tw-outline-style:none;outline-style:none}
.c-dialog:not(:is(.dark *)){background-clip:padding-box}
.c-dialog:before{content:var(--tw-content);pointer-events:none;content:var(--tw-content);position:absolute;content:var(--tw-content);inset:calc(var(--spacing)*0);content:var(--tw-content);border-radius:calc(var(--radius-2xl) - 1px);content:var(--tw-content);--tw-shadow:0 1px var(--tw-shadow-color,#0000000a)}
@supports (color:color-mix(in lab, red, red)){
.c-dialog:before{--tw-shadow:0 1px var(--tw-shadow-color,color-mix(in oklab,var(--color-black)4%,transparent))}
}
.c-dialog:before{box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}
@media not all and (min-width:40rem){
.c-dialog{max-width:none;transform-origin:bottom;border-radius:0;border-inline-style:var(--tw-border-style);border-inline-width:0;border-top-style:var(--tw-border-style);border-top-width:1px;border-bottom-style:var(--tw-border-style);border-bottom-width:0}
.c-dialog__header{padding-bottom:calc(var(--spacing)*4)}
.c-dialog:before{content:var(--tw-content);display:none;content:var(--tw-content);border-radius:0}
}
@media (min-width:40rem){
.c-dialog__footer{flex-direction:row;justify-content:flex-end;border-bottom-right-radius:calc(var(--radius-2xl) - 1px);border-bottom-left-radius:calc(var(--radius-2xl) - 1px)}
}
.c-dialog:is(.dark *):before{content:var(--tw-content);--tw-shadow:0 -1px var(--tw-shadow-color,#ffffff0f)}
@supports (color:color-mix(in lab, red, red)){
.c-dialog:is(.dark *):before{--tw-shadow:0 -1px var(--tw-shadow-color,color-mix(in oklab,var(--color-white)6%,transparent))}
}
.c-dialog:is(.dark *):before{box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}
/* propio */
.c-dialog{position:fixed;padding:0;margin:auto;max-height:calc(100dvh - 2rem);width:calc(100% - 2rem);color:var(--popover-foreground);opacity:0;scale:.98;transition:opacity .2s ease-in-out,scale .2s ease-in-out,translate .2s ease-in-out,overlay .2s allow-discrete,display .2s allow-discrete}
.c-dialog[open]{opacity:1;scale:1}
@starting-style{.c-dialog[open]{opacity:0;scale:.98}}
.c-dialog::backdrop{background:color-mix(in oklab,#000 32%,transparent);backdrop-filter:blur(4px);opacity:0;transition:opacity .2s,overlay .2s allow-discrete,display .2s allow-discrete}
.c-dialog[open]::backdrop{opacity:1}
@starting-style{.c-dialog[open]::backdrop{opacity:0}}
@media (width < 40rem){.c-dialog{width:100%;max-width:none;margin:auto 0 0;scale:1;translate:0 1rem}.c-dialog[open]{translate:0 0}@starting-style{.c-dialog[open]{translate:0 1rem}}}
.c-dialog__close{position:absolute}
.c-dialog__form{display:contents}
html:has(.c-dialog[open]:modal){overflow:hidden}
```

```js
/* Diálogo: respaldo para navegadores sin commandfor ni closedby.
   Con soporte nativo, este archivo no hace nada. */
(function () {
  const C = (window.Componentes = window.Componentes || {});
  const hasCommand = 'command' in HTMLButtonElement.prototype;
  const hasClosedBy = 'closedBy' in HTMLDialogElement.prototype;

  function open(dialog) { try { if (!dialog.open) dialog.showModal(); } catch (e) { console.warn('[dialog]', e); } }
  function close(dialog, value) { try { dialog.close(value); } catch (e) { console.warn('[dialog]', e); } }

  if (!hasCommand) {
    document.addEventListener('click', (e) => {
      const btn = e.target.closest?.('button[commandfor]');
      if (!btn) return;
      const dialog = document.getElementById(btn.getAttribute('commandfor'));
      if (!(dialog instanceof HTMLDialogElement)) return;
      const cmd = btn.getAttribute('command');
      if (cmd === 'show-modal') open(dialog);
      else if (cmd === 'close' || cmd === 'request-close') close(dialog);
    });
  }

  // closedby="any": clic en el fondo cierra. Se mide el rectángulo, no el target,
  // para que soltar el ratón fuera tras seleccionar texto dentro no cierre.
  if (!hasClosedBy) {
    document.addEventListener('pointerdown', (e) => {
      const d = e.target;
      if (!(d instanceof HTMLDialogElement) || !d.open || d.getAttribute('closedby') !== 'any') return;
      const r = d.getBoundingClientRect();
      const inside = e.clientX >= r.left && e.clientX <= r.right && e.clientY >= r.top && e.clientY <= r.bottom;
      if (!inside) close(d);
    });
  }

  C.dialog = { open, close };
})();
```

## Credits

- coss ui (MIT) · dialog.tsx: https://coss.com/ui
- W3C WAI-ARIA APG, Dialog (Modal): https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/
