-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Update Preflight html
styles to include shadow DOM :host
pseudo-class
#11200
Update Preflight html
styles to include shadow DOM :host
pseudo-class
#11200
Conversation
This would be great for Tailwind in the Shadow DOM, especially for Maybe worth noting, maybe not - but using Tailwind in shadow DOM would make global styles irrelevant, like the |
2f3e79b
to
457b0a6
Compare
@RobinMalfait Nvm after merging your recent sass and cssnano version bumps, the build errors disappear AFAICT |
Question: does this affect how the Tailwind config might add rules to this selector? |
@jonathandewitt-dev no, I don't believe this preflight style affects how any selectors are built. @RobinMalfait can you confirm this? |
That might be the desired result, actually. I can imagine a situation where someone is using Tailwind in the shadow DOM, but their configuration generates a stylesheet that targets selectors outside the scope. Like, the theme configuration, for example. |
80e84b9
to
205dc2f
Compare
It shouldn't affect this 👍 I rebased this PR on top of master such that we only have to look at a single commit instead of 10+ commits 👍 |
@RobinMalfait Thanks! To @jonathandewitt-dev's point, I think he was asking if you use the config to override some default root-related styles like font-size or line-height, do those get applied to Although, to my knowledge, overriding any such values in the config only affects utilities and not any styles actually applied at the root. Could you confirm this as well? Thanks again! |
That's precisely what I was asking, yep! Where Tailwind normally replaces styles on I confirmed that the configuration does indeed modify styles in the /** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {
fontFamily: {
sans: ['Orbitron'],
},
},
},
plugins: [],
} |
@jonathandewitt-dev Ooo interesting I wasn't aware it did that. I'll poke around the codebase a bit more to see if I can find where that's coming from. |
205dc2f
to
6e198a5
Compare
@brandonmcconnell you may want to include
|
I am not sure if adding It may be worth considering making the root selector user configurable, so the default could be |
@brandonmcconnell Before we explore merging this, can you share some very concrete examples (with code + demos) of things that someone might want to do that currently don't work in Tailwind that would work when this is merged? Will make it a lot easier to make a decision on 👍 |
@adamwathan - In essence, this PR allows tailwind styles to permeate all scopes, even the ShadowDOM, rather than only the top document. @jonathandewitt-dev made a solid point about the implications of this in regard to the ShadowDOM here: #11200 (comment) @jonathandewitt-dev, could you share any other helpful examples or information contributing to the value of this change, seeing as you are the author of the package you mentioned? |
There's not much to add that I haven't already said, but just for a summary - adding The use case is primarily to avoid clashing with other libraries. Tailwind depends on global CSS classes, which may collide with custom or other third party classes. For incremental adoption, or even just adoption in an isolated micro-frontend, it would help to have that two-way encapsulation that protects those styles from global collisions. The package I authored is not popular, but the approach is not unusual. It might serve as an illustration of how this could be used in a real-world scenario. What's happening above, under the hood, is Tailwind's main CSS file is being converted to a single constructed style sheet, which can then be adopted/shared by each of these shadow roots. |
Thanks, @jonathandewitt-dev! In short, the ShadowDOM is the primary way to set up custom web components (not to be confused with JS framework components), doesn't inherently have access to the global scope and cannot use Tailwind's preflight styles as they do not use an This PR retains support for the existing Tailwind CSS. Everywhere you need it. |
I've never done any Shadow DOM stuff so forgive me for needing some real ELI5 explanations here with extremely concrete examples. What I'd love to see is a demo that shows something that is currently broken that I can open in my browser and see being broken and understand why that's surprising — does that make sense? For example, based on what I'm getting out of what you guys are saying, it sounds like maybe one example of this could be that using the I literally don't even know how styling works in the Shadow DOM at all to be clear, I am absolutely and completely ignorant of this technology 😂 Is the idea here that if you mount a normal Tailwind-generated stylesheet within a shadow DOM tree, that the classes and stuff in that stylesheet work, but anything targeting Are there any downsides or things that would break? Could anyone be relying on that behavior today deliberately? |
Sure thing! I think there is a multitiered answer to this question, so I'll try to explain in steps. At its most basic, here is a minimal example of shadow DOM (this uses its declarative syntax, so no JS is needed here.) <style> .container { background-color: red; } </style>
<div class="container">This is outside the shadow DOM. It honors global styles.</div>
<div>
<template shadowrootmode="closed">
<style> .container { font-size: 2rem; } </style>
<div class="container">This is inside the shadow DOM. It inherits nothing from global styles. Styles defined inside here don't leak out of this scope.</div>
</template>
</div>
<div class="container">Even after the shadow DOM above, the styles inside it don't cascade to affect the outside world.</div> In the devtools, you'll see the shadow root appear differently, because it's more or less "hidden" away from the outside world. Originally, shadow DOM was not exposed for developers to use. It was used to style things like the video playback controls in a This is useful for Tailwind because it guarantees isolated scopes that cannot be touched by third-party styles. If a project is using Bootstrap, for example, you could still use Tailwind without worrying about whether it's compatible. Just basically ignore it, and guarantee your Tailwind styles look the same no matter where they're used. <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-4bw+/aepP/YC94hEpVNVgiZdgIC5+VKNBQNGCHeKRQN+PtmoHDEXuppvnDJzQIu9" crossorigin="anonymous">
<x-tailwind>
<template shadowrootmode="open">
<style>/* tailwind styles here */</style>
<div class="text-slate-900 font-extrabold text-2xl">
Tailwind could be used inside the shadow DOM, instead of globally. This could be great for incremental adoption!
</div>
</template>
</x-tailwind> Now, there's a couple problems here.
To resolve number 1 here is pretty interesting. Constructable Stylesheets allow you to use the browser's CSS parser to create a single stylesheet reference that can be passed around by JavaScript. You then can "adopt" these stylesheets from either the global const tailwindEl = document.querySelector('x-tailwind');
const stylesheet = new CSSStyleSheet();
stylesheet.replaceSync(/* tailwind css string */);
tailwindEl.shadowRoot.adoptedStyleSheets = [stylesheet]; This is nothing Tailwind should specifically be responsible for, but since you wanted a detailed explanation, I figured I'd throw this in there too. My package essentially takes care of all this for you, with React. Moving on to number 2, this part falls on Tailwind.
Yes, each shadow root does not own its own You can demonstrate this issue in action: <div>
<template shadowrootmode="open">
<style>
html {
line-height: 1.5;
-webkit-text-size-adjust: 100%;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
font-feature-settings: normal;
font-variation-settings: normal;
}
</style>
<div>
The HTML selector doesn't select anything in this scope. Try swapping the <code>html</code> selector with the <code>:host</code> selector and watch what happens.
</div>
</template>
</div>
When used at the global level,
Also worth mentioning, I think it would be useful to add a quick little But that may end up being branched off into its own separate conversation, tbh. Personally, I would be happy even with just |
Supports SVG root scope, ShadowDOM, and presumably other root scopes
87012b2
to
8640184
Compare
@reinink Did you notice any adverse effects to using
|
After mulling it over, I suppose someone could be depending on that level of specificity. If somone added |
Hey @jonathandewitt-dev, thanks so much for that detailed explanation. I meant to respond sooner after making those changes (actually had the following written up already), but got distracted with something else. I think we've got a decent handle on how this shadow DOM stuff works now, and I think your last solution is actually the right approach — to use Our concern with using @tailwind base;
html {
line-height: 1;
} As for the suggestion to add Given all that, I've gone ahead and updated this PR to use Thanks to you and @brandonmcconnell for this contribution 👍 |
html
styles to support other :root, :host
scopeshtml
styles to include shadow DOM :host
pseudo-class
This PR simply replaces
html
with:root, :host
to support all:root
and:host
contexts::root
supports…html
(same as plainly usinghtml
):host
supports…