Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove Shadow DOM Support #4

Open
markmals opened this issue Sep 8, 2023 · 2 comments
Open

Remove Shadow DOM Support #4

markmals opened this issue Sep 8, 2023 · 2 comments

Comments

@markmals
Copy link
Member

markmals commented Sep 8, 2023

The shadow DOM is not fit for production use. It causes accessibility issues and makes using CSS in a natural way nearly impossible. It makes the dev tools a nightmare. In order to participate in <form>s, custom elements that use the shadow DOM must wire-up a bunch of JavaScript; I don't suspect ElementInternals or form-associated custom elements will work with SSR or the declarative shadow DOM, which completely and absolutely breaks progressive enhancement for custom elements inside forms with inputs as children…

However, it provides a lot of functionality; mainly, it gives us:

  1. Styling that is scoped to our component, so we can write a style selecting for span and only affect the spans in the element with which the style is associated, and
  2. A way to slot child elements/content into our element, using projection to avoid styling them with the scoped styles and making sure they receive the scoped styles of their parent, as well as providing a way to be notified when the slotted content changes and a way to declare multiple name slots at different positions in the DOM tree

Scoped Styles

Now, scoped styling actually isn't that difficult of a feature to replace or polyfill in the light DOM. We can use the same techniques frameworks like Vue, Svelte, Astro, and even CSS Modules have used for scoping CSS styles. These techniques actually work better for the way people write websites and web apps as well, since they don't completely and totally encapsulate against global styles. In order to keep making use of the platform, we can also use the new @scope CSS decorator, which doesn't yet have browser support, but it’s due to land in Chrome 117 and Safari has a positive position on the specification. We can polyfill it in browsers until it's widely supported. Using @scope would allow us to do something like:

<my-custom-element>
    <style>
        @scope {
            span { color: red; }
        }
    </style>
    <span>this is red</span>
</my-custom-element>
<span>not red</span>

Children

The bigger problem here is replacing <slot>. Managing children is complex because most of the time we're talking about real DOM nodes. To my knoweldge (and in my experimenting), lit-html doesn't support passing DOM nodes directly into holes in the template string so we can't just query our children, append them to a document fragment, and insert that fragment in our template before a render, trusting that Lit will cache and manage them in a performant way. Additionally, we also don't have a way to be notified of when our children in the light DOM change and it could very well override our component's template if one of it's parents changes the content in our element's children (e.g. as the result of a structural change, such as a loop or conditional).

These problems might be able to be solved if we had a runtime that lived above our entire app, somehow. Because React and Svelte and even Solid have something like this, they can rely on their runtime to help manage their children in one way or another. Unfortunately, custom elements are instantiated by the browser, in a context to which we're not privy, so we're not able to instantiate a runtime that has knowledge of every element. Not even a reactive-root-based runtime.

So, here in lies the problem. If I could figure out how to solve slotting children into custom elements in the light DOM, I would rip out the shadow DOM tomorrow.

It may mean I need to develop my own tagged template literal templating solution that can accept real DOM nodes as expressions and manage them in a performant way. Perhaps something that works more similarly to Arrow.js or lit-dom-expressions

@markmals markmals self-assigned this Sep 8, 2023
@markmals markmals changed the title Remove Shadow DOM Support and Render All Components in the Light DOM Remove Shadow DOM Support Sep 8, 2023
@markmals markmals removed their assignment Sep 8, 2023
@markmals
Copy link
Member Author

Apparently, lit-html does support rendering vanilla DOM nodes, so this may be viable now with manually passing in children… (see: passle_'s reply) https://twitter.com/thetarnav/status/1703297240809316552

@markmals
Copy link
Member Author

Also, Lightning CSS supports @scope now 🎉 parcel-bundler/lightningcss#586

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant