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

jb-tab

v0.1.2

Published

Accessible tabs web components for the JB design system

Readme

jb-tab

GitHub license NPM Version GitHub Created At

Accessible, composable tabs for the JB Design System. The package contains four independently importable custom elements and matching React wrappers.

  • Supports horizontal and vertical tab lists.
  • Supports keyboard navigation and disabled triggers.
  • Supports controlled, default, and nullable selection.
  • Supports multiple panels for one trigger value.
  • Provides size and color variants with CSS customization.
  • Supports both RTL and LTR pages.

When to use

Use jb-tab to organize related content into views where one view is active at a time. It is a good fit for settings sections, dashboards, profile pages, and compact content switchers. See the basic tabs demo for the default interaction.

Use another navigation pattern when each item should open a separate page or URL. Tabs represent views within the current context rather than application-level navigation.

Demo

Using with JS frameworks

Other integrations: Angular · Vue · Nuxt · Svelte · Next.js · Lit

Installation

npm install jb-tab

Importing the root package registers all four custom elements:

import "jb-tab";

Web component usage

<jb-tab value="home">
  <aside>
    <jb-tab-list aria-label="Account sections">
      <jb-tab-trigger value="home">Home</jb-tab-trigger>
      <jb-tab-trigger value="about">About</jb-tab-trigger>
      <jb-tab-trigger value="user">User</jb-tab-trigger>
    </jb-tab-list>
  </aside>

  <main>
    <jb-tab-content value="home">Home content</jb-tab-content>
    <jb-tab-content value="about">About content</jb-tab-content>
    <jb-tab-content value="user">User content</jb-tab-content>
  </main>
</jb-tab>

The list and panels may be nested in ordinary light-DOM elements. Triggers must be direct children of jb-tab-list. Nested jb-tab instances manage only their own nearest descendants.

Selection

jb-tab is the only selection owner. Its public properties are:

| Property | Attribute | Type | Behavior | | --- | --- | --- | --- | | value | value | string \| null | Current selected value. | | defaultValue | default-value | string \| null | Initial value used when value is absent. | | nullable | nullable | boolean | Keeps the initial value null instead of selecting the first enabled trigger. |

Child element API

| Element | Property/attribute | Type | Description | | --- | --- | --- | --- | | jb-tab-list | orientation | 'horizontal' \| 'vertical' | Controls layout and arrow-key direction. Defaults to horizontal. | | jb-tab-list | size | 'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' | Applies one size to the list's direct triggers. Defaults to md. | | jb-tab-trigger | value | string | Required value used to select matching panels. | | jb-tab-trigger | disabled | boolean | Prevents selection and removes the trigger from keyboard navigation. | | jb-tab-trigger | color | variant or CSS color | Sets the moving indicator color for this trigger. | | jb-tab-content | value | string | Shows the panel when its value matches the owning tab. | | jb-tab-content | hidden | boolean | Exposes and optionally overrides panel visibility. |

When nullable is absent and neither value is supplied, the first enabled trigger is selected. Setting value to null on a nullable tab hides every panel. Trigger values must be unique. Multiple panels may use the same value; every matching panel is shown.

Selecting a trigger dispatches a bubbling, composed select event from jb-tab-trigger. When the value changes, jb-tab dispatches a bubbling, composed change event.

tab.addEventListener("change", event => {
  console.log(event.detail.value);
  console.log(event.detail.previousValue);
  console.log(event.detail.trigger);
});

React

import { useState } from "react";
import { JBTab } from "jb-tab/react";
import { JBTabList } from "jb-tab/list/react";
import { JBTabTrigger } from "jb-tab/trigger/react";
import { JBTabContent } from "jb-tab/content/react";

export function AccountTabs() {
  const [value, setValue] = useState("home");

  return (
    <JBTab value={value} onChange={event => setValue(event.detail.value)}>
      <JBTabList aria-label="Account sections">
        <JBTabTrigger value="home">Home</JBTabTrigger>
        <JBTabTrigger value="about">About</JBTabTrigger>
      </JBTabList>
      <JBTabContent value="home">Home content</JBTabContent>
      <JBTabContent value="about">About content</JBTabContent>
    </JBTab>
  );
}

