Missing form labels
forms/placeholder-as-label
- WCAG success criterion
- 3.3.2 (level A)
- Outcome class
- Hint only. An automated check that by design reports review hints only: its findings are marked for manual review, never as confirmed violations.
- Area
- Accessibility, Forms & Interaction
- Default severity
- high
- axe-core rule
label
Why we flag this
Users do not know what input is expected.
Missing <label> elements.
What the rule checks
Scope. Rendered input, select and textarea elements with a non-empty placeholder.
Expectation. Needs review when the placeholder is the only visible label: it names the field, but it disappears as soon as the user types, so whether it suffices as a label is a human judgement. A field with label[for] or a wrapping <label> is not reported. aria-label is not visible, so a field whose only visible label is the placeholder needs review even when it also has aria-label (forms_gap_placeholder_aria). Never a confirmed violation.
Source. WCAG 2.2 SC 3.3.2 Labels or Instructions (Understanding: placeholder text disappears on input); HTML §4.10.5.3.11 placeholder
Detection: shared; needs dom.
Failing example
The check flags this for review. Corpus case forms_gap_placeholder_aria, element input#porch-note. The page may contain further findings for other rules.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Porch delivery</title>
<style>
body { font-family: sans-serif; color: #222; background: #fff; line-height: 1.5; margin: 0 16px; }
a { color: #0050a0; }
.skip, nav a, .solo { display: inline-block; padding: 12px; min-height: 44px; min-width: 44px; box-sizing: border-box; }
button, input, select, textarea, [role="button"], [role="textbox"], [role="radio"], [role="link"], [role="searchbox"] {
display: inline-block; min-height: 44px; min-width: 44px; box-sizing: border-box; font-size: 1rem; }
input[type="checkbox"], input[type="radio"] { width: 44px; height: 44px; }
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
</style>
</head>
<body>
<a class="skip" href="#main">Skip to main content</a>
<header><p>Example Shop</p>
<nav aria-label="Main"><a href="/" aria-current="page">Home</a> <a href="/help">Help</a></nav>
</header>
<main id="main">
<h1>Porch delivery</h1>
<!-- G2 #783: aria-label names the field, but the only visible label is the placeholder, which disappears on input (3.3.2). Same review as a placeholder-only field. -->
<form action="/porch" method="post">
<p><input id="porch-note" name="porch" type="text" aria-label="Porch note" placeholder="Porch note"></p>
<p><button type="submit">Save note</button></p>
</form>
</main>
<footer><p>Example Shop, 2026</p></footer>
</body>
</html>Passing example
The check reports nothing here. Corpus case forms_labels, element input#lbl-ph. The page may contain further findings for other rules.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Delivery preferences</title>
<style>
body { font-family: sans-serif; color: #222; background: #fff; line-height: 1.5; margin: 0 16px; }
a { color: #0050a0; }
.skip, nav a, .solo { display: inline-block; padding: 12px; min-height: 44px; min-width: 44px; box-sizing: border-box; }
button, input, select, textarea, [role="button"], [role="textbox"], [role="radio"], [role="link"], [role="searchbox"] {
display: inline-block; min-height: 44px; min-width: 44px; box-sizing: border-box; font-size: 1rem; }
input[type="checkbox"], input[type="radio"] { width: 44px; height: 44px; }
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
</style>
</head>
<body>
<a class="skip" href="#main">Skip to main content</a>
<header><p>Example Shop</p>
<nav aria-label="Main"><a href="/" aria-current="page">Home</a> <a href="/help">Help</a></nav>
</header>
<main id="main">
<h1>Delivery preferences</h1>
<!-- G2 #783: every labelling mechanism next to fields without one. A field needs a programmatic name (4.1.2, 1.3.1) and a visible label (3.3.2); a placeholder or title names it but is no lasting visible label. -->
<form action="/preferences" method="post">
<p><input id="coupon-code" name="coupon" type="text"></p>
<p><textarea id="gift-message" name="gift"></textarea></p>
<p><select id="wrap-style" name="wrap"><option>Plain</option><option>Festive</option></select></p>
<p><input id="voucher" name="voucher" type="text" placeholder="Voucher code"></p>
<p><input id="door-code" name="door" type="text" title="Door code"></p>
<p><label for="lbl-for">Gate number</label> <input id="lbl-for" name="gate" type="text"></p>
<p><label>Floor <input id="lbl-wrap" name="floor" type="text"></label></p>
<p><input id="lbl-aria" name="stairs" type="text" aria-label="Stairwell"></p>
<p><span id="bell-label">Doorbell label</span> <input id="lbl-by" name="bell" type="text" aria-labelledby="bell-label"></p>
<p><label for="lbl-ph">Courier note</label> <input id="lbl-ph" name="note" type="text" placeholder="Leave at the door"></p>
<p><label for="lbl-title">Drop-off spot</label> <input id="lbl-title" name="spot" type="text" title="Where to leave the parcel"></p>
<p><label for="lbl-select">Wrapping paper</label> <select id="lbl-select" name="paper"><option>Kraft</option><option>Gloss</option></select></p>
<p><label for="lbl-area">Card text</label> <textarea id="lbl-area" name="card"></textarea></p>
<p><span id="title-aria" role="textbox" contenteditable="true" tabindex="0" title="Gift note"></span></p>
<input type="hidden" name="token" value="abc">
<p><button type="submit">Save preferences</button></p>
</form>
</main>
<footer><p>Example Shop, 2026</p></footer>
</body>
</html>This page is generated from the rule catalogue and the rule contracts; see therule overview for how the outcome classes are derived.