Skip to content
Fast, accessible sites

Overscroll Behavior

Stop the desktop bounce. Keep pull-to-refresh on phones.

  • CSS
  • Scrolling
  • Mobile

Install

npx skills add webrenew/skills --skill overscroll-behavior
GitHub

1 file · 140 lines · any agent that reads SKILL.md

Use it when

  • The page bounces at its edges on a Mac
  • A modal or sidebar scrolls the page behind it
  • You build an app shell, a chat or an editor layout

What your agent delivers

  • One root CSS rule that applies only to mouse and trackpad users
  • Scroll containment for modals, drawers and nested panels
  • Pull-to-refresh that still works on touch screens

Source files

Read every line before you install. Copy or download one file.

SKILL.md

140 lines

---
name: overscroll-behavior
description: >
  Kill the macOS/desktop rubber-band overscroll bounce without breaking mobile pull-to-refresh,
  by setting `overscroll-behavior: none` on the root scroller gated behind `@media (pointer: fine)`
  (the pattern Vercel ships). Also covers `overscroll-behavior: contain` for modals, drawers,
  sidebars, and other nested scroll areas to stop scroll chaining. Triggers whenever building or
  reviewing a root layout, global CSS (globals.css, app/layout.tsx, _document, index.css), app
  shells, dashboards, full-height/fixed-viewport UIs, chat or editor layouts, scrollable panels,
  modals, sheets, or when the user mentions overscroll, rubber-band, bounce, elastic scroll,
  scroll chaining, pull-to-refresh, or "the page bounces on my trackpad".
---

# Overscroll Behavior — No Bounce on Desktop, Pull-to-Refresh on Mobile

**RULE: Disable root overscroll only for fine pointers. Never disable it for touch.**

```css
@media (pointer: fine) {
  html,
  body {
    overscroll-behavior: none;
  }
}
```

- `overscroll-behavior: none` stops the elastic rubber-band bounce when a trackpad or mouse
  scrolls past the top or bottom of the page. App-like UIs (dashboards, editors, sticky
  headers, fixed sidebars) stop showing a blank gap or a dragged-off header.
- `(pointer: fine)` means the **primary** input is a mouse or trackpad. Phones and tablets report
  `pointer: coarse`, so the rule does not apply there and **pull-to-refresh keeps working**.
- On Chrome desktop, `none` on the root also blocks the horizontal swipe back/forward gesture.
  That is usually fine for app UIs. If you want to keep it, set only the vertical axis:
  `overscroll-behavior-y: none`.

## Why the gate matters

An ungated `html { overscroll-behavior: none }` removes pull-to-refresh on Android Chrome and
iOS Safari. Mobile users lose a gesture they expect, and nothing tells them why. The media
query keeps the fix on the devices that have the problem.

## Placement

The root scroller is the viewport, which reads the value from `html`. Put the rule in global
CSS, once. Include `body` too because it is harmless and covers layouts where `body` is the
scroll container.

### Tailwind v4.1+

Use the built-in `pointer-fine` variant on the root element:

```tsx
// app/layout.tsx
<html lang="en" className="pointer-fine:overscroll-none">
  <body className="pointer-fine:overscroll-none">{children}</body>
</html>
```

Or in `globals.css`:

All skills
  1. 18

    Website Speed Optimization

    website-speed-optimization

    Find what slows the page and fix the metrics that users feel.

    Find what slows the page and fix the metrics that users feel.

  2. 19

    Accessibility Audit

    accessibility-audit

    Find and fix barriers before customers do.

    Find and fix barriers before customers do.

  3. 01

    Technical SEO

    Growth & marketingseo-technical

    Make each page clear to search engines. Give them a path to every link.

    Make each page clear to search engines. Give them a path to every link.

  4. 02

    Local SEO

    Launch packGrowth & marketinglaunch-local-seo

    Get found by customers nearby, in local search and on Google Maps.

    Get found by customers nearby, in local search and on Google Maps.