Declarative Overscroll Actions (Explainer)
Last updated: February 12, 2026
Table of Contents
- Table of Contents
- Introduction
- Proposal
- The API
- Events
- Implementation Model
- Accessibility Considerations
- Progressive Enhancement
- Interaction with Browser Gestures
- Alternatives Considered
- Open questions
Introduction
The web platform allows for sophisticated scrolling experiences, but it currently lacks a semantic way to utilize “overscroll” space (the area beyond the scroll boundary).
Common UI patterns like side-menus (swiping past the edge to reveal a menu) or swipe-to-action (swiping a list item to reveal delete buttons) currently rely on complex nested scrollers or JavaScript gesture polyfills. These workarounds are difficult to implement, computationally expensive, and often fail to provide accessible alternatives for non-touch users.
Proposal
We propose a set of HTML attributes that declaratively bind an element (the “overscroll content”) to the scroll boundary of a container.
Crucially, this binding is defined on an activatable element (like a <button>). This ensures that every gesture-based action has a guaranteed, accessible fallback interaction (click/Enter) without extra developer effort.
Goals
- No JavaScript: Enable swipe-to-reveal and pull-to-refresh gesture-based interactions using only HTML and CSS.
- Accessibility by Default: Enforce the existence of a semantic button to toggle the view, ensuring keyboard and assistive technology support.
- Performance: Offload gesture physics and animation to the browser’s compositor thread (via scroll timelines).
The API
There are two parts to the API:
First, the container needs to be identified as supporting overscroll areas. This is done by specifying overscrollcontainer attribute on the container.
Second, we introduce a new command value, toggle-overscroll, to bind a trigger button to both the container (the scroll port) and the content (the element hidden in the overscroll area).
<div id="container" overscrollcontainer>
<menu id="menu">
<li>Home</li>
<li>Settings</li>
</menu>
Some content.
</div>
<button commandfor="menu" command="toggle-overscroll" id=btn>
Toggle Menu
</button>Behavior
- Positioning: the
#menuis effectively absolutely positioned within the “overscroll” area of the overscroll container. - Chaining: When a user scrolls
#containerto its limit, the scroll chains to the#menu, pulling it into view. - Activation: As an alternative to scroll gestures, activating the
<button>will perform ascrollIntoView-like action on the#menu. Activating it again scrolls#menuback out of view.
(Here is a simple demo implementation on Github.io. This demo does not require any browser features.)
Terminology
In this explainer, we’ll use the following terminology:
- “overscroll container”: the scrolling container with the
overscrollcontainerattribute.#containerin the above code snippet. - “overscroll area”: the element (and its descendants) within the overscroll container that gets rendered as overscrolled content.
#menuin the above code snippet. - “overscroll invoker”: the command invoker with
command=toggle-overscrollpointing to the overscroll area.#btnin the above code snippet.
Nesting and Structure
- Overscroll invoker: The
<button>does not need to be a descendant of the container. It can live anywhere in the tree scope, subject to the normal rules for command invoker relationships. - Overscroll area: The overscroll area element (
#menu) must be a descendant of the overscroll container, but it does not have to be a direct child. It can also have other “normal” scrollable content before or after it. Crucially, the overscroll area behaves as if it is a layout child of the scroller, allowing it to escape containing blocks and clips of intermediate ancestors. This behaves in a very similar fashion to how popovers escape ancestors.
Chaining and Ordering
A scroller can have multiple overscroll areas (e.g., one on each side). The ::overscroll-area-parent manages these as siblings.
- LIFO Order: Chaining follows a “last-in, first-out” order. The last overscroll area in DOM order within the overscroll container is chained, and scrolls, first.
- Chaining Path: When the innermost scroller reaches its limit, it chains to the next overscroll area, and so on.
Overlay Mode
By default, overscroll pushes the container’s content. The overscrollcontainer="overlay" mode changes this so that the overscroll content slides over the container, similar to position: sticky.
<div id="container" overscrollcontainer="overlay">
...
</div>Note that this is a value of the overscrollcontainer attribute on the overall scroller, for simplicity of API design. It therefore affects all overscroll areas within that container. If use cases arise that require different overflowing elements to have different overlay modes, we can revisit this. But that does not seem to be a common use case today on the web.
Events
To allow developers to hook into the lifecycle of the gesture (e.g., for refresh logic or haptics), we expose the following events on the host container:
| Event Name | Description |
|---|---|
overscrollstart | Fired when the scroll boundary is breached and chaining begins. |
overscrollchanging | Fired when the gesture sufficiently drags overscroll to snap it to an open area (similar to scrollsnapchanging). |
overscrollend | Fired when the gesture completes and the state has changed. |
overscrollcancel | Fired when the gesture ends but snaps back to the original state. |
Implementation Model
Note: This section details the conceptual rendering tree structure.
When configured, the browser constructs an internal box structure to handle hit-testing and painting order:

.containercreates an internal::overscroll-area-parent. This is not affected by scrolling container, and scroll chains to::overscroll-area-parentaftercontainer.::overscroll-area-parentcontains the menu element. As a result, scrolls targeting the menu element chain directly to the::overscroll-area-parent, which can bring the typically-offscreen menu into view.

Accessibility Considerations
We think this proposal solves a major accessibility hurdle in gesture UIs. By attaching the behavior to a <button>:
- Keyboard Users: Can tab to the button and activate it to reveal the menu/action.
- Screen Readers: Perceive a standard button connection rather than an invisible gesture zone.
- Discoverability: The button provides a visible affordance for the action.
- Mitigation: Because the invoking element and the content both live in HTML, standard
aria-*attributes can be used for any necessary accessibility mitigations, as needed.
Focus Management
The overscroll area functions similarly to a popover with respect to focus. Activating the overscroll invoker button should not move focus to the menu automatically, but the menu should be next in the focus order.
It is a bit unclear what to do if the overscroll invoker is located within the overscroll area, especially if (see below) the overscroll content should be aria-hidden. Perhaps an overscroll invoker always remains focusable, and tab-focusing it automatically activates the overscroll area and scrolls it into view?
Progressive Enhancement
For browsers that do not support overscrollcontainer, the content simply becomes an inline part of the ordinary scroller. This likely presents as a broken experience.
A polyfill strategy might be to switch the overscroll content to a popover and switch the command invoker to command=toggle-popover. This would preserve the semantic relationship and a11y, while providing a functional fallback.
Interaction with Browser Gestures
Many browsers provide built-in overscroll behaviors, such as “pull-to-refresh” (reload) or “swipe-to-navigate” (back/forward).
These behaviors continue to function as-is, since the overscroll area is simply a chained scroller. Once the overscroll area is scrolled fully into view, the scroll will chain to the viewport scroller, and will activate these browser- based overscroll actions.
Alternatives Considered
CSS-only Properties
We considered defining this relationship purely in CSS. While powerful, CSS lacks the semantic enforcement of an interactive element. A CSS-only solution runs the risk of creating “invisible” gestures that are inaccessible to users who cannot perform swipe actions. By requiring an HTML activator, we enforce progressive enhancement.
Open questions
- ARIA Attributes: Should the browser automatically handle
aria-expandedandaria-controlson the overscroll invoker button based on the visibility of the overscroll content, in the same way that popovers are handled? - Visibility: When not revealed, should the overscroll content be treated as
aria-hidden="true"? - Backdrop: Should there be a
::backdroppseudo element when the overscroll area is activated? - Light Dismiss: Should overscroll areas support a “light dismiss” behavior where clicking outside or pressing Escape closes them? If so, how, exactly?
- Modal Behavior: Should there be a way to make an overscroll area modal when opened? (Feels like no.)
- Activatable Elements: Are there any interactive elements (like
<area>) that should not be allowed as overscroll invokers? - Pseudo Classes: Should the
:openpseudo class match the overscroll area when it is scrolled into view? Or perhaps a new pseudo class?
Open UI