The React event props are onSelect on JBTabTrigger and onChange on JBTab.

Keyboard behavior

  • ArrowLeft and ArrowRight navigate horizontal lists.
  • ArrowUp and ArrowDown navigate vertical lists.
  • Home and End move to the first and last enabled trigger.
  • Enter and Space select a focused trigger.
  • Disabled triggers are skipped.

Arrow navigation automatically activates the newly focused trigger.

Panel display and animation

Inactive jb-tab-content hosts have hidden set to true and use display: none; visible hosts have hidden set to false and use display: block. Visibility is exposed through the standard hidden property and attribute and through :state(hidden). Changing a panel's hidden property does not change the selected trigger, so a consumer may explicitly show an additional panel when needed.

jb-tab-content {
  opacity: 0;
  transition: opacity 200ms ease, display 200ms allow-discrete;
}

jb-tab-content:not([hidden]) {
  display: grid;
  opacity: 1;
}

@starting-style {
  jb-tab-content:not([hidden]) {
    opacity: 0;
  }
}

display: none removes inactive panels from layout and painting. It does not defer custom-element construction or JavaScript execution inside a panel.

List styling

jb-tab-list exposes list and indicator CSS parts. Each trigger exposes a content part.

| CSS variable | Purpose | | --- | --- | | --jb-tab-list-background | List background. | | --jb-tab-list-padding | Uniform space between the list background and its triggers. | | --jb-tab-trigger-border-radius | Shared trigger and indicator radius; also used to derive the outer background radius. | | --jb-tab-list-indicator-color | Default indicator color. | | --jb-tab-list-indicator-duration | Indicator transition duration. | | --jb-tab-list-indicator-easing | Indicator transition easing. | | --jb-tab-list-indicator-shadow | Indicator shadow. | | --jb-tab-trigger-indicator-color | Per-trigger indicator color override. | | --jb-tab-trigger-color | Trigger text color. | | --jb-tab-trigger-color-selected | Selected trigger text color. | | --jb-tab-trigger-padding | Trigger padding. |

Each trigger can set the indicator color with a design-system variant or any valid CSS color:

<jb-tab-trigger value="home" color="primary">Home</jb-tab-trigger>
<jb-tab-trigger value="about" color="#efefef">About</jb-tab-trigger>

Supported base variants are primary, secondary, positive, danger, warning, light, and dark. Accent colors also provide primary-subtle, secondary-subtle, positive-subtle, danger-subtle, and warning-subtle. Base colors use inverse selected text while subtle and light colors use primary selected text, with no delayed color update. Arbitrary CSS colors automatically choose primary or inverse text. --jb-tab-trigger-indicator-color and --jb-tab-trigger-color-selected remain available as explicit per-trigger overrides.

Set size once on jb-tab-list so every direct trigger uses the same xs, sm, md, lg, or xl preset. The default is md:

<jb-tab-list size="xl">
  <jb-tab-trigger value="first">First</jb-tab-trigger>
  <jb-tab-trigger value="second">Second</jb-tab-trigger>
</jb-tab-list>

The outer list radius is derived as calc(var(--tab-trigger-border-radius) + var(--padding)), keeping the background curve concentric with the shared trigger and indicator curve at every size.

Use orientation="vertical" on jb-tab-list for a vertical list. The default orientation is horizontal.

Accessibility notes

Give each jb-tab-list an accessible name with aria-label or aria-labelledby. The components provide the tablist, tab, and tabpanel roles, connect triggers and panels through ARIA attributes, manage roving focus, and skip disabled triggers. The keyboard demo exercises the supported navigation.

Related docs

AI agent notes

  • Import jb-tab once to register all four custom elements, or import the individual jb-tab/list, jb-tab/trigger, and jb-tab/content entry points.
  • Keep jb-tab-trigger elements as direct children of jb-tab-list; panels may be nested elsewhere inside the owning jb-tab.
  • Treat jb-tab.value as the selection source of truth. Trigger selected state and panel visibility are synchronized from it.
  • Use unique trigger values. Multiple jb-tab-content elements may intentionally share one value.
  • Listen for change on jb-tab for selection changes and select on jb-tab-trigger for the initiating interaction.
  • Use aria-label or aria-labelledby on every jb-tab-list.