Lenis Scroll-To Anchor Target

Wires data-anchor-target links to Lenis's scrollTo method with a custom quartic ease and configurable duration and offset. Clicking a link smoothly scrolls to the matching section id without any native anchor jump.

lenisscrollanchorsmooth-scrolljavascriptnavigation

Setup — External Scripts

Setup: External Scripts
html
<!-- CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/lenis@1.2.3/dist/lenis.css">

<!-- JS -->
<script src="https://cdn.jsdelivr.net/npm/lenis@1.2.3/dist/lenis.min.js"></script>

Code

index.html
html
<!-- Anchor Links -->
<nav class="nav">
  <div class="nav__inner">
    <span data-anchor-target="#pink" class="nav-link">Pink</span>
    <span data-anchor-target="#yellow" class="nav-link">Yellow</span>
    <span data-anchor-target="#blue" class="nav-link">Blue</span>
    <span data-anchor-target="#orange" class="nav-link">Orange</span>
  </div>
</nav>

<!-- Sections -->
<section id="pink" class="section-resource is--pink">
  <div class="section-resource__inner">
    <p class="section-resource__inner-p">#pink</p>
  </div>
</section>
<section id="yellow" class="section-resource is--yellow">
  <div class="section-resource__inner">
    <p class="section-resource__inner-p">#yellow</p>
  </div>
</section>
<section id="blue" class="section-resource is--blue">
  <div class="section-resource__inner">
    <p class="section-resource__inner-p">#blue</p>
  </div>
</section>
<section id="orange" class="section-resource is--orange">
  <div class="section-resource__inner">
    <p class="section-resource__inner-p">#orange</p>
  </div>
</section>
styles.css
css
.section-resource {
  justify-content: center;
  align-items: center;
  height: 100vh;
  display: flex;
}

.section-resource__inner {
  background-color: #131313;
  border: 1px solid #1f1f1f;
  border-radius: 2em;
  justify-content: center;
  align-items: center;
  width: 80%;
  height: 60%;
  padding-left: 5%;
  padding-right: 5%;
  display: flex;
  position: relative;
}

.section-resource.is--pink   { color: #ff98a1; }
.section-resource.is--yellow { color: #ffee98; }
.section-resource.is--blue   { color: #98caff; text-align: center; }
.section-resource.is--orange { color: #ff4c24; }

.section-resource__inner-p {
  text-align: center;
  font-size: 5vw;
}

.nav {
  z-index: 999;
  pointer-events: none;
  justify-content: center;
  align-items: center;
  width: 100%;
  padding-top: 2vw;
  padding-bottom: 2vw;
  display: flex;
  position: fixed;
  bottom: 0;
  left: 0;
}

.nav-link {
  -webkit-user-select: none;
  user-select: none;
  padding-top: 1vw;
  padding-bottom: 1vw;
  font-size: 2vw;
  transition-property: opacity;
  transition-duration: .2s;
  transition-timing-function: ease;
}

.nav-link:hover {
  opacity: .5;
}

.nav__inner {
  gap: 2vw;
  pointer-events: auto;
  background-color: #1f1f1f;
  border-radius: 30vw;
  justify-content: center;
  align-items: center;
  padding-left: 2vw;
  padding-right: 2vw;
  display: flex;
}
script.js
javascript
// Lenis
const lenis = new Lenis({
  autoRaf: true,
});

// Scroll-To Anchor Lenis
function initScrollToAnchorLenis() {
  document.querySelectorAll("[data-anchor-target]").forEach(element => {
    element.addEventListener("click", function () {
      const targetScrollToAnchorLenis = this.getAttribute("data-anchor-target");

      lenis.scrollTo(targetScrollToAnchorLenis, {
        easing: (x) => (x < 0.5 ? 8 * x * x * x * x : 1 - Math.pow(-2 * x + 2, 4) / 2),
        duration: 1.2,
        offset: 0 // Option to create an offset when there is a fixed navigation
      });
    });
  });
}

// Initialize Scroll-To Anchor Lenis
document.addEventListener('DOMContentLoaded', () => {
  initScrollToAnchorLenis();
});

Guide

data-anchor-target

Add to any clickable element (link, button, span, etc.) with a value matching the target section's id including the # prefix — e.g. data-anchor-target="#orange" to scroll to <section id="orange">.

Target Sections

The element you want to scroll to needs a plain id attribute without the # — e.g. id="orange". Any element on the page can be a scroll target, not just sections.

Easing & Duration

The scrollTo call uses a custom quartic ease-in-out function and a 1.2s duration. Adjust duration to taste. Replace the easing function with any custom curve or use a named Lenis easing string.

Offset

The offset option (default 0) shifts the final scroll position by a fixed pixel amount. Set it to a negative value (e.g. -80) to account for a fixed navbar so the section heading is not hidden behind it.

Lenis Setup

This component requires Lenis to be initialised on the page. See the Lenis Smooth Scroll Setup component for the full initialisation pattern including GSAP ScrollTrigger integration.