Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Demo modal styles for micromodal.js and corresponding expected html. If using this, set the `awaitCloseAnimation` in config to true
/**************************\
Basic Modal Styles
\**************************/
.modal {
font-family: -apple-system,BlinkMacSystemFont,avenir next,avenir,helvetica neue,helvetica,ubuntu,roboto,noto,segoe ui,arial,sans-serif;
}
.modal__overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.6);
display: flex;
justify-content: center;
align-items: center;
}
.modal__container {
background-color: #fff;
padding: 30px;
max-width: 500px;
max-height: 100vh;
border-radius: 4px;
overflow-y: auto;
box-sizing: border-box;
}
.modal__header {
display: flex;
justify-content: space-between;
align-items: center;
}
.modal__title {
margin-top: 0;
margin-bottom: 0;
font-weight: 600;
font-size: 1.25rem;
line-height: 1.25;
color: #00449e;
box-sizing: border-box;
}
.modal__close {
background: transparent;
border: 0;
}
.modal__header .modal__close:before { content: "\2715"; }
.modal__content {
margin-top: 2rem;
margin-bottom: 2rem;
line-height: 1.5;
color: rgba(0,0,0,.8);
}
.modal__btn {
font-size: .875rem;
padding-left: 1rem;
padding-right: 1rem;
padding-top: .5rem;
padding-bottom: .5rem;
background-color: #e6e6e6;
color: rgba(0,0,0,.8);
border-radius: .25rem;
border-style: none;
border-width: 0;
cursor: pointer;
-webkit-appearance: button;
text-transform: none;
overflow: visible;
line-height: 1.15;
margin: 0;
will-change: transform;
-moz-osx-font-smoothing: grayscale;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
-webkit-transform: translateZ(0);
transform: translateZ(0);
transition: -webkit-transform .25s ease-out;
transition: transform .25s ease-out;
transition: transform .25s ease-out,-webkit-transform .25s ease-out;
}
.modal__btn:focus, .modal__btn:hover {
-webkit-transform: scale(1.05);
transform: scale(1.05);
}
.modal__btn-primary {
background-color: #00449e;
color: #fff;
}
/**************************\
Demo Animation Style
\**************************/
@keyframes mmfadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes mmfadeOut {
from { opacity: 1; }
to { opacity: 0; }
}
@keyframes mmslideIn {
from { transform: translateY(15%); }
to { transform: translateY(0); }
}
@keyframes mmslideOut {
from { transform: translateY(0); }
to { transform: translateY(-10%); }
}
.micromodal-slide {
display: none;
}
.micromodal-slide.is-open {
display: block;
}
.micromodal-slide[aria-hidden="false"] .modal__overlay {
animation: mmfadeIn .3s cubic-bezier(0.0, 0.0, 0.2, 1);
}
.micromodal-slide[aria-hidden="false"] .modal__container {
animation: mmslideIn .3s cubic-bezier(0, 0, .2, 1);
}
.micromodal-slide[aria-hidden="true"] .modal__overlay {
animation: mmfadeOut .3s cubic-bezier(0.0, 0.0, 0.2, 1);
}
.micromodal-slide[aria-hidden="true"] .modal__container {
animation: mmslideOut .3s cubic-bezier(0, 0, .2, 1);
}
.micromodal-slide .modal__container,
.micromodal-slide .modal__overlay {
will-change: transform;
}
<div class="modal micromodal-slide" id="modal-1" aria-hidden="true">
<div class="modal__overlay" tabindex="-1" data-micromodal-close>
<div class="modal__container" role="dialog" aria-modal="true" aria-labelledby="modal-1-title">
<header class="modal__header">
<h2 class="modal__title" id="modal-1-title">
Micromodal
</h2>
<button class="modal__close" aria-label="Close modal" data-micromodal-close></button>
</header>
<main class="modal__content" id="modal-1-content">
<p>
Try hitting the <code>tab</code> key and notice how the focus stays within the modal itself. Also, <code>esc</code> to close modal.
</p>
</main>
<footer class="modal__footer">
<button class="modal__btn modal__btn-primary">Continue</button>
<button class="modal__btn" data-micromodal-close aria-label="Close this dialog window">Close</button>
</footer>
</div>
</div>
</div>
@feederaccount
Copy link

