npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

sectify

v1.0.0

Published

Section-based scroll navigation with snap transitions (lightweight, framework-agnostic)

Readme

Sectify

Framework-agnostic full-page section navigation for modern websites and apps.

Sectify provides:

  • Wheel, keyboard, and touch/swipe navigation
  • Vertical (y) and horizontal (x) modes
  • Programmatic controls (next, prev, goTo)
  • Active section state + classes
  • Optional URL hash/anchor deep linking
  • Optional navigation dots
  • Scrollable-content aware behavior (lets inner scroll containers scroll naturally)
  • Responsive layout updates with robust cleanup (update, destroy)

Installation

npm install sectify

Quick Start

<div id="app">
  <section class="page" data-sectify-anchor="home">Home</section>
  <section class="page" data-sectify-anchor="features">Features</section>
  <section class="page" data-sectify-anchor="pricing">Pricing</section>
</div>
import { createFullpage } from "sectify";

const root = document.getElementById("app");
if (!root) throw new Error("Missing #app");

const sectify = createFullpage(root, {
  selectors: { section: ".page" },
  useUrlHash: true,
  navigation: { enabled: true },
});

createSectify is exported as an alias of createFullpage.

You can also inspect the canonical defaults:

import { SECTIFY_DEFAULT_OPTIONS } from "sectify";

API

createFullpage(root, options?)

  • root: root container that contains sections (and optional header/footer)
  • options: optional configuration object
  • returns SectifyInstance

Options

type Axis = "y" | "x";

type SectifyDirection = "up" | "down" | "left" | "right" | "none";

interface ChangeEventPayload {
  index: number;
  previousIndex: number;
  direction: SectifyDirection;
  anchor?: string;
  previousAnchor?: string;
}

interface SectifyOptions {
  scrollingSpeed?: number; // alias of duration
  duration?: number;
  easing?: string;
  autoScrolling?: boolean;
  wheel?: boolean;
  keyboardScrolling?: boolean; // alias of keyboard
  keyboard?: boolean;
  touchMouseDragging?: boolean; // alias of touch
  dragAndMove?: boolean; // alias of touch
  touch?: boolean;
  loop?: boolean;
  startIndex?: number;
  axis?: Axis;

  // Optional static layout blocks inside root
  // In non-overlay mode, footer is treated as an extra snap step after the last section.
  footerSizeRatio?: number; // default: 0.3 (30% of visible area)
  footerOverlayOnLastSection?: boolean; // default: false
  selectors?: {
    section?: string;
    header?: string;
    footer?: string;
  };

  // Input / behavior controls
  ignoreWheelOn?: string;
  normalScrollElements?: string; // alias of ignoreWheelOn
  scrollOverflow?: boolean; // alias of allowScrollWithinSections
  allowScrollWithinSections?: boolean;
  scrollableSelector?: string;
  wheelMinDelta?: number;
  wheelCooldown?: number;
  touchSensitivity?: number; // alias of touchMinSwipe
  touchMinSwipe?: number;
  lockBodyScroll?: boolean;

  // Section state
  activeClass?: string;
  anchors?: string[];
  lockAnchors?: boolean;
  useUrlHash?: boolean;
  animateAnchor?: boolean;
  recordHistory?: boolean;

  // Optional dots UI
  navigation?: {
    enabled?: boolean;
    container?: HTMLElement | string;
    clickable?: boolean;
    itemClass?: string;
    activeClass?: string;
  };

  // Callbacks similar to onLeave / afterLoad
  afterRender?: () => void;
  afterResize?: () => void;
  onLeave?: (payload: ChangeEventPayload) => void;
  afterLoad?: (payload: ChangeEventPayload) => void;
}

Default behavior highlights

  • duration: 700
  • scrollingSpeed: 700 (alias of duration)
  • easing: "cubic-bezier(0.2, 0.8, 0.2, 1)"
  • autoScrolling: true
  • axis: "y"
  • loop: false
  • allowScrollWithinSections: true
  • scrollOverflow: true (alias of allowScrollWithinSections)
  • scrollableSelector: "[data-sectify-scroll], .sectify-scrollable"
  • wheelMinDelta: 10
  • wheelCooldown: 0.85 (multiplied by duration)
  • touchMinSwipe: 50
  • touchSensitivity: 50 (alias of touchMinSwipe)
  • activeClass: "sectify-active"
  • lockAnchors: false
  • useUrlHash: false
  • animateAnchor: true
  • recordHistory: false
  • lockBodyScroll: true

Instance

interface SectifyInstance {
  next(): void;
  prev(): void;
  moveSectionDown(): void;
  moveSectionUp(): void;
  goTo(indexOrAnchor: number | string): void;
  moveTo(indexOrAnchor: number | string): void;
  silentMoveTo(indexOrAnchor: number | string): void;
  setAllowScrolling(allowed: boolean): void;
  setKeyboardScrolling(allowed: boolean): void;
  update(): void;
  reBuild(): void;
  destroy(): void;
  on(event: "change" | "leave" | "load", cb: (payload: ChangeEventPayload) => void): () => void;
  readonly index: number;
}

reBuild() is an alias of update().

Events and Callbacks

Sectify provides both callback options and event subscriptions:

  • onLeave callback and leave event
  • afterLoad callback and load event
  • change event (fires on index changes)
const off = sectify.on("change", ({ previousIndex, index, direction, anchor }) => {
  console.log({ previousIndex, index, direction, anchor });
});

Anchors and Deep Linking

Use any of:

  • anchors: [...] in options
  • data-sectify-anchor on sections
  • section id

Then enable hash syncing:

createFullpage(root, {
  selectors: { section: ".page" },
  useUrlHash: true,
});

You can navigate programmatically by index or anchor:

sectify.goTo(2);
sectify.goTo("pricing");

Scrollable Content Inside Sections

Sectify lets inner containers consume scroll before changing section when:

  • allowScrollWithinSections is true (default), and
  • target is scrollable (native overflow or matching scrollableSelector)

Example:

<section class="page">
  <div class="sectify-scrollable" style="max-height: 60vh; overflow: auto;">
    <!-- long content -->
  </div>
</section>

Navigation Dots

createFullpage(root, {
  navigation: {
    enabled: true,
    clickable: true,
    itemClass: "my-dot",
    activeClass: "is-active",
    // container: "#my-nav" // optional
  },
});

Sectify renders dot buttons with data-sectify-dot attributes and toggles active state automatically.

Header/Footer Pattern

You can keep a static header/footer inside the root while sections move within the track.

When footerOverlayOnLastSection is false (default), the footer behaves like an extra snap step:

  • Reaching the last section and scrolling forward once more moves to the footer step.
  • Footer height is controlled by footerSizeRatio (for example 0.3 = 30% of visible area).
  • Scrolling back up returns to the last section.
  • No fade/reveal delay is required; it behaves like continuous section movement.

When footerOverlayOnLastSection is true, the footer is layered above the last section instead of acting as a separate step.

createFullpage(root, {
  selectors: {
    header: ".site-header",
    section: ".page",
    footer: ".site-footer",
  },
  footerSizeRatio: 0.3,
  footerOverlayOnLastSection: false,
});

Example: footer as a 30% final step

createFullpage(root, {
  selectors: {
    section: ".page",
    footer: ".site-footer",
  },
  footerSizeRatio: 0.3,
  footerOverlayOnLastSection: false, // extra snap step after last section
});

Updating and Destroying

  • Call update() after adding/removing sections or significant layout changes.
  • Call destroy() to remove listeners, unwrap the internal track, and restore inline styles.

TypeScript

Type declarations are bundled in dist and exported via package exports.

License

MIT