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

@risklight/modal

v0.1.1

Published

Framework-agnostic modal stack with guards, typed prompts and Vue 3 and React adapters.

Readme

@risklight/modal

npm CI docs license

Modals as a stack, not as markup. Open a modal from anywhere, await a typed result, and veto closing with async guards. One framework-agnostic core with adapters for Vue 3 / Nuxt, React and plain JS.

Documentation · Live demo · Why this package

npm install @risklight/modal

Vue / Nuxt

import { createVueModal } from '@risklight/modal/vue'

createApp(App).use(createVueModal()).mount('#app')
<template>
  <RouterView />
  <ModalContainer />
</template>
const modal = useModal()
const ok = await modal.prompt<boolean>(ConfirmDelete, { title: 'Delete the report?' })

React

const modal = createReactModal()

export const App = () => (
  <ModalProvider manager={modal}>
    <Routes />
    <ModalContainer />
  </ModalProvider>
)

const ok = await useModal().prompt<boolean>(ConfirmDelete, { title: 'Delete the report?' })

Plain JS

Markup stays in HTML:

<template data-modal="confirm">
  <div>
    <h2 data-prop="question"></h2>
    <button data-resolve="true">Yes</button>
    <button data-close>No</button>
  </div>
</template>
import { createVanillaModal } from '@risklight/modal/vanilla'

const modal = createVanillaModal()
const ok = await modal.prompt('confirm', { question: 'Delete?' })

Features

  • Imperative and typed. open, push and prompt<R> from anywhere. Props are inferred from the component, and prompt resolves with a typed value or null.
  • Async close guards. They run on Escape, backdrop, close(), stack replacement and route changes. close() resolves true or false instead of throwing on a veto.
  • Stacks and namespaces. Modals in modals, toasts with auto-close, singleShow, a named registry, and beforeOpen hooks at four levels.
  • Routing everywhere. vue-router, react-router (ModalRoute) and a built-in History router for plain JS. Modals bound to a route close on navigation, go back on close, fall back to a page for deep links, and let a guard block leaving.
  • Animations everywhere. The same enter and leave classes in Vue, React and plain JS. A closing modal stays visible until its transition ends.
  • Accessible by default.
    • role="dialog" on the surface, labelled by its heading.
    • A focus trap that returns focus to the opener.
    • An inert background, with allowOutside for portaled popovers.
    • Escape on keydown, and backdrop closing that ignores text-selection drags.
  • Practical. Scroll lock with scrollbar compensation, dragging by a handle, CSP nonce, SSR-safe managers per request.
  • Small and tested. Pure ESM with strict types. 600+ tests cover the core, DOM behaviour, every adapter and SSR, and each adapter is also checked in a real browser.

Packages

Pick one adapter:

| Import | For | |---|---| | @risklight/modal/vue | Vue 3 and Nuxt | | @risklight/modal/react | React 18 and 19 | | @risklight/modal/react-router | ModalRoute for react-router 7 and 8 | | @risklight/modal/vanilla | Plain JS: no framework, server-rendered templates, Alpine, htmx, jQuery |

Low-level entry points. You only need them to build an integration for another framework (docs):

| Import | What it is | |---|---| | @risklight/modal | The manager: stacks, guards, prompts. No DOM. | | @risklight/modal/dom | Browser behaviour: focus trap, inert, Escape, scroll lock, dragging | | @risklight/modal/style.css | Default styles, if you disable injection |

vue, vue-router, react and react-router are optional peer dependencies. It needs Node 22.12+ and is ESM only; require() works on Node 22.12+.

Development

npm ci
npm run lint
npm run typecheck
npm test
npm run build
npm run docs:dev

Releases are published from a vX.Y.Z tag through npm trusted publishing with provenance.

License

MIT. See LICENSE. The project started as a rewrite of jenesius-vue-modal by Jenesius.