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-behavior1 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`:
Related skills
All skills- 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.
- 19
Find and fix barriers before customers do.
- 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.
- 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.