feederaccount commented Jul 22, 2020

@charlesy1971 I have persevered with this package but I would advise anyone reading this not to bother.

@charlesr1971
Copy link

charlesr1971 commented Jul 22, 2020

@feederaccount I use this library all the time now. Once, you have the correct CSS, it is solid. All the API options work correctly. I think my 2 CodePen examples prove that this library can be utilized successfully.

@lifeinchords
Copy link

lifeinchords commented Jul 27, 2020

Demos are really helpful. Thank you for those who posted.
Keep in mind this is free, author doesn't owe anyone anything. Be nice :)
This gist is a working example.

@charlesr1971
Copy link

charlesr1971 commented Jul 27, 2020

@lifeinchords I agree with your sentiments.

We should always be nice to authors, who often make a big effort. I think we are trying to provide some constructive criticism to how this library could have been made more valuable, if there had been working demo, right from the start. I mean let’s face it, a demo is the bare minimum for any library. It seems strange that the author went to such a great effort, writing the code and then forgot about the final piece of the puzzle!

Anyway, I hope my codePens can act as the demo page!

@mattkaye
Copy link

mattkaye commented Aug 23, 2020

I dug into the library code a bit and the only way I could get the exit animation working was passing a config value as the second argument to the show method. I'm using nuxt for what it's worth:

MicroModal.show('modal-1', { awaitCloseAnimation: true })

@piperh
Copy link

piperh commented Aug 25, 2020

charlesr1971, I used your full code as posted above in comments (not from your codePens) and it resolved an issue.

Within the modal I have 2 buttons, an 'X' close button and a 'Buy' button. With the authors' original code, when you add 'data-micromodal-close' to the close button the initial focus goes to the other button. This causes the modal, on opening, to slide to the focussed element (which happens to be at the bottom of the div), so in the case of a modal with content taller than the browser window the top part of the div is out of view. Playing with tabindex or autofocus in the html made no difference. Your code resolves the issue. The close button is the first item in my modal__container and focus is now initially on it when the window is opened.

Apart from the above, a couple of general observations: if you set
disableScroll: true
you get a nasty content horizontal jump in the base, underneath modal, when the page scrollbar (if present) is hidden and shown.

Also, if you animate the modal closing, then
awaitCloseAnimation: true
is essential if the triggering object in the base is not entirely covered by the modal content above it. Otherwise, on touchscreen it becomes possible to retrigger the modal whenever you tap the overlay outside the modal content window.

Thanks to the authors and to others for persisting with this.

@charlesr1971
Copy link

charlesr1971 commented Aug 25, 2020

@piperh Thanks for these observations. Yes, despite a few issues and the lack of a demo, create by the author, this little library is worth persisting with. I use it for all my projects in production.

I also recommend having a look at Google's Material Lite [Vanilla JS/CSS] and Angular Material libraries. Both, have modal components, which are pretty robust:

https://getmdl.io/components/index.html#dialog-section [Dialog component]
https://material.angular.io/components/dialog/overview [Dialog component]

@yor-so
Copy link

yor-so commented Sep 19, 2020

@charlesr1971 the code you shared above for IE is great. I'd like to use it in my proyects that support IE, but if I want to fix a bug or I want to add improvements, that code is unmaintainable. Can you please explain me how did you generate that code? Are you using babel? I tried different settings in babel and I can't get any code similar to yours. Plus, the generated code I'm getting in babel is actually not working in IE. I'd really appreciate your help.

@charlesr1971
Copy link

charlesr1971 commented Sep 19, 2020

Why can’t you just copy & paste it, into your repo? And then maintain it from there? I am not sure what Babel is?

@stuartchaney
Copy link

stuartchaney commented Sep 21, 2020

@charlesr1971 - really appreciate your input for this library 🙏

Any chance you have a codepen with your IE code present?

