466: Tabs In Component Libraries, Grouping @media Queries, and When is Software Finished?

Download MP3

What's the best way to include tabs as part of a component library? D-d-d-discord update on Alex's CSS-Trickz clone. Do you group your @media query items together or spread them around? And is software ever truly finished?



Chris Coyier and Dave Rupert in silly sunglasses and a sign that says Shawp Tawlkk Shough DOT COM

Chris Coyier and Dave Rupert

This episode is with just Chris & Dave, ShopTalk Show's hosts. Chris is the co-founder of CodePen and creator of CSS-Tricks, and Dave is lead developer at Paravel.

Time Jump Links

  • 00:52 Send us your thoughts!
  • 04:12 Tabs
  • 17:24 Sponsor: Dexecue
  • 18:26 Putting tabs in as a library component
  • 42:30 Sponsor: Netlify
  • 48:36 Do you group your @Media queries together?
  • 53:47 Is software ever finished?


[Banjo music]

MANTRA: Just Build Websites!

Dave Rupert: Hey there, Shop-o-maniacs. You're listening to another episode of the ShopTalk Show. I'm Dave--kind of regret eating hardboiled eggs in my tiny shed--Rupert [laughter] and with me is Chris--comfortable in the booth--Coyer.

Chris Coyier: Oh, no! Bud, I'm sorry. That stinks.

Dave: There are just foods you shouldn't eat in your shed, Chris, and it turns out hardboiled eggs is one of them.

Chris: Oh, my God.

Dave: Yeah.

Chris: Maybe in a few minutes, though, you'll get used to it. You'll get Stockholm Syndrome for your nose.

Dave: Yeah. Yeah, I'll be fine. I'll be fine.

Chris: [Laughter] Okay. Well, there's a bunch of stuff to talk about this week. We'll do some questions that readers have sent in. I would implore you to use that button on to send in questions. They don't have to necessarily be in the format of a question. They could also be, "I saw this cool thing and I think you should talk about it," or "Here is something interesting I did." Even though it asks you for a question, I don't really care.

Dave: Yeah.

Chris: It can be anything.

Dave: Or, like, "Can you have my boss on the show?" That's a common one. [Laughter]

Chris: Oh, my gosh. Anything but that.

Dave: Yeah.

Chris: Anyway, it's a weird one. We get it a lot, and it's like--

Dave: Sometimes it's genuine, and sometimes it's just completely, like--whatever--hustle culture. You know?

Chris: Yeah.

Dave: It's hard to tell the difference sometimes. You know what I mean?

Chris: Yes. It feels disingenuous, and then it forces me to use my disingenuous alarm internally. I'm going to always err on - because so much of it is actually disingenuous that even the ones that aren't, I might slip in that direction. So, sorry about that.

Dave: Yeah! Isn't it wild? To take that posture, it's like, "This is a scam artist trying to do a scam." Yeah. I don't know. Even if it's something nice.

Chris: What was that thing that Zac mentioned in the chat the other day? It was like a crypto thing for open-source. It was like, "Here's a new way." You know everybody is trying to fix open-source, right? Like, oh, it's so hard for the maintainers. Which it totally is, and there's not enough money in it. How do we maintain it?

This was an interesting one, and it looked interesting, especially because Sindre was one of three devs behind it, which gave it some legitimacy. But then we were all looking at the homepage and trying to read it and be like, "I literally don't get it. I'm in tech. I do some open-source stuff. I even have some crypto, believe it or not, and I just don't get it."

Dave: Yeah.

Chris: Just completely--

Dave: Twenty different projects, cool projects: React and Head.

Chris: Well, 11ty, of course.

Dave: 11ty.

Chris: That's why Zac was asking about it.

Dave: Yeah. Top-tier, top-shelf projects had 3,223. Every single one had dev coin and whatever that dev coin equals $17,000, or something like that. I just was like, "Kind of made up."

Chris: You didn't blame Zac because he's like, "Is there $17,000 sitting on a shelf that I can just take if I sign up for this thing?"

Dave: Right.

Chris: You know what? I think that's what he was--

Dave: Yeah. That's a lot of open-source bucks.

Chris: But I think it was predicated on this new -- it didn't really say that it was, but is it? I think it's a new coin that they're minting.

Dave: A new coin that they're minting. Yeah, I don't know. Then how is that minted? I don't know anything. Is it really valued that on day one?

Chris: How do you know it's worth $17,000? Yeah.

Dave: Yeah.

Chris: Oh, I don't know. And so why we brought that up is my disingenuous alarm goes blaring.

Dave: Mm-hmm.

Chris: If I have all these alarms, I've learned as an adult to not ignore the alarms.

Dave: Mm-hmm. Mm-hmm. Yeah. It seems significant.

Chris: There are alarms for a reason. Sorry, I don't even remember the name of the thing. I'll try to put it in the show notes, but I don't guarantee it.


Chris: All right. I wanted to ask you. I'm going to derail us.

Dave: Oh, let's go.

Chris: We'll get to some questions.

Dave: Yeah. We're a question and answer podcast, but not this week. [Laughter]

Chris: It's kind of about pattern libraries and design systems and that type of stuff.

Dave: Yes.

Chris: I've been working on it more and more for us because we, at CodePen, have moved to this mono-repo thing. One of the reasons of moving to a mono-repo is we have different codebases that we deploy to different places, and that's kind of on purpose and has been kind of fun. Then the point is, what can you share in the mono-repo? Mono-repos are about sharing across codebases.

Dave: Mm-hmm.

Chris: One of the things is a pattern library, like we should put our tabs in there because then project A, B, and C can all use those tabs. Isn't that efficient? Right?

Dave: Yes.

Chris: The answer is yes; it totally is efficient and a very good way to work. Right?

But that component library has opinions of its own. How are they built? How do you import them? What framework is being used? And that kind of stuff.

