Labels: The Most Important Element in a Form
A label names a field for every reader and grows its click target. Two ways to bind one, and the reasons a form without labels is not usable.
-
HTML Basics
- What is HTML: The Structure Layer of Every Web Page
- HTML Document Structure: DOCTYPE, html, head and body
- Elements, Tags and Attributes: The Vocabulary of HTML
- HTML Comments: Notes That Ship With Your Code
- Block Level and Inline Elements
- Writing and Running Your First HTML Page
- How a Browser Turns Markup Into a Page
- Text and Formatting
- Links and Navigation
- Images and Media
- Lists
- Tables
-
Forms
- Form Structure: form, action and method
- Input Types: Text, Email, Number, Date and the Rest
- Labels: The Most Important Element in a Form
- Checkboxes, Radio Buttons and Grouping
- select, option, optgroup and datalist
- textarea, File Uploads and Hidden Fields
- Buttons: submit, reset and button
- Built In Form Validation
- GET or POST: What Happens When a Form Is Submitted
- Semantic HTML
- HTML5 Features
- Head and Metadata
- HTML with CSS
- HTML with JavaScript
- Accessibility
-
HTML SEO
- How Google Works: Crawling, Indexing and Ranking
- SEO Friendly HTML Structure
- Titles and Descriptions That Earn Clicks
- Headings and Content Structure for Search
- Internal Linking and Anchor Text
- robots.txt and XML Sitemaps
- Canonical URLs and Duplicate Content
- Structured Data and JSON-LD
- Image SEO
- Core Web Vitals and Mobile Friendliness
- DevTools and Debugging
- Editor Productivity
- HTML Best Practices
- HTML Projects
- Advanced Projects
- Practice and Exams
Concept
A label attaches a name to a form control. That sounds minor and is not. Without a label a control has no accessible name, which means a screen reader announces edit text, blank and the reader has no idea what to type.
A correctly bound label does three things:
- Gives the control its accessible name, announced whenever the control receives focus.
- Makes the label text itself clickable, focusing the field or toggling the checkbox. On a phone this turns a twenty pixel checkbox into a target the width of a sentence.
- Tells the browser what the field holds, which improves autofill.
Two ways to bind
Explicit - for and id
<label for="email">Email address</label>
<input type="email" id="email" name="email">The for attribute holds the id of the control. This is the preferred form: the two elements can sit anywhere relative to each other, so any layout is possible.
Implicit - wrapping
<label>
Email address
<input type="email" name="email">
</label>No ids needed, which is convenient in generated markup. The limitation is layout: the control must sit inside the label. It is most useful for checkboxes and radio buttons, where the text follows the control anyway.
Using both together - a wrapping label that also carries for - is fine, and a good safety net in templates.
Example
<form action="/register" method="post">
<p>
<label for="name">Full name</label>
<input type="text" id="name" name="name" required autocomplete="name">
</p>
<p>
<label for="password">Password</label>
<input type="password" id="password" name="password"
required minlength="12"
autocomplete="new-password"
aria-describedby="password-help">
<small id="password-help">At least twelve characters. A passphrase works well.</small>
</p>
<p>
<label>
<input type="checkbox" name="updates" value="yes">
Send me occasional course updates
</label>
</p>
</form>Explanation
The first two fields use explicit binding so the label can sit above the input. The password field adds aria-describedby, which points at the hint: the label is announced as the name, then the hint is announced as a description. Putting the hint inside the label would make the accessible name a whole sentence, read out every single time.
The checkbox uses implicit binding, because the text naturally follows the control and the whole phrase becomes the click target.
Marking a field required
<label for="email">
Email address <span aria-hidden="true">*</span>
</label>
<input type="email" id="email" name="email" required>
<p><small>Fields marked * are required.</small></p>The asterisk is hidden from screen readers because required is already announced. Without aria-hidden the field is announced as Email address star, required.
Some designs mark optional fields instead, which is clearer when most fields are required. Either way, explain the convention once at the top of the form.
Never hide a label with display none
If the design has no room for a visible label, the label must still exist for assistive technology. There are three correct approaches and one wrong one.
<!-- WRONG: removes it from the accessibility tree too -->
<label for="q" style="display:none">Search</label>
<!-- Correct 1: visually hidden, still announced -->
<label for="q" class="visually-hidden">Search the site</label>
<input type="search" id="q" name="q" placeholder="Search">
<!-- Correct 2: aria-label when no visible text exists at all -->
<input type="search" name="q" aria-label="Search the site">
<!-- Correct 3: point at existing visible text -->
<span id="q-label">Search</span>
<input type="search" name="q" aria-labelledby="q-label">.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}Prefer the visually hidden label. It survives translation and works with voice control software, which relies on matching spoken words to visible text.
Important rules
formust exactly match theid, and ids are case sensitive.- A label may be bound to only one control.
- A control may be named by several labels, and they are concatenated.
- Labels do not work with
div,spanor any non form element. aria-labeloverrides any visible label text, so use it only when there is no visible text.- Legends label a group; labels label a control. They are not interchangeable.
Common mistakes
- No label at all, relying on a placeholder.
forandidthat do not match, usually a typo or a case difference. The binding silently fails.- Duplicate ids on a page, so the label binds to the first one.
- Hiding a label with
display: none. - Putting the hint text inside the label, making the accessible name a paragraph.
- Using a bold
spanabove a field and calling it a label. - Labelling a group of radio buttons with a label instead of a legend.
Best practices
- Every control gets a label. No exceptions.
- Prefer explicit
forandid; use wrapping for checkboxes and radios. - Place labels above fields in a single column layout. It is the fastest to complete and works at any width.
- Put hints in a separate element bound with
aria-describedby. - Keep label text short and free of instructions.
- Click every label to confirm it focuses the right control. It is a two second test that catches every broken binding.
Practice
- Build a form with five fields, then click each label and confirm focus lands correctly.
- Break one
forattribute deliberately and note that nothing visibly changes. How would an audit catch it? - Add a hint to a password field with
aria-describedby, then move it inside the label and compare the announcements. - Build a search field with a visually hidden label and confirm it is announced.