# `hb-dialog`

Modal dialog built with Bulma’s `modal` and `modal-card` patterns inside a shadow root. Visibility is controlled with the string attribute **`show`**: `"yes"` opens the overlay, `"no"` keeps it unmounted.

While open, the component sets **`document.body.style.overflow` to `"hidden"`** so the page behind the modal does not scroll, and restores the previous value when the dialog closes.

**Category:** overlays · **Tags:** overlays, modal  
**Package:** `@htmlbricks/hb-dialog` · **Custom element:** `<hb-dialog>`

---

## Behavior

- **Open / close:** The modal tree is rendered only when `show="yes"`. Closing sets `show` to `"no"` (or removes the open state) depending on the interaction path.
- **Transitions:** The outer overlay uses a **fade** transition; the card uses **fly** (vertical motion). When the enter transition finishes, **`modalShow`** fires with `show: true`; when the exit transition finishes, **`modalShow`** fires with `show: false`.
- **Backdrop:** With the default **`backdrop`** behavior, a click on the dimmed **`.modal-background`** (the target that received the click) closes the dialog. To turn that off, set the **`backdrop` property** to `false` in JavaScript (a string attribute such as `backdrop="no"` is still truthy and will not disable dismissal—see the attributes section below). When **`backdrop`** is false, the background layer is still rendered but clicks on it do not dismiss because the handler checks **`backdrop`** before closing.
- **Keyboard:** When **`keyboard`** is enabled (default), an **`Escape`** listener is attached to `document` while the modal is open; **`Escape`** closes the dialog. When `keyboard` is disabled, that listener is not registered.
- **Header delete control:** The small Bulma **delete** control in the default header only sets `show` to `"no"`; it does **not** dispatch **`modalConfirm`**.

---

## Attributes and properties

Names are **snake_case**. For **`disable_confirm`**, **`hide_close`**, and **`hide_confirm`**, you can use the string values **`"yes"`** / **`"no"`** (or **`"true"`** / **`"false"`**) and they are normalized inside the component. **`show`** uses **`"yes"`** / **`"no"`** only. For **`backdrop`** and **`keyboard`**, use real booleans on the element **property** when you need `false` (see the note after the table).

| Name | Type | Description |
| --- | --- | --- |
| `id` | string (optional) | Identifier echoed on custom events as `detail.id`. |
| `show` | `"yes"` \| `"no"` (optional, default `"no"`) | Whether the modal is visible. |
| `title` | string (optional) | Default title when the `title` slot is not used. |
| `content` | string (optional) | Default body text when the `body-content` slot is not used. |
| `closelabel` | string (optional) | Default label for the footer close button (slot `close-button-label` overrides). |
| `confirmlabel` | string (optional) | Default label for the confirm button (slot `confirm-button-label` overrides). |
| `backdrop` | boolean (optional, default `true`) | If truthy, a backdrop click can dismiss the modal (when the click target is the background layer). |
| `keyboard` | boolean (optional, default `true`) | If truthy, `Escape` closes the modal while it is open. |
| `describedby` | string (optional) | Value for `aria-describedby` on the dialog root. |
| `labelledby` | string (optional) | Value for `aria-labelledby` on the dialog root. |
| `confirm_btn_class` | string (optional, default `"primary"`) | Visual style token for the confirm button; mapped to Bulma `is-*` modifiers (see below). |
| `close_btn_class` | string (optional, default `"secondary"`) | Visual style token for the footer close button; same mapping rules. |
| `disable_confirm` | boolean (optional) | If truthy, the confirm button is `disabled`. String `"yes"` / `"true"` is coerced to a disabled button. |
| `hide_close` | boolean (optional) | If truthy, hides the header delete control and the footer close button. Coerced from `"yes"` / `"true"`. |
| `hide_confirm` | boolean (optional) | If truthy, hides the confirm button. Coerced from `"yes"` / `"true"`. |
| `style` | string (optional) | Declared in typings; the Svelte implementation does not currently destructure or forward this as a component prop. |

For **`backdrop`** and **`keyboard`**, the implementation uses ordinary JavaScript truthiness (unlike `disable_confirm` / `hide_close` / `hide_confirm`, which normalize `"yes"` / `"true"`). A string attribute like `keyboard="no"` is still **truthy** in JS. For a reliable “off”, set the **property** to `false` in JavaScript (or omit the attribute and use defaults as appropriate).

