483: Q&A on XState, Getting Comfortable with JavaScript, Managing WordPress Sites, and Background Images in CSS

Download MP3

As Chris says, we're back to the meat 'n potatoes of ShopTalk with a Q&A episode including: XState thoughts, getting comfortable with JavaScript, tips on managing several WordPress sites, the best method to schedule high latency tasks with Node.js, a strategy for background images in CSS, and dealing with landing pages and subpages.



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

  • 01:18 What are your thoughts on XState?
  • 17:40 Sponsor: Hashnode
  • 18:47 What is a good way for someone to get comfortable with JavaScript?
  • 24:38 Any tips on managing several WordPress sites?
  • 33:06 Sponsor: Netlify
  • 34:50 What is the best method to schedule high latency tasks with Node.js?
  • 43:51 Is there any lazy loading strategy for background images in CSS?
  • 49:57 How do you deal with landing/hub pages and subpages?


[Banjo music]

MANTRA: Just Build Websites!

Dave Rupert: Hey there, Shop-o-maniacs. You're listening to another episode of the ShopTalk Show, a podcast all about front-end Web design and development. I'm Dave--in the shed--Rupert and with me is Chris--in the booth--Coyier. How are you doing today, Chris?

Chris Coyier: Yes, man. I'm doing all right.

Dave: Yeah?

Chris: Had to ride my -- it was really cold this morning and - whatever - I had to ride my bike a long way, and my hands are cold.

Dave: Oh, man. I do not have that problem here in Texas.

Chris: [Laughter] I figured.

Dave: One: Don't leave the house. Check. Two: Never cold. Check. So--

Chris: [Laughter] All right, well, we thought we'd do a bunch of questions that you write in. Getting back to the heart, the original intention of ShopTalk Show audio podcast where you call in or more like use your fingers to write in questions about stuff. They don't even have to be questions. They could be just general topics that you think are interesting or things you want to share with us. A bunch of you do that, and please continue doing that. Let's get started with doing some of them.

Luke Brown writes in, "What are your thoughts on XState?" You know XState, right, Dave?

Dave: Yeah.

Chris: Isn't that David Khourshid's thing who now kind of has a whole team behind him, I think?

Dave: Yup.

Chris: Quit his job. Decided to make state machines a full-time gig.

Dave: Yeah.

Chris: "We are slowly adopting it within our large React application at the company I work for. While I'm finding the learning curve a little steep, I can see the benefits of managing state via a finite state machine. Have you guys any experience with XState or state machines in general? Keep up the good work."

You know I've read article after article. I've published articles on this - not my own writing but on CSS-Tricks, which I tech reviewed and all that stuff. I've heard David speak on it, and he says a lot of things we do are State machines already, so it's not like you're unfamiliar with the concept of it. There are a lot of things like this state moves to this state moves to this state.