The fact that that's around makes me feel very opinionated about how they're used in there. It's just got me thinking a little bit, like I needed tabs. You have some stuff to say about tabs, I think, because you've been doing work with this community group on getting a little further with tabs. Isn't that right?

Dave: Yep. Yeah, I could give you the whole skinny on that because I kind of have questions for you about that.

Chris: Let's do it. Let's do you first because I want to see where this goes with tabs.


Dave: Okay, so tabs, right? We just landed a whole new research document. We've been going through dozens and dozens of design systems, even the historical, going back to 2004 where Hixie (Ian Hickson) proposed tabs in a forum or in the mailing list.

The idea of tabs on the Web has been around. That's like HTML 5 days, planning out HTML 5. It didn't make it.

Chris: It never did. Yeah. Okay.

Dave: And so, we've been looking at all these tabs and how different places do it. But the latest kind of research doc that we kind of put together, there are sort of like five different ways. I should pull this up, not just riff. There are sort of like five different ways people are doing tabs and it gets more micro-nuanced from there, like mini-features like select boxes in tabs or close buttons in tabs and stuff like that.

Chris: Hmm.

Dave: The design of them, I could even get it down to maybe four different designs. It's like the five-element concept, a very explicit, like, "This is a tab set. This is a tab bar. This is a tab." That's three elements, right?

Chris: Yep.

Dave: Then you have panel sets and that's the div that wraps all the panels. Then each panel has a panel tab. And so, that's the five elements.

Chris: Oh, that's the fifth one. I was like, I could name you four, but that's what I was missing was the one that wraps the panels. I feel like that's not always there.

Dave: Right. That's not always there. Yeah, so that's like the very explicit, gives you all the styling hooks that you need, right? There are pros and cons to each of these.

Then you get into a four one, and maybe you don't. Yeah, you just pop out the panel wrapper, right? Panels just kind of live as children, sort of like a details element or something. You know anything that's not in the tab bar is a panel, right?

Chris: Yes. Yeah.

Dave: Yeah. That's sort of how jQuery UI did it and stuff like that. Then there's the three-element option, right? Which is sort of like you have a tabs wrapper and then each tab is in a tab element.

Chris: A or something. Yeah, okay.

Dave: Yeah, you'd probably just write a tab element, and then that turns into a button, actually.

Chris: A button. Mm-hmm.

Dave: Then it goes to the panel. Then there's a panel element, a tab panel or something. That's what ARIA calls it.

Chris: Yeah. Three is pretty -- you could pull it off with three, probably.

Dave: You could pull it off with three. There is actually a two, right?

Chris: Oh, really?

Dave: Where there's a tab set element or tabs element. I'll just call it tab set just so we're all clear. Then you have a tab element in there, but you have tab label equals home, and then all the panel content inside the div, or inside the tab element. That's the two-element.

But when you start looking at that one, you start seeing the problems. You're like, "Oh, well, that label doesn't exist anywhere in the content." Right? It's almost like a placeholder. It never gets read by the screen reader unless the thing fires and creates the tabs.

Chris: Oh...

Dave: And it wouldn't get translated by Google Translate and stuff like that, so there are problems with stuffing the label in an attribute. Then there's sort of the--

Chris: Well, it's weird. Why try to be minimalist about this?

Dave: Right. Right. I think it looks really good.

Chris: Oh.

Dave: Like, which one is beautiful? This is the beautiful one, to me. But then there's--

Chris: Oh, really?

Dave: Well, it's just--

Chris: I getcha.

Dave: The functional problems are too much. The Google Translate thing is sort of an issue. But maybe the output would be Google Translate. I don't know. Anyway, but then there's this idea that we're kind of sort of coalescing around is the idea of a one element.

Chris: Oh, God.

Dave: You just tab set around a bunch of content, so like a heading div, heading div, heading div, and that way it basically is like a spicy section that kind of progressively enhances your content.

Chris: Oh, okay. And does that cover all the UI patterns that you found?

Dave: It does.

Chris: Yeah?


Dave: There's sort of like a lift, like landing three elements is a bit of an issue. I think it's kind of in my brain between the three-element and the one-element sort of thing. The three-element is really explicit and it's what you sort of expect if you read ARIA. It's like there's a tab and then you click the tab button. Then it goes and opens the panel.

Chris: Right.

Dave: However, if progressive enhancement fails, a browser that doesn't have tabs or whatever fails, the screen reader would be like button, button, button. Then it would just read content, and that has no meaning. It's sort of this table of contents pattern that has no meaning to the assistive technology. It's only when it's enhanced and has all the stuff.

Chris: Yeah, unless it's an A, and then it does, doesn't it?

Dave: An A, yeah, it does. But, in a screen reader, you rarely ever are navigating through a linear flow. Does that make sense? You would maybe just skip the A and the A is not--

Chris: I see.

Dave: You'd skip the A, the list of A tags and you'd get to the panel content and just be like, "What is this? What's this about?"

Chris: Oh, because it's just panel, panel, panel, panel, panel, panel.

Dave: Yeah, it's just unlabeled panels, right?

Chris: Ew.

Dave: So, the idea of "heading panel, heading panel" sort of gives you this content structure that then gets enhanced into tabs.

Chris: Yeah.

Dave: Then when you sort of look at tabs, like a row of bricks going horizontally across the screen -- that's what we sort of visualize when we say tabs -- even like now, Firefox redesigned their tabs this week.

Chris: Yeah. Tabs.

Dave: People are like, "They are not tabs because the content doesn't touch the thing," you know? [Laughter] But like--

Chris: Mm-hmm. That was me.

Dave: Semantically, they're tabs. Okay? [Laughter]

Chris: Yeah.

Dave: But visually, they do not maybe meet the requirements of tabs. When you sort of understand the underlying technology, a tab is really just an interface that shows one -- a grouped interface that shows one piece of content inside of it. Right?

