Ensuring your Users are on the Right Path: the Future of Modals and Focus Management

Rate this content
Bookmark

With *dialog* and the inert attribute landing in all major browsers in 2022, we as web developers now have simple yet powerful primitives to help build complex app-like flows on the web, rather than the over-engineered or leaky solutions we've relied on for years. Let's demystify these primitives and talk through how they make your code simpler: from plain HTML, Web Components, to React/similar.

17 min
20 Jun, 2022

Video Summary and Transcription

This Talk discusses the new features of Modals and focus management in web development. The dialogue element allows for the creation of modal dialogs that appear on top of other elements and prevent interaction with elements below. The note element can mark a subtree of the DOM as inert, making it unusable. The field set component can group input elements together and disable them. Using these new primitives can improve focus control and user experience in web applications.

Available in Español

1. Focus Management and the Dialogue Element

Short description:

Hey there, my name is Sam, and I'm here today to talk about ensuring your users are on the right path, the futures of Modals, and focus management. The web has come a long way, and for the first time this year, all the evergreens are supporting these amazing new features that we can use as either primitives or as part of some greater library. So firstly, what is focus management, and why do you care? Now, I feel like this is the quintessential reason of why it's important. The things I want to talk about today are dialog and a note. These are the two new concepts that are actually available on browsers this year, and they're both quite cool. Before we go any further, who is this guy, why am I talking to you? I'm a former Googler, I was there for over 10 years, it was obviously really fun, I was on the developer relations team, and so part of my job was to do this kind of outreach. Our technology is still on the web, and so we want to use these features even in our SaaS product to make our user experiences better. And my general vibe of why I'm coming here and talking about these concepts in general, and why I want this in my browser, I want this in my web app, is that to me, these new features are letting us throw away tons of old code that did weird hacks and horrible things to browsers that really they weren't designed to do. So having new primitives in 2022 is really lovely, I can get rid of all this code and get on with the more interesting parts of my job. The first component I'm gonna talk about today is called the dialogue element. Now, dialogue can do a whole bunch of stuff, the spec is pretty wide. But the reason why we most care about it as web developers is I can create this element on my page, wherever I like, and then call showModal in JavaScript. What this does is then creates that dialogue in what's called a top layer. Now the top layer is special, it does two things for us. One is that I can't tap behind it, this focus problem has been solved, while it's visible, I can't interact with anything below it including other dialogues that I previously opened. The second thing that's really cool here is that it actually exists outside the browser's kind of normal rendering layer. It's in a thing called the top layer, and what that means is even if you're inside some weird stacking context or your Z indexes are all bizarre, then actually the dialogue will always be on top. I actually like to demonstrate this by, if you can see here, I've got this weird element that's transformed in bizarre ways. If I click this button, the dialogue is actually inside this transformed element, but the browser's just gonna ignore all of that and hoist it to the top. So in that way, it's quite cool. It's scoped and encapsulated in a way that you can use it in a component or some library and it will still appear right at the top, ready for the user to interact with it.

♪♪ ♪♪ ♪♪ ♪♪ Hey there, my name is Sam, and I'm here today to talk about ensuring your users are on the right path, the futures of Modals, and focus management. Now, that's a handful, and I submitted this talk to JSNation a while ago, and since then, I think what it should be called is basically focus management in 2022.

Now, the web has come a long way, and actually, for the first time this year, all the evergreens are supporting these amazing new features that we can use as either primitives or as part of some greater library.

So firstly, what is focus management, and why do you care? Now, I feel like this is the quintessential reason of why it's important. Now, we've got dialog here. I can click around, I can hit submit, but what I can actually do while this is out operating is press Tab a bunch of times and get behind this UI. I can still click some buttons that are on the page here. Now, this is a demo, but lots of sites have this problem if you look around, and the challenge is that it's just really hard, and for the longest time, we've got libraries and polyfills that try to really maintain this, to try to solve this problem for you, but either they're not used correctly, they just can't do what you want them to do, or people just forget, and they just use a really basic mechanism and hope that users will never get there, and this example is obviously pretty tricky, not everyone knows how to tab behind UI elements, but this kind of problem will leak out in other ways, causing users some weird behavior, and some examples here.

To be very precise, the things I want to talk about today are dialog and a note. Now, these are the two new concepts that are actually available on browsers this year, and they're both quite cool. They're actually quite old in terms of specs, they were written a long time ago, but as I've mentioned, it's the first time that you can use them in pretty much all evergreens with no hassle.

