Published by Dmitry Pashkevich - March 24, 2020

Top 6 usability oversights that front-end developers make

Top list of front-end developer mistakes that make websites and web apps harder to use.

Engineering at Calendly

Being a successful front-end developer involves more than just being good at programming. You also have to be a strong advocate for usability.

Unlike a back-end API, the HTML, CSS and JavaScript that you write is something that the users directly interact with. It’s the face of the product and the company brand. It’s part of your job to consider user experience.

I’d like to share my list of top front-end mistakes that make websites and web apps harder to use. Of course, while it’s common to have issues with the original design, layout problems, or cross-browser support, this list is focused on functionality that is usually not called out in the requirements or illustrated in the designs, and therefore is easy to overlook during implementation.

1. Not associating labels with input elements

Checkboxes and radio buttons are difficult elements to click on (or tap) due to their relatively small size. Thankfully all browsers allow you to click on the label text instead to activate the input… but only if the label is properly associated with it in code.

Here’s a wrong way of doing it (clicking on the label text does not check the checkboxes):

Unfortunately this usability oversight is still very common and can be found even on highly trafficked websites of reputable companies. You are torturing your users by making them target these tiny squares that can be as small as 12×12 pixels, depending on the platform.

Many websites employ a custom checkbox and radio button design that’s typically a bit larger than the browser default, but it’s still much easier to click on the label text to toggle the control. And it costs virtually nothing to implement that.

There are two syntaxes for associating a label with an input. The first one uses the for attribute:

<input id="apples" type=checkbox> <label for="apples">Apples</label>

This syntax requires you to give the input a unique id attribute and reference it from the label, which can be inconvenient when creating long forms by hand or when generating inputs dynamically.

The second syntax does not require this. Nesting the input directly inside the label makes the association.

  <input type="checkbox"> Apples

This syntax is more popular because it’s easier to maintain. Here’s the same form, but with labels properly associated with their checkboxes (note that visually the form looks exactly the same):

Fun fact: properly associating labels with inputs also helps assistive technologies like screen readers interpret the page for visually impaired users.

2. Putting outline: none on interactive elements

I have an empathy exercise for you that I’m borrowing from Sarbbottam Bandyopadhyay. On your desktop or laptop computer, try to click the button below:

A simple task like clicking a button becomes extremely difficult and frustrating if you use a mouse or a trackpad but can’t see the mouse pointer. It’s just as frustrating if you use a keyboard but can’t see what elements you’re selecting.

It’s common to see a CSS rule that looks like this on many apps and websites:

:focus {
  outline: 0;

This is often done because the browsers’ default focus outline doesn’t match the visual style of the site, so designers decide to simply remove it. Instead, work with your designer to come up with an alternative style for indicating focus. It doesn’t have to be an outline border, of course. Popular alternatives are changing text color, background color or drop shadow.

It’s common to set the focus style to be the same as hover style. That way you can offer a universal experience that’s agnostic of input method. For example, consider this custom button:

The default focus outline that Chrome on OSX produces is blue:

Red button with blue focus outline

Clearly it clashes with the button’s red background. We can remove the outline, but provide an alternative way to indicate focus.

Here’s a version that sets a lighter background color on focus and also on hover:

3. Not closing modal dialogs on ESC

The Escape key has been around since the early days of personal computers, and its function has mostly stayed the same: “Get me out of here!” This paradigm persisted in graphical interfaces: every standard modal dialog or a dropdown menu in an operating system closes when you press Escape.

Let’s look at a standard confirmation dialog that is triggered by the confirm() JavaScript function. Click on the button below.

Clicking it will trigger a standard system confirmation dialog. Note that it can be closed by pressing Escape on the keyboard.

Now suppose we want to create our own custom confirmation dialog component from scratch. Here’s a prototype:

Now we have full control of how it looks and behaves, but so far we’ve introduced a usability issue: it doesn’t close when you press Escape. We also lost the automatic focus on the OK button. We need to implement that logic ourselves.

Here’s the same custom dialog but with an Escape handler:

More generally, any time you make a custom UI component (e.g. a checkbox), you should closely examine the accessibility features of its native counterpart and try to match it.

I won’t torture you with another empathy exercise, but trust that your keyboard users will not be happy if you forget to write that keyup handler for your custom component.

4. Not disabling forms upon submission

With the emergence of single-page application frameworks like Angular and React, data from user input is almost always submitted asynchronously via AJAX. This enables a faster, more dynamic desktop-like experience without requiring a full page refresh to transition between application states. But it also makes the front-end developer responsible for implementing the functionality that we used to get “for free” from the browser with traditional HTML forms – things like collecting the data from the form, encoding and sending it to the back-end, interpreting the response and providing the user with visual feedback along the way. That last part is often implemented poorly.

Let’s look at this prototype of a comment system:

Each time you click “Submit”, it simulates an asynchronous network request to the back-end that takes 2 seconds to complete, and then displays the new comment in the feed.

But what happens if we rapidly click the submit button several times, while it still says “Submitting…”? Go ahead and try it.

Did you expect your comment to post more than once? This comment feed example is quite innocent, but you can imagine how a multiple submission bug like this could create bigger problems in a more mission-critical context like processing payments.

With traditional forms, browsers have built-in protection against multiple submissions: they would cancel the existing request in flight every time you clicked submit. When building our own dynamic forms, we need to handle this case ourselves.

The solution is quite simple: disable the form while the data is submitting. I recommend setting a disabled attribute on the entire fieldset element that wraps all the inputs (add one if you don’t have it), instead of disabling just the submit button, because there may be multiple ways to trigger form submission.

Here is a fixed example (click “Edit on Codepen” to view the full source code):

5. Locking zoom on mobile

Not considering mobile devices in web design is hopefully a thing of the past. However, there are still a number of usability anti-patterns lingering from outdated “mobile optimization” advice. Here’s one of them:

<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=0">

Setting the above value on the viewport meta tag will prevent users from zooming in or out on mobile devices, which can cause accessibility issues. Modern versions of Safari on iOS will actually ignore the maximum-scale=1, user-scalable=0 part and still let you zoom in and out, but other browsers still lock the zoom.

Instead, use this snippet:

<meta name="viewport" content="width=device-width, initial-scale=1">

This will still give the browser a hint on how to optimally render the page on mobile, but won’t prevent the user from zooming in if the content is too small for them to read.

6. Inadequate error handling

How many times have you seen a web app pop up a generic error that looks something like this?

Generic error dialog that says "Oops, unknown error occurred"

Such messaging provokes many questions with no answers. Do I retry my last action? Should I retry several times? Maybe wait a few minutes? Did I do something wrong and need to correct my input or is this a bug inside the app, or a temporary service interruption? Is the whole app even still usable?

Ideally, all error states that can be reasonably expected would be designed up front by UX/Product people and included in the specification. Unfortunately, error handling is often completely ignored in application design or added as an afterthought.

Try to work with your team to fill the gap. As you write the error handling logic, remember to put yourself in the user’s shoes. Make sure to cover the error cases with automated tests. Log unknown errors so that you can analyze them later and further improve the code.


As a front-end developer, you cannot overlook usability. None of the considerations in this list have complex fixes, so an error in usability is just lazy programming.

Take the time to invest in the user experience of your brand and product by, at the very least, using this as a checklist for things that might’ve slipped your mind. And always remember that you have a lot of different kinds of users.

What are the things that you have seen that are not on this list?

Comments are closed.