When I review a production UI, one of the first things I test is how it behaves at the exact moment of interaction: the press, the click, the tap. That tiny slice of time is when users decide whether the interface feels crisp or sluggish. The CSS :active pseudo-class is my go-to tool for styling that moment. It’s the browser’s way of saying “this element is currently being activated,” and if you treat it well, your UI feels more tactile, more trustworthy, and simply faster. You should care because the pressed state is part of the feedback loop that keeps people confident they’re doing the right thing. In the next sections, I’ll show you what :active really means, how it differs from other link and hover states, where it shines, and where it should be avoided. I’ll also walk you through real examples for links, buttons, custom controls, and touch devices, plus common mistakes that make :active unreliable. You’ll leave with practical patterns, a few guardrails, and a mindset for designing interaction states that feel grounded and modern.
The Mental Model: What “Active” Really Means
I treat :active as the “pressed” moment, not the “selected” or “focused” state. The browser applies :active when the user presses down on an element and keeps it active until the press ends. On a mouse, that typically means between mousedown and mouseup. On touch, it’s between finger down and finger up. This makes :active transient by design. If you need a persistent state (like a tab that stays highlighted), :active is the wrong tool. Use a class toggle, aria attributes, or data-state instead.
A simple analogy I use: hover is a hand hovering over a door handle, focus is a hand resting on the handle, and active is the handle being pushed. That push is momentary, but it’s crucial feedback. If you skip it, your UI feels like a door that doesn’t move until after you’ve already opened it.
You should also know that :active isn’t limited to links. It can apply to any element that can be activated: buttons, list items, custom controls, and even paragraphs (though that’s more of a demo than a pattern). The key is user activation, not element type.
:active vs :hover vs :focus vs Visited/Link States
People often lump these pseudo-classes together, but they represent different phases of interaction. Here’s how I explain it when coaching teams:
- :link and :visited describe the history of a link. They’re about navigation memory.
- :hover describes proximity. It’s about intent, not action.
- :focus describes keyboard or programmatic attention. It’s about accessibility and navigation.
- :active describes physical action. It’s about immediate feedback on press.
Because :active is so brief, it can easily be overridden by :hover or :focus if your CSS order is wrong. For link styling, the classic order is LVHA: link, visited, hover, active. If you switch the order, your :active styles might never appear. For non-links, the order still matters: the last matching selector wins.
In practice, I usually set :active near :hover but with a slightly stronger feedback. For example, a button might get a deeper shadow on hover and a pressed, inset shadow on active. That tells the user, “yes, you’re pushing the button right now.”
Basic Syntax and the Smallest Useful Example
The syntax is straightforward. It’s a pseudo-class that targets active elements:
a:active { / CSS property / }
If you want a fast, runnable example that shows real behavior on links and text, this is the smallest form I recommend testing. It uses simple background changes so the active state is obvious.
/ Clear defaults so the active state is obvious /
body {
font-family: "IBM Plex Sans", system-ui, sans-serif;
padding: 2rem;
line-height: 1.5;
}
a {
display: inline-block;
padding: 0.35rem 0.6rem;
border-radius: 0.4rem;
text-decoration: none;
color: #0a3d62;
background: #dff1ff;
}
a:active {
background: #2ecc71; / active link feedback /
color: #0a1f12;
}
p {
display: inline-block;
padding: 0.35rem 0.6rem;
border-radius: 0.4rem;
background: #f5f6fa;
}
p:active {
background: #3498db; / active paragraph feedback /
color: white;
}
Active link
Active text
Click or tap me
I use this kind of minimal demo when I need to confirm device behavior. It’s also a good sanity check when I’m auditing a design system’s interaction states.
Real-World Patterns for Buttons and Controls
The :active selector becomes truly valuable when you encode physical metaphors into your UI. In my experience, the most reliable pattern is to combine a subtle visual “press” effect with a slight scale change. You want to avoid dramatic motion because :active is short-lived and can feel jittery.
Here’s a button pattern I use in modern design systems. Notice the pressed state uses an inset shadow and a tiny translate to simulate a button being pushed into the surface.
:root {
–accent: #ff6b35;
–accent-dark: #e1552a;
–ink: #1f2a44;
}
body {
font-family: "Space Grotesk", system-ui, sans-serif;
background: radial-gradient(circle at 20% 20%, #fff3e9, #ffffff);
padding: 2.5rem;
}
.cta {
display: inline-flex;
align-items: center;
gap: 0.6rem;
padding: 0.8rem 1.2rem;
font-size: 1rem;
border: 0;
border-radius: 0.7rem;
color: white;
background: var(–accent);
box-shadow: 0 8px 0 var(–accent-dark);
cursor: pointer;
transition: transform 120ms ease, box-shadow 120ms ease;
}
.cta:active {
transform: translateY(6px); / pressed down /
box-shadow: 0 2px 0 var(–accent-dark);
}
I recommend using :active on buttons even if you also have hover and focus states. The pressed moment is the confirmation pulse that the action is happening.
For custom controls like list items or cards, I keep :active simple and low-contrast so it doesn’t fight hover styles. A light background tint or a small shadow change is enough.
Links: Make Active Obvious Without Breaking Accessibility
Links are where :active historically started, and they still matter. But I’m careful with link active styles for two reasons: contrast and motion. A link that flashes to a low-contrast color can fail accessibility requirements. A link that animates too aggressively can feel like a flicker.
Here’s a pattern I like for content-heavy pages. I add a small background highlight on active, but keep the text color the same. This ensures readability and a smooth interaction.
body {
font-family: "Source Serif 4", serif;
padding: 2rem;
}
a {
color: #1a4d8f;
text-decoration-thickness: 2px;
text-underline-offset: 3px;
padding: 0.1rem 0.2rem;
border-radius: 0.25rem;
}
a:hover {
background: #eef5ff;
}
a:active {
background: #cfe2ff; / brief highlight /
}
You should check the documentation
before deploying.
I also recommend avoiding drastic underline changes on :active. It’s too fast to notice, and it can cause layout shifts if you change text-decoration thickness or line height.
Touch Devices and the 300ms Ghost
Touch has special behavior that can make :active feel unreliable. Some mobile browsers delay click events to detect double taps (historically 300ms), and some apply an internal highlight color that can clash with your :active style. These behaviors have improved over time, but they still matter.
Here’s what I do in 2026 to keep :active responsive on touch:
- I use
touch-action: manipulation;on interactive elements where safe. It reduces delay on taps without disabling scrolling. - I avoid heavy transitions on :active that could make the press feel laggy.
- I reset the browser’s tap highlight when I need full control, using
-webkit-tap-highlight-color: transparent;for WebKit-based browsers.
Example:
.tile {
display: inline-block;
padding: 1rem 1.2rem;
border-radius: 0.8rem;
background: #f2f4f7;
color: #2b2d42;
text-decoration: none;
touch-action: manipulation; / faster taps /
-webkit-tap-highlight-color: transparent; / remove default highlight /
transition: transform 80ms ease, background 80ms ease;
}
.tile:active {
background: #dce5f2;
transform: scale(0.98);
}
I only remove the tap highlight if my own :active state is clearly visible. Otherwise, I let the browser highlight remain because it’s important feedback on touch.
Custom Components: When :active Isn’t Enough
Modern component libraries often wrap elements in multiple layers. If you attach :active to the wrong element, it won’t appear because the actual active target is different from your styled wrapper. I see this most often with custom buttons, card components, and interactive list items built from divs.
Two practical rules I follow:
1) Apply :active on the element that receives the pointer events (usually the button or anchor). If your wrapper is the event target but your visuals are on a child, you might need to style the child with a parent selector like .card:active .cardsurface { ... }.
2) Avoid pointer-events: none on child elements unless you really need it. It can break your active styles because the browser can’t attribute activation correctly.
Here’s a pattern that handles a card with an inner surface. The :active is on the card, and it styles the surface child.
.card {
display: inline-block;
text-decoration: none;
color: inherit;
}
.cardsurface {
padding: 1.2rem 1.4rem;
border-radius: 1rem;
background: #ffffff;
box-shadow: 0 10px 20px rgba(18, 38, 63, 0.08);
transition: transform 90ms ease, box-shadow 90ms ease;
}
.card:active .cardsurface {
transform: translateY(2px);
box-shadow: 0 4px 12px rgba(18, 38, 63, 0.08);
}
Quarterly report
Open the financial summary


