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

@ismail-elkorchi/ui-shell

v0.1.2

Published

Token-driven Light DOM shell components for application layouts built with Lit.

Downloads

306

Readme

@ismail-elkorchi/ui-shell

Token-driven shell components (activity bar, sidebars, status bar, and an optional frame layout) for Light DOM composition. They depend on @ismail-elkorchi/ui-primitives for controls, expose slots + parts, and read all visual values from @ismail-elkorchi/ui-tokens CSS variables.

Layout layer

  • Regions: left rail (activity-bar), primary sidebar, main content, optional secondary sidebar, and status bar.
  • uik-shell-layout stitches the regions together and tags them with data-region attributes to keep the layout contract visible in the DOM.
  • Shell components expose only UI surface/state; business logic should live in the host app.
  • Contract: Shell components use ui-primitives strictly via their public API (attributes/props). Visual styling comes from --uik-* custom properties (no framework utility classes).

Landmarks & labels (Accessibility contract)

  • uik-shell-layout renders a role="region" container; override its label via aria-label or aria-labelledby on the host.
  • uik-shell-activity-bar renders an <aside> landmark and forwards aria-label/aria-labelledby to the internal nav rail; default label is "Activity bar".
  • uik-shell-sidebar and uik-shell-secondary-sidebar render <aside> landmarks; default labels come from the heading or fall back to "Sidebar"/"Secondary sidebar".
  • uik-shell-status-bar uses role="status" with aria-live="polite" for status messages.
  • Provide a semantic <main> element in the main-content slot and label additional landmarks as needed in host markup.

Focus + roving focus

  • Shell navigation surfaces delegate roving focus to primitives (uik-nav-rail, uik-tree-view) and do not add competing keyboard handlers.
  • Follow the Focus + Roving Focus contract in @ismail-elkorchi/ui-primitives when composing activity bars or navigation trees.

Overlay close semantics

  • Overlay-like shells emit close events with detail.reason aligned to primitives: escape | outside | programmatic | toggle.
  • uik-shell-secondary-sidebar captures the previously focused element on open and restores focus on close (unless a focus-return-target is provided).

Using the components

import { html } from "lit";
import "@ismail-elkorchi/ui-primitives/register";
import "@ismail-elkorchi/ui-shell/register";
import type { UikShellActivityBarItem } from "@ismail-elkorchi/ui-shell/activity-bar";

const activityItems: UikShellActivityBarItem[] = [
  {
    id: "explorer",
    label: "Explorer",
    icon: "M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z",
  },
  {
    id: "search",
    label: "Search",
    icon: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z",
  },
];

html`
  <uik-shell-layout ?isSecondarySidebarVisible=${true}>
    <uik-shell-activity-bar
      slot="activity-bar"
      .items=${activityItems}
      .activeId=${"explorer"}
      @activity-bar-select=${(e: CustomEvent<{ id: string }>) =>
        console.log(e.detail.id)}
    >
    </uik-shell-activity-bar>
    <uik-shell-sidebar slot="primary-sidebar" heading="Explorer">
      <uik-button slot="actions" variant="ghost" size="icon">…</uik-button>
      <div style="font-size: var(--uik-typography-font-size-2);">
        <!-- put your tree view or navigation here -->
      </div>
    </uik-shell-sidebar>
    <main
      slot="main-content"
      style="flex: 1 1 auto; min-height: var(--uik-space-0);"
    >
      Your editor or subviews
    </main>
    <uik-shell-secondary-sidebar
      slot="secondary-sidebar"
      .isOpen=${true}
      heading="AI Assistant"
      @secondary-sidebar-close=${() => console.log("close secondary")}
    >
      <p
        style="
          font-size: var(--uik-typography-font-size-2);
          color: oklch(var(--uik-text-muted));
        "
      >
        Auxiliary tools live here.
      </p>
    </uik-shell-secondary-sidebar>
    <uik-shell-status-bar
      slot="status-bar"
      message="Ready"
      tone="info"
      meta="3 files selected"
    ></uik-shell-status-bar>
  </uik-shell-layout>
`;

