CSS needs a name prefix selector
𒎏Wii 🏳️‍⚧️

𒎏Wii 🏳️‍⚧️ @darkwiiplayer

About: Blog: https://blog.but.gay Mastodon: @darkwiiplayer@tech.lgbt Pronouns: en.pronouns.page/@darkwiiplayer

Joined:
Dec 4, 2018

CSS needs a name prefix selector

Publish Date: Aug 27 '24
11 9

I love working with custom elements for all kinds of things, but sometimes I just want to hide stuff until it is loaded or do something else with it in CSS.

A simple solution looks like this:

framework-button:not(:defined) {
   display: none
}
Enter fullscreen mode Exit fullscreen mode

Put that in a <style> tag in the HTML and the button will just appear once it's loaded.

But with larger frameworks, this becomes really annoying. Sometimes you can get away with simply selecting :not(:defined), but that's not always viable.

An obvious (at least to me) fix: CSS needs a name prefix selector, so you can just do this

framework-*:not(:defined) {
   display: none
}
Enter fullscreen mode Exit fullscreen mode

It's not like this would be an entirely new thing. We can already kind of do this with attribute selectors.

And I'm sure allowing splits only at - in an element's name would make it reasonably easy to implement this effectively in browsers too.

What do y'all think? Would this be useful? Are there easier solutions that already work?

Comments 9 total

  • Ben Sinclair
    Ben SinclairAug 27, 2024

    I wouldn't find it useful myself because I can't imagine using a framework that way.
    That said, since you can select based on partial attributes already, it makes sense to me to allow selecting by partial tag names as well, if only for consistency.

    • 𒎏Wii 🏳️‍⚧️
      𒎏Wii 🏳️‍⚧️Aug 27, 2024

      I'm using "framework" very loosely there; if I wanted to be pedantic, "component library" would have probably been a more precise word to use ;)

  • Andrew Bone
    Andrew BoneAug 27, 2024

    I like it but I think something like

    [tag^="framework-"]:not(:defined) {
      display: none;
    }
    
    // OR
    
    :tag("^framework-"):not(:defined) {
      display: none;
    }
    
    
    Enter fullscreen mode Exit fullscreen mode

    would be more consistent with what we already have.

    • 𒎏Wii 🏳️‍⚧️
      𒎏Wii 🏳️‍⚧️Aug 28, 2024

      I don't particularly like [tag^="framework-"] because that's already a selector that checks a tag= attribute.

      The :tag() rule looks a lot better imho. I'm not sure if it would be better to have a dedicated prefix selector that maybe browsers could optimise for, or just a generic tag name matching mechanism where you can do prefix, suffix, infix, or whatever you need for your project.

      I honestly haven't put an awful amount of thought into this; it's really just a vague "I wish this was a thing" more than any serious proposal 🤭

      In defense of framework-* as a selector:

      1. it looks like a node name selector, which it kind of is
      2. the * at the end is similar to the actual * selector

      So it combines two concepts that people already know into something that, I hope, makes it easy to piece together what it's doing :)

  • Danny Engelman
    Danny EngelmanAug 28, 2024

    The is attribute is the safest attribute to do this after your Web Component sets it with this.is = this.localName (or set it yourself when you need it with :defined)
    Because is is reserved for Customized Built-In Elements, which Apple/Safari will never support

    • 𒎏Wii 🏳️‍⚧️
      𒎏Wii 🏳️‍⚧️Aug 28, 2024

      Yes, there's plenty of ways to hack this in; one could also just use a helper class like <framework-button class="hide-until-defined"></framework-button>, but the point is to achieve this in CSS.

      • Danny Engelman
        Danny EngelmanAug 29, 2024

        If there was a need it would have been implemented a long time ago.
        Attribute selections are possible since IE7; they are a performance hit.

        And yes, they are on my (MSCW) Would-Have list too,

        so I can do <queen-of-hearts> instead of <playing-card is="queen-of-hearts">

        But the latter works just fine, even more now the Custom Element can set the is value

  • Sean
    SeanAug 29, 2024

    This is a cool idea.

  • Besworks
    BesworksMar 12, 2025

    Having a CSS native way to do this would be fantastic. If there is not a proposal for this feature there should be. You can accomplish something equivalent by adding this magic little js snippet before ANY other DOM element is loaded. That is to say, OUTSIDE your <html> element, directly below the <!DOCTYPE html> declaration and before any other element.

    <!DOCTYPE html>
    <script>
      const style = document.createElement('style');
      document.head.appendChild(style);
    
      const observer = new MutationObserver((mutations) => {
        for (const mutation of mutations) {
          for (const node of mutation.addedNodes) {
            if (node.nodeType === 1 && node.tagName?.startsWith('CUSTOM-')) {
              style.textContent += `${node.tagName}:not(:defined) { display: none; }`;
            }
          }
        }
      });
    
      observer.observe(document, {
        childList: true,
        subtree: true
      });
    </script>
    <title>Custom Elements Partial Name Selector</title>
    <custom-element-1> test </custom-element-1>
    <custom-element-2> test </custom-element-2>
    <script>
      customElements.define(
        'custom-element-1', class extends HTMLElement {}
      );
    </script>
    
    Enter fullscreen mode Exit fullscreen mode

    This works due to this note in HTML5 - 8.2.5 Tree Construction :

    The HTML parser generates implied tokens when parsing elements if they are missing but required by the tree structure. When the parser reaches a script element, even before the tag is encountered, it switches to the "in head" insertion mode. This allows scripts to execute during the "initial" and "before html" insertion modes, which is why scripts can run and manipulate the DOM even before explicit structural elements are parsed.

    You can load this from an external file, just make sure it's cached for best performance and do not make it async or type="module" or it will not work.

    <!DOCTYPE html>
    <script src="./not-defined.js"></script>
    <title> External Script Example </title>
    <custom-element-1> test </custom-element-1>
    
    Enter fullscreen mode Exit fullscreen mode
Add comment