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

@favish/staffbase-drawer

v2.0.21

Published

A reusable drawer component for Staffbase applications

Readme

@favish/staffbase-drawer

A reusable drawer component for React applications with automatic CSS isolation for multi-widget environments.

📦 Installation

pnpm add @favish/staffbase-drawer
# or
npm install @favish/staffbase-drawer

🚀 Quick Start

1. Add the Vite Plugin

The plugin automatically generates unique CSS class prefixes to prevent collisions between multiple widgets:

// vite.config.ts
import react from '@vitejs/plugin-react'
import { staffbaseDrawer } from '@favish/staffbase-drawer/vite-plugin'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    react(),
    staffbaseDrawer() // 🎉 Auto-generates unique prefix!
  ]
})

2. Use the Component (Shadow DOM - Required)

// main.tsx
import { useState } from 'react'
import { StaffbaseDrawerShadowRoot } from '@favish/staffbase-drawer'

function App() {
  const [isOpen, setIsOpen] = useState(false)

  return (
    <>
      <button onClick={() => setIsOpen(true)}>Open Drawer</button>

      <StaffbaseDrawerShadowRoot
        open={isOpen}
        onClose={() => setIsOpen(false)}
        variant="drawer"
      >
        <h1>Drawer Content</h1>
        <p>Your content goes here...</p>
      </StaffbaseDrawerShadowRoot>
    </>
  )
}

Why this matters: the drawer (and its portal content) is rendered inside a per-instance ShadowRoot, and the compiled CSS is injected into that ShadowRoot only. This prevents global CSS (e.g. Tailwind preflight) from affecting the drawer and prevents the drawer’s CSS from leaking into the host page.

By default, the component also clones the host page stylesheets into the ShadowRoot so embedded content (e.g., Staffbase article styling) renders correctly. You can disable that with includeHostStyles={false} if needed.

If you only want specific host styles copied (e.g., widget content styles), use hostStyleSelectors:

<StaffbaseDrawer
  open={isOpen}
  onClose={onClose}
  hostStyleSelectors={[
    'link[href*="staffbase"]',
    'style[data-widget-styles]',
  ]}
>
  <ArticleContent />
</StaffbaseDrawer>

3. Modal Variant + Fully Isolated Shadow DOM

Use variant="modal" to render a centered dialog instead of a right-side drawer. Use modalWidth to set the panel width (e.g. "90vw" or "min(720px, 90vw)"); when omitted, it defaults to min(720px, 90vw).

Use isolated to run in a fully isolated Shadow DOM mode:

  • host styles are not copied
  • hostStyleSelectors is ignored
  • a :host reset is applied to prevent host-page style inheritance
<StaffbaseDrawerShadowRoot
  open={isOpen}
  onClose={() => setIsOpen(false)}
  variant="modal"
  modalWidth="90vw"
  isolated
>
  <h2>Modal Title</h2>
  <p>Modal content in a fully isolated shadow environment.</p>
</StaffbaseDrawerShadowRoot>

Note: StaffbaseDrawer is now an alias of StaffbaseDrawerShadowRoot to make Shadow DOM the only supported rendering path.

That's it! The Vite plugin automatically:

  • Generates a unique prefix based on your package.json name
  • If you build multiple widgets from the same repo, also incorporates build config (e.g. outDir/entry) to avoid collisions
  • Configures JS to use the same prefix at runtime
  • Logs the prefix during build: [staffbase-drawer] Using prefix: "d8f3a2"

📋 Props

All props supported by StaffbaseDrawerShadowRoot (and the StaffbaseDrawer alias):

