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

@loomidev/tab

v0.3.0

Published

<loomi-tabs>/<loomi-tab> — tabbed content (simple/system/pills styles).

Readme

@loomidev/tab

<loomi-tabs> builds a heading bar from its <loomi-tab> children and toggles which panel is visible. There is no separate heading/body/content wiring to keep in sync — each <loomi-tab> carries its own heading (label/icon) and its own panel content together, so there's nothing to name-match by hand.

npm install @loomidev/tab lit
import "@loomidev/tab";

Basic Usage

Wrap any number of <loomi-tab> elements in a <loomi-tabs>. The tab marked active is selected by default — it doesn't have to be the first one. If none is marked active, the first non-disabled tab is selected automatically.

<loomi-tabs>
  <loomi-tab label="Profile" active>
    <h3>Account profile</h3>
    <p>Update the public name, team role, and contact email shown across the workspace.</p>
    <loomi-button size="small">Save profile</loomi-button>
  </loomi-tab>
  <loomi-tab label="Security">
    <h3>Security</h3>
    <p>Require two-step verification and review the devices that are currently signed in.</p>
    <loomi-button size="small" type="secondary">Manage devices</loomi-button>
  </loomi-tab>
  <loomi-tab label="Notifications">
    <h3>Notifications</h3>
    <p>Choose which product updates, approvals, and billing alerts should send email.</p>
    <loomi-toggle label="Weekly summary" checked></loomi-toggle>
  </loomi-tab>
</loomi-tabs>