Component notes

  • uik-shell-layout: named slots activity-bar, primary-sidebar, main-content, secondary-sidebar, status-bar.
  • uik-shell-activity-bar: accepts .items (id/label/icon/path) and emits activity-bar-select; optional footer slot; delegates roving focus to uik-nav-rail (set aria-label if you need a custom name).
  • uik-shell-sidebar: slot="actions" for header actions, default slot for body, optional slot="footer"; isBodyPadded/isBodyScrollable toggle spacing + scroll.
  • uik-shell-secondary-sidebar: controlled via .isOpen; optional focus-return-target (selector or element) to restore focus on close; Escape and the close button emit secondary-sidebar-close (detail.reason is escape | toggle).
  • uik-shell-status-bar: .message + .tone colorize the left side; meta string (outline badge) or slot="meta" for custom content; optional slot="actions".
  • Use @ismail-elkorchi/ui-primitives/uik-nav or @ismail-elkorchi/ui-primitives/uik-tree-view for sidebar navigation content.

Custom properties

  • Activity bar: --uik-component-shell-activity-bar-bg, --uik-component-shell-activity-bar-fg, --uik-component-shell-activity-bar-width, --uik-component-shell-activity-bar-item-size, --uik-component-shell-activity-bar-item-icon-size, --uik-component-shell-activity-bar-item-indicator-bg, --uik-component-shell-activity-bar-item-indicator-radius, --uik-component-shell-activity-bar-item-indicator-width.
  • Sidebar: --uik-component-shell-sidebar-bg, --uik-component-shell-sidebar-fg, --uik-component-shell-sidebar-width.
  • Secondary sidebar: --uik-component-shell-secondary-sidebar-bg, --uik-component-shell-secondary-sidebar-width.
  • Status bar: --uik-component-shell-status-bar-bg, --uik-component-shell-status-bar-fg, --uik-component-shell-status-bar-height.
  • Shared: --uik-component-shell-divider-color, --uik-component-shell-scrollbar-track, --uik-component-shell-scrollbar-thumb.

Tokens & theming

Load tokens once and set theme/density attributes on a shared container (often :root):

@import "@ismail-elkorchi/ui-tokens/index.css";
<html data-uik-theme="light" data-uik-density="comfortable">
  ...
</html>

Routing store

A tiny EventTarget-based router lives in @ismail-elkorchi/ui-shell/router. It is framework-light, keeps state in memory (no history), and is meant for desktop flows that only need named views and optional subviews.

import {
  createUikShellRouter,
  UIK_SHELL_NAVIGATION_EVENT,
  type UikShellNavigationDetail,
} from "@ismail-elkorchi/ui-shell/router";

const routes = [
  {
    id: "explorer",
    label: "Explorer",
    subviews: ["code", "prompt", "apply"],
    defaultSubview: "code",
  },
  { id: "search", label: "Search" },
  { id: "settings", label: "Settings" },
] as const;

export const shellRouter = createUikShellRouter({
  routes,
  initialView: "explorer",
  initialSubview: "code",
});

// React to navigation anywhere in the app
const unsubscribe = shellRouter.subscribe(({ view, subview }) => {
  console.log("Current location", view, subview);
});

// Wire Lit components through events
html`
  <uik-shell-activity-bar
    .items=${routes.map((r) => ({ id: r.id, label: r.label ?? r.id }))}
    .activeId=${shellRouter.current.view}
    @activity-bar-select=${(e: CustomEvent<{ id: string }>) =>
      shellRouter.navigate(e.detail.id)}
  >
  </uik-shell-activity-bar>

  <editor-area
    .activeSubview=${shellRouter.current.subview ?? "code"}
    @subview-change=${(e: CustomEvent<{ subview: string }>) =>
      shellRouter.navigate(shellRouter.current.view, e.detail.subview)}
  >
  </editor-area>
`;

// Listen to the low-level navigation event if you prefer EventTarget
window.addEventListener(UIK_SHELL_NAVIGATION_EVENT, (event: Event) => {
  const detail = (event as CustomEvent<UikShellNavigationDetail>).detail;
  console.log(detail.from, detail.to, detail.route);
});
  • Routes are simple {id, label?, subviews?, defaultSubview?} objects.
  • navigate(view, subview?) resolves subviews per route (keeping the last used subview for that route).
  • subscribe immediately fires with the current location and returns an unsubscribe function.