Skip to content

Instantly share code, notes, and snippets.

@treshugart
Last active May 6, 2024 05:04
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save treshugart/b6aeac0df903f7d0efb7fae5b27bca22 to your computer and use it in GitHub Desktop.
Save treshugart/b6aeac0df903f7d0efb7fae5b27bca22 to your computer and use it in GitHub Desktop.

Defining a scoping inner-boundary in CSS

Concretely, I want to implement simulated CSS scoping for react-shade. This uses the imperative Shadow DOM APIs, by default, to enable scoping on the client. However, in order to get fully-scoped CSS when using SSR, we need either:

  • A declarative API for shadow DOM (closed due to lack of implementer interest), or
  • A way to tell CSS to stop selecting elements in slots. So, we'd need to be able to select between an ancestor and a descendant in a given DOM tree.
  • ...?

I am currently able to find solutions for :host and :host-context, and can scope stuff to descendants of a shadow root tree, the hard part is just simulating the exclusion of slotted content.

Notes

  • The content in the slot is meant to represent slotted (light DOM) content as this is how it would have to be SSR'd.
  • When applying simulated scoping, the CSS in <style> would be transformed so that it would do so.
<p class="title">Should *not* be styled by `.title`. This is fairly simple by prefixing `.title` with a something unique to the root.</p>
<root>
<style>
/* This is how it would be written by a consumer. */
.title { ... }
/* This is how it could be transformed to scope to the root. */
[__root_scope="1"] .title { ... }
/* Unsure how we transform it to only apply between the root and the slot. Just a thought for the sake of an example... */
[__root_scope="1"] .title:stop-cascading-at(slot)
</style>
<p class="title">Should be styled by `.title`. As mentioned above, this is pretty straight forward.</p>
<slot>
<p className="title">Should *not* be styled by `.title`. This is the hard part.</p>
</slot>
</root>
@bkardell
Copy link

by prefixing .title with something unique to the root

meaning that you will somehow rewrite the <style> rules?

@treshugart
Copy link
Author

meaning that you will somehow rewrite the <style> rules?

Yes, sorry, I had left that implied. Will clarify.

@bkardell
Copy link

bkardell commented Jan 29, 2019

if you already are rewriting the style rules, can you just be more specific about the target, like
root > .title, root :not(slot) .title

@treshugart
Copy link
Author

The only issue there is that :not(slot) doesn't select a .title that is a child of the root because it requires there be an element that matches :not(slot) between them.

@bkardell
Copy link

bkardell commented Jan 29, 2019

that's why there are two above... it's possible you replied before I very quickly realized that and fixed it - but that should work, no?

@treshugart
Copy link
Author

That I did. It works in theory, but I'm having issues with it still selecting stuff in the slot. I'll have a tinker. Thanks you so much for your help!

@bkardell
Copy link

can you show me in a glitch or something? I am intrigued.

@bkardell
Copy link

ah... yeah, wait that isn't going to work more than one deep... hmm.. yeah, if you have like a running example I would love to look around and poke.

@treshugart
Copy link
Author

ah... yeah, wait that isn't going to work more than one deep...

Yeah, that was the problem. I've been fiddling with the transformation.

If you:

  1. Clone https://github.com/treshugart/react-shade.
  2. npm i
  3. npm start
  4. Selectors are transformed at https://github.com/treshugart/react-shade/blob/master/src/internal/css.ts#L33.
  5. You can mess with the example at: https://github.com/treshugart/react-shade/blob/master/pages/index.tsx#L53-L65.

You should be able to see the flash of styling between SSR and client side upgrading when you refresh.

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