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

@julescript/nested-tab-bar

v0.2.1

Published

A tab bar whose selected tab can grow a panel, joined to its own tab by a stem, so the parent-to-child relationship is visible instead of learned.

Readme

@julescript/nested-tab-bar

A tab bar whose selected tab can grow a panel, joined to its own tab by a stem. The stem is the whole idea: it says which tab the panel belongs to without a word of explanation.

A five tab bar with a two page panel growing out of the middle tab, joined to it by a stem

The bar is a plain rounded rectangle that never changes shape. The branch, panel and stem and pill in one continuous outline, is drawn over it. Nothing is ever carved out of the bar.

The three stages: the plain bar, the branch on its own, and the two assembled

npm install @julescript/nested-tab-bar

React 18 or 19, as a peer dependency. No other runtime dependencies.

Use

import { NestedTabBar } from "@julescript/nested-tab-bar";
import "@julescript/nested-tab-bar/styles.css";

<NestedTabBar
  items={[
    { key: "home", children: <Tab icon={<Home />} label="HOME" /> },
    { key: "search", children: <Tab icon={<Search />} label="SEARCH" /> },
    { key: "library", children: <Tab icon={<Layers />} label="LIBRARY" /> },
  ]}
  activeKey="library"
  placement="bottom"
  panel={{
    anchorKey: "library",
    label: "Library pages",
    content: <Pages />,
  }}
/>;

The component owns layout, geometry, the silhouette and the motion. It does not own your tabs. Every item's children is whatever element you want, a link, a button, a logo, so you keep your own router, your own icons and your own type.

What goes in the panel

The panel is a slot. Two shapes of content are common and the width follows from which one you use.

Three bars: one panel holding two pages, one holding a search input, one holding a dropdown

A control wants the room, so leave width at its default and the panel spans the bar.

panel={{ anchorKey: "home", label: "Search", content: <SearchInput /> }}

A short list of sibling pages wants to sit over its own tab, so give it a fixed width. It is centred on the anchor tab, then clamped so it stays inside the bar. Near an end the panel stops moving and the stem sits off centre within it, which is right: the stem points at its tab, not at the middle of itself.

panel={{ anchorKey: "library", label: "Library pages", width: 186, content: <Pages /> }}

Two or three pages, no more. Past three the labels fall below reading size, so scroll the panel instead, or give the tab a real screen.

Four bars: no panel, the panel clamped at the first tab, clamped at the last, and a page with a count

Props

NestedTabBar

| Prop | Type | Default | | |---|---|---|---| | items | NestedTabBarItem[] | required | The tabs, in order | | activeKey | string \| null | null | Which tab reads as current. Null highlights nothing | | placement | "top" \| "bottom" | "top" | A bottom bar grows its panel upward | | panel | NestedTabBarPanel \| null | null | Omit and the bar is just a bar | | barHeight | number | 66 | | | barRadius | number | 20 | | | padding | number | 6 | Between the bar's edge and its tabs | | tabRadius | number | 8 | | | panelMetrics | Partial<PanelMetrics> | | Overrides any geometry number | | label | string | | Names the bar for assistive technology | | className | string | | The root carries this and nothing else |

NestedTabBarItem

| Field | Type | | |---|---|---| | key | string | Unique, and what activeKey and anchorKey refer to | | children | ReactNode | The whole interactive element | | width | number | Fixed slot width. Omit and the slot shares the leftover space | | selectable | boolean | False for anything that never reads as current |

NestedTabBarDivider

A rule between two groups. Pass it in items like anything else.

| Field | Type | Default | | |---|---|---|---| | key | string | required | | | divider | true | required | | | width | number | 11 | The slot, including the space either side of the rule | | height | number | 28 | The rule itself |

NestedTabBarPanel

| Field | Type | | |---|---|---| | anchorKey | string | The item the panel grows out of | | content | ReactNode | | | width | number \| "fill" | Defaults to "fill" | | height | number | Defaults to 40 | | label | string | Names the panel for assistive technology |

More than one kind of item

A bar does not have to be five equal tabs. Items can be any width, some of them need never read as current, and a divider separates one group from another. That is enough to build a toolbar: navigation at the start, a rule, then whatever else belongs in the corner.

A bar with four tabs, a divider, a wider logo tile and an accent action, shown closed and with a panel open

<NestedTabBar
  className="my-bar"          // position it yourself: fixed, in a header, anywhere
  activeKey={active}
  items={[
    { key: "now", width: 66, children: <Tab .../> },
    { key: "shows", width: 66, children: <Tab .../> },
    { key: "news", width: 66, children: <Tab .../> },
    { key: "rule", divider: true },
    { key: "brand", width: 80, selectable: false, children: <Logo /> },
    { key: "get", width: 66, selectable: false, children: <AccentButton /> },
  ]}
/>

An item with selectable: false still sits in the bar and is still clickable. It just never takes the highlight and never carries the panel, which is what you want for a logo, an external link, or a call to action that is not one of the tabs.

The component never positions itself. It fills the width it is given, so where the bar lives is entirely up to the className you pass: fixed to the bottom of a phone screen, or in the corner of a desktop header.

Theming

Six custom properties, set on the bar or on any ancestor. There are no colours in the component itself, so it takes your design system rather than bringing one.

| Property | | Default | |---|---|---| | --nb-surface | The bar | rgba(20,20,24,.9) | | --nb-branch | Panel, stem and pill, one shade up from the bar | rgba(31,31,35,.95) | | --nb-line | Hairline around both | rgba(255,255,255,.11) | | --nb-divider | The rule between groups | falls back to --nb-line | | --nb-active | The highlight when no panel is open | rgba(255,255,255,.1) | | --nb-shadow | A filter, since the silhouette is drawn rather than boxed | drop-shadow(0 14px 34px rgba(0,0,0,.55)) |

.my-bar {
  --nb-surface: rgb(18 18 20 / 0.92);
  --nb-branch: rgb(30 30 34 / 0.96);
}

Every property has the same value baked in as a fallback, so importing styles.css is genuinely optional: skip it and you get the defaults above, import it to have them as real declarations you can inspect and override in one place. Setting some and not others is safe.

Motion

The highlight follows the active tab with a critically damped spring, so moving between tabs is a glide rather than a jump. The first render is not animated: a page that loads with the third tab active shows it highlighted rather than sliding in from the first. Anyone who has asked for reduced motion gets the target immediately.

The spring runs on requestAnimationFrame and stops itself once it settles, so an idle bar costs nothing.

Right to left

Nothing to configure. Slot positions are measured from the container's own leading edge, so a bar inside dir="rtl" mirrors along with everything else.

Geometry

Every path is generated by pure functions you can use directly, which is how the component is tested and how a non-React renderer would be built.

import { barPath, fusedPath, panelBox, panelContentBox } from "@julescript/nested-tab-bar";

The drawing is always generated for a bar at the top with its panel below. A bottom bar renders the same path mirrored vertically, so there is one set of maths to reason about and one set of tests.

The rule that matters most

The stem must have a straight run between its two fillets. If the fillets meet with no straight section the tangents disagree at the join, and a visible corner appears where the curve should be continuous. That single detail is the difference between the shape reading as one body and reading as three parts glued together. The generator shrinks the fillet before it lets the straight run reach zero, so this holds at any gap.

Guidelines

The panel connected to its tab, beside the same panel floating free of it

Contributing

See CONTRIBUTING.md. The short version: the geometry is pure and fully tested, nothing product specific may enter the package, and every change lands here rather than being patched around in a consumer.