Hacker News new | past | comments | ask | show | jobs | submit login
HTML Web Components: An Example (jim-nielsen.com)
150 points by alexzeitler 10 months ago | hide | past | favorite | 82 comments



This is essentially what was called ~20 years ago "Progressive enhancement", and was lost togheter with "Graceful Degradation" when a new generation of "Front-End Engineers" started over-engineering the www around the 2010s.

My impression is that some common sense was lost too along the way. Remember the Rule of the Least Power, anyone?

    Good Practice: Use the least powerful language suitable for expressing information, constraints or programs on the World Wide Web.[0]
[0]: https://www.w3.org/2001/tag/doc/leastPower.html


This is exactly why I love HTMX [1] in combination with PicoCSS[2]. HTMX is just the regular html elements with ajax extensions built into the tags (it is a js library currently but they plan on lobbying to have these as default functionalities with HTML in the future) and picoCSS also works without classes so you are "trained" to use the semantic tags for the page to be rendered beautifully

[1] https://htmx.org/

[2] https://picocss.com/


What I don’t like about htmx is that you have html spread over two very different places - front end and backend. On the backend side you might not even have syntax highlighting.


If you use the principle of least power to make fundamental architectural decisions, you'll get stuck, very soon and very often when the requirements change.

People used to understand that too. But limited functionality frameworks became too enticing to ignore as a user (of the framework), and to sell as a developer. That lead the way into the stuff we have today, that are extendable limited functionality frameworks where the amount of functionality is so large that one hopes never to exceed it.


It's not that common sense gets lost, I think what happens is the lessons keep getting forgotten as the new generation enter the fold, and reinvent old concepts ("everything old is new again").

As an industry we don't do a great job of persisting lessons learned. Not sure how we could, I've got no answers, just an observation of what happens.


I quite like this approach but I wish the web component spec had been better to begin with, I shouldn't have to "feed in" the internal divs to the web component (which wrecks encapsulation), HTML templates should be possible to name and instantiate from within HTML, allowing for proper encapsulation, while having the javascript be entirely optional and allow for progressive enhancement.

I ramble about this on my blog if anyone cares: https://btmc.substack.com/p/making-sense-of-web-components

I've wanted to make a simple compiler (I'm talking a single python script level of complexity here) which takes a set of .html files with named templates inside (HTML+CSS+JS sane web components), encapsulates the CSS and the JS, then goes through the various actual .html page files and replaces each instance of the template <my-component></my-component> with a "HTML Web Component" as described in Jim Nielsen's article.

Alas, I lack the time, feel free to steal my idea.


What you describe is provided by XSL and XHTML! These are both standard web technologies, albeit not particularly popular ones. This is how you define a template:

  <xsl:template match="put the xpath query for your components name here">
Anything inside that template will be expanded when you use your component's name in your XHTML file, either by the web browser or (more practically in an HTML5 world) by a server-side XSL processor. It's pretty fun :)


You still need to deal with style and script encapsulation, but it's a start :)


Fwiw I love this idea for a simple js-free component compiler/bundler.


Django has something similar, on top of its template support

https://docs.djangoproject.com/en/4.2/topics/forms/media/


Do you mean like Declarative Shadom Dom?

https://github.com/whatwg/html/pull/5465


Nope, awful standard on top of an already awful standard.


It seems wild to me to write raw HTML and then write a layer of code that works with that specific, bespoke markup to enhance it. You lose the benefits of the code being modular! That's the whole point of it being a component!

What you could do is have some code on the server that generates the markup. That way it's not so bespoke. And the client side code could somehow be aware of what the server is doing, so you're not writing a layer to enhance the code, you're just adding interactivity...

... Congrats! You've just invented server side rendering.


