Always read the <label>
Text fields in forms need to have labels, so that people know what they're meant to enter into them. Next to a username field, for example, it'll say something like "Enter your username". The HTML you'd use for this would involve a <label>
tag, with id
and for
attributes tying everything together. The association between <label>
and <input>
lets the browser know which hint applies to which field.
<label for="username">Username</label>
<input id="username" type="text" />
When properly associated like this, the label becomes a handy way for users of visual browsers to send focus to the field. Users of non-visual browsers, however, need the label to be there so that when the field is reached, there is any information about it at all.
Multi-labels
The pairing of one input tag with one label tag only allows space for one chunk of text for each field. It's not unusual for a design to call for more than one chunk to be associated with a field, often with the second one appearing elsewhere on the page.
This design pattern often appears when we need to explain more than one thing about a field: the fact that the username can't contain spaces, or that it needs to be more than a certain number of characters, for example. It's also a common way to show feedback after form validation.
Can't we just add more labels?
Sadly, no. You might think that if you need two labels, you could just use two label tags. But as Roger Johansson pointed out in a while back, this appears not to work. Generally, only one or other of the labels is noticed by screenreaders.
As a result, out in the wild, extra bits of hint text usually end up separated from the label, inside another tag, with CSS then positioning them according to the design. The HTML generally looks something like this:
<label for="username">Username</label>
<input id="username" type="text" />
<span id="username_info">Pick a name to use on this site.</span>
The problem with this approach is that the second hint chunk won't always be read out by screenreaders, as only the main label tag, "Username", is explicitly associated with the input. To use a common but sometimes hard-to-pin-down phrase, this way of doing things is inaccessible.
A quick aside about forms mode
When interacting with forms, some screenreaders enter 'forms mode'. This switches the keyboard over from page navigation (using the keys to jump to different parts of the page) to allow regular text input. This allows you to type your username, rather than perform browser-related shortcuts. In this mode, only form-related tags are read out. Other tags (apart from links) tend to be ignored as you move around the page.
This means that if the text in your second chunk of label text is important, it may be ignored. This could be a problem:
<label for="user_code">Please enter your user code</label>
<input id="user_code" type="text" />
<span class="hint">but only if you want the nuclear reactor
to shut down immediately</span>
The importance of being important
If the additional chunk of text isn't vital for users to read, then you might say it doesn't matter that it's not available to screenreaders. In the example above, the hint text "Pick a name to use on this site" is arguably not as critical to understanding the form as the nuclear reactor label. But if something's important enough to be in the page, then - all other things being equal - it should be available to everyone... otherwise, it arguably shouldn't be there at all.
Two for one
Time for some actual answers. Provided we don't use it as an excuse to write an essay inside the label tag, a solution to the 'two labels' problem is to place both the main and hint chunks together. As they're both important to understand, it's really just one label anyway.
<label for="username">
<span class="main">Username</span>
<span class="hint">must be more than 5 characters</span>
</label>
<input type="text" id="username" />
If a screenreader reads this out, however, it'll join the two hints together as if they're a single sentence, and this might sound slightly odd. To make things read more sensibly, we can insert a hidden full-stop. This makes the screenreader pause for a moment and read the two chunks as we might expect them to be read: "Username [pause] must be more than 5 characters".
<label for="username">
<span class="main">Username</span>
<span class="hide">. </span>
<span class="hint">must be more than 5 characters</span>
</label>
<input type="text" id="username" />
Presentation
There are lots of ways that you might want to present the label and its hint text, and even more approaches to the CSS. But to separate the hint and place it below the field, you could do something like this:
#container {
background-color: #eee;
position: relative;
width: 270px;
}
#container label { display: block; line-height: 1.5em; }
#container span.main {
font-weight: bold;
display: block;
}
#container span.hint {
display: block;
margin: 1em 0 0 0;
text-align: right;
font-size: 0.9em;
}
#container input {
position: absolute;
top: 0;
right: 0;
width: 13em;
}
.hide { position: absolute; left: -2500px; width: 1px; }
Which turns out like this:
Potential issues
Nothing is ever simple, though. While this technique works well, it doesn't work everywhere... and in particularly quirky cases, we can end up with a situation where nothing is read out. This is, of course, exactly the problem we were trying to solve in the first place...
Voiceover 3.0 on Mac OS X, for example, appears to ignore elements inside the label that are set to display: block
; this problem doesn't seem to happen in earlier versions. And using absolute positioning to move the hint chunk into place can, in some situations, cause problems in JAWS. More work needs to be done to pin this one down, but it seems to involve the vertical height of the hint relative to the main label, and which elements (the label and the spans) are set to display: block
.
One way to resolve this, although it's a shame to have to do this, is to add the label and hint text as a title
attribute on the input tag:
<label for="username2">
<span class="main">Username</span>
<span class="hide">. </span>
<span class="hint">must be more than 5 characters</span>
</label>
<input title="Username. must be more than 5 characters" type="text" id="username2" />
In the absence of a label, the title is read out by Voiceover as if it was working normally. When things are working as expected, the title should be ignored in favour of the label.
hammer.crack("nut");
In summary...
- Form <input> fields need to be properly paired with a <label> tag
- Screenreaders in forms mode tend to ignore anything other than single <label> tags
- If you need to add more than one chunk of text to a label, try putting both chunks inside the label, and then moving the chunks into place with CSS
- Use hidden full-stops to make things flow as expected
- Always test what you've done as widely as you can, because these things can be unpredictable...
This is a tricky problem, so if you have any thoughts or suggestions, or have a chance to try these examples in assistive devices that you have access to, it'd be great to hear from you.
Comment number 1.
At 5th Jan 2010, Ms2ger wrote:How about
Does AT have problem with this construct as well?
Complain about this comment (Comment number 1)
Comment number 2.
At 6th Jan 2010, Isac wrote:what about skipping the span.hide and using pause [1]:
#container span.main {
font-weight: bold;
display: block;
pause-after: 10ms;
}
I'm not sure how well supported it is among browsers though...
[1]
Complain about this comment (Comment number 2)
Comment number 3.
At 6th Jan 2010, Richard wrote:Thanks for that, Ms2ger, I'll add that to the list of things to test (something for another post, sometime). It's more or less the same thing, I suppose, but you never know how these things turn out until you try them...
Isac - not sure how widely supported that is, either... will give it a go. Ta.
Complain about this comment (Comment number 3)
Comment number 4.
At 6th Jan 2010, Suzie B wrote:Thanks for posting- I always find form layouts particularly laborious to style!
There's a great article here on form usability: suggesting your last example is the best way to lay out form labels and elements. It's easy to run into problems when the size of the label is long or unknown though.
I'm also wondering about the full stop, as there's a inconsistancy in screen readers on whether they read or ignore display:none, and @media, and therefore it would be hard to force it to be 'spoken' if it was hidden on screen.
Complain about this comment (Comment number 4)
Comment number 5.
At 6th Jan 2010, Mark Stickley wrote:@Ms2ger: Apparently some AT has problems with that kind of implicit labeling (. It also feels kind of... unsemantic. The input it's really part of the label. It's what we're trying to label!
@Suzie B: Thanks, that's an interesting article. As for the full stop, screen readers will often ignore text which is display: none'd which is why the style .hide above is crucially defined to hide using absolute positioning off screen.
Complain about this comment (Comment number 5)
Comment number 6.
At 13th Jan 2010, Gilles Ruppert wrote:@Mark, @Ms2ger: IE6 doesn't like implicit labels either, but you could nest the label and input, and still use the for attribute, such as:
This can also help you get rid of extra divs, since you establish a relation between the elements which can be used for styling.
It will be interesting to see how this works in different screenreaders.
Complain about this comment (Comment number 6)