Inert affecting subtrees differently to dialog


#1

I’ve been reading the inert proposal and I’m curious why the design is for the inert attribute to apply to the entire tree of shadow-inclusive descendants.

The dialog element has a similar concept of making the page content inert, but it works differently—it makes the document inert, except for the subtree starting at the dialog.

When you use these two features together, you get something flexible enough to do, over the path from document root to leaf node:

non-inert → inert (inert attribute) inert → non-inert (dialog) inert → non-inert → inert (dialog and an inert attribute, see the example below)

<!DOCTYPE html>
<div>
  This is made inert by the dialog
  <dialog id="d">
    This is non-inert, it's in the dialog
    <div inert>
      This subtree is inert because of the attribute
    </div>
  </dialog>
</div>
<script>d.showModal();</script>

It seems dialog and the inert attribute are slightly non-orthogonal in how they affect inert-ness. It will also be necessary to decide how they interact, for example, what happens to a dialog in an inert subtree that’s then shown modally?

This kind of complexity makes me wonder if it would be worth generalizing the attribute a bit so it can turn inert “on” or “off” at any level of the tree. Then dialog is not a special case, just implicitly turns inert off if shown or something like that.


#2

This kind of complexity makes me wonder if it would be worth generalizing the attribute a bit so it can turn inert “on” or “off” at any level of the tree.

I think this would make the most sense. Similar to the cascading style of CSS, a child element could/should take priority over its parent.


#3

Counter-example: display: none.


#4

True, but in general CSS cascades. It’s literally in the name.


#5

Not allowing escaping out of inert subtrees makes things much simpler.

To allow escaping out of subtrees:

  • A bare attribute would no longer work; it would have to be inert=true or inert=false.
  • We would need to carefully consider what is “allowed” to break out of an inert subtree. For example, consider a custom element like this:
  <custom-thing inert=true>
    <div inert=true>
        <div inert=false>
          Hi!
        </div>
    </div>
  </custom-thing>

…the div in the middle should probably be still inert, because it was probably only intended to undo the inert within the custom element template. But what if it was intended as a dialog? How could you express “jump out of all the inert”?

  • Similarly, could elements in Shadow DOM jump out of inert set on or above the shadow host? What about iframes? You could imagine embedding an iframe which has <div inert=false> inside it potentially breaking out to the main page.

As discussed elsewhere, I agree we need another primitive to explain <dialog>. I don’t think inert should be generalised to this case.

In terms of the interaction between inert and today’s <dialog> - consistency with the existing <dialog> implementation in Chrome would seem to suggest that a modally shown dialog can jump out of an inert subtree. This would need to be explained, or revisited. However, I don’t think that is urgent.


#6

How does this affect use cases? The explainer mentions polyfilling dialog, but maybe the argument would be stronger without mentioning polyfilling dialog because of the difference in the attribute applying to subtrees and dialog applying inertness from document root except the dialog subtree.

The idea that it makes dialog easier to implement also might be stronger with some clarification—it requires an “inert” property for nodes which dialog also requires, but it doesn’t simplify how that property is computed or interactions between things defining that property.

Yes, there’s an encapsulation problem with the idea of it switching on and off.

It would be good to have a name for this idea. The HTML spec has “inert” and “expressly inert”, and this proposal is a third thing—“sticky inert” or something—because in your proposal it takes precedence over dialogs which aren’t inert, despite having inert ancestors, because they’re active and not expressly inert.

Untangling these concepts would be good.

I wonder if I’m just confused, that your proposal is a separate parallel concept which has the same effect on focus, etc. as inert does (or disabled for that matter) but exists separately, and I was just confused by overloading an existing coincidental spec term.

I think English works against us here, by the way, because “ert” isn’t a thing; there’s just in-ert and not in-ert looks like a double negative.


#7

I didn’t say it would be a high fidelity polyfill :slight_smile: You would need to put all body content inside a container element, and have all dialogs exist outside that container. However, that would still be an improvement on today’s situation, which requires hackery to prevent focus leaving the dialog, and still requires a container element to host the aria-hidden attribute.

I said it was almost a strict subset of implementing dialog - dialog requires the concept of “inertness” to be wired up as one of the steps, and this proposal more or less stops there (after exposing this concept via an attribute)


#8

Oops, hit the wrong button!

Yeah I changed my mind on that:

Editing to add: I don’t think explaining <dialog>'s ability to jump out of inert is urgent, I just think we should be consistent about whether <dialog> can jump out regardless of how a subtree comes to be inert.


#9

I think a better analogy is CSS properties which inherit. Inherit is about taking property values from ancestor elements in the DOM. Cascading in CSS is about combining sets of properties from different rules and stylesheets.

This proposal considers whether DOM ancestors have an attribute, which is more like inheritance than cascading. For example, if we tried to “translate” this proposal to CSS, it would be more likely with a property (say, inertness) for the effective value and then inheritance to make it work for subtrees:

/* CSS spec somewhere:
   inertness: lively | pining-for-fjords | inherit;
   default value is 'inherit' */

BODY { inertness: lively; }     
[inert] { inertness: pining-for-fjords; }

What’s missing here is the idea that once turned on for a subtree, it can’t be turned off.

(I don’t think this proposal should be mapped to CSS in this way, for what it’s worth.)


#10

Yes, that’s what I was trying to get at; I just mixed up cascading and inheritance.

I personally don’t see the issue with having a boolean “toggle switch”, so to speak. If you turn it on, it’s on until you say otherwise (within its DOM descendents). Likewise with off.


#11

So - I was involved in the discussions/creation of the polyfill and we spent probably a couple of days discussing this then… It is definitely a common “first reaction” but that comes largely from the fact that we are primarily visually minded, I think. I would encourage you to attempt to fork the speculative polyfill, think through how it would work and add what you are thinking of. I think that you will find that while it is easy to describe a simple display property like “font-size” that way, it is a very different beast to describe something with meaning that plays into the accessibility tree and interaction.

Keep in mind that we do have functional modal dialogs implemented today and the approach to all of them is more or less universal - you create a root level container for all of the things except “top level stuff” and another one for “top level stuff”. When you place a dialog, it goes into “top level stuff” and then you inert the other – all the way down… just like the polyfill. I would posit that browsers would probably effectively need to create something similarly simple to explain/implement rather than “ragged or interruptible inert regions”… But experimentation will tell you for sure and, maybe you can come up with something great. If so, it’d be very interesting to see and discuss.


#12

One potential problem here is that HTML’s dialog element doesn’t work this way in the DOM tree. You can have a dialog buried deep in the DOM tree (which is a nice property to have for composition) and when it is shown the tree “around” it becomes inert.

It can do this because of the invention of the toplayer which is the special pocket of a root level container, and it goes in there. But toplayer is not DOM.


#13

Right: my feeling is that “top layer” should be the primitive to explain <dialog> - but that that work should be done later.

Edit: I don’t mean “some day”, I mean “I am happy to take this on next quarter”.


#14

ok, but: a) the idea is very similar and we can’t polyfill that and b) the dialog element is currently not much more than useful fiction for discussion.

What we’re trying to do is create achievable, smaller steps that deliver low level things that can explain these concepts concretely, EWM style. This makes it easier to do non native s (and kind-of-dialog-like things) that are accessible until we arrive there. Today only one browser implements it or even has any plans to.

If, ultimately, we need to slightly change the spec’s inner-workings for dialog itself to get others on board, it is in my own estimation probably ok… Especially since arguably that one browser has the most resources and this is kind of the cost of being the first-mover.


#15

I’ve lost the thread of discussion—which idea is similar to which other idea?

Chrome has an implementation of DIALOG, albeit with some known bugs.

That is Extensible Web Manifesto, right? From memory that wants to: 1. Add new low-level capabilities; 2. “Explain” existing features by breaking them into orthogonal, low-level parts; 3. Build high-level features in JavaScript frameworks first; 4. Don’t do other things.

This is a new low-level capability. I’m not sure how many of the other boxes it ticks; you can’t really make the dialog element out of this and toplayer, for example. Even if you’re willing to move polyfilled dialogs up to the body, which changes a lot related to style, I don’t think you’ll be able to make something where the polyfilled dialog is non-inert and some text that’s a child of body is inert.

I view dialog as having a few parts:

  • Top layer interaction
  • Inertness
  • Magic and possibly dubious sizing and positioning
  • Dialog stack management
  • API: events, return values, etc.

I think you want the UA involved in most of those. Having toplayer alone is probably not enough.


#16

Definitely agreed.

I think we should bundle these together. Requesting top layer + any kind of ::backdrop style -> rest of page becomes inert.

Could these be done with regular CSS?

I think this is also a part of a top layer notion. Essentially you end up with the “blocking elements” idea but with the emphasis placed on presentation.

Agreed.

I misspoke in seeming to imply that it was; I meant more that it should explain dialog’s ability to both “jump out of” inert and also make everything else inert.

To stay on-topic, though: do we both agree that inert should not be expected to explain the above?


#17

Whether dialog’s magic sizing and positioning can be done by CSS is a good question for @tabatkins and others. My impression is dialog positioning is stateful, the HTML spec says (vaguely) to do it when what Blink would call a layout object is created for it. CSS tends not to be stateful in that way.

I think inert should not be expected to explain dialog in general, but it should work consistently with how dialog inertness works. I’m fine with that being layered: dialog built on toplayer + ::backdrop and having toplayer + ::backdrop affect inertness. But the reality of the HTML spec today is that dialog interacts directly with inert.

I think the explainer would be clearer if it separated two things. One is the concept of inertness, which is a property of nodes, and effects things like “how does this appear to accessibility tech?” In this sense of “inert” I think the statements the explainer makes about dialog, like being an implementation prerequisite, make sense.

Then there’s a second concept, the inert attribute, and how it sets the inertness property of a subtree. In this sense a lot of the statements about inert polyfilling dialog seem dubious to me. For example, I don’t think the inert attribute is useful for polyfilling dialog. The DOM structure constraints the inert attribute put you under will “break” CSS selectors and you will need to polyfill CSS to undo that breakage.

I also think you can’t polyfill dialog without toplayer/blockingElements. Without toplayer the polyfill-dialog will have to be a child of body to be rendered, and with the inert attribute I’m not sure there’s a way to make text node children of body be inert without also making the polyfill-dialog be inert. Can shadow DOM do it by a slot being inert?

I’m wondering the same kind of things about the explainer’s claim that inert is useful for polyfilling blocking elements. The impression I get is that inert is sufficient to polyfill dialog and toplayer but I don’t think that is intended.

My $0.02: I think I could understand the explainer more readily if it separated the inert bit on content from the inert attribute, and focused more on the positive use case of offscreen, hidden and non-interactive content. That dialog and blocking elements may also “set” the inert bit is an important technical detail to consider.


#18

That all makes sense - will update the explainer to clarify. Thanks :smiley:


#19

Updated the README to explain how this helps with <dialog>/blockingElements, and linked to an example implementation.

Note that no, it’s not a drop dead easy implementation - that is, it doesn’t remove the need for the things it’s polyfilling! - but since there is no existing notion of inert it removes a lot of hacks and compromises that are necessary today.