Maybe it’s the contrived example but I’m not convinced. First I understand that if a user disable JavaScript then it has a functioning fallback, but can’t accept browser double rendering(first pass HTML, second JavaScript and with potential layout shift) and with the risk of breaking accessibility, one should take for granted that the tooltip library has proper accessibility integrated. Then one thing that I will never understand: why calling them web components and not just custom elements? And shouldn’t the purpose of custom elements be to avoid JavaScript as much as possible? I can’t see why they were spec’d in this way, what’s the purpose of custom HTML elements if I need to wait javascript to kick in? Give me the possibility to define custom elements in a declarative fashion directly inside HTML with the <template> tag and with styles scoped to that node. Eventually, if one wants, could add some JavaScript to further enhance the custom element. That way we could have expanded the web more and more.

Edit: spelling corrections


"one should take for granted that the tooltip library has proper accessibility integrated"

Really? My experience to date has been that the majority of libraries for things like tooltips fail to take accessibility into account at all.


I agree, and imo users of those libraries fails as well


> First I understand that if a user disable JavaScript then it has a functioning fallback

That isn't the only case. They could be on an unreliable internet connection and the JS fails to load. They could be referencing unpkg and it's down or otherwise unreachable. There could have been a syntax or other error that escaped testing that causes the JS to fail to execute. The point is that there are many failure modes that may cause the JS-only approach to fail to display anything at all, where the progressively enhanced version is still usable.


> If the user’s browser fails to download, parse, and run any JavaScript, nothing will display on screen.

> So before any JavaScript has been downloaded, parsed, and run, a desktop browser could display something like

If? Before? I’m sorry but that’s just not based in reality at all.

These examples are so hopelessly contrived. Their fallbacks are rough and often ugly, require a ton more testing and development, and are not what users want. If your JS is slow to load or often fails loading then that’s what you should focus on. Not some half-working BS.

Web _apps_ require JS. Pure and simple. Pretending that it’s worth the time to implement a fraction of your web app’s functionality for the case where JS doesn’t load is ludicrous. “Oh look you can use a title instead of a tooltip”, cool, now make some actually complicated work.

If I had unlimited budget and unlimited QA time I still would think this was a stupid idea. Dealing with all these “graceful” (press X to doubt) fallbacks means double the QA time: once to test the happy path that users demand and once to test the absurd path that <1% of your uses will see or want. Anti-JS/noscript people don’t make any sense to me. Might as well reject CSS or h1 tags while you are arbitrarily drawing the line somewhere.

Yes I know HN has a higher proportion of these people than the internet at large but it’s simply not worth the extra effort and testing. It’s like installing peddles in a car in case the engine goes out.


I still regularly see spinning dots of death or broken pseudo-progress-bars on top of pages that are little more than blogs and could very well be statically generated.

Don't get me started on broken browser history, even youtube fails at that.

Web _apps_ require JS, no doubt about it, the problem is that every website these days thinks it's a web app.


Fair, and in other comments on this subject I’ve complained that we don’t have good “language” for discussing things like blogs vs marketing sites vs JS-heavy sites vs actual web apps. More often than not people are taking past each other in the pro/anti-JS context with each person thinking of a different example.

I completely agree that your blog probably doesn’t need to require JS and might even be worth some of these fallbacks if you do want to enhance it but a blog is tiny testing space compared to any “web app”. Also even if your user-facing aspect of the blog can handle noscript your backend admin/editor is going to require JS in most cases.

Yes, yes, static site generators are great but let’s not kid ourselves and pretend they are suitable for most people. Hell, I used SSG for years before deciding I just wanted a web admin UI to edit/write in for my blog. And you can even pair the two together (JS-based admin that generates static frontend files for readers).

Bottom line if any part of your website/app requires JS then it’s a waste of time to try implement fallbacks. At a previous company I dealt with this, the core of our business was a website that needed JS to function but I had one dev who constantly wanted to have fallbacks on our less JS-heavy pages. A user using noscript would not be able to do the most important things since there was literally no conceivable way to have a noscript fallback (show me the fallback for drawing polygons on a map without JS, I’ll eat my hat). Being able to create calendar items in our system might be possible without JS (with a terrible UX) but good luck with the UI to assign people to the calendar entry. It just wasn’t worth the effort and QA time to support both.


