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

tiny-drawer

v1.2.0

Published

A production-ready, framework-agnostic drawer component for vanilla JavaScript and TypeScript.

Downloads

462

Readme

tiny-drawer

tiny-drawer is a production-ready drawer component for vanilla JavaScript and TypeScript. It supports top, right, bottom, and left placements, works inside any parent container, ships multiple bundle formats, and includes an optional tiny-engine-core adapter.

Features

  • Framework-agnostic API
  • TypeScript-first with bundled declarations
  • ESM, CJS, and UMD builds
  • CSS and minified CSS output
  • Draggable resize with pointer events and requestAnimationFrame
  • Optional scroll-to-full-screen behavior with configurable gap
  • Backdrop, close button, and escape handling
  • Container-scoped mounting by default
  • Optional tiny-engine-core integration

Install Dependencies

npm

npm install tiny-drawer

yarn

yarn add tiny-drawer

pnpm

pnpm add tiny-drawer

CDN Usage

<link
  rel="stylesheet"
  href="https://unpkg.com/tiny-drawer/dist/tiny-drawer.min.css"
/>
<script src="https://unpkg.com/tiny-drawer/dist/tiny-drawer.umd.js"></script>
<script>
  const drawer = new TinyDrawer.TinyDrawer("#app", {
    content: "<div>Hello from CDN</div>"
  });
  drawer.open();
</script>

Vanilla JS Usage

import { TinyDrawer } from "tiny-drawer";
import "tiny-drawer/dist/tiny-drawer.css";

const drawer = new TinyDrawer("#app", {
  position: "bottom",
  size: "40%",
  onScrollFull: {
    backDrop: true,
    drawer: true,
    scrollTarget: ".custom-content"
  },
  fullScreenGap: 24,
  minSize: "20%",
  maxSize: "90%",
  appendToBody: true,
  themePrefix: "acme",
  content: `
    <div class="custom-content">
      <h2>Drawer Title</h2>
      <p>This is drawer content.</p>
    </div>
  `
});

document.querySelector("#openDrawer").addEventListener("click", () => {
  drawer.open();
});

TypeScript Usage

import { TinyDrawer, type TinyDrawerOptions } from "tiny-drawer";

const options: TinyDrawerOptions = {
  position: "right",
  size: "40%",
  content: "<div>Typed content</div>"
};

const drawer = new TinyDrawer("#panel", options);
drawer.open();

SSR and Next.js

tiny-drawer is safe to import in SSR environments because it does not touch the DOM until the browser is available.

For React and Next.js, create or open the drawer from a client-side effect:

"use client";

import { useEffect, useRef } from "react";
import { TinyDrawer } from "tiny-drawer";

export default function Page() {
  const drawerRef = useRef<TinyDrawer | null>(null);

  useEffect(() => {
    drawerRef.current = new TinyDrawer("#app", {
      appendToBody: true,
      content: "<div>Drawer content</div>"
    });

    return () => {
      drawerRef.current?.destroy();
      drawerRef.current = null;
    };
  }, []);

  return <button onClick={() => drawerRef.current?.open()}>Open drawer</button>;
}

This keeps hydration safe and avoids server-side DOM access. If you need the drawer mounted inside a specific element, pass parentContainer: "#your-parent" on the client.

API

Constructor

new TinyDrawer(container: string | HTMLElement, options?: TinyDrawerOptions)

Methods

| Method | Description | | ----------------------- | ----------------------------------------------------- | | open() | Opens the drawer and focuses it. | | close() | Closes the drawer and restores focus. | | toggle() | Toggles between open and closed states. | | destroy() | Removes DOM, listeners, and dispatches destroy hooks. | | setContent(content) | Replaces drawer content with HTML or an element. | | setPosition(position) | Changes drawer placement. | | setSize(size) | Applies a new size value. | | getSize() | Returns the current size string. | | isOpen() | Returns current open state. |

Options

| Option | Type | Default | Notes | | ------------------- | ---------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------- | | position | "top" \| "right" \| "bottom" \| "left" | "bottom" | Defines attachment edge. | | size | string \| number | "40%" | Default open size. | | minSize | string \| number | "20%" | Minimum drag size. | | maxSize | string \| number | "90%" | Maximum drag size. | | content | string \| HTMLElement | undefined | HTML string, selector, plain text, or element. | | parentContainer | string \| HTMLElement | undefined | Optional mount target for td-host when you want to mount into a specific parent element. | | themePrefix | string | undefined | Adds prefixed theme classes such as acme-drawer and acme-drawer--active. | | backdrop | boolean | true | Enables backdrop layer. | | closeButton | boolean | true | Renders the close button. | | closeOnBackdrop | boolean | true | Closes when the backdrop itself is clicked. | | outSideClickClose | boolean | false | Closes when the user clicks outside the mounted parent/container area. | | closeOnEscape | boolean | true | Enables keyboard escape closing. | | draggable | boolean | true | Enables resize handle and drag. | | onScrollFull | { backDrop, drawer, scrollTarget } | false | backDrop and drawer choose which area can trigger fullscreen behavior, and scrollTarget overrides the watched element. | | fullScreenGap | string \| number | 0 | Gap preserved from the opposite edge while scroll-full is active. Numbers are treated as pixels. | | animation | boolean | true | Disables transitions when false. | | appendToBody | boolean | false | Mounts the drawer to document.body with fixed positioning. | | lastDragLocation | boolean | true | Keeps the last dragged size after close when enabled. | | className | string | undefined | Extra class appended to .td-drawer. | | zIndex | number | 1050 | Base layer stack. | | ariaLabel | string | "Drawer" | Accessible label for dialog role. | | onOpen | (instance) => void | undefined | Open callback. | | onClose | (instance) => void | undefined | Close callback. | | onDragStart | (instance) => void | undefined | Drag start callback. | | onDrag | (size, instance) => void | undefined | Drag callback. | | onDragEnd | (size, instance) => void | undefined | Drag end callback. | | onDestroy | (instance) => void | undefined | Destroy callback. |

Events

The drawer dispatches custom events from the original parent container:

| Event | Detail | | ------------------------ | -------------------- | | tiny-drawer:open | { instance } | | tiny-drawer:close | { instance } | | tiny-drawer:drag-start | { instance } | | tiny-drawer:drag | { instance, size } | | tiny-drawer:drag-end | { instance, size } | | tiny-drawer:destroy | { instance } |

Example:

const container = document.querySelector("#app");

container.addEventListener("tiny-drawer:open", (event) => {
  console.log(event.detail.instance);
});

Position Behavior

  • bottom: attached to the bottom edge, dragging upward increases height.
  • top: attached to the top edge, dragging downward increases height.
  • left: attached to the left edge, dragging right increases width.
  • right: attached to the right edge, dragging left increases width.

Draggable Behavior

  • Uses pointer events for mouse, touch, and pen support.
  • Adds .td-drawer--dragging during active resize.
  • Applies requestAnimationFrame updates for smoother rendering.
  • Clamps size between minSize and maxSize.
  • Accepts px, %, vh, vw, and numeric pixel values.
  • Percentage sizes are resolved from the mounted parent container when embedded, and from the viewport when body-mounted.

Scroll Full Behavior

  • Enable onScrollFull to control scroll-full behavior with one object config.
  • onScrollFull.drawer means scrolling inside the drawer content can trigger fullscreen.
  • onScrollFull.backDrop means scrolling the parent/background area can trigger fullscreen.
  • If both are true, both areas can trigger fullscreen.
  • If backDrop is true, the backdrop also gets td-backdrop--scroll-full while fullscreen is active.
  • The default backdrop target is the mounted parent container for embedded drawers and the page scroll element for body-mounted drawers.
  • The default drawer target is the built-in .td-drawer__inner content area.
  • Use onScrollFull.scrollTarget with an HTMLElement or selector string to watch a custom scroll container.
  • Use fullScreenGap to preserve space from the opposite edge. It accepts px, %, vh, vw, numbers, and negative values.
  • Scroll-full sizing is resolved in pixels from the mounted parent or viewport, which keeps the transition smoother in smaller containers.
  • When the scroll target returns to the top threshold, the drawer restores its original configured size.
  • onScrollFull: true still works and uses the default scroll target automatically.

Example:

const drawer = new TinyDrawer("#drawer", {
  position: "bottom",
  size: "40%",
  onScrollFull: {
    backDrop: true,
    drawer: true,
    scrollTarget: ".drawer-content"
  },
  fullScreenGap: 24,
  content: `
    <div class="drawer-content" style="height: 100%; overflow: auto;">
      <div style="height: 1200px;">Long content</div>
    </div>
  `
});

Accessibility

  • role="dialog"
  • aria-modal
  • aria-hidden toggling
  • Configurable aria-label
  • Escape-key close support
  • Focus moves into the drawer on open and returns on close

tiny-engine-core Adapter

import { createTinyEngineDrawer } from "tiny-drawer";

const plugin = createTinyEngineDrawer(core, {
  backdrop: true
});

plugin.install();

const drawer = core.drawer?.("#app", {
  content: "<div>Installed from tiny-engine-core</div>"
});

tiny-engine-core is kept as an optional peer dependency, so the base package stays framework-agnostic.

Demo Scenarios

  • Bottom drawer with default 40% height
  • Right drawer with 40% width
  • Drawer inside a parent card
  • Dynamic content update
  • Draggable bottom drawer

Open demo/index.html after building to try them locally.

Build Outputs

  • dist/tiny-drawer.esm.js
  • dist/tiny-drawer.cjs.js
  • dist/tiny-drawer.umd.js
  • dist/tiny-drawer.css
  • dist/tiny-drawer.min.css
  • Type declarations in dist/*.d.ts

Development

npm install
npm run build
npm run lint
npm run test

Browser Support

Modern browsers that support:

  • ES2019+
  • Pointer Events
  • CSS transforms and transitions
  • Custom Events

For older browsers, add the relevant polyfills at the application level.