---
title: "Markup Reference · RUAL Documentation"
description: "Every data attribute the web runtime reads: data-action, data-trigger, data-key, data-confirm, data-optimistic and data-toggle, with what each one does."
canonical: https://docs.rual.nl/web/markup
language: en
---

# Markup Reference

Every attribute the script in a web page reads, grouped by what it is for, and the hooks it leaves for your CSS. These attributes work on any element, and everything marked "no request" happens in the browser alone.

## Running an action

| Attribute | Does |
| --- | --- |
| `data-action="name"` | Runs the [`web_action`](https://docs.rual.nl/block-types/web/web_action) of that name. A button or link runs it on click, a form on submit, an input, select or textarea on change. |
| `data-trigger="input"` | On a field: runs while typing, for live search. An earlier request from the same field still running is cancelled. |
| `data-debounce="300"` | With `data-trigger="input"`: how many milliseconds after the last key to run. The default is 300. |
| `data-trigger="load"` | Runs once when the element appears, also when an update puts it on the page later. |
| `data-trigger="every 10s"` | Runs repeatedly while the page is visible and the last request has finished. Units are `ms`, `s` and `m`, and the shortest interval is 1 second. It stops when the element leaves the page. |
| `data-key="..."` | Identifies a row. The closest one around the element is sent as the action's `key`, and update blocks find their element by it. [`web_foreach`](https://docs.rual.nl/block-types/web/web_foreach) writes it for you. |
| `data-param-size="xl"` | An extra field `size=xl` for the action. It works on the element and on the button that submitted a form, and wins over a form field of the same name. |
| `data-confirm="Sure?"` | Asks before running. Triggers that run by themselves (typing, load, repeat) do not ask. |

## While an action runs

| Attribute | Does |
| --- | --- |
| `data-loading-text="Saving"` | The button's text while the action runs. The old text comes back afterwards. |
| `data-optimistic="remove"` | Hides the row at once. It comes back if the action fails, or if the answer neither removed nor replaced it, for example because the server answered with [`web_errors`](https://docs.rual.nl/block-types/web/web_errors). |

## No request: toggles and dismissals

| Attribute | Does |
| --- | --- |
| `data-toggle="#menu"` | Shows or hides another element through its `hidden` attribute, and sets `aria-expanded` on the element you clicked. No request. It takes priority over a `data-action` on the same element. |
| `data-dismiss` | Removes the closest row, the nearest element with a `data-key`. No request. |
| `data-dismiss="#notice"` | Removes the given element instead. No request. |

```
<button type="button" data-toggle="#menu" aria-expanded="false">Menu</button>
<nav id="menu" hidden>...</nav>

<div id="notice">
  We use one cookie, for your cart.
  <button type="button" data-dismiss="#notice">Got it</button>
</div>
```

## Forms

| Attribute | Does |
| --- | --- |
| `data-error-for="email"` | Where the message for the field `email` goes. `_form` is for the whole form. Left out, the place is created right after the field. |
| `data-msg-required="..."` | Your own message per failed check: `required`, `type`, `pattern`, `minlength`, `maxlength`, `min`, `max`, `step`. |
| `data-msg-failed="..."` | On the form: the text shown in its `_form` place when the request itself fails. Without it the place shows the server's short message. |

[Forms and Validation](https://docs.rual.nl/web/forms) explains when each one appears.

## Prefetching and default styles

| Attribute | Does |
| --- | --- |
| `data-no-prefetch` | On a link: never load it ahead of the click. A link with `data-action` or `rel="nofollow"` is left alone too. See [Speed and Caching](https://docs.rual.nl/web/speed#prefetch). |
| `data-web-no-style` | On ` `: the script adds none of its default styles. |

## Hooks for your CSS

The script sets these while it works. Its default styles have no weight at all, so any rule of yours wins without extra effort.

| Hook | Is there | Default look |
| --- | --- | --- |
| `[data-loading]`, `[aria-busy="true"]` | On the button, its form and its row while an action runs. Buttons are also disabled meanwhile. | A progress cursor, 60% opacity on buttons, forms and rows, and a small spinner on buttons. |
| `[data-removing]` | On an element [`web_remove`](https://docs.rual.nl/block-types/web/web_remove) is about to take away, for 150 milliseconds. | A fade out. |
| `.rual-web-error` | On a message element the browser created. | Small red text. |
| `[data-error-for]:empty` | On any message place without a message. | Hidden. |
| `[aria-invalid="true"]` | On a field with a message. | A red outline. |

```
<style>
  button[data-loading] { opacity: 1; background: #666; }
  [data-removing] { transition: transform .15s; transform: translateX(16px); }
  .rual-web-error { color: #b00020; }
</style>
```

Requests made by typing, by `load` and by a repeat are silent: they set no loading state and show no confirm.

## Hooks for your own scripts

Web pages need no script of your own, but one fits in easily. Give an inline script `nonce="{{nonce}}"`.

| Hook | Is |
| --- | --- |
| `rual:web-updated` | An event on `document` after each answer is applied. `event.detail.ops` holds the updates. |
| `rual:web-error` | An event that bubbles from the element when an action fails. `event.detail` holds `action` and `error`. |
| `RUALWeb.action("name", {element, silent})` | Runs an action from your script, and answers with a promise. `element` decides the key, the form and the parameters that go along, and `silent` skips the confirm and the loading state. |
| `RUALWeb.apply(ops)` | Applies a list of updates you got some other way, for example over your own WebSocket. |

> **Scripts in returned HTML do not run** HTML that an action sends back is inserted as markup only. Put behaviour in data attributes or in a script the page already loads. Attributes in new HTML work at once: a form is set up for its checks, and a `data-trigger="load"` runs as soon as it arrives.

## Next steps

- [Actions](https://docs.rual.nl/web/actions): The blocks that answer these attributes.

- [Forms and Validation](https://docs.rual.nl/web/forms): Checks, messages and forms without JavaScript.

- [Escaping and Security](https://docs.rual.nl/web/security): Why onclick is blocked, and what the policy allows.