I agree with you, JS is a part of the web and disabling it entirely is silly. My issue is solely with the modern approach of making the web nothing but JS. I blame the W3C in part for moving in that direction (and, in turn, google, for pushing it in the first place).

I've been on poor internet connections where being able to get half broken HTML with barely any CSS loaded was a godsend. Progressive enhancement was a good idea. Nowadays if some little bit of JS doesn't load right the whole website goes down. That's a shame.


>Bottom line if any part of your website/app requires JS then it’s a waste of time to try implement fallbacks. At a previous company I dealt with this, the core of our business

It certainly is a waste of time for incorporated legal persons who pay human persons to implement things to generate profit. But for human persons making websites for other human people just because they want to it isn't.


> and are not what users want

Ohoho.

I, as a user, want to see the content. The amount of times when the content is not available because for whatever reason JS/CDN/whatever didn't work is uncountable.

Hell, sometimes I get a blank page for the login page, which could be replaced with a three lines of HTLM form, but now it requires a ten web requests, wait for them to load and only then it renders something.

It's even more amusing if you look at Network tab of Devel Tools.

> but it’s simply not worth the extra effort and testing

Maybe you shouldn't made a simple page require tons of JS and CSS to show the content in the first place?

Maybe not everything should be a web _app_?


And this gets back to point I’ve made here and elsewhere about talking past each other. Not everything has to be a web app but some things do and those things need (require) JS, end of story. Yes a blog frontend doesn’t need JS but we are talking about different things here I think.


the problem is that react is being used outside of SPAs, for any sort of interactivity, because it is easy to hire for at this point and because there are a lot of existing components to choose from. so yeah, progressive enhancement doesn't work if the actual component is state-heavy, or the entire site is state-heavy. When React came out, its tutorial was very centered around being embeddable on existing web pages, and it was framed as more of a "jQuery replacement". IIRC the term SPA didn't even exist yet. The other commenter already pointed out what the end result is.

if you don't do it for accessibility, do it for SEO. yeah a lot of commenters here already pointed out that SSR is a thing now, but the concerns you had still apply then. There's two paths to test now, one with JS and one without. This kind of always was the case though.


Maybe it's just the used example that irks me the wrong way a bit, but I do these things with pure CSS or even HTML.

<img title='Jim Nielsen'/> anyone?

For making apps on the web, I abide to the request/response cycle that's inherent to HTTP. JS is only for cropping images, picking colors and other necessary functionality that adds to the otherwise serverside monolith.

Of course there are use cases where one needs a JS framework, but I think it would surprise a lot of devs to see what's possible with just HTML, CSS and a little bit of JS on the client side. Although that seems to require a big mindshift nowadays...


I have angered so many frontend devs by touting this for years. It amazes me how common sense like this is instantly attacked and belitteled. Unless you create a user-interface UI in a webpage (a complicated frontend state), I think its way more reasonable to just use html/css with small sprinkles of js to open/close menues and modals. It loads fast, its less code so its easier to maintain. It does not require a complicated build step that instantly becomes deprecated once its deployed. Heck, its even greener, it requires less CPU power to build and render.


I’ve found it is quite doable to have open/close menus purely with html and css these days using selectors! The text you click on is a label for a hidden checkbox. The checkbox you put adjacent to the item you’re hiding/showing. Then put css on it that uses input[type=checkbox]:checked and the tilde operator


I wonder why the HTML menu element was so half hearted. This is such a common requirement that it shouldn’t require hacks like that.


As CSS has developed over the last few years it is amazing how much you can now do on the front-end without JavaScript.

I wish the browsers would enable their "behind-a-flag" support for masonry so many sites could then remove another chunk of JavaScript being used for layout.


actually you can do a pseudo masonry style using pure CSS with display: grid . I done it


There's a ton of things that sorta work, I use an all-CSS solution as a fallback for browsers that don't have the CSS property enabled. The problem is that all the pure CSS solutions order the grid in a different order than would be expected by the user. All the JavaScript solutions fix this, but, you know, JavaScript...


I want to add that using the title attribute should be very specific and narrow[0] as it has some very bad implications for accessibility.

I know some devs might see this and think "Titles are builtin in tooltips!" and unfortunately, they are not.

[0]: https://yoast.com/developer-blog/dont-rely-title-attribute/


The example here is clearly picked to be as simple as possible an illustration of the underlying concept.

I recommend trying not to get distracted by the example and focus on the core idea.


> <img title='Jim Nielsen'/> anyone?

That's an accessibility gotcha, the title attribute tend to be neglected by screen readers and you should use aria attributes to communicate that information, and display it with other means


This is a safe opinion, web components are not designed to replace <img>. Perhaps the article could better explain why this approach is better than <img title='Jim Nielsen'/>.


I read the article and don’t understand the example.

So every time I want to use the avatar component (let‘s say inside a navbar, in a profile page, in an article header etc.) , I‘d have to include the <img> tag. Because it’s basically a required „input template“ of the web component. And when the web component changes, I‘d have to go through all the places I use it and adapt the <img> tag to the new requirements of the web component…? that‘s… ridiculous?


Right. It's the difference between this:

    <user-avatar
      src="https://www.jim-nielsen.com/.well-known/avatar"
      alt="Profile photo of Jim Nielsen"
      width="32"
      height="32"
      title="Jim Nielsen"
    />
And this:

    <user-avatar>
      <img
        src="https://www.jim-nielsen.com/.well-known/avatar"
        alt="Profile photo of Jim Nielsen"
        width="32"
        height="32"
        title="Jim Nielsen"
      />
    </user-avatar>
I will argue that the extra <img> there is worth it for the benefits you get in terms not just of no/slow-loading JavaScript but also better semantics - code that process images in an HTML document will know what to do with the latter, it will be unable to handle the former.


What if next year you decide to change the img tag to an svg?


You have to go back to your codebase and edit every implementation of the component under the sun.

<user-avatar> content is both a view and an interface, which is not obvious


Use find and replace.


The difference is not what you write, but what ends up in your user's DOM. You can still use custom components in your static site generator or in your backend code. Abstract it however you want. You could use React Server Components with whatever component and interface you'd like. But the result in the SSRed or SSGed DOM should be the one from the article.


Agreed. It's weird. If the goal is to extend HTML elements, should just send the compound elements definition with the html.

The image should just be refered by a src attribute of user-avatar.

But in any case, you still want javascript if you have to change something dynamically.

People are hell-bent on using a static markup language designed for static documents even if it doesn't fit.

Ok it's slow but that's another problem.


If we're not doing any state manipulation and are just using static props I don't see why we even need any abstraction at all. I would really like to see an example where "setShowTooltip" is used and the equivalent is implemented in Web Components. That's the part React is solving for me.


> If we're not doing any state manipulation and are just using static props I don't see why we even need any abstraction at all.

People are using React outside of SPAs just because they're already used to it, and because it's got a rich ecosystem of reusable components.

I don't know how to implement setShowTooltip in vanilla js and webcomponents. It is probably really ugly. But also, making your web components work with progressive enhancement does not necessarily mean you have to get rid of React. You can still use React to define a custom HTML element, and combine the state management of React with the progressive enhancement of web components.


But that is just going to be the vanilla JS and React versions of the same thing. React doesn’t solve UI. It solves state management of UIs (IMO).


> This is nice enough. But the point is: it requires JavaScript (React, 3rd-party-tooltip-on-npm, your JS code) before anything will render in the browser. It’s an all-or-nothing approach.

As was already pointed out in the previous discussion (of the previous blog post) here on HN, this is a complete non-issue. In most frameworks these days you would prerender your application or maybe even server-side render it.

The issue I see with the suggested "solution" (of the non-issue) is that the "placeholder" <img> tag is not "type-safe". I can put anything in there without paying attention to what exactly my web component does or expects. If the behavior of my web component changes, I won't get a warning at code writing or compile time. Instead the layout will silently break.

All in all, I don't think this approach scales well with the size of the code base and the number of people working on it. (Let's say, e.g., <user-avatar> is provided by some other team.) And again, it's trying to solve a non-issue.


