Theme
On this page (9)
Component Skill — components/window/component-skill.md

Native basis

A non-modal <dialog class="window" open> in a .window-desktop; the controls are a <form method="dialog">, the resize handle is CSS resize.

Web Platform APIs

dialog.show()method="dialog"resizesetPointerCapture()touch-action

Classes

.window-desktop.window.window-titlebar.window-icon.window-title.window-controls.window-toolbar.window-body.window-statusbar

Data attributes

data-chrome (windows, mac, linux, retro), data-resizable, data-maximized, data-minimized, data-active (set by the runtime), data-flush on the body; --window-x / --window-y / --window-w / --window-h; events window-focus, window-move, close; API df$.shadcn.win.

§Window

Drag the title bar to move it (it never leaves the desktop for good), double-click it to maximize, − rolls it up to its bar, □ fills the desktop, × closes it - a native <form method='dialog'>. The corner handle resizes (CSS resize). Focus the title bar and use the arrow keys to move it by keyboard.

§Several windows

Press any window - its bar, its content - and it comes to the front; the others dim their title (data-active marks the front one). Stacking is shared across the page.

§Chrome

data-chrome: windows (the default - wide controls, a red × on hover), mac (traffic lights on the left, glyphs on hover, centred title), linux (GNOME round buttons, centred title) and retro (a bevelled 9x window). Same markup, same behaviour.

§Driven from script

df$.shadcn.win.create() builds a window (title, icon, content, position, size, chrome) in the first desktop; cascade() and tile() arrange the open ones; close() takes an element, id or selector. The log reads window-focus, window-move and close.

§Window states

The State API per window: default (open, normal size - also reopens a closed one), maximized, minimized (rolled up to the bar) and closed. The × and close() land in closed too - the state follows the native dialog.

§A desktop app: an editor with its toolbar

The editor window carries its app toolbar under the title bar (.window-toolbar): a ghost menubar - File, Edit, Window, Help - and tool buttons, all driving the windows through df$.shadcn.win (Window → Tile / Cascade / Maximize, Help → About opens a retro window). The editor opens on the left, the Preview on the right; the editor is a textarea over a Shiki-highlighted copy (loaded from esm.sh, like these docs), re-highlighted as you type; the status bar follows the caret and the Preview window renders the page live.

§States

Named states via the shared State API, driven per window through the bound api:

  • default - open at its normal size; reopens a closed window, { x, y } moves it
  • maximized - fills the desktop
  • minimized - rolled up to its title bar
  • closed - the dialog is closed; also entered when the × or close() closes it

The first example carries data-state-demo; bun run screenshots drives it via el.api.setState(name) and captures screenshots/{mode}/window-{state}.png.

Machine contract - verified against window.schema.json by bun run verify:

StateTypeValuesDefaultDescription
maximizedbooleantrue, falsefalseFills the desktop.
minimizedbooleantrue, falsefalseRolled up to its title bar.
closedbooleantrue, falsefalseThe dialog is closed - also by the × or close().

§CSS view file

/* -- Window component --------------------------------------------
   A desktop-style window: a non-modal <dialog> with a title bar (icon,
   title, minimize / maximize / close), moved by dragging the bar, raised to
   the front on click, resized natively. Chrome styles after Windows, macOS,
   Linux (GNOME) and a retro 9x look. The controls are a
   <form method="dialog">, so the × closes the window without script. */
@layer components {
  /* the area windows live in: positioned, clipped - a desktop */
  .window-desktop {
    position: relative;
    overflow: hidden;
    min-height: 18rem;
    border-radius: var(--radius-lg);
    background:
      radial-gradient(circle at 1px 1px, color-mix(in oklch, var(--foreground) 12%, transparent) 1px, transparent 0) 0 0 / 1.25rem 1.25rem,
      var(--muted);
    isolation: isolate;
  }
  .window {
    /* the UA dialog defaults (centered, padded, bordered) - replaced */
    --_titlebar: 2.25rem;
    --_ctl: 2.75rem;
    position: absolute;
    inset: auto;
    left: var(--window-x, 2rem);
    top: var(--window-y, 2rem);
    box-sizing: border-box;
    width: var(--window-w, 24rem);
    height: var(--window-h, auto);
    max-width: none;
    max-height: none;
    min-width: 12rem;
    margin: 0;
    padding: 0;
    display: none;
    flex-direction: column;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--card);
    color: var(--card-foreground);
    font-family: var(--font-sans);
    font-size: 0.875rem;
    box-shadow: var(--shadow-lg);
    overflow: hidden;
    transition: box-shadow 150ms;
    &[open] { display: flex; }
    /* native resize handle, bottom-right */
    &[data-resizable] {
      resize: both;
      min-height: calc(var(--_titlebar) + 4rem);
    }
    /* -- Title bar ---------------------------------------------------- */
    & > .window-titlebar {
      display: flex;
      align-items: center;
      gap: 0.5rem;
      flex: none;
      height: var(--_titlebar);
      padding-inline-start: 0.75rem;
      background: var(--muted);
      border-bottom: 1px solid var(--border);
      cursor: default;
      user-select: none;
      touch-action: none;
      &:focus-visible {
        outline: 2px solid var(--ring);
        outline-offset: -2px;
      }
    }
    & .window-icon {
      flex: none;
      width: 1rem;
      height: 1rem;
      object-fit: contain;
      color: var(--muted-foreground);
    }
    & .window-title {
      flex: 1;
      min-width: 0;
      margin: 0;
      overflow: hidden;
      font: inherit;
      font-size: 0.8125rem;
      font-weight: 500;
      white-space: nowrap;
      text-overflow: ellipsis;
    }
    /* -- Controls: CSS-drawn glyphs --------------------------------------- */
    & .window-controls {
      display: flex;
      align-self: stretch;
      flex: none;
      margin: 0;
    }
    & .window-controls > button {
      position: relative;
      box-sizing: border-box;
      width: var(--_ctl);
      height: 100%;
      margin: 0;
      padding: 0;
      border: 0;
      background: transparent;
      color: var(--foreground);
      cursor: default;
      transition: background-color 120ms, color 120ms;
      &::before,
      &::after {
        content: "";
        position: absolute;
        inset: 50% auto auto 50%;
        translate: -50% -50%;
        box-sizing: border-box;
      }
      &:hover { background: color-mix(in oklch, var(--foreground) 10%, transparent); }
      &:focus-visible { outline: 2px solid var(--ring); outline-offset: -2px; }
    }
    /* minimize: a bar */
    & .window-minimize::before {
      width: 0.625rem;
      height: 1px;
      background: currentColor;
    }
    /* maximize: a box; restore (while maximized): two boxes */
    & .window-maximize::before {
      width: 0.625rem;
      height: 0.625rem;
      border: 1px solid currentColor;
      border-radius: 1px;
    }
    &[data-maximized] .window-maximize {
      &::before { translate: -40% -60%; width: 0.5rem; height: 0.5rem; }
      &::after {
        translate: -65% -35%;
        width: 0.5rem;
        height: 0.5rem;
        border: 1px solid currentColor;
        border-radius: 1px;
        background: var(--muted);
      }
    }
    /* close: an × */
    & .window-close {
      &::before,
      &::after {
        width: 0.75rem;
        height: 1px;
        background: currentColor;
        rotate: 45deg;
      }
      &::after { rotate: -45deg; }
    }
    /* Windows chrome: the × turns red with a white glyph. Stated through
       .window-controls > so it outranks the shared control hover (a light
       tint) - otherwise the white glyph landed on light grey. The other
       chromes style their own close button. */
    &:not([data-chrome="mac"], [data-chrome="linux"], [data-chrome="retro"]) .window-controls > .window-close:hover {
      background: oklch(0.58 0.21 27);
      color: oklch(0.99 0 0);
    }
    /* -- Toolbar: an app's menubar / buttons under the title bar -------- */
    & > .window-toolbar {
      display: flex;
      align-items: center;
      gap: 0.25rem;
      flex: none;
      min-height: 2.25rem;
      padding: 0.125rem 0.375rem;
      border-bottom: 1px solid var(--border);
      background: var(--background);
      overflow-x: auto;
    }
    /* -- Body / status bar -------------------------------------------- */
    & > .window-body {
      flex: 1;
      min-height: 0;
      padding: 1rem;
      overflow: auto;
      overscroll-behavior: contain;
    }
    & > .window-body[data-flush] { padding: 0; }
    & > .window-statusbar {
      display: flex;
      align-items: center;
      gap: 1rem;
      flex: none;
      padding: 0.25rem 0.75rem;
      border-top: 1px solid var(--border);
      background: var(--muted);
      color: var(--muted-foreground);
      font-size: 0.75rem;
    }
    /* -- Active / inactive ---------------------------------------------- */
    &:not([data-active]) {
      box-shadow: var(--shadow-sm);
      & > .window-titlebar { color: var(--muted-foreground); }
    }
    /* -- Maximized: fill the desktop; minimized: rolled up to the bar -- */
    &[data-maximized] {
      inset: 0;
      width: auto;
      height: auto;
      border-radius: 0;
      border-width: 0;
      resize: none;
      box-shadow: none;
    }
    &[data-minimized] {
      height: auto;
      min-height: 0;
      resize: none;
      & > :not(.window-titlebar) { display: none; }
      & > .window-titlebar { border-bottom: 0; }
    }
    &[data-dragging] { transition: none; }
    /* -- Chrome: macOS - traffic lights on the left, centred title ------- */
    &[data-chrome="mac"] {
      --_ctl: 1.25rem;
      & > .window-titlebar { padding-inline: 0.75rem; }
      & .window-controls {
        order: -1;
        align-self: center;
        gap: 0.5rem;
        margin-inline-end: 0.25rem;
      }
      & .window-controls > button {
        width: 0.75rem;
        height: 0.75rem;
        border-radius: 999px;
        color: oklch(0.3 0.05 30 / 0);
        &:hover { background: var(--_light); }
        &::before,
        &::after { scale: 0.7; }
      }
      /* the glyphs appear when the pointer is over the lights */
      & .window-controls:hover > button { color: oklch(0.3 0.05 30 / 0.8); }
      & .window-close { order: 1; --_light: oklch(0.68 0.2 25); }
      & .window-minimize { order: 2; --_light: oklch(0.82 0.16 85); }
      & .window-maximize { order: 3; --_light: oklch(0.74 0.17 145); }
      & .window-controls > button { background: var(--_light); }
      & .window-close:hover { color: oklch(0.3 0.05 30 / 0.8); }
      & .window-title { text-align: center; }
      /* the icon sits beside the centred title */
      & .window-icon { order: 1; }
      & .window-title { order: 0; }
      &[data-maximized] .window-maximize::after { background: var(--_light); }
      &:not([data-active]) .window-controls:not(:hover) > button { background: color-mix(in oklch, var(--muted-foreground) 35%, transparent); }
    }
    /* -- Chrome: Linux (GNOME) - round grey buttons on the right --------- */
    &[data-chrome="linux"] {
      --_ctl: 1.5rem;
      & > .window-titlebar { padding-inline-end: 0.5rem; }
      & .window-title { text-align: center; font-weight: 600; }
      & .window-controls { align-self: center; gap: 0.5rem; }
      & .window-controls > button {
        width: var(--_ctl);
        height: var(--_ctl);
        border-radius: 999px;
        background: color-mix(in oklch, var(--foreground) 9%, transparent);
        &::before,
        &::after { scale: 0.8; }
        &:hover { background: color-mix(in oklch, var(--foreground) 16%, transparent); }
      }
      & .window-close:hover { background: color-mix(in oklch, var(--foreground) 16%, transparent); color: var(--foreground); }
      &[data-maximized] .window-maximize::after { background: transparent; }
    }
    /* -- Chrome: retro - a bevelled 9x window with a gradient title ------ */
    &[data-chrome="retro"] {
      --_face: oklch(0.8 0 0);
      --_hi: oklch(1 0 0);
      --_lo: oklch(0.5 0 0);
      --_ctl: 1.125rem;
      padding: 3px;
      border: 0;
      border-radius: 0;
      background: var(--_face);
      color: oklch(0.15 0 0);
      box-shadow:
        inset -1px -1px 0 oklch(0.1 0 0), inset 1px 1px 0 var(--_face),
        inset -2px -2px 0 var(--_lo), inset 2px 2px 0 var(--_hi);
      & > .window-titlebar {
        height: 1.375rem;
        padding-inline: 0.25rem 0.125rem;
        gap: 0.25rem;
        border: 0;
        background: linear-gradient(90deg, oklch(0.3 0.16 264), oklch(0.62 0.12 240));
        color: oklch(1 0 0);
      }
      & .window-icon { color: inherit; }
      & .window-title { font-weight: 700; font-size: 0.75rem; }
      & .window-controls { align-self: center; gap: 2px; }
      & .window-controls > button {
        width: calc(var(--_ctl) + 2px);
        height: var(--_ctl);
        background: var(--_face);
        color: oklch(0.1 0 0);
        box-shadow:
          inset -1px -1px 0 oklch(0.1 0 0), inset 1px 1px 0 var(--_hi),
          inset -2px -2px 0 var(--_lo);
        &:hover { background: var(--_face); color: oklch(0.1 0 0); }
        &:active { box-shadow: inset 1px 1px 0 oklch(0.1 0 0), inset -1px -1px 0 var(--_hi), inset 2px 2px 0 var(--_lo); }
        &::before,
        &::after { scale: 0.75; }
      }
      & .window-close { margin-inline-start: 2px; }
      & .window-minimize::before { height: 2px; translate: -50% 150%; }
      & .window-maximize::before { border-top-width: 2px; }
      & .window-close::before,
      & .window-close::after { height: 2px; }
      &[data-maximized] .window-maximize::after { background: var(--_face); border-top-width: 2px; }
      & > .window-body {
        margin-top: 2px;
        background: oklch(1 0 0);
        box-shadow: inset 1px 1px 0 var(--_lo), inset -1px -1px 0 var(--_hi), inset 2px 2px 0 oklch(0.1 0 0);
      }
      & > .window-toolbar {
        margin-top: 2px;
        border: 0;
        background: var(--_face);
        color: oklch(0.15 0 0);
      }
      & > .window-statusbar {
        margin-top: 2px;
        border: 0;
        background: var(--_face);
        color: oklch(0.15 0 0);
        box-shadow: inset 1px 1px 0 var(--_lo), inset -1px -1px 0 var(--_hi);
      }
      &:not([data-active]) > .window-titlebar {
        background: linear-gradient(90deg, oklch(0.55 0 0), oklch(0.72 0 0));
        color: oklch(0.85 0 0);
      }
      &[data-maximized] { padding: 3px; }
    }
  }
  /* -- Accessibility -------------------------------------------- */
  @media (prefers-reduced-motion: reduce) {
    .window,
    .window .window-controls > button { transition: none; }
  }
  @media (prefers-contrast: more) {
    .window { border-color: var(--foreground); }
    .window > .window-titlebar { border-bottom-color: var(--foreground); }
    .window:not([data-active]) > .window-titlebar { color: var(--foreground); }
  }
  @media (forced-colors: active) {
    .window { border: 1px solid CanvasText; }
    .window[data-active] > .window-titlebar {
      background: Highlight;
      color: HighlightText;
      forced-color-adjust: none;
    }
    .window .window-controls > button::before,
    .window .window-controls > button::after { border-color: ButtonText; }
  }
}

§JS view file

// -- Window -----------------------------------------------------
// A desktop-style window on a non-modal <dialog>: the browser owns open /
// close (the × is a <form method="dialog"> submit, so it works without
// script) and the close event; this runtime adds what it cannot do - moving
// the window by its title bar (pointer capture, arrow keys), raising the
// clicked window to the front, maximize / minimize - plus the named-state
// API (AGENTS.md "State API") and the imperative df$.shadcn.win.
// Shared preamble (AGENTS.md "State API"); the implementation lives in core.js —
// build.ts rewrites this import into a df$.shadcn.shared binding in dist/.
import { defussGlobals } from '../../shared/state-api.js';
const df$ = defussGlobals();
const windowStates = ['default', 'maximized', 'minimized', 'closed'];
/** Stacking order is shared by every window on the page - a counter, not state. */
let topZ = 10;
/** How much of a window must stay inside its desktop, so it can be grabbed back. */
const KEEP = 48;
const resolve = (target) =>
  typeof target === 'string'
    ? document.getElementById(target) ?? document.querySelector(target)
    : target;
const titleOf = (w) => w.querySelector('.window-title')?.textContent?.trim() ?? '';
/** The window's position inside its desktop, in px. */
const posOf = (w) => ({ x: w.offsetLeft, y: w.offsetTop });
/**
 * Moves a window, keeping it reachable: at least KEEP px of GRABBABLE title
 * bar stay inside the desktop - the controls don't count, so a window pushed
 * to an edge never shows only its buttons.
 */
function moveTo(w, x, y) {
  const parent = w.offsetParent;
  const bar = w.querySelector('.window-titlebar');
  if (parent) {
    const ctl = w.querySelector('.window-controls')?.offsetWidth ?? 0;
    const maxX = parent.clientWidth - KEEP - ctl;
    const minX = KEEP + ctl - w.offsetWidth;
    const maxY = parent.clientHeight - (bar?.offsetHeight ?? KEEP);
    x = Math.min(Math.max(x, minX), maxX);
    y = Math.min(Math.max(y, 0), Math.max(maxY, 0));
  }
  w.style.setProperty('--window-x', `${Math.round(x)}px`);
  w.style.setProperty('--window-y', `${Math.round(y)}px`);
  return { x: Math.round(x), y: Math.round(y) };
}
/** Raises a window above every other and marks it the active one. */
function raise(w) {
  if (!w.open) return;
  if (w.hasAttribute('data-active') && Number(w.style.zIndex) === topZ) return;
  topZ += 1;
  w.style.zIndex = String(topZ);
  document.querySelectorAll('.window[data-active]').forEach((o) => { if (o !== w) o.removeAttribute('data-active'); });
  w.setAttribute('data-active', '');
  w.dispatchEvent(new CustomEvent('window-focus', { bubbles: true, detail: { title: titleOf(w) } }));
}
/**
 * show() runs the dialog focusing steps - it moves focus into the window
 * (onto the focusable title bar), which paints a focus ring on a window the
 * user never touched and pulls focus away from whatever opened it. Opening
 * leaves focus where it was; pressing into the window focuses as usual.
 */
function showQuietly(w) {
  const prev = document.activeElement;
  w.show();
  if (prev && prev !== document.body && prev.isConnected && prev.focus) prev.focus({ preventScroll: true });
  else if (w.contains(document.activeElement)) document.activeElement.blur();
}
/** Hands "active" to the front-most open window left (after one closed). */
function activateTopmost() {
  const open = Array.from(document.querySelectorAll('.window[open]'));
  if (!open.length) return;
  const top = open.reduce((a, b) => (Number(b.style.zIndex || 0) > Number(a.style.zIndex || 0) ? b : a));
  raise(top);
}
/** Maximize / minimize drop an inline size (from a native resize) and put it back after. */
function stashSize(w) {
  if (w._stash) return;
  w._stash = { width: w.style.width, height: w.style.height };
  w.style.width = '';
  w.style.height = '';
}
function restoreSize(w) {
  if (!w._stash) return;
  w.style.width = w._stash.width;
  w.style.height = w._stash.height;
  w._stash = null;
}
/**
 * UI side of setState. 'default' = open, normal size (opens a closed
 * window; `{ x, y }` moves it); 'maximized' fills the desktop; 'minimized'
 * rolls it up to its title bar; 'closed' closes the dialog.
 */
function triggerStateChange(w, stateName, config) {
  const maxBtn = w.querySelector('.window-maximize');
  if (stateName !== 'closed' && !w.open) showQuietly(w);
  switch (stateName) {
    case 'default':
      w.removeAttribute('data-maximized');
      w.removeAttribute('data-minimized');
      restoreSize(w);
      if (config.x !== undefined && config.y !== undefined) moveTo(w, Number(config.x), Number(config.y));
      raise(w);
      break;
    case 'maximized':
      w.removeAttribute('data-minimized');
      stashSize(w);
      w.setAttribute('data-maximized', '');
      raise(w);
      break;
    case 'minimized':
      w.removeAttribute('data-maximized');
      stashSize(w);
      w.setAttribute('data-minimized', '');
      break;
    case 'closed':
      if (w.open) w.close(); // the close event hands "active" on
      break;
  }
  if (maxBtn) maxBtn.setAttribute('aria-label', stateName === 'maximized' ? 'Restore' : 'Maximize');
  w.querySelector('.window-minimize')?.setAttribute('aria-label', stateName === 'minimized' ? 'Restore' : 'Minimize');
}
/** Registry-level API; pass the .window element explicitly. Unknown names throw. */
export const windowApi = {
  setState(w, stateName, config = {}) {
    if (!windowStates.includes(stateName)) {
      throw new Error(`window: unknown state "${stateName}" (supported: ${windowStates.join(', ')})`);
    }
    w.dataset.stateName = stateName;
    w._stateConfig = config;
    triggerStateChange(w, stateName, config);
  },
  getState(w) {
    // the dialog is the truth: closed the moment it closes, before the
    // queued close event updates data-state-name
    const name = !w.open ? 'closed' : w.dataset.stateName || 'default';
    return { name, config: name === 'closed' ? {} : w._stateConfig ?? {} };
  },
};
df$.windowApi = windowApi;
df$.windowStates = windowStates;
/** Title-bar dragging: pointer capture, so a fast drag never loses the window. */
function bindDrag(w, bar) {
  bar.addEventListener('pointerdown', (e) => {
    if (e.button !== 0 || e.target.closest('button, a, input, select, textarea')) return;
    raise(w);
    if (w.hasAttribute('data-maximized')) return;
    e.preventDefault();
    const start = posOf(w);
    const sx = e.clientX, sy = e.clientY;
    bar.setPointerCapture(e.pointerId);
    w.setAttribute('data-dragging', '');
    const onMove = (m) => moveTo(w, start.x + m.clientX - sx, start.y + m.clientY - sy);
    const onUp = () => {
      bar.removeEventListener('pointermove', onMove);
      bar.removeEventListener('pointerup', onUp);
      bar.removeEventListener('pointercancel', onUp);
      w.removeAttribute('data-dragging');
      w.dispatchEvent(new CustomEvent('window-move', { bubbles: true, detail: posOf(w) }));
    };
    bar.addEventListener('pointermove', onMove);
    bar.addEventListener('pointerup', onUp);
    bar.addEventListener('pointercancel', onUp);
  });
  // double-click the bar: maximize / restore, like every desktop
  bar.addEventListener('dblclick', (e) => {
    if (e.target.closest('button')) return;
    windowApi.setState(w, w.hasAttribute('data-maximized') ? 'default' : 'maximized', {});
  });
  // keyboard: the bar is focusable; arrows move (Shift = bigger steps)
  bar.addEventListener('keydown', (e) => {
    const step = e.shiftKey ? 64 : 16;
    const d = { ArrowLeft: [-step, 0], ArrowRight: [step, 0], ArrowUp: [0, -step], ArrowDown: [0, step] }[e.key];
    if (!d || w.hasAttribute('data-maximized') || e.target !== bar) return;
    e.preventDefault();
    const p = posOf(w);
    moveTo(w, p.x + d[0], p.y + d[1]);
    w.dispatchEvent(new CustomEvent('window-move', { bubbles: true, detail: posOf(w) }));
  });
}
function init() {
  document.querySelectorAll('dialog.window:not([data-init])').forEach((w) => {
    w.dataset.init = '';
    const bar = w.querySelector(':scope > .window-titlebar');
    if (bar) {
      if (!bar.hasAttribute('tabindex')) bar.tabIndex = 0;
      bindDrag(w, bar);
    }
    if (!w.hasAttribute('aria-labelledby') && !w.hasAttribute('aria-label')) {
      const title = w.querySelector('.window-title');
      if (title) {
        if (!title.id) title.id = `window-title-${Math.random().toString(36).slice(2, 8)}`;
        w.setAttribute('aria-labelledby', title.id);
      }
    }
    // any press inside a window brings it to the front (capture: before
    // the content handles it); so does focus arriving by keyboard
    w.addEventListener('pointerdown', () => raise(w), true);
    w.addEventListener('focusin', () => raise(w));
    w.querySelector('.window-maximize')?.addEventListener('click', () =>
      windowApi.setState(w, w.hasAttribute('data-maximized') ? 'default' : 'maximized', {}));
    // the × closes through the API too: the native form submit already
    // does it, but a page that cancels submits (a sandbox, a SPA) must not
    // strand the window open
    w.querySelector('.window-close')?.addEventListener('click', (e) => {
      e.preventDefault();
      windowApi.setState(w, 'closed', {});
    });
    w.querySelector('.window-minimize')?.addEventListener('click', () =>
      windowApi.setState(w, w.hasAttribute('data-minimized') ? 'default' : 'minimized', {}));
    // however it closed - the × (form method="dialog"), Escape-less
    // close(), the API - the state follows the dialog
    w.addEventListener('close', () => {
      // the event is queued: a window reopened meanwhile stays open
      if (w.open) return;
      w.dataset.stateName = 'closed';
      w.removeAttribute('data-active');
      activateTopmost();
    });
    w.api = {
      setState: (stateName, config) => windowApi.setState(w, stateName, config),
      getState: () => windowApi.getState(w),
    };
    const initial = !w.open ? 'closed' : w.hasAttribute('data-maximized') ? 'maximized' : w.hasAttribute('data-minimized') ? 'minimized' : 'default';
    w.dataset.stateName = initial;
    if (w.open) {
      w.style.zIndex = String(++topZ);
      // the last authored open window starts active
      document.querySelectorAll('.window[data-active]').forEach((o) => o.removeAttribute('data-active'));
      w.setAttribute('data-active', '');
    }
  });
}
/** Builds a window element from options - the shape the skill documents. */
function create(options = {}) {
  const {
    title = 'Untitled', icon, content, html, statusbar, id,
    x, y, width, height, chrome, resizable = true, parent, focus = true, flush = false,
  } = options;
  const host = resolve(parent) ?? document.querySelector('.window-desktop') ?? document.body;
  const w = document.createElement('dialog');
  w.className = 'window';
  if (id) w.id = id;
  if (chrome) w.dataset.chrome = chrome;
  if (resizable) w.setAttribute('data-resizable', '');
  const count = host.querySelectorAll(':scope > .window').length;
  w.style.setProperty('--window-x', typeof x === 'number' ? `${x}px` : x ?? `${24 + (count % 8) * 28}px`);
  w.style.setProperty('--window-y', typeof y === 'number' ? `${y}px` : y ?? `${24 + (count % 8) * 28}px`);
  if (width !== undefined) w.style.setProperty('--window-w', typeof width === 'number' ? `${width}px` : width);
  if (height !== undefined) w.style.setProperty('--window-h', typeof height === 'number' ? `${height}px` : height);
  const bar = document.createElement('header');
  bar.className = 'window-titlebar';
  if (icon) {
    const i = document.createElement('i');
    i.className = 'window-icon';
    i.setAttribute('data-lucide', icon);
    bar.append(i);
  }
  const h = document.createElement('h2');
  h.className = 'window-title';
  h.textContent = title;
  const controls = document.createElement('form');
  controls.method = 'dialog';
  controls.className = 'window-controls';
  for (const [cls, label, type] of [['window-minimize', 'Minimize', 'button'], ['window-maximize', 'Maximize', 'button'], ['window-close', 'Close', 'submit']]) {
    const b = document.createElement('button');
    b.type = type;
    b.className = cls;
    b.setAttribute('aria-label', label);
    controls.append(b);
  }
  bar.append(h, controls);
  const body = document.createElement('div');
  body.className = 'window-body';
  if (flush) body.setAttribute('data-flush', '');
  if (content instanceof Node) body.append(content);
  else if (typeof html === 'string') body.append(document.createRange().createContextualFragment(html));
  else if (content !== undefined) body.textContent = String(content);
  w.append(bar, body);
  if (statusbar !== undefined) {
    const s = document.createElement('footer');
    s.className = 'window-statusbar';
    s.textContent = String(statusbar);
    w.append(s);
  }
  host.append(w);
  init();
  showQuietly(w);
  if (focus) windowApi.setState(w, 'default', {});
  if (icon) globalThis.lucide?.createIcons?.();
  return w;
}
/** Windows (open unless `all`) inside `scope` (default: the page). */
const list = (scope, all = false) =>
  Array.from((resolve(scope) ?? document).querySelectorAll(all ? '.window' : '.window[open]'));
/** Steps the open windows diagonally from the top-left, front-most last. */
function cascade(scope, step = 28) {
  list(scope)
    .sort((a, b) => Number(a.style.zIndex || 0) - Number(b.style.zIndex || 0))
    .forEach((w, i) => { windowApi.setState(w, 'default', {}); moveTo(w, 16 + i * step, 16 + i * step); });
}
/** Lays the open windows side by side in a grid that fills their desktop. */
function tile(scope) {
  const wins = list(scope);
  if (!wins.length) return;
  const cols = Math.ceil(Math.sqrt(wins.length));
  const rows = Math.ceil(wins.length / cols);
  wins.forEach((w, i) => {
    windowApi.setState(w, 'default', {});
    const p = w.offsetParent;
    if (!p) return;
    const cw = p.clientWidth / cols, ch = p.clientHeight / rows;
    w.style.width = '';
    w.style.height = '';
    w.style.setProperty('--window-w', `${Math.floor(cw)}px`);
    w.style.setProperty('--window-h', `${Math.floor(ch)}px`);
    moveTo(w, (i % cols) * cw, Math.floor(i / cols) * ch);
  });
}
// the imperative API: df$.shadcn.win.* - every method takes an element,
// an id or a selector
df$.win = {
  create,
  open: (t, config = {}) => { const w = resolve(t); if (w) windowApi.setState(w, 'default', config); return w; },
  close: (t) => { const w = resolve(t); if (w) windowApi.setState(w, 'closed', {}); return w; },
  focus: (t) => { const w = resolve(t); if (w?.open) raise(w); return w; },
  move: (t, x, y) => { const w = resolve(t); return w ? moveTo(w, x, y) : null; },
  resize: (t, width, height) => {
    const w = resolve(t);
    if (!w) return null;
    w.style.width = '';
    w.style.height = '';
    w.style.setProperty('--window-w', typeof width === 'number' ? `${width}px` : width);
    if (height !== undefined) w.style.setProperty('--window-h', typeof height === 'number' ? `${height}px` : height);
    return w;
  },
  maximize: (t) => { const w = resolve(t); if (w) windowApi.setState(w, 'maximized', {}); return w; },
  minimize: (t) => { const w = resolve(t); if (w) windowApi.setState(w, 'minimized', {}); return w; },
  restore: (t) => { const w = resolve(t); if (w) windowApi.setState(w, 'default', {}); return w; },
  toggleMaximize: (t) => {
    const w = resolve(t);
    if (w) windowApi.setState(w, w.hasAttribute('data-maximized') ? 'default' : 'maximized', {});
    return w;
  },
  active: () => document.querySelector('.window[open][data-active]'),
  list,
  cascade,
  tile,
};
init();
new MutationObserver(init).observe(document, { childList: true, subtree: true });

Comments, ideas or improvements? Edit this page's source on GitHub