It’s 10PM
do you know where your JavaScript is?
We’ll talk about best practices to either reduce (or increase!) the JavaScript footprint on your web site to a sweet and very practical spot for best results. It’s far more common to have too much JavaScript on your web site, but can you go too far? Is zero JavaScript a worthwhile goal? Let’s talk about it!
Videos
Links
- HTML Tags 1992
- The Trojan Room Coffee Machine
- Cascading Style Sheets, level 1 - 17 Dec 1996
- Inclusive Web Design For the Future with Progressive Enhancement
- oogle Hosted Libraries
- Andy Davies - Safari, Caching and Third-Party Resources
- Double-keyed Caching: How Browser Cache Partitioning Changed the Web
- Gaining security and privacy by partitioning the cache
- Polyfill Supply Chain Attack: Details and Fixes
- Subresource Integrity
- Bake, Don’t Fry
- Cascading HTML style sheets -- a proposal
- Media queries W3C Working Draft, 4 Apr 2001
- Responsive Web Design by Ethan Marcotte
- We're Bringing Responsive Video Back!
- Low←Tech Magazine
- Styled Components
- The many definitions of Server-Side Rendering
- Linaria - Zero-Runtime CSS in JS
- Zero-runtime Stylesheets in TypeScript.
- Library Upgrade Guide: <style> (most CSS-in-JS libs)
- Web Awesome Comparison component
Transcript
That's great. Oh, I just want to set this up real quick. [laughter] [applause] I had to upgrade my bag, my luggage, to bring that with me on the trip, and the... [laughter] Yes. I don't know if everyone knows the story here, but Manuel spoke here, and he put this on the stage. I saw it from my basement in Iowa when you guys were having this conference. It was very lovely to see. He's brought it around, I guess, to many places in Europe. It's been in far more places in Europe than I've been. That really made me feel really good. [laughter] I brought my own.
All right, let's get started. Today I'm going to talk to you about, not 11ty, about JavaScript. In 1964, only five years before the Apollo 11 moon landing, it did happen. The midpoint between the very first release of Internet Explorer and the current year, there was a PSA, and it was a television advertisement, and it asked parents. It showed up on the news. It's 10 p.m. Do you know where your children are? Now, as a parent of two myself, I'm very curious why this public service announcement was needed. I don't know why children were roaming the streets at 10 p.m., but here we are. Today I want to ask you a very similar question. [laughter] As you create the next generation of applications and websites on the web, as we have more and more JavaScript on our sites, I want to give you the same call to action. It's 10 p.m. Do you know where your JavaScript are? [laughter] Is? Are? JavaScripts are? I don't know. You all say maths. I say math. I'm an American. I'm sorry for that.
But here we are talking about JavaScript. To do that, through the lens of this optometrist, we're going to our eye doctor. Today we're going to walk through a bunch of different little web components, components on the web. We're going to learn how to build an image comparison, and we're going to do it 14 different times. [laughter] Oh, too far. Here's what a component like this might look like. Play. Okay. You have a left image, you have a right image, a before and after, and you're comparing them. This is one from the Web Awesome library. Some of my colleagues work on this. It's a web component. I work for Font Awesome. They also have a Web Awesome component library that you can use. We're going to walk through 14 different kinds of this style of component.
We're going to show where we came from and where we can go next. Let's hop into our little Mandalorian time machine and travel back to the year 1991. It's only 35 years backwards in time and 22 years, again, after humans landed bravely on the moon. It really happened. We can only assume that dinosaurs had just recently gone extinct. We can find a young spry, London-born, 36-year-old Tim Berners-Lee publishing the very first draft of HTML. How might you create an image comparison component using that technology that was very first introduced in 1991? Call it out if you know. >> You can't. >> What was it? >> You can't. >> Back and forward. >> What did you say? >> You can't. >> Oh, you can't. No. [laughter] >> What did you call me? [laughter] >> It's fine. No, no, no. It's fine. We're fine. We're fine. That's right. ASCII art. ASCII art. We're pivoting. ASCII art. You might do it with ASCII art. Let's put just some ASCII art onto the screen and show what it looks like in the web browser.
Oh, no. There's no preserved white space. It renders terribly. The first HTML spec did not have a pre tag. What you can do... Here's the elements that were available at that time. Some of them are not available now. Does anyone know what one you might use for this use case? >> Plain text. >> Plain text. Great. Sure. Yeah, that works. The interesting thing about plain text is that it vacuums up the rest of the page. When you use it, there's nothing else that will render that is not parsed by the HTML parser. Yes, you could use that. It works great. But that text, anything that you want after that is not going to work. You would use it like this. Do you have a start tag, but no end tag? I don't know if HTML sanitizers realize this, because this rendering is using the Arc browser now, so this tag is still supported in Chromium browsers. If you ever want to screw with a website that allows you to upload HTML, maybe use this element.
The more appropriate one for this use case that doesn't vacuum up the rest of the document is the <listing> element. It still works, and it allows you to have embedded preformatted text. Preserves your white space. I think we've maybe spent a little bit too much time on that, so let's move forward a full year to 1992 when some dude, we don't need to remember who it was, introduced the <img> tag. Amazing. I'm now mortified to realize that the next slide does not have alt attributes on the markup that I'm showing. I am so sorry for that, and I will fix that before I publish these slides. The <img> element was first available in the Mosaic browser, and it offers the mechanism that we can use to make our first image comparison component. Of course, you would have to crane your neck left and right to see what the comparisons are with the image.
Here I'm showing the Trojan Room coffee pot from the University of Cambridge. It was one of the very first images published on the Web, and it was used to see if the coffee pot was empty. You would check the website so you didn't have to go downstairs to see if the coffee pot was empty before you went to the coffee room. I don't know. It was useful. It's one of the first images that I remember seeing. I didn't see it live, of course, because this was so many years before I was born. Not true. [laughter] But yeah. Importantly here, as we go through these different examples, I want to document the different dependencies that are shown in these examples. We're making two different network requests, two for images, and I want to document those with the diamond shapes there. As we go through these different examples, I'll have those documented and show how they affect.
Let's skip a few more years ahead to 1995. Can anyone predict what's going to happen in 1995? [audience member speaks indistinctly] [imitates drumming] [laughter] That's okay, some people have seen 2001 Space Odyssey. Great. Yes, the introduction of JavaScript. Amazing. We're going to use the mm_swapImage. Who knows this? [audience cheers] All right, amazing. Yes, it was a very old script that people would use. It was introduced by Macromedia, Dreamweaver. It was classic, right? You would hover over the image, it would swap it out with a new image.
We're going to use this as the JavaScript example, because what better option? This is what it might render like. Of course, you wouldn't see the Dreamweaver logo showing up there, but it's fine. I added it for fun. You hover over the image, it switches from gray-scale to color. Great, amazing. Awesome. When we look at this script, let's document the dependencies that we have here. We have a giant block of JavaScript. We have two network dependencies, the same as before. One of those is dependent on JavaScript. We start to see how these things interrelate. Now, of course, I did not consider the img element to be a dependency in this, because we're moving forward with a new baseline of using HTML with the support. It's important to think about how these things interconnect. That can play into a lot of ways in how your website renders.
Just as an example, on the left, you can see this is the HTML-only representation of the site. When you interact with JavaScript on the right, you can see how those differ. It's important to consider those in your brain when you're developing a site. What will happen when you don't have JavaScript available? You'll notice in this example there is no layout shift. There's no sort of jumps in the website between HTML and JavaScript representation. It is important to consider how different things load. Importantly, we no longer use this. This JavaScript is no longer in use, and no websites currently have it on their GitHub repository as public. We don't need to worry about that anymore.
Let's skip forward to 1998. Who can guess what's coming? That's right, CSS. Amazing. Now, of course, CSS 1 was in 1996, but I wanted to use position absolute, so we're going to go skip ahead to CSS 2 in this example. Now, Håkon Wium Lie and Burt Bos, who is, I guess, no relation to Wes Bos, worked on this spec. This is what it might look like if you have an image comparison component using the technologies that we have available to us in 1999. Again, I'm so mortified that I did not go back and add alt attributes there. OK, that's fine. We're moving forward.
Here are the dependencies that we have here. We have a CSS block. We have our same two image elements, and we have a JavaScript block at the end. All of this is inlined, our CSS and JavaScript, so we don't need to make network requests there. I'm denoting those with a circle instead of a diamond. Here's how it might render. When you move around your mouse on the page, it will show the before and after, very similar to the web component that I showed at the beginning.
We're moving forward in time. We're getting a little bit more sophisticated with our representation, and we're skipping ahead another couple of years to 2003. This was the introduction of... [laughter] progressive enhancement. Yeah, here we go. This is, I think, one of the biggest impacts that I've had in my web development journey, is understanding what progressive enhancement was. It is the sort of knowledge that you start with the baseline and you layer enhancements on top. Everything that you add is an enhancement, and the support of that thing needs to be considered independently. It was introduced in a SXSW talk in 2003 by these two gentlemen. Funnily enough, when I went back and tried to find the original slide show, some of the images were not working. But they're progressively enhanced, right? Because you can still see the alt text attributes shown there, and you know what the intention is for those. The thing that I think I really want to communicate here is how your resources load is just as important as what those resources are. This ties into not just your progressive enhancement, but your browser support, your web performance, your flash of unstyled content, which is what happens when your components show up before your CSS is available. The layout shift of that, when you visit a page and stuff jumps around as you try to click on it, that is not a good look. We need to consider all of these things independently and consider how they transition between. In our example now, we have two images that are stacked, one on top of the other, because we just have an HTML representation of this. When we layer our CSS on top, then we can stack those images visually to show one at a time. We will need JavaScript to add the interactivity to that. It's important to visualize, at least in your mind, if your computer is way too fast to have those visual in your loading experience on your development machine, you at least need to consider how these play out independently so you can serve the best experience to the widest variety of user agents. Someone might be on the subway and might not have the best network connection. Hotel WiFi is getting better, but it's still not great. I like to stack those up to see how the component will change as these different technologies are rendered and completed in the browser.
When you have a CSS stylesheet, of course you use an external stylesheet. You just use a link element here. We can use a script tag to load an external JavaScript. But importantly, each one of these individual things is now a network dependency, right? Network dependencies can fail. We can't guarantee that a network request is going to complete successfully. There's a lot of combinatorics happening here. You need to consider how each resource might load slow, might not load at all. That's what that might look like to the end user when they're visiting a site. If your CSS takes a long time to load, they might have a white screen for a long time since external stylesheets will block rendering if they're in your head. If your JavaScript is slow to load, your component may render using, in this example, the monochrome example, and it won't be interactive to show the full color example until the JavaScript has completed. Likewise, if those resources fail to load, what will your component look like? I'm using this sort of performance notation to try and simplify how we think about this a little bit.
I'm trying to stay away from those more complicated network waterfalls. When you think about a component like this, you load your external CSS. The eyeball is supposed to indicate your first render when a end user might first be able to view the component. Your images might come in later. Your JavaScript will load, and then the little finger, I guess, pointing up is supposed to be interactivity. They can interact with the component. I think it's important to consider this sort of experience independently for each step and think about how your components, each individual component loads and how it might act. This range between when a component is first available to be viewed and when it's interactive, I don't know if this has a real name.
I don't think there's a name associated with this range of time. Let's just call it the Temporal Dead Zone. Of course, this is a term that's already reused in JavaScript when you try to use a variable that hasn't yet been declared. Let's not use that, sorry. Just to make a completely off the cuff joke, let's call it the extemporal dead zone. That was in no way prepared. No, let's call it the twilight zone. No, let's call it the interactive dead zone, I guess is what I'm settling on for the purposes of this talk. This is not a term that anyone else uses. It's something I just made up. But I guess I've learned that you can just make up terms and people use them sometimes. That's what I'm doing now. This is happening.
To test that in your web browser developer tools, you can actually go in and block external requests. You can throttle external requests. That's what it looks like in Firefox. You can block external requests. If you want to test how your app works with only one of your JavaScript files not loading or one of your external stylesheets not loading, you can do that. It will show you. You can go through and customize how your application or your individual component might load and see all these different scenarios and how they might play out.
Another thing you can do is use a service worker to play with this. If you don't want to go into dev tools and modify things in the dev tools, you can actually add a service worker to your application to add artificial delays. Here's what that might look like. I've used this in the testing for this talk. There's just a function delay. Then when a generic fetch handler, I look for a delay search parameter. I'm going to use a laser pointer like I promised. A delay search parameter, then I use the number that goes into that search parameter to add an artificial delay to the request. You can customize in your code how individual things might have an individual delay. I add a three second delay to my stylesheet or a three second delay to my script. That has been very... It makes it much easier to test different web performance scenarios.
Now if you really want to be mean, how many people have heard of 56k Fridays? Nobody? Oh yeah, one person. It's kind of a thing in the web performance community where you throttle your network connection to be very slow to see how the internet works. If you want to do this at an application layer, you can add this service worker, which penalizes anyone for having a lot of cores in their CPU cores here. The more cores you have in your CPU, the extra delay we add to each request. It's just a fun little thing that you can do with your coworkers to have a nice time. They'll really respect you for it, I'm sure.
We're getting really advanced here. We're going to the year 2008. You might guess from the logo there, no, we're talking about CDNs. 2008 was really the introduction, or around the time that jQuery was very popular. One of the things that the Google folks did is host jQuery on a CDN for everyone to use. The idea behind that was if folks are using the same version of jQuery, it would be cached and they would get reuse across different websites even, using the same library URLs, which was great. Here's what our previous example might look like with the introduction of a CDN URL. We're just changing the library JavaScript that we're using. We're adding another external style sheet request. We're using jQuery to implement this image comparison component. This is relying on a third party at runtime, which is maybe not the best thing for your web performance. You're basically injecting additional JavaScript for this component, which might be fine for your application's needs. It is lengthening that interactive dead zone because you're putting extra JavaScript in between when the component is first visible and when it's interactive.
This practice is not really used as much anymore. Andy Davies has talked about this because of double keyed cache. Privacy-focused folks started to realize that maybe these CDNs were tracking how these things were used. Browsers started to implement cache that would be tied to the individual origin itself. There was no cross-site reuse, even if you were using the same URL on two different sites. Browsers started to lock down that in a more privacy-focused way. There were supply chain attacks that came into CDN usage. polyfill.io was a big one. It got sold to another vendor and they started to inject malicious JavaScript into their CDN at the CDN level, which was not great. It was not a great look. It was quite bad.
That's when folks started thinking, "Oh, maybe we should use sub-resource integrity."
Sub-resource integrity is basically a hash that you put on your script element that is tied to the content that you're pointing to. As long as the content is immutable and not changing and the integrity hash matches the content that's expected and downloaded by the browser, it will execute that. If not, then it fails pretty miserably, which is fine. But CDN started to realize that maybe not all the URLs that they were using were immutable. There became this sort of back and forth between the CDN...you should only use sub-resource integrity on immutable content. CDN started to maybe discourage using sub-resource integrity a little bit because they wanted a little bit more control over the content that was served, or maybe they wanted to fix a security issue later. It would not allow them to do that without failing a bunch of applications in runtime, which is...the failure experience of this is not forgiving at all. You don't see that sub-resource integrity encouraged as much anymore. All right.
We're skipping ahead to 2008. This is maybe one of the ones that is most near and dear to my heart. Static site generators. Now Eleventy was not around in 2008, but it was really popularized from Jekyll. Yeah, Jekyll is great. I used Jekyll for my website for many years, and I love it. It was really the turning point, I think, in a lot of...it was the turning point for me in a lot of thinking about how server rendered applications could work. So yeah, I think Jekyll was great. Of course, this is not Tom Preston Warner. I don't know if anyone noticed that, but this is Tom Preston Warner. That other guy was Tom from Myspace. Really different Tom, but yeah, great.
If you go back even a couple of years before this, in 2002, Aaron Swartz wrote this great article called "Bake, Don't Fry," and it was about how you can run a build step to make a website that is just static HTML and the spillover between dynamic and static and how those can work. Now of course, observers will know that this is not Aaron Swartz at all. Actually it is, I'm sorry. That is Aaron Schwartz. I've gone and reproduced the same example. We're making an image comparison component, but I've done this using 11ty.
If you're noticing the code here, I'm using an image map. How many people have used image maps? Oh, wow! That is way more than I expected. Incredible. What I'm doing here is creating a bunch of different pages and an image map for each page and customizing that so that every slice of my image comparison is its own static HTML page. Now don't do this. Don't do this. But I've done this just to show that you can make an image comparison component using no JavaScript at all. I've completely removed the JavaScript piece of this equation, the client-side JavaScript piece of this equation to create 100 different HTML files. It's a terrible user experience, but I do think that it's important to know that there are creative ways to work around not having JavaScript on the client. This is one of them. Don't do this because the user experience is not great. You're clicking through and each one of these is a full page reload, but the image map is set up so that each slice is its own URL. Don't do this. But again, we've completely removed this interactive dead zone because there is no JavaScript. The user experience is terrible, but this is an option. Don't do it. But yeah, this is kind of what we're going for here a little bit in that we want the renderer and the interactivity to be available at the same time. Don't do that.
Let's skip ahead to 2010. Of course, responsive web design. Another pivotal moment in my web development journey. Incredible. I completely forgot why I added this. Oh, sorry. Responsive web design was sort of introduced in... What's the year? 2010 is this big article in A List Apart written by Ethan Marcotte. No, wrong button. Sorry. It was the idea of customizing your HTML and rendering different content based on your viewport size or different user agent conditions was not introduced in 2010. It was first introduced in 1994 in this Cascading HTML Style Sheets proposal. It was not actually implemented anywhere, but the idea was there from the beginning, which I thought was very interesting. I don't know, maybe you won't, but it's fine. There are various stages in which this was introduced and talked about before media queries were even available. But this is the first mention of media queries in 2001. Now again, the impetus for this was Ethan's responsive web design article in a list of part. If you go to the article now, you can see that the image that is shown here is customized tailored to your user agent. If you make your viewport a little bit wider, it shows you a different image, which I think maybe not everyone knows, even though everyone's heard of this article, I'm sure. But you get even wider viewport, and it shows a different image, which I thought was really great. If you go super wide, not many people know this viewport size, but it will actually show you Ethan's head on all of... No, it doesn't actually do that. But there's a lot of different ways to tailor your user experience and how different assets load on your page based on user agent conditions, and here's a couple of them up front. You can customize your CSS. You can customize your JavaScript, your images, your videos.
Video source media, adding media queries for those is a recent reintroduction. When it was removed, it was available, then removed, and then re-added by my friend Scott Jehl, put a bunch of work into getting that back. If you have your image comparison component, you could tailor this to be a completely different visual representation at a wide screen. I don't know if this is the correct aspect ratio of televisions at this time, but that's what we're going with. If you go out to GitHub, they do have a diff, but it just stacks the images at different viewports. Why not switch the mode to be a different visual representation as well? I don't know. Something you could do. Customizing your site's experience based on your client or user agent is one way to do it.
Another sort of interesting thing that I've noticed is you could do it on the server as well. This website is running a completely solar powered web server. I think in a way that's almost tailoring to the server resources you have available as well.
We're skipping ahead a little bit more. We're going to 2013. Single page applications. We love single page applications. They're our favorite way to make websites. They are the fastest way to make websites. I don't want to call anyone out for doing this, but we will do it a little bit. I don't want to be too on the nose about it, but there's a lot that goes into single page applications. It is sort of this idea that when they first were introduced, it was really tied into client side rendering. We render everything on the client. The client is responsible for doing the full rendering of our components. That really had a huge effect on web performance at the time because it was very close after people started to view the web more on mobile phones, and smartphones were woefully underpowered compared to what we have today. Those two things at the same time was a very unique problem. If you go out to React's documentation today, they still have client side rendering as sort of the default experience when you try out React. But one of the nice things here is that when you make a React component, when you import a style sheet in your JavaScript, which is an interesting thing to do, an interesting way to think about it, it does get bundled up nicely using the React scripts that they have available. They don't really take a great default on CSS bundling, but they sort of have farmed that out to larger frameworks in the space. So this is what your rendered markup might look like if you go through the React tutorial right now.
We have our JavaScript external dependency, we have our style sheet, and we have our empty div ID root, which is the bane of my existence. I don't like that. It does have a huge impact on the web performance of this site. You sort of need to, even when JavaScript is available, you still need your CSS to load. So it puts all the eggs in one basket. All of our external network requests need to finish before we have a usable component, and it puts everything together. You can kind of see why they wanted to do this, right, because it simplifies the mental model a little bit in that you never see an unrendered component. You never see a component that is available on a page that isn't interactive. They've removed the interactive dead zone because they've frontloaded everything. They put all the JavaScript at the beginning, all the CSS at the beginning, and that's one way to do it. It's just much slower. But it does solve that problem, and it frontloads almost 200 kilobytes of JavaScript, which is a lot. [laughter] We'll just leave that there. I think that spoke for itself.
So let's go ahead again to 2014, maybe a year later. There was this introduction of CSS and JS. So we have this JavaScript bundler piece that's happening in our React world, and we put everything into the JavaScript bundle. We frontloaded all of our JavaScript. Why not just put the CSS in with the JavaScript too? Why not mix those together? That eliminates our external stylesheet, removes the dependency, right, and puts it all into the JavaScript. Ooh, we love that. And even from this perspective, it's no worse than what we had before, right? We made our JavaScript bundle bigger. We put our CSS into the JavaScript bundle. But again, that has huge performance concerns because CSS is much faster at rendering and parsing than JavaScript. JavaScript is the most heavy piece of the stack, so not great. So just as an example, here's what it looks like in styled components. We have our comparison component. I've added a little just opacity 0.5 declaration there in CSS, and that will roll up into our JavaScript bundle. Now this, I don't know if folks know this reference, but this is a callout to I just copied and pasted this slide from the original CSS and JS slideshow. Yep, just keep an open mind. So as I go to the next slide, maybe, this online 14,928, we can now see in our output JavaScript that we have our opacity declaration there, which is... Who could predict that this would be slow? I don't know how we could have seen this coming. But yeah, we'll move on.
2016 was the introduction of server-side rendering, so the idea that you can render your HTML on the server, you can do all of your HTML processing on the server and just show the finished HTML product to the client. To me this was kind of like a mea culpa moment for the client-side rendering that we've really harped on for many years at that point. They've acknowledged that client-side rendering wasn't the fastest thing, and now we have server-side rendering. When you come at this problem and you hear someone say server-side rendering, there are multiple different definitions of server-side rendering. I would recommend reading this article to really learn what those are, because I feel like if you recognize that this is a vague term, I think the confusion is being monetized, and you can read this article to learn why. I don't need to go into it too much, but I think the reason people are confused about what server-side rendering is and the different contexts in which that exists is that people are making money from that. I don't need to go into that anymore.
What I did is I went back and installed Next.js version 1. I thought it was okay when I looked at it. Next.js is sort of the thing that popularized server-side rendering in React. Version 1 was decent. It was only 107 megabyte node modules installed, which is not great, but the bar has gotten so much lower now. But importantly, Next.js brought server-side rendering in a way that no one had ever done before. The concept did not exist. It was really pioneering. It was new. It was fresh. We loved it. Yeah, it was something that we just had never seen before.
Okay. But this is what the output looks like. We have our CSS there. We have some JavaScript there. We have more JavaScript if we scroll down. We have a CDN URL there, which is an interesting thing to add to default output. Yeah, more JavaScript. We can see this interactive dead zone is just widening. Our components might be visible, but we're adding a ton of stuff in the middle before when the component is viewable and when the component is usable. We're just widening that gap in a huge way. Here's what it looks like with the latest version. If you're willing to download 500 megabytes of node modules, you can use the newest version, which is great. The output is even more complex. We have our images showing there. We have our JavaScript here. We have our CSS here. There's just a lot. Again, this interactive dead zone that we're talking about, when the component is first visible... I'm using, I guess, one of the most popular image comparison React components that I could find. I'm using one of the most NPM downloads. This is what it looks like when it's server generated, before it's interactive. The slider handle that we have on the left here is not usable, but it's rendered to the end user. That's what server rendering gets you. It gets you an unusable component that looks like it could be interacted with, but it isn't. Here's what it looks like when it is interactive. After those hundreds and hundreds of JavaScript kilobytes have downloaded. Again, we're just widening this gap. It's making this interactive dead zone even wider and wider and wider. We're putting even more and more JavaScript in here, more external requests, more things that can fail. Each one of these is required. Each one of those external network requests was required. I went through and blocked each one individually just to make sure that some of them weren't optional. You're putting all of these... You're basically gambling that all of these network requests will finish. You're hoping that the user doesn't try to interact with the page in between when they can see it and when it is available to be interacted with when the JavaScript is finished. Again, this is just more and more kilobytes injected into this interactive dead zone, widening that gap. Okay, that was a big section.
Let's jump ahead to 2019. We're getting closer to modern day times. The introduction of CSS and HTML. I think maybe people know where this is going. Tailwind. I know a lot of CSS advocates aren't a fan of Tailwind. I'm not a fan of Tailwind, but I do see it as a pivot from putting all of our CSS into the JavaScript bundle. It was sort of a way out for these framework-heavy folks to say, "Well, maybe we can put CSS back into a CSS bundle." Even though React doesn't take a heavy stance on how CSS is bundled, we can put all of our CSS classes in the HTML, which means that we don't have to manage CSS at all. But really this did help in reducing JavaScript bundle sizes. Now, I don't know why things like zero-runtime CSS and JS didn't catch on as much, and Tailwind sort of went to the moon. But there are a bunch of CSS and JS options that don't have runtime JavaScript bundle. I don't know why you wouldn't use something that would compile to a style sheet, but there are options to do this now. React sort of walked back a lot of the CSS and JS advocacy that they had done. This was an approach that came from the React team originally, but yeah, walked it back in 2021.
Let's move ahead a little bit more to one of my favorite web technologies that have come out recently, and that is Web Components. Web Components are amazing. I love using them. I love building them. I think that they have an incredible longevity to them. I think it's a great way to build for the web. This is, yeah, again from the Web Awesome library. They have an image comparison component. As we come to the end of the talk, I do want to show what the markup of this component might look like. We have our CSS style sheet. It's very standard. There's not much to this. But we've frontloaded our CSS that we need for our initial render. We still have an interactive dead zone here, which is a problem. Using Shadow DOM and scope CSS and Shadow DOM, we can use Web Component technologies to sort of couple our CSS to our JavaScript, which sounds scary when I just talked about the dangers of CSS in JS and putting all of our eggs in one basket. But I do think there is value in understanding the difference between frontloaded JavaScript that can help render your components without a JavaScript dependency, frontloaded CSS, excuse me, and CSS that is coupled to your JavaScript rendering. So if you're doing markup transformations in your Web Component, the CSS that's tied to that, you can bundle it with your component as well. The browser itself, importantly, can help optimize those things.
But the thing that I really want to encourage here, and the big takeaway that I really want you to think about, is that when you have a server rendered component and you have widened the interactive dead zone as much as possible, there are things that we can do without server rendering to make that a little bit nicer. And I'll go through those. Now some folks have encouraged solving the flash of unstyled content problem. So flash of unstyled content is when you can see your component, but it doesn't have the CSS available to it and the CSS that it needs to render. And folks in the web font performance world have recognized this problem from the beginning. There's font display block and font display swap showing you sort of the fallback fonts before your web fonts have loaded successfully. And this is the same tact. I don't like this. I don't think it's good. I don't think it's good to hide the page. I think that we can do better and we can show things more progressively. Now a lot of the flash of unstyled content problems and solutions hide the entire page until your components have initialized, which is bad. I don't like the web performance of that. Instead of using :root and :has here, you can just take that off and then your individual components, you can solve that problem just on an individual component level. But again, I don't like the idea that we need to hide components when we can show something that's usable. So here's one solution to this. So in web component world, you can use defined, the colon defined pseudo selector, and that allows you to style things before the component JavaScript has been registered. So we can use defined and not defined to control how our component renders before the JavaScript has initialized the component. So we can hide one of the images. You can use this approach to hide one of the images instead of hiding the whole component, so at least one of the images will render. You can get rid of the flash of unstyled content problem because it's not showing two images and then jerking it back to one image, which is not a great experience. But even this I don't think I'm super happy with.
So how do we get a little bit more imaginative here? We're going to go forward. We're going to use our imagination, or imagination. We're going to use slots. Progressive enhancement slots, hey! So here is just... This code is not super important, but the code that I have added here is to a Web Awesome component, our Web Awesome comparison slider, and I've used, because this is using named slots for the before and after images, I've just added my own sort of child HTML inside of this component that will get completely removed when the component is initialized. That's just how this component is set up. I've added a small form to show the grayscale version and the color version. Here's what it might look like. I've used, again, the colon not, colon defined pseudo selector in CSS to say without any JavaScript the component is still interactive now. So if our JavaScript fails or doesn't load or whatever, you'll still have a somewhat usable component in this interactive dead zone space. So this is maybe not the greatest visual representation because it's just a couple of lines of CSS and a couple lines of JavaScript. You can get more clever with it if you want. But it does give you an idea of how you can be more inventive to give a middle ground experience before the JavaScript has even finished loading.
Here's another example that just uses an inline event handler and a range input to emulate the full experience. Now again, this will go away when the component initializes, but it's just one line that I've embedded inside of my WA comparison component that gets removed when the component's initialized. So it's not server rendered, really, but it's not client rendered either. It's a little bit of both, I guess. In this space between our first visual representation of our component and when the component is interactive we can do more creative things. There are options available to us where we can remove this interactive dead zone. [laughter] All right. I know that was a lot, sorry. Just to summarize, I do think that we should just think about how all of these things are interacting, how we can develop websites in the way that it's the most broadly accessible to the widest variety of experiences and user agents. Just think about it when you're making things. Thank you.
About Zach Leatherman

Zach is a builder for the web at Font Awesome and the creator/maintainer of Eleventy (11ty), an award-winning open source site generator. At one point he became entirely too fixated on web fonts. He has given 86 talks in nine different countries at events like Beyond Tellerrand, Smashing Conference, Jamstack Conf, CSSConf, and The White House. Formerly part of Netlify, Filament Group, NEJS CONF, and NebraskaJS.