Chris: Right.

Dave: Another example of that is one item, like a one-item accordion, an exclusive accordion. You can have -- a different presentation of tabs would be like a one-item accordion. You actually see this in responsive patterns like Brad Frost collected a long time ago. That's one pattern. You have a tab or a vertical accordion that shows one item and then gets converted into tabs. Right?

Chris: Sure.

Dave: That's sort of another consideration, "How does this work responsively?" and stuff like that. There's sort of like this--

Chris: Are you thinking you could use details internally? Probably not, huh? Maybe.

Dave: You could. You could totally do that, but details sort of has its own little just micro-baggage. The thing about details is it eats the headings and stuff like that.

Chris: That's pretty bad.

Dave: Yeah, I mean if it's bad if you browse by headings. [Laughter]

Chris: Yeah.

Dave: There's just this idea that maybe we can have these spicy sections and then these sections sort of transmogrify. I hate to use this analogy, too, because it maybe has feels from people, but input type equals text, or something. You could do spicy section or whatever, spicy panel type equals accordion, or spicy panel type equals tabs. Maybe, through one element, we can kind of get a bunch of different interfaces out of it.

Chris: Yeah.

Dave: I don't know. How does that sit with you? Is that good, bad, ugly? Chris, what do you think?

Chris: Oh, just being exposed to this thinking for the first time and it feels good, I guess.

Dave: Okay. Okay.

Chris: I like the idea because you're focusing on that pre-JavaScript thing, right?

Dave: Mm-hmm.

Chris: Like, what if JavaScript doesn't run at all, or what about SSR, whatever, right? This content needs to be super semantic to begin with. If the content never changed from what it is when you're just marking it up, but that's fine. It's fine, semantically solid content.

Then the Web component boots up and does whatever. It turns it into a bunch of buttons. You click the buttons and reveal the panels. Yadda-yadda, right? Yeah. Yeah, I mean that's important stuff.


Dave: What's kind of neat about this -- or at least I think -- if you wanted that two-element one that I like that's just like tab label, you could author that in Vue. Then your Vue code just has to Shadow DOM or whatever Vue out. Your Vue component just has to write out the heading structure and the panel structure that the native tabs element could then intercept. It doesn't limit you from the thing you like or the explicit way you like to write it or author it. We just now have a way to express it in HTML.

Chris: Yeah.

Dave: I don't know. Anyway, that's tabs.

Chris: Well, and also, your goal here is we're trying to cow path out a little bit, or whatever, what this would be like if HTML picked it up. Right? This is great as a Web component, too. Maybe it will always be a Web component.

Dave: Mm-hmm. Yeah.

Chris: But wouldn't it be cool? Isn't part of the goal here to maybe convince HTML to do this?

Dave: Yes, and there are people from browsers involved.

Chris: Yeah.

Dave: It does seem like a possibility. I mean it's very within the realm of what we can do. But you kind of get into this place where, if you make it too complicated, you're never going to appease everybody. Right? Google Docs is going to show up and be like, "What about the plus button to generate your own tabs in the thing?" It's like you've maybe graduated to a point where you need to code your own tabs interface. You know what I mean? [Laughter] We're just kind of like--

Chris: Yeah.

Dave: We're never going to make everyone happy, but I think we can get to a point where we're all -- I don't know. You have to build in just enough flexibility and then just enough purpose.


[Banjo music starts]

Chris Enns: This episode is brought to you by Dexecure, a company that saves developers time by automating mundane tasks that you all hate to do but have to do. Images, JavaScript, CSS, HTML, fonts, and third-party assets: Dexecure does the optimization with just one line of code, and you can focus on what you love doing; building new and exciting websites.

It's super easy to implement. Just one line of code needed for integration, or they've got a plugin if you're using WordPress. No matter the device or browser type, Dexecure will always have the best version of your website.

You can visit for one month free when you sign up for any basic or pro plan or try it out for free with their free account. Our thanks to Dexecure for sponsoring this episode of ShopTalk Show.

[Banjo music stops]


Chris: Well, look at what I just sent you then. This is -- I got started talking about component libraries and design systems and stuff, right? We have the mono-repo, and we have the thing. We needed a tabs component in there.

Now, we already have tabs on the site in a number of places. This was an opportunity to hone them in. Make it a standard library component. Make sure everywhere on the site is-- It's not a particularly hard job, I don't think, you know.

Dave: Mm-hmm. Mm-hmm.

Chris: But for us, this is, let's start from scratch here. You know? Let's define the API of what we want and make sure it works for us and have the different variations that we need and all that stuff. Let's probably not write them by hand for us.

Dave: Yeah. Yeah. Yeah.

Chris: Well, at least let's look at other stuff out there because we know that tabs are hard. They should be buttons, and they should be activatable by pressing the enter key, but you should also use the arrow keys to get between them and stuff like that. If we do it ourselves, are we going to remember or prioritize arrow key pressing? Maybe not. You know?

Dave: Mm-hmm. Yeah.

Chris: But maybe we could reach for an existing solution that has arrow key handling built into them.

Now, an example of that is React tabs. These ones that I'm specifically mentioning are in the React repo. They're the tabs from the React team. Right?

Dave: Yeah.

Chris: These are battle-tested tabs.

Dave: Ought to be good, right?

Chris: Well, sure. I mean are they? Probably. I don't know.

Dave: [Laughter]

Chris: I didn't, unfortunately, get them totally vouched, but in looking at them they do look pretty good. They do have that kind of keyboard handling and focused stuff - yadda-yadda.

I'm looking at these. I'm like, "Well, let me base ours off these for now." You know? Like this isn't even on production yet. It's just part of our library - whatever.

I moved them over into our component library because I couldn't just use them off the shelf. I couldn't just import it from theirs because -- I don't know -- they have opinions that our thing doesn't have. It uses a class names library that isn't the one we use. So, all of a sudden, we'd be bundling two class name libraries. Is that a big deal? No, but it is to me. I think that stuff is obnoxious, and they were written as class components, not functional components, like the rest of our library. And if we just pull them from NPM, then we have no control.

And it was styled weird. We style our stuff with CSS modules. They didn't. They just import a raw vanilla style sheet to do their stuff.

It didn't offer the same kind of control that I would want to have because every other element in our entire design system library offers that control. Well, I have one new one that just doesn't. I'm not cool with that, so I port these over.

What would you call these, looking at the JSX implementation of them? A four? A four-element?

Dave: This would be like a four-element thing, yeah. I mean, yeah. It could be three. You could automate the tab....

Chris: The tab lists from...?

Dave: The imports are import tab, tabs, tab list, tab panel, so it has four components.

Chris: Yeah. It just doesn't have a tab panel wrapper, whereas the tabs due for some reason.

Dave: That would be the five, yeah. Yeah.

Chris: Yeah, and I liked -- it made sense to me because I don't even mind tab list because tab list, conceptually in my brain, is where I put border bottom two pick solid white. You know?

Dave: Mm-hmm. Mm-hmm.

Chris: Like you were saying, it's a row of bricks or a bar or something. That matches with our design aesthetic of, like, a row of links at the top and some panels below it. There's a separator in between. The tab list gives me that clean opportunity to do that.

Dave: Definitely.

Chris: Not to mention, I can break off the CSS for each one of these four things. Yeah?

Dave: But if you wanted to flip this into an accordion, this pattern doesn't work for you, right? Because all the tabs, the bricks are grouped at the top.

Chris: Totally! It doesn't work for that.

Dave: [Indiscernible]

Chris: Yeah. Do we need to do that? I don't know. Maybe we'd just make another library component that does that instead. I'm just talking about brass tacks, like day-to-day reality of using this kind of thing.

Dave: Yeah, yeah.

Chris: I think our usage is slightly different than the meetings you were in where you're trying to solve it for the whole Internet. We're trying to solve it for one website. I don't know. It feels a little different.


Dave: So, what's the problem?

Chris: Ah... I don't know that there is any dramatic problems other than now we're broken off from development of these. If they ship a new version of it, I've rewritten these, so I can't use their improvements. I'd have to figure out what they did and port over the changes somehow.

Dave: What'd you have to do specifically to switch it up? Maybe you described it, but you had to reclass it and--

Chris: Well, yeah. I rewrote them in functional components instead of class components, which was not that bad, actually.

Dave: Okay. Okay.

Chris: I know you don't run a ton of React, but it really wasn't that bad.

Dave: Mm-hmm. Mm-hmm.

Chris: They mostly just used a render function in their class components and that's really easy to move over to just return from the thing. Then I ripped out their class name library and used ours when needed. There was some functionality that we just didn't need, like I don't really need a lot of stuff to be able to disable a tab.

Dave: Mm-hmm.

Chris: If I need that, we can just put it back in, but we don't disable tabs ever, so didn't need that.

Dave: Right.

Chris: I kind of got the chance to reimagine it a little bit. But I'd say 80% of the code is the same and then broke up to CSS into how we do CSS and then was able to totally restyle it how we would style it, not how they style it. I used none of their CSS, pretty much. That's a pretty dramatic change.

Dave: I think that's for the shareable component lifestyle that we're all a part of now. [Laughter] I think style is where it gets really tough. You want to ship with some default style. For example, flex row for all the bricks. You want to ship with some default style. But the second you start providing style, you've provided kind of a heavy opinion. Are you including styled-components or CSS modules? Especially in React land.

Chris: In this case, the only assumption was that they import the style sheet with Webpack, which that's not how we roll, so we didn't do it. But it was a vanilla style sheet, so I could have just copied and pasted it into some global or something, but that's just not really how I like to roll either.


Dave: No, I think that's tough. I think Web components have this problem too. I know Vue components do.

I just did a thing the other day. This is similar. I used a pagination component for an app that I'm building. I don't want to code a pagination component. Right? Surely, somebody has done that, so I import this one. I spent the next three days just reading docs. It's the most boring thing in my whole life. [Laughter] It's all fine. I got through it. But I just was reading docs, like, what's the attribute to do this? Then how do I do this?

None of the styles worked from the thing we had spec'd out, so I had to marry the two, like the undesigned one. You know? Or unwired up, undynamic one to this. I don't know. It was a lot of grunt work.

I thought, "Oh, I'll save so much time pulling this off the shelf," and then I still spent like three days on trying to get the pagination all working. Figuring out how their API integrates into my whole system and then how can I paginate from the database from what they offer me?

Chris: Yeah. What was the final? Did you just not use theirs? Just kind of copy the code?

Dave: It was fine and it's good. The nice thing is they kind of automate the ellipses. My client literally has like 600 pages of content on one view. It was just like, "Oh, well, that's a bummer," but they paginate, give you the ellipses in the middle there, and you can say how many on the side and how many in the middle. That was all very cool.

I could kind of program, just type in numbers and change that. It took me a while to figure that out, but I could. But then you know what happens. Media queries. Oh, crap!

Chris: Mm-hmm.

Dave: I want to show different numbers at different viewports. It just becomes a bit of a problem when you're in a Vue component to add and resize, window resize, but you can do it. But it's just doesn't feel great. But then I did it, and now I'm flipping some computed variable based on the window resize - or some data variable, local data state, just to handle window resize.

Chris: That's funny. In JavaScript, there are media queries in JavaScript with whatever match media, but that fires once. There's not like a watcher.

Dave: Yeah. Yeah, there's not a watcher, so you have to bind a watcher. I guess in React hooks land it'd be like use whatever - use effect resize observer or something. I don't know. Yeah, and then you....

Chris: Does resize observer work on the whole window? I guess it would, right?

Dave: Yeah. Yeah, or the component itself.

Chris: You're not watching. You're not literally watching a window.resize event.

Dave: I am, actually.

Chris: Oh, really? Don't you have to debounce it then?

Dave: You could, but I don't.

Chris: [Laughter]

Dave: But it's window resize and then it does an if window matches, so I'm trying to be as quick as I can about it. I think where window resize gets a bad name is where you're trying to reposition and remove things on resize, so you're calculating paints and stuff like that.

Chris: Oh, I see.

Dave: I don't think flipping a variable is really the problem with window resize, personally. But anyway.

Chris: Okay.

Dave: But then I just say, okay, is it this big? I just said an is desktop because I was like, well, if this component needs it, then something else does. But then I have to pop is desktop into ViewX or whatever and so it's fine. It's all working. But it just turned into, like, "I want to use this thing off the shelf," to -- and this is me because I'm like mister roll my own components, but I just was like, "Okay, I'm going to do the responsible thing and use code that already exists on the Internet."

I just was like, "Man, this took me three days to do this. It's done and I'm happy and it works, but it was not a pop it off the shelf, open the package, now I'm playing GI Joes." It was not that experience for me. You know what I mean?

Chris: Right. That's what I'm curious about, too. Should I have tried harder to not rip these things apart and just use them as is? Then if the answer is, "Ah, nah, you're cool," like I'm not really considering that anyway, but shouldn't Web components be trying to get to that point? If you finalize these great tabs you're working on, Dave, the point is to use them off the shelf, isn't it? You don't want to rip them apart to use them because they should be doing all the good stuff internally without having to have to do that.


Dave: Yeah. In theory, they should be. and so, I think they can. I think where it gets weird, though, in my experience with Web components, building a site, and we put in these tabs. The tabs then enhance the content or whatever. But the client was using Tailwind utilities.

How do you use Tailwind and progressive enhancement? It's possible, obviously. But it was kind of hard to be like -- and traversing the Shadow DOM. That's actually kind of an actual problem. How do you change a component, change the Shadow DOM or inject Shadow DOM, but then, on change, now you apply a whole new class set. You give them the tab appearance or whatever.

It was pretty difficult, so we just wrote CSS. I bailed out of Tailwind.

Chris: Oh, you just bailed?

Dave: Yeah.

Chris: What's the Tailwind answer? Is it to be like, well, select inside but then use at apply or whatever?

Dave: Yeah, but you'd have to really hijack the Shadow DOM. From the outside, you've have to kind of come through. Or there's the option to roll your own component. You can import that component, just like JavaScript, and then maybe modify it. Or you have to just clone it and make a Tailwind version or something like that.

Chris: I'm not as worried about it with Web components as I would be here. I would trust the Web component to just -- I don't know. I'd just have to have a higher level of, like, these things are already ready to go.

Dave: Mm-hmm.

Chris: You just use them and that's it. If you can't use them, then don't use them.

Dave: Yeah. I think the idea is you provide a little bit of style. From a generic Web component perspective, you provide flex row for the tab bar and maybe just one RAM of margin or something like that. Then you kind of bail out. Jonathan Neil has--

Chris: Like a user agent stylesheet, almost.

Dave: Like a user agent stylesheet.

Chris: Like we just do the bare minimum so it's not a problem.

Dave: Then you bail out. Jonathan Neil had the idea, like when you're authoring Web components, maybe use the where selector to basically get zero specificity on your thing.

Chris: Oh...

Dave: Then people can kind of come through and style it.

Chris: Ah, I mean it's clever, but it's like you already have to jump through such hoops to get in there to style anyway.

Dave: Yeah, like using parts and everything.

Chris: Yeah.

Dave: Yeah.

Chris: This doesn't really help you that much. Yeah.

Dave: But, you know, maybe that--

Chris: If you weren't using Shadow DOM, maybe.

Dave: Maybe that is a best practice, though, is just saying we're going to use where for our default styles and then just let people kind of style from there. I don't know.


Chris: Okay, so tabs. The unactive tab is hidden, right? That's a thing.

Dave: Unactive is hidden. Yep.

Chris: Probably even like display none hidden because it shouldn't even be in the--

Dave: Yep.

Chris: The screen reader tree because it will be once you activate the tab, but we're trying to intentionally hide it because that's the point here, in a way. So, you use display none, right?

But I was just reading this morning, and whatever. I've been hearing about this for a while. Content visibility: if you say, Dave, div or whatever tab, something, content visibility hidden, it's hidden. It's a lot like display none.

Dave: Mm-hmm.

Chris: It behaves very similar to that, except it has this one caveat that it goes through the trouble of rendering it, I think, and then hides it so that when you unhide it (content visibility visible) it's faster.

Dave: Okay. Okay. Interesting.

Chris: I wonder if that type of thing will become, over time, a best practice for something like tabs if you expect the tabs to be highly interacted with. At the moment, I'm like, I don't really think of activating a tab as something that's particularly slow.

Dave: Yeah.

Chris: It just goes blip and it's there. But I don't know. Maybe if you had some real heavy content in there or something, then maybe that becomes a best practice. Interesting. That's not really its main use case. I think when people are talking about content visibility, at least so far these days -- this is a very brand new thing (Chrome world only) -- that you say content visibility auto and it's this lazy loading for the whole DOM.

Dave: Yeah.

Chris: You say, I'm going to identify this below-the-fold stuff and say content visibility auto on it, meaning that the browser doesn't even bother to render it until it heuristically determines that you are about to see it.

Dave: Yeah.