Listen for loomi-tab-change on <loomi-tabs> if you need to react to the switch (e.g. lazy- loading a panel's data):

document.querySelector("loomi-tabs").addEventListener("loomi-tab-change", (e) => {
  console.log(e.detail.label); // the newly active tab's label
});

Different Colors

The active tab's underline (or background, depending on style — see below) uses the default. Set color on <loomi-tabs> to pick a different one; it applies to every child tab.

<loomi-tabs color="error">
  <loomi-tab label="Incidents" active>
    <p>2 open incidents are affecting checkout. The team was paged 6 minutes ago.</p>
  </loomi-tab>
  <loomi-tab label="Resolved">
    <p>14 incidents were resolved this week, with a median time-to-fix of 22 minutes.</p>
  </loomi-tab>
</loomi-tabs>

<loomi-tabs color="success">
  <loomi-tab label="Passing" active>
    <p>All 312 checks are passing on the main branch as of the last deploy.</p>
  </loomi-tab>
  <loomi-tab label="Skipped">
    <p>4 checks are skipped for this branch because they only run on release tags.</p>
  </loomi-tab>
</loomi-tabs>

Common semantic colors: primary secondary info success error warning gray.

Other Tab Styles

<loomi-tabs> comes in three styles, set via tab-style. The default is simple (an underlined heading row, as in every example above).

When the active tab changes, the heading indicator slides to the next tab. In simple style the underline moves; in system and pills styles the selected surface moves.

System Tab Style

A segmented-control look — the active tab gets a raised pill inside a tinted track.

<loomi-tabs tab-style="system">
  <loomi-tab label="Monthly" active>…</loomi-tab>
  <loomi-tab label="Yearly">…</loomi-tab>
</loomi-tabs>

Pills Tab Style

The same segmented-control look as system, with fully rounded tab buttons.

<loomi-tabs tab-style="pills">
  <loomi-tab label="All" active>24 open conversations</loomi-tab>
  <loomi-tab label="Unread">5 conversations need a reply</loomi-tab>
  <loomi-tab label="Archived">128 resolved conversations</loomi-tab>
</loomi-tabs>

With Icons

Set icon on a <loomi-tab> to prefix its heading with an icon from the shared @loomidev/icons registry. Works in any of the three styles.

<loomi-tabs>
  <loomi-tab label="Overview" icon="information-circle" active>
    <p>Your workspace is on the Team plan with 8 of 10 seats in use.</p>
  </loomi-tab>
  <loomi-tab label="Activity" icon="bell-alert">
    <p>Ada invited a new member and Sara updated the billing contact today.</p>
  </loomi-tab>
  <loomi-tab label="Security" icon="lock-closed">
    <p>Two-step verification is required for all admins and enabled for 6 of 8 members.</p>
  </loomi-tab>
</loomi-tabs>

Need an icon that isn't built in? Register your own — no need to fork the registry:

import { registerLoomiIcon } from "@loomidev/icons";
import { svg } from "lit";

registerLoomiIcon("rocket", svg`<path d="…" />`);
<loomi-tab label="Launches" icon="rocket">…</loomi-tab>

Disabled Tabs & Tabs That Navigate

Set disabled to fade out a tab and ignore clicks on it (and skip it during keyboard navigation — see below):

<loomi-tabs>
  <loomi-tab label="Standard Shipping" active>
    <p>Arrives in 5-7 business days. Free on orders over $50.</p>
  </loomi-tab>
  <loomi-tab label="Same-Day Delivery" disabled>
    <p>Not available for this address yet.</p>
  </loomi-tab>
</loomi-tabs>

Set url instead of relying on the built-in panel switching to make a tab behave like a plain link — clicking it navigates via location.href rather than showing a panel:

<loomi-tabs>
  <loomi-tab label="Dashboard" active>
    <p>Quick glance: 1,284 active users, 42 open invites, $12.8k in monthly spend.</p>
  </loomi-tab>
  <loomi-tab label="Full Settings →" url="/settings"></loomi-tab>
</loomi-tabs>

Keyboard Navigation

<loomi-tabs> implements the WAI-ARIA APG "tabs" pattern out of the box: once a tab heading has focus, / (or /) move between tabs and switch the active panel immediately (automatic activation), and Home/End jump to the first/last enabled tab. Disabled tabs are skipped. No setup required — this works the same in every style.

Accessibility

  • See APG link in README.

For the library-wide baseline, see Foundations — Accessibility.

Responsive behavior

For the shared container and viewport rules, see Foundations — Responsive behavior.

Dark mode

For theme activation, token overrides, and contrast guidance, see Foundations — Dark mode.

Attributes

<loomi-tabs>

| Attribute | Default | Description | | ----------- | ------- | ---------------------------------- | | color | primary | Active-tab color. Any loomi color. | | tab-style | simple | simple | system | pills |

<loomi-tab>

| Attribute | Default | Description | | ---------- | --------- | ------------------------------------------------------------------------ | | label | (blank) | Heading text. | | icon | (blank) | Heading icon name (see @loomidev/icons). | | active | false | Selected by default. (boolean) | | disabled | false | Disabled tab — faded out, ignores clicks and keyboard focus. (boolean) | | url | (blank) | Navigate to this URL instead of switching panels. |

Slots

| Slot | Description | | ----------- | ------------------------------------ | | (default) | Content placed inside the component. |

Events

| Event | Description | | ------------------ | --------------------------- | | loomi-tab-change | Fired when the tab changes. |

Full Example

<loomi-tabs color="success" tab-style="pills">
  <loomi-tab label="Overview" icon="information-circle" active>
    <p>Revenue is up 12% this month and all scheduled payouts have cleared.</p>
  </loomi-tab>
  <loomi-tab label="Activity" icon="bell-alert">
    <p>Three payment disputes need a response before Friday.</p>
  </loomi-tab>
  <loomi-tab label="Archived" disabled>
    <p>Nothing archived yet.</p>
  </loomi-tab>
  <loomi-tab label="Full report →" url="/reports/full"></loomi-tab>
</loomi-tabs>

Framework integration

<loomi-tab> and <loomi-tabs> are standard custom elements, so the browser can use them in plain HTML, Blade, React, Vue, Angular, Svelte, Astro, and most other frameworks. The important beginner rule is: install the package, import it once before the tag is rendered, then write the Loomi tag in your template.

Where to run commands

Run install commands from the app where you want to use this component. That means the folder that contains that app's package.json. Do not run these install commands from packages/tab unless you are editing LoomiUI itself.

cd /path/to/your-app
npm install @loomidev/tab lit

If you are contributing to LoomiUI itself, first move to the top-level components folder. That is where the main package.json for all packages lives, and pnpm --filter ... commands should be run from there:

cd /path/to/your-copy-of-loomiui/components
pnpm --filter @loomidev/tab build
pnpm --filter @loomidev/tab typecheck

Choose your framework

Use the CDN version for prototypes, documentation pages, or a quick reproduction. The import map tells the browser where to find Lit, which Loomi components use internally.

<script type="importmap">
  { "imports": { "lit": "https://esm.sh/[email protected]", "lit/": "https://esm.sh/[email protected]/" } }
</script>
<script type="module" src="https://esm.sh/@loomidev/tab"></script>

<loomi-tabs>
  <loomi-tab label="Overview" active>Account summary</loomi-tab>
  <loomi-tab label="Invoices">Recent invoices</loomi-tab>
</loomi-tabs>

In Vite, Webpack, Parcel, Rollup, or a framework build pipeline, install the package and import it once in your main app JavaScript file. After that, you can use the Loomi tag anywhere in your app.

import "@loomidev/tab";

Run the install command from your Laravel project root, then import the component in resources/js/app.js. If your project uses Laravel Vite, npm run dev and npm run build should also be run from the Laravel project root.

cd /path/to/your-laravel-app
npm install @loomidev/tab lit
npm run dev
// resources/js/app.js
import "@loomidev/tab";
<loomi-tabs>
  <loomi-tab label="Overview" active>Account summary</loomi-tab>
  <loomi-tab label="Invoices">Recent invoices</loomi-tab>
</loomi-tabs>

React can render Loomi tags directly. If you are on React 18, or if you need to pass arrays, objects, or functions, use a ref and assign those values after the component mounts.

import "@loomidev/tab";

export function LoomiExample() {
  return (
    <loomi-tabs>
      <loomi-tab label="Overview" active>Account summary</loomi-tab>
      <loomi-tab label="Invoices">Recent invoices</loomi-tab>
    </loomi-tabs>
  );
}

If TypeScript does not recognize the Loomi tag in JSX, add it to your app's JSX type declarations.

Import the package in the component that uses it, or once in your main Vue file. Vue templates can use Loomi tags directly. For arrays, objects, or functions, pass the value as a JavaScript property instead of as plain text.

<script setup>
import "@loomidev/tab";
</script>

<template>
  <loomi-tabs>
    <loomi-tab label="Overview" active>Account summary</loomi-tab>
    <loomi-tab label="Invoices">Recent invoices</loomi-tab>
  </loomi-tabs>
</template>

If Vue warns that the tag is an unknown component, configure compilerOptions.isCustomElement for tags that start with loomi- in your Vite or Vue config.

Import the package once and tell Angular to allow custom HTML tags with CUSTOM_ELEMENTS_SCHEMA. For NgModule apps, add the schema to the module instead of the standalone component.

// app.component.ts
import { CUSTOM_ELEMENTS_SCHEMA, Component } from "@angular/core";
import "@loomidev/tab";

@Component({
  selector: "app-root",
  standalone: true,
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  template: `
    <loomi-tabs>
      <loomi-tab label="Overview" active>Account summary</loomi-tab>
      <loomi-tab label="Invoices">Recent invoices</loomi-tab>
    </loomi-tabs>
  `,
})
export class AppComponent {}

Svelte can import the package inside a component script. Astro can import it in the frontmatter of the page or layout where the tag appears.

<script>
  import "@loomidev/tab";
</script>

<loomi-tabs>
  <loomi-tab label="Overview" active>Account summary</loomi-tab>
  <loomi-tab label="Invoices">Recent invoices</loomi-tab>
</loomi-tabs>
---
import "@loomidev/tab";
---

<loomi-tabs>
  <loomi-tab label="Overview" active>Account summary</loomi-tab>
  <loomi-tab label="Invoices">Recent invoices</loomi-tab>
</loomi-tabs>

Server-side rendering notes

Frameworks such as Next.js, Nuxt, SvelteKit, and Astro sometimes render HTML on the server before browser-only code runs. If your framework complains, move the Loomi import to client-side code. In Next.js, that usually means a component with "use client"; in Nuxt, it often means a .client.ts plugin.

Dependencies

  • @loomidev/core
  • @loomidev/icons