# Error Text

> Error message below a form field. A CSS component with one class and no JavaScript.

The message shown when a field cannot be submitted as it stands. It belongs right below the field, not at the top of the page. If the text should explain what the field expects before the user has done anything, use [Help Text](/components/help-text.md).

## How to use it

**Plain HTML**

```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lo-tek/interfaces@0.36.0/src/tokens/tokens.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lo-tek/interfaces@0.36.0/src/components/css/error-text/error-text.css">
<!-- Or everything in one file: https://cdn.jsdelivr.net/npm/@lo-tek/interfaces@0.36.0/dist/lotek.css -->
```

```html
<p class="lt-error-text">Enter an email address with an at sign, for example jane&#64;example.com.</p>
<p class="lt-error-text" data-variant="warning">We cannot find this address in the population register. Check that it is correct.</p>
```

**TypeScript**

```ts
import "@lo-tek/interfaces/tokens.css"
import "@lo-tek/interfaces/error-text.css"
```

`lt` is used in two ways, and which one depends on whether you create the element or find it.

**If you create the markup,** spread the attributes in. That is the way in React, Astro and anything else that renders:

```tsx
import { lt } from "@lo-tek/interfaces/react"


<p {...lt.errorText()}>Enter your name.</p>
```

The import is from `/react` here, since the block is JSX. From there the keys are `className` and `htmlFor`. In Astro and anything else without React, import `@lo-tek/interfaces` and you get `class` and `for`.

**If the element is already there,** set them on it. That is the way when you enhance HTML a server or a template has sent:

```ts
import { lt } from "@lo-tek/interfaces"


const input = document.querySelector<HTMLInputElement>("#email")
const errorText = document.querySelector<HTMLElement>("#email-error")


function update(isValid: boolean) {
  const field = lt.field({ id: "email", error: true, invalid: !isValid })


  lt.setAttributes(input, lt.input({ type: "email" }), field.control)
  lt.setAttributes(errorText, lt.errorText(), field.error)
}
```

[`lt.field()`](/components/field.md) sets all three things that have to agree: `data-state`, `aria-invalid` and `aria-describedby`. `field.error` contains `hidden` when the field is valid, and the error text is then left out of `aria-describedby` too, since a hidden message should not be pointed to.

| `data-variant` | Used for |
| - | - |
| *(omitted)* | Errors that stop submission |
| `warning` | Something looks odd, but the user may continue |

## Wired to the field

Three things have to agree at the same time: `data-state` for the colour, `aria-invalid` for the screen reader, and `aria-describedby` for the text itself.

**Plain HTML**

```html
<label class="lt-label" for="email">Email address</label>
<input
class="lt-input"
id="email"
type="email"
data-state="invalid"
aria-invalid="true"
aria-describedby="email-error"
value="jane.example.com"
/>
<p class="lt-error-text" id="email-error">Enter an email address with an at sign.</p>
```

[Field](/components/field.md) sets all three when you give it `invalid`. That is why that component exists.

## Wording the message

- Say what the user should do, not what the system objected to. “Enter the date as DD-MM-YYYY” helps where “Invalid value” does not.
- Include an example when the format is unusual.
- Do not blame the user, and drop the exclamation mark.

## Classes you can style

| Class | What it is |
| - | - |
| `.lt-error-text` | The message. The colour comes from `data-variant`. |

See [Customising](/customising.md) for how the layer works.

## Accessibility

- Show the error when the user has left the field, not while they are typing.
- If the message appears while the page is otherwise still, put `role="alert"` on the element.
- If a form is submitted with several errors, move focus to the first one that failed.
- Never remove the message without removing `aria-invalid` at the same time.
