How to Implement and Style the Dialog Element

A dialog is a component of web interfaces providing important information and requiring user interaction in response. The user’s attention should be focused exclusively on the active dialog element, both visually and interactively. Information and user actions presented by the dialog should be phrased simply and unambiguously. So, a dialog is interruptive by nature and should be used sparingly.

Usage

Imagine a user browsing a web application from a mobile phone. The application needs the user’s decision about its settings immediately in order to keep functioning properly – like enabling location services in order to give directions on a map. This could be a use case for a dialog:

  1. The dialog pops up. Only the dialog is interactive, lying over the rest of the content.
  2. The dialog’s header explains the required actions in short. The dialog’s body may contain more detailed information.
  3. One or more interactive elements provide possible user actions in order to find a solution.

Structure

A modal dialog consists of a container, title, description, buttons and a backdrop. If the user’s flow browsing the application must be interrupted anyway, the least we can do is to present the user a concise and well structured, clear dialog to attract their focus and quickly make an action in order to continue browsing.

The basic structure of a modal dialog element.
Basic structure of a modal dialog

It’s essential to phrase a clear and unambiguous message in the title, so the reader can understand it at a glance. Here is one example:

  • Not such a good title: “Do you want to proceed?”
  • Better, because direct and clear: “Allow access to the file system?”

Of course, further information can be put in the body of the dialog itself, but the gist should be comprehensible by reading the title and button texts only.

Behavior

A dialog always needs to suit a notable purpose: Getting the user to make a choice in order to finish a task or to keep the application functioning properly (like enabling location services for navigation).

Should the dialog close by clicking the backdrop?

Well, I only asked myself this question after trying to implement that behavior with the native dialog element. As it turns out, it’s far easier with ordinary divs to achieve.

Without the native dialog element, your markup would look something like this:

<div id="dialog" role="dialog" aria-modal="true">
  <!-- Your dialog content -->
</div>
<div class="backdrop"></div>

And the corresponding CSS

.backdrop {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}

Here we have an ordinary div stretching out over the full viewport. We can easily grab that div.backdrop with JavaScript and implement our “close-modal-on-click-behavior”.

const backdrop = document.querySelector(".backdrop");
const dialog = document.getElementById("dialog");

backdrop.addEventListener("click", function() { dialog.style.display = none; });

So, why can’t we do exactly this with the native dialog element?

The native dialog element comes with a pseudo-element called ::backdrop when invoked with dialog.showModal(). As the name suggests, it is not part of the DOM, and so we cannot access it using JavaScript…

How can we add an event listener on an element, which is essentially not part of the DOM? Well, there are workarounds, like detecting a click outside of the active dialog, but that’s a completely different story.

And once I’ve come to understand, that it is not that easy, I’ve revisitied the initially posed question: Is it worthwhile to close the dialog on click outside?

No, it is not. Keep in mind, that we wanted the user to make a decision. We interrupted the user’s flow of browsing the application. We phrased the message clearly and directly, so that it’ll be comprehensible at a glance. And then we allow the user to dismiss everything we have carefully put together with a single click?! I don’t think so.

Implementation

When we implement a dialog, the following requirements must be observed carefully:

  • Focus the first interactive element inside the modal once the dialog opens (*)
  • Trap focus inside the modal dialog
  • Provide at least one button that closes the dialog
  • Prevent interaction with the rest of the page (*)
  • Close the dialog when the user presses ESC (*)
  • Return focus to the element, that opened the dialog in the first place

Requirements marked with (*) are handled by the native dialog element out of the box, when opened as a modal.

So in order to get all the benefits listed above, we’re going to invoke the dialog using the method showModal provided to us by the native dialog JavaScript API.

// Open dialog as a modal
const dialog = querySelector("dialog");
dialog.showModal(); 

Example HTML structure

<button id="open_dialog">Open Dialog</button>

<dialog
  aria-labelledby="dialog_title"
  aria-describedby="dialog_description"
>
  <img
    src="./location-service.svg"
    alt="Illustration of Location Services"
  />
  <h2 id="dialog_title" class="h2">Use location services?</h2>
  <p id="dialog_description">
    In order to give directional instructions, we kindly ask you to turn
    on the location services.
  </p>
  <div class="flex flex-space-between">
    <button id="close_dialog">Close</button>
    <button id="confirm_dialog" class="cta">Confirm</button>
  </div>