I can't seem to get it to work 😞

Thanks!

@victorrica
Copy link

victorrica commented Sep 22, 2020

Is there any css sample for mobile?

@guilherme-funchal
Copy link

guilherme-funchal commented Sep 23, 2020

Hello

How I can pass some value to modal ?

I try to use the option openTrigger: 'data-custom-open' but doesn't worked...

@piperh
Copy link

piperh commented Oct 25, 2020

For those who give up on micromodal, charlesr1971 also posted a couple of alternatives above (material, angular) but I don't find them easy to use, so I'll point out here that there's a great, straightforward, accessible dialog from Ire Adenirokun at https://bitsofco.de/accessible-modal-dialog/
Very easy to adapt. Her dialog.js file is 2k unminifed. It does everything an accessible dialog should do.

The IA code uses display;none / display:block but if you substitute this for another method of initial hiding (eg, css clip:rect) then the dialog display can be animated.

Micromodal makes it very easy to have multiple modals. My js ability is rudimentary so with IA's version it took me a lot of stabbing around in the dark to use multiple dialogs, but can be done.

There's also the Eden Spiekerman a11y dialog at http://edenspiekermann.github.io/a11y-dialog/#introduction - has had a lot of developer input. It uses the new native 'dialog' element when available (currently only Chrome/Edge) which may be good but has major drawback that dialog::backdrop does not currently accept a click to close, though a workaround to use vanilla js overlay is suggested (in 'closed issues' tab).

Apologies to micromodal's developer for directing visitors away - just suggesting some alternatives.

@charlesr1971
Copy link

charlesr1971 commented Dec 1, 2020

And here is an ES5 version, if anyone needs it:

