Synthetic Focus Delegation

Summary

This RFC defines how to enable delegatesFocus flag when attaching a synthetic ShadowRoot. This defines the navigation sequence of elements contained by the ShadowRoot. Few rules:

Goals

Proposal

Delegate Focus Flag

In LWC, users do not have control over when the shadowRoot is going to be attached, and what options to use when attaching it. This invariant must be preserved.

delegatesFocus flag

We will like delegatesFocus to be a class value, it can't be defined per instance since it might be used by the compiler to do extra optimizations. If you need a component that delegates or not a focus on demand, it is very likely that you can just create two components, once extending the other, and adding the delegate focus static flag.

For now, we can stick to a public static field, e.g.:

export default class Foo extends LightningElement {
    static delegatesFocus = true;
}

We could extract that value when analyzing the class for the first time.

Open question:

Focus and Blur

Implementation Details

We need a way to know, if in a particular shadow, there is at least one element that is focusable, because this is what triggers the cascading effect of the navigation order.

This is very difficult problem to solve, but if we rephrase the question, in the context of the fallback mode, we only really need to figure the actual focusable elements in the subtree, which could be achieve by a simple query that can return false positives, and zip through it to discard them.

This means that in fallback, no custom element itself, whether they have delegate focus or not, should retain the focus, in fact they should not be focusable at all, which means that the next focuseable element might or might not be inside its subtree. The only rule to achieve this is that no one, no exceptions, can have a tabindex bigger than 0.

Basically, the rules are:

undefined