Search topics
Split date and phone fields with no name for each box
A date of birth asked for in three dropdowns has one label, tied to the first, so a screen reader reads the month and the year with no name.
<label for="dob-day"> The problem
Screen reader users hear the month and the year as nameless dropdowns, and once all three hold numbers, nothing on screen says which is the day and which is the month.
An online shop's sign-up form asks for a date of birth. It is drawn as one row: three dropdowns side by side, Day, Month and Year, with a slash between each, under the label "Date of birth". Each dropdown opens on a prompt ("Day", "Month", "Year") until something is chosen. Someone picks 12, 05 and 1990, and the row reads "12 / 05 / 1990". The prompts are gone: nothing on screen now says which box is the day and which is the month, and in a country that writes the month first, "12 / 05" reads as 5 December. A screen reader user who presses Tab hears "Date of birth, combo box, 12", then "combo box, 05", then "combo box, 1990". The second and third boxes have no name at all: a screen reader has none to read, and a voice control user has none to say.
The label is a real label with for="dob-day", so it names the first dropdown and nothing else. A label names one field; it can't name three. The month and year dropdowns are left with no accessible name, and a select's first option, "Month", is its value, not its name: it is read only until something else is chosen. That fails 4.1.2, which needs every control to have a name; W3C's failure F86 describes exactly this, a field asked for in parts whose later parts have none.
It fails 1.3.1 as well. On screen the three boxes are plainly one answer, and their order and the slashes say which part each one is. None of that is in the code: no group holds the three, so the words "Date of birth" never reach the second and third, and nothing but position says this box is the month.
And it fails 3.3.2, which needs labels or instructions people can use while they fill in the form. The prompt option is the only thing that says "Month", and it disappears as soon as a month is chosen, as a placeholder does once something is typed. From then on, the set has a label but its parts have none, and the layout is all that is left to tell 12 from 05. W3C's Understanding page for 3.3.2 makes the same point about a phone number in three boxes: the punctuation is not enough to label the fields, and one "Phone number" label can't label all three.
The failure shows once the form is filled in, which is when people check it. Someone who goes back to correct a date, or reviews the form before sending it, meets three numbers and has to remember, or guess, the order they chose them in.
3 of 10 groups affected
- No vision (affected)
- Low vision (not affected)
- Colour vision (not affected)
- No hearing (not affected)
- Hard of hearing (not affected)
- No speech (not affected)
- Motor (affected)
- Reach, strength (not affected)
- Cognitive (affected)
- Seizures (not affected)
- Without vision Screen reader and braille users hear the first dropdown as "Date of birth" and the next two as a bare combo box with a number, so they can't tell which part they are in or check what they chose.
- Limited language, cognitive and learning abilities Once every box holds a number, nothing on screen says which is the day and which is the month, so people who find dates hard to read, or who come back to check, have to work it out from the order.
- Limited manipulation Voice control users can say "click Date of birth" to reach the first dropdown, but the other two have no name to say, so they have to call up numbered labels instead.
axe's select-name rule and ANDI find the two dropdowns with no name, and axe's label rule finds the same mistake in a phone number split into text boxes. What they can't tell is whether the set's label reaches the parts. Give every dropdown an aria-label and the scanners go quiet, even if nothing ties "Date of birth" to them, or if the names are heard only by screen readers. A quick fix can do worse: aria-label="Date of birth" on all three dropdowns silences the scanners too, and a screen reader then hears "Date of birth" three times and still doesn't know which box is the month. EaseWeb's audit sends a set of named parts with no group around it, or with names only screen readers get, to a person as a lead (ew:split-field-manual-check), because whether each part can be told apart, by ear and by eye, is something a person has to check.
Try it
Press Tab to move through the date of birth, and listen to what each box is called.
The date is already chosen. Below the form, check whether each box is read with a name, then look at the row: which box is the month?
Broken Can you fix this?
Screen reader says:
(Tab to the date of birth)
The fix
Name every box, and tie the set's label to all of them.
<label for="dob-day" class="field-label">Date of birth</label><div class="date-parts"> <select id="dob-day" name="dob-day"> <option value="">Day</option> <option>01</option> … </select> / <select name="dob-month"> <option value="">Month</option> <option>01</option> … </select> / <select name="dob-year"> <option value="">Year</option> <option>2026</option> … </select></div>What is wrong3 points
The first dropdown is named "Date of birth". The rest is where it fails.
- The month and year dropdowns have no name, so a screen reader reads only "combo box" and the value.
- Nothing groups the three, so "Date of birth" is never read with the month or the year.
- The words Day, Month and Year are only the first options, and they disappear once a value is chosen.
A fieldset with a label on each box
<fieldset class="date-parts"> <legend class="field-label">Date of birth</legend> <div class="date-part"> <label for="dob-day">Day</label> <select id="dob-day" name="dob-day"> <option value="">Day</option> <option>01</option> … </select> </div> <div class="date-part"> <label for="dob-month">Month</label> <select id="dob-month" name="dob-month"> <option value="">Month</option> <option>01</option> … </select> </div> <div class="date-part"> <label for="dob-year">Year</label> <select id="dob-year" name="dob-year"> <option value="">Year</option> <option>2026</option> … </select> </div></fieldset>How it is fixed4 steps
- Make the row a
fieldset, so the three dropdowns are one group. - Make "Date of birth" its
legend, the group's name. A screen reader says it as focus enters the group, roughly "Date of birth, grouping, Day, combo box, 12". - Give each dropdown an
idand a visiblelabelof its own: Day, Month and Year. They stay on screen once values are chosen, and voice control users can say "click Month". - Drop the slashes: the labels now say what they said.
This fixes the Before and passes 1.3.1, 3.3.2 and 4.1.2. It is the pattern the GOV.UK date input and the US Web Design System's memorable date use, with a label above each part.
Why this fix: Use native HTML first
Both give every box a name and tie "Date of birth" to all three. Use the fieldset with a label on each box: it is the only one that also tells sighted people which box is which once the form is filled in, and it does that with plain HTML and nothing hidden. The hidden names are a stopgap for a design that can't change this release.
The order is not a label. Day, month, year is the order in much of the world, month first in the United States, year first in Japan. With a label on each box, any order works; without one, the order is a guess. Put the parts in the order your readers expect, and keep the labels anyway.
The first option is not a name. A prompt such as "Day" or "Choose day" can stay as the empty first option, but it is the select's value, read only until a value is chosen. Frameworks fill it in this way: Django's SelectDateWidget takes these prompts through empty_label when the date is not required. The name has to come from a label.
Framework helpers leave the parts unnamed. Rails' date_select renders three selects with ids ending _1i, _2i and _3i, so a label written for the attribute names none of them. In Django's default as_div() layout, the three selects of a SelectDateWidget sit in a fieldset with the field's label as the legend, with no label on each select; the as_p(), as_table() and as_ul() layouts tie the label to the first select alone. In both, add a label to each part yourself, or render the parts from your own component.
Phone numbers and postcodes are better as one field. A phone number split into area code, prefix and line number needs a name on every box just as a date does, but the better fix is not to split it: one field takes a number however it is typed or pasted. The US Web Design System says not to split a phone number into separate fields, and GOV.UK asks services to let people enter it in whatever format is familiar to them. A field that formats its value as it is typed has problems of its own, covered in input masks that change a typed or pasted number. A card expiry in two dropdowns needs a "Month" and a "Year" label inside a fieldset whose legend is "Expiry date".
Let the browser fill it in. For a date of birth, put autocomplete="bday-day", "bday-month" and "bday-year" on the three parts, so a browser can fill in what it has saved. That is its own criterion, covered in form fields that block or break autofill.
Leave focus where it is. Don't move focus to the next box as soon as one is filled in; GOV.UK's date input pattern says never to. It surprises people who go back to correct a part. Boxes that move focus on by themselves are covered in code boxes where a keyboard can't correct a wrong digit.
Errors point at the part. When one part is wrong, say which in the message ("Enter a month from 1 to 12") and mark that part, as form errors that don't say how to fix them describes. A date people pick from a calendar is a different control, covered in date pickers that can't be typed in or used by keyboard.
Design ideas
Split the date, but keep a word on every box.
Three ways to draw the same date of birth, with the day and the month filled in and the year empty. Tab through each and type a year: two keep a name on every box, and one loses them as you type.
In the design file
- Draw the set filled in, and check every box still shows its name.
- Write the question once, above the boxes, as the title of the set.
- Put the boxes in the order your readers write dates, and keep the names whatever the order.
- Draw the focus ring on each box, and an error on the one box that is wrong.
- Write each label colour’s contrast ratio beside it, 4.5:1 or more (9.46:1 in the first sample).
Passes
- Label in a gap in each border Three outlined boxes, each with its name in a gap in its top border, 13px #4a4541 at 9.46:1 on white. The name sits there empty or filled, so the label never has to move, and the 1px #8f8882 edge holds 3.49:1.
- One pill, a name in each segment The three boxes drawn as one soft #f6f4f2 pill, split by thin lines, with each segment’s name small at its top, 13px #4a4541 at 8.63:1, and the value under it. It reads as one answer and still names every part; focus rings the one segment.
Fails
- The same pill with only DD, MM, YYYYWCAG 3.3.2 The same pill, with the grey letters DD, MM and YYYY inside the segments, #5c5650 at 6.60:1, and no names. Each set of letters goes as soon as a digit is typed, so the filled segments show 12 and 05 and nothing that says which is the month.
Verify the fix
4 checks, no mouse.
Test with: Keyboard, Screen reader, Zoom
Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the shared date component changes.
Limits
This is a starting pattern, not a guarantee for every application. A screen reader reads a group's name as focus enters the group, so coming back with Shift + Tab from below, it is read with the year rather than the day, and some screen readers may repeat it on every part. The guide covers dates asked for in parts; a value split into one box per character, such as a one-time code, is covered in one-time code boxes that won't take a pasted code. Radio buttons and checkboxes under one question are covered in radio buttons and checkboxes without a group name. Test the complete form with your target assistive technology.
This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.
References
- WCAG Understanding: info-and-relationships (opens in a new tab)
- WCAG Understanding: labels-or-instructions (opens in a new tab)
- WCAG Understanding: name-role-value (opens in a new tab)
- WCAG Technique F86 (opens in a new tab)
- WCAG Technique ARIA17 (opens in a new tab)
- design-system.service.gov.uk: date-input (opens in a new tab)
- designsystem.digital.gov: memorable-date (opens in a new tab)
EaseWeb service
Fixed it? Keep it from coming back.
I turn guides like this one into rules your coding agents follow and checks your CI runs. Then I retest by hand with a keyboard and screen readers, periodically.