home ~ projects ~ socials

DEPRECATED: Web Component CSS Encapsulation Test For The Neb RSS Reader

Updated Introduction

This page tests Web Component Custom Elements. It works, but I'm also looking at Declarative Shadow DOM. I think it's going to make a better approach. Feels a little cleaner and less shuffling things around.

The prototype for it is here:

Declarative Shadow DOM CSS Encapsulation Test For The Neb RSS Reader

Original Introduction

I'm building a new social network1. The two biggest differences between it and other social networks are:

  1. It's built by connecting individual websites via RSS instead of relying on custom protocols or centralized sites/services.
  2. Posts can use HTML elements and CSS styles2.

This post is a prototype attempting to use Web Components to deal with the CSS3.

The Current Challenge

The primary interface to the network is an RSS reader. It combines posts into a single timeline that's output as an HTML page which is rendered in an embedded browser4.

This post is a prototype designed to examine two things:

  1. How to prevent CSS in one post from effecting other posts.
  2. How to prevent CSS in all posts from effecting the parent page.

The goal is to prove the basic approach works and present it for feedback.

The Idea

Web Component Shadow DOMs encapsulate the content they contain. The idea is to create a one that allows styling HTML without changing the styles of other posts or the parent page.

The Component

Here's the basic starting point for the web component:

JavaScript

class NebPost extends HTMLElement {
    constructor() {
        super()
    }

    connectedCallback() {
        this.attachShadow(
          { mode: 'open' }
        )

        const sourceStyles =
          this.querySelector("#css")
          
        const styles = 
          document.createElement("style")

        styles.textContent = 
          sourceStyles.innerText

        this.shadowRoot.append(
          styles
        )

        this.shadowRoot.append(
          this.querySelector("#html")
        )
    }
}

customElements.define('neb-post', NebPost)

It does two things:

  1. Grabs the content from a element with a id="css" attribute, puts it in a stylesheet, and appends that stylesheet to the instance of the component.
  2. Finds the element with the id="html" attribute and copies its contents into the output element.

The Test Run

Here's a sample usage of the component:

Head's Up

The example is live. The code you see in the code blocks is rendering what appears on the page. The results show up in the Output area below.

HTML

<neb-post>
  <code id="css">
    p { background: crimson; }
    html { padding: 10rem; }
    body { background-color: red; }
  </code>
  <div id="html">
    <p>This is the first post</p>
  </div>
</neb-post>

<neb-post>
  <code id="css">
    p { background: rebeccapurple; }
    * { color: red; }
  </code>
  <div id="html">
    <p>This is the second post</p>
  </div>
</neb-post>

Output

p { background: crimson; } html { padding: 10rem; } body { background-color: red; } p { background: rebeccapurple; } * { color: red; }

The first neb-post has styles for p tags which are applied. It also has styles for html and body which have no effect on the page. Exactly what I want to happen.

The second neb-post also has a p style applied. It has an additional * setting the text color to red. It's applied inside the neo-post but doesn't escape out of it. Again, exactly what I'm looking form.

So Far, So Good

The app uses the native browser on each platform. I also want to make sure the approach works on stand-alone browsers. So far, I've testing on:

  • macOS 15.4 - Chrome, Firefox, Safari
  • Window 10 - Chrome, Edge, Firefox
  • iPhone 16 Simulator (iOS 18.2) - Safari
  • Android Virtual Device Manger - Pixel 4 API 32 - Chrome

Everything passes the sniff test. There's still more to figure out, but this is a good start.

Please, Send Me Your Notes

My biggest question is if there's a way for the CSS from a post to break out of its Shadow DOM. My understanding is that there isn't. But, I'm not an expert in Web Components.

If you have feedback, please let me know. You can reach me on Mastodon and Bluesky.

-a

-- end of line --

Endnotes

  • This is the second prototype. The first one attempted to use CSS nesting. That approach doesn't guarantee containing the styles. The feedback I got from it pointed me to this Web Component approach.

  • This prototype doesn't do any scrubbing of the HTML/CSS. The mechanisms to do that happen before the <neo-post> elements are populated.

  • I also tested the components with

    *:has(&){
      color:green;
    }
    
    & + neb-post {
      color:blue;
    }

    Those are what allowed styles to break out of their containers in the CSS only prototype. They remained contained with the web component.

Footnotes

1 ⤴

This is the post where I'm refining ideas for Neb. A new social network built by networking websites.

(The name Neb comes from portmanteau of "networked" and "websites").

2 ⤴

Opening up the available HTML and CSS provides an incredible increase in the posts that can be created. Each one will be like its own embedded web page.

Determining which elements and styles can be used securely is the topic of other posts.

It's worth mentioning here that raw JavaScript is not allowed. However, there's a mechanism for using web components that have been authorized by the person using the app.

Details on that are the topic of other posts.

3 ⤴

The Shadow DOM available in Web Components offers strong encapsulation of content that seems like the perfect fit.

4 ⤴

The app is built in Tauri which utilizes native browsers for display.