628: Tending to RSS Feeds, Code Hike’s Fine Markdown, and Cloudflare R2
Doc told me to travel but there's COVID on the planes, Dave's got a 2x life update, how often do you manage or prune your RSS feed subscriptions, checking in on Code Hike and their fine grained Markdown approach, JavaScript decorators use case, and using Cloudflare R2 for image storage.
Guests
Chris Coyier and Dave Rupert
Time Jump Links
Episode Sponsors 🧡
Transcript
[Banjo music]
MANTRA: Just Build Websites!
Dave Rupert: Hey there, Shop-o-maniacs. You're listening to another episode of the ShopTalk Show. I'm Dave--back in health--Rupert, and with me is Chris.
Chris Coyier: Yeah, dude. You look good. You sound good.
Dave: I'm feeling better, man. I had the COVID, but no one likes to talk about that. That's sad times, but I think I got it on a flight. Yeah.
Chris: I heard John Stewart has got it. Even John Stewart.
Dave: Oh, really?
Chris: Yeah.
Dave: He's old. Hopefully, he's okay.
[Laughter]
Chris: Yeah.
Dave: Yeah.
Chris: Yeah, that's cool.
Dave: Generally speaking, there are therapeutics now, which make it a little better. Less danger zone, so.
Chris: Yeah.
Dave: But yeah, it was like a hangover for five days (for me). That's how it felt.
Chris: [Lip trill]
Dave: Blah! Pretty bad.
Chris: Still sucks. Who knew?
Dave: Yeah.
Chris: Everybody knows.
Dave: What gets me is, every trip you're going to go on, it's just like roll the dice on whether or not you get the bad sick. You know?
Chris: Mm-hmm.
Dave: You're probably going to catch something on an airplane anyway, even masking. But it's just like, you just... Do you get the bad sick or do you just get a normal sick?
Chris: Uh-huh.
Dave: Ugh! Hate that. Anyway--
Chris: I think that's why that Noah Kahan song hit so hard. Have you heard that one?
Dave: No.
Chris: Stick Season or Season of the Sticks. I don't know. It's like two years old now - or something, but it felt like a big one. There's this line about the doc said to travel but there's COVID on the planes and the people go crazy. Yeah, you're right. There's COVID on the planes.
Dave: Yeah.
Chris: Anyway, moving on.
Dave: Hmm...
Chris: Here's a baby thought--
Dave: Do we have to pay royalties for that? [Laughter] Do we have to pay royalties for that?
Chris: Maybe.
Dave: Yeah.
Chris: How do some podcasts get away with that? Is there a minute limit or something? I feel like I listen to some podcasts that are not that big sometimes that'll throw in a little Led Zep ender - or something - and you're like, "How did you do that?"
Dave: Yeah.
Chris: Did you spend $12,000 for that?
Dave: Is Jimmy Page your uncle?
Chris: Yeah.
[Laughter]
Chris: Or can you just slide it in if you're a certain kind of podcast? Is there a fair use thing if you talk about it or something? I don't know.
Dave: I don't know. I think it's like 15 seconds. But I think even with those, though, it's not that - or something like that.
Chris: Yeah. I don't think you can just slide that in.
Dave: I don't know.
Chris: I've wanted to so many times, but then I remember I had the perfect song for when Ethan was on (way back)--
Dave: Yeah.
Chris: --talking about unions and stuff. I was like, "Oh, I'm going to play this clip." Then I was like, "No, I'm not." I'm literally just exploiting the artist (that wrote that song) on this episode about not exploiting employees.
Dave: Workers, yeah.
Chris: Yeah.
Dave: You're just like--
Chris: Probably not going to do that.
Dave: Steal money to make a cool show about not--
Chris: Yeah.
Dave: About supporting workers.
Hey, so a little life update for me.
Chris: Okay. Yeah.
Dave: You were going to say something, and then I interrupted you.
Chris: It's more important.
Dave: I have two dogs.
Chris: Ahh!
Dave: We adopted two dogs. Yeah. And we're surprising the kids, so don't post this before we do that. [Laughter]
Chris: They haven't even seen them yet?
Dave: They haven't even seen them yet. They'll see them tomorrow, so that's cool.
Chris: Oh, my gosh!
Dave: But anyway, it's very cute. One is named Laneige, which is like a Korean makeup brand, and one is named Rosebud, which is also a plan or, like, a bud, but it's a type of - whatever - lip balm kind of thing.
Chris: Yeah?
Dave: But anyway, the names are pending. They'll probably change. My daughter really wants a dog named Celsius.
Chris: Working titles?
Dave: Yeah, working titles. Laneige is probably going to--
Chris: Celsius is an amazing name.
Dave: Yeah. Laneige will probably stick, but anyway.
Chris: Okay.
Dave: Anyway, so I got two puppies in the office, and they're biting my fingers. So, if I sound distracted this episode, it is because I'm distracted.
[Laughter]
Dave: I have two dogs right now.
Chris: Yeah, that's because of little puppy nibbles.
Dave: Yeah.
Chris: They're hanging out in the shed.
Dave: Yeah, they're in the shed.
Chris: They're not barking their heads off, so that's kind of a good sign.
Dave: Not right now. But they are mouthing. They're eating my finger. Anyway, let's go! What were you saying? You were jumping into something.
Chris: I have a variety of topics we can get into, and we can do some questions and stuff, too. But I had this baby thought about how much do you tend to your feeds. You know?
Dave: Ah... Like groom old feeds and stuff like that?
Chris: More like unsubscribe.
Dave: Hmm... You know probably once a year - or so - I try to do something.
Chris: Yeah.
Dave: But... Sorry, they're going wild.
Chris: Well, one of them was about... No, that's all right. I think I can set this up for the audience.
A) It's okay, because I do feel like there's a little bit of potential guilt sometimes when you're like, "Unsubscribe!" particularly if it's a person - or something. But I had some really abstract ones in there, and I was realizing that I was just reading some people's really unformed thoughts too much. [Laughter]
Dave: Mm-hmm.
Chris: You know?
Dave: Okay. Yeah, yeah, yeah.
Chris: There are posts that are just a little bit like, "I watched the debate last night. I'm not sure what to think," kind of thing. You're like, "Why did I just ready that? I don't care."
Dave: Yeah. Yeah.
Chris: Anyway, now I feel like I'm shaming people. You can post whatever you want. This is a free Internet. You are free to do that. If that's your vibe, please do so.
It's just that I feel like, in social media, that feels normal and fine (these little baby thoughts as far as length and depth of thought and stuff). But then sometimes I'm like the RSS has these different expectations for me.
Dave: Mm-hmm. That's... Yeah.
Chris: That I'm choosing a length here. I also don't want to read a book through RSS.
Dave: For sure.
Chris: So, I'm realizing that there's this middle ground for me that I prefer, and I want to subscribe to things that are... They can be small thoughts. They just need to be more clear - or whatever.
Dave: Yeah.
Chris: I don't know. That's why I'm not sure what to say about this because I'm still working through it myself. But I was like, "I don't know. I like complete thoughts."
Dave: Microblogging, we've had Manton Reece on. I support microblogging, but it is sort of like when somebody's blog turns into a microblog, sometimes they lose me, which is not to say you shouldn't do that or you shouldn't... Please, express yourself how you want to express yourself. For me, it's like I subscribe to your long-form thoughts. I wasn't necessarily subscribing to your every idea you had or every little just - I don't know - thought bubble you came up with. That's sort of how I feel, like tweets and all that.
Chris: Hmm...
Dave: I kind of try to keep that on Twitter or on Mastodon - or whatever.
Chris: Right. Kind of just a consumption thing. That's why I brought it up with feeds specifically because I like small thoughts, too, and that's all... If I sign into my Mastodon on elk.zone - or whatever, it's just full of tiny baby thoughts. In there, I'm super-fine with it. But I feel like when I take the leap to RSS, I'm looking for more than that - or something.
Dave: Yeah.
Chris: Anyway--
Dave: This makes me a terrible person, but sometimes somebody is really going through it. [Laughter] You know?
Chris: Mm-hmm.
Dave: Depression or just - I don't know - having a public breakup or something like that on the Internet and kind of just blogging through that.
Chris: Posting through it. [Laughter]
Dave: I support that. But sometimes, in an effort to guard my own mental health, I think to myself, "I'm going to say goodbye, and I'll catch you on the other side," because I want you to get through it but I don't necessarily need to experience this experience all the way through. You know?
Chris: Right. If we were like BFFs, that would be awesome. Let's do this in person. But yeah, kind of experiencing someone else's minor trauma just recreationally doesn't feel right.
Dave: Yeah. Yeah, kind of casual trauma bonding. I don't need that. I don't have to do that.
I've kind of emoted that on Mastodon before, and somebody was like, "You need to... That's stupid. People are humans." And I understand that. But sometimes people take a weird, angsty, teenager path towards something, and I did that already in my life.
Chris: Yeah.
Dave: And I don't need to do it again. You know?
Chris: Yeah. Yeah, fair enough. Fair enough. Yeah, you're like, "They're a complete human being, and I want them and respect them as a whole human being, but so am I."
Dave: Yeah. Remember when I went through all of my titles are lower case, "I only type in lower case" phase? Yeah.
Chris: Hmm... Seen it. Yeah.
Dave: I don't need to experience that again. [Laughter]
Chris: Uh-huh. Are you listening, Brad? [Laughter]
Dave: Brad?
Chris: [Laughter]
Dave: All lower case. All lowercase.
Chris: All right. It's okay.
Dave: No, just kidding.
Chris: Everybody knows that all lowercase is just so people know it's a joke.
Dave: Mm-hmm. Yeah.
Chris: Sorry. No.
Dave: No, serious poetry.
Chris: Just kidding. You can do whatever you want.
Dave: It's serious candles and, you know, a little bit of slam poetry. But not too much to, you know, yeah.
Chris: I see. Yeah. Awkward line breaks.
Dave: Awkward line breaks to emote feelings.
[banjo music starts]
Chris: Hey, you should check out this browser extension. It's really cool. It's about making much better bug reports when working on websites, and I'll tell you how to do it.
Anyway, go to jam.dev -- a nice URL, right? -- jam.dev, sign up, get the browser extension. It's all free.
Then it's a cute little strawberry icon (as a browser extension). And when you see a bug, you just click on the little strawberry, and then you either just click and get the whole screen or you click and drag and get a piece of the screen that you're trying to show somebody.
It has nice annotation tools in there, so if you need to type something or draw an arrow to something or boxes around it, whatever, that's nice because you're trying to point out what the bug is. You can write with words about the bug - or whatever. And that's it.
It's really no harder than just taking a screenshot, but what you get then is a URL that you can share of the problem. That's nice. Or you can have a team account. It's automatically part of the team and all that.
But here's the greatest part is that it's full of all this metadata that you wouldn't have gotten normally with a screenshot, like the entire browser console.
Here's a story that happened on our team. We were debugging this problem and somebody left a tab and came back. There was just an error sitting there. Another user (we were sitting on a Zoom call - or whatever) was like, "Oh, make sure you take a screenshot of that, but do it with the console open because we need to see if there are errors in there." They were like, "Oh, okay. I guess I'll do that."
If you were to use Jam to do that, you wouldn't... It takes all the thinking out of it. The console is automatically captured, and it can't be on the wrong tab or you didn't capture all of it or the important thing has already scrolled past. None of that can happen. You're going to have the whole console as part of the metadata around this little screenshot/bug report that you've automatically created just by clicking and dragging really quickly.
It's got network information. You can connect it up to integrations if you want to later send it to Jira or send it to Notion or something. There are tons of integrations. You can hook it up to Sentry, so you know if it was a bug that triggered in Sentry. You can connect the two up together. There's AI stuff built into it for diagnosing what's wrong. That's all the juicy stuff on top of the Jam.
But the best part is, when you're just working with your team, all you do is be like, "Oh, yeah." Muscle memory. Screenshot. Take screenshots. There's the screenshot of the bug and, automatically, without having to do any extra work, there's all this useful debugging information along with it, so it makes bug reports way easier with no additional work.
Jam.dev, a super-cool tool.
[banjo music stops]
Chris: All right. Here's another link for you. Do you remember when this came out? I feel like it was a year ago or something. I looked at it, and I was like, "Hey, this is pretty cool, but I wonder if it's going anywhere or if it feels more like vaporware."
Dave: Mm-hmm.
Chris: What I'm talking about is Code Hike, codehike.org. There's a link in the show notes. It seems like they have been doing good.
Dave: Okay. Okay.
Chris: What it looks like to me... and I think they have different goals. But one of the things for sure is literally just code blocks. Just like you're writing a blog post or documentation and you want to display a block of code in there. They're trying to raise the bar on what's possible there. Although, it's complicated because it looks like it's pretty heavy on, like, "Well, it's Markdown and it has comment directives in it, and a lot of our stuff is Next.js, MDX, Tailwind." There's a lot of buy-in to get there.
Dave: Mm-hmm.
Chris: But the end result is pretty sweet code blocks.
Dave: Yeah.
Chris: Stuff in there that is really nice, like being able to hover over a block and seeing a block of it highlighted, or putting clickable URLs in the code block, which is weirdly complicated. I guess I could spend more time than just say that, but it's kind of like--
Dave: Yeah, or like--
Chris: --what you don't want to do is escape your own code. That's why people like Markdown. You put the three backticks in Markdown, and then all your angle brackets in HTML and stuff are automatically escaped, right? That's great. That's ideal. You don't want to hand do that. That sucks.
But then you put an anchor link in there and then it escapes the anchor link, too. So, you can't do that. So, it's kind of like you have one or the other, and they have a novel technique for linking stuff in there, so it kind of works. That's nice.
I don't want to go through every list in here, but as you go through it, you're like, "Actually, that looks great. That little callout looks good." You have a very classy copy button. You have a very classy way to gray out some of the other content on there. It's like these are, I'll say, the nicest blocks of code I've ever seen. Good job.
Dave: There are a lot of really nice things in here. Well, and it's almost like... You know when you get a good text editor and it has all the little doodads you need?
Chris: Yeah.
Dave: One is a callout. You know how you always slap an inline comment on the end and be like, "This is really important," but this is more like a little popover tool-tip-looking bubble.
Chris: The callout is beautiful, yeah.
Dave: But it's not an interaction pattern. But that's cool to just be like, "Hey, this needs to be highlighted or called out."
Chris: Yeah. You see how they do it with a RegEx format, too? It's pretty classy, I think.
Dave: Yeah. And it's like, "Hey, on the next line, this is the anchor point."
Chris: Mm-hmm.
Dave: Then just add some text next to it. You know code collapsing looks awesome. Difs are awesome.
Chris: Right, and you can get that stuff if you're like, "Oh, I'll put a whole code mirror in here," or "I'll put a whole little VS Code instance in there." What's the thing underneath VS Code? I always forget the name of it. There's some kind of--
Dave: Oh...
Chris: A-word or something. But it's starting to fuzz together and people just think of it as VS Code, but there's a code editor kind of under the hood there. It's like, "You could use that in a blog post, too, but it's weighty as heck." You know? I don't need three megabytes of JavaScript for a code block, so I think the idea here, too, is it's still lightweight.
Dave: Mm-hmm.
Chris: But it's quite... But it has all these features, too, like code folding. It's harder than it looks. You need an AST-something figuring out where and what to fold there. It's kind of a non-trivial feature.
Anyway, that's almost not what I wanted to talk about. If you click over to the blog link there, their latest blog post is called Fine-Grained Markdown.
Dave: Mm-hmm.
Chris: I found this very interesting thing to read. So, the idea is you have a file (like a blog post). I always bring up daverupert.com because you write blog posts in Markdown, surely.
Dave: Mm-hmm. Mm-hmm.
Chris: At the top of that post, there's metadata. You probably have fences. Do you use fences on your thing?
Dave: Yep. Yep, a little three lines.
Chris: Put the title. Yeah.
Dave: Yeah.
Chris: That's a way to add metadata information to a piece of Markdown (right) the blog title and when it's published, who the author was, whatever you want in there. But then--this is true of all kinds of CMSs--at some point, the fences end - or whatever. Or if you're on a WordPress site, you've added the title. You've had some opportunity to do some metadata. But then there's a big, giant block, and it's just like that's where the content goes. Blah.
Dave: Mm-hmm. Mm-hmm.
Chris: Eventually, in a template somewhere, it says, "Blah the content." You know? Splat, here it is.
Dave: Mm-hmm. Mm-hmm.
Chris: Here's all of it. And you just have to be careful of what you put in the content because, like, don't put too much handcrafted HTML in there. That's not going to last.
There's this thing that you learn when you work with CMSs long enough to be clean in your content area.
Dave: Yep. Yep.
Chris: This isn't about that, but it goes on a little bit and says, "Let's say you wanted to do columns, for example, inside of it." That's why I'm kind of a fan of the WordPress block editor because they said, "Hey, we're going to help you do columns." You add the column block, and then you pick the columns, and you put the stuff in there. You kind of do it visually.
It's going to output HTML, and that HTML is going to have some expectations of some CSS that turn it into columns. But because we're baking it into the editor, those expectations are okay and are going to last over time.
Dave: Mm-hmm. Mm-hmm.
Chris: I kind of like that. This is more... It has kind of a cool way of handling it, so this Code Hike blog post. I don't quite understand why they're in the Markdown parsing space all of a sudden, but maybe it's related to what they're doing. I'm not sure.
You know you have headers in Markdown like number sign, number sign, or octothorpe, octothorpe, tile of section. They have some extra syntax they're putting in here, which means that--
When you change the syntax, I'm always a little bit like, "Okay. Let me see where you're going here." But I'm always a big fan of then changing the file extension, too.
Dave: Changes extension, yeah.
Chris: Yeah. When you change syntax away from standard, you should change the file extension, too, which looks like they're not quite doing. But what that's doing in their world is it's kind of like blocks to me is how I'm reading this. Now you have this piece of Markdown that can be imported in JavaScript in pieces.
No longer do you have this content that's just like, "Blah, here's all the content." You can say, "Give me all the individual blocks for it, and I'll take over the responsibility of, for example, mapping over those chunks that I imported and outputting them."
For example if you wanted to do columns, that you could say, "Well, give me the first three blocks you find, and I'm going to map over them and set them in some HTML that's going to make columns." It gives you some extra ability here.
Part of me is like, "This is pretty clever. I get why people would want to do this, would want to have a Markdown file that's not just a big chunk." I keep making that barf sound, but I just mean I've dealt with this in CSS or CMS land my whole life. At some point, you just have this ball of crap that you just barf out onto the page.
Dave: Mm-hmm.
Chris: It looks like it's kind of challenging that notion in that it doesn't have to be that. You can add just enough metadata within the content that if you want to barf it all out, you can. But if you want to import little pieces of it to do something more clever with, you can.
I do connect this a little bit with the block editor that there's just enough metadata in WordPress now that you can kind of get your hands on individual blocks if you really need to. This is kind of doing the same thing. I just found it interesting.
Dave: Yeah. No, I like... It is funny when you do Markdown. I've been doing a Markdown blog for, what, ten years or something now. I don't know.
Chris: Yeah.
Dave: You're all in. There are times where you're just like, "Oh, god. I just wish I could put two images side-by-side." I wish I had a gallery component.
Chris: Yeah.
Dave: And I could just do this. It's not easy, and I wish it was. That said--
Chris: Web components.
Dave: Chris, have I told you about Web components?
[Laughter]
Chris: Have I told you about the side-by-side Web component?
Dave: Yeah.
Chris: You're going to love it.
Dave: It's called side-by-side.
Chris: Yeah.
Dave: And it fricken' costs zero dollars. It's eight kilobytes or, no, 800 bytes. You know there's stuff like that.
Chris: Yeah.
Dave: That's how I'm currently solving this problem of, like, rather than inventing a new Markdown extension, but I do like this sort of... It looks a lot like JavaScript decorators. Have you messed with decorators? I'm only saying that because I wrote one yesterday. [Laughter]
Chris: I was just reading about them.
Dave: So, yeah. [Laughter]
Chris: Did you really? Tell me about the use case first.
Dave: Okay, so the use case is two cleanly kind of modify or pass basically the next line of text through a function.
Chris: Right, without wrapping it - or something.
Dave: Without wrapping it or doing a bunch of extra work. So, where you see it... You see it in Web components a lot. Lit has @property and then you say @property. Then under that you would say appearance primary - or whatever. Brand or something like that.
Chris: Sure.
Dave: And so, when you author my-component, you would say appearance equals brand, and that's the brand button or my component, brand component. Registering a property in JavaScript in Lit is not hard. Registering it in Web components is actually kind of stupid, but it's static observed attributes, and then you have the attribute.
Then there's a static properties thing in Lit JavaScript, but if you want to just kind of use the new and cool, you just say @property. Then under there you just say the variable name, and that's now a registered property on the component.
Chris: Yeah, so decorators work on both properties and methods.
Dave: Properties, methods, classes, so you can say... There's another one in Lit that is at custom element. So, you know how you have to do the custom elements define my element points to this class, right?
Chris: Yep. Yep.
Dave: I can say at custom element, my element--
Chris: Right.
Dave: And it'll just know the next thing, the class, the next class I see is that element. It auto-infers, basically.
Chris: Yeah, it's funny how it's always the next line, right? It doesn't propagate through the whole class. It's just like the very next one, it decorates.
Dave: Yeah. The next one you find, decorate it. Yeah.
Chris: Yeah. If you want to do three properties, do I have to say @property three times in the class?
Dave: Yeah, you would have to say @property foo, and then the next line, @property bar, @property foo.
Chris: Okay. Okay. I see, in Lit here, this is in TypeScript, but it's just because whatever. Isn't it only in stage three, so it's not quite in JavaScript yet?
Dave: Yeah, it's stage three, but TypeScript, I think, ships the transpiler by default, so you can use it in TypeScript just fine. What's cool about decorators is you can stack them. So, I can have @property and @wizbang and they both kind of modify the variable. And so, I wrote one that kind of--
We were doing registering properties and Fast has two, actually. It has ATTER and observable. ATTER is for HTML attributes, like strings and bullions and numbers. And it'll be able to convert those numbers. Then observables are for JavaScript things like arrays and objects. So, if you're trying to pass a list of posts in, that would be an observable post thing.
Chris: Okay. So, Fast has a bunch of these decorators, too?
Dave: Fast has these, but what we were doing is we would have these primary variables, and we converted a bunch of this stuff to element internals. And element internals uses... We're using elementinternals.states, which is like custom states.
Instead of in our CSS doing square bracket disabled, we say :state disabled, so sort of like a like a pseudo... It's like your own custom pseudo state.
Chris: Neat!
Dave: Yeah, in Web components. So, it's really cool and it's actually normalized our CSS quite a bit so we're not just doing a whole bunch of--
Chris: Disabled is a fun one. You're like, "How do you want to do disabled? Do you want to do it from the attribute? Do you want to have a class? Do you want to have a data attribute?"
Dave: Yeah, how do you want to do that? And so, we just kind of said, "You know what? All these things we style on, we're doing in states, like in the custom element state."
Chris: Yeah. Where do you write this :state thing?
Dave: In whatever CSS. Yeah.
Chris: In CSS, but not in just regular CSS. It has to be CSS inside the Fast component or something?
Dave: Yeah, inside the Shadow DOM CSS. It'd be like :host, which is kind of to say this element.
Chris: Yep. Yep.
Dave: Then you do parentheses. Then you say :state. Then another parentheses disabled, so it's sort of like your own pseudo-element, but you have to just use the word "state" before it.
Chris: Do you use a tag template literal or not in Fast?
Dave: We do have those, yeah. Fast has HTML and CSS (just like Lit).
Chris: I bet that's what it does. Isn't it the point of a tag template literal is then it iterates over all the tokens it finds within the template literal, right?
Dave: Yeah.
Chris: It probably finds that :state one and is like, "I'm going to manipulate this into actual CSS."
Dave: Yeah, and we had to... Because elementinternals.states is not supported everywhere. Womp-womp. Or backported.
Chris: Really?! Isn't that the one that you need to make it work with forms?
Dave: It is... Well, element internals is, but this custom states is not.
Chris: Oh, I see.
Dave: Element internals is pretty well supported.
Chris: Okay.
Dave: But the states part... And so, we actually have... We inject some JavaScript into that CSS template literal, but we have some variables set up to where it's like :is(state disabled) and then we have also [state--disabled]. We have a BEM fallback for free, so that's kind of how we're doing it right now.
We could optimize that, for sure, and make it better. But it's been cool. So, we just have this big states file that's just basically all of our states.
Chris: Oh, so it's like... Yeah, that's neat. I mean that reminds me of... That's kind of the use case for decorators, too. If you have a bunch of really generically useful decorator things, it's a little bit of a point, right? It makes for cleaner-looking code. But you can also reuse it a bunch.
Dave: Yeah, and so my decorator basically sits above or sits alongside the attribute definition, the property one.
Chris: Mm-hmm.
Dave: It basically creates this on-off, toggling on and off, because if the attribute changes, you need to change. And so, typically, you'd have to do that in an attribute change callback - or something like that - which isn't fun to write. So, we had all this extra code, but--
Chris: Yeah.
Dave: I don't know if it's going to land, but it did reduce. I did eliminate 750 lines of code by just writing a decorator.
Chris: Dang! Neat. I've seen some cool ones. A decorator can also take... there are parenths at the end of the decorator.
Dave: Yeah! Yeah.
Chris: Meaning it can take stuff. I've seen one that's like, "Set up my environment." Then you pass in dev.
Dave: [Laughter]
Chris: [Laughter] Then because it's dev, then it makes a bunch of variables that are specific to dev.
Dave: Right. Yeah.
Chris: Then you can pass in prod, and it makes them all for prod. That's kind of cool. Also, a logger one, like for debugging purposes. If you make one that just spits out all the arguments that the function got and then says, "This function was called. Here are all the arguments."
Dave: Yeah.
Chris: You could just put @logger above any class method - optionally.
Dave: Yeah.
Chris: Then it logs all the crap from that function. Then you're like, "Oh, I learned what I need. I'll just remove that now."
Dave: Yep. Yeah, it's pretty cool. Kind of a hop-in. It's like a layered cake, really. You just put it on top. You don't have to put it inside. You don't have to wrap. You just put it on top and it kind of does the job.
Chris: Yeah.
Dave: Anyway, really cool.
Chris: Are you a fan? You liked it. I could see... I haven't heard any people talk crap against it, but I can kind of see how you're like, "How do you follow the rabbit trail of them?" I'm looking at some Lit code randomly that used it. I didn't really realize that they... I've never used Lit and TypeScript before.
I just wrote a Lit component the other day, and I was like, "Dang it! Now I finally get it." I kind of like it.
Dave: [Laughter]
Chris: Damn it!
Dave: Dang it!
Chris: Yeah. But I didn't use TypeScript, so I didn't use any decorators. But for example, I'm looking at one now, the sat property thing. I guess you have to import it, right?
Dave: Yeah.
Chris: Yeah.
Dave: You import the decorator just like a helper.
Chris: Just like a variable.
Dave: Yeah.
Chris: You could, in theory, if you're in VS Code here and not this weird playground, I could right-click it and say, "Show definition."
Dave: Mm-hmm. Mm-hmm.
Chris: Yeah, okay.
Dave: Yeah and get to it. And so, anyway, they're just really neat, and I'm not... I'm just kind of baby-stepping into them and don't really... You know.
Chris: Yeah, fair enough.
Dave: They seem cool because we just kind of had all this extra code, and I was kind of like, "Uh, I don't want to overwrite this thing." And then how we're defining attributes because that's an actual framework-level change. And so, I was just like, "I don't want to do that, but I could just write my own decorator and make it happen?"
Chris: I think the syntax is so cool looking that it helps.
Dave: Yeah.
Chris: Because another way to do this would be like, "Well, I'm just going to wrap every function in Dave's helper method."
Dave: Yeah.
Chris: And nobody would approve that PR. No way.
Dave: No.
Chris: You know?
Dave: Yeah.
Chris: [Laughter] But throw this little @decorator thing above it and people are like, "Oh, that's fine." [Laughter]
Dave: Well, and I kind of wonder how far you could take it. Could you do an @debounce or something like that? You know how you have to wrap a thing in a...?
Chris: Yeah. The reason I know about this is because I read a kind of in-progress post from Alex McArthur, and that was one of the use cases he put in the blog post was a debouncer.
Dave: Oh, god. That's what I want. I don't want to write a debounce wrapper ever again. I just want to say @debounce.
Chris: Right. Where is the--? What's the famous library that you import everything from?
Dave: Lodash, yeah.
Chris: Where do you get a debouncer today? Yeah, Lodash.
Dave: Yeah.
Chris: And now there's another new one that's even cooler, I guess. But where is Lodash for decorators? That's an opportunity.
Dave: Yeah, I don't know. I think there is an opportunity.
Chris: Oh, it's coming. Yeah.
Dave: Somebody, get on it. Get on it. Somebody, make a million dollars.
Chris: Right because the logger one, nobody needs to write that. Somebody, write a really nice logger one and do cool stuff in there where it logs colors and fonts and stuff. Make it awesome.
Dave: Yeah. Right, yeah. Just have fun. It's kind of fun territory. I do think it's level three. It's a little bit... a smidge risky but kind of not.
Chris: Hmm...
Dave: But you know.
Chris: It's going to look weird, weird, weird to me in JavaScript when it's there, I think.
Dave: I mean you can use it in JavaScript today. Babel, I think, will fix it. I think I've used it on CodePen before, just kind of straight in JavaScript.
Chris: Yeah, yeah. It babels away and it typescripts away. But it's headed for just JavaScript.
Dave: Yeah, which would probably help my bundle out quite a bit, too.
Chris: Because your ESM will be smarter? Yeah.
Dave: Well, yeah, because right now it's transpiling something probably doing a wrapper somewhere. But it would, in theory, not need to do that.
Chris: Right, it probably will.
Dave: Anyway, decorators are cool. But back to that Markdown thing. The custom exclamation point tag sort of seems like a decorator. Sort of like, "Wrap this in a div," or something. I think that's cool. I don't know.
Chris: You're right. Maybe it is even that simple - almost.
Dave: Yeah.
Chris: I mean I'm sure it doesn't wrap it in a div just because then you'd need to DOM parse the div to get the import. I'm sure it's slightly smarter than that.
Dave: Well, and it seems to create some sort of JavaScript object when they sort of say, "Okay, give me all the steps," or whatever it parses.
Chris: Right.
Dave: Yeah, they have a parser that parses the content.
Chris: It's a custom parser.
Dave: Yeah.
Chris: Yeah, it's a custom AST thing, surely.
Dave: That's actually pretty easy. Interesting. Okay, so they just kind of say... All right. Yeah, we write a schema, and you say this is what it has.
Chris: Yeah, import blocks from content.md.
Dave: Yeah.
Chris: But they don't call them blocks, but that's what they are. Or maybe they do call them blocks.
Dave: They do call them blocks.
Chris: Oh, nice.
Dave: Block extend, image block, blocks, blocks are the world.
Chris: Here's one you know, and I just saw this. Somebody was asking, "What do you got an 11ty site," or something, or whatever. "What do you do for images?" kind of thing.
11ty actually has a really nice image component. So, I think if people were really scratching their heads on how to do images in 11ty, I think theirs is pretty industry standard best, right?
Dave: Mm-hmm. Mm-hmm.
Chris: Kind of. And I do like the idea of, like, general advice, "You're going to use... You're going to put a post, and you're going to automate the construction of the final HTML to display that post." Strongly in favor of that. I'm also strongly in favor of put a 2,500-pixel original in there somewhere. That's usually not super get-friendly, but too bad. Whatever. Get can handle it.
Dave: Mm-hmm.
Chris: People have been doing it forever. If you're really fancy, get LFS - or something - or put the file somewhere else. But I am a fan of automating that in some way. There are other approaches though, too. I had to do this just the other day, so I thought I would recommend it. I put an R2 bucket up.
Dave: Okay.
Chris: R2 is the cheeky Cloudflare naming for their S3 compatible product. Remember when that came out? It's way cheaper than S3 because no bandwidth costs.
Dave: Right, yeah.
Chris: Which is like, how do you do that? That's insane. But okay.
Dave: Yeah.
Chris: I'll take it.
Dave: It's like $0.06 a gig or something (I read). That's probably wrong.
Chris: For storage?
Dave: But it's massively cheap compared to what you're going to do on S3.
Chris: Yeah. I would assume, now that it's been a year or something, and people are massively exploiting this, including me.
Dave: Yeah.
Chris: Thank you, Cloudflare, for this very inexpensive product. They have some very expensive products, too, as we've learned. But this is not one of them.
Dave: They like to get you. Yeah, yeah.
Chris: And also, I'm more familiar with Cloudflare's setup and how they do stuff than I am AWS. For whatever reason, I just think it's easier. [Laughter]
Dave: Yeah. Well, dude. I mean I don't know. Setting up iSam or whatever--
Chris: Yeah.
Dave: --permissions and all that stuff is a nightmare.
Chris: I could figure it out, but it's also... I'm usually in the context of CodePen, and I just stay out of that. You don't want me. CodePen is too important for me to have Chris in there mucking with the IMs.
Dave: Yeah.
Chris: Not good.
Dave: Not who you want on that.
Chris: Nah, it isn't.
Dave: If I ended up here, that's somebody--
Chris: [Laughter]
Dave: Somebody--
Chris: It's just whatever. I make mistakes. It's the thing.
Dave: Oh, no.
Chris: But an R2 bucket is a little less - I don't know - mission critical, especially for what I was doing, which is a little unrelated anyway. But you can make an R2 bucket by clicking a few buttons. Maybe in a perfect world, you're provisioning those buckets with code as infrastructure and stuff. But you know not everything in the world has to be like that. You can also just make one real quick.
With an R2 bucket, too, you can... Because this is the smartest thing Cloudflare ever did is they also have your DNS - remember. So, that means they're in control of your URLs and stuff. So, if you're like, "Uh, just give me a subdomain called, like, coolimages.daverupert.com, that's one click away on Cloudflare.
Dave: Mm-hmm.
Chris: They'll just provision that for you. You have to do that with an R2 bucket. When you make one, it's hidden by default. If you want to expose what's in that bucket, you have to do it via a URL of your own, unlike S3. They give you a weird S3 URL that you should not use publicly, I would highly recommend.
Dave: Yeah.
Chris: Because it's not CDN-backed. You have to run it through CloudFront. Wait. There's some other thing they use that's their actual CDN front to it.
Dave: Oh...
Chris: And you've got to wire them together. Yet another thing that's like, "That's why I like Cloudflare. You just don't have to deal with stupid stuff like that. It just automatically is handled."
Okay. That's the whole story. Make an R2 bucket, but they also have nice APIs for stuff like that. And they document those APIs nicely. You can write to an R2 bucket from the client with good environment keys and stuff. It's not crazy, especially if the client in this case is a highly locked-down application.
Dave: Mm-hmm.
Chris: Who cares? You could just write from the client rather than spin up a server to write to it - or whatever. I was trying to do this. I'm like, "This is so simple. Why not just do it this way?"
I needed a machine that was like, "Hey, you're writing something and you need to put an image there." You should be able to drag an image on that's 2,500 pixels wide. That's the Chris Coyier rule. But the giant original in.
Dave: Mm-hmm.
Chris: Do not size it and optimize it on your computer first. I want the original one somewhere in the cloud for me to use (or in the repo or something). Then you optimize it later. And so, I used this thing called Uppy, uppy.io.
Dave: Okay. Oh, okay.
Chris: It's a sleek, modular, open-source, JavaScript uploader. And it's all S3'd up. But because R2 is S3, all these S3 things. They just use the same exact API. It just works fine for R2 also. And I just had it squooshed.
I just saw this week they released 4.0. I didn't get to use 4.0. I should have waited.
Dave: Oh.
Chris: Not that I really care, really. It still does the job. But it's the kind of thing where NPM installing something was very good. I don't want to write this from scratch, not even a little bit, Dave.
Dave: No.
Chris: I want to NPM install a thing, and then I put the component on the page, and it makes a little dotted line thing, and I drag files onto it, and it handles all of the events, error handling, and everything. I just give it some light configuration that tells it what R2 bucket to put some crap in. And it just does it. And it really was not a hard job at all.
Dave: Well--
Chris: I'm just mentioning it, and it's like, "Well, that was cool. Big fan."
Dave: Okay. No, I'll have to check it out. I've been using a solution by Jim Nielsen who basically figured out how to trigger a Netlify folder deploy sync from an iOS shortcut - or whatever - a Mac OS shortcut.
Chris: Yeah, I saw. I saw his post on that.
Dave: Yeah. He put it into Raycast.
Chris: Because there's a built-in thing that just says, "Post a URL."
Dave: So, he just goes, "Bloop," and so I don't know. That has kind of been working for me, but very interesting. I don't know. I'm kind of like that's... I think I tend to--
Chris: That's your OS, though, not like a website that you could share with your coworkers or whatever. Although, I guess you could give them the shortcut.
Dave: Yeah, I could just give them the shortcut. But I'd have to give them the key to my Netlify build - or whatever. But you know.
Chris: Yeah.
Dave: We could make that happen. But it only does the assets, so everything else is controlled by the continuous integration to GitHub and stuff like that. So, I basically just punch a folder to the cloud, and it all just syncs up. And so, that's been really cool for me.
But I am sizing my images to 1,200 or 600 pix. Smaller images go to 600, and it's really arbitrary. But I wish it could be more--
Chris: I see.
Dave: Netlify does have a new image optimization API, so it could maybe send up the whole big daddy up there.
Chris: Yeah, that's what I didn't even mention about R2 is that built into that is that you can just put a bunch of URL params after the image (once it's up there) that says, "I just want this at 1,000 pixels, not the 2,500 I just uploaded." Oh, and by the way, do the thing that Cloudinary kind of innovated, the format auto thing. Whatever is requesting this, give it in the best format you can for that. Meaning, AVIF or WebP or whatever, which I think is great. I know people like to poo-poo on that, like, "I hate WebPs." I don't care what you like.
Dave: Yeah.
Chris: I want the website to be fast.
Dave: I did hit an issue with R2 on Luro. I do want to call to say--
Chris: Yeah.
Dave: It's S3-like. And it's S3 up until the point you want to have signed assets. So, if you wanted to verify that this user can see this asset - or whatever.
Chris: Ooh...
Dave: Or the person that uploaded it, that's where it gets tricky.
Chris: Okay, yeah.
Dave: Yeah. R2 does not have that feature yet, the last time I tried.
Chris: Yeah. My goal here was a very public URL with images.
Dave: Yeah, so if you were doing your -- well, whatever. Even if you are doing your little, "Download our whitepaper," website thing, [laughter] that would probably be fine if somebody gets the whitepaper link. But then if somebody gets mad that they got that without clicking a link or whatever.
Chris: That's funny that the real S3 has that nailed because of course they do because I think that's the Modus operandi of AWS is get the permissions working really well because permissions suck to move. So, when you... Nobody ever will. If you have your permissions on an S3 bucket set up perfectly that's handling the security of assets, you're just a lifetime customer. [Laughter]
Dave: Yeah. Why would you--? Yeah, and you have customer assets. I mean we actually built an asset uploader for Luro. Three or four times, we've built it.
Chris: Mm-hmm.
Dave: But we never opened it up because it was just like, "Cool. Now we have their assets." That's a good "hook" - or whatever - to get a hook into somebody. But it was just kind of like, "Man, it adds this whole level of responsibility, customer responsibility are we sure we can deliver on?" that was kind of like, "Once we have your stuff, can they get it out? Can we make sure it is not accessible from outside?" Stuff like that.
Chris: Mm-hmm.
Dave: It was just this big, like, "Ah, man. We have to do a whole security loop here on it to make sure it's done correctly." Yeah.
Chris: Yeah, we're working on a little bit of that in our CodePen 2.0 thing. We're rounding out some. You know when you upload assets on CodePen, those are just public. We don't face the same thing that you faced at Luro.
Dave: Well, they kind of need to be for, like--
Chris: Yeah, a website that you're making.
Dave: "Hey, I'm making a website." Yeah. [Laughter]
Chris: Right. But the Pens themselves, we do have privacy stuff on. For a long time, we had private Pens. We're kind of extending that ability to have more types of privacy. For example, the way that privacy works now is just has a very long, obscure URL that can't be guessed, and we hide it from various areas of the site. That's served us well for a while, but there have been requests for more than that, like, "I want to put a password on it," or "I want it to be really private, like nobody can see this unless I literally type in their email and invite them to it and they accept that invite."
Dave: Yeah.
Chris: That's very private.
Dave: Yeah, yeah. Well, you'll have a long URL. But then y'all kind of went to, like, you had the base URL plus kind of like a token key or--
Chris: That's exactly what it was. We moved to that, so they're all that way now.
Dave: Yeah.
Chris: So, the slug of the Pen (we sometimes call it). Although, I don't think we call that in our docs a slug, or maybe we do. I don't know. That never changes.
Dave: Mm-hmm.
Chris: It's like codepen.io/pen/daverupert/xyl12.
Dave: Mm-hmm.
Chris: Cool. That's the public slug for it. It never changes. But then if you make it private, everything about that stays. Then it's like / and then a token.
Dave: Which is kind of the key to get in.
Chris: That's what gives you... grants you the access.
Dave: Yeah.
Chris: Yeah, exactly. That's the key to get in. It has a bunch of nice properties, as a structure--
Dave: Yeah.
Chris: --because then if it becomes un-private again, the public URL is still functional.
Dave: Mm-hmm.
Chris: The private URL still functions as the public URL, too.
Dave: Yeah.
Chris: Like if you go the other direction, that's awfully handy.
Dave: Mm-hmm.
Chris: Then having a token is interesting because it's like, "Yes, you could put the token in the URL, but you can grant a token any number of other ways."
Dave: Mm-hmm.
Chris: A password could then grant that token or a number of other ways to grant that token opens up more privacy abilities.
Dave: Mm-hmm. Yeah. Nothing is perfect in that regard. But I do like that kind of lock and key mechanism. You went from just the token to the slug plus token is how you get into a Pen. It's whatever.
Chris: Yeah.
Dave: But the entropy for the number of letters and numbers you have to get right goes up.
Chris: It does.
Dave: That's nicer but it's not bulletproof in terms of only authorized users can view this Pen.
Chris: A perfect world, you don't even have slugs either, man. It's just UUIDs all the way down.
Dave: Yeah.
Chris: UUIDs are the best.
Dave: Yeah.
Chris: The ability to be generated on the client and have them be guaranteed unique and syncable later is so cool. They're just not nice to look at ever.
Dave: We did UUIDs for Luro. [Laughter]
Chris: Yeah.
Dave: Man, Luro is just every responsible decision that didn't work out for us anyway. [Laughter]
[Laughter]
Dave: But it was... We did use UUIDs, and it's great because it's probably never going to have a collision, unless a literal miracle happened, like, we will not have a collision.
Chris: Nah, it's not going to happen. Even if it does, you're like, "Oh, I'll just grant you a new one quick. Big deal."
Dave: Yeah. Okay. Could you just delete it and recreate it? Thank you.
Chris: Yeah.
Dave: Or I'll delete it in the database and recreate it automatically. Those are cool, but the UUIDs are just like so cool, but the slugs are just so handy and feel very clean.
Dave: I did read a post from NanoID. It's like a slug generator, a small ID generator.
Chris: I have not heard this of this.
Dave: They were saying... It's kind of like what you're doing.
Chris: Yeah.
Dave: But it's like a library that abstracts it. But somebody wrote a post, and it was something about default NanoIDs maybe like eight - or something like that. But somebody was saying you may think six is cool, but if you move it up to eight digits, you get billions of more combinations.
Chris: Yeah, yeah.
Dave: If you think you're going to have more than a billion records - or whatever - you're fine until that point. You're not going to hit billions ever.
Chris: I almost wish we could, as the industry just stopped with, like, it's already gibberish. Just go full UUID. Screw it.
Dave: Yeah, yeah. Yeah or URLs would automatically... [Laughter] URL bars, location bars, would automatically shorten UUIDs.
Chris: Yeah, if it saw that, make it--
Dave: ABCDEF and then ... Yeah.
Chris: Gibberish, yeah.
Dave: Yeah.
Chris: That would be great.
Dave: Sort of like... Yeah.
Chris: And stopping penalized for long URLs, like -- cough, cough -- Threads. I didn't realize they were. I remember when Twitter made that change back in the day and it was largely clapped at that the URL length did not count against the length of the tweet.
Dave: Mm-hmm.
Chris: It was so silly that it would, like who cares.
Dave: Yeah.
Chris: But it still does on Threads. It's like, "Oh, geez. Does nobody learn anything?!"
Dave: [Laughter]
Chris: Gosh!
Dave: Just don't! Everyone is picking bad decisions over again.
Chris: [Laughter]
Dave: Hey, sometimes you've got to let people make bad decisions. But then you know what they're going to do? They're going to launch it as a big feature. Somebody is spending eight months of their life to fix this problem. [Laughter] And they're going to whole RegEx. They're going to have to do a whole RegEx, and it's just going to be--
Chris: [Laughter] Yeah.
Dave: You know some database nerd was like, "I'm going to make this 256. Done." [Laughter] You know?
[Laughter]
Chris: Yep. Yeah. Everybody has a little bit of different opinions on how things should go down. That's for sure.
Dave: It will only ever be 256 characters.
Chris: Yeah. Obviously, the world would be better if just everyone would just quickly run everything by me first.
Dave: Yeah.
Chris: And I'll point in the right direction.
Dave: I can be the bottleneck for the world.
Chris: [Laughter] Sure.
Dave: That would--
Chris: Phrase everything into--
Dave: --suit me and my ADD nicely.
[Laughter]
Dave: I need some bulleted lists. That would actually up the threshold of this working.
Chris: Mm-hmm. It is kind of how I imagine, like for high governmental jobs, up to president. You wake up, and there's some stuff at your desk. You're like, "A or B, sir?" You're like, "B! A!"
Dave: [Laughter]
Chris: A? B.
Dave: [Laughter] You just say, "B!" and see if everyone's face goes full pale.
Chris: Yeah, right.
Dave: Then you go, "Just kidding!"
Chris: "Ah... That was an A."
Dave: "A!"
[Laughter]
Dave: This fits into our political platform. This is great. Yeah, "A or B, sir?"
Chris: Yeah.
Dave: A?
Chris: Yeah. [Laughter] You flip around in your really expensive chair and flip a coin.
Dave: [Laughter]
Chris: Heads. I mean, B.
Dave: B.
Chris: [Laughter]
Dave: That's great.
Chris: Yeah, nice.
Dave: Add a D20, and I'm like, [lip trill] "$6 billion for that."
[Laughter]
Dave: I'm just rolling dice. Anyway--
Chris: [Laughter] Yeah. We agree with this proposal for... roll-roll-roll... $14 million.
Dave: I'd be happy. Yes.
Chris: I'm sure there's some governmental RPGs out there that are just exactly like that.
Dave: Ooh... That would be up my alley. We should... I'll work on that. We'll work on it. Bureaucratic RPGs, we'll make it happen.
Chris: [Laughter] Okay.
Dave: All right. Well, I don't know where we're at. Probably at time here, so maybe we'll wrap it up, Chris. Sound good?
Chris: Sure. I didn't dig out any questions this time. We've got to fill the bucket again.
Dave: We'll fill the bucket. All right. 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.
Join us in the D-d-d-d-discord. That's where things are happening, patreon.com/shoptalkshow. And, yeah, I've got to two doggos. Fun.
Chris: Yay! Go play with your dogs.
Dave: Yeah.
Chris: ShopTalkShow.com.
Dave: They heard me do the outro, and they got all excited. [Laughter]
[Puppies barking]