</dialog>

Because we’re using the native dialog element here, we do not need to use role="dialog", modal="true" or similar for an accessible implementation.

Based on this simple HTML structure, which is taken from the example CodePen shown at the end of this article, we can now go ahead and implement the requirements listed above. Once the reader clicks the “Open Dialog” button, the first interactive element inside the dialog will receive focus by default.

Return focus to last active element after closing the dialog

The HTML of a modal dialog can be placed nearly anywhere in the page’s markup. So, when the reader opens the modal, the user agent jumps to the dialog’s markup, like using a portal. Once the reader closes the dialog again, the focus needs to be returned back to the element that the reader was interacting with before opening the dialog. The portal to and from the dialog should go two-way, otherwise the reader will get lost.

const dialog = document.querySelector("dialog");
const openDialogBtn = document.getElementById("open_dialog");
const closeDialogBtn = document.getElementById("close_dialog");

const openDialog = () => {
  dialog.showModal();
};

const closeDialog = () => {
  dialog.close();

  // Returns focus back to the button
  // that opened the dialog
  openDialogBtn.focus();
};

openDialogBtn.addEventListener("click", openDialog);
closeDialogBtn.addEventListener("click", closeDialog);

// If the buttons of the dialog are contained inside a <form>
// Use event.preventDefault()
const closeDialog = (event) => {
  event.preventDefault();
  dialog.close();
  openDialogBtn.focus();
};

Trap focus inside the dialog while open

A focus trap is often a horror regarding UX – in case of a modal dialog it serves an essential purpose: Keeping the reader’s focus on the dialog, helping to prevent interaction with the background.

Based on the same markup and existing JS above, we can add the focus trap to our script.

const trapFocus = (e) => {
  if (e.key === "Tab") {
    const tabForwards = !e.shiftKey && document.activeElement === lastElement;
    const tabBackwards = e.shiftKey && document.activeElement === firstElement;

    if (tabForwards) {
      // only TAB is pressed, not SHIFT simultaneously
      // Prevent default behavior of keydown on TAB (i.e. focus next element)
      e.preventDefault();
      firstElement.focus();
    } else if (tabBackwards) {
      // TAB and SHIFT are pressed simultaneously
      e.preventDefault();
      lastElement.focus();
    }
  }
};

// Attach trapFocus function to dialog on keydown
// Updated openDialog
const openDialog = () => {
  dialog.showModal();
  dialog.addEventListener("keydown", trapFocus);
};

// Remove trapFocus once dialog closes
// Updated closeDialog
const closeDialog = () => {
  dialog.removeEventListener("keydown", trapFocus);
  dialog.close();
  openDialogBtn.focus();
};

Disable closing the dialog on ESC

Just in case you want to disable the built-in functionality of closing the dialog once the user has pressed the ESC key, you can listen for the keydown event when the dialog opens and prevent its default behavior. Please remember to remove the event listener after the modal has closed.

Here is the code to make it happen:

// Inside the function that calls dialog.showModal()
const dialog = document.querySelector("dialog");

const openDialog = () => {
  // ...
  dialog.addEventListener("keydown", (e) => {
    if (e.key === "Escape") {
      e.preventDefault();
    }
  });
};

Styles for the dialog element

The user agents provide some default styles for the dialog element. To override these and apply our own styles, we can use this tiny CSS reset.

dialog {
  padding: 0;
  border: none !important;
  /* !important used here to override polyfill CSS, if loaded */
}

Admittedly, there are more default user agent styles, which center the dialog inside the viewport and prevent overflowing content. We’ll leave these default styles untouched, because in our case they are desirable.

CSS ::backdrop pseudo-element

Perhaps the coolest thing about the native dialog element is, that it gives us a nice ::backdrop pseudo-element right out of the box. The serves several purposes for us:

  • Overlay to prevent interaction with the background
  • Easily style the surroundings of the dialog while open

Accessibility aspects of a dialog element

To ensure accessibility of your modal dialog you’ve already got a great deal covered by simply using the native HTML dialog element as a modal – i.e. invoked with dialog.showModal(). Thus, the first interactive element will receive focus, once the dialog opens. Additionally, interaction with other content on the page will be blocked while the dialog is active. Plus, the dialog closes with a keystroke on ESC. Everything coming “for free” along with the native dialog element.

In contrast to using a generic div as a wrapper instead of the semantically correct dialog element, you do not have to put role="dialog" accompanied by aria-modal="true.

Apart from these benefits the native dialog element has to offer, we need to make sure the following aspects are implemented:

  • Put a label on the dialog element – e.g. <dialog aria-label="Use location services?"> or use aria-labelledby if you want to reference the ID of another element inside the dialog’s body, which presents the title anyway
  • If the dialog message requires additional information, which may already be visible in the dialog’s body, you can optionally reference this text with aria-describedby or phrase a description just for screen readers inside an aria-description
  • Return focus to the element, which opened the dialog in the first place, if the dialog has been triggered by a click interaction. This is to ensure that the user can continue browsing the site or application from the same point of regard where they left off before the dialog popped up.

Polyfill for the native dialog element

Sadly, the native HTML dialog element still lacks browser support here and there. As of this writing, Chrome, Edge and Opera support it, Firefox hides support behind a flag. No support from Safari and IE. The support coverage is around 75% globally. Reference browser support

On the bright side, the dialog element is easily polyfilled with this dialog polyfill from GoogleChrome.

In order to load the polyfill only on those browsers not supporting the dialog element, we check if dialog.showModal is not a function.

const dialog = document.querySelector("dialog");

if (typeof dialog.showModal !== "function") {
  // Load polyfill script
  const polyfill = document.createElement("script");
  polyfill.type = "text/javascript";
  polyfill.src = "dist/dialog-polyfill.js"; // example path
  document.body.append(polyfill);

  // Register polyfill on dialog element once the script has loaded
  polyfill.onload = () => {
    dialogPolyfill.registerDialog(dialog);
  };

  // Load polyfill CSS styles
  const polyfillStyles = document.createElement("link");

  polyfillStyles.rel = "stylesheet";
  polyfillStyles.href = "dialog-polyfill.css";
  document.head.append(polyfillStyles);
}

Example of a styling a native dialog element

Here is a CodePen showing off an accessible, polyfilled modal dialog. It implements the requirements listed above regarding accessibility, managing focus and polyfill on-demand. The style is based on Giovanni Piemontese’s Auto Layout Dialogs – Figma UI Kit.

See the Pen
Accessible Material Dialog
by Christian Kozalla (@ckozalla)
on CodePen.0

Apart from CodePen, you can view the source code of the example here on GitHub. A live example of that native dialog is hosted here.

Wrapping up

In this tutorial discussed the structure and purpose of dialogs regarding user-experience, especially for modal dialogs. We’ve compiled a list of requirements for creating user-friendly dialogs. Naturally, we’ve gone in-depth on the native dialog HTML element and the benefits we gain from using it. We’ve extended its functionality by building a focus trap and managing focus around the life-cycle of the native dialog altogether.

We’ve seen how to implement an accessible modal dialog based on the requirements we set before. Our implementation will be polyfilled only when necessary.

Finally, I’ve noticed during my research about the native dialog element, that its reputation in the community has changed alot over the years. It may have been welcomed with an open mind, but today’s opinions are predominantly criticizing the dialog element while simultaneously advising to rather rely on libraries.

Nevertheless, I’m sure the native dialog element has proven to be a suitable basis for implementing modal dialogs in this tutorial. I definitely had some fun!

Thanks for reading, I hope you enjoyed it!

Related

Another tutorial that might be interesting to you is this one by Osvaldas Valutis, where you’ll learn how to style and customize the upload button (file inputs).

The post How to Implement and Style the Dialog Element appeared first on Codrops.

Creating a Modal Image Gallery With Bootstrap Components

Have you ever clicked on an image on a webpage that opens up a larger version of the image with navigation to view other photos?

Some folks call it a pop-up. Others call it a lightbox. Bootstrap calls it a modal. I mention Bootstrap because I want to use it to make the same sort of thing. So, let’s call it a modal from here on out.

