-
Notifications
You must be signed in to change notification settings - Fork 27.2k
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
Style isolation through Shadow DOM #22
Comments
Could this work with styled-components? https://styled-components.com |
Styled-components will indeed do exactly this |
hey @rauchg here is a codepen of what I was talking about yesterday at the React Vienna meetup. As you can see it is not perfect and probably server-side rendering is not an option – SSR should fallback to some other solution I am afraid or serve a skeleton that gets enhanced on the client. There is an old PR where Pete Hunt made a POF for adding ShadowDOM support. Styled components are nice but imho the codebase is a bit too heavy and, as I said yesterday, they don't provide full styles encapsulation You may want to take a look at CSJS which has the same "problems" but it is quite lightweight. FWIW we added an experimental It is not perfect but I believe that in CSS-in-JS it'd be more robust – in fact we might add a similar feature to JSS. |
I also need to add that JSS has already inheritance isolation using jss-isolate plugin. It isolates all rules by default but allows to avoid isolation on per-rule basis. This is basically what Pete Hunt promoted recently. |
@orta @Munter styled-components should already work! To clarify, the "dream" here is as follows. You write export default ({ text }) => (
<button>{ text }</button>
<style>{`
button {
border: 1px solid red;
background: #fff;
padding: 10px;
}
`}</style>
) The idea here is that the Now: what happens if you instantiate multiple export default shadow(
() => (
<button/>
),
<style>{`
/* … */
`}</style>
) Also, the server-render story of custom elements / shadow dom is also a big question mark for me. |
Perhaps I'm missing something here, but couldn't you use a higher-order component that would assign a unique id to its rendered component and encapsulate within the scope of the unique id? const MyComponent = () => (
<button>{ text }</button>
<style>{`
button {
border: 1px solid red;
background: #fff;
padding: 10px;
}
`}</style>
)
export default StyleWrapper(MyComponent) Rendering something like: <div id="asdf1234">
<button>Click me</button>
<style>
#asdf1234 {
button {
border: 1px solid red;
background: #fff;
padding: 10px;
}
}
</style>
</div> |
glamor itself has work in progress to allow 'real' inline css, taking support for syntax highlighting and linting from styled components' work. https://twitter.com/threepointone/status/790268053124632576/ the advantage of using glamor for the same, is we can optionally 'precompile' the source with a babel plugin and eliminate the need for css parser in the bundle / runtime. https://twitter.com/threepointone/status/791232832865603584/ all glamor goodies would still apply - SSR, nesting, etc. |
@threepointone inheritance still applies right? If so it is still missing one feature |
@giuseppeg yes, you're right. this isn't proposed as an alternative to SD, but more as an alternative 'css' syntax to glamor. |
Just to chime in here
Working on this for |
Sorry this is inaccurate, see similar issue. |
@rauchg You should take a look at the Shadow DOM v1 spec. https://github.com/polymer/polymer/tree/2.0-preview#scoped-styling |
@dstreet that's an interesting solution. I'll add that the main problem it has is that it still requires us to parse the entire CSS grammar to prefix all the selectors with that ID. It also introduces some unexpected side effects. If we wrap with
so it would have to be Also, the issue of not being able to "server-render" shadow DOM still stands. If we merely prefix, child elements of child components would get unexpected styles |
@threepointone that's the most solid solution right now. We might include that babel extension by default, or have people extend babel to add it themselves in |
Here's is a webpack plugin for css literals. const styles = css`
.button {
color: black;
border: 1px solid black;
background-color: white;
}
`;
const Button = () => <button className={styles.button} />; |
@rauchg Here you go: webcomponents/shadycss#3 |
someone may find this useful. here's how i did it with scoping. http://jsbin.com/jeweqis/7/edit?js,output it works by providing a |
@rauchg fwiw here's how I addressed these problems in Ripple:
:host { }
:host > .dropdown { }
:host > .dropdown > li { } Using |
Yes I believe each shadow root gets its own
My take is that the actual styles get shared (hence the hash table lookup). So I don't think this should be a performance concern. Regarding the other question:
Custom Elements have lifecycle callbacks for when they're constructed, connected/disconnected from the DOM, and anytime their attributes change. On the server you'd probably want to ensure that these lifecycle callbacks get triggered. There's a library that does this today called server-components. I guess a next.js element could choose to ignore these callbacks if you have your own system for bootstrapping a component and rendering its template, but someone may include a vanilla js custom element in their app and you might want that element to stamp its template server side as well. cc @treshugart |
@robdodson it's still not clear to me what the DOM markup string that the server outputs looks like for shadow DOM / CSS? |
btw thanks everyone for your insight, super interesting thread! |
This is non-trivial. I commented on a Polymer issue relating to that. There's also a w3c issue that's been closed due to no concrete proposals for declarative shadow roots. Basically the major things that need to be answered are:
Number 4 may be moot if we can assume 5 is sync as the nodes are rendered. If this was pure web components, custom element definitions can be loaded async. It could just be a requirement of SSR web components to assume all definitions are loaded before the HTML is piped. |
I basically can't use Next.js because it doesn't support traditional CSS. My projects have to use existing CSS files and having consistent class names does make browser-based testing easier. I really just want:
|
@rws-github actually, you can use traditional CSS too, by loading style files through export default () => (
<div>
<Head><link rel="stylesheet" href="/static/styles.css"></Head>
<div className="myStyle">hi</div>
</div>
) |
@nkzawa Thank! That worked. |
@robdodson, @treshugart: I think it might be better to differentiate between "rendering a web component in next.js" (that potentially uses all the functionality like slots etc) with just "adding style encapsulation for next.js components". The former scope is relatively a lot bigger, will be very hard to reliably and without bloat.
@rauchg: just as it is today without shadow roots! Consider the HTML output today a baseline that works everywhere, and the Shadow DOM on the client as a progressive enhancement if available. This approach also dovetails pretty well in a unidirectional architecture with declarative components. |
Not sure what you're referring to here. You don't have to go all in on WC. Just Shadow DOM webcomponents/shadycss#3 |
Yep, that was my point :) There's some low hanging WC fruits (e.g. invoking ShadyCSS looks pretty good. The API doesn't quite make sense to me though: Why is limited to a I use const scope = require('cssscope')
, styles = `
:host { }
:host > .dropdown { }
:host > .dropdown > li { }`
, scoped = cssscope(styles, 'd3-chosen')
// scoped:
// d3-chosen { }
// d3-chosen > .dropdown { }
// d3-chosen > .dropdown > li { } |
@rws-github in general we advocate you use |
Agreed, I was just responding within the context of the original issue. |
@treshugart Are you planning to make something like nextjs for skatejs? Then it would really easy to adopt? |
@pyros2097 it's likely something will happen at some point (open issue here for tracking it. I'd prefer a generic solution for web components, though. Making something Skate specific wouldn't help the rest of the community. I don't want to derail from this conversation. Happy to discuss in Skate's issues :) |
FWIW angular2 has a nice strategy for a similar problem using ViewEncapsulation. It would probably have to be modified a bit to fit less structured usage, but perhaps worth looking into |
@traviskaufman does angular have support for server-rendering components with isolated styles? That's a requirement for Next.js |
So i built stylis.js, that when given a string and namespace it returns css that is isolated to that namespace and prefixed, it's low level so you might be able to build an abstraction upon it. |
@rauchg I believe so. angular/universal render's components server-side and it uses the same style system as angular2's browser platform. |
I want to revisit this when web components have a server-rendering story (WICG/webcomponents#71). For now, |
For the vast number of users who with very good reasons reject CSS-in-JS, we could support something like this:
In the future, if we find a way of making this work with server-rendering, we could make every single component a custom element with built-in isolation capabilities out of the box, no
shadow()
wrapping needed. If you want global effects, you can use<Head>
, otherwise everything is isolated.This would put CSS-in-JS and full CSS support on equal ground, both supported, both with pros-and-cons and great performance.
The problems:
The text was updated successfully, but these errors were encountered: