---
title: a11y/placeholder-label-option · Missing placeholder label option
description: A required, single-selection select needs an empty first option so users can't submit it unchanged.
---

**Severity:** warning · **Category:** a11y

## What it checks

Flags a `<select required>`, with no `multiple` attribute and a display size absent or `1`, whose first `option` element child is not a placeholder label option.

Per the HTML spec, a placeholder label option is the first `option` and must have either:

- An empty `value` attribute: `<option value="">Choose…</option>`.
- No `value` attribute and no text content: `<option></option>`.

Not flagged:

- `multiple`, or a display `size` greater than `1`. The browser doesn't force an initial selection, so there's nothing to placeholder.
- No `required` attribute.
- An expression-valued `required`, `multiple`, or `size`, whose runtime value is unknown statically.
- An expression-valued `value` on the first option, or text content containing an `{expression}`, whose effective value is unknowable statically.
- The select's first child being an `{#each}` block or a component, since what it renders as the first option can't be resolved statically.
- A spread attribute on the `<select>` (it may supply `multiple` or `size`) or on its first `<option>` (it may supply `value`). Either makes the check's inputs unknowable, so the element is skipped.

Flagged on purpose: a `<select required>` with **no content at all**. It has no placeholder and no options, so as written it can never be satisfied; if its options arrive from script at runtime the rule cannot see them, and that one element is what an inline directive is for.

```svelte
<select required>
  <option value="">Choose…</option>
  <option value="a">A</option>
</select>
```

## Why it matters

A required `<select>` initially displays its first option as the selected value. If that option is not an empty placeholder, the field already holds a value the user never actively chose. They can submit the form without making a real selection, and a screen reader announces that value as already selected, giving no cue that a choice is still needed.

## How to fix

Make the first `option` an empty placeholder:

```svelte
<select required>
  <option value="">Choose…</option>
  <option value="a">A</option>
</select>
```

Do not mark it `disabled` on its own. The select's reset algorithm selects the first option that is **not** disabled, so a disabled placeholder leaves `A` selected, `required` satisfied, and the user submitting a value they never chose, the exact harm this rule reports. Write `disabled selected` if you want it unselectable, so the placeholder is still what the field starts on.

## Mode differences

None. This rule reads source, the same `.svelte` and `.ts` files, everywhere it runs. The CLI, the Vite plugin's build pass, and the live dashboard's static baseline all report it identically, and the rendered-HTML pass never re-evaluates it. Scoping a run with `--route` skips it: component-scoped rules have no route to attribute a finding to.

## Disabling

If the first option is intentionally a real, non-placeholder value, silence a single element with `<!-- svelte-vitals-disable-next-line a11y/placeholder-label-option -->`, or turn the rule off:

```js svelte-vitals.config.js
export default {
  rules: {
    'a11y/placeholder-label-option': 'off'
  }
};
```