Why Bootstrap? you might ask. Well, a few reasons:

  • I’m already using Bootstrap on the site where I want this effect, so there’s no additional overhead in terms of loading resources.
  • I want something where I have complete and easy control over aesthetics. Bootstrap is a clean slate compared to most modal plugins I’ve come across.
  • The functionality I need is fairly simple. There isn’t much to be gained by coding everything from scratch. I consider the time I save using the Bootstrap framework to be more beneficial than any potential drawbacks.

Here’s where we’ll end up:

Let’s go through that, bit by bit.

Step 1: Create the image gallery grid

Let’s start with the markup for a grid layout of images. We can use Bootstrap’s grid system for that.

<div class="row" id="gallery">
  <div class="col-12 col-sm-6 col-lg-3">
    <img class="w-100" src="/image-1">
  </div>
  <div class="col-12 col-sm-6 col-lg-3">
    <img class="w-100" src="/image-2">
  </div>
  <div class="col-12 col-sm-6 col-lg-3">
    <img class="w-100" src="/image-3">
  </div>
  <div class="col-12 col-sm-6 col-lg-3">
    <img class="w-100" src="/image-4">
  </div>
</div>

Now we need data attributes to make those images interactive. Bootstrap looks at data attributes to figure out which elements should be interactive and what they should do. In this case, we’ll be creating interactions that open the modal component and allow scrolling through the images using the carousel component.

About those data attributes:

  1. We’ll add data-toggle="modal"  and data-target="#exampleModal" to the parent element (#gallery). This makes it so clicking anything in the gallery opens the modal. We should also add the data-target value (#exampleModal) as the ID of the modal itself, but we’ll do that once we get to the modal markup.
  2. Let’s add data-target="#carouselExample"  and a data-slide-to attribute to each image. We could add those to the image wrappers instead, but we’ll go with the images in this post. Later on, we’ll want to use the data-target value (#carouselExample) as the ID for the carousel, so note that for when we get there. The values for data-slide-to are based on the order of the images.

Here’s what we get when we put that together:

<div class="row" id="gallery" data-toggle="modal" data-target="#exampleModal">
  <div class="col-12 col-sm-6 col-lg-3">
    <img class="w-100" src="/image-1.jpg" data-target="#carouselExample" data-slide-to="0">
  </div>
  <div class="col-12 col-sm-6 col-lg-3">
    <img class="w-100" src="/image-2.jpg" data-target="#carouselExample" data-slide-to="1">
  </div>
  <div class="col-12 col-sm-6 col-lg-3">
    <img class="w-100" src="/image-3.jpg" data-target="#carouselExample" data-slide-to="2">
  </div>
  <div class="col-12 col-sm-6 col-lg-3">
    <img class="w-100" src="/image-4.jpg" data-target="#carouselExample" data-slide-to="3">
  </div>
</div>

Interested in knowing more about data attributes? Check out the CSS-Tricks guide to them.

Step 2: Make the modal work

This is a carousel inside a modal, both of which are standard Bootstrap components. We’re just nesting one inside the other here. Pretty much a straight copy-and-paste job from the Bootstrap documentation.

Here’s some important parts to watch for though:

  1. The modal ID should match the data-target of the gallery element.
  2. The carousel ID should match the data-target of the images in the gallery.
  3. The carousel slides should match the gallery images and must be in the same order.

Here’s the markup for the modal with our attributes in place:

<!-- Modal markup: https://getbootstrap.com/docs/4.4/components/modal/ -->
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-hidden="true">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">×</span>
        </button>
      </div>
      <div class="modal-body">
        
      <!-- Carousel markup goes here -->


      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
      </div>
    </div>
  </div>
</div>

We can drop the carousel markup right in there, Voltron style!

<!-- Modal markup: https://getbootstrap.com/docs/4.4/components/modal/ -->
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-hidden="true">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">×</span>
        </button>
      </div>
      <div class="modal-body">
        
      <!-- Carousel markup: https://getbootstrap.com/docs/4.4/components/carousel/ -->
      <div id="carouselExample" class="carousel slide" data-ride="carousel">
          <div class="carousel-inner">
            <div class="carousel-item active">
              <img class="d-block w-100" src="/image-1.jpg">
            </div>
            <div class="carousel-item">
              <img class="d-block w-100" src="/image-2.jpg">
            </div>
            <div class="carousel-item">
              <img class="d-block w-100" src="/image-3.jpg">
            </div>
            <div class="carousel-item">
              <img class="d-block w-100" src="/image-4.jpg">
            </div>
          </div>
          <a class="carousel-control-prev" href="#carouselExample" role="button" data-slide="prev">
            <span class="carousel-control-prev-icon" aria-hidden="true"></span>
            <span class="sr-only">Previous</span>
          </a>
          <a class="carousel-control-next" href="#carouselExample" role="button" data-slide="next">
            <span class="carousel-control-next-icon" aria-hidden="true"></span>
            <span class="sr-only">Next</span>
          </a>
        </div>
      </div>

      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
      </div>
    </div>
  </div>
</div>

Looks like a lot of code, right? Again, it’s basically straight from the Bootstrap docs, only with our attributes and images.

Step 3: Deal with image sizes

This isn’t necessary, but if the images in the carousel have different dimensions, we can crop them with CSS to keep things consistent. Note that we're using Sass here.

// Use Bootstrap breakpoints for consistency.
$bootstrap-sm: 576px;
$bootstrap-md: 768px;
$bootstrap-lg: 992px;
$bootstrap-xl: 1200px;


// Crop thumbnail images.
#gallery {
  img {
    height: 75vw;
    object-fit: cover;
    
    @media (min-width: $bootstrap-sm) {
      height: 35vw;
    }
    
    @media (min-width: $bootstrap-lg) {
      height: 18vw;
    }
  }
}


// Crop images in the coursel
.carousel-item {
  img {
    height: 60vw;
    object-fit: cover;
    
    @media (min-width: $bootstrap-sm) {
      height: 350px;
    }
  }
}

Step 4: Optimize the images

You may have noticed that the markup uses the same image files in the gallery as we do in the modal. That doesn’t need to be the case. In fact, it’s a better idea to use smaller, more performant versions of the images for the gallery. We’re going to be blowing up the images to their full size version anyway in the modal, so there’s no need to have the best quality up front.

The good thing about Bootstrap’s approach here is that we can use different images in the gallery than we do in the modal. They’re not mutually exclusive where they have to point to the same file.

So, for that, I’d suggest updating the gallery markup with lower-quality images:

<div class="row" id="gallery" data-toggle="modal" data-target="#exampleModal">
  <div class="col-12 col-sm-6 col-lg-3">
    <img class="w-100" src="/image-1-small.jpg" data-target="#carouselExample" data-slide-to="0">
  
  <!-- and so on... -->
</div>

That’s it!

The site where I’m using this has already themed Bootstrap. That means everything is already styled to spec. That said, even if you haven't themed Bootstrap you can still easily add custom styles! With this approach (Bootstrap vs. plugins), customization is painless because you have complete control over the markup and Bootstrap styling is relatively sparse.

Here’s the final demo:

The post Creating a Modal Image Gallery With Bootstrap Components appeared first on CSS-Tricks.

Comparing the Different Types of Native JavaScript Popups

JavaScript has a variety of built-in popup APIs that display special UI for user interaction. Famously:

alert("Hello, World!");

The UI for this varies from browser to browser, but generally you’ll see a little window pop up front and center in a very show-stopping way that contains the message you just passed. Here’s Firefox and Chrome:

Native popups in Firefox (left) and Chrome (right). Note the additional UI preventing additional dialogs in Firefox from triggering it more than once. You can also see how Chrome is pinned to the top of the window.

There is one big problem you should know about up front

JavaScript popups are blocking.

The entire page essentially stops when a popup is open. You can’t interact with anything on the page while one is open — that’s kind of the point of a “modal” but it’s still a UX consideration you should be keenly aware of. And crucially, no other main-thread JavaScript is running while the popup is open, which could (and probably is) unnecessarily preventing your site from doing things it needs to do.

Nine times out of ten, you’d be better off architecting things so that you don’t have to use such heavy-handed stop-everything behavior. Native JavaScript alerts are also implemented by browsers in such a way that you have zero design control. You can’t control *where* they appear on the page or what they look like when they get there. Unless you absolutely need the complete blocking nature of them, it’s almost always better to use a custom user interface that you can design to tailor the experience for the user.

With that out of the way, let’s look at each one of the native popups.

window.alert();

window.alert("Hello World");

<button onclick="alert('Hello, World!');">Show Message</button>

const button = document.querySelectorAll("button");
button.addEventListener("click", () => {
  alert("Text of button: " + button.innerText);
});

See the Pen
alert("Example");
by Elliot KG (@ElliotKG)
on CodePen.

What it’s for: Displaying a simple message or debugging the value of a variable.

How it works: This function takes a string and presents it to the user in a popup with a button with an “OK” label. You can only change the message and not any other aspect, like what the button says.

The Alternative: Like the other alerts, if you have to present a message to the user, it’s probably better to do it in a way that’s tailor-made for what you’re trying to do.

If you’re trying to debug the value of a variable, consider console.log(<code>"`Value of variable:"`, variable); and looking in the console.

window.confirm();

window.confirm("Are you sure?");

<button onclick="confirm('Would you like to play a game?');">Ask Question</button>

let answer = window.confirm("Do you like cats?");
if (answer) {
  // User clicked OK
} else {
  // User clicked Cancel
}

See the Pen
confirm("Example");
by Elliot KG (@ElliotKG)
on CodePen.

What it’s for: “Are you sure?”-style messages to see if the user really wants to complete the action they’ve initiated.

How it works: You can provide a custom message and popup will give you the option of “OK” or “Cancel,” a value you can then use to see what was returned.

The Alternative: This is a very intrusive way to prompt the user. As Aza Raskin puts it:

...maybe you don’t want to use a warning at all.”

There are any number of ways to ask a user to confirm something. Probably a clear UI with a <button>Confirm</button> wired up to do what you need it to do.

window.prompt();

window.prompt("What’s your name?"); 

let answer = window.prompt("What is your favorite color?");
// answer is what the user typed in, if anything

See the Pen
prompt("Example?", "Default Example");
by Elliot KG (@ElliotKG)
on CodePen.

What it’s for: Prompting the user for an input. You provide a string (probably formatted like a question) and the user sees a popup with that string, an input they can type into, and “OK” and “Cancel” buttons.

How it works: If the user clicks OK, you’ll get what they entered into the input. If they enter nothing and click OK, you’ll get an empty string. If they choose Cancel, the return value will be null.

The Alternative: Like all of the other native JavaScript alerts, this doesn’t allow you to style or position the alert box. It’s probably better to use a <form> to get information from the user. That way you can provide more context and purposeful design.

window.onbeforeunload();

window.addEventListener("beforeunload", () => {
  // Standard requires the default to be cancelled.
  event.preventDefault();
  // Chrome requires returnValue to be set (via MDN)
  event.returnValue = '';
});

See the Pen
Example of beforeunload event
by Chris Coyier (@chriscoyier)
on CodePen.

What it’s for: Warn the user before they leave the page. That sounds like it could be very obnoxious, but it isn’t often used obnoxiously. It’s used on sites where you can be doing work and need to explicitly save it. If the user hasn’t saved their work and is about to navigate away, you can use this to warn them. If they *have* saved their work, you should remove it.

How it works: If you’ve attached the beforeunload event to the window (and done the extra things as shown in the snippet above), users will see a popup asking them to confirm if they would like to “Leave” or “Cancel” when attempting to leave the page. Leaving the site may be because the user clicked a link, but it could also be the result of clicking the browser’s refresh or back buttons. You cannot customize the message.

MDN warns that some browsers require the page to be interacted with for it to work at all:

To combat unwanted pop-ups, some browsers don't display prompts created in beforeunload event handlers unless the page has been interacted with. Moreover, some don't display them at all.

The Alternative: Nothing that comes to mind. If this is a matter of a user losing work or not, you kinda have to use this. And if they choose to stay, you should be clear about what they should to to make sure it’s safe to leave.

Accessibility

Native JavaScript alerts used to be frowned upon in the accessibility world, but it seems that screen readers have since become smarter in how they deal with them. According to Penn State Accessibility:

The use of an alert box was once discouraged, but they are actually accessible in modern screen readers.

It’s important to take accessibility into account when making your own modals, but there are some great resources like this post by Ire Aderinokun to point you in the right direction.

General alternatives

There are a number of alternatives to native JavaScript popups such as writing your own, using modal window libraries, and using alert libraries. Keep in mind that nothing we’ve covered can fully block JavaScript execution and user interaction, but some can come close by greying out the background and forcing the user to interact with the modal before moving forward.

You may want to look at HTML’s native <dialog> element. Chris recently took a hands-on look) at it. It’s compelling, but apparently suffers from some significant accessibility issues. I’m not entirely sure if building your own would end up better or worse, since handling modals is an extremely non-trivial interactive element to dabble in. Some UI libraries, like Bootstrap, offer modals but the accessibility is still largely in your hands. You might to peek at projects like a11y-dialog.

Wrapping up

Using built-in APIs of the web platform can seem like you’re doing the right thing — instead of shipping buckets of JavaScript to replicate things, you’re using what we already have built-in. But there are serious limitations, UX concerns, and performance considerations at play here, none of which land particularly in favor of using the native JavaScript popups. It’s important to know what they are and how they can be used, but you probably won’t need them a heck of a lot in production web sites.

The post Comparing the Different Types of Native JavaScript Popups appeared first on CSS-Tricks.

Weekly Platform News: WebAPK Limited to Chrome, Discernible Focus Rectangles, Modal Window API

In this week's roundup: "Add to home screen" has different meanings in Android, Chrome and Edge add some pop to focus rectangles on form inputs, and how third-party sites may be coming to a modal near you.

Let's get into the news.

WebAPKs are not available to Firefox on Android

On Android, both Chrome and Firefox have an “Add to home screen” option, but while Firefox merely adds a shortcut for the web app to the user’s home screen, Chrome actually installs the web app (as long as it meets the PWA install criteria) via a WebAPK.

Progressive Web Apps installed in such a way are added to the device’s app drawer, and URLs that are within the PWA’s scope (as specified in its manifest) open in the PWA instead of the default browser.

Tiger Oakes who is implementing PWA-related features at Mozilla, explains why Firefox cannot install PWAs on Android: “WebAPK is not available to us since we don’t own an app store like Google Play and Galaxy Apps.”

(via Tiger Oakes)

More accessible focus rectangles are coming to Chrome and Edge

Microsoft and Google have made accessibility improvements to various form controls. The two main changes are the larger touch targets on the time and date inputs, and the redesigned focus rectangles that are now easily discernible on any background.

The updated form controls are available in the preview version of Edge. Mac users may have to manually enable the “Web Platform Fluent Controls” flag on the about:flags page.

(via Microsoft Edge Dev)

A newly proposed API for loading third-parties in modal windows

The proposed Modal Window API would allow a website to load another website in a modal window (in a top-level browsing context) for the purposes of authentication, payments, sharing, access to third-party services, etc.

Only a single modal window would be allowed at a time, and the two websites could communicate with each other via message events (postMessage method).

This API is intended as a better alternative to existing methods, such as pop-ups, which can be confusing to users and blocked by browsers, and redirects, which cause the original context to be torn down and recreated (or completely lost in the case of an error in the third-party service).

(via Adrian Hope-Bailie)

More news...

Read even more news in my weekly Sunday issue that can be delivered to you via email every Monday morning.

More News →

The post Weekly Platform News: WebAPK Limited to Chrome, Discernible Focus Rectangles, Modal Window API appeared first on CSS-Tricks.

Movin’ Modals Along a Path

Modals always be just appearin'. You might see one once in a while that slides in from one of the edges, or uses some kind of scale/opacity thing to appear from "above" or "below." But we can get weirder than that. Why not have them come in on an offset-path?

Just a swoopy arc is kinda fun.

See the Pen
Move Modal In on Path
by Chris Coyier (@chriscoyier)
on CodePen.

Or we could Mary Poppins it and have it come floating in from afar.

See the Pen
Move Modal In on Path: Mary Poppins Edition
by Chris Coyier (@chriscoyier)
on CodePen.

Or get straight up wiggly woggly.

See the Pen
Move Modal In on Path: Wackadoo
by Chris Coyier (@chriscoyier)
on CodePen.

That's all. I figured you were here for the CSS tricks, anyway. ;)

The post Movin’ Modals Along a Path appeared first on CSS-Tricks.

Prevent Page Scrolling When a Modal is Open

Please stop me if you've heard this one before. You open a modal, scroll through it, close it, and wind up somewhere else on the page than you were when you opened the modal.

That's because modals are elements on a page just like any other. It may stay in place (assuming that's what it's meant to do) but the rest of page continues to behave as normal.

See the Pen
Avoid body scrollable in safari when modal dialog shown
by Geoff Graham (@geoffgraham)
on CodePen.

Sometimes this is a non-issue, like screens that are the exact height of the viewport. Anything else, though, we're looking at Scroll City. The good news is that we can prevent that with a sprinkle of CSS (and JavaScript) trickery.

Let's start with something simple

We can make a huge dent to open-modal-page-scrolling by setting the height of the entire body to the full height of the viewport and hiding vertical overflow when the modal is open:

body.modal-open {
  height: 100vh;
  overflow-y: hidden;
}

That's good and all, but if we've scrolled through the <body> element before opening the modal, we get a little horizontal reflow. The width of the viewport is expanded about 15 pixels more, which is exactly the with of the scroll bar.

See the Pen
Avoid body scrollable in safari when modal dialog shown
by Geoff Graham (@geoffgraham)
on CodePen.

Let's adjust the right padding of the body a bit to avoid that.

body {
  height: 100vh;
  overflow-y: hidden;
  padding-right: 15px; /* Avoid width reflow */
}

Note that the modal needs to be shorter than the height of the viewport to make this work. Otherwise, the scroll bar on the body will be necessary.

Great, now what about mobile?

This solution works pretty great on desktop as well as Android Mobile. That said, Safari for iOS needs a little more love because the body still scrolls when a modal is open when tapping and moving about the touchscreen.

We can set the body to a fixed position as a workaround:

body {
  position: fixed;
}

Works now! The body will not respond when the screen is touched. However, there's still a "small" problem here. Let's say the modal trigger is lower down the page and we click to open it up. Great! But now we're automatically scrolled back up to the top of the screen, which is just as disorientating as the scrolling behavior we're trying to resolve.

See the Pen
Avoid body scrollable in safari when modal dialog shown
by Geoff Graham (@geoffgraham)
on CodePen.

Boo!

That's why we've gotta turn to JavaScript

We can use JavaScript to avoid the touch event bubble. We all know there should be a backdrop layer when a modal is open. Unfortunately, stopPropagation is a little awkward with touch in iOS. But preventDefault works well. That means we have to add event listeners in every DOM node contained in the modal — not just on the backdrop or the modal box layer. The good news is, many JavaScript libraries can do this, including good ol' jQuery.

Oh, and one more thing: What if we need scrolling inside the modal? We still have to trigger a response for a touch event, but when reaching the top or bottom of the modal, we still need to prevent bubbling. Seems very complex, so we're not totally out of the woods here.

Let's enhance the fixed body approach

This is what we were working with:

body {
  position: fixed;
}

If we know the top of the scroll location and add it to our CSS, then the body will not scroll back to the top of the screen, so problem solved. We can use JavaScript for this by calculating the scroll top, and add that value to the body styles:

// When the modal is shown, we want a fixed body
document.body.style.position = 'fixed';
document.body.style.top = `-${window.scrollY}px`;

// When the modal is hidden, we want to remain at the top of the scroll position
document.body.style.position = '';
document.body.style.top = '';

This works, but there's still a little leakage here after the modal is closed. Specifically, it appears that the page already loses its scroll position when the modal is open and the body set to be fixed. So we have to retrieve the location. Let's modify our JavaScript to account for that.

// When the modal is hidden...
const top = document.body.style.top;
document.body.style.position = '';
document.body.style.top = '';
window.scrollTo(0, parseInt(scrollY || '0') * -1);

That does it! The body no longer scrolls when a modal is open and the scroll location is maintained both when the modal is open and when it is closed. Huzzah!

See the Pen
Avoid body scrollable in safari when modal dialog shown
by Geoff Graham (@geoffgraham)
on CodePen.

The post Prevent Page Scrolling When a Modal is Open appeared first on CSS-Tricks.