Unlabeled form fields are invisible to screen reader users. Without a label, users cannot understand what information to enter.
Form elements like <input>, <select>, and <textarea> need associated labels to be accessible. When a label is missing, screen readers announce the field as just "edit text" or "combo box" without context. Users cannot determine whether they should enter their name, email, or credit card number.
<label> element nearbyfor attribute matching input idaria-label or aria-labelledby attributeQuick console check:
document.querySelectorAll('input, select, textarea').forEach((el) => {
const hasLabel = el.labels?.length > 0
const hasAriaLabel = el.hasAttribute('aria-label')
const hasAriaLabelledby = el.hasAttribute('aria-labelledby')
if (!hasLabel && !hasAriaLabel && !hasAriaLabelledby) {
console.warn('Unlabeled form element:', el)
}
})
<label> with for AttributeThe explicit label association using matching for and id attributes:
<!-- Before: No label -->
<input type="email" id="email">
<!-- After: Properly labeled -->
<label for="email">Email address</label>
<input type="email" id="email">
This is the most robust method. Clicking the label focuses the input, improving usability for everyone.
<label>Implicit association by wrapping the input inside the label:
<!-- Implicit label - works without for/id -->
<label>
Email address
<input type="email">
</label>
This approach is simpler but less flexible for styling. Both methods are equally accessible.
aria-label for Visual LabelsWhen a visible label exists but isn't associated, or when the design requires no visible label:
<!-- Icon-only search button -->
<button aria-label="Search">
<svg>...</svg>
</button>
<!-- Input with visible placeholder but no label -->
<input type="search" aria-label="Search products" placeholder="Search...">
Use aria-label sparingly - visible labels are better for all users, not just screen reader users.
aria-labelledby for Complex CasesWhen the label text exists elsewhere on the page:
<h2 id="contact-heading">Contact Information</h2>
<div id="phone-hint">Include country code</div>
<input
type="tel"
aria-labelledby="contact-heading phone-hint"
>
Multiple IDs can be space-separated to combine text from several elements.
<template>
<UFormField label="Email address" name="email">
<UInput v-model="email" type="email" />
</UFormField>
</template>
<script setup>
const inputId = useId()
</script>
<template>
<div>
<label :for="inputId">{{ label }}</label>
<input :id="inputId" v-model="value">
</div>
</template>
htmlFor instead of for:function EmailField() {
const id = useId()
return (
<div>
<label htmlFor={id}>Email address</label>
<input id={id} type="email" />
</div>
)
}
Console verification:
// Should return empty array
[...document.querySelectorAll('input, select, textarea')].filter((el) => {
const hasLabel = el.labels?.length > 0
const hasAria = el.hasAttribute('aria-label') || el.hasAttribute('aria-labelledby')
return !hasLabel && !hasAria
})
<label> element near the input is not enough. Use for attribute or wrap the input inside the label.aria-label="" is worse than no label at all. Always provide meaningful text.type="hidden" should not have labels. Screen readers ignore hidden inputs.id, only one gets the label. IDs must be unique per page.Form label issues often appear alongside:
Form labels are easy to miss across a large site, especially in footers, search bars, and newsletter signups that appear on every page. Unlighthouse scans your entire site and identifies every page with unlabeled form elements, helping you fix them systematically.