I think the concept, though, is that if you're a little more - I don't know - emphatic about it or lean into that structure or model it before you do it that (I think, in David's words) can eliminate a whole category of bugs.

Dave: Mm-hmm.

Chris: That there are an awful lot of bugs on websites that are essentially state problems, like your app got into a state that it should have never gotten into because you just didn't -- I don't know. You happy pathed it. You know?

Dave: Mm-hmm.

Chris: You thought this would be how it would work and it didn't and that something like XState -- I don't even know if there's really any big competitors to it. As far as I know, it's kind of the big name.

Dave: Yeah. I think there was a state machine DSL, like a domain-specific language, basically like shorthand, like Markdown for state machines kind of thing somewhere. I forget what the name of it is.

Chris: I saw something like that called Lucy (the other day), just like L-U-C-Y.

Dave: Yeah, it might have been Lucy. Yeah.

Chris: That looked a little bit -- I don't know. It's like coffee-script for state machines or something.


Dave: Yeah, yeah, that's it. Lucy, a concise language for describing finite state machines. It's basically that you enabled. You have to hit a toggle event to become disabled. In disabled, you hit a toggle event to go to enabled.

Chris: Yeah, it looks beautiful.

Dave: Mm-hmm.

Chris: But it is still XState. It compiles to XState compatible state machines.

Dave: Yeah. Yeah, it makes an XState, which is kind of awesome, and props to David's project, David K. Piano's project.

Chris: Yeah.

Dave: Just because somebody was like, "I'm going to make a state machine language, but I'm just going to offload all that state stuff to David's state machine."

Chris: It looks very React-specific. Does it? Can you use XState in Vue?

Dave: I think there are adapters, but I don't know. That's probably one of the reasons I don't use it - to answer the question.

Chris: Right. It looks like it's pretty well supported in Vue, now that I'm just Googling the docs and stuff. But whatever. I see what you mean.

Dave: Okay. Yeah. The example is immediately, you know, it's just a JavaScript object, so I think anything could hook into it. The example is very kind of React-driven.

Chris: Yeah.


Dave: At least from my point of view. But what I love about it is you can define the state machine. I think it's worth backing up. Every component has state - maybe. I don't know. You have the loading state. You have the loaded state.

Chris: Yeah, error state.

Dave: You have the idle state, I guess, would be that in video games, you know? [Laughter]

Chris: No data state. Oh, sure.

Dave: No data state. You have the error state. You have the success state.

Chris: Mm-hmm.

Dave: You have all these states in your component at the component level, let alone the application level, right?

Chris: Yeah, that's before you even get into what the component actually does.

Dave: Yeah, just kind of out-of-the-box, you know you have that. And I've said on the show before; I really like that Redwood kind of has that built into, like into the cells concept. Like your component is a group of cells.

But I think XState takes it a step further. If you create this machine, the visualizer -- is it -- do they have something called visly? Am I remembering that right? I feel like that was a product.

Chris: Yeah, it looks fairly new, too. Yeah. Right,, yeah.

Dave: Yeah. Yeah.

Chris: It looks cool. You drop your state machine code in there that you might already have.

Dave: Mm-hmm.

Chris: Then hit "visualize" and you'll see fancy boxes with arrows and stuff. Right? [Laughter]

Dave: Yeah because you know how if you've ever been in a thing where you're like, "If this state and this state, then do this." You know? You end up with all this business logic, all these rules in your template. I feel like the state machines or these XState visualizes and stuff might be a really good way to visualize what's happening in your code so that somebody could just grab that machine, pop it in here, and immediately understand what's going on, where the bug might be.

Chris: The breakdown in my head is like, even the example. There's one in the visualize that says load example. You load example, hit the visualize button, and I got it.

Dave: Mm-hmm.

Chris: I see boxes and arrows, right?

Dave: Mm-hmm.

Chris: I don't get it.

Dave: You don't get it.

Chris: It's my own failing, but it doesn't help me. Even the simplest example.

Dave: You sound like my coworkers. Let's see.



Dave: Okay. Yeah. When you come into the component, like when it shows up on the page, there's a box with fetch. The first box is idle, right?

Chris: Mm-hmm.

Dave: Idle means it's on the page doing nothing, not being interacted with or anything.

Chris: This is before it even launches that fetch.

Dave: Yeah.

Chris: Yeah.

Dave: Then it'll auto-trigger, I guess, a fetch, right? It'll go into a loading state because it's saying, "Hey, I'm fetching some data."

Chris: Uh-huh.

Dave: Then from loading, there are two paths. We can have a success and a fail, but if the promise or the fetch is rejected, then we go to the failure state. But if you go to the failure state, you might have -- Axios does this by default. It retries it three times, right? It'll go retry the fetch.

Chris: Okay. Yeah.

Dave: From failure, you can go to retry. Then when you're retrying, you're back into the loading state. Then maybe it'll come back as a resolve, a success, and you get to success, which is the ultimate goal. Right?

Chris: Yeah. I mean I guess that does make some obvious sense, but is that--? I don't know. Is that helpful to see it in this way?

Dave: This is probably very micro, but you do know what's going on. On this example. Sorry, it's an audio podcast. [Laughter]

Chris: Yeah.

Dave: I've got to describe it. We do have video now, so if you want to check out that over on YouTube CSS-Tricks, the real CSS-Tricks YouTube. You can do that.

The little -- the contrast is hard to describe here. Idle, loading, failure, success: those are your four states it can be in. Right?

Chris: Mm-hmm.

Dave: As a designer or a developer, you know you have to code all of those off black boxes, this kind of dark gray box.

Chris: I see! So, that could be helpful right there is just knowing what all the--

Dave: Yeah.

Chris: If it's a box, you've got to style it.

Dave: If it's a box, you've got to style it or take care of it.

Chris: Yeah.

Dave: Then those little pill-shaped things are the actions. The pill-shaped things on the arrows, those are actions that can happen. As a developer, you know you have to code--

Chris: If it's a pill, you've got to code it. [Laughter]

Dave: You've got to code it.

Chris: Yeah, there you go.

Dave: You've got to do the templating part on the boxes and the pills are the things you have to code. I don't know. As a developer, that's kind of relieving because it sort of takes the guesswork out, I guess, if you're sort of like, "Well, immediately, I know what this does or what this is. The flow of this component."

Chris: Mm-hmm.


Dave: This example is cool, but I would love to see something weirder, like an infinitely configurable pizza machine. [Laughter]

Chris: [Laughter]

Dave: Based on past experiences. We built a state tree for that or a diagram, and it was wild.

Chris: Mm-hmm.

Dave: But I would love to know how weird this can get. This example is simple and cool but give me a weirder one. I'd love a weirder one.

Chris: Partly what I want to understand, though, is if you do it because it's some buy-in, like Luke Brown is saying here. There is some learning curve action and stuff. If the promise is now you did this, now there's no way your component can get into an unknown, weird, broken, buggy state, how does that enforcement express itself? I can't quite see it in the code here.

Dave: Yeah, because this doesn't really show. This just shows the machine. It doesn't really show the component code. Right?

Chris: Yeah.

Dave: I think it's sort of -- yeah, I don't know. I've actually done an XState tutorial, but I think there's something like mappings to--

Chris: Do I return a stateless component for each state? Maybe that's it, right? If it's in the fetch state then return this component that doesn't have any state.

Dave: Yeah. You'd kind of render based on the state, basically. Then your methods would just change the state, sort of.

Chris: Right. So, if you have a click action or whatever, its only job is to change state.

Dave: To dispatch fetch. Yeah.

Chris: Yeah, it cannot. It would not do its own fetch. The way that you author code is that you have these dispatchable events. It's like pub-sub kind of, right?

Dave: Sort of. Yeah.

Chris: Yeah, you only dispatch. You never actually do anything yourself.

Dave: Yeah.

Chris: Oh, man. That is quite the -- that's a lot of buy-ins, isn't it?

Dave: [Laughter] Yeah. Yeah, I did a course. I'm trying to find the name of it.

Chris: People that swear by this, though, I think, in their mind, you would never write code any other way. Every single component in your entire codebase would basically be a state machine. In TypeScript, too, because if you're buying into crap, you might as well just buy it all.

Dave: [Laughter] Just buy everything. I'll take one of everything on the shelf.

Yeah. Was it Learn State Machines? I feel like that was it, but maybe it's been picked up. I'm so blanking. Well, there's a Front-End Masters course on it.

Chris: Yeah, Front-End Masters is kind of our learning partner at CSS-Tricks, we point to for this kind of thing, and they super definitely have it.

Dave: But I think, yeah, there's a learning curve because it is sort of like brings in a new piece of machinery -- literally, it's called a state machine -- into your components that you have to kind of sort of build around. But you know it's hard for me to poke holes in David's argument that what we are coding are state machines. You know?

We're coding all these wild things. But the way we do it is, "I'm adding 'is loading' as a CSS class and then that's going to show/hide some divs and do all this crud." You know? I feel like David's methodology is just very explicit on what it does. You know?


Chris: Right. So, you think, if you had to bet, we're just in a state where eventually the world will come around to building in this way? Then that's just how we'll do it?

Dave: Yeah.

Chris: We're just not there yet?

Dave: I'm waiting for a component system that has this built-in. That's part of the reason Redwood is very attractive to me.

Chris: Yeah.

Dave: It has this stuff sort of built-in. It has common ones, but states can be a little bit weirder, right? Animating, transitioning.

Chris: Yeah. I wonder if that's almost a negative if it has some built-in ones, which might be slightly at odds with custom ones.

Dave: Right. Right. I feel like expressing -- you need to express a few things.

If you think of video games and stuff like that, complex organizations or complex components, rather, like game models and stuff, you have the animation idle animation where Mario is kind of bouncing around. Maybe he's doffing his hat. Maybe he sits down or something. There's an animation that runs when he's idle. There's a run animation when he's running. There's a jump animation when he's jumping.

Chris: Mm-hmm.

Dave: He can really only be doing one of those tasks at the same time. You can, I guess, jump and throw your hat, I guess, in the new ones. Now you can, from jumping, you can also be throwing your hat. You have to program these machines. Video game folks are very comfortable with building these animation state machines and it works for them. Maybe we should do it. You know? I mean ours are a little different, but if you think about it, I can never be in a success state if I've never submitted data, so there you go. [Laughter] I need to figure this out.

Chris: Well, that's the thing, right? Yeah. I wonder, then, if you get this into your brain and then start thinking about all the bugs as they roll into your app. A bug was just opened this morning on CodePen from a new developer that was like, I can look at it and see that it looks state-related to me.

Dave: Mm-hmm.

Chris: It's in between two states. It's like state is already loaded, but then you need to unload it and reload some new state and, in between those two, the element essentially collapses because it doesn't have anything to render.

Dave: Yeah.

Chris: I wonder if that's a state problem then. It would have been like, "Oh, well, when the state it empty then it needs to be showing placeholders to deal with the size," or something. But then it's like, "Was it really just a CSS issue, though?" You know?

Dave: That's hard because there are other things. I kind of have this state issue right now where if you save a file, it should go back - or whatever - save a page. It should go back to the page, right?

But something is happening where the data that returns on the save is crunching up everything. So, I have some sort of data problem where the data isn't coming back correctly, but I sort of wonder if it's just a general state problem. From the edit screen, you can only go to the save, fetching, and then save can either success or fail. You know? What if I had just a very explicit route that the code had to follow? That would be cool, but I think I'm in this kind of weird zone where it's like, "No, you can upload this and do this." So, I think I've created too many pathways for failure on this one page. You know?

Chris: All right. Good luck, Luke. XState, Dave is calling it the future of programming.

Dave: I'm waiting for it to be a built-in to a framework. I think that would be the greatest place to be - or whatever. Let me see if I can find this course I took, and I'll recommend it because I did take it. But now my brain is--

Chris: Yeah, that's cool. You get it. I'll make sure it's in the show notes.


[Banjo music starts]

Chris: This episode of ShopTalk Show is brought to you in part by Hashnode. Hashnode is a free developer blogging platform. Go over there. Sign up. It's free. It's easy. Boom! Now you've got a blog, and it's filled with features that are good for developer blogs. It's a community of developer blogs, so syntax highlighting and all those features you need for a developer blog, they have.

It's running on Next.js and Vercel. Near perfect Lighthouse score, so nobody is going to accuse your blog of not being super-fast and performant. That's great.

Then publishing on Hashnode ensures that your content can be discovered by millions of users, meaning that you have the blog. It's your blog. In fact, you can map it to your own domain name, so it's totally yours. But it's plugged into the full community there, so it's a fun place to just have an account and go explore anyway because there are loads of developer blogs there. It's this hive of developer writing, so that's fun.

Best of both worlds. You own what you create there without the hassle of having to build everything from scratch. It plugs into your content into the massive global dev community.

Markdown support. Code syntax highlighting support. GitHub backups. No ads. No paywalls ever.

Thanks for the support, Hashnode.

[Banjo music stops]


Chris: Chris Bishop, thanks for writing in. He wrote in. He's a front-end dev and a designer. He's always struggled with JavaScript - classic.

Dave: Mm-hmm.

Chris: "What's a good way for someone who is more of a designer and a CSS person to get comfortable with JavaScript?" What's the pathway there?

These days, Chris Bishop, I'd say so many people are -- you're in JavaScript anyway because you're building these sites with React and Vue or even preprocesses that are based in those languages. It feels like there are so many people that just are in JavaScript anyway that that's their journey is that they didn't really have any choice. There's JavaScript sitting right there.

But if you're mostly in HTML land and you're looking at JavaScript, one of the classics I've always pointed to is just learn about toggle class. If you're going to learn one API, learn about that toggle class because you know what classes are. You're a CSS person. Imagine if an element didn't have that class. What would it look like, or if you swapped this class with this class?

That opens up massive amount of interactivity on the Web. It's like, all you've got to learn is just enough JavaScript for that one. But that makes me actually think what you're really doing there is dealing with clicks, so maybe you could learn just enough JavaScript to essentially deal with a click. How do you attack a click handler to something?

Once you know that, I feel like it's a big old snowball of the click changes the class. Now, what else can a click do? The click could move something or start an animation or do anything.

If that's all you ever learned in JavaScript, you still have become more powerful in your front-end abilities. That's my advice, Chris Bishop. Good luck. What do you think, Dave?


Dave: You know, old-school, call me old-school. Read a book. Mat Marquis "Wilto" has JavaScript for Web Designers.

Chris: There you go.

Dave: Literally, your question is right here. Mat is good. He works with a lot of designers. He understands the problems that they have and good at communicating this. I think that would be a great way to get your head around because sometimes it depends on your blocker.

Is it the syntax that's confusing, like the curly braces and when to put parentheses? Is it the events? Is that what's confusing? Okay. Is it, what can I do with JavaScript? Is it, how do I make an if statement do what I want it to do?

There are a lot of, I guess, sliders of difficulty, different barriers that you have to get over to feel comfortable in JavaScript. Eventually, you have an ah-ha moment and start to feel pretty powerful in the language. I think all of us have had that because you're like, "This is so dumb and weird and it just yells at me all the time. But, hey, this is -- now I'm writing. This feels good."

There are other things you can do. Open up console and type stuff in console because it actually has a really good -- that's in your Web browser, Web inspector tools. Console will kind of tell you, as you type, whether or not it's going to return something. That's a great way to figure out what it's doing.

Now if you want to do big, complex, big things, I'm a big fan of taking a course, like a video course. I know that's not for everybody, but Wes Bos has some good intro to JavaScript, JavaScript for Beginners course, I think is what his course is called. But then he also has like 30 Days of JavaScript, I think. It gives you 30 little games or tutorials to go through. You could hone your skills over a month. I think that -- what's that called? -- repetitive learning or intentional learning goes a long way, so I would recommend that.

JavaScript for Web Designers by Mat Marquis and Beginner JavaScript by Wes Bos would be my big point places.

Chris: Nice. I totally agree with that stuff. It's also kind of unavoidable, so I'd get on it - really. JavaScript just gets bigger every single day. It doesn't show any sign of slowing down. Unfortunately, that, like, "Hey, where are all the jobs for just us HTML and CSS people?" I can't change the industry. Those jobs might be going away, even though that sucks. Having some accessibility expertise and all that stuff in just those languages feels like you should be able to get a job in it, but I never hear good news on that front. I only hear people who have that skill set and are looking and are disappointed, so it might just be where we are right now. Sorry about that, but certainly, that JavaScript skill is going to open up some doors for you that may have been closed otherwise.

Dave: Yeah.


Chris: Let's see. Matt Ondo, a long-time listener, first-time caller. That could be you out there, people, too. Feel free to use the form. Just go to Click that "Ask" button - or whatever it says. "Send in a Question," I think.

"I'd love to hear your thoughts on managing like 30+ WordPress sites. Are there tools you have used, that you currently use, that you might recommend? I lead the Web dev team for a large company. Our team acts like an internal agency servicing all 50 companies we have acquired and are constantly adding to.

We currently use ManageWP with WP Engine as a host, and we like the ManageWP tool, but we don't love it. For some reason, it isn't quite fit, as we're adding one to three sites per month to our list of them."

Woo! That's a lot. I don't think I've ever done that, Matt. So, anything I tell you might be a grain of salt with you living that life and I am not. So, I don't quite know what to tell you, but it certainly processes your game now.

Dave: Mm-hmm.

Chris: One to three sites a month, that's all you should care about. What you shouldn't be doing is just be like, "I don't know. Throw it on the stack. We'll just manually update it - or whatever we were doing before," because it just doesn't scale as well. Every choice you make has to be able scale and adding things that are scalable, too.

One of the things I think about -- this might be the world's hardest transition -- is just the fact that WordPress has that multisite tool.

Dave: Mm-hmm.

Chris: You can have one installation of WordPress and share it across multiple sites. [Laughter] I have no idea what transitioning 50 sites that are not on it too might look like. But what's kind of cool then is let's say you use Jetpack or something. It's just one plugin.

Dave: Mm-hmm.

Chris: You just install it once. You update it once and all 50 sites can share it. Doesn't that sound amazing? I mean I would think one of your problems is just keeping all the damn plugins updated across all of them. Just that problem alone, what are you doing about it?

Jetpack solves that or can help, but I would think even WP Engine can help with that. Right? They probably have some internal product that you switch the "keep my plugins up to date" button switched and it'll just do that.

But what else are the problems that you face? What else is it? Is it hard to design them? Is the build process different for all of them? What are your pain points? That is not in this question, so I don't know what your pain points are. What would you guess their pain points are, Dave?


Dave: Well, my immediate thought is they have made some smart decisions already. They have a tool in place to help them manage.

Chris: Yeah.

Dave: Using WP Engine is really smart because you don't want to be managing 50 Bluehosts in addition to [laughter] 50 websites. You know what I mean?

Chris: No. Yeah, WP Engine seems a little happy path there, right?

Dave: Yeah. Lower overhead wherever possible and I think, like what you were saying about plugins, I was nodding along here (in audio format).

Chris: Mm-hmm.

Dave: You've got to reduce the surface area of maintenance.

Chris: Mm-hmm.

Dave: That's what's going to kill you. Or you need to, staffing-wise, come up with how you're going to zone offense or zone defense these 50 sites. Is it each developer is responsible for like seven sites or something? What's a manageable number that they could service?

Chris: Mm-hmm.

Dave: If this was Amazon, you would have a full seven-member team per site, like a full two-pizza team per service, install that you're servicing. That's probably not how you make -- Amazon bucks and your bucks are probably different, but you need to reduce the surface area, I think.

WordPress can be hard because it's oftentimes like, "Hey, I need you to build me this WordPress site. But we use this one weird plugin that's not maintained anymore, and that handles all of our payment." You're like, "Ahh!"

Chris: Yeah, that's a big deal, right? Matt, you allude to the fact that you're buying companies, so those companies, before they're bought, have no incentive to do things how you're doing them.

Dave: Yeah.

Chris: They probably do things in all sorts of weird ways. If you bought CodePen, you'd inherit our WordPress blog and it definitely has some weird plugins in there. We had one that I just installed a few months ago that's one of those that has this "Was this helpful?" kind of thumbs up, thumbs down thing for the docs.

Dave: Yeah. Yeah.

Chris: That's not -- [Laughter] It has like eight stars or something. You know?

Dave: [Laughter]

Chris: It's not like some massive thing that you definitely have experience with. You've probably never dealt with it in your life.

Dave: Yeah.

Chris: Is that okay now? Whose job is that now? It sounds like it's your job. [Laughter]

Dave: Yeah.

Chris: So... Sorry.

Dave: I think you're going to have to do surface area. You'll probably have to -- maybe as you go through every single site, document all the plugins the best you can. You're going to have to do it quick because people add plugins overnight. [Laughter]

Chris: Yeah.

Dave: You document all the plugins, and then you maybe rate and grade. Then you give each site a grade. Maybe that helps your staffing decisions. Like, "Oh, this site is a C. This site is a D."

For maintenance and stuff like that, you probably -- I don't know if there's a tool, really. We use -- it's part of the WP Engine family now -- Local by Flywheel.

Chris: Yeah, for the host as well. Local is the local thing, but Flywheel is just the hosting, and they are owned by WP Engine.

Dave: I would say that's actually a banger experience for us just because I just use it for ShopTalk's site, but you can pull that up and spin up your site locally on a MAMP thing. You're not using system MAMP and maintaining 50 sites there. It's all synced through your Local by Flywheel setup.

Chris: Yeah.

Dave: Something like that might be helpful, too, like how you're developing locally. Make that fast. But you're going to have to attack, like lower the surface area for pain.


Chris: Yeah, that's what I'm curious about. What's the worst thing so far?

My guess would be that "Oh, this site uses--" some sites are just easy. They just use some off-the-shelf theme and they mostly made it work, right?

Dave: Mm-hmm.

Chris: But some sites are super bespoke, and they're like, "Oh, this site has a local Webpack build," and so now nobody knows how it operates anymore. But you can't just rip it out.

Dave: Mm-hmm.

Chris: You know? Just changing a blue to a red, you've got to NPM install, NPM run start, then change it, and that'll process the CSS, which has this thing. But then one dude remembered that "Oh, but you actually have to go into header.php and change the cache on the end of the string because there's no cache thing in place," or something. There are all these little bespoke processes. I would think that's the pain point.

It's not so much a plugin because if plugin updates are your biggest problem, then get it on Flywheel and turn on their auto-plugin updating feature. Then call it a day. That's exactly the kind of problem that needs a solution - or something. But it can be solved with money and buttons.

Dave: Right.

Chris: The other problems that require human being intervention are not so easy.

Dave: Yeah. Yeah, I think turning on auto-updates would actually be cool because maybe that's easier than update. [Laughter] Maybe handling breakages is easier than handling or the maintenance cost is the same or less than going through every site and clicking "update." Maybe it is.

Chris: All right. Best of luck there. Let us know what you do. It seems like there are options. I like Dave's audit idea a lot. Find the sites that are the most important and the ones that are in the worst shape and look at the Venn diagram of that. If you have important ones in bad shape, then deal with those first.

Dave: Yeah. I think that'd help your situation.


[Banjo music starts]

Chris: This episode of ShopTalk Show is brought to you in part by Netlify. You know it's no secret, I guess, that the folks behind Netlify kind of coined the word "Jamstack" and there's just so much talking and thinking about what Jamstack even means lately, which I really appreciate.

What's the nuance behind it? Is there a point where Jamstack almost means that it's not totally static, that you're leaning into the interactive and data-backed possibilities of Jamstack if it's totally static? Maybe that's even a different word. I'm not sure if that's the case, but I like that it's leaning into this idea, which it kind of meant from day one, that Jamstack does not mean that it's just like this blog that doesn't even have a comment form because - I don't know - that would take a database. And we can't do that on Jamstack.

No way! Jamstack says this is statically rendered, but there are all kinds of interactive stuff just like any other website has, but we're just leaning into the fact that it's prebuilt. There's still JavaScript functionality involved. I can still hit APIs, grab data from database, put data in databases, and do anything dynamic that any other website could do.

If there was only some kind of conference we could talk about all this at. Oh, wait! Jamstack Conf is coming up October 6th and 7th. This show that you're listening to drops on October 4th. So, as you're listening to this, you have to act fast!

Fortunately, you don't have to ask your boss if you can have the money for it because, uh, it's free. Just go to and register. You get a badge. You can come to the show. Hear all about Jamstack. Excellent speakers, as always.

I've gone to this every single year, I think, that they've had it. It's pretty great. Thanks for the support Netlify. Bye-bye.

[Banjo music stops]


Dave: James Edward writes in, "You are using Node." This is some context. "You are using Node and need to make a network request at a given time. This time is stored in the database with some additional metadata. You query the database for all the records that are scheduled to time with the scheduled time matching the current time (say by minute or hourly), and then you execute the network request on the related metadata. The result of the network requests are then stored in another database table. There are some cron related packages and they all seem equally popular without one that stands out among the rest. What is the most common or best available method to schedule high latency task jobs with Node.js?"

Chris: Talk about questions I'm unqualified to answer.


Chris: I'm imagining a cron-based Lambda. The Lambda calls the database. It figures out if it needs to make other requests and does them, but it'll need to be slightly long-running because you don't have any idea how long the first query or the second job is done. It seems like one of those 15 minute kind of Lambdas. [Laughter]

Dave: Mm-hmm.

Chris: If you can. But what else? Is he saying, "How do you run a Lambda on a cron?" He doesn't even say Lambdas. What if it's just a regular old server and you just run crons on it? Do you trust crons these days? Is there the best cron thing in the world?

Dave: [Laughter] Best cron. Go!

Chris: Yeah. [Laughter]

Dave: What is the best cron? You know I can say, from experience, we are doing this currently on an app using Agenda, which is a Mongo backend.

Chris: Oh, really?

Dave: That might again sort of be a deal-breaker. It basically does what you say. It goes through and it grabs. Then it pulls in and spits out. I'm looking at the agenda--

Chris: What kind of server do you run it on?

Dave: DigitalOcean, like in a container.

Chris: So, it's just on all the time? It's an always-on beast?

Dave: It's an always-on thing, but the thing is, a cron job hits it and says, "Okay. Now go process this every week."

Chris: Oh. It doesn't run its own cron? It runs -- something else runs the cron?

Dave: Maybe it has its own cron or it can schedule a cron and it runs periodically. But the cool thing is it's in the database when it should run, like, "Go run this task at this time. It's in the Mongo." And so, if it falls over or something breaks--

Chris: Yeah.

Dave: There's still a list of when jobs need to run. Does that make sense?

Chris: Oh...


Dave: It's always saying, "Hey, this is when this job is supposed to run," so you could sort of pop in and see if stuff broke.

The problem we're having is the Mongo ran, the thing ran, but the server ran out of memory or something and didn't complete all the jobs, and so we're kind of in this weird, like, "Ah, crud. That broke on us. A real bad state."

That's where Redis is kind of cool. Redis is limited by memory, like literal RAM, I think. Redis is kind of the standard job doer, so you may need another database, not just your relational database, to do that. That might be something.

I'm looking at the agenda docs and they're talking about Bull and Bree.

Chris: Hmm. What are they?

Dave: Bull is maybe another package for sort of, I guess, crunching through your Redis, like a bull plowing through the Redis queues.


Dave: Redis is just sort of a task store that says its key values.

Chris: I like the queueing idea here because if something goes wrong, that is kind of a problem that job didn't get done and it was probably kind of important.

Dave: Yeah.

Chris: Yeah.

Dave: Yeah.

Chris: Well, that's interesting because, yeah, I'm thinking of a couple of things all of a sudden. One is, I just had my host, Flywheel -- we just got done talking about them -- emailed me and said, "Oh, there's a bunch of jobs," because WordPress has jobs that it needs to run on a cron, too, that aren't timed necessarily. They're just like, "This is a thing that needs to get done," and it doesn't--

It's PHP, so it's not always on, right? PHP just responds to requests. You know? It's just different in that way.

What it does is it has this queue of stuff that it needs to do. Then when a request comes in and starts executing PHP, it can check that and see if stuff should run now.

Dave: Mm-hmm.

Chris: Anyway, that queue can get backed up. It can be like, "Why aren't these going?"

Another thing you can do -- this is easy on WordPress -- is just to turn it off. Just be like, "Don't do that," because I think it can slow down a request that's probably a user requesting your website or something.

Dave: Hmm.

Chris: They're like, "We turned it off on purpose for some X, Y, and Z reason. What we recommend is that you set up an external cron that hits your website /wp-cron-php," which is a route that you hit that runs these cron jobs on a schedule.

They're like, "Just go to You can sign up for it, set up URLs to hit at certain times, and it'll just do it." I was like, "Oh, that sounds vaguely familiar," so I log into this website and, sure enough, I've had this set up for like three years--

Dave: [Laughter]

Chris: --doing this cron job flushing on CSS-Tricks, and it had stopped working for some reason that we tracked down that wasn't the fault of this website. It was the fault of something else. Not that I'd rely on this for anything super mission-critical because it seems like just a little whim of a website.

Dave: Mm-hmm.

Chris: In fact, I just donated them ten euro bucks because I've been using them for three years and didn't even realize they don't even a paid plan. They just help me out forever. It's pretty incredible that they do that. Anyway, that's something.


Dave: Yeah. That's a great one. We just saw -- we were talking in the D-d-d-d-discord about Renderer. It's a hosting platform. They kind of have a cron job feature built in, so that's kind of cool.

Chris: Yeah. Nice.

Dave: It might be worth your while if hosting is an option.

Chris: Speaking of the Discord, remember we were talking about that Netlify on-demand workers and stuff. Alex Revere made that one that needs to clear the Netlify builder cache every day so that it will pull again from CSS-Tricks and build his funny little website.

Dave: Mm-hmm.

Chris: I think we ended up -- didn't we use--? I don't know what he used. I can't remember. I think he wrote something in GitHub actions, actually, which can do cron stuff, too. Then I used either Zapier or if this then that. Both of them have little cron abilities, too. I used one and mine reruns the Astro build process, which is another way of clearing the cache, essentially. Yeah, so many cron things. Woo! But this is....

Dave: It's really cool. Yeah.

Chris: But this adds extra complexity, though, because it's not just run the cron. It's run the cron. Then run a query to get the information about other things that you need to run. It's like a double trouble.

Dave: Yeah, you need a scheduling apparatus. This Bull one looks good. There was Sidekick and Ruby, which is really good, but I don't know. I'm giving you terms to Google here. [Laughter]

Chris: Yeah.

Dave: Sidekick for Rails might be kind of cool. I would love to see a Postgres version, if I could find one, just because that's what I have. It can run at midnight on Sunday night. It doesn't have to run -- my jobs don't have to run every day at a certain time when there might be high use or something like that.

Chris: Yeah. This Bull looks pretty sweet. Node in Redis.

Dave: Yeah. It just kind of looks like you just set a queue and then you say, "Process that." Then you just go through it. Just plow.

Chris: Yeah. There are many things that can fail in James' scenario. It's like the cron could fail and the network request that the cron is requesting could fail. They should probably queue them both.

Dave: Mm-hmm.


Chris: All right. Let's see. Let's see. Ben Buford, "When doing speed audits, Google Lighthouse often complains about the lack of width and height attributes on images when using loading lazy. It's confusing as often our images will be responsive, so won't the width and height stop that? What's the right methods for this and are there any lazy loading strategies for background images in CSS?"

Let's wait for the background images one and think about the width and height thing. That's just kind of outdated knowledge, actually, Ben. You should put width and height attributes on all your images all the time no matter what. Literally, all of them and be truthful when you put them on there. Have them match.

I think the right thing to do is have them match the actual size of the image, like the actual pixel width and height. Because what's going to do is, before the image loads, yes, it reserves space for the thing and you think, "Oh, well, that ruins the responsiveness." No, it doesn't. Not anymore. Browsers are smart enough to make that the aspect ratio for them, which is fantastic.

Dave: Mm-hmm.

Chris: Just put width and height on all images and, for all time, Jen Simmons made that happen, basically. I'm sure there were lots of people involved in the end, but I think, without her, it never would have happened. If that's your one legacy, which it won't be -- you'll have many legacies, Jen -- that one is flipping huge. It's reduced a ton of jank across the entire Web. Wow!

Do that and use loading lazy, unless it's the hero image. Did you have something to say?

Dave: Oh, no. I think that's it. Width and height are back, baby.

Chris: [Laughter] They're back. Yes!

Dave: You've got to use them, baby. They're back, baby!

Chris: Lazy loading in CSS, I'm not so sure. If you can lazy load the whole element, that's cool, I guess. I'm thinking of that Harry Roberts tweet recently where he had this great analogy where he says, "CSS doesn't request an image. If you put background image URL blah in your CSS, it doesn't guarantee that that image is going to load. It only does if there's a matching DOM element and then it will load." That's where my brain goes, a little bit.

Dave: Mm-hmm.

Chris: If you lazy load the element itself (which, that's complicated), it won't request the CSS, but at least that's a lead. That might be one possible way to do it. How do you think it works now, Dave? If an image is clearly 20,000 pixels down a page and not there, but the DOM element is there, it does get loaded right away, doesn't it?

Dave: I think it does because I think it's probably eager, set to eager by default, like, "I'm going to need that anyway and loading images takes time, so I'm going to go get it."

Chris: Yeah. I doubt it blocks anything, though. It's probably relatively safe.

Dave: That's what I wanted to say. "Isn't it lazy already?" was sort of what I was thinking. It's not lazy in, like, only when it's in the viewport sense, but I think it's lazy in the browser is going to go ahead and try to do everything else while it's fetching this image and painting it.

Chris: Right. Background images don't affect -- they affect paint, but not layout. So, they're probably fairly safe that way.

Dave: Yeah.

Chris: Sure. Would it be cool to have a CSS thingy that says, "Don't load this until it's in the viewport"? I mean maybe. Yes, it would, I guess.


Dave: Yeah, well, it's interesting because this point alone is why putting images, like content images, in your content is kind of really cool because you can load this. If you're using background images for big blocks of styled heroes and stuff like that, I've done that in the past, for sure. That's not a best practice. Now the browser can kind of determine this, so I think using the image tag.

Chris: Mm-hmm.

Dave: I'll give you some riffs. You could create an attribute or something, or data source or something, data BG or something, and then use an on don content loaded - or something - and then query selector all data BG and set a variable, a CSS custom property that sets the background image. Then it'll shoot that in later, so that's something - a trick you could do, I guess.

Chris: Yeah. I was going to do this on a video with you. It's one I jotted down. I saw this website. It was pretty beautiful, and this is really simple. They just had some video images and, as you scroll down to them, it would play them once and then stop.

I was like, "I wonder how they did it." We could look into it and investigate it, but it made me think it's almost certainly intersection observer, and it's probably about the simplest thing you can do with intersection observer, I think.

Dave: Yeah.

Chris: Although, that API--

Dave: Mm-hmm.

Chris: Sometimes I use it, and I'm like, "That was great. What a great API to exist. Off the main thread. Love it." But somehow, when I go to use it, I can never just immediately remember how it works. I feel like it's a little weird. But I think that could be an answer here, too.

Dave: Yeah.

Chris: Like Dave was saying, you could put an intersection observer on for the element and only when it's visible then set a class or adjust the custom property or whatever. But then it's like you're learning JavaScript too.

Dave: Triggering the background, yeah.

Chris: There's some cost to that that's maybe comparable to the cost of loading the image.

Dave: Yeah. That would be an issue. I'm also in the "let the browser do what the browser wants to" sometimes. We're actually in a good place where we're finally getting some control because if your big images in your page can be lazy-loaded, then your background images can kind of be in that middle ground where they're fetched but not blocking. That's a good place. That's a good place to be because the worst place would be your background image is blocked by the 700 other images you put on the page. [Laughter] That would be the worst-case scenario.


Chris: All right. Let's see. What do you got? Is there another one here?

Dave: Hey, we got one more by anon-anon. "How do you deal with landing hub pages and sub-pages? You have a page called services, but a bunch of services are listed under /services accessible from a dropdown menu. Should you be able to click on the landing page too? If not, what would find if you typed /services into the address bar?"

Chris: Yeah. I'm kind of old school. Yeah, like a dropdown menu.

Dave: Yeah, in the directory. Do you need an index.html in every director of your page, I guess?

Chris: Yeah. Well, because it's /servicesfoo, /servicesbar. What's at /services? Well, you did that to yourself. [Laughter] If you didn't want to have /services, then don't have /services. You could have it /service-foo, -bar, and then there is no parent page to them and you don't need the index page. But I think it's cleaner to have that. I think, if you have ten services and you put them at /services (/ the name of the service) that just makes a certain kind of logical sense and it's not the most difficult thing in the world to design a page for services that list all ten of them.

Then people can URL hack it and whatever. That means your dropdown menu, yes, you can click on the top-level thing, too, and it takes you to that intermediary page. I'm a little old school in that regard. That's a perfectly fine thing to do.

Dave: I'm into it. There is not cost, really, in spinning up that service page. Maybe there's a maintenance cost, but you can literally just put a list of links in a grid, like a CSS Grid. Done. You've made the page. That's all it does it link out to things.

Put the little icons in there for the products or put a little screenshot or something. I think your users will thank you. You could even beta test or track it or something.

"We've made that services page. It's totally not linked. How many people visit it?" I would be curious to know that.

Chris: That's true.

Dave: If it's in your site map, you're probably getting some free SEO juice.

Chris: Because you've got good content on that page. Yeah.

Dave: I would probably do that just for the SEO. I would do it for the users. I would do it for -- there's even a progressive enhancement hack where your services link, you just assume that button, the dropdown never works, and so the JavaScript failed and the dropdown would never work. You could just go to the /services or, if you're using details, which you know it'll kind of work - or something - you open it. Maybe you have an all services thing.

In my experience, clients name services really poorly and it's not very clear what their offerings are. You might just want to say, "Show me them all," and figure out from there.

Chris: There might be some advantaged you didn't even think about in there. Even if you're like, "I don't think that's useful," well, too bad. Somebody else might. [Laughter]

Dave: Right. Right? Anyway. I think it's useful, and so I think you should do it. Just do it.

Chris: I remember when doing the CodePen challenges area, you can go to, click "Challenges," and it goes to /challenges. Then you can go to a particular month. If you click on the title of that month, it goes to / the month. It's not just the month. It goes to the year and the month. Then, each month, there's a week, so you can click onto the week.

It's /challenges, /2021, /September, /4 is when we're recording this. When you listen to it, we'll be in October and it'll say /October/1.

We intentionally decided on that URL structure because there's, in a sense, index pages all the way down. If you go to just /2021/September, you'll see all four weeks of that challenge. If you go to /2021, you'll see all of the challenges that happened in 2021.

Dave: Yeah.

Chris: I just discovered a bug while we're doing it that we don't actually link ever to just the year just because, in the UI, there's just not a good reason to link to a whole year of challenges. If you go to /challenges/2020, it loads, but it loads the 2021 challenges.

There is some maintenance cost to it, I suppose, but not too much.

Dave: Can you do me a favor? Can you look in your analytics right now and see how many hits to 2020 there have been? If you don't want to share that, I'd be curious.

Chris: I'd share it to you in a minute, but you know what we can't afford is analytics.

Dave: Okay.

Chris: Isn't that crazy? It's not totally true. I've been looking at some options. But I cannot just grab Google Analytics -- not that I would anyway because there's GDPR concerns and whatever that CodePen is at the scale where you have to really consider crap like that. It's so much traffic, so far beyond into enterprise.

Dave: No, Chris.

Chris: Yes.

Dave: Get rid of Europe. Get rid of Europe. Screw Europe. Screw California. Don't even worry about them.

Chris: Check the user ID of the user logged in and only load it for one out of 100 sessions or whatever and then just know that you need to times 100 everything you see in there, but just been annoyed by that. Then we have--

You know what? I could look at Cloudflare because we have that in front of it. But they only give you 30 days.

Dave: Well, in the last 30 days, how many people went to challenges 2021, I guess, then?

Chris: The problem is, we don't ever link to that. But that's your point, right?

Dave: Yeah. A non-linked page, does it get traffic at all?

Chris: [Laughter]

Dave: I have at least one hit right now.

Chris: Yeah, that's a good point. Another concern, though, is that that area of the site uses React Router.

Dave: Okay, so it might be biffed.

Chris: Yeah.

Dave: Yeah, yeah.

Chris: Cloudflare might not see it as a network request that came through because it just got browser-ified.

Dave: Mm-hmm. Mm-hmm.

Chris: Anyway, do you want to do any sort of wrap-up things?

Dave: Yeah, we'll wrap it up. Anyway, I think, do the services page, and do yourself a favor. I think it'd work.

Chris: I agree.

Dave: Speaking of favors -- hey, like and subscribe over on the YouTube. We could use it. We have a new YouTube over on, I believe it was. Chris and I have been doing thrice-weekly video uploads. We think you'll like it.

Follow us on Twitter, @ShopTalkShow, for 16 tweets a month. Thank you for downloading this in your podcatcher. We're still doing the audio podcast, of course, so like, heart, favorite it up. Join us on the Discord, It's a lot of fun.

Chris, do you have anything else you'd like to say?

Chris: Um...