A flexible, dependency-free, CSS-driven JavaScript modal.
JavaScript CSS HTML


Vanilla Modal

npm version

See the demo.

A tiny, flexible, completely dependency-free CSS-powered JavaScript modal.

Written in ECMAScript 2015 and transpiled for universal use with Babel 6.

Contributions welcome.

Please be aware of recent breaking changes.

If importing using a <script/> tag

new VanillaModal() will now be new VanillaModal.default(). This is due to the way in which Babel 6 handles default exports.

The semantically-unsound [rel="modal:open"] and [rel="modal:close"] default listeners have been deprecated

They have been replaced by [data-modal-open] and [data-modal-close]. Please update your codebase if you were not supplying open or close parameters to the modal.


MIT. Please feel free to offer any assistance - pull requests, bug tracking, suggestions are all welcome. The issue tracker is over here.

Q & A


I was pretty fed up looking for a modal script that wasn't variously:

a) Bloated. b) Inaccessible. c) Needlessly complicated. d) Riddled with third party dependencies (here's looking at you, jQuery). e) Trying to hijack beautiful hardware-accelerated CSS transitions using JavaScript.

Can I integrate this with a single page app framework?

Since the modal's open and close event listeners are delegated from the document, you can use this script with any client-side routing or DOM-affecting framework.

If you're concerned about garbage collection, you may be pleased to know there's a modal.destroy() method baked in, which removes all internal events and references.

Usage and Examples

1. Install the script.

  • Using NPM:

    npm install vanilla-modal --save
  • Using Bower:

    bower install vanilla-modal --save

2. Include the script in your project.

  • The script is compiled using UMD module declarations. Use it with Webpack, Browserify, RequireJS or by simply including a <script> tag.

  • ES 2015

    import VanillaModal from 'vanilla-modal';
  • CommonJS:

    const VanillaModal = require('vanilla-modal');
  • AMD

    require(['/node_modules/vanilla-modal/dist/index.js'], function(VanillaModal) {
    const vanillaModal = new VanillaModal();
  • Browser

    <script src="/node_modules/vanilla-modal/dist/index.js"></script>
    <script>var vanillaModal = new VanillaModal.default()</script>

3. Create the modal's container using HTML.

This part is important. Vanilla Modal doesn't use any template strings or DOM building algorithms (although this is on the roadmap for version 2).

As a result, you will need to add your modal's container HTML to your document - by using JavaScript ahead of the modal's instantiation, or by writing HTML into your document.

The payoff is that you can make the modal look any way you wish.

<div class="modal">
  <div class="modal-inner">
    <a data-modal-close>Close</a>
    <div class="modal-content"></div>

Following this, create some off-screen containers to house your modal's content. Give each an ID to make them selectable via anchor elements, and accessible using JavaScript-disabled browsers.

The modal will pick up the contents that are inside the container with the ID specified by the triggering anchor's href attribute. It will place them in the modalContent container specified by your settings object. In the example above, the default container class of .modal-content is used.

<div id="modal-1" class="modal-hider">Modal 1 content</div>
<div id="modal-2" class="modal-hider">Modal 2 content</div>

Note: Vanilla Modal applies the class specified by loadClass to the page element. Both are specified in settings, and default respectively to vanilla-modal and body. This is done in order to make the modal as accessible as possible for all use cases.

<style type="text/css">
  body.vanilla-modal .modal-hider {
    position: absolute;
    left: -99999em;

4. Create a VanillaModal instance.

const modal = new VanillaModal(options);

(Where options is a configuration hash. The full list of options, as well as their defaults, are listed below under the "Options and Defaults" heading.)

5. Add your own CSS rules.

Here's the demo's stylesheet.

Vanilla Modal handles display logic using CSS. Hardware acceleration via CSS transforms comes highly recommended, for a smooth device-agnostic experience.

Two things to keep in mind:

  • Using display: none; on any element will efface transitions you might otherwise wish to use.

  • Whatever property you're using when closing the modal (z-index in the example below) will need a transition-length of 0 and a transition-delay property of the length of the longest other transition. This prevents the modal's obfuscating property from kicking in ahead of the closing animation (e.g. changing the z-index before the opacity animation has played out).

transition: opacity 0.2s, z-index 0s 0.2s;

6. Delegation and Built-in Methods

Default delegate targets are as follows:

  • [data-modal-open] triggers modal.open().

  • [data-modal-close] triggers modal.close().

Examples follow:

The following element will open #modal-1 using VanillaModal.

<a href="#modal-1" data-modal-open>Modal 1</a>

The element below will close the modal.

<a data-modal-close>Close</a>

These defaults can easily be changed at instantiation:

const modal = new VanillaModal({
  open: '.my-open-class',
  close: '.my-close-class'

7. Programmatically opening a modal

If you need to open the modal automatically, you can do so by passing a DOM ID string to the open() function.

For example:

const modal = new VanillaModal();

The modal can likewise be closed programmatically using the close() method.

VanillaModal Public Properties

  • {Object} $

    A hash of DOM nodes used internally by the modal. Useful if at any stage the modal's container needs to change.

  • {Object} $$

    The modal's settings object.

  • {Boolean} isOpen

    true if the modal is open.

  • {Node} current

    The DOM node currently displayed in the modal. null if not set.

  • {Function} close()

    The modal's callable close method.

  • {Function} open(String)

    The modal's callable open method. This requires the passed DOM ID target to be present on the page.

  • {Function} destroy()

    Closes the modal and removes all event listeners and internal references. This releases an instantiated modal to the next garbage collection cycle.

Options and Defaults

The options object contains DOM selector strings and bindings. Defaults are overridden by providing an options object to a new VanillaModal instance.

Note: this API is feature-frozen for the 1.x release, but subject to change at 2.x.


  modal: '.modal',
  modalInner: '.modal-inner',
  modalContent: '.modal-content',
  open: '[data-modal-open]',
  close: '[data-modal-close]',
  page: 'body',
  loadClass: 'vanilla-modal',
  class: 'modal-visible',
  clickOutside: false,
  closeKeys: [27],
  transitions: true,
  onBeforeOpen: null,
  onBeforeClose: null,
  onOpen: null,
  onClose: null
  • {String} modal

    The class of the outer modal container. This is usually a fixed position element that takes up the whole screen. It doesn't have to be, though - the modal can take the form of a toast popup, for example, or any type of overlay you can think of.

  • {String} modalInner

    The inner container of the modal. This usually houses a close button at the very least (see HTML above). It should also contain the modalContent element.

  • {String} modalContent

    The container used to house the modal's content when it's transferred to the modal. This must be a child of modalInner.

  • {String} open

    The selector to bind the open() event to.

  • {String} close

    The selector to bind the close() event to.

  • {String} page

    The outermost DOM selector to apply the loadClass and class classes to. This is body by default but could just as easily be html or main in any common web app.

  • {String} loadClass

    The class to apply to the page DOM node at the moment the script loads.

  • {String} class

    The class to apply to the parent container when the modal is open.

  • {Boolean} clickOutside

    If set to true, a click in the area outside the modalInner container will fire a close() event.

  • {Array} closeKeys

    Hitting any keycodes contained within this array while the modal is open will fire a close() event. Set this to false or an empty array to disable keyboard modal closure. Defaults to [27], which is esc on a traditional keyboard.

  • {Boolean} transitions

    If set to false, the modal will treat every browser like IE 9 and ignore transitions when opening and closing.

  • {Function} onBeforeOpen {Function} onBeforeClose {Function} onOpen {Function} onClose

    Hooks that fire before their respective events. These are context-bound to the VanillaModal instance, and receive their triggering events (e.g. click or keydown) as its only arguments.


This script works in the evergreen mobile & desktop browsers, as well as IE 11, 10, and 9 (the last has no support for transitions).

It is not compatible with Opera Mini or the Blackberry browser, and there are currently no plans afoot to support either.