HTML link hover color

Set the colour on a:hover. The rule works only if it comes after a:link and a:visited in the stylesheet, which is where most hover colours silently fail.

To set an HTML link hover color, write a rule for the :hover pseudo-class.

a { color: #2563eb; }
a:hover { color: #1e40af; }

The second rule applies while the pointer is over the link. That part is simple. The part that goes wrong is the order the rules appear in.

A link under the pointer, showing the hover colour next to an untouched link in the base colour.
A link under the pointer, showing the hover colour next to an untouched link in the base colour.

The order that makes it work

Five pseudo-classes describe a link's states, and they all have the same specificity. When two of them match at once, the one written last wins.

a:link    { color: #2563eb; }
a:visited { color: #7c3aed; }
a:focus-visible { outline: 2px solid #2563eb; }
a:hover   { color: #1e40af; }
a:active  { color: #172554; }

If a:visited is written below a:hover, then hovering a link the reader has already opened keeps the visited colour. The hover colour works on new links only.

That symptom, "it works on some links", is nearly always this. The usual memory aid is LVHA: link, visited, hover, active, with focus before hover.

State When it applies Typical use
:link An unvisited link Base colour
:visited Already opened A muted variant
:focus-visible Reached by keyboard A visible ring
:hover Pointer is over it Colour or background shift
:active Being pressed A brief darker state

Browsers restrict styling on :visited deliberately, because a page could otherwise read a person's history by measuring how links render.

You can change colour, background colour and border colour. You cannot change size, weight, display or anything that alters layout, and the computed style is reported as if the link were unvisited.

So a visited style is a colour shift and nothing more. Plan around that rather than trying to work past it.

More than colour

Colour alone is a weak hover signal, and readers with low vision or a low contrast monitor may not register it.

a:hover {
  color: #1e40af;
  text-decoration-thickness: 2px;
}

.nav a:hover {
  background: rgba(0,0,0,.06);
  border-radius: 4px;
}

An underline that thickens, or a faint background block, is visible to more people than a shift from one blue to another.

For links that already have no underline, a background change is usually the clearest. Links without underlines covers which links should be in that group.

A navigation item on hover, showing a soft background block behind the text rather than only a colour change.
A navigation item on hover, showing a soft background block behind the text rather than only a colour change.

Focus needs its own rule

Hover does not exist for keyboard users. If your only clickable signal is a hover style, tabbing through the page shows nothing.

a:focus-visible {
  outline: 2px solid #2563eb;
  outline-offset: 2px;
}

:focus-visible shows the ring when the link is reached by keyboard and not on a mouse click, which is usually what people wanted when they removed the default outline.

Never write outline: none without a replacement. It is the most damaging single line in this area.

Touch devices

On a phone a tap fires hover briefly, then navigates. Some browsers leave the hover state stuck on the last tapped element.

The consequence is that hover cannot carry meaning. If a card is only identifiable as clickable on hover, half your readers never learn that it is.

If you want hover effects restricted to devices that actually have a pointer:

@media (hover: hover) {
  a:hover { background: rgba(0,0,0,.06); }
}

That keeps the stuck state off touch screens while leaving the desktop behaviour intact.

Contrast, in both themes

A hover colour has two jobs: differ from the base colour, and stay readable on the background.

Going darker on a light background is safe. Going darker on a dark background is the usual mistake, and the link becomes harder to read exactly while the reader is pointing at it.

a { color: #2563eb; }
a:hover { color: #1e40af; }

@media (prefers-color-scheme: dark) {
  a { color: #93c5fd; }
  a:hover { color: #dbeafe; }
}

On dark, hover moves toward white rather than away from it. Dark mode CSS covers the wider set of pairs.

The same link on a dark background, with the hover state lighter than the base colour rather than darker.
The same link on a dark background, with the hover state lighter than the base colour rather than darker.

A short transition

a {
  color: #2563eb;
  transition: color .15s ease;
}

Put the transition on the base rule, not on :hover, so it applies on the way out as well as on the way in.

Keep it short. Anything past about 200 milliseconds reads as lag rather than polish on an element people move across quickly.

Keeping the styles attached to the file

A page that gets pasted or emailed may arrive without its stylesheet, and pseudo-classes cannot be written in a style attribute.

That means hover styling needs a <style> block inside the document itself. Inline CSS covers the trade offs between the two.

<style>
  a { color: #2563eb; text-decoration: none; }
  a:hover { color: #1e40af; text-decoration: underline; }
</style>

In a NOS document the pasted HTML renders as written, including a style block of this kind, so hover states behave the same for the reader as in your own browser.

If the rules live in an external file the page cannot reach, fold them in before sharing.

A default to start from

a { color: #2563eb; transition: color .15s ease; }
a:visited { color: #6d28d9; }
a:focus-visible { outline: 2px solid #2563eb; outline-offset: 2px; }
a:hover { color: #1e40af; text-decoration-thickness: 2px; }
a:active { color: #172554; }

Five rules, in the order that makes them all apply. Change the colours to your own and the behaviour stays correct.

Three habits keep it working as the page grows.

  1. Add new link rules below this block, never above it, so the state order survives.
  2. Never delete the focus rule to tidy up the appearance. Replace it if you dislike the default ring.
  3. Check the hover colour against the background, not only against the base colour.

If the links in question are styled as buttons, hover usually belongs on the background rather than the text. HTML button with a link shows that variant.

Questions people ask

Why is my hover color not applying?

Usually order. If a:visited appears after a:hover in the stylesheet, it overrides it for any link the reader has already opened, so the hover colour works on new links and not on old ones. Put the rules in the order link, visited, hover, active.

What is the right order for link pseudo-classes?

a:link, a:visited, a:focus, a:hover, a:active. All of them have the same specificity, so the last matching rule wins. Writing them out of order produces states that appear to work on some links and not others, which is hard to diagnose later.

Does hover work on a phone?

Not meaningfully. A tap fires hover for an instant before the navigation, so a hover only design gives touch readers no signal at all. Make sure the normal state already looks like a link and treat hover as a desktop refinement.

Can I animate the hover colour change?

Yes, with a transition on the colour property, kept short at around 120 to 200 milliseconds. Put the transition on the base rule rather than on the hover rule, so it applies both entering and leaving the hover state.

Keep reading