CSS User-Select

user-select is one of those CSS properties you don’t think about… until your UI starts doing something annoying. Like selecting text when you’re trying to drag a card. Or refusing to let people copy an invite code. Or highlighting half the page when you double-click a word.

This tutorial will teach you how to control text selection on the page, how to style the selection highlight color, and how to keep Safari happy (spoiler: use the -webkit- prefix).

What user-select does

user-select controls whether (and how) the user can select text/content with their mouse, trackpad, or touch selection handles.

  • It’s about selection, not copying. (Copy is just what people do after selecting.)
  • It’s often used for UI: buttons, draggable cards, custom controls.
  • It can hurt UX if used too aggressively (like disabling selection on the entire page).
.demo-area {
  -webkit-user-select: auto;
  user-select: auto;
}
  
.demo-area {
  -webkit-user-select: text;
  user-select: text;
}
  
.demo-area {
  -webkit-user-select: none;
  user-select: none;
}
  
.demo-area {
  -webkit-user-select: all;
  user-select: all;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.demo-area {
  border: 3px solid #111;
  padding: 16px;
  font-family: ui-monospace, SFMono-Regular, monospace;
  background: #f4f4f4;
  border-radius: 12px;
}

.demo-area p {
  margin: 0 0 12px 0;
  line-height: 1.4;
}

.demo-area .chip {
  display: inline-block;
  padding: 6px 10px;
  border: 2px solid #111;
  background: #fff;
  border-radius: 999px;
}
  

Try selecting this text. Click + drag, double-click words, or triple-click lines.

I am a tiny UI chip. Try selecting me too.

Change snippets in the playground above to switch between the common values and feel the difference immediately.

CSS user-select property basics

The property is straightforward:

  • user-select: auto; — browser decides (usually normal behavior)
  • user-select: text; — selection allowed
  • user-select: none; — selection disabled
  • user-select: all; — selecting any part selects the whole element

You typically apply it to the specific element you want to affect (not the whole page).

CSS user select auto

auto is the “default-ish” behavior. In most cases, it behaves like normal text selection, but it also respects native control behavior and browser heuristics.

In other words: if you’re not sure what you need yet, auto is the “do nothing weird” setting.

user-select:
.card {
  -webkit-user-select: auto;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.card {
  width: min(640px, 100%);
  border: 3px solid #111;
  border-radius: 16px;
  padding: 16px;
  background: #fff;
  font-family: system-ui, -apple-system, sans-serif;
}

.card h4 {
  margin: 0 0 10px 0;
  font-size: 18px;
}

.card p {
  margin: 0 0 10px 0;
  line-height: 1.5;
}

.card code {
  font-family: ui-monospace, SFMono-Regular, monospace;
  background: #f2f2f2;
  padding: 2px 6px;
  border-radius: 6px;
  border: 1px solid #ddd;
}
  

Selection playground

Try selecting parts of this paragraph. Then switch the radio value above.

Copy target: INVITE-7K2M-91QX

CSS user select text

user-select: text; explicitly allows selection. This is handy if you have a component where selection is being blocked by another rule (like a parent set to none).

Common use: disable selection on a draggable card, but re-enable it on the text area inside the card.

.draggable {
  -webkit-user-select: none;
  user-select: none;
}

.draggable .content {
-webkit-user-select: text;
user-select: text;
} 
.draggable {
   -webkit-user-select: none;
  user-select: none;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.draggable {
  width: min(680px, 100%);
  border: 3px solid #111;
  border-radius: 16px;
  background: #f4f4f4;
  padding: 12px;
  font-family: system-ui, -apple-system, sans-serif;
}

.drag-handle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 12px;
  border-radius: 12px;
  border: 2px dashed #111;
  background: #fff;
  margin-bottom: 12px;
}

.drag-handle strong {
  font-size: 14px;
}

.content {
  padding: 14px;
  background: #fff;
  border-radius: 12px;
  border: 2px solid #111;
  line-height: 1.5;
}

.small {
  opacity: 0.8;
  font-size: 14px;
}
  
Drag handle area Try selecting text inside vs outside
Select this paragraph. This is the “content” area where copy/paste is useful.

In the first snippet, the overall card is “non-selectable” (great for drag interactions), but the inner content is selectable (great for humans).

CSS user-select none / CSS user select disable

user-select: none; disables selection. This is perfect for “clicky UI” elements where accidental selection feels sloppy:

  • buttons
  • icon controls
  • drag handles
  • tabs

Don’t apply user-select: none; to body or the entire page. People will resent you because they can’t copy text, select a word to search, or use selection-based accessibility tools.

.ui-row button,
.ui-row .tab {
  -webkit-user-select: none;
  user-select: none;
}
  
.ui-row button,
.ui-row .tab {
  -webkit-user-select: text;
  user-select: text;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.ui-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  border: 3px solid #111;
  background: #fff;
  padding: 14px;
  border-radius: 16px;
  font-family: system-ui, -apple-system, sans-serif;
}

.ui-row button {
  border: 2px solid #111;
  background: #f4f4f4;
  padding: 10px 12px;
  border-radius: 12px;
  cursor: pointer;
  font-weight: 700;
}

.ui-row .tab {
  border: 2px solid #111;
  background: #fff;
  padding: 10px 12px;
  border-radius: 999px;
}

.note {
  flex-basis: 100%;
  margin-top: 6px;
  opacity: 0.85;
}
  
Tab label
Try to click-drag across the button text and the tab label.

CSS user select all

user-select: all; makes selecting any part of the element select the whole element. It’s excellent for “copy this thing” UI such as:

  • invite codes
  • short tokens
  • single-line commands
.copy-token {
  -webkit-user-select: all;
  user-select: all;
}
  
.copy-token {
  -webkit-user-select: text;
  user-select: text;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  width: min(720px, 100%);
  border: 3px solid #111;
  border-radius: 16px;
  padding: 16px;
  background: #fff;
  font-family: system-ui, -apple-system, sans-serif;
}

.copy-token {
  display: inline-block;
  margin-top: 10px;
  padding: 10px 12px;
  border-radius: 12px;
  border: 2px solid #111;
  background: #f4f4f4;
  font-family: ui-monospace, SFMono-Regular, monospace;
}

.small {
  margin: 0;
  opacity: 0.85;
}
  

Try selecting the token below. With all, any drag selects everything.

npm install my-awesome-package

Real-world patterns: combining values

Most real UI isn’t “selectable” or “not selectable” everywhere. It’s mixed:

  • Disable selection on the draggable container
  • Enable selection on the text region
  • Use all on copy tokens
.panel {
  -webkit-user-select: none;
  user-select: none;
}

.panel .body {
  -webkit-user-select: text;
  user-select: text;
}

.panel .copy {
  -webkit-user-select: all;
  user-select: all;
} 
*,
::before,
::after {
  box-sizing: border-box;
}

.panel {
  width: min(760px, 100%);
  border: 3px solid #111;
  border-radius: 18px;
  background: #f4f4f4;
  font-family: system-ui, -apple-system, sans-serif;
}

.panel .header {
  padding: 14px 16px;
  border-bottom: 3px solid #111;
  background: #fff;
  border-radius: 16px 16px 0 0;
  display: flex;
  justify-content: space-between;
  gap: 12px;
  align-items: center;
}

.panel .header strong {
  font-size: 16px;
}

.panel .header span {
  opacity: 0.8;
  font-size: 14px;
}

.panel .body {
  padding: 16px;
  line-height: 1.55;
}

.copy {
  display: inline-block;
  margin-top: 10px;
  padding: 8px 10px;
  border-radius: 12px;
  border: 2px solid #111;
  background: #fff;
  font-family: ui-monospace, SFMono-Regular, monospace;
}
  
Draggable panel (pretend you drag me)

Try selecting this paragraph. It should be selectable even though the panel itself isn’t.

Copy this token: RESET-4F19-ABCD

CSS user select color

This is a super common search phrase, and here’s the secret: there is no user-select-color property.

The “selection color” is styled using the ::selection pseudo-element. That’s what controls the highlight background when the user selects text.

Styling selection with ::selection

You can usually style:

  • background (or background-color)
  • color

Many other properties are ignored by browsers for security/consistency reasons, so keep it simple.

.demo ::selection {
  background: #111;
  color: #fff;
}
  
.demo ::selection {
  background: #ffdd57;
  color: #111;
}
  
.demo ::selection {
  background: #7c3aed;
  color: #fff;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.demo {
  width: min(760px, 100%);
  border: 3px solid #111;
  border-radius: 16px;
  padding: 16px;
  background: #fff;
  font-family: system-ui, -apple-system, sans-serif;
  line-height: 1.6;
}

.demo h4 {
  margin: 0 0 10px 0;
  font-size: 18px;
}

.demo p {
  margin: 0;
}

.tip {
  opacity: 0.85;
  font-size: 14px;
  margin-top: 10px;
}
  

Select some text in this box

Drag to select a few words, then switch snippets to see different selection highlight colors.

Tip: selection styling usually looks best with high contrast.

If you want the selection style globally, you can target ::selection without a class (but many people prefer scoping it to avoid surprises).

Learn more about ::selection and other pseudo-elements in the CSS Pseudo Elements Interactive Tutorial.

CSS user select safari

If you want reliable support in Safari, you should include the prefixed version:

  • -webkit-user-select
  • user-select

In practice, you’ll often write both. The prefixed one is a “Safari seatbelt”.

.safari-safe {
  -webkit-user-select: none;
  user-select: none;
}
  
.safari-safe {
  -webkit-user-select: text;
  user-select: text;
}
  
.safari-safe {
  -webkit-user-select: all;
  user-select: all;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.safari-safe {
  width: min(720px, 100%);
  border: 3px solid #111;
  border-radius: 16px;
  padding: 16px;
  background: #f4f4f4;
  font-family: system-ui, -apple-system, sans-serif;
}

.safari-safe .pill {
  display: inline-block;
  margin-top: 10px;
  padding: 8px 10px;
  border-radius: 999px;
  border: 2px solid #111;
  background: #fff;
  font-family: ui-monospace, SFMono-Regular, monospace;
}
  

This playground uses -webkit-user-select and user-select together.

Select me (or try not to)

Rule of thumb: if you write user-select for UI behavior, also write -webkit-user-select. It’s a tiny line of CSS that prevents “why is this weird on Safari?” headaches.

Common mistakes and good practices

Don’t disable selection on the whole page

Disabling selection on body (or a page wrapper) blocks everyday behaviors people rely on: copying text, selecting an address, selecting a word to search, and some assistive workflows.

If you need non-select behavior for UI, scope it to the UI element only.

Use none for UI, and text for content

  • UI regions: user-select: none;
  • Content regions: user-select: text;
  • Copy tokens: user-select: all;

Remember selection styling is separate

user-select controls whether selection can happen. ::selection controls how the selection highlight looks.

Debugging: user-select not working

  • Check parents: a parent with user-select: none; can prevent selection unless you re-enable it on the child with user-select: text;
  • Safari support: add -webkit-user-select alongside user-select
  • Pointer interactions: some drag/gesture scripts prevent selection via JS (so the CSS may be fine, but JS is blocking)
  • Is it actually text? selection behavior differs between plain text, form inputs, and custom widgets

Quick copy/paste recipes

Recipe: disable selection on buttons

Keep UI feeling crisp:

button,
.icon-button {
  -webkit-user-select: none;
  user-select: none;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
  border: 3px solid #111;
  border-radius: 16px;
  padding: 16px;
  background: #fff;
  font-family: system-ui, -apple-system, sans-serif;
}

button,
.icon-button {
  border: 2px solid #111;
  border-radius: 12px;
  padding: 10px 12px;
  background: #f4f4f4;
  cursor: pointer;
  font-weight: 700;
}

.icon-button {
  width: 44px;
  height: 44px;
  display: flex;
  justify-content: center;
  align-items: center;
}
  

Try click-dragging across the label text.

Recipe: select-all for a copy token

Make copy-friendly snippets:

.token {
  -webkit-user-select: all;
  user-select: all;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.box {
  width: min(680px, 100%);
  border: 3px solid #111;
  border-radius: 16px;
  padding: 16px;
  background: #fff;
  font-family: system-ui, -apple-system, sans-serif;
}

.token {
  display: inline-block;
  margin-top: 10px;
  padding: 10px 12px;
  border: 2px solid #111;
  border-radius: 12px;
  background: #f4f4f4;
  font-family: ui-monospace, SFMono-Regular, monospace;
}
  

Click-drag anywhere on the token to select everything:

curl https://example.com/setup.sh | sh

Wrap-up

You now have the full toolkit:

  • user-select to control selection behavior (auto, text, none, all)
  • ::selection to style the highlight color
  • Safari compatibility by always including -webkit-user-select

If you want a simple mental model: disable selection for “controls”, allow selection for “content”, and use select-all for “copy tokens”. Your users (and your future self) will thank you.