Chris: Like it's close to being in the viewport and then it renders it. Just interesting. It doesn't -- it seems like some people are talking about it, but people don't seem to be taking it up in droves or anything. Probably partially because there was some bad press for it when it rolled out that it had some accessibility problems, not to mention scrollbar problems. If you're used to--

Dave: Yeah.

Chris: This website having this long scrollbar to indicate how much content it has, if you don't even render the stuff below it, well, [laughter] your scrollbar is all wrong.

Dave: Now you're going to fail your cumulative layout shift.

Chris: Oh, perhaps. Yeah. Then as you scroll down, it's not like you scroll down a little bit and then it all pops in. It progressively loads in like lazy loading works, meaning you get kind of a jittery scrollbar, which Alex Russel has written about. There are ways of combating this problem, but I think that was part of the bad press in the beginning was, like, "Oh, God. Way to roll out this feature. Did anybody use it?" [Laughter]

Dave: Yeah, you know, I saw Jake Archibald took the whole entire HTML spec and turned it from a 20-second render to sub-millisecond.

Chris: One or something, yeah.

Dave: Yeah, sub-second render using content visibility on the second level or something like that. You think of HTML. It's ginormous, right?

Chris: Right.


Dave: But there's apparently a page on the Internet that does it all in one go. [Laughter] It's mostly text, thankfully. I tried to use content visibility on my bookshelf on my website, which I thought, like, "This is perfect," because it's broken up by years and you don't need to see all the years or all the years don't have to render at the same time.

Chris: Yeah.

Dave: I did not have good luck with it, man. It actually thrashed my Lighthouse scores.

Chris: Oh--

Dave: I think it was cumulative.

Chris: What if you don't care about that? Did it feel good even though your score was bad?

Dave: It did not feel good.

Chris: Okay.

Dave: It did not feel like I made my site faster. It could be because I am using this blank SVG lazy loading technique, so I kind of have a square for my image assets to show up. Because they're books and they're not the same asset or the same size, I have to do something clever. I basically have to draw a div and put a thing in there. I'm using an SVG to lazy load all these images in.

Chris: Okay.

Dave: Or to act as a placeholder and then I scroll intersection observer and then I pop it off. Maybe I'm already doing enough to where it's not making a difference to un-render whole blocks of content. I don't know. We'll see. I may try it again.

Chris: I wonder if it will get better or not or what. It seems like -- to me, it feels like image loading lazy, right?

Dave: Mm-hmm.

Chris: It's a thing. A lot of browsers support it. No-brainer. Put it on every single image of your website. It's just a good idea. Why would you load an image that's not visible? Screw it. Just don't load it at all. What a great feature.

Especially now that images are all self-aware, and if you put width and height on them, even in a fluid environment, they reserve the space thanks to aspect ratio and all this stuff. It's not going to hurt your CLS. Awesome! Just great! Put it on everything.

Why not put content visibility on everything too? It's lazy loading for DOM elements. Even if you put it on everything above the fold -- I'm just saying theoretically, in my brain, this is how it works. Wouldn't that be nice? Then the page renders it, knows what's above the fold, and it brings the stuff in. Even if it's content visibility auto, it should be fine.

In practice, I understand it's all borked and not good, but it seems like the kind of thing, like, why bother with all the trickery? Why not just put it on everything? In a theoretical world where you fix it and it's not janky.

I just keep seeing stuff like this. I read this article about images and all the stuff you should do. Lazy loading, yeah, but now there's this new one. Have you seen this one? Image decoding equals async. They're saying, "Oh, this is a big one for chilling out the CPU or GPU or whatever." Don't even bother decoding the image until later, so you're not tying up the main thread. I don't even know how to talk about it intelligently.

The advice so far has been really scant. It's just been like, "Oh, that's a new thing you put on your images. It's good." With no guidance. Like, okay. All my images or some of my images? If this is this panacea of good performance for images, why aren't you just doing it, browser? Why isn't it the default then? Why do I have to know about this? Why do I have to add crap to my HTML to take advantage of this performance thing when the guidance -- if there isn't any, but it kind of seems like just use it?

I don't know. Somebody from some browsers need to take this one by the cojones and tell me what to do.

Dave: [Laughter] Totally. I'm kind of with you. If it's something I should put on everything, why don't I--? I don't know. Why isn't it the default? What's the opposite of decoding async? Decoding sync?

Chris: Yeah.

Dave: That sounds kind of dangerous. I stop my whole page to decode some image?

Chris: [Laughter] Yeah, or auto or something.

Dave: Is that what's happening right now?

Chris: Is it saying, don't do it? Have I heard whispers, "Don't do it on your hero image. You want that to render fast?" Okay, well, I won't then if you tell me.

Dave: Yeah. Just say, "Don't do this here, Dave." I think every docs, blog post on should have a "For Dave Rupert" section.

Chris: [Laughter] Yeah.

Dave: Okay, Dave. Here's how it works.

Chris: Yeah. Behind the scenes stuff.

Dave: That would be good.

Chris: It's not that big of a deal, but how can somebody be talking about this in an evangelist capacity and not have that occur to them that it should be accompanied by actual advice?

Dave: I would love it. I would. Not to bust people. Not to publicly audit people too much--


Dave: --because that's always going around. I would love not just, "Hey, here's a cool new thing." I would love, "Here's when to use it and when not to use it." You know?

Chris: Yeah. Yeah.

Dave: Rather than, "Go build 100 websites and figure it out yourself," maybe give me some tips.

Chris: Or even better, just do it.


Chris: Let's do a little spot for Netlify that works in nicely to literally everything we talk about and do. I'm going to talk specifically about Netlify OnDemand Builders, which is a fancy word, I think it makes sense, and I think it's well named, but is also really not that complicated. It's kind of like a cloud function that just caches itself.

Dave: Mm-hmm.

Chris: When you return a value to it, it will only run once. It will run once and do its thing. Then the next time it gets hit, it will return from the cache and not run. Pretty simple concept.