So, before we go any further, who is this guy, why am I talking to you? I'm a former Googler, I was there for over 10 years, it was obviously really fun, I was on the developer relations team, and so part of my job was to do this kind of outreach. I've actually recently left Google and gone to a small early stage startup in the green energy space called Gridcog, it's a lot of fun, I would highly recommend working in climate, it's an important thing, and I'm always happy to talk to people about that, orthogonally to the content of this talk.

Having said that, our technology is still on the web, and so we want to use these features even in our SaaS product to make our user experiences better. And my general vibe of why I'm coming here and talking about these concepts in general, and why I want this in my browser, I want this in my web app, is that to me, these new features are letting us throw away tons of old code that did weird hacks and horrible things to browsers that really they weren't designed to do. So having new primitives in 2022 is really lovely, I can get rid of all this code and get on with the more interesting parts of my job. Not dealing with focus management, but actually writing applications that do interesting things.

The first component I'm gonna talk about today is called the dialogue element. Now, dialogue can do a whole bunch of stuff, the spec is pretty wide. But the reason why we most care about it as web developers is I can create this element on my page, wherever I like, and then call showModal in JavaScript. What this does is then creates that dialogue in what's called a top layer. Now the top layer is special, it does two things for us. One is that I can't tap behind it, this focus problem has been solved, while it's visible, I can't interact with anything below it including other dialogues that I previously opened. The second thing that's really cool here is that it actually exists outside the browser's kind of normal rendering layer. It's in a thing called the top layer, and what that means is even if you're inside some weird stacking context or your Z indexes are all bizarre, then actually the dialogue will always be on top. I actually like to demonstrate this by, if you can see here, I've got this weird element that's transformed in bizarre ways. If I click this button, the dialogue is actually inside this transformed element, but the browser's just gonna ignore all of that and hoist it to the top. So in that way, it's quite cool. It's scoped and encapsulated in a way that you can use it in a component or some library and it will still appear right at the top, ready for the user to interact with it.

Now, that's the most interesting part of the dialogue for me, but there's a few other things I wanna cover before we go on.

2. Backdrop Element and Escape Key Behavior

Short description:

One of the interesting aspects of the dialogue element is the backdrop element, which provides a full-screen element for every dialogue. It also includes a built-in handler for the escape key. While this behavior may seem strange, it dates back to when it was created in 2014. If you want to prevent the dialogue from closing when the escape key is pressed, you can add an event handler for the Cancel event.

One of them is this backdrop element, and so every dialogue for free will have like a full screen element. You can change its color or background or whatever. It also has a built-in handler for the escape key. Now this is kinda bizarre but kinda reflects when it was created back in 2014, but if I can hit escape on my keyboard now, then this dialogue will close, and that's fine, but a bit weird because there's no analogy on mobile or other platforms. You know, you can't press your back button on your mobile phone to close it. That will still, unfortunately, go to the previous page. There is something coming on the pipeline called Close Watcher as a bit of an aside, which is totally in draft state at this point, which does help deal with things like closing dialogues, and in fact, it's one of the examples called out in its description, but who knows if that'll come about. My advice, honestly, is if you don't want this behavior, you can actually add an event handler for the Cancel event, and then the user hitting Escape will do nothing, right? And you can control that keyboard interaction, or maybe you close the dialog with buttons. You can do that entirely yourself.

3. The Note Element and Inertness

Short description:

The next thing I wanna talk about is a Note. This is a whole different feature that lets you build interesting apps together. You can mark any subtree of your DOM to be a Note, making it unusable and like a better version of pointerEvents None. Once a DOM tree is inert, you can't turn it off, which can be seen as a feature in component systems.

The next thing I wanna talk about is a Note. Now, this is a whole different feature. It has nothing to do with the previous dialogue, but it also lets you build interesting apps together. This is something where I can mark any subtree of my DOM to be a Note. Now, what this means is I can't click on it, I can't interact with it, I can't tap into it. This is basically like a better version of pointerEvents None. And that's something that people often use, right? They just say, well, I can't click on this area, therefore you can't use it, right? Well, no, as you can see in my example before, it's very easy to tap behind that area. So a Note basically gives you that for free, but also makes the whole, anything in this space just unusable. Now, this is a very broad primitive, right? Like, I'll go through some examples later on, but it's worth thinking about, right? You can build a lot of things with it. One interesting downside is that you can't actually turn it off. So once a DOM tree is inert, that's it, I can't make some sub section uninert. Now, whether you think that's a feature or a bug, well, that's up to you, but to me, it's kind of a feature, right? If I have a component system and say, well, everything within me is turned off. Something within that system can't say, oh, no, I really want to be turned on, right? So it turns everything off all the way down.

