Full Page Scroll — Snap Sections + Dot Nav HTML CSS JS

Full Page Scroll · Layouts · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Native CSS scroll-snap
scroll-snap-type + 100vh sections snap smoothly with no scroll hijacking.
IntersectionObserver nav
The active dot tracks whichever section is in view, however the user scrolled.
Input-agnostic tracking
Wheel, trackpad, touch, keyboard, and dot clicks all update the indicator correctly.
Smooth dot navigation
Clicking a dot scrolls to that section via scrollIntoView.
Keyboard navigation
Arrow and Page keys move one section at a time, clamped to the ends.
Hidden but scrollable
The scrollbar is hidden for the clean look while staying fully scrollable.
Auto-adapting
Dots and observers derive from the sections, so adding panels just works.
No library
Replaces fullPage.js with native snap and vanilla JS — zero dependencies.

About this UI Snippet

Full Page Scroll — Snapping Full-Viewport Sections with a Dot Navigator

Screenshot of the Full Page Scroll snippet rendered live

The full-page scroll layout (the fullPage.js look) presents content as a series of full-viewport panels that snap into place one at a time as you scroll, with a dot navigator down the side. This snippet builds it with native CSS scroll-snap and vanilla JavaScript — no fullPage.js, no library — getting the smooth, jank-free behaviour that older scroll-hijacking scripts struggled with.

Native scroll-snap, not scroll hijacking

The scroller is a container with scroll-snap-type: y mandatory, and each 100vh section has scroll-snap-align: start. The browser handles snapping each section to the top as you scroll, swipe, or fling — using native, momentum-aware scrolling. This is the modern replacement for the old approach of intercepting wheel events and animating scrollTop yourself, which fought the browser and felt laggy. Native snap is smoother, respects the OS scroll physics, and needs almost no code.

A dot navigator that tracks the active section

Down the side, a dot navigator shows which section you are on. Rather than computing scroll positions, an IntersectionObserver watches the sections (scoped to the scroller, with a 0.6 threshold) and marks the dot for whichever section is mostly in view. This works no matter how the user got there — wheel, trackpad, touch swipe, keyboard, or clicking a dot — because it observes the result, not the input. Observing visibility is the robust way to sync nav state with scroll.

Click and keyboard navigation

Clicking a dot scrolls smoothly to that section with scrollIntoView({ behavior: 'smooth' }), and Arrow/Page keys move one section at a time (clamped to the ends). Both go through one goTo(), and because the active dot is driven by the observer, the indicator updates correctly when the smooth scroll lands — there is no separate state to keep in sync. Supporting keyboard and dots alongside natural scrolling makes the layout accessible and navigable every way users expect.

Hidden scrollbar, full control kept

The scrollbar is hidden for the clean full-page look, but the container stays fully scrollable by every means and the dots provide an always-visible position indicator and jump control — so hiding the bar never traps the user. Each section is a flex-centred panel with a gradient background and large type, the typical full-page presentation style.

Drop-in and adaptable

Add or remove <section> panels and the dots and observers adapt automatically. Put any content in the sections, swap the gradients for images, and adjust the threshold. It is a clear, dependency-free reference for CSS scroll-snap full-page layouts and IntersectionObserver-driven navigation. The 0.6 intersection threshold is also a useful knob: a lower value flips the active dot as soon as a section starts entering view, while a higher one (as used here) waits until a section dominates the viewport, which avoids the indicator flickering between two dots while the user is mid-scroll past the boundary between them.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't have to reason through the observer-versus-scroll-position tradeoff by hand. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why the active dot is driven by an IntersectionObserver watching each section rather than by computing scrollTop directly, and what the 0.6 threshold specifically controls about when a dot flips versus a lower or higher value. The same assistant can help optimize it — ask whether observing all sections against the scroller root has any cost worth caring about with many more sections, or whether the keydown handler should ignore Arrow keys when focus is inside a text input on a real page. It's also useful for extending the layout: have it add horizontal sub-sections within one vertical panel, a progress indicator that shows scroll fraction within the current section, or URL hash syncing so a direct link opens on the right section. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a "full page scroll" layout with snapping full-viewport sections and a dot navigator, in plain HTML, CSS, and JavaScript using native CSS scroll-snap and IntersectionObserver — no scroll-hijacking libraries, no manual scrollTop animation.

