In English
Code
<script> nordburgPwReq.custPwRequirements["notPassword"] = {"notPassword" : {"text" : {"en" : "Does not contain 'Password'", "fr" : "Ne pas contenir « Password »"}, check : function (p1, p2) { let re = /password/i; return !(p1.match(re) || p2.match(re)); }}}; nordburgPwReq.custPwRequirements["pasMotdepasse"] = {"text" : {"en" : "Not contain 'MotDePasse' or 'Password'", "fr" : "Ne pas contenir « MotDePasse » ou « Password »"}, check : function (p1, p2) { let re = /password|motdepasse/i; return !(p1.match(re) || p2.match(re)); }}; </script> ... <div class="fieldHolder"> <label for="pword1">Password:</label> <input name="pword1" id="pword1" type="password" class="nbpr-new-password lowercase uppercase special-char digit max-consecutive nospaces" data-minchars="8" data-maxchars="10" data-match="pword2" data-password-requirements-container="passwordRequirementsEn" aria-describedby="theH1" data-max-consecutive="4" autocomplete="off"> </div> <div class="fieldHolder"> <label for="pword2">Confirm Password:</label> <input name="pword2" id="pword2" type="password" class="nbpr-confirm-password" data-match="pword1"> </div> <div id="passwordRequirementsEn"></div>
En Français
Code
<div id="passwordRequirementsFr"></div> ... <div class="fieldHolder"> <label for="pword3">Mot de passe :</label> <input name="pword3" id="pword3" type="password" class="notPassword maxchars minchars nbpr-new-password lowercase uppercase special-char digit pasMotdepasse max-consecutive" minlength="4" data-minchars="6" data-max-consecutive="6" data-match="pword4" data-maxchars="20" maxlength="21" data-password-requirements-container="passwordRequirementsFr"> </div> <div class="fieldHolder"> <label for="pword4">Confirmez le mot de passe :</label> <input name="pword4" id="pword4" type="password" class="nbpr-confirm-password" data-match="pword3"> </div>
Notes
The important parts of this widget are:
- Only the first password field references the list of requirements using
aria-describedby
- âMetâ and âUnmetâ only change when the criteria becomes met, or unmet.
- Each list item is like:
<li aria-live=âpoliteâ artia-atomic=âtrueâ>
.aria-live=âpoliteâ
makes a screen reader read out the contents when thereâs a change, andaria-atomic=âtrueâ
makes the screen reader read out the whole line, not just âmetâ or âunmetâ. Note that each list item needs to be its own live region. If the whole widget is a live region, then the whole list gets read out whenever thereâs a change. You donât want that.
Screen Reader results
In the above, I have the widget demo'd in French and English. In English, the list of requirements comes after the password inputs. In French it comes before. In English, the status of the requirements are exposed to screen readers after the requirement (Ex: "At least 1 uppercase letter. Met"). In French, they come before (Ex: "Remplie : Au moins 1 lettre majuscule"). In both languages, the status is a <span>
nested inside the <span>
that has the requirement. In English, the <H1>
is referenced by the first password input with aria-describedby
to demonstrate that you can still use aria-describedby
and this widget won't override your values - only add to them.
The following tests will be done and results listed below:
- Top to Bottom
- Read through the page top to bottom (Insert+Down in NVDA, JAWS, and Narrator. Control+option+Right in VoiceOver OS X, swipe-right in VoiceOver iOS.) to check if the list of requirements is read as a list, if each requirement is listed with its status.
- Bit by Bit
- Read through the page line by line, item by item. (Down in NVDA and JAWS. Insert+Right in Narrator. Control+option+right arrow key in VoiceOver OS X, swipe-right in VoiceOver iOS.) This shouldn't give different results than Top to Bottom. If it never does, I'll just stop tracking, and erase this. Else, differences will be noted.
- Focusing
- When the first password field takes focus, are all elements referenced by
aria-describdedby
read out, including the<H1>
in the English widget, the phrase "All passwords must contain", and each criteria along with their status? - Typying
- As you type, when requirements are met or unmet is the change announced? Does the announcement include both the requirement and its new status?
- Re-typing
- If you type a few characters, then Shift+Home or Shift+Back to select the whole password, then delete and start again, all requiremtns should be set to their initial state, then they should change as they're met/unmet as you type more characters. I'll type "P-a-s-s" to change the status for Uppercase, Lowercase, and no two letters consecutively.. Then Select-all -> Delete -> retype the same thing.
Each will have notes about support and behavior. Here's what they mean:
- Full support
- Everything works as it should, including
lang
attributes. A screen reader user shouldn't experience any barriers beyond the inherent complixity of the process. - Mostly supported
- Most things will work as they should, but there may be something that's not right which could be part of how the screen reader and/or operating system work, such as not respecting the
lang
attributes. My results could be because I don't have French properly downloaded or installed on my OS or SR. But this is mostly an edge case. - Partial support
- Some aspects work. Some don't. A screen reader user may be able to find workarounds, for example if not all password requirements are read in a top-down reading, but they are when the input takes focus.
- No support
- Not a thing in that test works. A screen reader user will face a signifiant barrier. The thing just doesn't work.
- Odd behavior
- This is for things that made me go "hmmmmm". Like if the screen reader only reads part of a list item; or reads it twice or thrice.
- N/A
- Not Applicable. Due to a limitation in the system (or my skills), this test couldn't be performed.
Windows:
NVDA 2022.1
- Firefox 101
-
- Top to Bottom
- Everything is read out as it should be. Full support.
- Bit by Bit
- Everything is read out as it should be. Full support.
- Focusing
- Everything is read out as it should be. Except the French is read out with a strong English accent. Mostly supported.
- Typying
- Everything is read out as it should be. Full support.
- Re-typing
- Everything is read out as it should be. Full support.
- Edge 103
-
- Top to Bottom
- Everything is read out as it should be. Full support.
- Bit by Bit
- Everything is read out as it should be. Full support.
- Focusing
- Everything is read out as it should be. Except the French is read out with a strong English accent. Mostly supported.
- Typying
- Everything is read out as it should be. Full support.
- Re-typing
- Everything is read out as it should be. Full support.
- Chrome 102
-
- Top to Bottom
- Everything is read out as it should be. Full support.
- Bit by Bit
- Everything is read out as it should be. Full support.
- Focusing
- Everything is read out as it should be. Except the French is read out with a strong English accent. Mostly supported.
- Typying
- Everything is read out as it should be. Full support.
- Re-typing
- Everything is read out as it should be. Full support.
- Internet Explorer 11
-
- Top to Bottom
- Everything is read out as it should be. Full support.
- Bit by Bit
- Everything is read out as it should be. Full support.
- Focusing
- When focus lands in the
<input>
, the list of requirements, along with their status, is read out as it should be. But that's all that's read out. The<H1>
and "Your password must contain" are not read out. Partial support. - Typying
- As I type, when the status of a requirement changes, the requirement is read out along with the status of what it was, not what it is. Ex: when I type "P" I hear "At least 1 uppercase letter unmet". Partial support.
- Re-typing
- When I select the text and delete it, I hear the requirements that have changed all read out along with their old status. Ex: "At least 1 uppercase letter met, At least 1 lowercase letter met", etc. Then as I retype the password, I hear everything as I did when I first typed the password. Partial support.
JAWS 2021
- Firefox 101
-
- Top to Bottom
- Everything is read out as it should be. Except the French is read out with a strong English accent. Mostly supported.
- Bit by Bit
- Everything is read out as it should be. Except the French is read out with a strong English accent. Mostly supported.
- Focusing
- Everything is read out as it should be. Except the French is read out with a strong English accent. Mostly supported.
- Typying
- Everything is read out as it should be. Full support.
- Re-typing
- Everything is read out as it should be. Full support.
- Edge 103
-
- Top to Bottom
- Everything is read out as it should be. Except the French is read out with a strong English accent. Mostly supported.
- Bit by Bit
- Everything is read out as it should be. Except the French is read out with a strong English accent. Mostly supported.
- Focusing
- Everything is read out as it should be. Except the French is read out with a strong English accent. Mostly supported.
- Typying
- Everything is read out as it should be. Full support.
- Re-typing
- Everything is read out as it should be. Full support.
- Chrome 102
-
- Top to Bottom
- Everything is read out as it should be. Except the French is read out with a strong English accent. Mostly supported.
- Bit by Bit
- Everything is read out as it should be. Except the French is read out with a strong English accent. Mostly supported.
- Focusing
- Everything is read out as it should be. Except the French is read out with a strong English accent. Mostly supported.
- Typying
- Everything is read out as it should be. Full support.
- Re-typing
- Everything is read out as it should be. Full support.
- Internet Explorer 11
-
- Top to Bottom
- Everything is read out as it should be. Full support.
- Bit by Bit
- Everything is read out as it should be. Full support.
- Focusing
- Everything is read out as it should be. Full support.
- Typying
- Everything is read out as it should be. Full support.
- Re-typing
- Everything is read out as it should be. Full support.
Narrator
- Firefox 101
-
- Top to Bottom
- It reads everything fine until it gets to the list. At first it reads out it all as it should. Then when it gets to the bottom and reads the last list item, it goes back up and re-reads "Your password must contain". Then it reads the list again properly. Then when it's done reading the last list item, it goes back and re-reads the list starting with the first list item. Then at the second list item, after it reads out the requirement, it goes back and re-reads the second half of the requirement and the status Ex: "Passwords must match unmet. At least 8 characters met. A maximum of 10 characters. Of 10 characters met. At least 1 lowercase letter unmet. 1 lowercase letter unmet." It seems that whenever Narrator meets a new block of content, it wants to read it all out. Also,
lang
attributes are not respected when it gets to the French. - Bit by Bit
- Maybe I just don't know Narrator well enough yet, but when I Insert+Right and I come to an
<input>
, my focus gets placed there, and Insert+Right doesn't take me out, like I'm in some kind of Forms Mode. All I hear is "No next item". I'm going to consider this to be total failure no support. - Focusing
- When the password input takes focus, after about a 2 second pause only the very first thing in the
aria-describedby
(ie: the<H1>
) is read. In French, none of thearid-describedby
content is read. But thelang
attributes are not respected. No support. - Typying
- As I type, with each character, all I hear is "hidden". No changes are being read by Narrator. No support.
- Re-typing
- With no changes being read out as I type, this doesn't work either. No support.
- Edge 103
-
- Top to Bottom
- It reads everything the way it should. respecting
lang
attributes. - Bit by Bit
- When it gets to the
<label>
, it does not read it out immediately. You have to press Insert+Right again. Then the<label>
gets read. When it gets to the list, first it reads out "1 of 10 level 1". Then when you press Insert+Right again it reads out the requirement. ("At least 8 characters"). Then you have to press Insert+Right to hear the status. When it gets to the French, it reads the text out in French. - Focusing
- When the password input takes focus, everything is read out as it should (after about a 2 second pause). But then the
<H1>
is read out again at the end. In French,lang
attributes are respected. - Typying
- When I start typing, the Narrator visual focus indicator goes to the next thing (the "Confirm Password"
<label>
) and Narrator reads out "End of line selected". In fact, it reads out "End ofline selected" after every character I type. Then it reads out the "At least 1 uppcase letter met" as it should. - Re-typing
- When I select all the typed-text and press delete, Narrator reads out all changed requirements with heir new status. And as I retype, all changes are read out as they should be.
- Chrome 102
-
- Top to Bottom
- It reads everything the way it should until it gets to the list items. When it reads each list item it starts with its position, but then reads out "Heading level 1". Then it reads out the requirement and its status. Ex: "1 of 10 level 1, heading level 1, At least 8 characters unmet". And it does not respect
lang
attributes. - Bit by Bit
- It skips over "In English" and goes right from "Main landmark" to "New Password". When it gets to the list, first it reads out "1 of 10 level 1 heading level 1". Then when you press Insert+Right again it reads out the requirement. ("At least 8 characters"). Then you have to press Insert+Right to hear the status. When it gets to the French, again it skips En Français and goes right to "Votre mod de passe doit contenir" it reads the text out in a strong English accent.
- Focusing
- When the password input takes focus, after about a 2 second pause only the very first thing in the
aria-describedby
(ie: the<H1>
) is read. In French, none of thearid-describedby
content is read. And thelang
attributes are not respected. No support. - Typying
- As I type, with each character, all I hear is "hidden". No changes are being read by Narrator. No support.
- Re-typing
- With no changes being read out as I type, this doesn't work either. No support.
- Internet Explorer 11
-
- Top to Bottom
- It reads everything the way it should. Since IE can't handle more than one set of requirements on one page, I can't test the French section.
- Bit by Bit
- It's goes well until it gets to the list. For each list item, it reads the requirement, the status, and the position. Ex: "At least 8 characters unmet. 1 of 10". But then when I press Insert+Right, it reads out the requirement and status, then it reads out the requirement again. Ex: "At least 8 characters unmet At least 8 characters". Then I press Insert+Right again, and it reads out the status. When I press Insert+Right again, it goes to the next requirement line.
- Focusing
- When the password input takes focus, after about a 2 second pause only the list of requirements is read, but it does read each requirement along with its status. The
<H1>
and "Your password must contain" are both ignored. Partial support. - Typying
- As I type, with each character, after I hear "hidden", the changed requirements are read out with their new status. Full support with odd behavior.
- Re-typing
- When I select the text and delete, all changed requirements, along with their new status, get read out twice. Then when I retype the password, as things change, those changes are read out as they should be. Full support with odd behavior.
iOS with VoiceOver
iOS v14
- Sarafi, Firefox, and Chrome
-
- Top to Bottom
- It reads top to bottom reading everything as it should, respecting all
lang
atributes. Full support - Bit by Bit
- It reads everything as it should. But, it does consider the requirement a separate thing than its status. So you have to swipe-right after you hear "At least 8 characters" to hear "Unmet". All
lang
atributes are respected. Full support. - Focusing
- When VoiceOver focuses the input, everything referenced by
aria-describedby
is read out as it should be. Alllang
atributes are respected. But when you enter the input to start typing, it's not. I think this is just how VoiceOver on iOS just works, but I find it odd. Mostly supported with odd behavior. - Typying
- As you type, changes are announced. But in French, the changes are read in a strong English accent. Mostly supported
- Re-typing
- If there's a way to do this without an external keyboard (which I don't have) then I haven't found it. N/A
Mac OS X with VoiceOver
Mac OS X 10.15.7 (Catalina)
- Safari 15.5
-
- Top to Bottom
- It reads properly until I get to the list and read "List 10 items", then it just stops reading. So if I hit Control+Option+A again, it reads out the last part of the first item, then jumps down to the first thing outside the list. Ex: "Unmet Submit button". Note that when you do get to the list, if you wait for a second or two VoiceOver reads out instructions about navigating the list. Okay, things have just gotten funny. In playing with this, now VoiceOver keeps reading the list when it gets to it. It doesn't stop. I have no idea what's going on. Mostly supported with odd behavior.
- Bit by Bit
- Most is fine, but each list item is treated as two parts: the requirement, and the status. When you Control+Option+right to the first requirement, it reads out the requirement and position (ex: "At least 8 characters 1 of 10") then you need to Control+Option+Right to hear "Unmet". Then when you get to the French part, it reads out all French text in a French accent. Full support
- Focusing
- The name, role, and value are all read. Then it reads all descriptions including each requirement and its status as it should. In French, everything is read with a strong English accent. Full support.
- Typying
- As I type, changed requirements are read out along with their new status. Full support.
- Re-typing
- When I select everything and delete it, it seems like it's trying to read all changes out, but only says one. ("At least 1 uppercase letter unmet.") Then as I start typing again, the requirements are read out as their status changes, but the status itself is only read out if it was not announced in the deletion process. When I retype "P" I hear "At least one uppercase letter met". But then when I type "a" I hear "At least one lowercase letter". When I type "s-s" I hear "No two characters the same consecutively". But if I type a "!" I hear "At least one special character met". Then if press Backspace as statii changes, their requirement along with their new status is read out. Mostly supported with odd behavior.
- Firefox 102
-
- Top to Bottom
- It reads properly until I get to the list and then it only reads the last thing (ie: a
<span>
in the list. Ex: "Your password must contain list 10 items unmet met unmet unmet" then for the French: Votre mot do passe doit contenir list 10 items "Ne contient pas 'Password', Un maximum de 20 caractĂšres", etc. Also, it reads the French part with a really strong English accent. Partial support with odd behavior. - Bit by Bit
- Everything is fine. When you get to the list elements, it reads each requirment with its status and position in the list. Ex: "At least 8 characters unmet 1 of 10." Same with the French. Note that it still reads all French in a strong English accent. Mostly supported.
- Focusing
- The name, role, and value are all read. Then it reads all descriptions including each requirement and its status as it should. In French, everything is read with a strong English accent. Mostly supported.
- Typying
- As I type, changed requirements are read out along with their new status. Full support.
- Re-typing
- When I select everything and delete it, it seems like it's trying to read all changes out, but only says one. ("At least 1 uppercase letter unmet.") Then as I start typing again, the requirements are read out as their status changes, but the status itself is only read out if it was not announced in the deletion process. When I retype "P" I hear "At least one uppercase letter met". But then when I type "a" I hear "At least one lowercase letter". When I type "s-s" I hear "No two characters the same consecutively". But if I type a "!" I hear "At least one special character met". Then if press Backspace as statii changes, their requirement along with their new status is read out. Mostly supported with odd behavior.
- Chrome 103
-
- Top to Bottom
- It reads properly all the way down. However, most French is read out in a strong English accent, with the exceptions of En Français, "Submit", and "Code", which it reads as French. It's as though anything added after page-load has its
lang
attribute ignored. Note that Chrome is the only one that reads out everything referenced byaria-describedby
in a Top-to-Bottom scan. The others only read that out when the input takes focus. Mostly supported with odd behavior. - Bit by Bit
- Everything is fine. When you get to the list elements, it reads each requirment with its status and position in the list. Ex: "At least 8 characters unmet 1 of 10." Same with the French. It still reads the French in a French accent as it's reading an item. When it reads something referenced by
aria-describedby
it reads those parts out in a strong English accent. Mostly supported. - Focusing
- The name, role, and value are all read. Then it reads all descriptions including each requirement and its status as it should. In French, everything is read with a strong English accent. Mostly supported.
- Typying
- As I type, changed requirements are read out along with their new status. Full support.
- Re-typing
- When I select everything and delete it, it seems like it's trying to read all changes out, but only says one. ("At least 1 uppercase letter unmet.") Then as I start typing again, the requirements are read out as their status changes, but the status itself is only read out if it was not announced in the deletion process. When I retype "P" I hear "At least one uppercase letter met". But then when I type "a" I hear "At least one lowercase letter". When I type "s-s" I hear "No two characters the same consecutively". But if I type a "!" I hear "At least one special character met". Then if press Backspace as statii changes, their requirement along with their new status is read out. Partial support with odd behavior.
Conclusion
This works as expected in most browser, OS, and screen reader combos, except Narrator, which seems to have issues with aria-describedby
, with one exception: If the Narrator setting "Hear advanced detail, like help text, on buttons and other controls" is turned on, then the aria-describedby
part is consistently read in Edge only. Also, Internet Explorer has reached end of life, so don't worry about it.
This is pretty robust in Windows, except with Narrator with any non-Edge browser. This works in iOS 14. And there are a couple of issues with VoiceOver in Catalina. As usual, Safari works best overall with VoiceOver.
Recommendations
- This is the best way I could think of doing this. It works in most stacks. Feel free to use!