10 UX Mistakes I Keep Seeing
Buttons that want to work as links, boxes promising a tap they cannot answer, focus rings switched off, and 7 more things still wrong on most sites I open.
None of these count as new. Which rather makes the point. Every one has sat documented for a decade, and every one turned up on a site I opened this week.
1. Buttons As Links
Ouch, does this one ever grind my gears. First, let me get this straight:
A button does not equal a link.
Somewhere down the line we started confusing the 2. It makes a kind of sense, since you press both and they can look identical.
But:
- A link goes somewhere. It has a destination. Middle-click it, copy it, open it in a new tab, bookmark it, text it to someone.
- A button does something. Submits. Opens. Toggles. Deletes. It holds no address, because it goes nowhere.
Get it wrong and you took away everything above. <button onclick="location.href='/pricing'"> gives you a link you cannot open in a new tab, cannot copy, and that never shows up in a screen reader’s list of links.
One question settles it. If I did this, would the URL change? Then call it a link.
2. Boxing Plain Text
A word in a small bordered rectangle reads as a button to anyone looking at your page. Put your metadata in that shape, a status or a tag or a date, and someone will tap it, and nothing will happen.
I did this twice on my own site. A “Full-Time” badge on my resume, filled in the accent colour, which matches exactly the shape of every button on the page. It looked designed. It made a promise the word could not keep.
Metadata gets distinguished the way type distinguishes anything: case, tracking, colour, space. No box.
/* The site has exactly 1 boxed shape, and only things you can press wear it. */
.action {
display: inline-flex;
align-items: center;
min-block-size: 2.75rem;
padding: 0.75rem 1.5rem;
border: 1px solid var(--color-line-strong);
text-transform: uppercase;
letter-spacing: 0.08em;
}
/* A status reads as a legend entry: a short rule, then the term. */
.capacity {
display: inline-flex;
align-items: center;
gap: 0.6em;
color: var(--color-ink-muted);
text-transform: uppercase;
}
.capacity::before {
content: "";
inline-size: 1.75em;
block-size: 2px;
background-color: currentColor;
}
3. Killing Focus Rings
*:focus { outline: none; }
Every time I find this, somebody decided the ring looked ugly on a mouse click.
Which explains why :focus-visible exists. Ring for keyboard users, no ring for pointer users. Exactly the behaviour you wanted.
If the default looks ugly to you, draw a better one. Draw none and you lock people out.
4. Text Without Scrims
You laid your heading over a hero image and checked it and it looked great, because the part of the photograph behind your text happened to run dark.
Then somebody opened it on a phone, the image cropped differently, and your heading landed on a bright sky.
A photograph gives you no background colour. Text over one needs a gradient underneath, and you have to check the contrast against the brightest pixel behind the text, not against your page’s background. Any checker sampling your CSS will tell you everything passes.
5. Placeholders As Labels
The placeholder vanishes the second someone types. Now they sit 3 fields down, they cannot remember whether the second one wanted the postcode or the house number, and nothing short of deleting what they wrote will tell them.
Placeholders also, in most browsers’ defaults, render grey on white and fall under the contrast minimum.
Use a <label>. Put it above the field. Use the placeholder for a format hint if you like. Never for the name of the thing.
6. Tiny Targets
WCAG 2.2 wants anything tappable to measure at least 24 by 24 CSS pixels, unless it sits as a link inside a sentence. Every icon-only button in every toolbar fails this, always for the same reason: 16px icon, 4px padding.
Making the icon bigger solves nothing. Give the control a min-block-size and let the icon sit in the middle:
.toolbar-button {
display: inline-flex;
align-items: center;
justify-content: center;
min-inline-size: 1.5rem;
min-block-size: 1.5rem;
}
Nothing in your layout moves.
Watch for this when you make text smaller, too. Move 3 links from body size into a caption and they drop from 19px tall to 15, and nothing in your build will say a word.
7. Copy Naming Nothing
”Read the work.” Read what? You do not read a repository, and “the work” names no place.
”Learn more.” About which of the 4 things on this screen?
Name the destination or name the effect. “See the projects” names a page. “Download the PDF” names a file. If you cannot write the label without a vague noun, you probably do not yet know what the button does.
And a pair of links either runs parallel or covers 2 different things. “Visit” and “Source” gives you a verb and a noun. “Live site” and “Source” gives you 2 places.
8. Hover-Only Controls
Roughly half your visitors hold no pointer at all. If a caption, a menu or a set of controls only shows up on :hover, it does not exist on a phone.
Related sin: a :hover state on something you cannot interact with. A hover response makes a promise. Nothing happens on the press? You lied.
9. Ignoring Reduced Motion
Not everyone who turns this on gets headaches from parallax. Some people simply find a moving page hard to read. It costs 1 media query:
@media (prefers-reduced-motion: no-preference) {
.reveal {
animation: reveal linear both;
animation-timeline: view();
animation-range: entry 0% cover 25%;
}
}
Note where the guard sits. Around the animation, so a reader who asked for stillness never sees the declaration. This goes wrong when you start an element at opacity: 0 unguarded and rely on the animation to bring it back, which hands anyone the animation misses a blank page. Never hide content and hope.
10. Broken Skip Links
You added <a href="#main" class="skip-link">Skip to content</a> and it does nothing.
Almost always the same cause: <main> carries no tabindex="-1". Without it the browser scrolls to the content but leaves focus behind, so the next Tab starts over from the top of the navigation.
2 lines:
<a href="#main" class="skip-link">Skip to content</a>
<main id="main" tabindex="-1">
Then use it. Load your own site, press Tab, press Enter, press Tab again. 4 seconds. I would guess 3 quarters of the sites shipping a skip link have never done it.
Read back through the 10 and most of them come down to the same thing. Something got styled to look like a thing it does not do. A link dressed as a button. Metadata dressed as a control. A placeholder dressed as a label. Decoration dressed as a target.