Search code examples
javascripthtmlcssweb-componentcustom-element

Applying :focus CSS to input-field in shadowDOM and :focus CSS to shadow host


I'm currently working on a custom element which is basically a slightly augmented version of an input element, hosting all of its building blocks (including an input element) in a shadow DOM.

When the internal input element has focus, the host element should be styled with a colored outline and box-shadow, as seen below:

enter image description here

Therefore the focus and blur event handlers of the input toggle an attribute "focussed" on the host element with the encapsulated styles looking like this:

:host([focussed]) {
    transition: outline 0.3s ease-in-out;
    outline-color: var(--focus-color, var(--default-focus-color)) !important;
    box-shadow: 0px 0px 3px var(--focus-color, var(--default-focus-color)) !important;
}

What I don't like about this approach:

Exposing a custom attribute on the host that needs to be observed, in order to ensure the correctness of the visually represented state (e.g. consumer calls setAttribute('focussed', ''))

Alternatives I considered:

Of course my first thought was to encapsulate the attribute within the shadow DOM (or even toggle a class) on a container element filling out the space of the host element, but the problem is that overflowing contents such as outline and box-shadow seem to be forcefully hidden by the host element - which seems kind of logical.

I could dictate a fixed padding on the host element to ensure the visibility of the outline and shadow, but this would require considering different browser rendering behaviour of box-shadow and would feel counter-intuitive for custom styling by the consumer.

I'm looking for a best practice approach here and I would very much appreciate your educated thoughts on this one.


Solution

  • this.attachShadow({
          mode: 'open',
          delegatesFocus: true
        })
    

    works in Chrome, Edge, Opera, not the others (yet)

    This styles the input (in shadowDOM) itself with:

    :focus {
      transition: outline 1s ease-in-out;
      outline: 2px solid var(--focus-color,blue);
      box-shadow: 10px 0px 10px var(--focus-shadow-color,red);
    }
    

    And styles the host element with (global) CSS:

      :focus {
        outline: 5px solid green;
      }
    

    Full explanation and playground JSFiddle
    use Chrome/Edge/Opera first, then see lacking behaviour in others:
    https://jsfiddle.net/WebComponents/Lpqyg201/

    It has some pointers for click/focus/blur workarounds.

    For FireFox , Safari support I would add something not too fancy that can easily be removed.

    For now it is unclear to me what the time frame at Mozilla and Apple is,
    maybe Supersharp knows