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

@kawaiininja/layouts

v1.2.0

Published

High-performance, premium mobile-first layouts for the Onyx Framework, featuring gesture-driven navigation, radial quick actions, and integrated theme support.

Readme

@kawaiininja/layouts

High-performance, premium responsive layouts for the Onyx Framework. Designed with a focus on fluid gestures, adaptive sidebar navigation, and state-of-the-art aesthetics that switch seamlessly between Mobile and PC.

📖 Read the Full Developer Guide | 🎨 Explore Examples


✨ Features

💻 Responsive Engine (Adaptive Mode)

  • Layout Swapping: Automatically switches between OnyxMobileLayout and OnyxPcLayout at the 1024px breakpoint.
  • Adaptive Navigation: Mobile uses a bottom rail and popup drawer; PC uses a fixed sidebar and hover-based quick menus.
  • Single Config: Define your navigation once—the framework handles the physics and layout changes for all screen sizes.

🖐️ Gesture & Mouse Interactions

  • Horizontal Tab Swiping: Seamlessly transition between sub-tabs with natural swipe gestures. Built on a custom gesture engine for zero-lag response.
  • Vertical Pull-to-Refresh: Granular refresh logic at the global, tab, or individual sub-tab level with integrated spring physics and haptic feedback support.
  • Async State Management: Refresh handlers now support Promises, allowing the framework to automatically manage the loading spinner lifecycle during async operations.

🔘 Radial Quick Actions (Long-Press)

  • Zero-Friction Access: Long-press any navigation rail item to reveal a radial selection menu.
  • Directional Selection: Swipe towards an action item to select and execute it instantly without lifting your finger.
  • Programmable Logic: Quick actions can trigger internal state changes, external redirects, or open custom layout drawers.

🖼️ Modular Layout Engine

  • Dynamic Headers: Auto-syncing header titles with current navigation state. Supports custom right-aligned actions (buttons, icons, etc.).
  • Flexible Side Drawer: Fully configurable drawer menu with support for deep-linking (target specific tabs/sub-tabs) and custom action links.
  • Panel System: Register any number of custom drawers (Bottom Sheets) that can be triggered from anywhere in the app.

🛣️ Integrated Routing Support

  • Full URL Sync: Sync layout state with react-router-dom or any history-based router.
  • Controlled Navigation: Pass activeTab and activeSubTab from your routing hook to drive the UI.
  • Path Mapping: Define path strings for tabs and sub-tabs to automatically trigger the onNavigate callback.

🎨 Design System & Theme

  • Premium Glassmorphism: High-quality backdrop blurs and subtle gradients for a modern, high-end feel.
  • Integrated Theme Support: Robust dark/light mode toggle with local storage persistence and system-aware defaults.

🚀 Installation

npm install @kawaiininja/layouts

Note: This package requires framer-motion, lucide-react, and react >= 18.0.0.


📦 Usage

Integrated Routing Support (Responsive)

import { OnyxResponsiveLayout } from "@kawaiininja/layouts";
import { BrowserRouter, useNavigate, useLocation } from "react-router-dom";
import { Home, Grid, PenTool } from "lucide-react";

const App = () => {
  const navigate = useNavigate();
  const location = useLocation();
  const currentPath = location.pathname;

  const tabs = [
    {
      id: "home",
      label: "Home",
      icon: Home,
      path: "/home",
      subTabs: [
        {
          label: "Feed",
          icon: Grid,
          path: "feed",
          view: () => <div>Main Feed</div>,
        },
      ],
      quickActions: [
        {
          label: "New Post",
          icon: PenTool,
          onClick: ({ openDrawer }) => openDrawer("editor"),
        },
      ],
    },
  ];

  // Derive active states from URL
  const activeTabId = tabs.find((t) => currentPath.startsWith(t.path))?.id;

  const [isRefreshing, setIsRefreshing] = useState(false);

  return (
    <OnyxResponsiveLayout
      tabs={tabs}
      user={{ name: "Alex", handle: "@alex", avatar: "..." }}
      activeTab={activeTabId}
      onNavigate={(path) => navigate(path)}
      isRefreshing={isRefreshing}
      drawers={{
        editor: MyEditorPanel,
      }}
    />
  );
};

⚠️ Critical Integration Checklist

Do not skip these steps, especially for Native/Capacitor builds:

  1. Wrap in <Suspense>: If your views are lazy-loaded, this is mandatory.
  2. Use 100dvh: Desktop 100vh causes "bounce" and sizing bugs on mobile. Always use 100dvh (Dynamic Viewport Height) on your main app container.
  3. Sync isRefreshing: Ensure your refresh handler always toggles the boolean, or the spinner will never disappear (though the framework has a 12s safety fallback).
  4. Absolute Parent Paths: Ensure tab paths start with / to avoid navigation state confusion.

🧭 Mastering Navigation & Routing

To ensure your application feels premium and works correctly on native devices, follow these core principles for pathing and state.

1. The Pathing Hierarchy

The framework intelligently joins paths. To maintain a clean structure, follow these rules:

  • Parent Tabs: Always use Absolute Paths (e.g., /settings).
  • Sub-Tabs: Use Relative Paths (e.g., security).
    • Resulting Path: /settings/security
  • Default Views: Use an empty string "" for the path of your primary sub-tab.
    • Resulting Path: /settings (shows the first tab by default).

2. Deep-Linking Safety

When using a router (like React Router), your activeTab logic should handle sub-paths. Instead of a direct equality check, use .startsWith():

// ✅ CORRECT: Supports sub-paths like /settings/security
const activeTabId = tabs.find((t) => location.pathname.startsWith(t.path))?.id;

3. Native Stability (Suspense)

