@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
hostStyleSelectorsis ignored- a
:hostreset 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:
StaffbaseDraweris now an alias ofStaffbaseDrawerShadowRootto make Shadow DOM the only supported rendering path.
That's it! The Vite plugin automatically:
- Generates a unique prefix based on your
package.jsonname - 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:
portalContaineris not part of the public API forStaffbaseDrawerShadowRoot; 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/scssor@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
