Follow up on Chris' desk adventures, trying to work out Auth and OAuth with Nuxt, fixing a fixed header issue, more on Apple's M1 processor, and a blog post reading of the Truths about Digital Accessibility.
Time Jump Links
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 rainy day, kids are in the house getting kind of wild, buck wild here, over here--Rupert and with me is Chris--in the new booth--Coyier. Chris, it looks just like the same old booth but in the new office, huh?
Chris Coyier: Oh, yeah. Very happy here in the new office. Pretty cool. The same exact setup. It doesn't feel any different at all, except for when I'm at my desk. That feels very different. I'm very excited about that. As you've been following here on ShopTalk Show, getting my cables organized, getting my Zoom background looking good, all that stuff.
Dave: Yeah. I saw that. I appreciated you tweaked the desk a little, the angle, and we're seeing some beam. We're seeing some brick. We're seeing some wall.
Chris: Yeah, some fake plant I got back there. I put up an art.
Dave: Oh, yeah. A Sarah Drasner original, right?
Chris: I've had that for years. It's been up somewhere, but it's a bold move when you choose the art to go behind you in the Zoom.
Dave: Mm-hmm. Yeah. Sarah, I think she told us when she was on, but she's got a history of doing medical art, right?
Chris: Illustration, yeah.
Dave: illustration. Oh, man, that's great.
Chris: Definitely the world's most interesting person.
Dave: Yeah, hands down.
Chris: I have this other friend, too, not as interesting as Sarah, but do you ever have a friend like that where, every time you talk to them, they reveal some fact about themselves and you're like, "What?!"?
Dave: Yeah. Yeah.
Dave: Well, I was a paleontologist for eight years.
Dave: You're like, "What?!"
Chris: She's like, "I was the shoe shopper for the Queen of France," or something. You're like, "What the--?! What?!"
Dave: [Laughter] No, those people are very interesting.
Chris: Yeah, and then I look at your life now. I'm like, "What's going to happen to you next?" because unless we've ruined you somehow in Bend, Oregon, every year of your life has been a fantastic voyage.
Dave: [Laughter] "So, how did you end up here talking to me?" Yeah, I lead a pretty interesting life, Chris. The other day, I arranged rocks in my yard by the size.
Chris: Did you?
Dave: It was cool.
Chris: [Laughter] Yeah?
Dave: The previous owners apparently filled a koi pond with limestone rocks, as you do, and covered it with English ivy. I got rid of the ivy, found the rocks, said, "Oh, no!" So, I started removing the rocks, organizing them by size. There's the small rocks, the medium rocks--
Chris: For esthetics?
Dave: Just for organizing because they were in one big pile and I was like, "That's a big pile of rocks. I have no idea what to do with a big pile of rocks." But now I have little piles of rocks organized by size and I can do something with them. I've been watching a lot of videos on the Internet. [Laughter]
Now, they're organized by size and it's all great. I love the piles of rocks organized by size. But I believe I have broken my buttocks. I believe those don't work anymore and they won't work for the rest of 2020. I have injured my posterior.
Chris: Oh, they're not just sore like good sore? They're like, "Ouch. Whoops," sore?
Dave: No, they're good sore. It's good sore, but it's also like, oh, wow. There you go. It hurts sitting up and getting up and down is difficult. That's interesting. That's a new feeling.
Chris: Oh, okay. You know, there's some interesting stuff. I'm always interested in what you have to say. You threw out a phrase that I'm now going to remember for the rest of my life called nerd sniped.
Chris: Nerd sniped has this particular definition which means that somebody brings up some technological conundrum that they're in. Then instead of doing what you're supposed to do or anything useful for yourself that day, you just become totally consumed by their problem. [Laughter]
Dave: Yeah. No, that happened to me this morning in a little chatroom with Scott Jehl. He's like, "Did anyone know about Nuxt and OAuth?" I'm like, I know those two words, so let me help you, Scott.
Dave: Meanwhile, the blog post I'm writing, the company I'm working on, yeah, those things don't happen. I'm helping Scott.
Then it's even like I only -- I was like, you know, I think I've not helped him enough, so let's see here. I'll just watch some videos on this topic that Scott is also probably watching the same videos, or whatever, but now I'm just watching videos about Nuxt, OAuth, and stuff like that. I'm just like, oh, man. Why did I--? Okay. Okay. Yeah, I'll just keep helping Scott for the rest of the day, I guess, because now it's in my head. Now it's like, how do you do this particular way of OAuth or whatever with Nuxt?
Chris: Yeah. Nuxt has helpers, right?
Dave: The whole thing about Nuxt is it's built for application development.
Dave: Vue, React, or whatever, they're just kind of the view layer.
Chris: Isn't its origin documentation sites? But that's not true anymore?
Dave: That would be more like Gridsome and stuff like that.
Dave: What's the other one? VuePress is another one.
Chris: Oh, VuePress. That's probably what I'm thinking of.
Dave: I could be totally wrong, but it feels like there's some maybe lessons learned from VuePress is now in Nuxt or something. I could be totally wrong on that.
Chris: Nuxt, what's the primary goal with Nuxt, though, is the SSR, right?
Dave: Server-side render because you can do that with Vue, but it takes a few Webpack jumps. It's kind of designed for application-specific tasks. Then they have a whole suite of first-party modules like Auth and PWAs, like an Axios integration and stuff like that.
Chris: But it makes more sense to build that as a Nuxt plugin because Nuxt has the routing and stuff. If you're going to build OAuth, isn't OAuth -- just Auth, period. OAuth is very specific technology, right?
Dave: Yeah. Yeah.
Chris: We should be clear about that because we have a question about OAuth here we should do too. Let's try to weave that in. You might say, like, well, /account is a protected route.
Chris: You need to be auth to see that.
Dave: Yeah. Yeah, and so you would want a root level state, like a store for your user. If dollar user or something like that, you'd want to protect that route. Right?
Chris: Yeah. Isn't it funny, the conversations we're having now, that this front-end show, we're talking about protected routes and auth data and stuff?
Chris: Think about it. It's just weird.
Dave: Hi. Welcome to Web design.
Dave: Let's get into your 80,000-line Webpack config. You know? [Laughter]
Chris: Yeah. [Laughter]
Dave: Then, yeah, we're having to talk about protected routes and stuff like that. It's hard. State is brutal, but that's what the front-end does. Logged in or logged out. If this then that. We do a lot of that.
Chris: When we're saying OAuth, OAuth is like Auth that is like auth from some other website and not the one that you're currently on, right? In a way, it's not. If you were going to build a login and password system and you're going to keep those passwords salted, hashed, and whatever, and all securely, do it all the right way, but keep the information in your own database, you'd just call that Auth.
Dave: I think that would just be authentication.
Dave: But if you then expose that, like you allow people to log in with your CodePen ID or something like that--
Dave: --like with, say, Dave's new startup. You can log in with CodePen, right?
Chris: That's OAuth.
Dave: That's OAuth, yeah, and then you log into my site and you get a list of your CodePens or whatever. That's open authentication, OAuth. You have to--
Chris: If you ever see one of those buttons -- log in with Google, log in with Facebook, log in with Twitter, log in with GitHub, log in with CodePen (we don't actually offer that, but let's say we did) -- those are all under the umbrella of OAuth. Yeah?
Dave: Yeah, and it's like a protocol, basically. If you send your credentials, I'm going to give you this back. It's just like a contract.
Dave: I'll give you a bearer token, is usually kind of what it is. Or I'll give you a token back.
Chris: Token. Is JSON Web tokens involved here?
Dave: JWTs would be another thing you could get back, but it's basically like I'm going to send credentials to CodePen or website A and website A will say yes or now, that is this person. Does that make sense? Then it will send me something back to verify that identity going forward, whether it's a token. Usually, it's just a token, a JWT or a bearer token and the response, or something like that.
There's usually a little handshake, like, "Hey, I would like to log in with these credentials." Then it sends it back and it says, "Okay. Are you sure you'd like to log in?"
Dave: Your site says, "Yes, I really would like to login and I'll even give you this key," or whatever. Then they say, "Okay, well, then thank you for identifying yourself. Here's this key back."
Chris: Yeah, so why does Nuxt care at all? It doesn't, right? It's just like helpers and stuff.
Dave: Yeah, it doesn't care, but that's a very common task that you do in Web development, especially client-side Web development. You have to get a login with Twitter or Facebook could be the most common one, or Google now. That's kind of popular.
You log in with Google and you send the keys back. Now, I have your identity. Now, when you perform a user action like save a new post, I know it's you. You're not communicating with my user database. I guess you could be, but you're not specifically communicating to my user database. I'm using your Google cookie token. I'm using your Google token to authenticate you, to identify who you are.
Chris: Yeah. Right. Okay. Okay, so I think we covered that pretty good.
I don't know what Scott's actual problem was or what yours is or what you're looking into, but here's a setup from a reader or a listener, I guess, Andrew Smith. A lot of Andrew Smiths out there. I wonder if I know this Andrew Smith or if it's different. Anyway--
Dave: The inventor of modern economics?
Chris: Rachel Smith's husband? Oh, that's Andy Smith.
"While I think this is great," speaking about OAuth and that Andrew reaches for these logins when he builds his own stuff, "my question is," he's working on some bigger app now, "how do I handle the data? My app needs its own set of users. But with OAuth, now there are three or four different ways to sign up, including my own signup form. I need to ensure that the user really did login through another site and manage the access, tokens, and that kind of stuff."
I looked at a lot of the best practices, but I think Andrew is just getting confused. I agree that's a little confusing, too. I can tell you how I think about it, at least, even though I'm not necessarily in charge of this on the sites that I build. I know that the way that it works on CodePen is entirely email-based so that your account on CodePen is like, if you log into CodePen, you are definitely in our database.
Chris: You don't have to use social auth. We think of them just as convenience methods to login but there's no version of logging into CodePen where we just don't even keep you in our database. You're just this ethereal, randomly logged in user to our app. No, no, no, if you click that login button.
Another weird little trick here--I find this common on both sites--is those login buttons, they're the same for signup and login. They literally go through the same routes and do the same stuff.
Chris: If you've clicked that button, first it's going to go do the OAuth little flow. Then it's going to come back in. In our case, we get an email address. We make those OAuth providers give us an email address. Then we're like, "Okay. We got this email address. Now we're going to match it with our own record in the database and see who you are. If there's a match, then you're logged in. Congratulations."
Meaning, we don't really need that token, but you don't have to go authorize or deauthorize apps within CodePen itself. We don't care. We just get a new one every time if there's a match with the email.
The problem, which is totally not a problem on our side, is that you can't have a different email address on CodePen and GitHub and still use GitHub login.
Chris: They have to be the same email address. That works good for us because we also require that you have an email address with us. You need it in order to reset your password or something or if we decide not to have these social logins someday, which is a right that we reserve. I don't want to have it be absolutely mandatory that you need a Twitter account to log in. That's not a safe place to be.
If you use social auth to sign up, you'll have an email address that goes into the system. You can always just set a password and use it that way anyway, or we know that you can reset your password because we have your email address and we'll send you an email in order to reset your password and authenticate right on CodePen itself. It just feels very safe so that these social logins are just a little helper.
Dave: You keep a list of your own users and then OAuth, all it does is just prove whatever: Dave Rupert showed up.
Dave: Because I clicked the GitHub button on CodePen.
Dave: Then GitHub says, "Oh, this is Dave Rupert. Here's his email address." You say, "Oh, let me look up Dave Rupert by his email address." And you say, "Oh, we got a Dave Rupert." Now, Dave Rupert is logged in.
Chris: Yeah. There are apps out there that just don't bother with their own user table in their database. They're just like, "I'm just going to OAuth it in. That OAuth will provide me with the information I know about the login ability and I just don't even bother with my own user table.
Dave: You know--
Chris: That to me feels like, whoa, scary, dangerous. But for a little baby app, maybe it's fine. You know?
Dave: Yeah. No, I mean Netlify authentication offers that. They're like, "We'll handle your users and you pay for X thousands of users," or whatever. I think Auth0 is something Andrew mentioned in his question. That's pretty standard, too. I think they can manage the user. I think it's a good place to get up and going. But, yeah, you do probably want to have a user table of some kind and then map to whatever.
I think the classic thing is an account page or something. If you want to hang data off of that, you probably need a user thing. Otherwise, you want that relational data. I don't know. However you store your data, maybe you're just chucking in a user ID or OAuth ID or something. I don't know. Then you just, on the record itself, like a JSON structure kind of thing. I don't know.
Chris: All right. Well, good luck, Andrew. I'd say, keep digging in for what you need your app to do and what feels safest for you, long term.
Dave: Yeah. I'll say, this stuff is not my favorite. It's like my Achilles heel. You get into auth and I'm just like, "Ugh! I don't want to do it." You know? Some of it is the fear of doing security wrong, but then some of it is just like, it's always clumsy because you have to do this weird handshake and stuff like that. It's just never my favorite.
Anyway, I'll confess it's not my favorite. There you go. Good luck. Pay somebody else to do it. [Laughter]
Chris: Yeah. Billing is absolutely in this category, too. It's just complicated. You know?
Chris: You know we've talked Stripe on this show a number of times. There's kind of a fundamental issue, I think, with billing that complicates the entire Internet's billing problems, at least in my mind. Maybe I'm blowing this out of the water.
One is, everybody should use Stripe. Stripe has amazing APIs. It's really good to use. You can do anything you want in there. They provide all kinds of helpers.
If you went all-in on Stripe, I think you could have a really nice, simple payment flow, have it not be terribly complicated, and just live a happy developer life. But Stripe doesn't support PayPal and an awful lot of the Internet likes to pay with PayPal. I don't know what it is. There is some safety there or something. They just like it. Yeah, okay.
Dave: Maybe it's the only thing available in your country or something.
Chris: Sure. I think, to some degree, in people's minds, and I think there's probably data to bear this out that if you don't support that that you're leaving some money on the table and who wants to do that in a Web app. So, you support it too.
Fortunately, these days, you can use Braintree, which is not as good as Stripe but is okay. It has APIs and stuff. But now you're using two sets of APIs and there's not an active record for payment APIs, really.
Chris: There's not some middleman abstraction layer.
Dave: User.didpay or something.
Chris: Yeah, you're on your own, kind of, to make that stuff.
Chris: I think it complicates the crap out of payment flows. It's just tricky.
Dave: Well, it's kind of like OAuth. You have payment strategies instead of login strategies. Then you have to know, oh, this user is paying with PayPal, so we need to make sure that they paid. Then the remote payment stuff, not like I have any better ideas, but the remote payment stuff is a bummer too because, with the charge-backs and stuff like that, did the user pay? I don't know. I guess I'll find out next month." You kind of have to hope all that data stays up to date. You have to check. It's a lot of work.
[Banjo music starts]
Chris: This episode of ShopTalk Show is brought to you in part by Mux Video. That's Mux, M-U-X, mux.com. There'll be a link in the show notes, of course. Make sure to do that because you can sign up for free and you get $20 in free credit. Anyway, you might as well do that. Very generous. Thank you.
Think of Mux for video as an API for video. If your app has anything to do with, like, your users uploading video or you needing to do that for anything video related at all, it's like an API for that. You post them, like an API request. You post them, the video that you have and, in seconds, you get back a URL that's like, "Okay. Ready to watch. Here you go."
There's not that big gap of time between, "I uploaded this video. Now we need to do a bunch of stuff to get that video ready to play." It's not like that with Mux. It's just instantly ready to go because they have this very unique just-in-time transcoding engine that's super optimized, super CDN-backed, all that. But it encodes the video to be ready to be played on any devices as like the first person is watching it. How cool is that? So clever.
It's just like the first person even isn't penalized for doing it. It just happens during that first playback, so there's just no gap. It's just like, upload the video. Okay, it's absolutely perfectly ready for everybody to watch.
You know it's based on teams. You can get started in minutes. It's free to sign up. You get your $20 if you follow the link in the show notes and all that kind of thing.
You know how beloved Stripe is for their API for payments and everything? You can kind of think of Mux that way. It's like preferred, beautiful APIs around video. That's what Mux is for video.
Thanks for the sponsorship. See ya!
[Banjo music stops]
Chris: I got nerd sniped yesterday, too, on a little thing. It wasn't like an all-dayer. I wasn't watching videos, but here's the situation. It's a classic. I've even tried to implement this in the past. I guess CSS-Tricks design tried to have it or whatever. I'm sure everyone will be familiar with this.
There is a header at the top of the website. It might be a little beefy, even. In fact, headers are just fascinating, I think, as a front-end developer. Often, the most complicated bit of code on the site CSS-wise, all the stuff that goes into it.
Now, it's got to rearrange and the different UX and blah-blah-blah. They're complicated enough and then you add this little beauty. I've scrolled down but, like, 20% now, so now I want to show this little fixed position thin-y header at the top.
Dave: Mm-hmm. Mm-hmm.
Chris: Ugh! Okay. The question is then, well, is it a separate DOM element? Is it another different header that I'm showing, or is it that same one? I think that there's a tendency to want to do the same one.
Dave: [Speaking in the voice of Foghorn Leghorn] I do declare, that is the correct answer. You never repeat an element in the DOM.
Chris: Yeah, just don't repeat it for accessibility reasons or complication reasons.
Dave: [Speaking in the voice of Foghorn Leghorn] Oh, only the best Web developers, the most certain Web developers in the whole world would only use a single header because if you ever write two headers, well, you have just -- oh, by golly, you have disgraced yourself.
Chris: [Laughter] But if you could use two headers, Mr. Dave, then it would solve a lot of problems because having to hide and show, et cetera, in the big header to shuffle everything around to get it perfect can be a huge pain in the butt. Why not just display none one and display block the other one?
Chris: Yeah, well, if you can do that, that means that that one, the little thin-y one that only hide and shows sometimes, is never in flow of the document.
Chris: That means when you hide and show it, it's not taking up or removing any existing space in the document. Whereas, that big one at the top there, that is in flow of your website and probably doesn't have a fixed type and probably is taking up flow of the website.
When you remove it, being display none, you get the infamous jank. You get the whole content of the website shifts up a little bit.
Now, believe it or not, I think, in Chrome, it all of a sudden got smart at that, Chrome and friends. It's just like, ooh, I'm not going to jank the content. I'm going to scroll lock because I know what's happening here.
Chris: But not all browsers do that, including Safari or mobile Safari.
Chris: Meaning, you're going to see that jank. Uh! It even makes me not want to trust the browser to handle this. I feel like there's got to be another, better way to handle it.
Chris: Then the question is, how do you prevent the jank? Do you apply padding-top to however big that header was, so there's no layout shift? Or can you leave it in flow? I guess that's why we need the inert attribute. You could just inert the whole thing and then it's like, just leave it there. You know?
Dave: Yep. Use something like intersection observer and be like, "Oh, did that guy pop out? Boom. That's inert."
Chris: Yeah, exactly. Intersection observe is getting better and better to use for this. I don't think it required changes. I just mean the pattern for it is pretty simple.
Dave: Yeah, well, and Safari didn't have it until last year, so. [Laughter]
Chris: Yeah. What we really need is position sticky. The cool thing about sticky is that it never suffered from jank.
Chris: If you use sticky for the header, it just was never a problem. The element kind of like leaves that space in flow and there's no jank. If you could use sticky for this instead of fixed, that kind of solves the problem, too. It's just, we don't have a stuck selector in CSS in which to do the rearrangements of the header and stuff.
Dave: Yeah. You can't change the size, really. You can't change the size because the way it subtracts from the top and the bottom, if you remove padding, and so then it gets into a fixed unstick fugue state, kind of. Yeah, but if we had a stuck selector, maybe we could do something, or remove padding origin or something. [Laughter] I don't know. Sizing origin, something like that.
Yeah, it's not. Yeah, it's not easy. Sticky is awesome, but it doesn't quite meet the product demands, in my experience. People want to show a micro nav or something. You know?
Chris: Yeah. Yep.
Dave: Yeah. Well, can I give you a weird edge case bug here? [Laughter] This is a good one that we ran into.
You're tabbing through the page, right? You're using intersection observer to stick something fixed to the top of the page, right?
Dave: But you're tabbing through the page. The first focus state is above the sticky thing but the next focus state is super way below the next one, even out of the viewport, right?
Chris: Oh, okay.
Dave: Like three viewports down.
Dave: You tab and you focus that next element.
Dave: Does the header stick or not stick?
Chris: Uh... Well, doesn't it move the scroll position?
Dave: The header never sticks because it never showed up in the position, so you never set it like, "Yes," or the sticky element never showed up in the viewport. You just jump the viewport entirely, basically.
Dave: We jumped the sticky element by tabbing so it never stuck to the top. We were just like, "Dude, what can we do?" It's weird.
Dave: Yeah, anyway--
Chris: That's kind of a -- you say it's an edge case, but it's not, really. It's possible that a sticky element is past the next viewport. That's not that edge case-y.
Dave: No. No, I mean you think like an accordion header or something like that. It could totally happen and you just tab and miss it entirely. Anyway, we hit a weird bug. It's a hash change issue, I guess.
Chris: Everybody, that's what being nerd sniped is, when somebody else's problem becomes yours just because you get sucked into it. You can even get sucked in to the point where it's starting to not feel healthy. You're not even sure if they really even need your help anymore and you're not sure if you get to the finish line that there's any satisfaction there, but you're just in so you're in.
Dave: Yeah. Yeah, I'm like eight hours into this whole OAuth thing.
Dave: I got a board with red yarn everywhere and I'm like, "Okay, I think this solves your problem, Scott. Okay. Check this out." He's like, "Oh, no. I quit that project. We're done. We wrapped it up six weeks ago."
Anyway, no, that's what it means. It's a bummer.
[Banjo music starts]
Chris: This episode of ShopTalk Show is brought to you by AWS Amplify. AWS Amplify is the fastest, easiest way to develop mobile and Web apps that scale. It gives you tools to build real-world, full-stack Web apps using their existing skillsets.
You don't have to be a super expert here about every-every-everything full-stack. No need to have deep knowledge around back-end, DevOps, scalable infrastructure, all that stuff. You can largely bring your front-end skillset to the party and have that go a long way in building apps that are absolutely huge production apps. Amplify handles all this for you.
It's available free, so you just build, build, build. Then you only pay for the back-end services that you actually use that happen to cross the free threshold.
It's also a CLI. I think that's a main important part of this. You're using the CLI to scaffold out your app to set up things that might otherwise be a little tricky on AWS. You're like, "Oh, it has all these great media storage things, and other storage, database type of stuff. What do I do with all my data?" You can use the CLI and it will walk you through setting all that stuff up, like, "I'd like to add this type of storage. I'd like to add this type of API to my app." It walks you through that, at the command line, setting it all up. That's powerful stuff.
Not to mention, it's kind of based on the JAMstack, too, so there's a deployment center for this thing. You're building your app. It kind of assumes a JAMstack architecture. But it is otherwise really unopinionated on what you do with it. You want to build a React native thing? Cool. You want to build a Vue app? Sure. Fine. You want to use a static site generator for the base of things? Yeah, go for it.
Pretty rad. Thanks for the support, Amplify.
[Banjo music stops]
Chris: [Loud groan] We talked about the ARM stuff a little bit and we were like, "There'll probably be some website or something that lists if software is ready." Since then, I've seen now three.
Chris: There are three websites.
Dave: You know, those ... are very interesting. The speed that people are reporting are incredible. But it seems like there's some Web stuff that is maybe not working right now.
Chris: They say VS Code isn't even ready, native, yet. Insider Build to run that.
Dave: I think it seems like all the Electron apps are not quite there yet.
Chris: Oh, yeah, because I guess Slack was listed in the same way, which is definitely Electron.
Dave: Slack, Notion, VS Code.
Chris: You know I brought up Local by Flywheel that we use. I was like, no way is that Electron. I heard from them and they're like, "Yeah, it's totally Electron."
Dave: Oh, really?
Chris: Wow! I did not know Electron had access to--
Dave: VMs and stuff. [Laughter]
Chris: Like install docker and, yeah, what the what?
Chris: But it does, so that's cool. That makes Electron so compelling to me. I still think Electron is cool.
Dave: Maybe this is something we don't talk about. Maybe we need to have an Electron person on.
Well, shoot. You've got some experience. I read your blog. You made a little thing.
Chris: Yeah, but I don't actually get it all the way. You know? I just hopped into somebody else's Electron codebase. I was going to hop in there today and make some updates and stuff. Once you get it going, which I don't know if that's hard or easy or what, and I don't think it's too hard, it's really normal.
Chris: It just does not feel weird, but there are some special APIs and stuff, and some special packages. One thing you might want to do, for example, is save some data, session-to-session, obviously. Right?
Dave: Mm-hmm. Yeah.
Chris: There is a nice little one from Sindre, the person who has 20 million Git repos.
Dave: Sindre Sorhus?
Dave: Of course has a bunch of great Electron ones. One of them is just like a KV kind of deal, key-value blaster.
Chris: You just use that and you just save some crap. You totally quit the app, come back to the app later, and those KVs are still sitting there for you. You can't use local storage for that. You can use local storage because it's a browser context, but it's also a node context.
Chris: That's just weird, but it's not that weird, though. You know? It feels pretty comfortable. You're just like, "Oh, yeah. I just require stuff and use it. It's fine."
Dave: No because, as a Windows user, whenever something is like, "Oh, this is Mac only," it's like, "Oh, that's a bummer. I guess I'll never see it."
Dave: But more and more, I feel like a lot of people are preferring Electron. You're working in your Web wheelhouse. You're just writing an app and then it goes out. Then you instantly have Windows, Linux, and Mac support.
Dave: You tripled your customer base.
Chris: ...technology. It's doing great, isn't it? I feel like it was a little touch and go there for a minute, a couple years ago. [Laughter]
Dave: Well, you know, built on Chrome. Chrome is kind of a notorious resource hog, but I think that's getting better. I know Microsoft has poured a lot of bucks into it.
I saw a screenshot from Microsoft on their Edge. They have a beta Edge whatchamacallit.
Dave: Edge ARM, ARM Edge.
Chris: Oh, yeah?
Dave: For Mac.
Dave: But the CPU usage was 1.8% or something, and I'm just like, "For Chrome, 1.8%?!"
Chris: Smokin'! That's great.
Dave: How?! How? Tell me more.
Dave: I don't know. That's pretty compelling. Those Macs are really compelling. I don't know if I want to switch or not, but you know.
Chris: Hmm. Yeah, we have Big Sur now, too, over on the Mac side. I uploaded this machine but not my main machine. I did a full backup. I've been using Carbon Copy Cloner, which is a nice little app that does the perfect backup thing.
Dave: Yeah. Yeah.
Chris: Such that you just plug that into the other computer and restore to it. It's like an identical copy, which is highly appealing to me. That's the kind of backup that I want, that I always thought was really weird that Mac's native backup thing can't do.
Dave: Just like a full, like, "Just clone this machine to that thing and then plug it into--"
Chris: Yeah, like clone-clone-clone-clone-clone everything.
Chris: I don't want to log back in and be like, "This app isn't authorized anymore, or this dev environment. My homebrew is all jerked up." No, no, no. Exactly, please.
Dave: Homebrew is on the list of works with caveats or something on the ARM stuff?
Chris: Ooh, is it? That sucks. Yeah, it's definitely kind of wait territory because I can't -- they put Node on the list. Node apparently does work, but I don't know. For some reason, it's on people's maybe list. Docker is definitely not happening and that makes it a total out for me until that's going.
Dave: Yeah, even parallels, virtualization of any kind, if you do that.
Dave: A lot of podcasting software, like Audio Hijack and stuff like that, wasn't available.
Chris: Oh, I would assume Audio Hijack is ready to go just because they're so deep into nerdy Mac land.
Dave: Yeah. They're full. If it's not available, it's coming, I'm sure.
Chris: Right. The one that wasn't -- so, I decided to go for this very fancy -- here in the booth, for some reason, I have a USB audio interface because I use a nondigital microphone. You know?
Chris: It's the Scarlett. I feel like tons of people use the Scarlett. I forget all the acronyms. I have just one random that takes four inputs here in the booth even though I only use one, so who knows why I bought the four input one. But it turns one of those big, thick microphone input things into a digital signal. It has a job and that's it.
Chris: Then you take that into your computer. In your computer, it sees the Scarlet as the microphone.
Chris: But then you get complicated and you put stuff in the chain and stuff and you get nerdy from there. Well, that's fine, but these Scarletts are not on the cutting edge of technology. It uses an old-school USB input. Fine, but I'm on my third Mac now that doesn't have old-school USB. I don't know, but it's at least two.
Dave: Yeah. Yeah.
Chris: I don't want old USB things anymore. Can we be done with those, please? They're slow and they need dongles and blah-blah-blah.
A way long time ago now -- well, not way long, but early pandemic, I went for one of these -- I can't even remember the brand of it now -- very fancy replacement to the Scarlett.
Chris: No. No, it's a universal something. It has a cool name. It doesn't matter. I'll put it in the show notes, but it's a beautiful gray thing. It's like a Scarlett, an audio interface, and it's got a big, juicy knob on it for changing the volumes and stuff. It's got all this cool stuff that it can do but, for some reason, at my desk, it's so complicated that I can't -- I think sometimes it causes harm to my audio recording because I don't configure it correctly and such.
Chris: Anyway, what I was kind of going with there is it also requires some software. The reason I did it, by the way, is because of the USB thing. I didn't follow up with that, but it's got Thunderbolt, so it connects to my computer in the way that I want it to. That was the main reason I bought it.
It also requires some software to be recognized on the machine, which to me is a little code smelly. Why can't you just see it? It's just Thunderbolt. Just see it.
Dave: Mm-hmm. Mm-hmm.
Chris: But whatever. It needs the software and they sent out this very big warning email, like, "Do not upgrade. Not ready." By upgrade, it's not M1. It's Big Sur.
Dave: Oh, really?! Yeah. Wow. I've lost audio interfaces in an OS update. I had that Line 6, the amplifier maker. You know?
Dave: I had a Line 6 input into my computer, and so I could play shredding guitars into my computer. It was sweet. Then, just low and behold, wham! It didn't work. I just had $100 piece of junk because of a Mac update.
Chris: Oh, thanks. Yeah.
Dave: I could never find drivers or they just never showed up. It was just like, man, what a bummer. Then I could shake it and hear screws in there. I was like, maybe this wasn't the best piece of hardware. [Laughter]
Chris: It's the Apollo Twin. Apollo.
Dave: Apollo Twin. Okay.
Chris: It's a very beautiful piece of hardware but I feel a little underqualified to use it. I'm a little less excited. Right when I bought it, I was all excited, to like, I'll watch any video. I am going to get this right.
Chris: I feel like it worked for a while and now it's doing something weird and I'm giving up on it at the desk to go back to a Scarlett, just for now. What I want to do is hire somebody local to come over who is really knowledgeable about sound.
Look at me! I've got all of this audio equipment. I've got wires coming out of my ears and I'm dedicated to making it sound good, but I don't actually know what I'm doing.
Dave: [Chuckles] Yeah, that's fair. Yeah.
Chris: I need somebody that went to school for this, you know, like I've got a buddy back in Wisconsin, Randy, who basically went to school for knowing what all these wires and knobs do. I wish I could fly Randy out here, but that ain't happening with the pandemic. Anyway, I need a local person to come by.
Dave: No, I think that's just it. All this, kind of pulling it back to websites, all this stuff is so complicated. You almost want an expert who works in this to set you up. Just give me a setup, you know. Give me the base setup and then I promise I won't touch it for a whole year. Then I'm going to call you next year and ask you to fix it again. You just wish you could get that person to give you the base setup and then, yeah, how much does that cost?
Chris: Yeah. I'm working on it. The new office, so right here in the booth, it doesn't matter. There's no sound. But in the new office, it's a little -- there's not enough stuff in there yet. We have this classic echo problem thing going on. There are glass walls and stuff that it's very bouncy. I've been trying stuff right and left to get it to work.
This microphone, this Shure SM7B is supposed to be pretty forgiving to that, I hear from Mr. Chris Enns -- what's up, Chris? -- but there's something just wrong in my office and I don't know if it's entirely echo or if it's this Apollo Twin. There are just some settings that are wrong and stuff. But I want to have really good audio at the desk, too, for Zoom calls where you look good and sound good, and for doing little YouTubes and stuff. I just want to have good sound there, too.
I bought one of these mic shields, so it's this really nice looking thing that attaches to the boom arm and then surrounds and hugs the microphone. Because it's a condenser mic then, even in a big room, it is really tight walling. Maybe that wall. I think that will go a long way for echoing in a room that's otherwise not ready.
I also ordered some acoustic panels and stuff. I found a place that will print something onto the panels for you.
Dave: Ooh! Okay.
Chris: You know?
Dave: Yeah. Yeah.
Chris: Which is really fun, like one of those, print my Instagram things on a canvas, but it's audio panels that do that. I picked out some really cool Pens that are visual art.
Chris: Took a high res screenshot of it and send them that art. They're like three months backed up, so I was like, oh.
Dave: Oh, gosh.
Dave: Yeah, well, we need to cut over. We have a guest read, but I'm undergoing shed attempt number four here.
Chris: You're going to build a shed?!
Dave: Going to build a shed. Just paid a bunch of money to do it.
Chris: What do you mean number four? You've built four sheds in your life? That's crazy.
Dave: Well, this is a whole episode, dude, and I'll share the whole saga next episode.
Dave: Okay? But I'm on shed four, but the guy was just like, "Ordering windows and lumber is four months out," or it's like double the time and triple the cost because all this stuff is all backed up because of COVID and all that stuff. I'm not surprised that this, whatever, picture, soundproof shop is like, "Yeah, dude. We can't do it because we're A) backed up and then everyone works from home now, and so they all need sound-proofing." It doesn't surprise me. Everything is backed up, is what I want to say. No, once I build my shed, then I can get into cool office stuff.
Dave: Yeah. Yeah.
Chris: I'm going to try -- in my mind, I'm going to say, "Oh, it's backed up because there's a lot of demand for stuff because we're not in full economic collapse. Isn't that nice?" [Laughter]
Dave: [Laughter] That's a very positive spin on the collapse of Western economies. [Laughter]
Chris: All right, well, this is wonderful and I want people to know that this is possible for you, too. We want to use some of the airtime on ShopTalk Show to share with you some excellent ideas, some things that have been written down and, over the years, proven themselves to be excellent writing in the form of stuff about the Web industry because good written stuff often makes good audio, too.
We have David Luhr here, who is a friend of mine and lives in Bend, Oregon, who is a front-end architect, an accessibility specialist, and really knowledgeable guy about accessibility. Really quite passionate about it. Another one of our friends Eric Bailey is too. I think they're kind of spirit animals in that way.
Eric is a wonderful writer and speaks really intelligently about Web everything but particularly about accessibility and wrote this blog post called "Truths About Digital Accessibility." He wrote this back in July of 2019 but has really proven to be a tremendously good piece of writing about accessibility. We wanted to share that with you. Actually, David wanted to share that with you and was very excited to read this post from Eric. Eric said that's absolutely great, so here is David Luhr reading Eric Bailey's "Truths About Digital Accessibility."
David Luhr: "Truths About Digital Accessibility," but Eric Bailey.
Creating, maintaining, or evaluating accessible technology? Here are some things to keep in mind. Note that identity-first language is intentional.
Each screen reader behaves differently. This is by design. Behavior is a balancing act between a screen reader's features, the requirements of the operating system it's installed on, the form factor it's available in, and the types of input it can receive.
Each screen reader also has different interaction modes. These modes help the people who use them to understand, navigate, and take action on digital content in the way that is the most effective for them. Generating a list of all headings elements, links, or landmarks are all examples of what these modes can be.
Modes are activated in different ways, depending on both the form factor and version of the screen reader being used. For example, VoiceOver on iOS can navigate between lists by swiping when the Rotor mode is set to navigate via lists. In comparison, JAWS uses the L key on a keyboard to do the same. Many of these modes can also be activated in multiple ways, such as a keyboard shortcut, macro, voice command, or gaze dwell duration.
Screen readers are more than just VoiceOver. Windows may not be your primary operating system. However, chances are it will be for the people who rely on screen readers. Because of this, it is vital to include JAWS and NVDA, the two most popular Windows screen readers, in your tests.
It's also worth pointing out here that the desktop and mobile versions of VoiceOver are separate apps, each with its own bugs and quirks to work around. Don't get me wrong: Apple bundling a powerful, sophisticated screen reader into its desktop and mobile operating systems has done a ton for normalizing access, and the world is better off for it.
Not all screen reader users are blind. This fact is quite nicely elaborated on by Adrian Roselli. If you don't read his post, one of the more important takeaways is ensuring there is parity between your user interface components' text labels and what is announced by screen readers (while still respecting someone's screen reader verbosity settings). A discrepancy between the two will most likely create a confusing and disorienting experience.
There is also an elegant, inclusive aspect to maintaining this parity. By not making assumptions about a screen reader user's circumstances, we don't unintentionally create separate experiences for them.
Anecdotally, my only vision concern is some mild nearsightedness, but I love using macOS' speak selected text feature as a way of keeping myself honest when reading my writing out loud to myself when proofreading. This feature is effectively a simplified screen reader, similar to Microsoft Narrator.
Assistive technology is more than screen readers. Wheelchairs, canes, head pointers and mouth sticks, switches, sip-and-puff devices, eyeglasses, adaptive keyboards, eye trackers, refreshable braille displays, adaptive grips, ramps, grab bars, prosthetics, voice command, screen magnifiers, hearing aids, memory aids, et cetera. The list goes on.
Many of these devices now (or soon will) incorporate a computer and/or interface with other digital technology. These devices rely on the Accessibility Tree--the mechanism computers use to allow assistive technology to programmatically read and interpret user interface content--to function.
I would also be remiss if I didn't mention specialized browsing modes or devices that have been repurposed to serve as assistive technology. Assistive technology may not be running on high-powered devices or be running the latest software.
There are four factors to consider here and these factors are not necessarily mutually exclusive:
First is that disabled people are discriminated against in many areas, especially employment. A lack of access to good-paying jobs also means less income to spend on high-quality devices. A lack of high-quality devices means lower-powered hardware.
Second is that assistive technology may be running on a lower-power device due to the device's form factor. Here, size requirements may mandate there won't physically be any extra space to put larger, more powerful hardware. This requirement sometimes stems from a desire to diminish the device's profile, and therefore lessen chances of stigmatization by its use in public--think the evolution of the hearing aid.
Some, but not all devices get around this limitation by offloading their computational needs on another device, say a hearing aid that pairs with a smartphone. While this does let a lower-power device do things it normally couldn't on its own, the device is still limited by what is allowed by both the operating environment and the communication protocols it utilizes. For example, Bluetooth Low Energy clocks in at around 0.27 Mbps, which imposes a pretty severe bottleneck on how much information can be transmitted at any given moment.
Third, it's not uncommon for some assistive technology users to purposely not upgrade their hardware and software. Think of how many times you've updated something and it broke a feature in some small, yet annoying way. Building off this feeling, it's easy to understand why many people are so risk-averse about their upgrade path, as it represents a very real fear of breaking an important tool they use to interact with the world.
This is to say the presence of an evergreen browser may not mean it is fully up-to-date, or the presence of a popular operating system means it is running the latest and greatest version. In fact, a person may be taking deliberate measures to prevent a happy upgrade path from occurring.
Fourth, a huge swath of the world's population is geographically predisposed to using a low power device, just by accident of birth. Many emerging markets (and underserved communities) are coming online via inexpensive Android smartphones.
These devices represent a person's main form of access to the Internet, and with Android comes the presence of the Android Accessibility Suite (formerly known as TalkBack). While the Suite is not nearly as robust or feature-rich as VoiceOver and Apple's other accessibility settings, Android's adoption numbers means you should be thinking about, and testing, how your work deals with its limitations.
Assistive technology may be augmented by other assistive technology. Multiple disability conditions can and do affect people. Many of these conditions can be chronic, and their severity can compound as time goes on. Because of this, more than one assistive technology application or device, each with its own unique features and capabilities, may be used at any given moment.
The presence of assistive technology isn't necessarily permanent. Much as how more than one form of assistive technology may be swapped out for another on the fly, some people may only conditionally employ assistive technology in specific circumstances. An example of this would be someone using a magnifying glass or screen magnifier application in places where they can't control text resizing.
A person who could benefit from assistive technology may not be using it. This touches on lack of resources and stigmatization issues discussed earlier.
A couple of other reasons for non-adoption may be a person not feeling comfortable admitting to themselves that they need help, or that they are unaware that assistive technology offerings for their disability exist. The latter reason is further amplified the more specialized the technology is--there is a world of difference between adjusting text size and discovering and installing a NVDA add-on.
Another reason is a person simply not knowing that what they're experiencing constitutes a disability condition. In the United States, and in many emerging markets, many barriers towards access and education are present. These are just a few of the many, many reasons why we don't make separate digital experiences for disabled people anymore.
Assistive technology may not be considered assistive technology by the person using it. Think of the last time you struggled to tap a tiny button on your smartphone. Or when you were reading the subtitles on a muted video, so as to not disturb the other passengers on the train. Or when you boosted the brightness on your display. There are countless examples of this, where a person may be situationally disabled, but does not identify as such.
This is important to think about for things like accessibility options in preference menus, where someone may skip over features that could help them because they don't consider themselves as being disabled. Configuring things to make them accessible by delving into preference menus, instead of leading with accessible defaults, also brings up the topic of less digitally-literate users. This brings us to our next point:
Not every assistive technology user is a power user. This is something that's easy to forget when you work on a computer all day making things for other people to use on their computers.
A digital device can be just one of many touchpoints that are required for a person to do their job or live their everyday life. Some people accept this as a matter-of-fact, but others may approach technology with a sense of dread.
Every user is a new user at some point, and some never progress past the initial metaphors required to operate an interface at its most basic level (open and close, save and delete, et cetera). It's not uncommon for these kinds of users to rigidly stay with what works (messaging friends and family, looking at photos, listening to music, watching movies, et cetera), and never explore the other capabilities of their device.
This is completely acceptable. User interfaces exist to make complicated code commands usable by the general population. This means that it's our job to make sure our interfaces work for as many people as possible, as easily as possible. Being able to invest the time and energy into understanding the borrowed metaphors utilized by a user interface, or series of user interfaces oftentimes stems from a position of privilege. In a business context, this unnecessary deciphering of intent also directly corresponds to lost opportunities.
On top of all this, learning how to operate assistive technology can occur at a moment where someone is learning how to re-navigate many other important aspects of their life. This can translate to creating a very circumstantially steep learning curve.
Not every assistive technology user is non-technical. This is an ugly, unfortunate myth that needs to die.
Non-technical people may make highly technical modifications to their technology. People are creative, resourceful, and persistent, especially when the motivating factors are high. To think anything less is a disservice.
People also rely on other people. In the disabled community, this is known as the myth of independence.
Think of how many times a friend or family member has asked you to fix their computer for them, because you possessed a skill they needed. Then think of the times you asked them to provide aid with something you didn't know how to do.
Is it so much of a stretch to imagine that a motivated individual can research and install a browser extension? Or that a technical person can create or configure a specialized software or hardware solution for someone they care about? No, of course not.
For those people who self-serve, it's also important to remember that there are levels of technical complexity, many of them unnecessarily artificial. Chances are good that someone can learn to tweak the CSS class of .u-color-black to be green without knowing what a utility class namespace is. Compare this to reverse-engineering a declaration like .css-1dbjc4n.r-1awozwy and so on.
Does this mean abandoning modern frameworks and development tools entirely? No. Just remember that what they output, and how they output it is potentially another user-facing interface.
ARIA has varying levels of support. While there are a finite (if large) amount of user agent and assistive technologies, the possible permutations of Web content implementations is near-infinite. Because of this, ARIA (or Accessible Rich Internet Applications) was created. Its purpose is to address potential unanticipated gaps between a content implementation pattern and assistive technology.
As it is a relatively new and niche technology, ARIA has varying levels of support. This is further complicated by the fact that support is dependent on the combination of operating system, assistive technology, and browser being used.
A lack of support may mean a couple of things: First, assistive technology may simply not report anything. Second, and more worryingly, it may mean assistive technology reports, but reports inaccurately.
ARIA support fragments further if it includes combinations of multiple ARIA declarations, both on the same DOM node, and working in coordination on a more complicated multi-node component. Support concerns compound even more if these multiple multi-node components communicate with each other.
This is also to say nothing of niche browsers, browsing modes, bugs and regressions, and software rot. The devil is in the details here.
ARIA increases the time it takes to calculate an accessible name. An accessible name is the phrase assistive technology will report to the person using it and is the core aspect of how assistive technology functions.
For example, a link may have an accessible name of "The history of whiskey," which is provided by the words written in between the opening and closing anchor element tags. The anchor element provides a Role, which communicates expectations around behavior. Combined, they inform the person using assistive technology that this is a link that takes them to a page where they can learn more about the history of whiskey.
The anchor element, like every other HTML element, comes with what is known as native semantics. Native semantics are roles that are inherent to the element being used. You don't have to make an additional, redundant ARIA Role declaration on something described semantically. It is something you get for free by virtue of using the proper element and attribute.
The button element, for example, is a clear, unambiguous signifier of intent to both authors and browsers. It also enjoys wide and deep support, even for non-traditional or atypical devices that may possess rudimentary browsing capabilities.
When ARIA is used to recreate the native semantics inherent in HTML, it increases the work the Accessibility Tree needs to do to figure out what the ARIA is trying to describe, including its accessible name and permitted (and therefore expected) behavior.
Small amounts of ARIA, skillfully applied to address the occasional gap, makes for a small amount of processing work a browser sends to the Accessibility Tree. Larger amounts of ARIA, especially when not augmenting a native semantic HTML element--and especially when being used to construct multiple, complicated interactive user interface components--translates to a large amount of processing work that an Accessibility Tree's heuristics needs to do to create meaningful reporting.
There is a reason the First Rule of ARIA prioritizes using appropriate native HTML elements whenever possible. Too much ARIA can create a slowdown and/or lack of synchronization between the current state of a website or Web app, and what is being reported by assistive technology--especially lower power versions of it. In some extreme cases, it can even overwhelm the Accessibility Tree and crash it. It's worth stating directly that both of these scenarios create an unacceptable user experience.
There are many good reasons to build performant websites and Web apps. Slowing down or crashing the Accessibility Tree is another vital reason, one that is unfortunately not commonly known.
Windows High Contrast Mode ignores ARIA. Windows High Contrast Mode is a specialized assistive technology solution that affects the entire Windows operating system, including all browser content. It strips away styling, prioritizing making text content legible above all else. This is by design.
For people experiencing low vision, High Contrast Mode can be their last, best hope for viewing digital content. While I am loathed to speak about accessibility support in terms of numbers, some quick, back-of-the-envelope math tells us that assuming if even 1% of the 800 million devices using Windows 10 also use High Contrast Mode, it means that there are 80,000 users (to compare, my hometown has a population of around 25,000). This is to say nothing of still-active versions of Windows 8 and 7.
I should also point out that there is no minimum acceptable threshold of support, and that it is an awful way to think about the problem. If you can't empathize with such a large figure, can't make the connection that the figure might contain people you care about, or can't understand that it may also be a future version of yourself, understand that inaccessible websites are illegal.
In Windows High Contrast Mode, an element's native semantics affects how it appears. For example, an anchor element will get a different visual treatment than paragraph text or list items. Anchors created from non-semantic elements such as spans or divs that rely on ARIA to create link functionality won't get this visual treatment. The same goes for any and all interactive user interface components built from non-semantic elements.
This means that unless an interactive component uses incredibly obvious language for its functionality, there is a large chance that it won't be perceived as something can be interacted with--even more so in the presence of a lower screen resolution and/or screen magnification scenario, where only a portion of the component may be visible. This means that it is inaccessible.
Once again, the First Rule of ARIA applies. By relying on well-supported, time-tested markup to describe our content, we are able to make our work versatile and robust. This is for not only current modes of operation we may not be familiar with, but also future technologies that have yet to be invented.
A disabled person may not be present in the communication channels you use. There are many reasons you may not be aware of inaccessibility issues, but the main one is systemic structural inequality.
As the practice of digital accessibility becomes more mainstream, we're discovering that many things we took for granted may in fact not be true. The tools we use, by their very nature, may prohibit someone from operating the mechanisms required to make you aware of the issues preventing someone from being present. This does not mean that their lived experiences are invalid.
Just because someone is disabled doesn't mean that they must devote their life to reporting or fixing these errors, either. It's a huge, unfair ask. The responsibility should be on you, the digital technology maker, to do this. It is a code quality issue, another thing to be on the lookout for alongside concerns such as bugs, unoptimized code, regressions, and memory leaks.
Your ego may be the biggest barrier. Perhaps the most difficult thing is internalizing all of this. Digital accessibility work is not easy, but it is vital. It's a holistic, multifaceted discipline that touches on multiple interconnected social and technological issues.
Hopefully, this isn't discouraging. Everyone makes mistakes, and I'm certainly not exempt from making them. Fortunately, digital technology--and especially the Web--allows us to make good on our errors and missteps quickly and with minimal cost.
A question I constantly ask myself is if it's worth being perceived as being correct at the expense of not meeting the needs of those who I am trying to help. Part of that means being willing to listen and having the humility to make improvements on what I say and make, even if it requires internalizing an uncomfortable truth.
Dave: All right. Thank you, David. Thank you, Eric. I remember when this post came out and it just hits the nail on the head, if you have ever worked with accessibility. Thank you all so much.
Chris, I guess we'll just wrap it up 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. Follow us on Twitter, @ShopTalkShow, for tons of tweets a month.
If you hate your job, head over to ShopTalkShow.com/jobs and get a brand new one because people want to hire people like you.
Chris, do you got anything else you'd like to say?
Chris: Oh, ShopTalkShow.com.