Navigation menus and link lists often react only when you click directly on the text, which makes them awkward to use.
The short answer: the cause is that the <a> keeps its initial display: inline, so it only occupies the size of its own text. The fix goes on the <a>, not the <li>: give it display: block and padding.
This article covers why the click area shrinks, the basic fix, how to make the whole list item clickable, and how large the target actually needs to be.
Sponsored
Why is the click area so small?
The short answer: the initial value of display on an <a> is inline, and an inline element only occupies the size of its text. However large the parent <li> is, the surrounding space does not respond to clicks.
<ul class="nav">
<li><a href="/about/">About</a></li>
<li><a href="/contact/">Contact</a></li>
</ul>
With this markup only the word “About” is a link. Whitespace to its left and right does nothing.
Padding and margin on the li will not help
The short answer: adding margin or padding to the <li> does not widen the clickable area by a single pixel.
Margin is space outside the element that belongs to nothing. Padding is space inside the <li>, and the <li> is not a link, so clicking there does nothing at all.
/* This does not fix the click area */
li {
margin: 10px;
padding: 10px;
}
What needs to grow is the link itself. Get this backwards and you add visual spacing while the element stays just as hard to hit.
The basic fix: display block and padding on the a
The short answer: make the <a> a block and let it own the spacing as its own padding.
li a {
display: block;
padding: 12px 16px;
}
display: block stretches the <a> across the width of its parent, and the padding adds height. Because that padding belongs to the <a>, all of it becomes clickable.
Which display value should you use?
The short answer: block for stacked menus, inline-block when the width should follow the text, flex when an icon sits beside the label.
| Value | Link width | Good for |
|---|---|---|
display: block |
Full width of the parent | Stacked menus and sidebars |
display: inline-block |
Follows the text | Horizontal menus with uneven widths |
display: flex |
Full width of the parent | Icon and label side by side |
In all three cases the padding becomes the click area. Only inline ignores vertical padding for layout purposes, which is exactly why it fails.
Sponsored
For horizontal menus, put flex on the ul
The short answer: decide the arrangement with display: flex on the <ul>, and the click area with padding on the <a>.
.nav {
display: flex;
list-style: none;
margin: 0;
padding: 0;
}
.nav a {
display: block;
padding: 12px 16px;
}
Floating the list items with float: left still works, but you then have to clear the float. Flex needs one line on the parent.
Keeping “the <ul> arranges, the <a> sizes the target” as a rule means changing the layout later will not quietly break your click areas.
How do you make the whole li clickable?
The short answer: put position: relative on the <li> and stretch a pseudo-element of the <a> across it with position: absolute and inset: 0.
Use this when the item holds a thumbnail and a description as well as the link text, and the entire card should be pressable.
li {
position: relative;
}
li a::after {
content: "";
position: absolute;
inset: 0;
}
inset: 0 is shorthand for top: 0; right: 0; bottom: 0; left: 0. An empty pseudo-element covers the whole list item, and because it belongs to the <a>, everything it covers becomes the link.
Bootstrap ships this as a helper class called .stretched-link, so with that framework you only add one class.
It breaks if the li holds two links
The short answer: the stretched-link pattern only works when a list item contains exactly one link.
The pseudo-element covers the entire item, so any second link or button inside it can no longer be clicked, and selecting text stops working too.
When an item needs several links, go back to the basic approach of display: block plus padding on each <a>.
Sponsored
How big does a click target need to be?
The short answer: WCAG 2.2 success criterion 2.5.8, Target Size (Minimum), asks for at least 24 by 24 CSS pixels at Level AA. If you are designing mainly for touch, Apple’s Human Interface Guidelines suggest 44 by 44 points.
| Guideline | Minimum size | Status |
|---|---|---|
| WCAG 2.2 SC 2.5.8 | 24 × 24 CSS pixels | Level AA |
| Apple Human Interface Guidelines | 44 × 44 pt | Recommendation for iOS |
SC 2.5.8 has exceptions, including targets with enough spacing around them and links inline within a sentence. A row of same-sized navigation links is not covered by those exceptions, so treat 24 by 24 as the floor.
A link with 16px text and padding: 12px 16px is 16 + 12 + 12 = 40px tall, which already clears it.
Style focus, not just hover
The short answer: :hover alone tells keyboard users nothing about where they are. Add :focus-visible.
li a {
display: block;
padding: 12px 16px;
}
li a:hover {
background-color: #f2f2f2;
}
li a:focus-visible {
outline: 2px solid #1a73e8;
outline-offset: -2px;
}
:focus-visible draws the outline only when the element was reached by keyboard. It does not fire on mouse clicks, so there is no reason to remove outlines with outline: none for aesthetic reasons.
If a menu also needs to open and close, how to hide elements in CSS with display, visibility and opacity covers which property to pick. For a full menu implementation, 5 simple ways to implement a dropdown menu with jQuery uses these same patterns.