!(function(e, t) {
  "object" == typeof exports && "undefined" != typeof module
    ? (module.exports = t())
    : "function" == typeof define && define.amd
    ? define(t)
    : ((e = e || self).MicroModal = t());
})(this, function() {
  "use strict";
  return (function(){
    var e = [
      "a[href]",
      "area[href]",
      'input:not([disabled]):not([type="hidden"]):not([aria-hidden])',
      "select:not([disabled]):not([aria-hidden])",
      "textarea:not([disabled]):not([aria-hidden])",
      "button:not([disabled]):not([aria-hidden])",
      "iframe",
      "object",
      "embed",
      "[contenteditable]",
      '[tabindex]:not([tabindex^="-"])'
    ];
    class t {
      constructor({
		targetModal: e,
        triggers: t,
        onShow: o,
        onClose: i,
        openTrigger: n,
        closeTrigger: s,
        disableScroll: a,
        disableFocus: l,
        awaitCloseAnimation: d,
        awaitOpenAnimation: r,
        debugMode: c
      }) {
        (this.modal = document.getElementById(e)),
		  t = t || [],
		  o = o || function(){},
		  i = i || function(){},
		  n = n || "data-micromodal-trigger",
		  s = s || "data-micromodal-close",
		  a = a || !1,
		  l = l || !1,
		  d = d || !1,
		  r = r || !1,
		  c = c || !1,
          (this.config = {
            debugMode: c,
            disableScroll: a,
            openTrigger: n,
            closeTrigger: s,
            onShow: o,
            onClose: i,
            awaitCloseAnimation: d,
            awaitOpenAnimation: r,
            disableFocus: l
          }),
          t.length > 0 && this.registerTriggers(...t),
          (this.onClick = this.onClick.bind(this)),
          (this.onKeydown = this.onKeydown.bind(this));
      }
      registerTriggers() {
		var e = Array.prototype.slice.call(arguments,0);
		var that = this;
        e.filter(Boolean).forEach(function(e){
          e.addEventListener("click", function(e){return that.showModal(e)});
        });
      }
      showModal() {
        if (
          ((this.activeElement = document.activeElement),
          this.modal.setAttribute("aria-hidden", "false"),
          this.modal.classList.add("is-open"),
          this.scrollBehaviour("disable"),
          this.addEventListeners(),
          this.config.awaitOpenAnimation)
        ) {
		  var that = this;
          var e = function(){
            that.modal.removeEventListener("animationend", e, !1),
              that.setFocusToFirstNode();
          };
          this.modal.addEventListener("animationend", e, !1);
        } else this.setFocusToFirstNode();
        this.config.onShow(this.modal, this.activeElement);
      }
      closeModal() {
        var e = this.modal;
		//console.log('micromodal.min.js: this.config.awaitCloseAnimation: ',this.config.awaitCloseAnimation);
        this.modal.setAttribute("aria-hidden", "true"),
          this.removeEventListeners(),
          this.scrollBehaviour("enable"),
          this.activeElement && this.activeElement.focus(),
          this.config.onClose(this.modal),
          this.config.awaitCloseAnimation
            ? this.modal.addEventListener(
                "animationend",
                function t() {
                  e.classList.remove("is-open"),
                    e.removeEventListener("animationend", t, !1);
                },
                !1
              )
            : e.classList.remove("is-open");
      }
      closeModalById(e) {
        (this.modal = document.getElementById(e)),
          this.modal && this.closeModal();
      }
      scrollBehaviour(e) {
        if (!this.config.disableScroll) return;
        var t = document.querySelector("body");
        switch (e) {
          case "enable":
            Object.assign(t.style, { overflow: "", height: "" });
            break;
          case "disable":
            Object.assign(t.style, { overflow: "hidden", height: "100vh" });
        }
      }
      addEventListeners() {
        this.modal.addEventListener("touchstart", this.onClick),
          this.modal.addEventListener("click", this.onClick),
          document.addEventListener("keydown", this.onKeydown);
      }
      removeEventListeners() {
        this.modal.removeEventListener("touchstart", this.onClick),
          this.modal.removeEventListener("click", this.onClick),
          document.removeEventListener("keydown", this.onKeydown);
      }
      onClick(e) {
        e.target.hasAttribute(this.config.closeTrigger) &&
          (this.closeModal(), e.preventDefault());
      }
      onKeydown(e) {
        27 === e.keyCode && this.closeModal(e),
          9 === e.keyCode && this.maintainFocus(e);
      }
      getFocusableNodes() {
        var t = this.modal.querySelectorAll(e);
        return Array(...t);
      }
      setFocusToFirstNode() {
        if (this.config.disableFocus) return;
        var e = this.getFocusableNodes();
        e.length && e[0].focus();
      }
      maintainFocus(e) {
        var t = this.getFocusableNodes();
        if (this.modal.contains(document.activeElement)) {
          var o = t.indexOf(document.activeElement);
          e.shiftKey &&
            0 === o &&
            (t[t.length - 1].focus(), e.preventDefault()),
            e.shiftKey ||
              o !== t.length - 1 ||
              (t[0].focus(), e.preventDefault());
        } else t[0].focus();
      }
    }
    var o = null;
    var i = function(e){
        if (!document.getElementById(e))
          return (
            console.warn(
              `MicroModal: ❗Seems like you have missed %c'${e}'`,
              "background-color: #f8f9fa;color: #50596c;font-weight: bold;",
              "ID somewhere in your code. Refer example below to resolve it."
            ),
            console.warn(
              "%cExample:",
              "background-color: #f8f9fa;color: #50596c;font-weight: bold;",
              `<div class="modal" id="${e}"></div>`
            ),
            !1
          );
      },
      n = function(e, t){
        if (
          ((function(e){
            if (e.length <= 0)
              console.warn(
                "MicroModal: ❗Please specify at least one %c'micromodal-trigger'",
                "background-color: #f8f9fa;color: #50596c;font-weight: bold;",
                "data attribute."
              ),
                console.warn(
                  "%cExample:",
                  "background-color: #f8f9fa;color: #50596c;font-weight: bold;",
                  '<a href="#" data-micromodal-trigger="my-modal"></a>'
                );
          })(e),
          !t)
        )
          return !0;
        for (var o in t) i(o);
        return !0;
      };
    return {
      init: function(e){
        var i = Object.assign(
            {},
            { openTrigger: "data-micromodal-trigger" },
            e
          ),
          s = [...document.querySelectorAll(`[${i.openTrigger}]`)],
          a = (function(e, t){
            var o = [];
            return (
              e.forEach(function(e){
                var i = e.attributes[t].value;
                void 0 === o[i] && (o[i] = []), o[i].push(e);
              }),
              o
            );
          })(s, i.openTrigger);
        if (!0 !== i.debugMode || !1 !== n(s, a))
          for (var l in a) {
            var e = a[l];
            (i.targetModal = l), (i.triggers = [...e]), (o = new t(i));
          }
      },
      show: function(e, n){
        var s = n || {};
        (s.targetModal = e),
          (!0 === s.debugMode && !1 === i(e)) || (o = new t(s)).showModal();
      },
      close: function(e){
        e ? o.closeModalById(e) : o.closeModal();
      }
    };
  })();
});