### Button style tokens (`confirm_btn_class` / `close_btn_class`)

Values are compared case-insensitively, optional `btn-` prefix is stripped, then mapped to Bulma modifiers:

| Token | Bulma modifier |
| --- | --- |
| `primary` | `is-primary` |
| `secondary` | `is-light` |
| `success` | `is-success` |
| `danger` | `is-danger` |
| `warning` | `is-warning` |
| `info` | `is-info` |
| `light` | `is-light` |
| `dark` | `is-dark` |
| `link` | `is-ghost` |
| *(unknown)* | `is-light` |

---

## Slots

| Slot | Purpose |
| --- | --- |
| `header` | Replaces the entire modal head (title row and optional delete control). The default head includes the `title` slot and the delete button when `hide_close` is not set. |
| `title` | Heading inside the default header when `header` is not overridden. |
| `body-content` | Main body; falls back to the `content` prop (or a short default string). |
| `modal-footer` | Wraps the footer region; default content wraps the `footer` slot. |
| `footer` | Default footer: a `.buttons` group with close and confirm actions (respecting `hide_close` / `hide_confirm`). |
| `close-button-label` | Inner text of the default footer close button (defaults to `closelabel` or `"Close"`). |
| `confirm-button-label` | Inner text of the default confirm button (defaults to `confirmlabel` or `"Save changes"`). |

---

## Custom events

Listen with `addEventListener` or framework bindings on the host element.

| Event | `detail` | When it fires |
| --- | --- | --- |
| **`modalShow`** | `{ id: string; show: boolean }` | After the overlay **enter** transition ends (`show: true`) or after the **leave** transition ends (`show: false`). |
| **`modalConfirm`** | `{ id: string; confirm: boolean }` | **`confirm: true`** when the user activates the confirm button (then the dialog closes). **`confirm: false`** when the user activates the default footer **close** button (then the dialog closes). Not emitted for header delete, backdrop dismiss, or `Escape`. |

---

## Styling: CSS custom properties

These are documented for `:host` (Bulma modal layout tokens; see [Bulma CSS variables](https://bulma.io/documentation/features/css-variables/)).

| Variable | Role |
| --- | --- |
| `--hb-modal-max-width` | Maximum width of the modal card (`.modal-card`). Default `40rem`. |
| `--bulma-modal-card-head-padding` | Padding for `.modal-card-head`. Default `1rem 1.25rem`. |
| `--bulma-modal-card-body-padding` | Padding for `.modal-card-body`. Default `1rem 1.25rem`. |
| `--bulma-modal-card-title-size` | Title font size. Default `var(--bulma-size-5)`. |
| `--bulma-modal-card-title-line-height` | Title line height. Default `1.25`. |
| `--bulma-modal-card-spacing` | Spacing between card regions. Default `1.5rem`. |

Additional **`--bulma-*`** variables from Bulma’s setup may apply on `:host` depending on your theme.

### CSS parts

| Part | Target | Use |
| --- | --- | --- |
| `modal-dialog` | `.modal-card` | Size, border radius, shadows, or layout of the dialog surface. |

---

## HTML example

```html
<hb-dialog
  id="confirm-delete"
  show="yes"
  title="Delete item?"
  content="This cannot be undone."
  confirmlabel="Delete"
  closelabel="Cancel"
></hb-dialog>
```

```js
const el = document.querySelector("hb-dialog");
el.addEventListener("modalConfirm", (e) => {
  if (e.detail.confirm) {
    // user chose Delete
  } else {
    // user chose Cancel
  }
});
el.addEventListener("modalShow", (e) => {
  console.log("dialog visibility:", e.detail.show);
});
```

To open or close from script, set the reflected property or attribute, for example `el.setAttribute("show", "yes")` / `el.setAttribute("show", "no")`.

---

## Accessibility notes

- The root node uses **`role="dialog"`**, **`aria-modal="true"`**, and optional **`aria-labelledby`** / **`aria-describedby`** when you pass `labelledby` and `describedby` (typically IDs of elements in the light DOM).
- Focus management is not fully delegated outside the shadow tree; validate focus order and screen reader behavior in your host page when using slots or dynamic content.

---

## Internationalization

No built-in `i18n` language list is shipped for this component; use props and slots for copy.