4. Field Set and Browser Evolution

Short description:

The field set is an ancient component that can be used to group input elements together and set them as disabled. While not as powerful as inert, it can serve as a fallback for form submission. Firefox, Safari, and Chrome support these features, making it easy to start using them. Browsers were originally designed for simple document navigation, but as the web evolved, the need for better focus control arose. Pointer Events in IE11 marked a new era in guiding users through applications.

And so the last component I want to talk about is actually quite ancient. It's the field set. Now, you might think of the field set as just some nice way to group input elements together. But what you don't know or what you might not know is that you can actually set disabled on the field set. What this does is kind of like a lesser version of inert. It's going to propagate that disabled down to any HTML form elements within the field set. So links will still be clickable, but like an input or a button will be read only and disabled.

Now, this is actually not as good as inert, right? It does fewer things. Inert's not quite there yet in Firefox. It's in nightly, which means it's probably gonna ship soon, but this is like an interesting fallback that you can use if all your problem is just submitting a form, right? I don't want users to be able to mark with a form while it's in progress. And I don't wanna modify every field. I can just wrap it in a field set and say, disabled, off we go. It has one weird nuance with shadow DOM, but it's possibly a bug more than a feature in that it doesn't propagate through to elements within a shadow DOM's shadow route.

And so one of the reasons I'm excited to talk today is that these three elements just work. Firefox, Safari, and Chrome support nearly all of these features. As you can see, and as I mentioned, Firefox is slightly behind on Inert. But honestly, by the time you watch this talk, it's probably gonna be there. You can just start using this stuff. These are primitives though. You can use them directly, that's fine. You can also think about using them as part of a framework or a library. And I fully expect some libraries to basically have a fast path where they just say, we've got this amazing new feature. We don't need to do our old weird hacks. We can just leverage Inert, Dialogue, or even Fieldset, although they might be doing that already. That's quite old.

Now, with these amazing new primitive introduced, it's worth talking about how we've gotten here. Now, you know, browsers were written for an ancient time where documents were documents, right? You clicked on links, you could go to other pages, and there was no app-like experiences in sight. But as we've added more functionality to the web and made it this amazing platform to build interesting stuff on, we've needed to control its focus much more distinctly. The tools people have had available over the years are things like literally just removing links, you know, making a button disabled one by one. I mentioned Pointer Events that came with IE11, and it was sort of a new era in trying to railroad or guide people through your application in like a nice, friendly, organic way.

5. Challenges of Inert and Disabled Components

Short description:

The challenges of using Inert or Disabled components include the ease of escaping Pointer Events and the need to manually disable elements. Polyfills can improve the situation, but they are not ideal. The new primitives provide a better way to intercept and handle user intent and goals.

And so, the challenges of doing things like Inert or Disabled was that Pointer Events, as I mentioned, can be really trivially escaped, right? I can't just hide an element behind a div. I could manually set elements to input disabled or, you know, disable the HRF on a link, but you've got to do it for every single one. You know, what if it was already disabled? What if you're in a shadow root? What if all these what ifs, right? And polyfills make this kind of better, but they're horrible hacks, right? And one of the challenges with these things is like, I can't really intercept the user's intent or goals globally. Now, these new primitives we have actually let us do that in a much nicer way.

6. The Importance of Built-in Dialogue Primitives

Short description:

Making a dialogue without a built-in primitive can be challenging. Frameworks like React can help, but some layouts don't support that behavior. Browsers were designed for documents, not apps. Polyfills are futile. Let's discuss the reasons for using these components.

And it's also worth calling out that making a dialogue without a built-in primitive is also quite challenging. The biggest one being, it's often not clear whether you can hoist yourself to the top of the page. You know, does Position Fix do what you want at the position you're currently in? And as you saw from the demo before, with the actual built-in dialogue, that's no longer a problem. I can just have a component that makes a dialogue and it will just appear on top, which is pretty much what I always want when I encapsulate elements together.

