Html: It is unclear how directionality should be inherited into Shadow DOM

Created on 21 May 2018  ·  89Comments  ·  Source: whatwg/html

...or if it should be inherited at all.
Per current spec it shouldn't be inherited
https://html.spec.whatwg.org/#the-directionality
Well, the spec doesn't really define how it should work in shadow DOM.

If host has dir="rtl", should that get inherited to shadow? If shadow DOM has dir="rtl" should that get inherited to a slots assigned nodes?

i18n-alreq i18n-hlreq i18n-tracker shadow

Most helpful comment

Semantic directionality and rendering directionality should match. We are using flat-tree for both.

That's not possible in the presence of dir() pseudo class which match elements based on the directionality defined by dir content attribute.

For example, it's totally valid to define a CSS selector using dir() pseudo class then specify direction and unicode-bidi proprieties which override the value defined by dir content attribute:

div:dir(ltr) {
    direction: rtl;
}

Regardless, it seems like we're not in an agreement so I don't think it makes sense to write a PR at this point.

All 89 comments

@hayatoito

We should upstream the section 6 in Shadow DOM spec to HTML Standard.
https://w3c.github.io/webcomponents/spec/shadow/#attributes

It seems natural that the directionality of text is inherited into the shadow tree. A component can always override the directionality of the content with dir content attribute within its shadow tree.

If the entire document is in RTL, for example, then text inside components (including slotted contents) should supposedly need to be shown in RTL by default.

Coincidentally, that's what WebKit and Chrome does: https://jsfiddle.net/ho7qhh43/

It seems natural that the directionality of text is inherited into the shadow tree.

This sounds reasonable, but what about inheriting directionality from slot to assigned nodes?
(jsfiddle doesn't seem to work at some can't check whether the example is testing that.)

https://jsfiddle.net/wcvncxma/
So in Chrome directionality does seem to propagate from shadow DOM to assigned slots, but not when
dir-attribute is on slot element itself.

A bit modified testcase https://jsfiddle.net/88w2druw/
Looks like dir="auto" handling works in the flattened tree, at least in Chrome.

I still don't understand the case 3. Why dir on <slot> doesn't work.

I think case 3 is simply a bug. We're probably not taking the directionality of the slot into account due to its not generating a CSS box.

In case dir attribute is used inside shadow DOM, I assume it is inherited also to fallback content of elements, so that querySelector(All) can use :dir pseudo class the normal way?
So, dir propagates to assignedNodes of slot, _and_ it propagates to fallback content too?
I guess same applies to the child nodes of a host. Directionality is propagated to the shadow DOM, but also to the explicit children if they aren't assigned to any slots.
Hmm, but if they are assigned, which one wins - I guess the path which goes through slot element.

And, should rtl text inside slot's fallback content affect to the ancestors even if the slot had assigned nodes?

The definition of flat tree is here: https://drafts.csswg.org/css-scoping/#flattening

The dir attributes sets the CSS direction property and rendering honors the CSS direction property, so it should inherit in the same way as CSS inheritance does in all places. Otherwise we might see DOM operations such as dir=auto or querySelector to behave differently from what we render.

querySelector doesn't follow the rendering model, I mean, it can return stuff which isn't rendered at all.

And dir=auto -handling is also unclear. Per current spec it follows DOM tree, excluding shadow DOM.

And should :dir(ltr) or :dir(rtl) work with querySelector when selecting slot's fallback content in case there are assigned nodes to the slot? If not, why? Other selectors do work.

Right; in particular, selector matching works over the tree-of-trees, not the flat tree, so having :dir pay attention to directionality inheriting thru the flat tree seems... confusing?

(I do think that inheriting it thru the flat tree makes the most sense, unfortunately.)

Ah, good point.

I don't remember details, but Aharon and a few other bidi/i18n experts preferred to inherit the dir and lang attribute when I asked them before, and there were some discussions I could find in w3c bugzilla 27222 and https://github.com/w3c/webcomponents/issues/385.

If we want to revisit this, I'm not opposed to it.

If it inherits through flattened tree, what should the directionality be for fallback content when there are assigned nodes for the slot?

This isn't about revisiting anything, but actually defining how this all should work.

That is to say, in this example:

<x-component dir=ltr>
 <::shadow>
  <slot select=* dir=rtl>
   <span id=foo>foo</span>
  </slot>
 </::shadow>
 <span id=bar>bar</span>
</x-component>

What's the directionality of #foo and #bar? If directionality inherits thru the flat tree, then #bar inherits an rtl direction from the slot (rather than an ltr direction from the x-component). But #foo isn't in the flat tree at all, so what is it's direction?

And also, if there is rtl text inside #foo and slot has dir=auto, what should the directionality of #bar be?
Or if slot has name=baz and #bar doesn't have slot=baz, yet #bar has rtl text and x-component has dir=auto, should the contents of #bar affect to the directionality of the x-component.

