Top
Best
New

Posted by todsacerdoti 9 hours ago

You can make up HTML tags(maurycyz.com)
237 points | 96 commentspage 2
rasso 6 hours ago|
This is how you can set default styles for all your custom elements:

    :where(:not(:defined)) {
         display: block;
    }
ianbutler 8 hours ago||
https://lit.dev/

That's the premise behind Lit (using the custom elements api)! I've been using it to build out a project recently and it's quite good, simpler to reason about than the current state of React imo.

It started at google and now is part of OpenJS.

sleazebreeze 8 hours ago|
I just evaluated Lit for work and while we didn't go with it, it was very nice. I love the base custom elements API regardless of using Lit or not, turns out that's all you really need to make intricate UIs that feel seamless.
g0wda 7 hours ago|||
Something about Lit/web components that's not written clearly on the label is the hoops you have to jump through to style elements. If you're using tailwind in the whole app, you just want to pull it into your custom elements without much boilerplate. My main app compiles a single minified app.css, it felt not so modular to now include that in every single component. The alternative is create a subclass that injects tailwind into Lit components. It'd be perfect it just had a switch to inherit everything the page already has.
hunterloftis 7 hours ago|||
> It'd be perfect it just had a switch to inherit everything the page already has.

It does! <https://lit.dev/docs/components/shadow-dom/>

By default, Lit renders into shadow DOM. This carries benefits like encapsulation (including the style encapsulation you mention). If you prefer global styles, you can render into light DOM instead with that one-line switch.

However, shadow DOM is required for slotting (composing) components, so typically what I'd recommend for theming is leveraging the array option of each component's styles:

    static styles = [themeStyles, componentStyles]
Then you define your shared styles in `themeStyles`, which is shared across all components you wish to have the same theme.
gitaarik 6 hours ago|||
I also made a simple lib to make it easy to implement common styles in your Lit components:

https://github.com/gitaarik/lit-style

g0wda 7 hours ago|||
oh nice! I didn't know that you can just make it use light dom.

  protected createRenderRoot() {
    return this;
  }
And that's what it takes! I like using tailwind/utility classes so for the styles I'd need to have layers of compiled css files rather than one giant one.
spankalee 7 hours ago|||
There's a long standing standards issue for this: https://github.com/WICG/webcomponents/issues/909

While you can easily turn rendering to shadow DOM off on a per-component basis, that removes the ability to use slots. It only really works for leaf nodes.

Pulling a stylesheet into every component is actually not bad though. Adopted stylesheets allow you to share the same stylesheet instance across all shadow roots, so it's quite fast.

ianbutler 7 hours ago||||
Interesting, I'd be curious to know why you all decided not to go with it if you're open to sharing! Minimally to know if I should look at any other promising frameworks.
sleazebreeze 5 hours ago||
I see a good case for my company to use Lit for creating complex components such as highly interactive panels/widgets to be shared between React/Angular apps in our large ecosystem. However the decision was: 1. Prefer sharing plain JS/TS over framework code so try that first and 2. if the component is so complex and tricky to get right, it probably needs to be re-implemented in each framework anyways (or some sort of wrapper)

My secondary concern with Lit is the additional complexity of using shadow and light DOM together in long lived React/Angular apps. Adding a new paradigm for 75+ contributors to consider has a high bar for acceptance.

ianbutler 4 hours ago||
Ah yeah, definitely a much different equation when introducing this across many apps as a shared component library. Mixing different DOM abstractions together could get tricky for sure.

And yes attempting to add a new paradigm for that many people is I am sure quite the task. More political than technical in many ways as well.

Thanks for sharing!

rubenvanwyk 5 hours ago|||
What did you go with?
crazygringo 7 hours ago||
But there's no real reason to, and it just adds confusion around which elements are semantic -- bringing formatting, functionality, meaning to screen readers and search engines, etc. -- vs which are custom and therefore carry no semantic meaning.

If there's no native semantic tag that fits my purposes, I'd much rather stick to a div or span as appropriate, and identify it with one (or more) classes. That's what classes are for, and always have been for.

Support for custom HTML elements seems more appropriate for things like polyfills for actual official elements, or possibly more complicated things like UX widgets that really make sense conceptually as an interactive object, not just CSS formatting.

Using custom element names as a general practice to replace CSS classes for regular formatting just feels like it creates confusion rather than creating clarity.

yawaramin 7 hours ago||
So, the article doesn't discuss this, but there's actually a really good reason to make up and use custom elements: the browser can hydrate their dynamic behaviour automatically. For example, suppose you have:

    <div class=expander>
      <button aria-expanded=false>Expand</button>
      <!-- Some other stuff here -->
    </div>
And you have some JS that handles the expander's behaviour:

    for (const expander of document.querySelectorAll('.expander')) {
      const btn = expander.querySelector('button');

      btn.addEventListener('click', () => {
        btn.ariaExpanded = 'true';
      });
    }
This will work fine for `.expander` divs that are already in the page when the event handler is set up. But suppose you dynamically load new expander divs, what then? Your event handler is not going to retroactively set up their click listeners too.

Custom elements solve exactly this problem. You can now do:

    <expander-elem>
      <button aria-expanded=false>Expand</button>
      <!-- Some other stuff here -->
    </expander-elem>
And then set up the listener:

    customElements.define('expander-elem', class extends HTMLElement {
      connectedCallback() {
        const btn = this.querySelector('button');

        btn.addEventListener('click', () => {
          btn.ariaExpanded = 'true';
        });
      }
    });
And the browser will ensure that it always sets up the listeners for all of the expanders, no matter whether they are loaded on the page initially or dynamically injected later. Without this you would have had to jump through a bunch of hoops to ensure it. This solves the problem elegantly.
dannye 3 hours ago|||
this.querySelector will return nothing when you define this Web Component before (light)DOM is parsed, because the connectedCallback fires on the opening tag.

Above code will only work when the Web Component is defined after DOM has parsed; using "defer" or "import" makes your JS file execute after DOM is parsed, you "fixed" the problem without understanding what happened.

I blogged about this long time ago: https://dev.to/dannyengelman/web-component-developers-do-not...

kcrwfrd_ 5 hours ago||||
Yes, the hydration behavior of custom elements is nice. You don’t even need to do anything special with JS bundle loading.

Simply render your <element> (server-side is fine) and whenever the JavaScript downloads and executes your custom elements will mount and do their thing.

rasso 6 hours ago|||
Beware that connectedCallback runs _every time_ a custom element is added to the dom. So you should make sure to only add event listeners once by tracking internally if the element was already initialized.
veqq 5 hours ago|||
> no real reason

Doing this for syntax highlighting on https://janetdocs.org/ shrank the homepage's .html from from 51kb to 24kb, or 8kb to 6kb compressed (at the time).

gitaarik 6 hours ago||
Why would it create confusion? Because you're not familiar with it? Things change..
veqq 5 hours ago||
That's how https://janetdocs.org/ does syntax highlighting:

``` <pre><code class="janet">(<special>defn</special> <symb>bench</symb> <str>`Feed bench a wrapped func and int, receive int for time in ns`</str> [<symb>thunk</symb> <symb>times</symb>] (<special>def</special> <symb>start</symb> (<built-in>os&#x2F;clock</built-in> <keyword>:cputime</keyword> <keyword>:tuple</keyword>)) ```

culi 4 hours ago|
That's kinda cool but the article addresses a major concern with this strategy that is not addressed here. Which is that many of these tags (e.g. <special>, <keyword>, etc) might someday become part of the HTML standard.

The article states that anything with a dash is guaranteed not to be and another commenter here shared their strategy that involved a naming convention like <x-special>, <x-symb>, etc. Perhaps substituting x for j would make sense and alleviate the concern of possible future clashes with web standards

veqq 3 hours ago||
Fair, I had a collision with <math> for a month before someone else noticed some minor error.
matt7340 6 hours ago||
This is what Angular does, where an Angular component is typically rendered as a custom tag. I find it to be one of the (very) few nice things about Angular, as it can be helpful to track down components in a large codebase. I haven’t used React for many years, but makes me wonder if custom tags as a convention would be similarly useful.
hollowturtle 3 hours ago||
> If you include hyphens in the name, you can guarantee that your tag won’t appear in any future versions of HTML.

Which future version? Is there something I'm missing? I'd like to have html6 that has feature parity with native, but I'm afraid we got html5 and just stopped there. And if there will be an html6 why can't we just state "use version 5 in this document" so there won't be any name clashing between supported and custom tags?

bazoom42 1 hour ago||
> but I'm afraid we got html5 and just stopped there.

They dropped numbered versions. Now it is just HTML which is continuously evolved in a backwards-compatible manner.

> And if there will be an html6 why can't we just state "use version 5 in this document"

Because browser vendors are opposed to explicit versioning, since it introduces multiple rendering modes.

reddalo 3 hours ago||
HTML 5 still gets new features all the time: https://github.com/whatwg/html

But they'll never add new standard tags with hyphens.

flanbiscuit 4 hours ago||
I learned this back when HTML5 was brand new around 15-ish years ago. If you wanted to use the new tags like <article>, the only “polyfill” needed was some css styles. You can see it in the early versions of the HTML5 Boilerplate:

https://github.com/h5bp/html5-boilerplate/blob/v0.9/css/styl...

I realized that I could just make up tags and style them and it was work.

benrutter 5 hours ago||
This is cool! I was actually wondering about how to make this work recently and obviously did a bad job of researching it because I didn't realise it already existed[0].

One place I'd love to see this is in a classless html framework. My experience is you always wind up wanting something like cards, and then either writing classes, or having a slightly magic "use these elements in this order" incantation[1].

It would be great to have a minimal framework that peppers in a couple elements of the kind <card>, <accordian> etc.

[0] I did ask an LLM at one stage too, and it also didn't mention this behaviour exists.

[1] pico css does this.for.example for cards/accordians

mg 4 hours ago|
My problem with custom tags is that they require a hyphen, and that makes it impossible to have consistency between tag names and variable names.

With classes, I can do:

    carList = document.querySelector('.carList')
With custom tags, that is not possible because variable names cannot have hyphens in them.
More comments...