Frameworks, I will say, can help. And if you're building your app entirely within a framework like React, then dialogue may be a solved problem for you. I know that when I write React code, then we use a library that will actually hoist that element to the top level for you automatically, right? Like the fact that it's a dialogue within my React component is sort of irrelevant, right? It gets rendered somewhere else anyway. But some frameworks don't allow that. Some layouts are never gonna support that behavior, so having a built-in dialogue primitive is quite lovely.

In general, the thing you're trying to avoid is fighting against the browser. As I mentioned, browsers were fundamentally designed for documents, not apps. One thing polyfills try to do is intercept and override every single focus event in totally weird ways, but it's futile. There's always more cases. So now that we've got here and I've given you a bit of a history lesson, let's talk through some of the reasons why you might use these components.

7. Inert and Fieldset for UI Control

Short description:

Inert and Fieldset are useful for controlling forms and UI parts where user interaction should be restricted. By marking the elements as inert, you can create new states and prevent users from interacting with them. This can be applied to different parts of your app, such as sidebars or larger sections.

Now, Inuit and Fieldset are pretty much good for controlling forms and controlling parts of your UI where users shouldn't interact with it after something is done. And so in this example, I've got this quite fancy form. And when I hit submit, yeah, I can block it all out almost immediately and say, don't mark with this, I'm, you know, processing a payment or something like that. I can even overlay a div on here showing them I cancel button or some behavior that I really want users to flow through. And what's important here is I don't have to mark with the underlying form, right? I don't have to worry about disabling things. I can just mark the whole thing as an inert and create some new interesting state for my users to be in. Now, this is a sub tree. Maybe I can do it for a sidebar or a larger part of my app that the users shouldn't interact with for some time for whatever reason.

8. Using and Animating Dialogues

Short description:

Now, let's talk about dialogue and its use cases, including when it's appropriate to use a RealModal. Modals provide a focused experience for users, allowing them to interact with specific content without distractions. You can also animate modals and control their behavior, such as disabling interaction during animation. While some features of dialogue may be less interesting, it's important to note its history and widespread availability across browsers.

Now, you know, you can go away with this talk and create some interesting experiences and I've got some ideas here, but I think these are wonderful primitives, right? I think we'll see over the next couple of years, people using them in vastly different ways.

So now let's talk about dialogue and it's worth saying and getting out of the way. I'm not a fan of you using a RealModal or even a giant div for blocking interstitial things like sign up to my newsletter when I really just want to read your content. I think we all as developers know that's a terrible anti-pattern. But having said that, there are gonna be cases where we do need to show a RealModal on your webpage. That can be because the users, I don't know, saving some state that you're confirming, you know, do they wanna leave? Are they sure they wanna cancel? And in this example here, we've got a pretty good, nice dialogue that shows, you know, the user's types and document content, but they aren't sure if they're ready to finish or whatever. And so we give them a few options here that make this experience feel quite nice. You've got a close button to get rid of it. We've got a cancel button and we can use this modal in an interesting way. You know, there's a lot of stuff going on behind the page here and the user doesn't have to worry about it. They can just be focused on one thing for a given amount of time and modals, you know, really shine in this example.

Now, another thing we can do with the dialogue is also animate it. In the end, it's a div, right? It's a fancy div that has some magical properties and it renders in a weird place, but it's a primitive that I can use in any way I like. So when I show a dialogue, I can combine the showModal call with a, perhaps I'm gonna start the dialogue off screen and then let it animate it in over a second. One interesting thing I can even do here is maybe disable the interaction on the dialogue with inert during that time. Now, maybe a second is too long, but you get the idea. Similarly, when it closes, yes, there is a built-in close method. Yes, there is an escape handle which I can prevent from happening. And when a dialogue closes, I can not actually close it for the same amount of time. I can say maybe for a short period, it animates out and once that is done, I then properly actually close it. In the end, it's just a primitive, right? Like I should be wrapping this behavior up in some other component or helper that does this for me because the close method is not something that a user should necessarily call directly. It's something that you can control.

So I'd like to finish up this talk by talking about some other things dialogue can do. I think it's worth being complete, but I will be clear, I think these things are the least interesting part of dialogue. Now, for a better backstory, right? Chrome introduced dialogue in 2014. That's literally eight years ago and Safari and Firefox have only just come to the table and built that feature out. And that's great, right? That's why the talk is here today and I can say, please go and use dialogue, it's available everywhere. But I will say some of its weird features were built for kind of a different part of the web. Now it has non-modal support. I can just toggle this open attribute and dialogue will appear and disappear.

