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

[css-scoping] @-rule to specify light DOM CSS rules within Shadow DOM styles #10941

Open
LeaVerou opened this issue Sep 24, 2024 · 1 comment

Comments

@LeaVerou
Copy link
Member

LeaVerou commented Sep 24, 2024

A recurring pain point around defining WC styling is that shadow DOM CSS is too limited to target what authors actually want to target. A common use case is targeting slotted (light DOM) descendants and/or relationships between slotted elements.

WCs today can always inject light DOM CSS the first time an instance of a WC is created or connected, but it is nontrivial to do well, and very easy to do wrong, which creates a footgun. The easy option would be to inject it in e.g. document.head, which means it does not get applied in any ancestor scopes (when you have nested shadow roots). Additionally, because this CSS needs to use regular selectors (rather than :host) it cannot support customizing element names or scoped registries. Not to mention that it requires CSS to be maintained in two separate places (or to jump through hoops so that the same CSS file can be used in both places).

An easy solution to this would be a new @-rule (@light? @global?), the contents of which are evaluated in the context of the light DOM, except :host still works. So for example this would work:

<foo-tree>
	<ul>
		<li>
	</ul>
</foo-tree>
@light {
	:host ul > li {
		list-style: ...
	}
}

Just like any other @-rule, it can also be combined with nesting to keep related styles together. E.g. the rule above could also be written as:

:host {
	@light {
		ul > li {
			list-style: ...
		}
	}
}

There is a lot of precedent in frameworks for being able to have this kind of escape hatch (e.g. Svelte’s :global, Vue’s :global() etc).


Talking about this with @tabatkins, it seems pretty straightforward. He said it may be easier to get consensus if it is scoped to not be able to specify anything outside :host. I think the vast majority of use cases are within :host, though being able to write whole rules means we can also use :host() and :host-context().

@LeaVerou
Copy link
Member Author

Talking about this with @emilio he pointed something out: this is not always as simple as "writing CSS as if the CSS was on the outside". Consider this case:

<fancier-list>
	<template shadowrootmode="open">
		<fancy-list>
			<template shadowrootmode="open">
				<slot></slot>
			</template>
			<slot></slot>
		</fancy-list>
	</template>
	<ul>
		<li>Hi</li>
	</ul>
</fancier-list>

The light DOM of the document is:

<fancier-list>
	<ul>
		<li>Hi</li>
	</ul>
</fancier-list>

The light DOM inside <fancier-list>’s shadow root is:

<fancy-list>
	<slot></slot>
</fancy-list>

So a selector targeting fancy-list li would not work in either context.
Maybe that’s okay though? Since this is a band-aid solution because we cannot have #7922 maybe it’s okay if some edge cases are missed?

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

No branches or pull requests

1 participant