# dialog-anchor

Open and close a native `<dialog>` — no JavaScript required.


```html
<div>
  <dialog-anchor role="button" target-ref="#da-simple" is-modal>Open dialog</dialog-anchor>
  <dialog id="da-simple">
    <p>A native, modal dialog.</p>
    <dialog-anchor role="button">Close</dialog-anchor>
  </dialog>
</div>
```


## Features

- **Click to toggle** Opens / closes a `<dialog>` on click
- **Target or fallback** Point at any `<dialog>` via `target-ref`, or let
  it find the nearest ancestor automatically (great for close buttons)
- **Modal or non-modal** `is-modal` blocks the rest of the page; omit it
  for a lightweight, dismissible popover
- **Any trigger event** Inherits `listen-for` to open/close on custom
  events instead of `click`

## Installation


`@excom/dialog-anchor` v0.1.4

```bash
pnpm add @excom/dialog-anchor
```

```bash
npm install @excom/dialog-anchor
```

```bash
yarn add @excom/dialog-anchor
```

### Import

```ts
import "@excom/dialog-anchor";
```

```css
@import "@excom/dialog-anchor/dialog-anchor.css";
```



## Usage

Wrap a trigger in `<dialog-anchor>` and point it at a `<dialog>`. A
`<dialog-anchor>` with no `target-ref`, placed inside the `<dialog>` it
should close, needs no configuration at all.

```html
<dialog-anchor target-ref="#confirm" role="button">Delete</dialog-anchor>
<dialog id="confirm">
  <p>Are you sure?</p>
  <dialog-anchor role="button">Cancel</dialog-anchor>
</dialog>
```

### API Reference


#### Attributes

| Name | Surface | Type | Default | Values | Description | Inherited from |
| --- | --- | --- | --- | --- | --- | --- |
| `target-ref` | option | `string` |  | `<CSS Selector>` | CSS selector for the `<dialog>` to toggle. If omitted, the element toggles the nearest ancestor `<dialog>`. |  |
| `is-modal` | option | `boolean` |  |  | Open the dialog as a modal (blocks the rest of the page) instead of a non-modal popover. |  |
| `host-ref` | option | `string` |  | `<CSS Selector>` \| `"window"` \| `"document"` \| `"html"` \| `"body"` \| `"head"` | Listen on another element / `window` / `document` — e.g. Escape to dismiss a dialog from a global `keydown`. Defaults to `:scope`. Used with `listen-for`. Not compatible with `listen-for-lifecycle`. The selector MUST resolve when `host-ref` is set — it will not wait for a match to appear. | `@excom/listenable-element` |
| `listen-for` | option | `tokenlist` |  | `<EventName>…` | Space-separated event names to listen for. Defaults to `click` when unset (and no lifecycle list is set). | `@excom/listenable-element` |
| `listen-for-lifecycle` | option | `tokenlist` |  | `"connected"` \| `"disconnected"` \| `"adopted"` | Space-separated element lifecycles to handle. | `@excom/listenable-element` |
| `listen-once` | option | `boolean` |  |  | Handle each distinct event name / lifecycle at most once. | `@excom/listenable-element` |
| `selector-filter` | option | `string` |  | `<CSS Selector>` | Only handle events whose `event.target` matches this CSS selector. Does not support `:scope` in the selector. | `@excom/listenable-element` |
| `keycode-filter` | option | `tokenlist` |  | `<key` \| `mod+key>…` | Space-separated key filters (OR). Join modifiers with `+` (AND, any order): `shift+k tab` → Shift+K or Tab. Modifiers: `shift`, `alt`, `ctrl`/`control`, `meta`/`cmd`. Name the space bar `space` / `spacebar` and the plus key `plus` (`shift+space`). Case-insensitive. | `@excom/listenable-element` |
| `pathname-filter` | option | `tokenlist` |  | `<pathname>…` | Only handle when `location.pathname` is one of these values — route-aware behaviors without a separate router element. | `@excom/listenable-element` |
| `prevent-default` | option | `boolean` |  |  | Call `preventDefault()` on matched events (ignored for lifecycles). | `@excom/listenable-element` |
| `stop-propagation` | option | `boolean` |  |  | Call `stopPropagation()` on matched events (ignored for lifecycles). | `@excom/listenable-element` |
| `stop-immediate-propagation` | option | `boolean` |  |  | Call `stopImmediatePropagation()` on matched events (ignored for lifecycles). | `@excom/listenable-element` |
| `vibrate-ms` | option | `number` | `"20 (when attribute is present with no value)"` |  | Vibrate on handle (`navigator.vibrate`). Empty / `0` uses a 20ms pulse. | `@excom/listenable-element` |
| `delay-ms` | option | `number` |  |  | Delay handling by this many milliseconds. | `@excom/listenable-element` |
| `is-debounced` | option | `boolean` |  |  | With `delay-ms`, coalesce bursts into one trailing call (debounce). | `@excom/listenable-element` |

#### CSS Classes

| Name | Description |
| --- | --- |
| `.unstyled` | Skip the pointer cursor from the listenable mixin. |

#### CSS Aliases

| Alias | Kind | Matches | Description |
| --- | --- | --- | --- |
| `:--dialog-anchor` | element | `dialog-anchor`, `.tag-dialog-anchor` |  |



### Examples

#### Modal dialog

`is-modal` opens the dialog as a modal, blocking interaction with the rest
of the page until it's closed (using native `::backdrop`). Without that attribute,
the dialog is opened without a backdrop.

If using Valence.css, setting `.absolute` on the `dialog` element will display it `absolute`
in the surrounding content (as opposed to fixed).


```html
<div>
  <dialog-anchor role="button" target-ref="#da-modal" is-modal>
    Open as modal
  </dialog-anchor>
  <dialog-anchor role="button" target-ref="#da-modal">
    Open as non-modal
  </dialog-anchor>
  <dialog id="da-modal">
    <article>
      <header>
        <dialog-anchor role="button" rel="prev"></dialog-anchor>
        <h4>Modal dialogs block interaction with the rest of the page.</h4>
        <p>This dialog is shown with Valence.css styling</p>
      </header>
      <p>Dialog content</p>
      <footer>
        Footer content
      </footer>
    </article>
  </dialog>
</div>
```


#### Close on a custom event

`listen-for` swaps the default `click` for any event. Pair it with a real
`<super-form>`'s `super-form-success` event to auto-close a dialog
once a form inside it succeeds — simulated here with `<event-handler>`.


```html
<div>
  <dialog-anchor role="button" target-ref="#da-auto" is-modal>Open form</dialog-anchor>
  <dialog id="da-auto">
    <dialog-anchor listen-for="super-form-success">
      <p>Simulating a form submit…</p>
      <event-handler role="button" fire-event="super-form-success">
        Submit
      </event-handler>
    </dialog-anchor>
  </dialog>
</div>
```

## Release notes

### 0.1.1 (2026-09-23)

- Ship only dist (and declared extras) in the npm tarball; drop build logs, tests and sources
