9 signs your frontend code has quality issues that affect your users
A lot of different people write frontend code. Frontend web developers write frontend code, obviously. But so do fullstack web developers. Some web designers write frontend code too. Even backend web developers write frontend code, in certain circumstances.
This might give you the wrong impression that writing frontends must be easy because “everyone does it”. If your manager is one of the people that devalue frontend work, you might be on a project that’s lacking frontend experts.
The experiences users have with such projects are usually… mixed.
Below I’ve compiled a list of 9 common signs that I see in projects with poor frontend code quality. I’ve focused only on issues that have a negative impact on your project’s users.
All the signs can appear in your project regardless of your technology choice. Static HTML files, classic server-side rendered multipage apps with Ruby on Rails/Django/Phoenix, real-time server-side pages updated over websockets with Phoenix LiveView, SPAs with React/Vue/Angular/Svelte… doesn’t matter, you can write bad frontend code in any technology 💩.
If you’re an inexperienced frontend developer, I hope this post provides you with a list of relevant topics to learn about. If you’re a backend developer or an engineering manager, I hope this post helps you develop an appreciation for what frontend developers do (so you don’t have to).
#1. Missing alt attributes
The most common of all HTML mistakes, missing alt text on images. It might seem benign on its own, but if your project has a lot if images with missing alt text, it’s almost certain to have other problems as well. Why? Missing alt texts mean that you didn’t test your project with a screen reader, you didn’t run an automated accessibility check of any kind, and you didn’t even run an HTML validator. All of those tools would have caught this common problem.
<!-- missing alt attribute, don't do this ❌ -->
<img
src="/assets/portrait.png"
width="300"
height="300"
>
Why does this matter?
Alt text is necessary for visually impaired users to know what is on the image. It is also useful for sighted users in case the image does not load (for example due to temporary network problems), as well as for SEO.
Learn more
- Read Joshua Hardwick’s “Alt Text for SEO”.
- Read Harvard’s accessibility guide about alt text.
- Watch videos by blind folks explaining how they use their devices, for example, this video comparing an accessible and an inaccessible online shop.
- Find out which screen reader software is available for your operating system and learn how to use it. This is one of the most important things you can do to really understand web accessibility.
- Download the WAVE browser extension and run automated checks on your website.
#2. Clickable divs
Another sure sign that frontend expertise is lacking somewhere is attaching a click handler to an HTML element that isn’t interactive by default, like a div, a span, or a list item.
<!-- a clickable div in React, don't do this ❌ -->
<div onClick={handleIncrement}>
+1
</div>
<!-- a clickable div in Phoenix LiveView, don't do this ❌ -->
<div phx-click="increment">
+1
</div>
Why does this matter?
Clickable divs can only be activated with a pointing device like a mouse or a touchscreen. However not everyone is able to use a pointing device. Some people navigate exclusively with a keyboard or a special switch. They won’t be able to use your website.
Learn more
- Read Ben Myers’s “How (Not) to Build a Button” to find out, why it’s a bad idea to create clickable divs, what’s the problem driving people to use a div instead a button, and how to solve that problem.
- Watch Rob Dodson’s “Just use button” for similar information as the article above but in a video form.
- Read WebAIM’s keyboard testing shortcut reference to learn how to test keyboard navigation on your websites.
#3. Mixing up buttons and links
The designs we’re told to implement as web developers often contain elements that look like buttons but behave like links. This can sometimes lead a developer to poorly reimplement a link’s functionality with a button’s click handler.
<!-- don't do this ❌ -->
<button onclick="document.location.pathname='/products'">
Discover our products
</button>
Or to nest a button inside a link.
<!-- don't do this either ❌ -->
<a href="/products">
<button>
Discover our products
</button>
</a>
Why does this matter?
A button is not a link: it is missing a lot of important link functions. You cannot middle-click it to open in a new tab. You cannot right-click it to see the contextual menu that allows you to open it in a new window, save it as a bookmark, or copy the link’s URL. Screen readers will not announce this element as a link, and they will not include it in the list of links available on the page. Browsing through all links and headings available on the page is a common way screen reader users skim web pages to find what they need.
A button nested inside a link for styling purposes is not only invalid HTML, but it doesn’t work for users navigating with a keyboard. Both the link and the button can be focused separately, and the button will not do anything when activated, giving the user the impression that your website is broken.
Learn more
- Read Eric Eggert’s “Buttons vs. Links” to find out what’s the difference between a button and a link, and why it matters that you use the correct element.
- Read Chris Coyier’s “Complete Guide to Links and Buttons” for a comprehensive list of everything you need to know about links and buttons.
#4. Hiding content the wrong way
There are many different HTML and CSS techniques for hiding content. Each technique has different side effects when it comes to interacting with the hidden content.
Example 1: custom styling of checkboxes and radio buttons. It can be achieved by hiding the input itself and styling its label with additional elements that emulate a checkbox or radio button.
Those form controls should be visually hidden, but physically present. Yet lack of awareness often leads developers to completely hide the input instead because display: none; was the first answer in that one Stack Overflow question that they read.
/* don't do this ❌ */
.checkbox {
display: none;
}
.checkbox-label {
/* some fancy styles that render a check in a box [✔] */
}
Example 2: hamburger menu content. A common requirement is for a hamburger menu to have an opening animation. You cannot animate from display: none to display: block without extra steps, so devs who don’t know better choose to only visually hide the menu content instead, to make implementing the animation easier.
/* don't do this ❌ */
.menu.menu--closed {
position: absolute;
left: -100%;
transition: left ease 300ms;
}
.menu.menu--opened {
left: 0;
}
Why does this matter?
A checkbox that has been completely hidden cannot be activated by using the keyboard, even if its label is visible. Users who navigate with a keyboard only won’t be able to accept your terms of service and create an account.
A hamburger menu content that has been only visually hidden will receive focus when navigating with a keyboard but remain invisible. The user will not see where the focus currently is and might activate menu items by accident, or just get frustrated and leave your website.
Learn more
- Read Sara Soueidan’s “Inclusively Hiding & Styling Checkboxes and Radio Buttons”.
- Read Kitty Giraudel’s “Hiding content responsibly”.
#5. Using the title attribute
This problem is often a symptom of a lack of a proper design process. Sometimes, you need a tooltip component, but it doesn’t exist in your project. Sometimes, you need to explain why an action is currently not available, but there’s no room in the existing UI to add that explanation. If you lack the design skills to do it the right way, you might reach out for the title attribute.
<!-- don't do this ❌ -->
<button
type="submit"
title="Report account"
>
<i class="fa-solid fa-flag" />
</button>
Why does this matter?
The title attribute can only be perceived by mouse users, and they have to really try to discover it. It takes a few seconds of hovering over an element before the title shows up. The title attribute cannot be perceived when using a touch screen, keyboard navigation, or a screen reader.
Learn more
#6. Removing button outlines
Did this ever happen to you? A product owner or a designer on your project tested the project in Chrome, and they saw an “ugly blue outline” on buttons that “doesn’t fit the company branding”. So they told you to remove it. And none of you knew better, so you did.
In the past, Chrome used to apply default focus styles to buttons on mouse click. No other browser did it, only Chrome. It was wrong, and they fixed it by now, but it did a lot of damage to web accessibility in the meantime, influencing many web developers to make this mistake.
/* don't do this ❌ */
button {
outline: 0;
}
Why does this matter?
To navigate your website with a keyboard successfully, the user must be able to see which element is currently focused.
Learn more
- Read Guilherme Simões’ “Never remove CSS outlines”.
- Read Andy Adams’s focus-visible guide.
- Read WebAIM’s keyboard testing shortcut reference to learn how to test keyboard navigation on your websites.
#7. Tiny clickable areas
Padding, margin, gap - same thing, right? Wrong. Choosing the right technique of adding whitespace around elements can impact their usability. Compare the clickable areas of the two sets of icon buttons. The buttons look identical if you don’t interact with them.
Why does this matter?
Tiny clickable areas will make it harder for your users to click or tap the links and buttons that they want to activate, leading to misclicks and frustration. Clickable area size also affects your search engine rankings, as websites with tiny clickable areas are deemed not mobile-friendly and do not rank as high.
Learn more
- Read Ahmad Shadeed’s “Designing better target sizes” for a comprehensive and interactive guide about clickable areas.
- Read Ahmad Shadeed’s “Enhancing The Clickable Area Size” for a slightly shorter explanation.
#8. HTML validation errors
If you hired a professional copywriter, would you be content with their work if it contained grammar mistakes? I don’t think so.
Yet, somehow it’s completely normal for “professional” web developers to produce HTML like this:
/* there are 3 HTML validation errors in this short snippet ❌ */
<a href="/home" id="home">
<button class="btn btn--primary">
<img src="/icons/home.svg" id="home">
</button>
</a>
Why does this matter?
Invalid HTML can often signal the existence of bugs - duplicate IDs, tags not closed, misspelled attributes, and so on. It is also less likely to work correctly with all the software that parses HTML that is not the browser: search engine crawlers, browser extensions, screen readers, braille displays, and many more. Invalid HTML is less future-prone - browsers promise to support web standards so old web pages still work, but you can’t count on this if your code does not adhere to the standards.
Learn more
- Read W3C’s “Why validate?”.
- Read my “5 bugs that HTML validation caught for me”.
- A fun way to test if you can recognize bad HTML is to read Manuel Matuzović’ HTML Hell.
#9. Poor Core Web Vitals
Web performance is a complex topic. External dependency choices, implementation mistakes, intentional trade-offs, imperfect deployment strategies, and many other details can all add up and cause performance problems. Google gave us a useful shorthand for web performance: Core Web Vitals. Is your website passing the assessment?
Why does this matter?
The Core Web Vitals assessment reported by pagespeed.web.dev is based on real visitor data. If you’re failing the assessment, it means your visitors are actually having a less-than-ideal experience, and it also hurts your page’s search engine rankings.
Learn more
- Check your website’s Core Web Vitals on pagespeed.web.dev.
- Watch Sematext’s “What are Core Web Vitals?” for a normal-paced overview.
- Watch Fireship’s “The ultimate guide to web performance” for a fast-paced overview.
- Read Philip Walton’s (web.dev) “Web Vitals” for a detailed explanation.
Note that in 2024, “First Input Delay” was replaced by “Interaction to Next Paint” as one of the 3 Core Web Vitals.