ScreenshotNeo

BlogHow-to

CSS Hover Effects: Examples and How to Create Them

Learn how to create CSS hover effects for buttons, links, and cards, with keyboard and touch alternatives and reduced-motion support.

By the ScreenshotNeo team4 October 20268 min read

To create a CSS hover effect, write a normal rule for an element and a second rule with :hover. For example, .button:hover { background-color: #173b98; } changes a button’s background while a pointing device hovers over it. Put a transition on the normal rule if you want the change to happen gradually.

Hover is an enhancement, not a reliable way to reveal essential information or make a control usable. Provide visible keyboard focus, keep important content available without hover, and respect reduced-motion preferences.

1. How CSS :hover works

:hover is a user-action pseudo-class. It selects an element while a pointing device designates it, such as when a mouse cursor is over it. It does not click the element or perform its action; a button still needs its normal click behavior.

The first rule below defines the resting appearance. The second applies while the pointer hovers over the button. The focus rule supplies separate feedback for keyboard navigation.

<button class="button">Save changes</button>

<style>
.button {
  background: #2457d6;
  color: white;
  border: 0;
  border-radius: 6px;
  padding: 0.7rem 1rem;
  transition: background-color 160ms ease;
}

.button:hover {
  background: #173b98;
}

.button:focus-visible {
  outline: 3px solid #111;
  outline-offset: 3px;
}
</style>

The transition belongs on the base style, so it applies both when the pointer enters and when it leaves. Choose only the properties that need to change. MDN documents :hover as a pointer state and notes that touchscreen behavior can vary by browser; see the MDN pseudo-classes reference.

2. Common CSS hover effect examples

Change a button color

This is a straightforward way to show that a button is interactive. Keep the text legible in both states and retain a visible focus style.

.button {
  background-color: #2457d6;
  color: #fff;
  transition: background-color 160ms ease;
}

.button:hover {
  background-color: #173b98;
}

Links should still look like links before hover. This example keeps a thin underline at rest and thickens it on hover and keyboard focus.

a {
  color: #174ea6;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
}

a:hover,
a:focus-visible {
  text-decoration-thickness: 3px;
}

Add a border or decorative underline

A pseudo-element can animate an underline without changing the link’s layout. The link remains recognizable at rest because it is underlined in the default style.

.nav-link {
  position: relative;
  color: #174ea6;
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.nav-link::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -0.25em;
  height: 2px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
}

@media (prefers-reduced-motion: no-preference) {
  .nav-link::after {
    transition: transform 160ms ease;
  }
}

.nav-link:hover::after,
.nav-link:focus-visible::after {
  transform: scaleX(1);
}

Add a modest shadow or lift to a card

Use a small transform and shadow to signal interactivity. Transforms do not take up new layout space, so nearby content is less likely to jump than when dimensions or margins change.

.card {
  border: 1px solid #d6dbe5;
  border-radius: 10px;
  padding: 1rem;
  background: white;
  transform: none;
}

@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .card {
    transition: transform 160ms ease, box-shadow 160ms ease;
  }

  .card:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 24px rgb(0 0 0 / 18%);
  }
}

.card:focus-within {
  outline: 3px solid #2457d6;
  outline-offset: 3px;
}

:focus-within helps when a card contains a link or other focusable control. If the card itself is intended to be interactive, use an appropriate link or button instead of adding click-like behavior to a generic container.

Show an image overlay

An overlay can add visual emphasis, but do not put the only copy, label, or action inside an overlay that appears only on hover. This version keeps the caption visible by default and changes its background on hover or keyboard focus.

<a class="feature" href="/articles/example">
  <img src="feature.jpg" alt="A view of the coastline">
  <span class="feature__caption">Read the article</span>
</a>

<style>
.feature {
  position: relative;
  display: block;
  color: white;
}

.feature img {
  display: block;
  width: 100%;
  height: auto;
}

.feature__caption {
  display: block;
  padding: 0.75rem;
  background: #172033;
}

.feature:hover .feature__caption,
.feature:focus-visible .feature__caption {
  background: #2457d6;
}
</style>

For a disclosure that contains extra information, use a control and interaction model that works with keyboard and touch, such as a button that toggles a panel. A hover selector alone is not a disclosure mechanism.

When link-state rules have equal specificity, their order affects which style wins. Use the LVHA order:

  1. :link — an unvisited link
  2. :visited — a visited link
  3. :hover — a link under the pointer
  4. :active — a link being activated
