Screenshots

8 min read

How to Screenshot a Hover State, Dropdown, or Animation

The menu closes the moment you reach for the shortcut. Use DevTools force-state, a timed debugger pause, or capture the CSS itself so the state comes with it.

Two panels: a navigation bar with an open dropdown under a cursor, and the same bar with the cursor moved to a keyboard shortcut and the dropdown gone.
The problem in one image. Reaching for the screenshot shortcut is what closes the thing you wanted to screenshot.

Hover states, dropdowns, tooltips and mid-animation frames share a property that makes them uniquely annoying to capture: the act of taking the screenshot is what destroys them. You move the mouse to reach the shortcut, and the thing you wanted is gone. There are three good answers, and the third one is to stop taking a picture altogether.

Why this is hard

Transient UI depends on continuous input. A :hover rule applies only while the pointer is over the element. A dropdown listens for a click outside itself, or for focus to move, and closes. A tooltip has a dismiss timer. All three are correct behaviours, and all three are hostile to a screenshot, because moving your hand to the keyboard is exactly the event they are watching for.

The workarounds fall into two categories: make the state permanent, or make time stop. Which you need depends on whether the state lives in CSS or in JavaScript.

1. Force the state in DevTools

This is the right answer for anything driven by a CSS pseudo-class, which covers most buttons, links, inputs and cards.

A DevTools styles panel with the :hov toggle highlighted and a checkbox list of :active, :hover, :focus and :focus-visible, with :hover ticked, beside a button shown in its plain and hover appearances.
One checkbox, and the element stays in its hover state no matter where your mouse goes.
  1. Open DevTools and select the element in the Elements panel. Right-click it on the page and choose Inspect to get there fastest.
  2. In the Styles pane, click the :hov button in the toolbar.
  3. Tick :hover. The element renders as if the pointer were on it, permanently.
  4. Screenshot however you like. The state survives moving the mouse, using the keyboard, and switching windows.

The same panel offers :active, :focus, :focus-visible, :visited and :target. :focus-visible is worth knowing: it is the state that draws the keyboard focus ring, and it is the one you want when documenting accessibility.

Where it does not work

Force-state only affects CSS pseudo-classes. If the dropdown opens because a script added a class or mounted an element, ticking :hover changes nothing — the browser is not the thing deciding. That is the next section.

2. Freeze the page with the debugger

For anything JavaScript controls — dropdowns, autocomplete suggestions, tooltips with timers, modals that close on outside click — the trick is to stop script execution while the overlay is open. A paused page keeps rendering exactly what was on screen and stops responding to the events that would close it.

A timeline with an overlay opening at one second and a pause glyph at three seconds, bracketed as “overlay is open”, captioned “the page stops, the pixels stay”.
Three seconds is enough to open a menu and get your hand off the mouse. The pause lands with the overlay still on screen.
  1. Open DevTools and go to the Console.
  2. Run setTimeout(() => { debugger; }, 3000).
  3. Switch back to the page and open the menu, hover the element, or trigger the tooltip.
  4. After three seconds the debugger pauses. The page is frozen with the overlay open and will not respond to anything.
  5. Screenshot. Then press F8 or the resume button in DevTools to let the page continue.

Chrome also offers an event-listener breakpoint under Sources → Event Listener Breakpoints → Mouse → mouseover, which pauses the first time the pointer enters any element. That is more precise, and considerably more irritating on a busy page.

3. Catching a specific animation frame

Sometimes the problem is not a state but a moment: a transition that flickers at 30%, a loading spinner that looks wrong at one angle.

A five-frame filmstrip showing a shape moving and growing across the frames, with a timeline marked 0%, 50% and 100% and a bracket over the middle frame.
An animation is a continuum. A screenshot gets one frame of it, and rarely the one you meant.

Three tools, in increasing order of precision:

  • Slow it down. Chrome DevTools has an Animations panel (in the drawer, via the ⋮ menu → More tools) with playback speed controls. Drop to 10% and the frame you want lasts ten times longer.
  • Scrub it. The same panel records animation groups and lets you drag a playhead through them, stopping wherever you like.
  • Pin it from the console. For an exact percentage, pause the animation and set its time directly:
Console — hold an animation at 40%
const el = document.querySelector('.spinner');
const anim = el.getAnimations()[0];
anim.pause();
anim.currentTime = anim.effect.getTiming().duration * 0.4;

And if you want the animation itself rather than a frame of it, a screenshot is the wrong artefact entirely. Record the screen, or capture the @keyframes rule — which brings us to the last option.

4. Stop screenshotting and capture the CSS

Every technique above produces one frame of one state. If what you actually want is to study how a component behaves, or to rebuild it, a picture is a poor container — you get the resting state and none of the transitions, or one hover frame and no idea what it looked like before.

Two columns: “screenshot” showing one flat frame with :hover, :focus and @keyframes crossed out, and “captured css” showing a code panel with the same three ticked.
A screenshot holds one moment. The CSS holds every state the component can be in, including the ones you did not trigger.

The states live in the stylesheet, not in the element. A :hover rule, a @keyframes block and a transition declaration are all sitting in CSS whether or not anything is hovering — which means a capture that reads the stylesheet gets them all, without you having to trigger anything.

This is what Grabby’s element capture does. Point at a component and it collects the computed styles plus the pseudo-class rules that apply to it, the @keyframes blocks those rules reference, the @font-face declarations the type depends on, and pseudo-elements. The capture opens in an editor where the component is live — you can hover it, resize it between desktop, tablet and mobile widths, and watch the animation run — and export it as standalone HTML or a single-file JSX component.

Copying that by hand from DevTools is harder than it looks, mostly because the styles are rarely on the element you copied. The five things that break when you copy HTML and CSS covers why.

Which one do you need?

Decision flow from “the state disappears when you reach for the shortcut” into three branches: it is a CSS state, it is JavaScript driven, or you need it again later.
Three questions, three answers. Most people only ever need the first branch.
You wantUse
A hover, focus or active state on one elementDevTools force-state (:hov)
A dropdown, tooltip or autocomplete panel held openA timed debugger pause
A precise frame of a transition or animationThe Animations panel, or set currentTime from the console
The keyboard focus ring for an accessibility auditForce :focus-visible
To rebuild the component, or study every stateCapture the CSS, not a picture
To show a motion problem to someoneA screen recording — a still cannot carry timing

The short answer

Nine times out of ten: inspect the element, click :hov, tick :hover, screenshot. It takes about five seconds once you have done it twice, and it works no matter where your mouse ends up.

For anything a script controls, freeze the page with a three-second debugger timeout. And if you are going to need that component again — to rebuild it, or to check what it does at another width — capture its code rather than its pixels. The states are in the stylesheet already.

That choice between pixels and code is the same one behind most capture decisions; the four ways to capture a web page lays out what each format keeps, and interactive state is the row where they differ most.

Frequently asked questions

How do I keep a hover state active while I take a screenshot?

Inspect the element in Chrome DevTools, click the :hov button in the Styles pane, and tick :hover. The element stays in its hover appearance regardless of where the pointer is, so you can take the screenshot any way you like. Untick it when you are done — it persists until you do.

How do I screenshot a dropdown menu that closes when I click away?

Run setTimeout(() => { debugger; }, 3000) in the console, switch to the page, open the menu, and wait. The debugger pauses the page with the menu still open and stops it responding to the click or blur that would close it. Screenshot, then press F8 to resume.

Why does forcing :hover in DevTools do nothing?

Two usual reasons. The state is applied by JavaScript rather than CSS, so no pseudo-class is involved — use the debugger pause instead. Or the hover rule targets an ancestor rather than the element you selected, which is common for cards that reveal a button. Check the selector shown in the Styles pane and force the state on whichever element it names.

Can I capture a hover state in a full-page screenshot?

Yes, if you force it first. A forced state is part of the rendered page, so any capture method sees it — including full-page tools. A debugger pause is different: a paused page cannot scroll or repaint, so scroll-and-stitch capture will not work while it is frozen. Use DevTools’ own capture command for those.

How do I capture a CSS animation rather than a frame of it?

Either record the screen, or capture the element’s CSS — the @keyframes rule and the properties that reference it fully describe the animation, and a captured component replays it. A still frame cannot carry timing or easing, which is usually the thing under discussion.

Does the DevTools “Paused in debugger” banner appear in my screenshot?

It appears in an operating-system screenshot and in a full-page extension capture, because it is drawn over the page. It does not appear in DevTools’ own screenshot commands. If you are using an external tool, crop it out — it sits at the top of the viewport.

Keep reading

A designed pricing card on the left and the same content pasted as bare unstyled text and default list markers on the right.
Code capture

10 min read

How to Copy the HTML and CSS of Any Element on a Website

The markup is the easy half. The styles are spread across a stylesheet, a :root block, a font declaration and possibly a shadow tree — which is why the paste comes back looking like 1996.

A browser window with a long page beside five numbered method chips labelled DevTools, print to PDF, OS capture, extension and headless.
Screenshots

9 min read

How to Take a Full-Page Screenshot in Chrome: 5 Methods, Tested

Chrome will capture an entire scrolling page without any extension at all. It just hides the command, and gives up on pages past a certain height. Here are five routes and where each one stops working.

A browser window with three numbered lime callout markers on different elements, a red highlight ring around a button, and a numbered legend panel beside it.
Screenshots

9 min read

How to Annotate a Website Screenshot So Feedback Lands

Most screenshot feedback fails not because the arrow was badly drawn, but because the image did not say which thing, in what state, at what width. Here is the craft of it.