Skip to content

Lightweight smooth scrolling powered by Lenis with automatic loading, attribute config, and multi-instance support.

License

Notifications You must be signed in to change notification settings

Rethink-JS/rt-smooth-scroll

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

rt-smooth-scroll

Platform: Web JavaScript npm version jsDelivr hits License: MIT

rt-smooth-scroll is a lightweight JavaScript library that seamlessly integrates the Lenis smooth scroll engine into your sites with:

  • Automatic Lenis loading (no extra installs needed)
  • Zero-config defaults (Lenis defaults, unless you override via attributes)
  • Support for multiple smooth scroll instances
  • A clean global API under window.rtSmoothScroll
  • Smart Scroll-To actions with indexed selectors and dynamic offsets
  • Automatic Anchor Link Conversion (hijack native links for smooth scrolling)
  • Scroll-To Completion Hooks (run actions/functions after a scroll-to completes)
  • Per-instance configuration via HTML attributes
  • Console logs showing each instance’s final resolved config

Lenis (GitHub): https://github.com/darkroomengineering/lenis


Table of Contents


1. Installation

1.1 CDN (jsDelivr)

<script src="https://cdn.jsdelivr.net/npm/@rethink-js/rt-smooth-scroll@latest/dist/index.min.js"></script>

1.2 npm

npm install @rethink-js/rt-smooth-scroll

Then bundle or load dist/index.min.js as appropriate for your build setup.


2. Quick Start

Add the script to your page. With no configuration provided, rt-smooth-scroll will:

  • Activate itself automatically (if you didn’t explicitly opt out)
  • Load Lenis from CDN
  • Create a root smooth scroll instance
  • Expose the global API

Example:

<script src="https://cdn.jsdelivr.net/npm/@rethink-js/rt-smooth-scroll@latest/dist/index.min.js"></script>

Note: If you do not set any rt-smooth-scroll-* config attributes, the root instance uses Lenis defaults.


3. Activation Rules

The library is activated when:

  • The attribute rt-smooth-scroll exists on <html> or <body> OR
  • You place one or more elements with rt-smooth-scroll-instance

If neither is present and no instance elements are found, it auto-enables itself on <body> by adding rt-smooth-scroll (so you get a working root instance by default).


4. Configuration (HTML Attributes)

Root Mode

Add to <html> or <body> to enable:

<body rt-smooth-scroll></body>

Global Options

Place on <html> or <body> to configure defaults:

<body
  rt-smooth-scroll
  rt-smooth-scroll-lerp="0.2"
  rt-smooth-scroll-wheel-multiplier="1"
  rt-smooth-scroll-easing="easeOutCubic"
></body>

Important Lenis behavior:

  • duration and easing are useless if lerp is defined (this is how Lenis works).

Core attributes:

Attribute Description
rt-smooth-scroll-duration Lenis duration (only applies when lerp is not used)
rt-smooth-scroll-lerp Lenis lerp (0 → 1)
rt-smooth-scroll-orientation Lenis orientation
rt-smooth-scroll-gesture-orientation Lenis gestureOrientation
rt-smooth-scroll-normalize-wheel Alias of Lenis smoothWheel (legacy naming supported)
rt-smooth-scroll-smooth-wheel Lenis smoothWheel
rt-smooth-scroll-wheel-multiplier Lenis wheelMultiplier
rt-smooth-scroll-touch-multiplier Lenis touchMultiplier
rt-smooth-scroll-sync-touch Lenis syncTouch
rt-smooth-scroll-sync-touch-lerp Lenis syncTouchLerp
rt-smooth-scroll-touch-inertia-exponent Lenis touchInertiaExponent
rt-smooth-scroll-infinite Lenis infinite
rt-smooth-scroll-auto-resize Lenis autoResize
rt-smooth-scroll-overscroll Lenis overscroll
rt-smooth-scroll-anchors Lenis anchors (boolean or JSON)
rt-smooth-scroll-auto-toggle Lenis autoToggle
rt-smooth-scroll-allow-nested-scroll Lenis allowNestedScroll
rt-smooth-scroll-easing Named easing function (only applies when lerp is not used)
rt-smooth-scroll-options-json Merge additional Lenis options via JSON

Easing options included:

  • linear
  • easeInQuad
  • easeOutQuad
  • easeInOutQuad
  • easeInCubic
  • easeOutCubic
  • easeInOutCubic
  • easeInOutSine
  • easeOutExpo