@smaug---- dir=auto is the scenario I discussed with experts, and they wanted flat tree to compute auto-dir, from #bar in this case. But given their understanding on shadow dom isn't very high, we could try other questions or try to find other experts.

@tabatkins good point. I don't know the answer from the top of my head, but does the directionality of #foo matter if it's not going to be rendered at all?

directionality of #foo matters because if affects how querySelector(:dir(...)) works. It can't be left undefined.

:dir() matches to elements with the 'dir' attribute, not the inherited value, no?

nvm, I was wrong. ^

Allow me to confirm what we're discussing. The original topic was about whether to inherit or not, I still prefer to inherit, but can't read whether we have consensus or not. Do we, or does it depend on another topic how it applies/inherits to fallback contents that are not rendered?

For the fallback contents that are not rendered, I don't have opinion. Anyone mind to suggest?

The directionality of fallback contents is one problem. The larger problem is that selector matching is defined over the tree-of-trees, not the flat tree, but :dir(), to continue to remain useful, needs to be based on the flat-tree directionality inheritance. (Selectors doesn't strictly define how :dir() matches; it just says that the document language can take various things into account to figure out what the directionality should be.)

I see, thank you, understood the topic. That part is probably about the dir attribute section defines to inherit from:

the element's parent element

so if we're in consensus to do this in flat-tree, we can define this more clearly, like saying "parent in flat-tree, but for elements that are not in flat-tree, inherit from parent in tree-of-trees"?

That doesn't solve the larger problem I just outlined. Again, Selectors are defined to match on the tree-of-trees, before we construct the flat tree, so if directionality is defined to inherit over the flat tree, :dir() can't match based on the inherited directionality. We'll need to define what it matches on - in other words, in my previous example, does #bar match :dir(ltr) or :dir(rtl), and whichever answer you give, why does it match that (without referencing the flat tree)?

We don't have :dir() yet, so maybe situation is different, but for dir=auto, we have flat-tree traversal code that can traverse before we build box tree, and our dir=auto uses it. In other words, the code to compute inherited directionality in HTML is different from CSS inheritance code, because logically speaking the two may be different; e.g.,

<div dir=rtl style="direction: ltr">...</div>

and HTML directionality only look at the dir attribute, while box tree honors CSS direction property. I believe (again we haven't implemented so I might be wrong) :dir() is supposed to look at only HTML directionality, not CSS directionality, no? Am I still not understanding?

Nobody's mentioned CSS's direction or inheritance at all. This is a completely unrelated issue; :dir() doesn't care about CSS properties.

Then...sorry, I guess I don't understand what you meant by "before we construct the flat tree", thought it's about box tree. I guess I should delegate to @hayatoito, it's been a while since I stopped following shadow DOM. Sorry for the noise.

Tree construction goes:

  1. DOM tree (tree-of-trees), which becomes
  2. Flat tree, which becomes
  3. Box tree, which becomes
  4. Fragment tree

Selectors run on (1), the actual DOM. But we want directionality to be determined based on (2), the flat tree. This means that :dir(), which can only depend on information present in (1), has a problem.

Directionality state needs to be defined without any CSS flat tree stuff, but just using DOM.
I guess something like (not sure about ordering, should light DOM have "higher priority" than shadow DOM?),
(1) parent element's dir state is propagated to its child nodes
(2) if an element has a shadow root, dir state is propagated to shadow root's child nodes
(3) slot element's dir state is propagated to its assignedNodes (this means that shadow DOM can override host's dir state on assigned nodes)

And when handling dir=auto, to find the state
(1) look for L, AL or R on element's subtree tree
if not found, and element has shadow root attached, look for subtree of the shadow root.

So I think the trouble here is that we have two sort of conflicting constraints

  1. The result of dir=auto depends on the contents by which the directionality is determined. Because of :dir, we can't rely on box tree to do this.
  2. direction and unicode-bidi CSS properties DO affect the rendered directionality of the text. In WebKit (and maybe still in Blink), dir attribute interacts in non-trivial fashion with these two (legacy/deprecated) CSS properties.

Just that, I wanted to help from my knowledge of the history but don't want to spend your time on spams, please do let me know if I don't seem to help.

I re-read the [flat tree definition], thank you Hayato for putting the link, and found that the first sentence is the problem. It wasn't there when I defined the attribute spec, so I concur we have a conflict.

In our model (and the model the attribute spec assumes):

  1. DOM tree (tree-of-tree)
  2. Flat tree (virtual in our impl)
  3. CSS style tree (semi-virtual in our impl)
  4. CSS box tree
  5. Fragment tree

and selectors can refer 1 or 2. Flat tree was, at least at that point, inputs to CSS but is not CSS nor rendering.

In this mental model, I agree selectors can't rely on "any _CSS_ flat tree stuff" as @smaug---- said and _box tree_ as @rniwa said, but still can rely on flat tree. IIRC, I heard from someone at Gecko that it has similar model but it was long ago, so maybe no longer valid.

So technically possible options are:

  1. Change the [attributes] section and convince the relevant parties to not to inherit.
  2. Change :dir() selector spec somehow.
  3. Back to this model and change the first sentence of the [flat tree definition] accordingly.

Any other possibilities? Is the dir only attribute whose HTML inheritance can affect selectors?

I think we should define the flat tree in DOM Standard, and let CSS Scoping refer to it.

So technically possible options are:

Change the attributes section and convince the relevant parties to not to inherit.
Change :dir() selector spec somehow.
Back to this model and change the first sentence of the flat tree definition accordingly.

That doesn't still solve the issue, what is the directionality state in fallback content when fallback isn't used, or what is the directionality state in explicit child nodes when they aren't assigned to any slots.

What I'd like to see is something similar to https://github.com/whatwg/html/issues/3699#issuecomment-391524183 where effectively "flatten DOM tree" is used, but it covers also fallback and un-assigned nodes.

If we can agree on using "flat-tree in DOM" concept, for fallback content, your proposal or one before "parent in flat-tree, but for elements that are not in flat-tree, inherit from parent in tree-of-trees" should work, no? I prefer applying existing definition and put something on top when needed than to come up with new definition, but either works for me.

No, that doesn't help. Selectors are always going to be defined over the DOM tree; they cannot work over a flat tree, regardless of what spec those are defined in. In my previous example, x-component > #bar must match the #bar element, but it won't do so in the flat tree, because #bar is no longer the child of the component (it's a grandchild, placed inside of the slot).

smaug's suggestion is an attempt to define dir inheritance based only on DOM tree information. I haven't studied it to see if it works, but that's the direction we should be going here.

(Uh, no pun intended.)

I'm not opposed to smaug's suggestion, need to study more carefully, but what I wrote did not seem to be understood, was that bad English? Sorry if so, I'm not trying to push, but let me try again.

Hayato and I discussed this today. In our mind, the "flattened element tree" is a DOM concept that selectors and CSS can use. In at least Blink implements the flat-tree that way. I understand the current spec isn't written to allow that, but I prefer, if impls can agree, to change it to allow.

By a quick look, smaug's suggestion looks like a copy of flat-tree algorithm. If you or anyone is aware of the difference, it's appreciated to clarify. I'd like to study whether the suggestion produces exactly the same tree as flat-tree or not, and the differences if not. If they're the same, we're actually talking about the same thing, there's no point to argue one or the other. If they're different, I'd like to talk to i18n experts if the difference is good.

Selectors don't use a flat tree. As an example, querySelector does find elements inside fallback content even if the slot has assigned nodes.

Yes, like I just said:

In my previous example, x-component > #bar must match the #bar element, but it won't do so in the flat tree, because #bar is no longer the child of the component (it's a grandchild, placed inside of the slot).

Selectors fundamentally cannot use the flat tree.

selector doesn't use the flat tree

Yup, selector doesn't use a flat tree. It uses a node tree. That shouldn't change.
However, the definition of each pseudo-class might want to use the definition of flat tree.
I think that was what @kojiishi meant to say.

e.g. :dir pseudo-class uses the concept of the directionality of an element.

There is a sentence there:

Otherwise, the directionality of the element is the same as the element's parent element's directionality.

We might rewrite this with the flat tree, like:

Otherwise, the directionality of the element is the same as the element's parent-element-in-flat-tree's directionality.


Regarding the definition of the flat tree,

If it inherits through flattened tree, what should the directionality be for fallback content when there are assigned nodes for the slot?

It looks there is an issue in the current definition of the flat tree. The coverage of the definition is not good. I remember I defined the parent-child-relationship-in-flat-tree for all nodes in a tree of trees in the Shadow DOM spec. However, it got lost when we defined the flat tree in CSS Scoping. The current difiniton only defines the flat tree whose root node is Document.

Basically, there can be more than one disjoint flat trees in one tree of trees. We should update the definition of the flat tree.

e.g. given the following a tree of trees:

├──/shadow-root
│   └── slot (name=s1)
│       └── fallback
│           └── fallback-child
├── child1 (slot=s1)
└── child2 (slot=foo)
    └── child3

Then, there are three disjoint flat trees:

└── slot
    └── child1
└── child3



md5-297382533a7ddc3d11725746eeba4193



fallback
└── fallback-child

In practice, no engine can compute the directionality of an element dynamically while evaluating a selector (any engine which attempts to does this would be insanely inefficient).

So it's fine if the definition of a directionality of an element relied upon a flat tree (with the fix for the issue @hayatoito just pointed above). We can compute the directionality of an element using the flat tree for once, and then use that when evaluating a selector.

Note that flat tree is simply an abstraction about how to walk tree across shadow boundaries in DOM. It doesn't depend on any CSS related concepts.

But if you use the flat tree, wouldn't that mean that the fallback elements won't have a direction since they're not in the flat tree? I'd much rather we just explicitly define where the direction is inherited from rather than rely on tree transformations for that.

@annevk Well, this is why I said that "with the fix for the issue @hayatoito just pointed [out] above". Yes, we would have to define what happens to the fallback content.

Flat tree is just an abstract construct we created for the purpose of describing what happens to shadow trees & slots. I agree we should just explicitly define what happens to the directionality but whatever we define there is just re-defining what flat tree is plus fallback contents & light shadow children.

Regarding the fallback content, don't we need to define it in flat-tree for getComputedStyle?

Thank you @hayatoito so much for describing us in much nicer way!

The CSSWG resolved that elements not in the flat tree (like unassigned nodes / fallback content) should return an empty style instead, fwiw.

OK, so, I haven't wrapped my head around this whole thread yet but @tabatkins asked me to explain how I thought directionality should inherit through shadows, so here's how I think it should work:

There are two independent concepts around directionality. One is the semantic directionality, which is a property of the document tree. The other is rendering directionality, which is a CSS property (direction). These are independent things and don't necessarily match (although they should, if we're doing things correctly). The :dir() selector works off of the semantic directionality, with no regard to the CSS property. Style rules in the UA style sheet map the semantic directionality to the rendered directionality.

So first let's deal with the semantic directionality.

Ideally, the directionality of the author’s content is bound to that content before we re-arrange the tree with components. (An author working with purely RTL content shouldn't need to retag subsections of its content to make sure they don't lose their directionality when they apply components which are not RTL.)

A component author should be able to inherit whatever direction is applied surrounding content. A very basic component would just be passing that through, and would be adapting to the direction that it receives. (Component authors might also set explicit directionality for their contents, in which case of course that directionality should be honored. But if none is set, we should get the directionality of the component’s parent as rniwa explained).

If we bind the semantic directionality using the tree of trees, this gets us the behavior we want. It also addresses the problem of needing the input to :dir() to resolve before flattening.

Here's the sample tree @tabatkins and I drew on the whiteboard:

<div dir=rtl>
  <x-component>
    <i>child,/i>
    <::shadow>
      <p>shadow content</p>
      <div dir=ltr>
        <slot select="i">
           fallback content v1
         </slot>
      </div>
    </::shadow>
  </x-compent>
</div>

In this case, the <i> element receives the directionality of its parent and is thus RTL, the component and thus the the <p> also inherit RTL, while the <div> is LTR (and fallback content, if it were rendered as the child of the div, would thus be rendered LTR).

Next, we need to make sure that rendered directionality is correctly mapped to each element based on its semantic directionality. Currently the rules that do that look like