9. Form Method Dialogue and its Limitations

Short description:

But it doesn't block the page, it doesn't render in weird ways. It also allows a thing called form method dialogue. Now, this is kind of cute, right? But the reality is what we do for every other form on the web today, or we want to intercept it with JavaScript or whatever, is we literally overload its submit method and then prevent default. Now, I don't know why form method dialogue exists when I can literally do this to a regular dialogue that has an implicit form method that I just disable or ignore with JavaScript. In fact, I'm already using the dialogue with JavaScript. So it's bizarre to me that we have this weird dialogue type form that sets a return value on the dialogue. Now, you might find it useful, but honestly, I think it's part of a spec that hasn't really aged that well. So I'm only mentioning this stuff for completeness.

But it doesn't block the page, it doesn't render in weird ways. And so it's kind of pointless, right? It's just a div in this regard. It also allows a thing called form method dialogue. Now, this is kind of cute, right? You create a form and when it's submitted, it'll automatically close the dialogue around it. But the reality is what we do for every other form on the web today, or we want to intercept it with JavaScript or whatever, is we literally overload it submit method and then prevent default. Now, I don't know why form method dialogue exists when I can literally do this to a regular dialogue that has an implicit form method that I just disable or ignore with JavaScript. In fact, I'm already using the dialogue with JavaScript. So it's bizarre to me that we have this weird dialogue type form that sets a return value on the dialogue. Now, you might find it useful, but honestly, I think it's part of a spec that hasn't really aged that well. So I'm only mentioning this stuff for completeness.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

React Advanced Conference 2021React Advanced Conference 2021
21 min
Asynchronous UX
Top Content
"Please do not close or leave this page" may send shivers down your spine, but coding the proper UX flow for async might make you question your daily job. How can we properly handle UX for asynchronous code in highly responsive applications? Let's explore how introducing asynchronous code creates a challenge for UX.
TestJS Summit 2021TestJS Summit 2021
33 min
Test your UI in the REAL Browser
Imagine writing a complex function without unit tests. You would have to verify every scenario manually, over and over again. Cumbersome, but that's how most teams build user interfaces.
Imagine if you could build UIs and test UIs in the same place. If your components included expectations for how they were supposed to behave, you'd know the instant they broke.Storybook provides an organized approach to building UIs. You document a component's use-cases as stories, which are then rendered in isolation. Stories are like tests, but for UI. Storybook interaction testing allows you to script interactions and check expectations in the story itself. That allows you to run and debug UI tests in the same environment UI components are developed for: your browser.
JS GameDev Summit 2023JS GameDev Summit 2023
22 min
Game Development with ReactJS, CSS, and React Three Fiber
In this talk, I will share my experience in game development using ReactJS/CSS. We will explore how to make the most out of the component management provided by this library, along with the capabilities of CSS for creating an appealing user interface. Additionally, we will uncover how to leverage the React Three Fiber library to create games with a 3D experience.
JSNation Live 2021JSNation Live 2021
8 min
Building a Custom Component Library – Fast
If your company is anything like the ones I’ve worked for, you have apps with seven different button designs, three different datepickers, and a bizarre collection of dropdowns that may or may not be accessible. A growing trend to deal with this inconsistency is to build a custom design system or component library. Essentially, you build The One Way™ to create a datepicker for your organization, and ask that the rest of your company to conform to your new system. But building a component library comes with a lot of challenges, like, which frameworks or libraries should you use (if any)? How do you make sure your components are accessible? And how to you distribute your components so your whole company can use them? In this lightning talk you’ll learn how to build a component library fast building on top of a library like Kendo UI. You’ll learn tips & tricks on how to get up and running, how to customize components, and how to distribute components throughout your organization.

Workshops on related topic

JSNation 2022JSNation 2022
148 min
Should we have business logic in the UI?
WorkshopFree
How many times did you say or hear “this is business logic, it should not be here”?In this workshop, we will create a modern frontend application using old patterns and you will learn how to build apps that have decoupled UI and services.We will start with a React application that has its whole logic in the UI. Then step by step we will extract the rules and operations to hit that sweet spot of independence.