jorgebucaran / superfine

Absolutely minimal view layer for building web interfaces
https://git.io/super
MIT License
1.56k stars 78 forks source link

Add support for declarative shadow dom #189

Open whaaaley opened 2 years ago

whaaaley commented 2 years ago

This addresses my issue #188.

This PR would allow you to write views that create shadow-roots. For example when using JSX:

function view () {
  return <div>
    <h1>Heading 1</h1>
    <div>The styles inside this shadow-root below are completely scoped to that node. This pairs great with importing CSS from JavaScript imports.</div>
    <div shadow-root='open'>
      <h1>
        <style>{'h1 { display: flex; padding: 12px; color: white; background: dodgerblue; } div { padding: 0 24px; }'}</style>
        <button onclick={increment}>Increment</button>
        <div>{state.count}</div>
        <button onclick={decrement}>Decrement</button>
      </h1>
    </div>
  </div>
}

Where the DOM output would look like: (The root h1 inside shadow-root is required to mount children into it and the parent div has to exist so we have a place to mount the shadow-root to.)

image

Here's a codepen of the PR in action. https://codepen.io/dustindowell/pen/rNYeLxg

If this is not something you're interested in it's no problem! I just think it's really cool to have encapsulation of styles and scripts without having to touch the custom elements api!

To briefly walk over the changes.

EDIT: Perhaps a solution to configuring the node you attach inside the shadow-root would be to not allow "fragments" inside a node which has a shadow-root property. I'll think about this and make some changes. (see changes)

EDIT 2: I also didn't account for patching. šŸ˜… So I do have some work to do still. (patching works fine)

whaaaley commented 2 years ago

After some simple tests, it seems like patch doesn't need to be changed for this to work. Let me know if I'm missing something. Here is an updated codepen that includes subsequent patching: https://codepen.io/dustindowell/pen/rNYeLxg

EDIT: Ok there's problems.

whaaaley commented 2 years ago

I believe I've fixed everything.

jorgebucaran commented 2 years ago

@whaaaley Awesome. How are you supporting JSX in your pen?

whaaaley commented 2 years ago

@jorgebucaran I'm using the typescript option in the pen settings then running that through a function based originally on zaceno's jsx pragma that I tweaked a bit. I just pasted it into the pen for experimenting.

whaaaley commented 1 year ago

@jorgebucaran I don't know if you're interested in supporting declarative shadow DOM at all. However, last year Chrome added support for declarative shadow DOM syntax. I was unaware of that syntax at the time of this PR. If you're not sold on adding support yet, totally fine, though it would help me out a getting static rendering and SSR working with Superfine views which use shadow DOM. (Isomorphic code and all that)

The syntax Chrome supports.

<div> <!-- this div has a shadow root attached -->
  <template shadowroot='open'> <!-- this template tag gets obliterated by the browser -->
    <h1>hello world</h1> <!-- contents are added to the shadow root -->
    <style>
      h1 { color: red; }
    </style>
  </template>
</div>

Here's a codepen showcasing the above syntax. https://codepen.io/dustindowell/pen/dymgvNg

Some details of my changes in my last commit to this PR. I removed what I had previously and did something different. When iterating over children I check for a shadow root template, If that check is true I "abort" appending children and forward the contents of that template to a new createNode function. This is the most efficient way I could think of doing this. On that next createNode call, I pass in the mode of the shadow root. If it exists, attach a shadowRoot and continue.

jorgebucaran commented 1 year ago

Thanks, @whaaaley. I'll have a look at this. šŸ‘