@pxdsgnco
Copy link

pxdsgnco commented Mar 15, 2021

Would be nice to trigger this modal on pageLoad instead of onClick. How do I go about this?

@charlesr1971
Copy link

charlesr1971 commented Mar 15, 2021

document.addEventListener("DOMContentLoaded", function() {
    MicroModal.show('my-modal',{
        awaitCloseAnimation: true,
        onShow: function(modal){
           // do something
        },
        onClose: function(modal){
           // do something
        }
    });
});

@pluxxury
Copy link

pluxxury commented Apr 23, 2021

My explanation on how to make it work proper with close animation

Step 1:
Add CSS and HTML examples from the top of the file.

Step 2:
Add this code after script initialization

window.addEventListener('DOMContentLoaded', function(){ MicroModal.init({ onShow: modal => console.info(${modal.id} is shown), // [1] onClose: modal => console.info(${modal.id} is hidden), // [2] openTrigger: 'data-micromodal-trigger', // [3] closeTrigger: 'data-micromodal-close', // [4] openClass: 'is-open', // [5] disableScroll: true, // [6] disableFocus: false, // [7] awaitOpenAnimation: true, // [8] awaitCloseAnimation: true, // [9] debugMode: true // [10] }); })

Step 3:
Add element that will trigger modal

<a href="#" data-micromodal-trigger="modal-1">Trigger modal</a>

It can be <a> or <button> doesn't matter, depends on what you want.

How to open modal immediately after page load ?
Just add MicroModal.show('your modal id')

To trigger by element watch "Step 3"

@yoowo222
Copy link

yoowo222 commented May 27, 2021

Hello everyone, I try to use barba.js to load Ajax, but after loading the page, micromodal doesn't work. Is there a solution?

@charlesr1971
Copy link

charlesr1971 commented May 27, 2021

@yoowo222 Have you tried loading the micromodal without barbs.js? We need to make sure that the micromodal is working without any interference.

I use this to initiate micromodal:

try{
 MicroModal.init({
  awaitCloseAnimation: true,
  onShow: function(modal){
  // do something when modal opens
  },
  onClose: function(modal){
   // do something when modal closes
  }
 });
}
catch(e){
 console.log('micromodal error: ',e);
}

@charlesr1971
Copy link

charlesr1971 commented May 27, 2021

You need to place the init code inside:

window.addEventListener('DOMContentLoaded', function(){ 
});

@yoowo222
Copy link

yoowo222 commented May 27, 2021

@charlesr1971 Thank you. I've tried to do it myself. I put it here
afterEnter: (data) => {}

@joshliptzin
Copy link

joshliptzin commented Jul 7, 2021

Doesn't work when used with jquery

@charlesr1971
Copy link

charlesr1971 commented Jul 7, 2021

@joshliptzin I use it with jQuery without any problems…

@charlesr1971
Copy link

charlesr1971 commented Jul 7, 2021

Is there any css sample for mobile?

@victorrica Here are two examples that work on the mobile:

https://codepen.io/charles1971/pen/qBELEVN
https://codepen.io/charles1971/pen/BayGZBK

If you want to make the modal full width/height on the mobile, then just add 100% to both properties in the CSS

@Meidanmor
Copy link

Meidanmor commented Sep 20, 2021

This library is perfect !
Only problem I have is on touch screen when I close the modal it clicks on the element under
So If I click on the close button and there is a link behind the close button it will click on the link too which is not good since that redirects me to the links page..

@kbzaso
Copy link

kbzaso commented Oct 14, 2021

This work for me.
I install microModal via NPM, and modify the micromodal.js inside the node_module, the line 80.

I change false for true

awaitCloseAnimation = _ref$awaitCloseAnimat === void 0 ? true : _ref$awaitCloseAnimat,

I hope this can help anyone.

@Elkaroui
Copy link

Elkaroui commented Mar 18, 2022

I find this library is sample is so useful,
You can easily use this library to change model to slide menu, I shared my example here if anyone interested 😁

https://codepen.io/Elkazi/pen/bGaEmKz

@charlesr1971
Copy link

charlesr1971 commented Mar 18, 2022

I find this library is sample is so useful, You can easily use this library to change model to slide menu, I shared my example here if anyone interested 😁

https://codepen.io/Elkazi/pen/bGaEmKz

Very cool! 🙏

@panphora
Copy link

panphora commented Apr 20, 2022

For anyone who comes back to this in the future, here's a minimal working example that you can just copy & paste:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>micromodal demo</title>
  <style>
    .micromodal {
      display: none;
    }

    .micromodal.is-open {
      display: block;
    }

    .micromodal__overlay {
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      display: flex;
      justify-content: center;
      align-items: center;
      background: rgba(0,0,0,0.65);
    }

    .micromodal__container {
      box-sizing: border-box;
      overflow-y: auto;
      max-width: 500px;
      max-height: 100vh;
      padding: 30px;
      background-color: #fff;
      border-radius: 4px;
    }

    .micromodal[aria-hidden="false"] .micromodal__overlay {
      animation: microModalFadeIn .2s cubic-bezier(0.0, 0.0, 0.2, 1);
    }

    .micromodal[aria-hidden="false"] .micromodal__container {
      animation: microModalSlideIn .2s cubic-bezier(0, 0, .2, 1);
    }

    .micromodal .micromodal__container,
    .micromodal .micromodal__overlay {
      will-change: transform;
    }

    @keyframes microModalFadeIn {
        from { opacity: 0; }
          to { opacity: 1; }
    }

    @keyframes microModalSlideIn {
      from { transform: translateY(15%); }
        to { transform: translateY(0); }
    }
  </style>
</head>
<body>
  <a data-micromodal-trigger="modal-1" href='javascript:;'>Open Modal Dialog</a>

  <div class="micromodal" id="modal-1" aria-hidden="true">
    <div class="micromodal__overlay" tabindex="-1" data-micromodal-close>
      <div class="micromodal__container" role="dialog" aria-modal="true" aria-labelledby="modal-1-title">
        <button aria-label="Close modal" data-micromodal-close>X</button>
        <p>hi there, I'm some modal content!</p>
      </div>
    </div>
  </div>
  <script src="https://unpkg.com/micromodal/dist/micromodal.min.js"></script>
  <script>
    MicroModal.init();
  </script> 
</body>
</html>

And here's a working codepen of the same code: https://codepen.io/panphora/pen/yLpwOOo

I made a few stylistic choices:

  • I removed the exit animation. If people are exiting a modal, they probably just want it gone instantly.
  • I cut down on the header content & aria labels there. Useful for some modals, but not every modal has a header.
  • I cut down on the number of classes & grouped all the CSS under a .micromodal class. Much easier to read this way.

@proimage
Copy link

proimage commented Aug 25, 2022

I've found that if you add an SVG icon (or presumably any other element) inside the Close button, clicking on that icon doesn't appear to bubble up to the close button, and won't close the modal. The simple solution is to add this CSS (adapt the selector to your needs, but take care because the overlay element containing the modal has [data-micromodal-close], too):

button[data-micromodal-close] > .icon {
    pointer-events: none;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment