HTML Web Components: An Example
blog.jim-nielsen.com
blog.jim-nielsen.com
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.htmlPeople 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.
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 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.
<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 :)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.
Edit: spelling corrections
Really? My experience to date has been that the majority of libraries for things like tooltips fail to take accessibility into account at all.
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.
> 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.
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.
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'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.
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.
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_?
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.
<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 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.
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/
I recommend trying not to get distracted by the example and focus on the core idea.
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
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?
<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.<user-avatar> content is both a view and an interface, which is not obvious
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.
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.
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 mean, depending on the CSS, things might already break if the <img> suddenly needs to be wrapped inside a div etc.
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.
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!)
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. :)
Sadly that concept is utterly foreign to today's "web developers" who have absolutely no idea of web development without JavaScript.
[1]: https://lit.dev/
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...
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.
<template tag="user-avatar" parameters="[src,alt]"> <img src="{src}" alt="{alt}"/> </template
[0]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/te...
[1]: https://developer.mozilla.org/en-US/docs/Web/API/Web_compone...
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.
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?
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.
If you're curious enough, I'm sure you can find the TC39 proposal.
With backticks we don't need to worry about quotes in the content inside being interpreted as the end of a string in JS