Scrollbars, Cursors & Interactions

Chapter 16: Scrollbars, Cursors & Interactions

Small details often have the biggest impact on how "native" or "polished" a web application feels. Default scrollbars can be clunky, and the default arrow cursor is often too generic for specialized interactive elements. This chapter covers how to refine these micro-interactions using modern CSS.


1. Customizing Scrollbars

Standard scrollbars often look out of place in a modern, minimal design. While CSS scrollbar styling used to require complex vendor prefixes, the modern standard is becoming much simpler.

Scrollbar Anatomy

A scrollbar consists of two main parts: the Track (the background) and the Thumb (the draggable part).

Thumb (draggable)Track (background)

The Modern Standard (scrollbar-width and scrollbar-color)

.custom-scroll {
  scrollbar-width: thin; /* auto, thin, or none */
  scrollbar-color: #94a3b8 transparent; /* thumb color, track color */
}

The Legacy Webkit Syntax (for fine-grained control)

/* Width of the scrollbar */
.webkit-scroll::-webkit-scrollbar {
  width: 10px;
}

/* Draggable thumb */
.webkit-scroll::-webkit-scrollbar-thumb {
  background: linear-gradient(180deg, #94a3b8, #64748b);
  border-radius: 10px;
  border: 2px solid #f1f5f9; /* Creates a gap around the thumb */
}

/* Track background */
.webkit-scroll::-webkit-scrollbar-track {
  background: #f1f5f9;
}

2. Preventing Layout Shift with scrollbar-gutter

A common issue in web apps is the "jumping" effect when a scrollbar appears or disappears, shifting the content slightly to the left. scrollbar-gutter: stable prevents this by reserving space for the scrollbar even if it isn't needed.

.stable-container {
  scrollbar-gutter: stable;
  overflow-y: auto;
}
Normal (Content jumps)Stable Gutter (No jumping)

3. Refining the Cursor

The cursor provides immediate feedback about an element's functionality. Beyond the standard pointer, modern apps use specialized cursors for specific tasks.

  • crosshair: Perfect for drawing apps or precision selection.
  • move: Indicates an element can be moved in any direction.
  • copy: Shows that an item will be duplicated if dropped.
  • help: Indicates that more information is available.
.precision-tool {
  cursor: crosshair;
}

.help-icon {
  cursor: help;
}

.expandable-image {
  cursor: zoom-in;
}
CrosshairMoveZoom In

4. Accent Colors & Highlight Control

You can brand native UI elements and control how text is selected to match your design system.

/* Change colors of checkboxes, radios, and range inputs */
form {
  accent-color: #3b82f6;
}

/* Custom text selection color */
::selection {
  background: rgba(59, 130, 246, 0.2);
  color: #1e40af;
}

/* Prevent text selection on buttons */
.button-icon {
  user-select: none;
}

5. Practical Interaction Pattern

Combining these properties creates a high-quality interactive row, like you would see in a file manager or email client.

.data-row {
  cursor: pointer;
  user-select: none;
  scrollbar-gutter: stable;
  transition: background 0.2s ease;
}

.data-row:hover {
  background: #f1f5f9;
}

.data-row:active {
  cursor: grabbing; /* Feedback for dragging */
}

6. Debugging Checklist

  1. Is the custom scrollbar making the content area smaller? (Use scrollbar-gutter: stable).
  2. Is the not-allowed cursor confusing users? (Ensure the element also looks disabled).
  3. Is user-select: none preventing users from copying important information?
  4. Are ::-webkit-scrollbar styles not showing in Firefox? (Firefox only supports scrollbar-width and scrollbar-color).
  5. Is the pointer cursor missing from an element that behaves like a button?