Per-Instance Configuration

Add attributes to any scroll container:

<div
  rt-smooth-scroll-instance
  rt-smooth-scroll-id="panel"
  rt-smooth-scroll-content=".scroll-content"
  rt-smooth-scroll-lerp="0.18"
></div>
Attribute Description
rt-smooth-scroll-instance Marks scroll container
rt-smooth-scroll-id Optional instance identifier
rt-smooth-scroll-content Selector inside container (defaults to first child if omitted)

Advanced Selectors (wrapper/content/eventsTarget)

You can map Lenis DOM targets using selectors:

<body
  rt-smooth-scroll
  rt-smooth-scroll-wrapper="#page-wrapper"
  rt-smooth-scroll-content="#page-content"
  rt-smooth-scroll-events-target="#page-wrapper"
></body>

Or per instance:

<div
  rt-smooth-scroll-instance
  rt-smooth-scroll-id="panel"
  rt-smooth-scroll-wrapper="#panel-wrapper"
  rt-smooth-scroll-content=".panel-content"
  rt-smooth-scroll-events-target="#panel-wrapper"
></div>

5. Scroll-To Actions

You can trigger a smooth scroll to any element, position, or specific instance using the rt-smooth-scroll-to attribute on any clickable element.

Basic Usage

<button rt-smooth-scroll-to="#footer">Go to Footer</button>

<button rt-smooth-scroll-to="top">Back to Top</button>

<button rt-smooth-scroll-to="500">Go to 500px</button>

Indexed Selectors

You can target elements by class order (1-based index) using .class(N) syntax.

<button rt-smooth-scroll-to=".section(2)">Go to Section 2</button>

Customization Attributes

You can customize the scroll behavior for specific triggers.

Attribute Description
rt-smooth-scroll-offset Offset in pixels. Supports selectors! (See below)
rt-smooth-scroll-duration Override scroll duration for this action
rt-smooth-scroll-immediate Jump instantly (true/false)
rt-smooth-scroll-lock Lock scroll during animation
rt-smooth-scroll-force Force scroll even if stopped
rt-smooth-scroll-target-id Explicitly target a specific scroll instance ID (e.g. "panel")

Dynamic Element Offsets

Instead of hardcoding pixels, you can pass a selector to rt-smooth-scroll-offset. The library will calculate that element's offsetHeight and apply it as a negative offset (perfect for sticky headers).

<button rt-smooth-scroll-to="#about" rt-smooth-scroll-offset="#nav">
  About
</button>

Scroll-To Completion Hooks

When a scroll-to finishes (including the built-in correction pass for layout shifts), you can run an action/function.

Supported hook attributes:

  • Per-trigger: rt-smooth-scroll-on-complete
  • Global default: rt-smooth-scroll-on-complete on <html> or <body>

5.1 Per-trigger completion action

Click an element after the scroll completes:

<a rt-smooth-scroll-to="#contact" rt-smooth-scroll-on-complete="#nav-show">
  Contact
</a>

The shorthand above treats the value as a selector and will click() it.

You can also be explicit:

<a
  rt-smooth-scroll-to="#contact"
  rt-smooth-scroll-on-complete="click:#nav-show"
>
  Contact
</a>

5.2 Global default completion action

Apply to all scroll-to triggers unless they override it:

<body rt-smooth-scroll rt-smooth-scroll-on-complete="click:#nav-show"></body>

5.3 Supported completion action formats

1) Selector-only (defaults to click)

<button rt-smooth-scroll-to="#x" rt-smooth-scroll-on-complete="#nav-show">
  Go
</button>

2) Typed strings

  • click:#selector
  • focus:#selector
  • dispatch:event-name (fires CustomEvent on window)
  • call:functionName (calls window[functionName])

Examples:

<button rt-smooth-scroll-to="#x" rt-smooth-scroll-on-complete="focus:#search">
  Go
</button>

<button
  rt-smooth-scroll-to="#x"
  rt-smooth-scroll-on-complete="dispatch:rt:smooth-scroll:complete"
>
  Go
</button>

<button
  rt-smooth-scroll-to="#x"
  rt-smooth-scroll-on-complete="call:afterScroll"
>
  Go
</button>

When using call:functionName, your function receives a single object:

window.afterScroll = function (ctx) {
  console.log("afterScroll", ctx);
};

ctx includes:

  • lenis (the Lenis instance used)
  • trigger (the element that initiated the scroll)
  • target (resolved target number/element/window)
  • value (the raw rt-smooth-scroll-to string)
  • id (the explicit rt-smooth-scroll-target-id if provided)

3) JSON actions (single or array)

This is the most robust option for multi-step actions:

<button
  rt-smooth-scroll-to="#x"
  rt-smooth-scroll-on-complete='[
    {"type":"click","selector":"#nav-show"},
    {"type":"dispatch","name":"rt:smooth-scroll:complete","detail":{"from":"scroll-to"}}
  ]'
>
  Go
</button>

Supported JSON action types:

  • { "type": "click", "selector": "#el" }
  • { "type": "focus", "selector": "#el" }
  • { "type": "dispatch", "name": "event-name", "detail": any }
  • { "type": "call", "name": "functionName", "detail": any }

For dispatch and call, detail is merged into the context under detail.


6. Anchor Link Conversion

You can automatically convert standard <a> tags (e.g., <a href="#contact">) into smooth scroll triggers without manually adding attributes to every link.

Enable Conversion

Add this attribute to your <body> or <html> tag:

<body rt-smooth-scroll rt-smooth-scroll-anchor-links="true"></body>

How it works

  1. Auto-Detection: Finds all links pointing to a hash on the current page.
  2. Hijacking: Converts them to use the rt-smooth-scroll-to logic.
  3. Clean URLs: Removes the href attribute so the browser URL bar does not update (no #hash in URL), keeping your history clean.
  4. Accessibility: Automatically restores tabindex="0", role="button", cursor: pointer, and keyboard Enter / Space key support.

Anchor link completion hook (auto-injected)

You can automatically attach a completion hook to every converted anchor link:

<body
  rt-smooth-scroll
  rt-smooth-scroll-anchor-links="true"
  rt-smooth-scroll-anchor-links-on-complete="click:#nav-show"
></body>

Rules:

  • If a link already has rt-smooth-scroll-on-complete, it is not overridden.
  • If rt-smooth-scroll-anchor-links-on-complete exists, it is copied into each converted link as rt-smooth-scroll-on-complete.

7. Multiple Instances

rt-smooth-scroll supports any number of independent Lenis instances on a page. Each instance has its own wrapper + content and can be controlled individually via API.

Context Awareness: If a rt-smooth-scroll-to button is placed inside a nested scroll instance, it will automatically control that parent instance, not the root window, unless you explicitly override it with rt-smooth-scroll-target-id.


8. Global API

After initialization, access:

window.rtSmoothScroll;

Common methods:

Method Description
ids() Array of registered instance ids
get(id) Returns Lenis instance
start(id?) Start scroll
stop(id?) Stop scroll
toggle(id?) Toggle scroll
resize(id?) Trigger Lenis resize
refreshAnchors() Manually re-run anchor link conversion (useful for dynamic content)
destroy(id?) Remove instance

Default root Lenis instance is also exposed as:

window.lenis;

9. Console Logging

On startup, each instance logs:

  • Instance ID
  • Wrapper element
  • Content element
  • Final resolved options

This helps you confirm exactly what configuration is applied in the browser.


10. Troubleshooting

Scroll feels laggy / too delayed

  • Increase rt-smooth-scroll-lerp (e.g. 0.2 → 0.35) for a snappier response.
  • Decrease rt-smooth-scroll-lerp (e.g. 0.1 → 0.05) for a smoother/heavier feel.
  • Leave rt-smooth-scroll-wheel-multiplier="1" unless you have a strong reason to change perceived speed.

Duration / easing doesn’t seem to do anything

Lenis treats duration and easing as useless if lerp is defined. If you want time-based behavior, ensure you’re not effectively running in lerp-mode.

My rt-smooth-scroll-on-complete didn’t run

Common causes:

  • The selector/function name is wrong or not available at runtime.
  • Your completion hook depends on DOM that’s not yet mounted/visible.
  • If you are using call:fnName, ensure window.fnName exists before the scroll completes.
  • If you are using JSON, ensure it is valid JSON (double quotes, no trailing commas).

11. License

MIT License

Package: @rethink-js/rt-smooth-scroll
GitHub: https://github.com/Rethink-JS/rt-smooth-scroll

by Rethink JS
https://github.com/Rethink-JS

About

Lightweight smooth scrolling powered by Lenis with automatic loading, attribute config, and multi-instance support.

Resources

License

Stars

Watchers

Forks

Packages

No packages published