Improving accessibility of forms
3 mins |
October 31, 2020Forms are an essential part of the web. Most of us would have built at least one form per app. For me, it has been one of the most difficult part in developing any app in web. Most of the applications we build would also have some kind of sign up/ login forms. Often they are not accessible and/or leaves a lot to be desired in terms of UX.
Let us try to understand this by creating a login form.
Bad
This is a not so good version of a login form. It is not accessible nor user friendly. Form won’t submit if you hit return key, it won’t suggest email in the email field. And in a real world app, it won’t even prompt to save password.
Go ahead and try it yourself! It is interactable!!! 🥳
Login
1function Form() {2 const [state, setState] = React.useState({})3 function handleChange(name, value) {4 setState(current => ({ ...current, [name]: value }))5 }6 function handleSubmit() {7 alert(JSON.stringify(state))8 }9 return (10 <div>11 <h3>Login</h3>12 <div>13 <span>Email</span>14 <input15 onChange={event => handleChange("username", event.target.value)}16 />17 </div>18 <div>19 <span>Password</span>20 <input21 type="password"22 placeholder="password"23 onChange={event => handleChange("password", event.target.value)}24 />25 </div>26 <div onClick={handleSubmit}>Login</div>27 </div>28 )29}
And here how it would look in action. This is not an exaggerated code. The above code sample is what I have seen in a real-world application. This is how NOT to build a form.
This form is broken (not accessible at all), let us make this form work. We need some basic functionality like suggest emails, accessible by keyboard and screen readers, ability to submit form by hiting return key etc.
Good
To improve accessibility and UX. We can achieve this by using semantic elements,
instead of using <div>
’s for everything.
Let’s use <form>
to wrap our forms, <label>
for labels of input (placeholders
are not enough), and <button>
for buttons. This might seem very obvious, but
there are plenty of websites which don’t follow this.
Go ahead and try it yourself!
1function Form() {2- const [state, setState] = React.useState({})3- function handleChange(name, value) {4- setState(current => ({ ...current, [name]: value }))5- }6 function handleSubmit(event) {7- alert(JSON.stringify(state))8+ event.preventDefault()9+ const { elements } = event.target10+ const { email, password } = elements11+ alert(JSON.stringify({ email: email.value, password: password.value }))12 }1314 return (15- <div>16+ <form onSubmit={handleSubmit}>17 <h3>Login</h3>18 <section>19- <span>Email</span>20+ <label htmlFor="email">Email</label>21 <input22- placeholder="E-Mail"23- onChange={event => handleChange("username", event.target.value)}24+ id="email"25+ name="email"26+ type="email"27+ required28 />29 </section>30 <section>31- <span>Password</span>32+ <label htmlFor="password">Password</label>33 <input34 type="password"35- placeholder="password"36- onChange={event => handleChange("password", event.target.value)}37+ id="password"38+ name="password"39+ required40 />41 </section>42- <div onClick={handleSubmit}>Login</div>43+ <button id="login" type="submit">44+ Login45+ </button>46- </div>47+ <form>48 )49}
Both these forms look very similar. But they are miles apart when it comes to accessibility. Now input has proper labels, screen readers can now read labels correctly. And we get tons of bonus features too 🕺.
When we click/tap on the label, the corresponding input is focussed.
We don’t have to maintain state value if we don’t need to. We can get all field values from
event.target.elements[NAME_OF_THE_FIELD]
We can submit the form by hitting the enter/return key.
Also we can get browsers to prompt save password. To get that working, we should
let the browser know that the form submit was successful. We can do that by
either navigating to a new page. Or by calling history.pushState
or
history.replaceState
and <form>
needs to be removed from the page.
Hold on, that’s not it. We can make it better. 🤯
Better
We can improve this by letting the browser know what kind of value we are
expecting from these fields. For that we can use autocomplete
attribute.
Go ahead and try it yourself!
1function Form() {2 function handleSubmit(event) {3 event.preventDefault()4 const { elements } = event.target5- const { email, password } = elements6+ const { email, "current-password": password } = elements7 alert(JSON.stringify({ email: email.value, password: password.value }))8 }9 return (10 <form onSubmit={handleSubmit}>11 <h3>Login</h3>12 <section>13 <label htmlFor="email">Email</label>14 <input15 id="email"16 name="email"17 type="email"18+ autoComplete="username"19 required20 />21 </section>22 <section>23 <label htmlFor="password">Password</label>24 <input25- id="password"26- name="password"27+ id="current-password"28+ name="current-password"29+ autoComplete="current-password"30 type="password"31 required32 />33 </section>34 <button id="login" type="submit">35 Login36 </button>37 </form>38 )39}
Autocomplete makes the UX a lot better. Few other values which might be helpful for sign-up. for eg.
1// for new passwords2 <input3 // ...other fields4 autoComplete="new-password"5 />67 // for OTPs8 <input9 // ...other fields10 autoComplete="one-time-code"11 />
new-password
is interesting and required because it wouldn’t prefill existing
password in password reset scenario or if someone else is trying to sign-up from
the same machine. And the cool thing about new-password
is modern browsers
will suggest secure passwords.
autocomplete
is a nice feature which we forget to use most of the time. This
helps in assisting the user in filling out the form reliably (avoid typos) and
faster. There are around ~50 possible values for auto-complete. Check them out
at
MDN.
There are a lot of things we can improve. Listing down a few
- use
aria-describedby
to describe any specific constraint on the field. eg. Password has to 8 chars. - use
autofocus
in the input, if that is the first input of the form that user would be filling etc. eg. username in our login form. - other UX improvements like size, the contrast of the text. Would recommend checking Web Vitals. Only add login if your application really needs it.
TL;DR
Use semantic HTML; form
for forms, button
for buttons etc. Make it
accessible by having proper labels (aria properties too). UX can be improved by
using the autocomplete
attribute of the input.
This might seem very small, but we often miss this. Let us check the apps we have developed are accessible or not. We should try to make our apps more accessible. By far this is not enough but this is a good start.
I have focussed on Login/Sign-Up forms, but this can be translated to any forms you make. Drop-offs in Sign Up could be drastically improved with these minor changes. Let me know if this helped your application.
Got a Question? Bala might have the answer. Get them answered on #AskBala
Subscribe Now! Letters from Bala
Subscribe to my newsletter to receive letters about some interesting patterns and views in programming, frontend, Javascript, React, testing and many more. Be the first one to know when I publish a blog.
No spam, just some good stuff! Unsubscribe at any time