Skip to content
svelte-vitals
English
Esc
navigateopen⌘Jpreview
On this page

a11y/placeholder-label-option · Missing placeholder label option

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.

<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:

<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:

export default {
  rules: {
    'a11y/placeholder-label-option': 'off'
  }
};