logo

Controlling text selection and clicks with CSS

Dragging across a control can accidentally select its label. A decorative overlay can block a button underneath it. CSS provides a different property for each problem:

PropertyControlsTypical use
user-selectWhether and how users select textAvoid accidental selection on a drag handle
pointer-eventsWhich element becomes the target of pointer inputLet clicks pass through a decorative overlay

User-select: control text selection

The user-select property controls text selection. Its common values are:

  • auto: the initial value. For ordinary text, selection is usually allowed, but a parent's none or all value can affect the result.
  • text: allow text selection.
  • none: prevent text selection in the element.
  • all: select the entire element's contents when a selection would include any part of it.

The specification also defines contain, but MDN reports that browsers do not support it. Check the page's compatibility table for your target browsers.

Prevent accidental selection

Apply none to a control whose label should stay unselected during interaction. Keep article text and other content readers may want to copy selectable.

<button type="button" class="select-none">Drag handle</button>
<p>You can select part of this sentence.</p>
<p class="select-all">npm install example-package</p>
.select-none {
  user-select: none;
}

.select-all {
  user-select: all;
}

Try dragging across the button's label, then selecting a word in each paragraph. The command uses all, so selecting part of it selects the whole command. This does not copy it to the clipboard. An explicit Copy button needs a separate implementation.

Try the original selection example.

Keep selection rules local

Although user-select is not inherited according to the specification, the initial auto value takes a parent's none or all into account. Browser implementations also have inheritance differences, described in the MDN reference. Apply the rule to the smallest relevant element and test nested content in your target browsers.

Disabling selection does not protect the underlying content from copying. The CSS specification describes it as a convenience for interaction, not a copy protection mechanism.

Pointer-events: control pointer targeting

For HTML elements, use auto for normal pointer targeting and none to exclude an element from it. Other values, such as stroke and fill, are primarily for SVG.

With none, the browser targets an eligible element behind the excluded element. The excluded element remains visible and keeps its layout space. See the MDN pointer-events reference.

Let clicks pass through an overlay

This example places a decorative tint over a button. The wrapper establishes the positioning context, and z-index puts the overlay above the button.

<div class="preview">
  <button type="button" onclick="alert('Button clicked')">Click me</button>
  <div class="overlay" aria-hidden="true"></div>
</div>
.preview {
  position: relative;
  padding: 2rem;
}

.overlay {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: rgb(0 0 0 / 15%);
  pointer-events: none;
}

Clicking the button through the overlay triggers its alert. Remove pointer-events: none to see the overlay intercept the click. The overlay is empty and decorative, so aria-hidden="true" keeps it out of the accessibility tree.

Try the original pointer-events example.

Restore targeting on a child

pointer-events is inherited. A child can explicitly use auto to become a pointer target inside a parent that uses none:

.floating-layer {
  pointer-events: none;
}

.floating-layer button {
  pointer-events: auto;
}

This lets a floating layer pass clicks through its empty areas while its buttons remain clickable. Events targeting those buttons still travel through the parent during capture and bubbling. A listener on the parent can therefore still run, as the CSS specification explains.

Disable controls with HTML semantics

pointer-events: none does not remove an element from keyboard navigation. A focusable link can still receive focus with Tab and be activated with Enter. It also does not prevent text selection.

For a button that must be unavailable, use its native disabled attribute:

<button type="button" disabled>Submit</button>

Links have no native disabled attribute. If a link must remain visible but unavailable, decide how to present that state and prevent activation through every supported input method. Adding aria-disabled="true" communicates the state but does not disable behavior by itself. The MDN aria-disabled reference explains this distinction.

Conclusion

Use user-select for text selection and pointer-events for pointer targeting. Keep selection restrictions local, let decorative layers pass clicks through, and use native control states when an action is unavailable. Test pointer interaction and keyboard access separately because changing one does not necessarily change the other.