CSS Web Components for marketing sites (2024)

(hawkticehurst.com)

105 points | by zigzag312 12 hours ago

13 comments

  • sublinear 0 minutes ago
    My experience with marketing pages is that they usually have a ton of inconsistent design requirements and change frequently.

    Most "frameworks" are the wrong tool because they assume that the markup and design (HTML/CSS) won't change as much as the functionality (JS) when it's exactly the opposite situation.

    All the consistency needs to be concentrated in the JS without the baggage of any particular HTML/CSS in mind.

    The only aspects of a framework you should want are a flexible way to register event listeners onto the elements, and organizing the styles and callbacks.

    In practice, this ends up looking like a static HTML file that is not up to the developers how to organize apart from the usage of CSS classes because it will be audited by many non-devs, a Sass build derived from design guidelines with some alt classes to contain the mess when people change their minds, and some very robust JS that you're gonna have to write almost entirely from scratch.

    I still don't get why this scares some people off though. You won't ever need to (re)write that much JS. Every new page design is just remapping existing functionality to the new HTML IDs, and maybe every now and then adding new functionality. Most of your time will be spent in CSS which just plain makes sense!

  • spankalee 10 hours ago
    I'm a big web components guy, but calling these web components is a massive stretch of the word component.

    The word "component" has to mean something ultimately, and to me the defining feature of a web component is that it's self-contained: it brings along its own dependencies, whether that's JavaScript, templates, CSS, etc. Web components shouldn't require an external framework or external CSS (except for customization by the user) - those things should be implementation details depended on directly by the component.

    This here is just CSS using tag names for selectors. The element is doing nothing on its own.

    Which is fine! It's just not web components.

    edit: Also, don't do this:

        <link-button>
          <a href="">Learn more</a>
        </link-button>
    
    That just adds HTML bloat to the page, something people with a singular focus on eliminating JavaScript often forget to worry about. Too many HTML elements can slow the page to a crawl.

    Use classes:

        <a class="button" href="">Learn more</a>
    
    They're meant for this, lighter weight, and highly optimized.
    • rafram 10 hours ago
      > To many HTML elements can slow the page to a crawl.

      You can read the entirety of War and Peace in a single HTML file: https://standardebooks.org/ebooks/leo-tolstoy/war-and-peace/...

      A marketing page, SaaS app landing, etc., will not even begin to approach that size, whether or not you add an extra wrapper around your <a>s.

      • Etheryte 6 hours ago
        This is true, yet I've seen plenty of poorly built webapps that manage to run slowly even on a top tier development machine. Never mind what all the regular users will get in that case.
      • shimman 9 hours ago
        Almost 15,000 elements! I do agree that too many elements can slow a page but from my experience that starts to happen a few hundred thousand elements, at least that's what we'd run into making data visualizations for network topologies (often millions of nodes + edges) but the trick for that was to just render in canvas.
      • wnevets 7 hours ago
        Thank you for this example. I'm going to keep it in mind the next time I asked myself if there are too many elements or not.
      • atoav 9 hours ago
        This is a wonderful example how people live in the inverse-world.

        Marketing is in the end a way of trying to get people to listen, even if you have nothing substantial to say (or if you have something to say, potentially multiply the effect of that message). That means you have to invent a lot of packaging and fluff surrounding the thing you want to sell to change peoples impression independent of the actual substance they will encounter.

        This to me is entirely backwards. If you want people to listen focus on your content, then make sure it is presented in a way that serves that content. And if we are talking about text, that is really, really small in terms of data and people will be happy if they can access it quickly and without 10 popups in their face.

        Not that I accuse any person in this thread of towing that line, but the web as of today seems to be 99% of unneeded crap, with a tiny sprinkle of irrelevant content.

        • j45 7 hours ago
          The experience also depends on the desired outcome, and who's outcome that is. The marketers? or the readers? Which comes first? How far should it go?
      • croisillon 8 hours ago
        nice, Firefox Reader Mode tells me i need 2968 to 3774 minutes
    • dfabulich 8 hours ago
      HTML elements can style themselves now using the @scope rule. (It's Baseline Newly Available.) Unlike the "style" attribute, @scope blocks can include @media and other @ rules. You can't get more self-contained than this.

          <swim-lane>
              <style>
                  @scope {
                      background: pink;
                      b {
                          background: lightblue
                      }
                      @media (max-width: 650px) {
                          /* Mobile responsive styles */
                      }
                  }
              </style>
              something <b>cool</b>
          </swim-lane>
      
      You can also extract them to a CSS file, instead.

          @scope (swim-lane) { /* ... */ }
      
      The reason approaches like this continue to draw crowds is that Web Components™ as a term is a confluence of the Custom Elements JS API and Shadow DOM.

      Shadow DOM is awful. Nobody should be using it for anything, ever. (It's required for putting child-element "slots" in custom elements, and so nobody should use those, either.) Shadow DOM is like an iframe in your page; styles can't escape the shadow root and they can't get into the shadow root, either. IDs are scoped in shadow roots, too, so the aria-labelledby attribute can't get in or out, either.

      @scope is the right abstraction: parent styles can cascade in, but the component's styles won't escape the element, giving you all of the (limited) performance advantages of Shadow DOM with none of the drawbacks.

      • no_wizard 5 hours ago
        Decoupling slots from shadow dom would make custom elements even better.

        I love custom elements. For non React.js apps I use them to create islands of reactivity. With Vue each custom element becomes a mini app, and can be easily lazy loaded for example. Due to how Vue 3 works, it’s even easy to share state & routing between them when required.

        They should really move the most worthwhile features of shadow dom into custom elements: slots and the template shadow-roots, and associated forms are actually nice.

        It’s all the extra stuff, like styling issues, that make it a pain in the behind

        • spankalee 5 hours ago
          There's really no way to decouple slots for shadow roots.

          For slots to work you need a container for the slots that the slotted elements do not belong to, and whose slots are separated from other slot containers. Otherwise you can't make an unambiguous relationship between element and slot. This is why a shadow root is a separate tree.

          • dfabulich 3 hours ago
            Agreed. The way I explain it is: suppose you have a `<super-table>` element, and you have a child slot called, for example, `<super-row-header>`. Presumably you want to write some JS to transform the slotted content in some way, decorating each row with the header the user provided.

            But, if you do that, what happens to the original `<super-row-header>` element that the user provided? Maybe you'd want to delete it…? But how can you tell the difference between the user removing the `<row-header>` and the custom element removing it in the course of its work?

            What you'd need is for `<row-header>` to somehow exist and not exist at the same time. Which is to say, you'd have one version of the DOM (the Light DOM) where the slot element exists, and another version of the DOM (the Shadow DOM) where the `<row-header>` element doesn't exist, and the transformed content exists instead.

            It's clever, I guess, but the drawbacks far outweigh the benefits.

            Client-side components inherently require JS anyway, so just use your favorite JS framework. Frameworks can't really interoperate while preserving fine-grained reactivity (in fact, Shadow DOM makes that harder), so, just pick a framework and use it.

      • spankalee 8 hours ago
        That's styling itself sure, but it's not self-evidently self-contained. Does every component emit those styles? Are they in the page stylesheet? How do they get loaded?

        Counterpoint: Shadow DOM is great. People should be using it more. It's the only DOM primitive that allows for interoperable composition. Without it you're at the mercy of frameworks for being able to compose container components out of internal structure and external children.

        • dfabulich 7 hours ago
          https://2025.stateofhtml.com/en-US/features/web_components/

          Sort by negative sentiment; Shadow DOM is at the top of the list, the most hated feature in Web Components. You can read the comments, too, and they're almost all negative, and 100% correct.

          "Accessibility nightmare"

          "always hard to comprehend, and it doesn't get easier with time"

          "most components don't need it"

          "The big issue is you need some better way to some better way to incorporate styling from outside the shadow dom"

          > It's the only DOM primitive that allows for interoperable composition.

          There is no DOM primitive that allows for interoperable composition with fine-grained reactivity. Your framework offers fine-grained reactivity (Virtual DOM for React/Preact, signals for Angular, runes for Svelte, etc.) and any component that contains another component has to coordinate with it.

          As a result, you can only mix-and-match container components between frameworks with different reactivity workflows by giving up on fine-grained reactivity, blowing away the internals when you cross boundaries between frameworks. (And Shadow DOM makes it harder, not easier, to coordinate workflows between frameworks.)

          Shadow DOM sucks at the only thing it's supposed to be for. Please, listen to the wisdom of the crowd here.

          • spankalee 5 hours ago
            > There is no DOM primitive that allows for interoperable composition with fine-grained reactivity. Your framework offers fine-grained reactivity (Virtual DOM for React/Preact, signals for Angular, runes for Svelte, etc.) and any component that contains another component has to coordinate with it.

            This just isn't true - composition and reactivity are completely orthogonal concerns.

            Any reactivity system can manage DOM outside of the component, including nodes that are projected into slots. The component's internal DOM is managed by the component using whatever reactivity system it desires.

            There are major applications built this way. They make have a React outer shell using vdom and Lit custom elements using lit-html for their shadow contents.

            On top of those basics you can also have cross-shadow interoperable fine-grained reactivity with primitives like signals. You can pass signals around, down the tree, across subtrees, and have different reactivity systems use those signals to update the DOM.

            • dfabulich 3 hours ago
              You can do it, but that undermines the whole point of React: fine-grained reactivity.

              If the child component had been a React component instead, the children would have participated in the virtual DOM, and React could do a minimal DOM update when it was done.

              React can't even see the shadow contents of Lit elements, so React just has to update the light DOM and let Lit take over from there.

              (Same applies to Vue, Angular, Svelte, Solid, etc. Each framework has a reactivity system, and you have to integrate with it to get its benefits.)

              • spankalee 1 hour ago
                You still get minimal DOM updates crossing shadow boundaries. This is true fact.

                React's vdom without shadow DOM passes props to components, which all return one big vdom tree, and then there's one big reconciliation. React used with shadow DOM evaluates smaller vdom trees per shadow root, and does more, but smaller reconciliations. It's the same O(n) work.

                But in reality it's often much _better_ with shadow roots because the common WC base classes like Lit's ReactiveElement all do change detection on a per-property basis. So you only regenerate vdom trees for components with changed props, and with slots that doesn't include children. So if children of a component change, but props don't, the component doesn't need to re-render. You can do something similar by hand with memo, but that doesn't handle children separately. The compiler will, of course, fix everything.

                Every other reactivity system works fine across shadow boundaries. Even the super-fine grained ones like Solid. The only issue with signals-based libraries like Solid is that they pass signals around instead of values, so to get true no-re-rendering behavior with web components you have to do that to, which means picking a signals library, which means less interoperability. The TC39 signals proposal points to a future where you can do that interoperably too.

          • 9dev 6 hours ago
            I feel like it’s a niche feature that got way too much attention. In a past job, we maintained a widget customers could embed onto their page. How much trouble we had with parent styles creeping into our widget and ruining the layout! This would have been so much easier with shadow DOM effectively isolating it from the customer site; that is the only valid use case for it, I feel.

            Yet, for actual web components, I entirely agree with you.

        • yawaramin 7 hours ago
          Yeah but most people don't need or want 'interoperable composition', they want sites with a consistent look-and-feel. Shadow DOM makes this much more difficult.
    • lucideer 10 hours ago
      There's a lot of contradictions in this comment.

      > it's self-contained: it brings along its own dependencies, whether that's JavaScript, templates, CSS

      > Also, don't do this [...] That just adds HTML bloat to the page, something people with a singular focus on eliminating JavaScript often forget to worry about. To many HTML elements can slow the page to a crawl.

      A static JS-less page can handle a lot of HTML elements - "HTML bloat" isn't really a thing unless those HTML elements come with performance-impacting behaviour. Which "self-contained" web-components "bringing along their own dependencies" absolutely will.

      > shouldn't require an external framework

      If you're "bringing along your own dependencies" & you don't have any external framework to manage those dependencies, you're effectively loading each component instance as a kind of "statically linked" entity, whereby those links are in-memory. That's going to bloat your page enormously in all but the simplest of applications.

    • graypegg 10 hours ago
      I might toss it out there that upcoming changes to attr() [0] as well as typed properties [1] will add some interesting features. Being able to provide a value that's subbed into a stylesheet from the HTML itself is neat.

      You can try to get by with auto-generated selectors for every possible value today, ([background="#FFFFFF"]{background: #FFFFFF}[background="#FFFFFE"]{background: #FFFFFE}...) but just mapping attributes to styles 1:1 does begin to feel like a very lightweight component.

      (Note... I'm not convinced this is a great idea... but it could be interesting to mess around with.)

      [0] https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/V...

      [1] https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/A...

    • akst 7 hours ago
      I disagree on the number of elements actually approaching problematic territory, but agree this just isn’t something you can’t do already without web components
    • akagusu 10 hours ago
      According to the dictionary, the word component means "a part or element of a larger whole" which I think goes to the opposite direction of "self contained"
  • hawkticehurst 10 hours ago
    Author of the blog post here! Since this blog post, I put this idea to practice on the VS Code website (https://code.visualstudio.com/) to create all the interactive graphics on the homepage. Which is a slightly different use case than what I described in the post, but cool and effective none-the-less.

    What woud have been a soup of `div` elements with various class names are now more meaningfully named elements like `<top-bar>`, `<chat-container>`, etc. that were mixed and remixed to create all the graphics.

    Also no issues regarding performance that we've seen up to this point, which makes sense; browsers are very good and fast at rendering HTML elements (native or custom).

    • megaman821 9 hours ago
      I have flirted with this in the past and an important note that you are missing from the post, that this type of custom element should only replace divs and spans. These new elements will have no meaning to the document outline or for accessibility.
  • Footprint0521 1 hour ago
    This feels like saying “what if we just coded in raw HTML CSS instead of JS library bloat.”

    Which is valid but also this article idea seems to make the rounds every two weeks

  • kelvindegrees 10 hours ago
    Is this not exactly what DaisyUI (https://daisyui.com) is?
    • spartanatreyu 3 hours ago
      No, daisyui is a bandaid over the class soup that is tailwind.

      It's treating the symptom rather than the cause.

      The solution is not to use tailwind in the first place.

  • dgb23 10 hours ago
    I like that the author came to the idea by cross pollination via web components.

    However, it's basically describing the "modifiers" part of BEM, which is a pattern that emerged from structuring CSS. Neither custom element or attributes are needed, even though they might feel different.

    If you like that kind of pattern to structure CSS, then combining it with custom CSS properties (often called "variables", example: --block-spacing: 2rem) makes it even more modular. These properties follow the cascade rule.

  • hyperhello 11 hours ago
    The reason fixing ads from the inside won’t work is that they are designed to disrupt. An ad that ruins your scrolling for three seconds is preferable to one that ruins your scrolling for 2.5 seconds. All ads are designed to wreck the environment that they are in, to create a space for irrationality to enter.
  • vaylian 11 hours ago
    I've never been deep into XSLT, but I kind of have the impression, that this would have solved the issue.
  • senfiaj 10 hours ago
    I'm not a fan of these custom elements. Unless you do something really interactive, dynamic and reusable (an element with complex behavior), I don't think it's worth to use them. The SEO / accessibility becomes more challenging. Also, worth to noting, web components require JS, so they are not pure "CSS" web components. Web components are useful for isolation, when used with shadow DOM.
    • jakelazaroff 10 hours ago
      Using custom elements as the article suggests doesn't require JavaScript, so they are "pure" HTML and CSS (though whether they count as "web components" is up to you). More to the point, all of the technologies that the term "web components" includes — custom elements, <template> tags, shadow DOM — can be used without JavaScript.

      <div> and <span> are semantically neutral, so I'm not sure what SEO and accessibility challenges custom elements would introduce?

      • senfiaj 9 hours ago
        My point is that defining a complex behavior for a custom tag is not possible without js. For example, you can't define a reusable 'host-element' tag and expect some additional elements (or some behavior) to automatically appear inside it each time your html includes or you create <host-element> ... </host-element>. I mean you can use something like <host-element> (html5 allows that), but it will just be an inline element, almost like <span>, but without semantics. It's not a full web component.

        > "shadow DOM — can be used without JavaScript" Yes, shadow DOM can be used without JS, but I was talking about web components.

        > "I'm not sure what SEO and accessibility challenges custom elements would introduce?" If you replace standard elements (such as 'p', 'a', 'button', etc) with custom ones it can hurt SEO and accessibility. There are very few reasons to use custom element names and attributes if they are not full web components.

        What's the point of using selector 'link-button[size="large"] a {...}' when you could do the same with '.link-button.large a {...}'?

        • jakelazaroff 6 hours ago
          Right, but the article isn't talking about defining complex behavior; it's talking about using custom element and attribute names as hooks for CSS. I don't think it's suggesting they be used in place of semantically meaningful elements like <p> or <button>, either; it's saying that you can use them instead of class names or data attributes.
        • spartanatreyu 3 hours ago
          > My point is that defining a complex behavior for a custom tag is not possible without js.

          Not necessarily. CSS alone can allow for a lot of useful complex behaviour. You can customise how something renders (or not) or is interactable based on parent elements, sibling elements, css variables, or other state.

          > For example, you can't define a reusable 'host-element' tag and expect some additional elements (or some behavior) to automatically appear inside it each time your html includes or you create <host-element> ... </host-element>.

          Actually you can, using <template> and <slot> elements. No JS required.

          > What's the point of using selector 'link-button[size="large"] a {...}' when you could do the same with '.link-button.large a {...}'?

          This is really two questions:

          1. What's the point of using <link-button> instead of a link-button class?

          2. What's the point of using a size="large" attribute instead of a "large" class?

          To answer 1:

          Classes end up being misused compared to custom elements. When you make a custom element (in this example "<link-button>"), you're explicitly saying this is a <link-button>. It is not a <blockquote class="link-button">, it is not a <form class="link-button> and it is most certainly not a <picture class="link-button>. It is a <link-button>.

          Also with what was stated above, you can use <link-button> to declare default internal elements (using <template> and <slot>) without using js to say what should be inside a link-button.

          To answer 2:

          Because you should make impossible states impossible (or at the very least present impossible states as impossible). Size is a state, it could be small, large or any other list of predefined values. If you use classes then you can end up with something like <link-element class="small large">. If you use attributes, you end up with something like <link-button size="small"> or <link-button size="large"> but not <link-button size="small" size="large"> (since that's illegal in html and duplicated attributes are ignored).

          Plus you're already using attributes for interactive aria roles (or you should be).

          So with a basic collapsible menu:

          <nav role="navigation">

          <a href="#">home</a>

          <button id="navbar-toggle" aria-expanded="false" aria-controls="navbar-menu">Menu</button>

          <ul id="navbar-menu">

            <li><a href="#">Login/Logout</a></li>
          
            <li><a href="#">Feedback</a></li>
          
            <li><a href="#">Other links...</a></li>
          
           </ul>
          
          </nav>

          You use css to style the menu being open or not with: `#navbar-toggle[aria-expanded=true] ~ #navbar-menu` and not something inaccessible like: `.navbar-menu.navbar-menu-open`.

  • Zardoz84 8 hours ago
    Using a expression from Spain : Es mas viejo que el cagar.
  • pier25 11 hours ago
    Why not just use CSS?
    • etchalon 11 hours ago
      It is using CSS.
      • pbowyer 10 hours ago
        Why not use CSS without the custom element? From this post I don't see the benefit of using <swim-lanes> over <section class="swim-lanes"> for example.
        • Kerrick 9 hours ago
          Arguably, that would be misuse of the semantic meaning of "section." While <section> is nearly as generic as <div>, they should always have a heading of their own. The author's <swim-lane> already has a nested <section> with its own <h2>, but the <swim-lane> itself doesn't get (or need) its own even-higher heading.

          And since that would drive us to <div>, I don't see any value in <div class="swim-lanes"> over <swim-lanes>.

          • akst 7 hours ago
            Web components come with additional complexity, heavy use of custom element definitions are more complicated to manage.

            It’s more than just an aesthetic preference

            But if you’re not really using web components it’s harmless but it’s a bit odd and pointless.

        • spartanatreyu 3 hours ago
          If you're not actually getting anything semantically useful out of the element, then you may as well use a custom element.

          Also by using a custom element instead of a class, you're saying this isn't anything else other than what I say it is.

          It's not a <section class="swim-lanes">, it's not a <form class="swim-lanes">, it's not a <blockquote class="swim-lanes">, it's a <swim-lanes>.

          If you make something only a class, people end up misusing it in odd places.

        • etchalon 10 hours ago
          A handful of benefits:

          1. Specificity - swim-line.buttons vs .swin-lines.buttons vs .buttons.swim-lanes. 2. Future pathing - Maybe you don't need a Web Component today, but you might need one tomorrow. 3. Cleaner - <swim-lane /> is just better than <div class="swim-lane" />

          • pier25 6 hours ago
            > Specificity

            :where() gives you zero specificity.

            > Future pathing

            Sounds like premature optimization. And I say this as someone who has been using custom elements and web components in production since 2016. In fact one of my products offers WCs for our customers:

            https://docs.wavekit.app/web-components-wavekit-audio/

            > Cleaner

            Debatable. Personally I find it cleaner to simply rely on CSS to solve something rather a combination of CSS, JS, and some custom markup.

            • etchalon 5 hours ago
              1. ... I do not understand what you mean.

              2. One person's "premature optimization" is someone else's "this was literally something I dealt with this week."

              3. This method relies on CSS and HTML, just as any other styling solution would.

              • grodriguez100 1 hour ago
                2. Yet it was you who said “future” in the first place…
          • smrq 8 hours ago
            "Clean" is the biggest lie in software development. It's an aesthetic opinion dressed up as objective fact. You think components are clean, someone else thinks classes are clean, and neither of you are wrong, except for believing that "clean" is a property of the code and not something entirely in your own mind.
            • etchalon 5 hours ago
              I'm going to start using this logic whenever my wife talks about my office.