I've learned quite early on to avoid jumping on the hype train, but the lesson truly stuck when I started working in web development.
I got burned a few times and quickly grew a healthy distrust for any fancy new name that would draw the attention of the tech news sites, especially when it had to do with frontend work.1
I vaguely remember dismissing Web Components as yet another trendy buzzword and lumping them with all the fancy revolutionary frameworks that seemed to be coming out every week.
That's the trouble with thought reflexes: sometimes they make it harder to sort the wheat from the chaff. I'll blame the general obsession with shiny new crap instead of any lack of curiosity on my part.2
I finally got to read about them at some point and my skepticism dropped as soon as I learned that they were actually part of the standard W3C specification, which meant it wasn't tied to some library likely to become obsolete in a few months. Reading on, I felt the allure of reusability, and I told myself I should definitely play with it someday.
That was a bit under ten years ago, and I finally got an occasion to try my hand at writing one last week when setting up this site's music page. I wanted an audio player that could manage a simple playlist. A friend of mine3 pointed me to an exemple on codepen he had used on his own site, and I pretty much took the code as is and stuffed it into a component, before tweaking it to my tastes.4
I haven't bothered publishing the code anywhere, but you can grab it here if you're interested. I'd turn it into a gist but forgejo doesn't seem to support those yet.
Bye, Font Awesome
A rather trivial change that was still important to me was to get rid of Font Awesome to handle icons.
I don't have anything against it, and have used it in the past, but I hate grabbing a whole library to use less than 1% of its features. 360 kb might not seem that big in today's bloated web, but I'm trying to keep this site on the lighter side and downloading that many bytes only for a copple icons feels crazy to me.
I see you can create custom "kits" or something, and maybe this provides a way to reduce the library size by selecting the icons you want to use. But this is exactly the kind of crap that drives me crazy. Depending on an external library is one thing, but managing it beyond simply including a copple of files always pisses me off.
Sometimes you don't get a choice. Sometimes the benefits (barely) justify dealing with the bullshit. But simple things should stay simple, and in this case it just ain't worth it. Also, they're asking for an email address to use a kit, so fuck that.
So I just went and copied the SVG code for the icons I actually wanted. Here's the entirety of my Font Awesome replacement:
.icon {
display: block;
width: 20px;
height: 16px;
background-color: currentColor;
-webkit-mask-image: var(--svg);
mask-image: var(--svg);
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-size: 100% 100%;
mask-size: 100% 100%;
color: var(--audio-playlist-foreground-color);
}
.icon.icon-play {
--svg: url(
"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20448%20512%22%3E%3C!--!Font%20Awesome%20Free%20v7.3.1%20by%20%40fontawesome%20-%20https%3A%2F%2Ffontawesome.com%20License%20-%20https%3A%2F%2Ffontawesome.com%2Flicense%2Ffree%20Copyright%202026%20Fonticons%2C%20Inc.--%3E%3Cpath%20d%3D%22M91.2%2036.9c-12.4-6.8-27.4-6.5-39.6%20.7S32%2057.9%2032%2072l0%20368c0%2014.1%207.5%2027.2%2019.6%2034.4s27.2%207.5%2039.6%20.7l336-184c12.8-7%2020.8-20.5%2020.8-35.1s-8-28.1-20.8-35.1l-336-184z%22%2F%3E%3C%2Fsvg%3E%0A"
);
}
.icon.icon-pause {
--svg: url(
"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20384%20512%22%3E%3C!--!Font%20Awesome%20Free%20v7.3.1%20by%20%40fontawesome%20-%20https%3A%2F%2Ffontawesome.com%20License%20-%20https%3A%2F%2Ffontawesome.com%2Flicense%2Ffree%20Copyright%202026%20Fonticons%2C%20Inc.--%3E%3Cpath%20d%3D%22M48%2032C21.5%2032%200%2053.5%200%2080L0%20432c0%2026.5%2021.5%2048%2048%2048l64%200c26.5%200%2048-21.5%2048-48l0-352c0-26.5-21.5-48-48-48L48%2032zm224%200c-26.5%200-48%2021.5-48%2048l0%20352c0%2026.5%2021.5%2048%2048%2048l64%200c26.5%200%2048-21.5%2048-48l0-352c0-26.5-21.5-48-48-48l-64%200z%22%2F%3E%3C%2Fsvg%3E%0A"
);
}
.icon.icon-prev {
--svg: url(
"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20576%20512%22%3E%3C!--!Font%20Awesome%20Free%20v7.3.1%20by%20%40fontawesome%20-%20https%3A%2F%2Ffontawesome.com%20License%20-%20https%3A%2F%2Ffontawesome.com%2Flicense%2Ffree%20Copyright%202026%20Fonticons%2C%20Inc.--%3E%3Cpath%20d%3D%22M204.3%2043.1C215.9%2032%20233%2028.9%20247.7%2035.2S272%2056%20272%2072l0%20136.3%20172.3-165.1C455.9%2032%20473%2028.9%20487.7%2035.2S512%2056%20512%2072l0%20368c0%2016-9.6%2030.5-24.3%2036.8s-31.8%203.2-43.4-7.9L272%20303.7%20272%20440c0%2016-9.6%2030.5-24.3%2036.8s-31.8%203.2-43.4-7.9l-192-184C4.5%20277.3%200%20266.9%200%20256s4.5-21.3%2012.3-28.9l192-184z%22%2F%3E%3C%2Fsvg%3E%0A"
);
}
.icon.icon-next {
--svg: url(
"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20576%20512%22%3E%3C!--!Font%20Awesome%20Free%20v7.3.1%20by%20%40fontawesome%20-%20https%3A%2F%2Ffontawesome.com%20License%20-%20https%3A%2F%2Ffontawesome.com%2Flicense%2Ffree%20Copyright%202026%20Fonticons%2C%20Inc.--%3E%3Cpath%20d%3D%22M371.7%2043.1C360.1%2032%20343%2028.9%20328.3%2035.2S304%2056%20304%2072l0%20136.3-172.3-165.1C120.1%2032%20103%2028.9%2088.3%2035.2S64%2056%2064%2072l0%20368c0%2016%209.6%2030.5%2024.3%2036.8s31.8%203.2%2043.4-7.9L304%20303.7%20304%20440c0%2016%209.6%2030.5%2024.3%2036.8s31.8%203.2%2043.4-7.9l192-184c7.9-7.5%2012.3-18%2012.3-28.9s-4.5-21.3-12.3-28.9l-192-184z%22%2F%3E%3C%2Fsvg%3E%0A"
);
}
Took me about 5 minutes of annoying copy-pasting, but I know I won't have to mess with it again unless I really want to, and tho whole component file (which includes way more than this css) is only 15 kb.
But the real benefit here is that if I want to reuse this component elsewhere (which is the whole point of the feature), then it will just work as is and won't force me to include another, apriori unrelated library that the site I'll be working on might not need at all.
I know the current developper culture doens't give a damn about minimizing dependencies, but the current developper culture is the one that blessed us with Single Page Apps that eat 10 megabytes of RAM to run and fail on stupid crap half the time, so I'll gladly ignore whatever it might consider best practices.5
Bye, javascript
Okay, not quite. Web Component do require javascript to work. But another slightly irrational requirement of mine is that I want the site to work even if javascript is disabled, so I had to have a fallback solution.6
Luckily I read early on that browsers that don't support components will just treat the custom element as a regular div. So I can just use the following html:
<audio-playlist id="recordings">
<h3>Someday Never Comes</h3>
<audio data-title="Someday Never Comes" data-duration="02:44" controls preload="none">
<source src="/snd/someday/someday_never_comes.mp3" type="audio/mpeg"/>
</audio>
<h3>Bright Days</h3>
<audio data-title="Bright Days" data-duration="02:30" controls preload="none">
<source src="/snd/brightdays/bright_days.mp3" type="audio/mpeg"/>
</audio>
<h3>L'air Mignonne</h3>
<audio data-title="L'air Mignonne" data-duration="02:47" controls preload="none">
<source src="/snd/mignonne/l'air_mignonne.mp3" type="audio/mpeg"/>
</audio>
</audio-playlist>
And the browser will simply render the regular audio tags if it can't instanciate the component:

Not as pretty, but it works, and that's what counts.
Currently the component just ignores everything contained within the audio-playlist
tag, but it can still access it and loop over the audio elements and use their
associated data to create the individual tracks in the resulting playlist (note
the data- attributes, which are used to populate the title and duration sections):

I'm sure there are better ways to handle this, and I know I could integrate the inner markup into the component's template (possibly using a different, sub component), but right now this seems sufficient and possibly more flexible, so I've kept it that way.
Verdict
I've barely scratched the surface, but so far the overall API has been pretty straightforward. I frowned a bit when I first read about the shadow DOM, but it's actually pretty simple to grasp.
I can see how making a component easy to customize could quickly get tricky, due to how the alternate DOM is so strictly isolated from the rest of the page. I understand why it's done, but this means that you can't simply override the css if you want to change how things look.
I defined a bunch of variables to handle changing the colors and left it at that. I've read about using the parts attribute to allow extarnal stylesheets to target specific elements within the shadow DOM, but I suck so much at CSS that I have a hard time deciding which tags should be targettable, short of simply sticking it everywhere.
Don't really need it right now anyway, so I just chose not to use it. Seeing as I'll probably simply copy the file if I use it in another project, then I figure I can just mess with the internal style anyway.7
All in all, I'm pretty glad I finally tried my hand at this tech that's almost ten years old altready. I don't know if I'll write another component any time soon, as I tend not to need a lot of UI widgets on the sites I typically get to tinker with, but at least I'll know it's not rocket science next time I feel it might be useful.
I remember the promise of SPA frameworks and how awesome it felt to be able to define your own custom html elements, and how painful those actually turned out to be. So I can appreciate how great it is to be able to do just that natively and without having to tie myself to whatever happens to be trendy these days.
I don't really follow the tech news these days, and it's not the first time I'm late to discover that browsers have implemented something pretty cool (last time it happened, it was with CSS nesting, which instantly prompted me to ditch LESS as it was the only feature I really used from it). I guess I should pay more attention, just in case I'm missing something else.
So long internet. See ya in another ten years when I find out IE is finally dead.
-
Do a quick search for "framework on the week" to find out why.
I'd write a rant about it myself, but I can't be arsed. Rest assured, I've got other things to complain about in this post. ↩
-
I like to play up my old fart tendencies, but I actually wish I could get excited with new stuff coming out.
Near-constant disapointment quickly beat that ability out of me, though. ↩
-
Thanks, Flo! ↩
-
Mostly minor stuff:
- Removed useless (IMHO) buttons
- Used actual buttons instead of
divs for every clickable elements (again, thanks Flo for the tip!) - Implemented "wrap around": clicking the next button when playing the last song switches back to the first track.
- Added scrolling behaviour to the playlist buttons (too bad there's not enough files listed yet to demonstrate it. This should push me to do another tune soon if I want to show it off).
- And a bunch of stylistic changes that feel pretty irrelevant to mention.
-
I'm not criticizing the codepen exemple for using Font Awesome.
I get it, it's meant to be a simple example and simply including it to handle the details is perfectly fine for that use case. ↩
-
I don't hate javascript. I even enjoy it sometimes, and I use plenty of small scripts on this very site.
But the fact that so much of the web just can't run without it horrifies me.
Back when I learned web developpement, the common advice was still to use it sparingly and always make sure that disabling it wouldn't break anything.
Whether you want to call it graceful degradation or progressive enhancement, I still think that's the way to go. ↩
-
In keeping with my above rant about modern practices and dependency management, I sure as hell won't integrate this in some fancy build system.
Package managers and their ilk have their uses, but for small tidbits like this, I'll just keep with vendoring. ↩