If your view components are Lazy Loaded (using React.lazy), you must wrap the OnyxMobileLayout in a <Suspense> boundary. Without this, the app may crash on native devices when transitioning to a new tab.

<Suspense fallback={<MyLoader />}>
  <OnyxResponsiveLayout ... />
</Suspense>

4. Sidemenu Deep-Linking (Global Navigation)

The sidebar (PC) and Drawer (Mobile) can navigate to specific tabs and sub-tabs using the drawerItems prop. This is the preferred way to create a "Global Menu" that crosses category boundaries.

const drawerItems = [
  {
    label: "Inventory Hub",
    icon: Activity,
    targetTab: "search", // The ID of the parent tab
    targetSubTab: "Inventory", // The Label of the specific sub-tab
  },
  {
    label: "Account Settings",
    icon: User,
    targetTab: "settings", // Defaults to first sub-tab of settings
  },
];

// ... inside render
<OnyxResponsiveLayout tabs={tabs} drawerItems={drawerItems} />;

The framework will automatically calculate the correct path from your TabConfig and trigger onNavigate.


🔄 Data Refresh & Safety

The framework includes a sophisticated gesture-based refresh system with built-in safety mechanisms.

1. Granular Hierarchy

You can implement refresh logic at three distinct levels:

  • Global Refresh: Pass onRefresh at the root. Best for app-wide syncs.
  • Tab-Specific Refresh: Defined in TabConfig. Refreshes all data within a main category.
  • Sub-Tab Refresh (Granular): Defined in SubTabConfig. The most efficient pattern; refreshes only the specific view the user is looking at.

2. Async Handler Support

Handers can now return a Promise. If a promise is returned, the layout will stay in its refresh state (and keep spinning) until the promise resolves or rejects.

onRefresh: async () => {
  await syncMyData(); // Framework stays in loading state automatically
};

3. Built-in 12s Safety Cutoff

To prevent your UI from getting "stuck" due to forgotten state updates or hanging API calls, the framework includes an automatic safety timeout:

  • If isRefreshing stays true for more than 12 seconds, the layout will automatically reset the pull-to-refresh UI to its idle state.
  • This ensures that your users are never locked out of the application by a loading spinner.

🛠️ Configuration API

OnyxMobileLayoutProps & OnyxPcLayoutProps

Both versions of the layout use almost identical properties. For automatic detection, use OnyxResponsiveLayout.

| Prop | Type | Description | | :------------- | :---------------------------- | :------------------------------------------------------------ | | tabs | TabConfig[] | Required. The main navigation stack (Rails on the right). | | user | UserConfig | Required. Data for the side drawer's profile section. | | drawers | Record<string, Component> | Dictionary of custom panel components. | | activeTab | string | Controlled ID of the active tab. | | activeSubTab | string | Controlled label of the active sub-tab. | | onNavigate | (path: string) => void | Triggered on path-based navigation (Rails, Tabs, Drawers). | | drawerItems | DrawerItemConfig[] | Custom links for the side menu. | | onSignOut | () => void | Triggered when the "Sign Out" button is clicked. | | onRefresh | () => void \| Promise<void> | Global pull-to-refresh handler. | | isRefreshing | boolean | Global refresh state. | | initialTab | string | Default: home. | | rightAction | ReactNode | Global header action element. |

TabConfig

| Field | Type | Description | | :------------- | :---------------------------- | :------------------------------ | | id | string | Unique identifier. | | label | string | Rail label. | | Field | Type | Description | | :------------- | :---------------------------- | :------------------------------ | | id | string | Unique identifier. | | label | string | Rail label. | | icon | LucideIcon | Rail icon. | | path | string | Absolute base path for routing. | | navTitle | string | Optional header title. | | subTabs | SubTabConfig[] | Nested horizontal navigation. | | quickActions | QuickActionConfig[] | Radial menu items. | | onRefresh | () => void \| Promise<void> | Tab-specific refresh handler. | | isRefreshing | boolean | Tab-specific loading state. | | rightAction | ReactNode | Tab-specific header action. |

SubTabConfig

| Field | Type | Description | | :------------- | :---------------------------- | :------------------------------------- | | label | string | Pill label. | | icon | LucideIcon | Pill icon. | | path | string | Relative or absolute path for routing. | | view | Component | The component to render. | | onRefresh | () => void \| Promise<void> | Sub-tab specific refresh handler. | | isRefreshing | boolean | Sub-tab specific loading state. |

QuickActionConfig & DrawerItemConfig

| Prop | Type | Description | | :------------- | :-------------- | :----------------------------------------------- | | label | string | Item text. | | icon | LucideIcon | Item icon. | | targetTab | string | Target Tab ID for navigation. | | targetSubTab | string | Target Sub-tab Label for deep-linking. | | onClick | (ctx) => void | Context: { openDrawer: (id: string) => void }. |


🌊 Gesture Interactions Guide

Pull-to-Refresh

  • Threshold: 60px pull triggers the refresh.
  • Implementation: Provide onRefresh and sync isRefreshing.

Quick Action Menu (Radial Selection)

  1. Trigger: Long-press (400ms) any Navigation Rail item.
  2. Selection: Drag towards an action to highlight.
  3. Execution: Release to trigger.

🎨 Design System (CSS Tokens)

:root {
  --bg-main: 0, 0, 0;
  --bg-surface: 18, 18, 18;
  --bg-elevated: 28, 28, 28;
  --color-accent: 99, 102, 241;
  --color-secondary: 236, 72, 153;
  --text-primary: 255, 255, 255;
  --text-muted: 156, 163, 175;
  --color-border-subtle: 255, 255, 255, 0.1;
}

⚖️ License

MIT © Tristan