Dave: Pretty simple. Yeah.

Chris: Yeah, I love that. Part of the deal is then, oh, you can run your 11ty site and tell it, "Hey, these at this route, these 500 pages, don't even build them ahead of time. Just wait until they come in."

The URL gets hit. Then it runs it. Then from then on, it's cached. But it just makes the build really efficient and it just makes the computers do less work that they don't have to do. It's very clever, I think.

Useful, but it's not just useful just for that. I think that's the main use case is a website that only builds pages that it needs on the first time, which is like a classic, old-school Web caching phenomenon. It's not even a new idea at all. That's one use case.

Another use case is a social media card. CSS-Tricks has tens of thousands of pages of content on it. I ran a build process. Would I pre-generate tens of thousands of social media images on it? It's not a JAMstack site anyway, so no, I wouldn't. But let's say yours is. Why would you make that part of the build? Just make your social media images an on-demand builder, so they're only called when they're needed. Then you're not wasting computer cycles, storage, and all that stuff for nothing.


Chris: Here's another use case for this. Alex Revere in the [techno voice] Discord jokingly registered CSS-Tricks with a Z .com.

Dave: Mm-hmm. Mm-hmm.

Chris: At first, he's like, "You know what? I'm just going to put 15 lines of fetch JavaScript on it and hit CSS-Tricks's JSON API to get the content and I'll just plop recent articles on the page," just as a joke.

It's actually kind of funny. Chrome renders this warning when you go the website that's like, "Some attackers use [laughter] URLs that look similar to other websites. Are you trying to actually go to" It's pretty funny.

Dave: Yeah.

Chris: Even though it's a totally legit website with nothing nefarious on it at all. Anyway--

Dave: And it's pretty clearly [laughter] a different website, but yes. Yeah, yeah.

Chris: Client-side JavaScript, fine. That's how so many websites work these days. It's just the way it is. You know? Hit the API every time. But you'd think that they--

There's no caching at all happening there. [Laughter] Every time you refresh that page or another user visits it, they've got to hit my API to get content, so you're hammering that API. I don't particularly block it. It's all well cached - I assume. There's probably three layers of cache between that and the MySQL anyway, so I'm not that worried about it. But should I be the kind of site that's worried about it, I might even rate limit that and be like, "No, no, no. You can't just hammer my website." Literally, a thousand people went to see like that, that would be kind of a problem. It would slow down.

Dave: Mm-hmm.

Chris: I mean I don't know how powerful that cache is. Maybe it would be fine, but it's generally a bad idea, especially because not all APIs are cached because they can't because they're returning real-time data, or whatever. [Laughter] So, I just jokingly poke Alex and be like, "Dude, you should server-side render it. Why would you do this client-side?"

We talked about it last week. It's starting to be a lot more en vogue to bring this into the build and bring this into server-side rendering.

Dave: Mm-hmm.

Chris: Here's how you could server-side render it. You use a Netlify function to do it. That way your cloud function returns the HTML for the page, but your cloud function runs in Node. It still just runs a fetch, turns it into HTML, and runs it. That's better for users, in a way, because then it's not client-side JavaScript running. By the time they get the HTML, it's already there ready to go - probably a little faster. But then every single request runs that cloud function, meaning that every single request is all of a sudden on Alex's dime, not spread out to the users. It consolidates the cost.

With a little joke site like this, it's going to fit comfortably on Netlify's free tier, I'm sure. But should it get popular one day, you're going to get overage charges and stuff because you're hammering that cloud function, which is running over and over and over and over and over and hitting the API and has the same problem with not caching the API.

Enter on-demand builders, a trivial change to that cloud function, that just wraps the thing in a build function - pretty much. All of a sudden, it only hits it once. It hits the API once, caches the results, and every other person that hits that page then just gets served the cached results. You've made this server-side rendered and super-efficient, which would be true for any API that you hit. It's very smart.

Then how do you break that cache? Well, that's on you. Does it add a little technical debt? Sure. I think Alex wired up a thing that's just like if this then that or Zappier or a GitHub action that runs on a cron or something that just tells Netlify to rebuild, which will clear the cache.

Dave: Yeah. There you go.

Chris: That's it, so that was a long spot, but I think that was a cool journey. Alex wrote all this up in a blog post I'll link to.

Dave: Yeah, and the fix is kind of incredible. Instead of exports, exports.handler=async function, it's exports.handler=builder.

Chris: Builder.

Dave: Parentheses async function.

Chris: Yeah. You just wrap it. Nice.

Dave: Just wrap a builder. That's so great. Anyway -- all right. Let's get to at least one question here on the question and answer podcast.

Chris: Yeah. Go for it.


Dave: Jimmy Davis writes in, "Do you guys typically group your @media queries together or just spit them out all over the place whenever you need one? I used to lump them all together at the end of a document. But these days, the existence of grid and, specifically, grid template columns, I've been burping them out right there in the middle of my CSS. I also used to be more concerned about very specific media queries, say 1200 plus 9060, 600, 300, whatever. Adding all those adjustments into those. Now, I'll literally resize the window of a particular element. Whatever. It looks weird. Drop a media query in."

Chris: Right.

Dave: "Maybe not very dry, but grid makes it so much faster than the olden days." What do you do? Do you just--? Are you a grouper?

Chris: You don't remember this used to be so common question on CSS-Tricks? It used to be the concern back in the day. We used to get this question all the time about, "I've looked at the CSS that my Sass is producing because Sass allows you to nest media queries and nesting media queries is very satisfying. I find it satisfying today. I love it.


Chris: It's a nice way to write CSS. It does not group them. Like Jimmy says, it burps them out right as soon as it can in the process of CSS, meaning that it's repeated a lot, a lot, a lot, a lot. It doesn't make any attempt to be like, "Oh, I'm going to find all of them that are the 1200 breakpoint and just group them all together and put it at the bottom of the style sheet or something." It can't do that because of the nature of CSS, because of the cascading source order issues of CSS.

And so, our answer classically on ShopTalk Show would be like, "Don't worry about it. It doesn't matter. It's not affecting the parse time of the CSS. Maybe a little, but so, so trivially. Please, do not worry about it. And it's not affecting the over-the-wire size of the file because of how Gzip works. It's incredibly efficient."

The answer was, please do not worry about it.

Dave: Through repetition, it's tokenized.

Chris: Tokenized, yes.

Dave: The repetition gets tokenized, @media, min-width 300 pixels. We'll just send it over the wire as X.

Chris: Yeah, and that applies to you too, Jimmy. You burp them out, buddy. It don't matter. Right?

Dave: I would say, burp them out. Yeah, I mean if it makes your team make more sense. I think, early in the days of responsive Web design, there was this idea you serve separate style sheets, and so grouping was maybe effective.

Heck, when responsive Web design came out, I was still authoring one CSS file. [Laughter] It was maybe 1200 lines long, but I was still working in one single CSS file. Maybe it was a little easier to do back then, but I think now....

Chris: And you'd choose your DX. Maybe grouping -- if grouping felt good because you'd be like, "Here are all the styles that relate to that breakpoint," because you like it that way, well, then do that. I don't know. But it sounds like it's easier for you to burp them out. That's cool.

You were saying also, like, why consistently use 1200 all over the place because it could be that it actually breaks better at 1170 on this particular thing or 928 is a good breakpoint for this one. Yeah, who cares. Just do it.

Dave: Yeah.

Chris: This is, of course, related to container queries, too. Once we have container queries where we're actually authoring them more so in our production projects, those are going to be all kinds of random numbers.

Dave: Yeah.

Chris: There'll be no grouping.

Dave: Yeah. I think, for me, [laughter] I've kind of started going back to, like, 1024, 768. I used to not do that at all. But I'm just like, "Whatever. I can nudge it later," and I never do.


Dave: I don't know. Or I'll do increments of 200, 600, 800. Sure, that looks good. All right. Next. What's next? You know?

Chris: There's some predictability to common breakpoints that you know when it hits that you can go look around at that size and verify that things are okay. Whereas, if you 100% use absolutely random numbers all over the place, it makes that quick spot-checking of what's happening with the page a lot harder. You have to -- whatever. That's just how I feel about it.

Dave: Yeah. No, I think there's -- I love it in the component. When I author Sass, though, I do actually try to do just my media queries at the bottom of my Sass file, like one tier deep. I don't try to do media queries four tiers, like nested, nested, nested.

Chris: Oh, sure.

Dave: Maybe I'm still doing it at a component level, but anyway.

Chris: Yeah.

Dave: Then there are breakpoint functions and stuff like that. I've kind of been getting into that, but even that I'm just kind of like -- I don't know -- just type a number. Everyone knows what the number means. If you have to change it, you can grip it. That's my professional opinion.



Chris: Good question, Jimmy. Please keep them coming. We really appreciate that. I'd like to shout out there's a nice podcast called Design Details. They reached their 400 show milestone. Congratulations.

Dave: Hey. Congratulations.

Chris: I listen to it off and on, and I think he'll do a good job with that. Number 400 was an episode called "Is Software Ever Finished" that I think they did a good job of. It was only 25 minutes. They did a good job of tackling it. It just made me think, think, think.

They really went into how software is just this very predictable cycle of a great software product being released that's so simple and people flock to it. They think it's so the bee's knees on how good it is. If it achieves some level of success, it'll grow. They'll hire people. They'll take investment, whatever.

This is just very generic terms I'm talking here. It doesn't even have to follow this exact path, but the temptation then will be to add and work on it, work on it, work on it.

Dave: Mm-hmm. Mm-hmm.

Chris: Not just fix stuff but add things to it because the more you add to it, the slightly wider your user base can be. Maybe you can hit a whole new vertical if you add this feature to it - or something. The temptation is add, add, add. That will never stop until it gets to the point where the software has lost its simplicity.

Even if it's your top goal in life, the more you add to it, it's inevitable that it becomes a more complex product. Maybe that's good. Maybe you make a lot more money, your team grows, and the influence of your software grows.

Everything is great until that day comes where a new software product comes on the market that's simple. It doesn't have as many stupid buttons as your website does. It's a lot simpler. People flock to it because it's better for their....

Dave: Remember the old chat app? How stupid!

Chris: Yeah.

Dave: Well, they try a new chat app. It doesn't do half the things you need it to do, but it's less. It's fast.

Chris: They focused on Figma versus Photoshop kind of stuff. You know? There's probably a day where Photoshop was simple. Those days are long gone. Figma arrives. Sketch arrives. And it's very, very simple. People flock to it because of how nice it is for the jobs we need to do. Yet, those companies are doing very well and growing and making their software much more complex. It's already happening to them.

Dave: Yep.

Chris: Someday, there'll be new design software that comes along. It's simple, nice, and refreshing, and lots of people start to use it and they become the old, complex dogs that people reject.

Dave: Oh, I hate new things. They're taking my money.

Chris: [Laughter]

Dave: My bucket. [Laughter] My bucket of money. No, that's interesting. I'm curious. I'm going to listen. Good pick, Chris.

Chris: [Laughter]

Dave: All right. We should wrap it up. We are kind of at time here. Thank you, dear listener, for downloading this in your podcatcher of choice. Be sure to star, heart, favorite it up. That's how people find out about the show. Thank you for listening. Follow us on Twitter, @ShopTalkShow, for tons of tweets a month. Join us on the Discord, [techno voice] Discord, Yeah. Chris, do you got anything else you'd like to say?

Chris: Yeah.