I agree although prerender land has it’s issues. Mainly it is hard to make forms that work both with/without JS, especially with validation. You need separate code paths for both. At least in NextJS. The latest version is very close though. Sans validation and with experimental features I think you can have a server action that handles both form and JS submissions the same way.


If <user-avatar> starts to provide something else, and depends on compiling a code base to alert that it's breaking something, an automated test(?), then that suggests everyone's working blind and hoping for the best while the application development methodology's broken.


Have you ever worked on (or with) a component library for a large code base? When updating the library you can't possibly manually check every single usage of every component.

I mean, depending on the CSS, things might already break if the <img> suddenly needs to be wrapped inside a div etc.


In my experience web components got a bad wrap because it was never really clear what exactly they are. Are they the shadow DOM? Custom elements? Yes! And no

I very rarely use the shadow DOM APIs but really lean heavily into custom elements for simple lifecycle hooks. Similar to the example in this article, its really easy to server render the HTML and have a bit of JS hook on in the client.

The main downside is that there's no support for delaying the component, the custom element will be created and connected immediately.


Agreed, in my opinion the unnecessary emphasis on shadow dom is what prevented web components from gaining more adoption.


It only works for sites that don't really require a framework. What bugs me in most examples and "getting started" articles is that they take a trivial, best-implemented thing and present it as if it will be an average experience and/or a common modality. Yes, a card can be implemented with a trivial fallback. Now show a repo of an average site or an app with a decent requirements+development history without waving hands about "classes of web content" and "limited application". We are not time-traveling oracles. If the base platform doesn't work for an average in-the-wild use case, fix it until it works. Make rules for styling tooltips, add strict data model for controls, tables and requests, etc. Meanwhile, turn your js on.


I think adding a bit (or a new article) about why someone should choose this approach for state mangaement over existing alternatives would make a stronger argument.

Perhaps clarify the connectedCallback() and customElements.define() methods are not arbitrary, but specific to the API (I know the API is linked to in the article, but readers are lazy).

(Better examples here than in the previous article, thank you!)


I used this technique in a project I'm working on extensively. This works great for server-side rendered projects, because you can just combine the HTML inside and the custom-tag in a partial, and re-use this partial like a default component.

A nice advantage is also that it will auto-execute code when you include components in the DOM through the connectedCallback() function. No need to do manual stuff, or wait for onDOMContentLoaded.

BUT when you go more advanced, like intra-component communication, or nested component structures, it starts to become a bit troublesome. Or you'll dream of some reactive templating or other advanced features, and you'll check out what LIT-components are doing...

I feel that in most simple cases doing vanilla enhancements on some .js-something classes are is even easier that using this technique. No need for a custom-element. Just go back to the jQuery days. :)


This entire article could have been replaced with two words: "graceful degradation."

Sadly that concept is utterly foreign to today's "web developers" who have absolutely no idea of web development without JavaScript.


wait why not progressive enhancement?


Because the vast majority of users have JS enabled, I suppose


I haven't used Web Components yet, but plan to. If you have worked with or evaluated vanilla Web Components and Lit[1], is there any reason to prefer one of the other?

[1]: https://lit.dev/


Lit is very close to vanilla Web Components, but makes the standard more convenient to work with. Especially when used with TypeScript and a nice plugin such as lit-plugin [1], you get syntax highlighted and checked CSS + HTML, auto-completion in templates, and so on.

Also, the reactivity offered by Lit is something you could implement yourself using the standard lifecycle methods of custom elements, but is quite boilerplate-y. Overall, Lit gives you a better developer experience with fewer foot guns and boilerplate without much overhead.

[1]: https://marketplace.visualstudio.com/items?itemName=runem.li...


Lit is a small lib that extends components to handle common needs, like state management, adding/removing event listeners, etc.

As someone who wrote and maintained a lot of vanilla web components, I much prefer using lit nowadays. It’s still just native components… but many of the parts you needed to write yourself are already there and very well implemented.