a:link { color: #174ea6; }
a:visited { color: #6b3fa0; }
a:hover { color: #0b347a; }
a:active { color: #8b1e1e; }

Specificity still matters: a more specific selector may override a later rule. If a state appears not to work, inspect the matched rules and computed style in your browser’s developer tools.

4. Make hover usable with keyboard and touch

Keyboard users need a clear focus indicator. Use :focus-visible for a keyboard-focused appearance, and make sure it is not removed by a broad rule such as outline: none without an equivalent replacement.

.button:hover,
.button:focus-visible {
  background-color: #173b98;
}

.button:focus-visible {
  outline: 3px solid #111;
  outline-offset: 3px;
}

Touchscreens may not have a convenient hover state. A tap can produce no hover match, a brief match, or a lingering one depending on the browser and device. Do not hide a required action or essential information behind hover. Make controls operable by tapping and keep critical labels visible.

You can scope a decorative effect to an input device that can conveniently hover:

@media (hover: hover) {
  .card:hover {
    box-shadow: 0 8px 24px rgb(0 0 0 / 18%);
  }
}

The hover media feature describes the primary input mechanism’s ability to hover. It is useful for optional decoration, not for deciding whether core content or functionality exists. See MDN’s hover media feature reference.

5. Respect reduced-motion preferences

For motion effects, check the user’s motion preference. One approach is to enable the transition and transform only when the user has not requested reduced motion.

.card {
  transform: none;
}

@media (prefers-reduced-motion: no-preference) {
  .card {
    transition: transform 160ms ease;
  }

  .card:hover {
    transform: translateY(-2px);
  }
}

This leaves the card in its resting position when reduced motion is requested. Apply the same care to animated scaling, sliding, and other movement. See web.dev’s accessibility guidance.

6. Choose properties and avoid layout jumps

Effect Typical properties Practical note
Color feedback color, background-color Check contrast in both states.
Underline or border text-decoration, border-color, pseudo-elements Keep links recognizable at rest; avoid changing border width if it shifts layout.
Depth box-shadow Use a restrained shadow and check it against the surrounding surface.
Small movement transform Keep motion subtle and respect reduced-motion settings.
Content disclosure Not hover alone Use a persistent or operable interaction model for keyboard and touch.

Declare transitions on the base rule so entering and leaving the state behave consistently. Avoid animating properties that trigger unnecessary layout changes, such as repeatedly changing an element’s width or margins for a small visual cue.

7. Troubleshooting CSS hover effects

Symptom Likely cause Fix
The hover style never appears The selector does not match, a rule is overridden, or another element covers the target. Inspect the element and computed styles; check selector spelling, specificity, stacking, and pointer events.
The style appears on tap and sticks Touch browsers vary in how they emulate hover. Keep essential content visible and scope optional effects with @media (hover: hover).
A link’s hover color is ignored A later or more specific link-state rule wins. Use LVHA order for equal-specificity states and inspect specificity.
The effect animates only in one direction The transition is declared only inside the hover rule. Put the transition on the normal state.
Nearby content shifts The hover state changes layout-affecting dimensions, borders, or margins. Reserve space in the base style or use a transform, color, shadow, or pseudo-element.
Keyboard users cannot see the state Only :hover is styled, or focus outlines were removed. Add a visible :focus-visible treatment and check it by tabbing through the page.
The motion feels excessive The effect ignores motion preferences or uses a large movement. Use a restrained effect and condition it on (prefers-reduced-motion: no-preference).

8. A practical review checklist

  • The resting appearance makes interactive links and controls recognizable.
  • Hover provides useful feedback without being required to discover content or operate a control.
  • Keyboard navigation has a visible focus indicator.
  • Touch users can access the same essential information and actions.
  • Motion effects account for prefers-reduced-motion.
  • Color contrast and text readability remain adequate in every state.
  • The effect does not cause nearby content to jump.

9. Or skip the browser setup

If you are building a page and need a screenshot to review its visual states, you can capture it with ScreenshotNeo, a website screenshot API and MCP server from Yorker Media. Its API accepts one GET request and returns an image or PDF. See the ScreenshotNeo API documentation for the available parameters.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);

Replace the example URL with your page and supply your API key. ScreenshotNeo accepts cookie or consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Create a free ScreenshotNeo account.

10. Frequently asked questions

Does :hover trigger a button click?

No. It selects the element for styling while the pointer is over it; the button’s normal activation behavior is separate.

Can I use hover effects on mobile?

You can, but touch devices do not have a consistent, convenient hover interaction. Keep essential information and actions available without hover.

Why does my hover effect work with a mouse but not a keyboard?

:hover responds to a pointing device. Add a corresponding :focus-visible style for keyboard navigation.

Should every hover effect have a transition?

No. A direct state change is valid. Add a transition only when it improves feedback, and account for motion preferences if it moves or animates content.

What does @media (hover: hover) do?

It applies styles when the primary input mechanism can conveniently hover. Use it for optional hover decoration, not to gate essential content.

References