# Modal

A modal is a dialog window that appears on top of the main content, requiring user interaction before returning to the main interface. Also known as dialog, lightbox, or popup.

## Installation

Install the modal, the theme package, and **the font package matching
your project's brand**. Footer actions typically use `AzButton`.

**Leroy Merlin projects**

```bash
npm install @ptlm-azulejo/modal @ptlm-azulejo/button @ptlm-azulejo/themes @ptlm-azulejo/fonts-leroy-merlin
# or
yarn add @ptlm-azulejo/modal @ptlm-azulejo/button @ptlm-azulejo/themes @ptlm-azulejo/fonts-leroy-merlin
```

**Adeo projects**

```bash
npm install @ptlm-azulejo/modal @ptlm-azulejo/button @ptlm-azulejo/themes @ptlm-azulejo/fonts-adeo
# or
yarn add @ptlm-azulejo/modal @ptlm-azulejo/button @ptlm-azulejo/themes @ptlm-azulejo/fonts-adeo
```

## Styles & theming

The component ships no colors or typeface of its own — it reads design tokens from
CSS variables at runtime. Those tokens come from `@ptlm-azulejo/themes`, and **the
brand is selected by a class on your app's `<html>` element**, so switching brand
never touches component code.

| Project      | Preset stylesheet                               | Root class    | Typeface        | Font package                       |
| ------------ | ----------------------------------------------- | ------------- | --------------- | ---------------------------------- |
| Leroy Merlin | `@ptlm-azulejo/themes/presets/leroy-merlin.css` | `preset-lm`   | LeroyMerlinSans | `@ptlm-azulejo/fonts-leroy-merlin` |
| Adeo         | `@ptlm-azulejo/themes/presets/adeo.css`         | `preset-adeo` | Roboto          | `@ptlm-azulejo/fonts-adeo`         |

**Leroy Merlin projects**

```js
import '@ptlm-azulejo/themes/presets/leroy-merlin.css'
import '@ptlm-azulejo/fonts-leroy-merlin'
import '@ptlm-azulejo/modal/style.css'
import '@ptlm-azulejo/button/style.css' // footer actions
```

```html
<html lang="pt" class="preset-lm"></html>
```

**Adeo projects**

```js
import '@ptlm-azulejo/themes/presets/adeo.css'
import '@ptlm-azulejo/fonts-adeo'
import '@ptlm-azulejo/modal/style.css'
import '@ptlm-azulejo/button/style.css' // footer actions
```

```html
<html lang="pt" class="preset-adeo"></html>
```

> The preset class is what resolves the brand at runtime. Without it — even with
> the stylesheets imported — the component renders uncolored and in a fallback
> typeface. Footer actions also need `@ptlm-azulejo/button/style.css` so Standard
> and ghost appearances render. See the [themes package](../themes/README.md) for
> brand switching, dark mode, and custom brands.

### Light and dark mode

Add `data-theme` alongside the brand class to pin the color scheme. Leave it off
and the preset follows the OS `prefers-color-scheme`:

```html
<html lang="pt" class="preset-lm" data-theme="dark"></html>
```

### Why the font package is separate

The preset only _names_ its typeface in `--font-family` and ships no font files.
[Loading them is your app's job](../themes/README.md#fonts), as with upstream
Mozaic, so you keep control of hosting, subsetting and preload. Without the
matching font package, `font-sans` falls back to a generic sans-serif. A
multi-brand app can install both and switch by swapping the `.preset-*` class:
only the active brand's file is ever downloaded.

### Custom properties

| Name              | Description                        | Default |
| ----------------- | ---------------------------------- | ------- |
| `--modal-z-index` | Customise the z-index of the modal | `5`     |

## Props

| Name             | Type      | Default | Description                                   |
| ---------------- | --------- | ------- | --------------------------------------------- |
| `open`           | `boolean` | `false` | If `true`, display the modal (`v-model:open`) |
| `title`          | `string`  | —       | Title of the modal                            |
| `description`    | `string`  | —       | Description of the modal                      |
| `closable`       | `boolean` | `true`  | Display the close button                      |
| `scroll`         | `boolean` | `true`  | If `false`, lock page scroll while open       |
| `closeOnOverlay` | `boolean` | `false` | Close when clicking the overlay               |
| `ui`             | `ModalUi` | `{}`    | Per-part Tailwind class overrides             |

## Slots

| Name      | Description                                      |
| --------- | ------------------------------------------------ |
| `icon`    | Icon next to the title                           |
| `default` | Extra content in the body                        |
| `link`    | Footer link (rendered only when `footer` is set) |
| `footer`  | Footer actions (typically buttons)               |

## Events

| Name          | Payload   | Description                         |
| ------------- | --------- | ----------------------------------- |
| `update:open` | `boolean` | Open state changed (`v-model:open`) |

## Keyboard

| Key      | Action                                                                  |
| -------- | ----------------------------------------------------------------------- |
| `Escape` | Close the modal (`update:open` `false`), whether or not focus is inside |
| `Tab`    | Cycle focus among focusable elements inside the dialog                  |

## Basic usage

```vue
<script setup>
import { ref } from 'vue'
import { AzModal } from '@ptlm-azulejo/modal'
import { AzButton } from '@ptlm-azulejo/button'

const open = ref(true)
</script>

<template>
  <AzButton @click="open = true">Open modal</AzButton>
  <AzModal
    v-model:open="open"
    title="Modal title"
    description="A short explanation of the decision the user must make."
  >
    <template #footer>
      <AzButton ghost @click="open = false">Cancel</AzButton>
      <AzButton @click="open = false">Primary action</AzButton>
    </template>
  </AzModal>
</template>
```
