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

fluid-tabs

v0.1.2

Published

A lightweight, zero-dependency tabs library with smooth animated indicators, swipe support, and multiple visual styles.

Downloads

509

Readme

fluid-tabs

A lightweight, zero-dependency tabs library with a smooth animated indicator, drag/wheel/swipe scrolling, and multiple visual styles.

npm version jsDelivr License: MIT

Features

  • No dependencies
  • Three visual styles out of the box: underline, classic tabs, and a sliding segmented control
  • Animated indicator that slides between tabs with an elastic stretch, and a spring "tease" on hover
  • Animated content panel that cross-fades and resizes between tabs
  • Too many tabs? They scroll horizontally (drag, wheel, or swipe) with momentum and edge fades. Opt into wrapping instead.
  • Swipe left/right on the content to change tabs on touch
  • Interruptible: click another tab mid-animation and the indicator redirects, no waiting
  • Detached tabs: the bar and its panels don't have to be siblings
  • Fires a tab-changed event, and works with plain .click()
  • Themeable with a handful of CSS variables

Quick Start

Install via npm

npm install fluid-tabs
import "fluid-tabs/styles.css"
import "fluid-tabs"

Or use via CDN

https://www.jsdelivr.com/package/npm/fluid-tabs

Add tabs to your HTML

<div class="tab-bar">
  <button class="tab-bar-button active" data-tab="overview">Overview</button>
  <button class="tab-bar-button" data-tab="details">Details</button>
  <button class="tab-bar-button" data-tab="reviews">Reviews</button>
</div>
<div class="tab-contents">
  <div class="tab-content active" data-tab="overview">Overview content</div>
  <div class="tab-content" data-tab="details">Details content</div>
  <div class="tab-content" data-tab="reviews">Reviews content</div>
</div>

Every .tab-bar on the page is initialised automatically on load - no setup or config required.

  • A .tab-bar holds the .tab-bar-buttons.
  • The matching .tab-contents holds the .tab-content panels.
  • Buttons and panels are paired by data-tab - clicking a button shows the panel with the same value.
  • Mark the starting button and panel with active.

The content panel is optional. A .tab-bar on its own still animates the indicator and fires tab-changed - useful as a filter/segmented control.

Styles

Set a tab-style-* class on both the .tab-bar and its matching .tab-contents. If you forget it on the panel, the library copies the bar's style onto it as a fallback.

| Class | Style | |---|---| | (none) / tab-style-buttons | Underline - text tabs with a sliding underline. The default. | | tab-style-tabs | Classic tabs - filled tabs joined to a bordered content panel. | | tab-style-slide | Slide - a pill-shaped segmented control with a sliding knob. |

<div class="tab-bar tab-style-slide"> … </div>
<div class="tab-contents tab-style-slide"> … </div>

Linking the content

There are three ways a bar finds its panels.

Adjacent (default)

Put the .tab-contents immediately after the .tab-bar:

<div class="tab-bar"> … </div>
<div class="tab-contents"> … </div>

Detached

If the bar and panels can't be siblings, give the bar an id and point the contents at it with data-tab-bar:

<div class="tab-bar" id="account"> … </div>

<p>…anything in between…</p>

<div class="tab-contents" data-tab-bar="account"> … </div>

Standalone

A .tab-bar with no matching .tab-contents just tracks the active button and fires tab-changed.

Options

Behavioural options are set as data attributes on the .tab-bar. Presence is enough - the value is ignored.

| Attribute | Description | |---|---| | data-tab-wrap | Wrap to multiple rows instead of scrolling when the tabs don't fit. | | data-tab-no-swipe | Disable swipe-to-change on the content panel. |

<div class="tab-bar" data-tab-wrap> … </div>

Scrolling & wrapping

When there are more tabs than fit, the bar scrolls horizontally by default. You can:

  • Drag it with the mouse or finger (with flick momentum)
  • Wheel over it
  • Swipe the content panel to step between tabs

A soft fade appears on whichever edge has hidden tabs, and selecting a tab scrolls it into view. Wheel scrolling only kicks in when a gesture starts over the bar, so scrolling the page past it isn't hijacked.

Add data-tab-wrap to wrap onto multiple rows instead of scrolling.

Events

The bar dispatches a tab-changed event when the active tab changes. event.detail is the new tab's data-tab:

document.querySelector(".tab-bar").addEventListener("tab-changed", e => {
  console.log("switched to", e.detail)
})

Programmatic control

Switch tabs by clicking a button - the library listens for normal clicks:

const bar = document.querySelector(".tab-bar")
bar.querySelector('.tab-bar-button[data-tab="reviews"]').click()

Each initialised bar also exposes an update() method that re-snaps the indicator and edge fades. It's called automatically on resize; call it yourself if you change the layout in a way a ResizeObserver won't catch:

bar.update()

Initialising dynamically-added tabs

Every .tab-bar present on load is initialised automatically. If you add tab bars later, call FluidTabs.init() to pick up any that aren't initialised yet. It's idempotent - already-initialised bars are skipped. Pass a root element to limit the scan:

FluidTabs.init()           // scan the whole document
FluidTabs.init(sectionEl)  // scan within an element

Theming

The library ships the structural and default styling it needs. Tweak the look with these CSS variables (shown with their defaults):

| Variable | Default | Description | |---|---|---| | --tab-transition-duration | .25s | Duration of every transition (indicator, panel, height). | | --tab-transition-easing | cubic-bezier(.4, 0, .2, 1) | Easing for every transition. | | --tab-slide-lag | .333 | Trailing-edge lag of the indicator slide, as a fraction of the duration. 0 removes the elastic stretch. | | --tab-tease-x | 10px | How far the indicator stretches toward a hovered tab. | | --tab-fade | 60px | Width of the scroll edge fade. |

.tab-bar {
  --tab-transition-duration: .2s;
  --tab-fade: 40px;
}

Colours, radii, and spacing are plain values in the stylesheet - override the .tab-bar, .tab-bar-button, .tab-contents, and .tab-content rules to restyle.

Classes

These are added by the library and can be targeted with CSS:

| Class | When applied | |---|---| | initialised | On each .tab-bar once set up. | | active | On the current .tab-bar-button and its .tab-content. | | tab-bar-active | The indicator element, appended inside each .tab-bar. | | tab-bar-wrap | When data-tab-wrap is set. | | tab-bar-dragging | While the bar is being drag-scrolled. | | tab-bar-fade-start / tab-bar-fade-end | When there are hidden tabs off the start / end edge. | | transitioning | On .tab-contents while a panel change is animating. |

License

MIT © Ewan Howell