[dir]:dir(rtl) { direction: rtl }

But this doesn't address the case of light children pulled into the shadows whose bound directionality is different from their shadow-parent, but whose directionality is inherited from the light-parent (not set on element itself). So we need to modify the rule a bit, to look like

[dir]:dir(rtl), :dir(ltr) > :dir(rtl) { direction: rtl; } /* apply direction when directionality changes */

I believe this model yields what we _want_ to happen. And afaict, also fits within the constraints of the techological system we have.

So.... apparently <slot> can pull in text content which is not wrapped in an element (which totally makes sense), so we might need some magic around that if we want such content to behave correctly when it’s nested inside a component which sets its own dir.

ok, that isn't what browsers do currently, since currently dir defined in shadow DOM propagates to assigned nodes.
(I don't have strong opinion on what should happen - since I can see use cases for different behaviors, -just that whatever happens is actually defined somewhere.)

we might need some magic around that if we want such content to behave correctly when it’s nested inside a component which sets its own dir

Now I'm a bit lost with the proposal. In the previous comment assignedNodes get their dir state from their parent, but here you're hinting dir state would be coming via slot. Which directionality the assigned text node should have if host has dir=rtl and slot has dir=ltr ?

No, just pointing out that per https://github.com/whatwg/html/issues/3699#issuecomment-395876573 a slot pulling in <span>some text</span> will render differently than one pulling in some text, since the latter has no element to bind directionality to before it gets pulled into the flattened tree. Ideally they should behave the same. I'm not sure how to fix that conceptually.

I think we shouldn't compute dir: auto in terms of the flattened tree since it'd make it observable to the host whether there are rtl chars on the root.

@emilio Not sure what that has to do with rtl characters on the root, since dir=auto only looks at characters in its own subtree, but I agree that computing dir=auto on the flattened tree is not a good plan. :)

I think we shouldn't compute dir: auto in terms of the flattened tree since it'd make it observable to the host whether there are rtl chars on the root.

To be clear, it is not Shadow DOM's goal to make everything unobservable. That has never been a design goal.

Regarding rendering stuff, it is reasonable that attaching Shadow DOM should have an effect.
e.g. hostchild.offsetTop.

since dir=auto only looks at characters in its own subtree

Yes, per spec, but at least blink does look at the shadow tree too.

https://w3c.github.io/webcomponents/spec/shadow/#attributes was written to make dir=auto to work in flat tree, and it reads so to me, but maybe not clear enough if other people reads differently. Sorry about that.

This was an i18n feedback at the point we investigated. When:

<my-input dir="rtl"></my-input>

sets the direction of the inside of <my-input> to RTL,

<my-input dir="auto"></my-input>

should work too.

I guess this is also related to https://github.com/whatwg/html/issues/3748
Which way should shadow DOM affect to non-shadow content?

(and as it has been explained many times, saying dir=auto works in flat tree isn't any explanation. This stuff needs to work on tree-of-trees, since dir-state needs to be defined also for elements which aren't in the flat tree)

@smaug---- I'm lost. Can you explain whether you are proposing to "traverse only in its own subtree" or "traverse tree-of-trees"? If the former, <x-input dir=auto> does not work, but if the latter, it should work IIUC. I feel a bit awkward when DOM and CSS inherit dir and direction differently, and that :dir may select different set, but I'm fine as long as <x-input dir=rtl> and <x-input dir=auto> work in most common cases.

EDIT: From the feedback, clarified I was talking about custom elements by changing <input> to <x-input>

Not sure what <input dir=auto> has to do with this all. If implementations use shadow DOM to implement <input> element, that is all then implementation details.

I'm proposing something close to https://github.com/whatwg/html/issues/3699#issuecomment-391524183. I'm not yet quite sure about dir=auto direction detection. If a child element is assigned to a slot and the slot has dir=ltr, child element should not affect to dir=auto. This would be similar to current HTML spec.

@fantasai and I chatted about this other day. Here's my attempt to summarize it.

dir content attribute's value is inherited from a parent node to a child node, and from the host element to its shadow root's children. So, if you set dir=rtl on the shadow host, then its value will be inherited into the children of the shadow root. This would imply that the value of ltr/rtl would NOT be inherited from the slot element to its assigned nodes. Because users of a component would expect the directionality of content set once at the top of the page to apply to all of its content (as it does in the absence of components which uses shadow tree).

However, when computing the directionality of text with dir=auto, we would consider the contents inside a shadow tree as well as the assigned nodes of a slot. This is necessary because, for example, when a contact card widget which uses flex box in its layout wants to always layout left-to-right (in its use of flex boxes) but it has a slot for the name of a person with an icon indicating person's status (e.g. VIP) needs to be displayed after their name in their natural text direction. In those cases, it's necessary for dir=auto to consider the directionality of the assigned contents to a slot.

I want to be clear that dir=auto on a light DOM element should be looking at its light DOM children only. The light DOM elements should all be able to assume that attaching a component does not change their directionality. (It would be weird and problematic if attaching shadow trees broke bidi markup and scrambled the text inside the host element.) The directionality of the light DOM elements should be resolved without regard to the shadow DOM.

As for dir=auto on a shadow element, looking at slotted content is probably useful, and certainly inheriting directionality from the host element is useful to the shadow DOM, so resolving the directionality of the shadow DOM makes the most sense assuming composition with the light DOM rather than in isolation.

rniwa, so I assume you're ok to change webkit's behavior quite a bit then? My proposal was trying to keep most of the behavior of the flattened tree approach, but if people want to change the behavior a bit more, that is fine to me, if we can figure out the model.

How should text nodes assigned to slots behave? (https://github.com/whatwg/html/issues/3699#issuecomment-396072098)

@smaug---- : so direction & unicode-bidi properties still need to use the flat tree. They probably need to just use the regular CSS inheritance chain, which does use the flat tree. We need to figure out how they all interact...

Sure. I was just wondering whether webkit is then ok to change how directionality inherits from slot to assigned nodes, or basically stop the inheritance.

(I'm still pondering what I should implement right now in Gecko.)

Update from Google:

Google's position didn't change from my previous comment: https://github.com/whatwg/html/issues/3699#issuecomment-391938958

Semantic directionality and rendering directionality should match. We are using flat-tree for both.

As I explained in https://github.com/whatwg/html/issues/3699#issuecomment-391938958, it mostly just needs someone to do the legwork to update the flat tree definition, so that there is a definition of "parent in flat tree" for any nodes in DOM Standard.

I'm happy to send a PR to DOM Standard to try to define a flat tree there.

Semantic directionality and rendering directionality should match. We are using flat-tree for both.

That's not possible in the presence of dir() pseudo class which match elements based on the directionality defined by dir content attribute.

For example, it's totally valid to define a CSS selector using dir() pseudo class then specify direction and unicode-bidi proprieties which override the value defined by dir content attribute:

div:dir(ltr) {
    direction: rtl;
}

Regardless, it seems like we're not in an agreement so I don't think it makes sense to write a PR at this point.

Ah, an interesting case. Thank you for pointing it out.

Semantic directionality and rendering directionality should match.

I think I shouldn't use these unfamiliar words.. :( Please forget that.
My intention was: "dir() pseudo class" should use flat-tree.

My intention was: "dir() pseudo class" should use flat-tree.

You mean flat trees in the sense of tree-of-trees? Meaning that the directionality of auto is resolved against the tree-of-trees? I think that's consistent with what we're saying.

I think what gets tricky then is what happens to the inheritance of dir content attribute. In particular, if the shadow content had dir=ltr and the assigned content was Arabic with dir=rtl set at the top of the tree (e.g. on html element), then I don't think it's okay for the assigned content to suddenly change the directionality. Very specifically, this would mean that :dir(rtl) would cease to apply to those contents, which is very problematic. We don't want attaching a shadow root to have that kind of side-effect on the slotted contents.

You mean flat trees in the sense of tree-of-trees?

No. When I use flat-tree, I use it in a sense that "an assigned node's parent (in flat tree) is the slot".

I think what gets tricky then is what happens to the inheritance of dir content attribute. In particular, if the shadow content had dir=ltr and the assigned content was Arabic with dir=rtl set at the top of the tree, then I don't think it's okay for the assigned content to suddenly change the directionality. Very specifically, this would mean that :dir(rtl) would cease to apply to those contents, which is very problematic. We don't want attaching a shadow root to have that kind of side-effect on the slotted contents.

Hmm. To be honest, I have the exact opposite thought on that, however, I am not 100% sure which is better. I am feeling that I am not ready to answer which is better. @kojiishi might have an opinion on that.

Speaking as an i18nWG Invited Expert who specializes in bidi, fwiw (this is why @tabatkins pulled me into the discussion), I am in strong agreement with https://github.com/whatwg/html/issues/3699#issuecomment-400938543 If the author of a document declared content to be RTL, a widget shouldn't be changing it to LTR by the accident of declaring directionality on its own content.

The dir attribute is description of the content. That it inherits through the markup tree is a convenience to the author, and that should not make it any less strong of a binding than an explicit declaration on that element.

Ok, that is fine to me. dir-attribute inside shadow DOM wouldn't affect to dir state in light DOM.
And dir=auto in host wouldn't use shadow DOM content to calculate the state.
(but dir=auto inside shadow DOM could use assigned nodes)

A remaining issue is how to define the handling of host's text node children which get assigned to a slot with different dir-state than host's dir-state.
Could we do something simple, yet a bit ugly, and say that a slot-with-assigned-nodes' dir-state is same as host's, even if slot had dir-attribute, and if slot doesn't have assigned nodes, it's state would be defined like any other element in shadow DOM.
Ugly, but fast to calculate at least.

@smaug---- Yeah, if slot is preserved in the flattened tree, that would be a nice way to attach the directionality. We could then add a few rules to the UA style sheet to set the rendering directionality correctly for slotted content:

slot:dir(ltr) { direction: ltr; } slot:dir(rtl) { direction: rtl; }
slot { unicode-bidi: isolate; } /* for safety, in case we're dealing with inline content */

The problem is, if I understand correctly, that while slot is preserved in the element tree (which will let direction inherit correctly to all descendant slotted content), it is not preserved in the box tree, and thus any inline content directly contained by the slot will not necessarily be rendered in the correct order. The direction property on inline boxes and text has no effect: it has to be set on either their block container or on an inline box which has unicode-bidi: isolate to affect reordering. If slot were preserved in the box tree, e.g. as an inline with unicode-bidi: isolate, the we'd have no problem. But if it has display: contents by default, then it can't function as a bidi container.

I'm not sure what to do about that, except maybe make unicode-bidi some exceptionally magic thing which has an effect on the rendering even when its box is removed from the tree.

I don't think we want the automatic unicode-bidi: isolate on slot. This would prevent the use case where you want to add text around the assigned content in their natural order. Attaching a shadow root with just a single default slot to a host should render identically to when it didn't have any shadow root. Otherwise, it would limit the use cases of shadow roots.

Agree with @rniwa

What about other attributes listed in the original spec? Are we proposing to change all these?

These are inputs to the selector engine:

These are UI-related:

  • draggable
  • dropzone
  • hidden
  • spellcheck
  • title

Let's discuss dir & lang/xml:lang content attributes here and defer other attributes for other issues in order to avoid derailing this issue.

@smaug---- mentioned title attribute in #3821 so probably we can discuss there?

Rough consensus at TPAC F2F: We would resolve the content directionality and the content language of each element in each tree before flattening them except a shadow root's children will inherit its content direction & content language from its shadow host.

We still have to work on how the content direction and language of a text node assigned to a slot is resolved.

Google will review how this proposal will impact their implementation.

So a little background here is that we were talking about two concepts of directionality:

  • content directionality, i.e., the directionality that comes from the dir attribute (including auto) and that the CSS :dir() selector can select on
  • CSS computed directionality, i.e., the computed value of the direction property. This is influenced by content directionality, but cannot influence content directionality. What happens to it is also a function of what the rules for CSS inheritance are

As we started to discuss, I think there's a little more complexity here. It seems like the underlying motivation here is that we want content in the light dom to have its directionality that it gets from its light DOM ancestors, even when there's shadow DOM in between (which we want to ignore). That's pretty straightforward for content directionality on elements. But text nodes don't really have content directionality (the thing that :dir() selects on); and the directionality that matters to them is really just textual -- from their text, the text around them, and the CSS directionality elements around them, up to the nearest ancestor block or bidi-isolation. So then the question is -- what do we actually want to happen to those text nodes? And do some of those things we want also apply to elements?

There may also be issues with whether dir=auto considers text crossing shadow tree boundaries.

It's also separately worth understanding how the rules for content directionality interact with how CSS directionality works (and inherits), which is a separate discussion (perhaps related to #3748).

We would resolve the content directionality and the content language of each element in each tree before flattening them except a shadow root's children will inherit its content direction & content language from its shadow host.

Implementing that in Gecko would be easy. But the tricky part is to have sane behavior for assigned text node. It would not be acceptable if assigned elements inherited directionality from host, but text wouldn't.

Thank you for the nice summary.

We would resolve the content directionality and the content language of each element in each tree before flattening them except a shadow root's children will inherit its content direction & content language from its shadow host.

Implementing that in Gecko would be easy. But the tricky part is to have sane behavior for assigned text node. It would not be acceptable if assigned elements inherited directionality from host, but text wouldn't.

This is actually very much simpler and easier for Blink too. We implemented our current behavior because we heard BiDi experts wanting to allow component authors to override dir for slotted content, but it looks like consensus is to prohibit that capability. If we don't take such requests, our implementation will be simpler and faster.

If we go that way, I assume we don't want dir=auto to cross shadow dom boundaries, correct? From implementation point of view, this is mixed. It makes dir=auto implementation very much simpler and faster, but we rely on this for <input dir=auto> to work. Again, we implemented this by requests from BiDi experts, but if the consensus is not to want to take such request, we can probably make such form control capability to be internal. /cc @tkent-google

IIUC the discussion, it looks like we also want to match the CSS direction property to the tree-of-trees, and apply unicode-bidi: isolate automatically for slots. This seems to me (and was to @rniwa above if he's still so) to limit the component capability a bit more for BiDi, but again, if consensus is there, I'm good. From implementer point of view, this is different from above two changes. I don't know the feasibility of this part, but I suppose it's not simple. /cc @lilles

@r12a this change will give some and take some for i18n. I can explain in i18n weekly if needed.

If we go that way, I assume we don't want dir=auto to cross shadow dom boundaries, correct?

I think we would want dir=auto to resolve on all light DOM elements prior to tree composition, so that the shadow DOM contents do not interfere with dir=auto resolution. But we probably also want to allow shadow DOM elements to resolve against either the contents of a slot or their resolved direction as set during light DOM dir resolution.

IIUC the discussion, it looks like we also want to match the CSS direction property to the tree-of-trees,

CSS 'direction' will have to follow the resolution of https://github.com/whatwg/html/issues/3748 Depending on how that comes out, we can adjust how we're handling things here: we can always reset the direction using :dir() selectors on the slot element, as described in https://github.com/whatwg/html/issues/3699#issuecomment-401167640

and apply unicode-bidi: isolate automatically for slots.

See https://github.com/whatwg/html/issues/3699#issuecomment-401167640 ; this currently has no effect if the default display of the slot is display: contents.

We implemented our current behavior because we heard BiDi experts wanting to allow component authors to override dir for slotted content

This is an interesting idea. Certainly we don't want component authors overriding dir unintentionally, but maybe it should be possible if the component author has a specific intention to do so. If this is desired, then dir resolution can't be done prior to flattening, but instead the content direction of the slot element would need to be defined to “inherit from” (default to) the content direction of the shadow root.

We implemented our current behavior because we heard BiDi experts wanting to allow component authors to override dir for slotted content, but it looks like consensus is to prohibit that capability. If we don't take such requests, our implementation will be simpler and faster.

I don't think such an use case makes much sense anyway. The component author knows much less about the direction of the content than the component user who knows a given content.

If we go that way, I assume we don't want dir=auto to cross shadow dom boundaries, correct?
I think we would want dir=auto to resolve on all light DOM elements prior to tree composition, so that the shadow DOM contents do not interfere with dir=auto resolution. But we probably also want to allow shadow DOM elements to resolve against either the contents of a slot or their resolved direction as set during light DOM dir resolution.

Right. We DO want the directionality of a slot and its surrounding content to be influenced by the assigned node's content. This is important so that you can place an icon, etc... at the end of the slotted content. On the other hand, we don't want dir=auto outside the shadow tree to be influenced by the contents in a shadow tree since that would leak the existence of it.

Now, this can lead to a bit of a tricky situation if dir=auto was specified on an ancestor of a slot element and it had a strong LTR/RTL character (1) before the slot element, and the assigned content of the slot element had a strong RTL/LTR, which is different from (2). If the outer DOM tree also had dir=auto, then the assigned content would wind up having two conflicting directionalities.

That would make no sense so I'd have to think about this some more.

It's not clear to me what @fantasai's proposal is intending for dir=auto on shadow DOM elements -- how does dir=auto resolve against "resolved direction as set during light DOM dir resolution" differ from resolving against the contents of the flattened tree (not sure if "flattened" is the current term). It seems reasonably to me to say that dir=auto in the light DOM resolves before tree composition but dir=auto in the shadow DOM resolves after -- but I'm not sure how that's different from what you're suggesting.

I'm also not clear what the previous comment means about conflicting directionalities -- dir=auto should always resolve to either ltr or rtl, and descendants should always override ancestors, so there should never be a conflict.

Just wanted to confirm:

On the other hand, we don't want dir=auto outside the shadow tree to be influenced by the contents in a shadow tree since that would leak the existence of it.

IIUC this, we'd like to think use cases such as <my-input dir=auto> to set the direction of <my-input> from its shadow tree content is not valid, correct?

@kojiishi , Correct.

@dbaron The complication in that sentence probably came from rniwa and I discussing how to get auto-resolution to work a bit better on slots. Normally dir=auto only looks at the text content, but if you have an explicit direction, that's better, so there was some discussion at TPAC about handling that a bit better somehow...

Overall I think there are two mechanisms that seem to make sense here:

  • A) Content directionality and language is resolved within each tree, with the shadow root inheriting from its host and dir=auto on a slot element resolving against the slotted content of the slot element.
  • B) dir=auto on a light DOM element resolves to ltr/rtl in the light DOM, however content directionality and language is _inherited_ in the flattened tree (and dir=auto on shadow elements is resolved in the flattened tree), except that slot elements inherit from the shadow host rather than their parent in the shadow tree.

The major functional difference between the two is that in second method, the component can override the slotted content's content language/directionality (by setting an explicit dir/lang on the slot element itself). But only intentionally, because the slot element defaults to inheriting from the light DOM.

Then there remain some further questions:

  • Do we want dir=auto on an ancestor of the slot element to be able to key off of the light DOM directionality of the slotted content? If so, we probably want the slot element to count as a “strong character” for the purpose of resolving dir=auto. (We'd need this exception because dir=auto ignores elements and neutrals and only looks for the first strong character in actual text. But an explicit direction is more reliable than this heuristic, so it would be good if we were keying off of one if it exists.)

  • Do we want slot elements directly containing text to affect the rendering direction of their contents by default? If so we need to create some kind of magic bidi isolation despite them not having a CSS box... :/

Just wanted to confirm:

On the other hand, we don't want dir=auto outside the shadow tree to be influenced by the contents in a shadow tree since that would leak the existence of it.
IIUC this, we'd like to think use cases such as <my-input dir=auto> to set the direction of <my-input> from its shadow tree content is not valid, correct?

Right, I don't think we want to support that kind of use cases because that would violate the encapsulation. On the other hand, dir=auto inside a shadow tree should be influenced by the directionality of the slotted content.

In 2020 spring F2F, we agreed that for the purpose of determining the directionality of an element,

  • When an element doesn't have a dir attribute, it inherits from its parent ignoring all shadow content (as if the shadows didn't exist).
  • When an element has no dir attribute it inherits as if shadow does not exist (?)Top level elements in the shadow tree inherit from the shadow host

The remaining question is whether the assigned nodes should affect the directionality of a slot element or not.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

benjamingr picture benjamingr  ·  3Comments

NE-SmallTown picture NE-SmallTown  ·  3Comments

lacolaco picture lacolaco  ·  3Comments

mustaqahmed picture mustaqahmed  ·  4Comments

lazarljubenovic picture lazarljubenovic  ·  4Comments