| Prop | Type | Required | Default | Description | | -------------------- | ------------------------ | -------- | -------------------- | --------------------------------------------------------------------------- | | open | boolean | Yes | — | Controls whether the drawer is visible. | | onClose | () => void | Yes | — | Callback when the user requests close (button, overlay, ESC, swipe). | | children | ReactNode | No | — | Content rendered inside the drawer panel. | | onClosed | () => void | No | — | Callback after the close animation has finished. | | lazyRender | boolean | No | false | If true, children are only mounted when the drawer is open. | | variant | 'drawer' \| 'modal' | No | 'drawer' | 'drawer': slide-in from right; 'modal': centered overlay panel. | | modalWidth | string | No | min(720px, 90vw) | When variant="modal", the panel width (e.g. '90vw', 'min(720px, 90vw)'). | | hideCloseButton | boolean | No | false | Hide the close button in the drawer header. | | hideTitle | boolean | No | false | Hide the drawer title / accessibility label. | | additionalCss | string | No | — | Extra CSS text injected into the ShadowRoot (widget-specific styles). | | hostStyleSelectors | string[] | No | — | CSS selectors limiting which host styles are cloned (e.g. ['link[href*="staffbase"]']). | | includeHostStyles | boolean | No | true | Clone host page stylesheets into the ShadowRoot so embedded content can use host styling. | | isolated | boolean | No | false | Fully isolate Shadow DOM: no host styles copied, :host reset applied. | | mode | 'open' \| 'closed' | No | 'open' | Shadow root mode; 'open' is recommended for debugging and portal targeting. |

Note: portalContainer is not part of the public API for StaffbaseDrawerShadowRoot; the component portals into its own ShadowRoot automatically.

🎨 Custom Prefix

If you want to specify your own prefix instead of auto-generating:

staffbaseDrawer({ prefix: 'mybulletins' })

If you build multiple widgets from the same repository/package name, you can also provide a seed:

staffbaseDrawer({ seed: 'alerts-widget' })

⚙️ Manual Configuration (Without Plugin)

If you're not using the Vite plugin, you must configure the JS prefix directly:

// vite.config.ts
import { defineConfig } from 'vite'

const DRAWER_PREFIX = 'mywidget'

export default defineConfig({
  define: {
    __SBAW_PREFIX__: JSON.stringify(DRAWER_PREFIX)
  }
})

⚠️ Critical: Ensure the prefix is unique per widget build to avoid collisions.

✅ Verifying Your Setup

In browser dev tools, CSS selectors should show your unique prefix:

/* ✅ Correct - unique prefix */
[class*='-d8f3a2-overlay'] { ... }

/* ❌ Wrong - default prefix (collision risk) */
[class*='-sbaw-overlay'] { ... }

📦 Package Exports

| Export | Description | | -------------------------------------- | ----------------------------------------------------- | | @favish/staffbase-drawer | Main component (Shadow DOM only) | | @favish/staffbase-drawer/vite-plugin | Vite plugin for auto-prefix |

Note: Deep imports (e.g. @favish/staffbase-drawer/scss or @favish/staffbase-drawer/styles) are intentionally unsupported. Only the top-level package export is supported.

🔧 Exported Utilities

import {
  StaffbaseDrawer,
  StaffbaseDrawerShadowRoot,
  DRAWER_CLASS_PREFIX,    // Current prefix (for debugging)
  DrawerClasses,          // Class name constants
  DrawerCSSVars,          // CSS variable names
  createDrawerSelector    // Generate DOM selectors
} from '@favish/staffbase-drawer'

💻 Local Development

# Clone
git clone https://github.com/favish/staffbase-drawer.git

# Install
pnpm install

# Build
pnpm build

# Dev server
pnpm dev

🛠 Available Scripts

| Script | Description | | ------------------ | -------------------------------------------------- | | pnpm build | Compiles the project | | pnpm dev | Starts development server | | pnpm format | Formats code using Prettier | | pnpm lint | Runs ESLint | | pnpm type-check | Checks TypeScript types | | pnpm publish:patch | Bump patch and publish (use publish:minor/major for other bumps) |

📝 Publishing

Ensure your git working directory is clean (commit or stash all changes) before running publish scripts.

pnpm publish:patch  # Bug fixes (1.0.0 -> 1.0.1)
pnpm publish:minor  # New features (1.0.0 -> 1.1.0)
pnpm publish:major  # Breaking changes (1.0.0 -> 2.0.0)

📝 License

UNLICENSED - © Favish