Requirements:
- A scrollable container with scroll-snap-type set to the y axis and mandatory strictness, containing several full-viewport-height sections, each with scroll-snap-align set to start, and each themed with its own background.
- Hide the scrollbar visually (cross-browser) while keeping the container fully scrollable by wheel, trackpad, touch, and keyboard — never disable native scrolling.
- Generate a fixed-position dot navigator with one button per section, built dynamically in JavaScript from however many sections exist rather than hard-coded to a specific count.
- Use a single IntersectionObserver scoped to the scroll container (not the viewport) watching every section, with a threshold high enough that a dot only becomes active once its section dominates the visible area (avoiding the indicator flickering between two dots while scrolling past the boundary between them). Whichever section intersects should mark its corresponding dot active and unmark all others — this must be the only mechanism that sets the active dot, regardless of whether the user scrolled, swiped, clicked a dot, or used the keyboard.
- Clicking a dot must smooth-scroll its section into view (via scrollIntoView with smooth behavior) rather than directly toggling the active class itself — the observer should be what updates the indicator once the scroll lands.
- Add a keydown listener so ArrowDown/PageDown and ArrowUp/PageUp move exactly one section forward or backward (clamped so it can't scroll past the first or last section), reusing the same navigation function the dots use.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSFour full-viewport sections render with a dot navigator on the right.
  2. 2
    Scroll or swipeEach section snaps into place; the active dot tracks which one is in view.
  3. 3
    Use the dotsClick any dot to smooth-scroll to that section.
  4. 4
    Use the keyboardArrow Up/Down or Page Up/Down move one section at a time.
  5. 5
    Add or remove sectionsAdd a <section class="fps-sec"> — the dots and observers adapt automatically.
  6. 6
    Put in your contentReplace the section content and swap gradients for images or your own backgrounds.

Real-world uses

Common Use Cases

Landing and product pages
Present a story as snapping panels — pair with a startup hero as the first section.
Portfolios and showcases
One project per full-screen section, alongside a portfolio hero.
Onboarding and intros
Walk through value props one screen at a time.
Pitch decks on the web
A slide-like scrolling presentation.
Editorial and campaign sites
Immersive full-bleed sections with motion.
Learning scroll-snap
A reference for native snap and observer nav — compare with a scroll snap gallery for horizontal.

Got questions?

Frequently Asked Questions

fullPage.js and similar libraries hijack the wheel/touch events and animate the scroll position themselves, which adds weight and can feel laggy or fight the browser. This snippet uses native CSS scroll-snap (scroll-snap-type: y mandatory with scroll-snap-align on each 100vh section), so the browser does the snapping with real momentum scrolling — smoother, lighter, and no library. The JS only handles the dot indicator and keyboard/click navigation.

An IntersectionObserver scoped to the scroll container watches each section with a 0.6 threshold; when a section becomes mostly visible, its dot is marked active. This observes the outcome rather than the input, so it stays correct whether the user scrolled with the wheel, swiped on touch, pressed a key, or clicked a dot — there is no scroll-position math to drift.

Yes. A dot click calls scrollIntoView({ behavior: 'smooth' }) to scroll to that section; it does not set the active dot directly. When the smooth scroll lands and the section crosses the observer threshold, the observer marks it active. Because one mechanism (the observer) owns the active state, the indicator is always correct regardless of how navigation was triggered.

Hiding the scrollbar (scrollbar-width: none and the WebKit pseudo-element) gives the clean, app-like full-page look. It is purely cosmetic — the container remains fully scrollable by wheel, trackpad, touch, and keyboard, and the always-visible dot navigator provides position feedback and a jump control. So the user is never trapped or left without a way to navigate.

Render the sections from an array and keep the active index in state. Set up the IntersectionObserver in a useEffect (React), onMounted/onUnmounted (Vue), or ngAfterViewInit/ngOnDestroy (Angular), disconnecting on cleanup, and add the keydown listener similarly. The scroll-snap CSS and scrollIntoView navigation are framework-agnostic — only the observer lifecycle and active state move into the framework.