as mentioned earlier, It will be nice if/when these components can also be defined within HTML

<template tag="user-avatar" parameters="[src,alt]"> <img src="{src}" alt="{alt}"/> </template


There _is_ a <template> element[0] for this purpose, but so far it falls short on being able to inject parameters like that, and is essentially useless if you don't have Javascript.[1]

[0]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/te...

[1]: https://developer.mozilla.org/en-US/docs/Web/API/Web_compone...


I got the impression that the people defining the web standards just gave-up on letting things work without javascript.

If you don't want to import things like a frontend framework and HTMX parser, the templates are incredibly useful. They are also very useful for the backend to pass structure over to the frontend, as they are plain HTML. What they do not do is helping with the things that a static site generator will do.


This is a decent fallback, but it really doesn't solve the underlying problem. The whole point of web components is the ability to encapsulate and reuse your work... and _specifically_ to extend the HTML tag library.

The fact that you need JS to load/run to parse your custom web component is an issue. But, the solution is to allow users to cache these components or add them to the browser itself.

Ideally, <user-avatar> should be available without JS just like <img> is. Imagine a world where we have this capability - a large library of reusable components that can be used as easily as native HTML. This is the vision, is it not?


i agree with the article, we dont need react to do things like this


The article really misses out the full power of Web Components <slots> ... that's how you would inject plain html tags into them


I'll never understand why JS decided to use backticks. It's so weird to type on German keyboard.


It’s fundamentally a Unixism, like the tilde and some other characters used in C family languages that don’t exist on many European keyboards.

If you look back 30-50 years, most of the popular programming languages like Pascal and BASIC used a very limited character set. Instead of symbols like various brackets and tilde and backtick, they used keywords. This was intentional because they were designed to be possible to type on various Latin keyboards.

But Unix and C were developed by American hackers who had no reason not to use all the ASCII characters available on the Teletype-whatever connected to the PDP-whatever. And that’s the path we ended up on.

In the 1980s the C standard committee tried to address this problem by adding support for trigraphs. There’s a whole set of three-character symbols (identified by a prefix of two question marks) that can be used in C code in place of curly brackets and all the rest of the exotic ASCII set. But I’ve never seen anyone use this, and I think trigraphs are scheduled for removal in C.


It's the closest thing to a quote without being a quote. Enabling interpolation for old syntax would break old websites that display strings that aren't intended to be interpolated.

If you're curious enough, I'm sure you can find the TC39 proposal.


I'm from the EU, but once I switched to US-keyboard layouts everywhere, so many programming languages' syntactical choices suddenly made sense.


Isn't it on the right side of question mark? On my nordic keyboard it is and you just need to press shift and backtick key. I don't see how it's too difficult.


Yes. But to me it feels far away compared to the quotes. And depending on the editor, it may end up above a letter if you forget to press space. For example "à". It can be a bit annoying.


Try the Neo layout, optimized for writing german, english and programing. It places the most used letters and symbols under your strongest fingers. Absolutely worth the effort, QWERTX is a pain in comparison.

https://neo-layout.org/


With your reasoning we wouldn't be able to use any special characters considering every character has layouts where it's awkward to type.


Backticks are used for certain letters in several European languages. à, ì, ù, ò. Depending on the editor, it's kinda annoying. Normal quotes or double quotes usually don't do that. Also, the key is right beside the backspace key, which feels kinda far away for my hands (which of course also depends on the keyboard).


> Normal quotes or double quotes usually don't do that

With backticks we don't need to worry about quotes in the content inside being interpreted as the end of a string in JS


Switch to the US layout for programming then. It's impossible to cater to all the languages in the world.


I like to think of these kind of web components as analogous to jquery plugins from back in the day. IF I think of them like that instead of a react component things make more sense as to why this is useful.


Why bother? the percentage of users that have JS disabled is what nowadays? 0.000000000001% maybe? even smaller?


Solutions in search of a problem.


[flagged]


I don’t think this item is the place to share it: your thing embodies the approach the article is arguing against! Try posting it as a Show HN instead.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: