Technology

CSS pointer-events: A Complete Guide to Hit-Testing Control

The CSS pointer-events property controls which elements become targets for clicks, hovers, and other pointer interactions. Learn how it works and when to use it.

The CSS pointer-events property controls which elements become targets for clicks, hovers, and other pointer interactions by altering the browser's hit-testing process. It accepts eleven keyword values, though only auto and none apply to HTML elements while the rest are SVG-specific. The property is inherited, which creates both useful patterns for modal overlays and potential bugs with hidden submenus. It does not affect keyboard focus, event bubbling, or text selection.

The CSS pointer-events property lets developers decide whether the browser should treat an element as interactive when a pointer hovers over it. Rather than disabling events outright, it changes which element becomes the event target during the browser's hit-testing process.

When the browser prepares to fire a pointer event, it first determines which element sits under the cursor. Normally it picks the topmost element. But if that element has pointer-events: none, the browser skips it and keeps searching for the next eligible element underneath. This single mechanism explains most of the property's behavior.

How Hit-Testing Works Under the Hood

Before any click, hover, or tap registers, the browser performs hit-testing. It maps the pointer's coordinates to the DOM tree, starting from the topmost layer and working downward. An element with pointer-events: none is transparent to this process. The event passes through to whatever sits below it, whether that is another element, the page background, or nothing at all.

This is fundamentally different from removing an event listener. The element still exists in the DOM. It still renders. It simply opts out of being the initial target for pointer events.

The Full Keyword Reference

The property accepts eleven keyword values alongside standard CSS global values like inherit and initial:

  • auto - The default. The element receives pointer events normally. In SVG, this behaves identically to visiblePainted.
  • none - The element cannot become a pointer event target. Events pass through to underlying elements.
  • visiblePainted - Events register only when the element is visible and the pointer is over a painted area (fill or stroke).
  • visibleFill - Events register over the fill area only, regardless of the fill property value.
  • visibleStroke - Events register over the stroke only, regardless of the stroke property value.
  • visible - Events register over either fill or stroke, ignoring fill and stroke property values.
  • painted - Events register over painted areas, regardless of visibility.
  • fill - Events register over the fill area, regardless of visibility or paint properties.
  • stroke - Events register over the stroke, regardless of visibility or paint properties.
  • all - Events register anywhere inside the element's bounding box, even over unpainted areas.
  • bounding-box - Events register anywhere inside the bounding box, regardless of shape or paint.

Only auto and none apply to HTML elements. The remaining nine are SVG-specific and give fine-grained control over which parts of a graphic are interactive.

Inheritance and the Modal Pattern

pointer-events is an inherited property. Setting it to none on a parent passes that value down to every child unless a child explicitly overrides it. This creates a common pattern for modal overlays.

A full-screen container used to center a modal dialog covers the entire viewport. Without intervention, it blocks all clicks on the underlying page. Setting pointer-events: none on the container solves this, but because the property inherits, the modal itself must reset to pointer-events: auto to remain interactive. This two-line fix is now standard in most UI component libraries.

What It Does Not Do

The property only affects which element becomes the initial event target. It does not alter event propagation afterward. If a child with pointer-events: auto is clicked inside a parent with pointer-events: none, the child still becomes event.target. The event then bubbles normally, so listeners on the parent still fire during the capture and bubble phases.

It also does not disable keyboard interaction. An element with pointer-events: none can still receive focus via the Tab key if it is otherwise focusable. Users can continue interacting with it using keyboard shortcuts. For native form controls, the disabled attribute is the correct tool. For making an entire section completely non-interactive, including removing it from the accessibility tree, the inert attribute is the better choice.

Text selection is unaffected too. Users can still select text inside an element with pointer-events: none using Ctrl+A or click-and-drag, because text selection operates independently of pointer event targeting.

The Hidden Submenu Trap

A frequent bug occurs when developers hide a dropdown menu by setting opacity: 0 without also setting pointer-events: none. The submenu is invisible but still occupies space in the DOM. It continues to intercept clicks and hovers, blocking interaction with whatever sits behind it. The fix is simple: pair opacity: 0 with pointer-events: none, then restore both properties when the menu should appear.

Why This Matters for Modern Web Development

As interfaces grow more layered, with fixed headers, floating action buttons, toast notifications, and complex SVG data visualizations, precise control over interaction surfaces becomes critical. The pointer-events property is the standard mechanism for managing these layers without resorting to JavaScript workarounds or fragile z-index stacking. Understanding its full range of SVG values is particularly valuable for anyone building interactive charts, maps, or custom drawing tools where different parts of a graphic need distinct interaction behaviors.

Developers working with modern design systems or component libraries should keep this property in their regular toolkit. It solves real layout and interaction problems with a single CSS declaration, provided its limitations around inheritance, keyboard access, and event bubbling are well understood.