Dave RupertTop Front-end Bloggersread at source
Bad at email
Good friends and family know this, but I am bad at email. Like chronically bad. If you have tried to email me, you know this too and you either never got a reply or received one 9-months later starting with the phrase, “Sorry.” I am truly sorry… but also I’m not. It’s a function of who I am. I was never great at email, but in a slower time before social media it raptured my attention. I would habitually refresh my inbox hoping for a long range communication from a friend or stranger. When I was homesick in Japan, a good email (or AIM chat) could turn my day around. I have fond memories of email and its unparalleled ability to connect me to other people through a single address. It of course would never be as familiar or intimate as scribing a hand-written calligraphic letter with a big feather pen, but it proved a fine substitute. At Paravel, I noticed that emails had a propensity to crash into my day with some new urgent task. A ding, a red badge, a bouncing icon in the dock . Could be a client or my accountant; something that required me to stop what I was doing and switch over to their need. Frank once described emails as (paraphrasing) “a to-do you send to someone else” and that resonated in my soul. For someone with an attention regulation disorder, intermittent distractions like email (or chats) can be devastating to staying focused on the work I actually need to finish. Email being disruptive to your productivity must be a pretty common phenomenon because nearly all the productivity books out there –and I’ve read them all– attempt to “solve” productivity by turning off your notifications and checking your email less. I must have really taken this advice to heart because I almost never check my email anymore. After a long day of expending dopamine on work, the last thing I want to do at home is check my personal email and receive more to-dos from strangers. I tend to check my personal email now once a week… or less. Inbox Zero is a fool’s errand for me, but I try to keep my work email groomed and as close to zero as possible. Part of that was because I started with a blank slate and could actually succeed. Each morning I make an analog to-do for myself to check my work email. I set up some priority inboxing and auto-deletes for org-level notifications. Importantly, work email stays sequestered to my work laptop. But despite the success at work, I’m sitting at nearly 500+ unreads in my personal Inbox. It doesn’t feel great and feels like a monumental task now. Since starting at Large Company I know of at least two conferences that I’ve missed out on speaking at because I’m so bad at email (sorry, PPK ). If you’ve ever wondered why I don’t have a contact form on my site or do that cool “email me, let’s be friends” link in my RSS footer, this is why… because I feel shame that I will disappoint you . There’s also the diminishing utility of email. As with phone calls and texts, the amount of spam and noise has ruined the service. It’s a needle in a haystack to find the one good email that actually deserves your attention. Just because we participated in online commerce once doesn’t mean you get the liberty to email me for the rest of my life. I wish there were more laws about this. When did a ten drip email campaign for every online transaction become the standard? You bought an item → Item is getting ready to ship → Want another item? → Item shipped → Need more items? → Item out for delivery → The holidays are coming up → Package delivered → Let us know how we did → Mother’s day coming up → Father’s day coming up → Step-father’s day coming up → etc… It’s entirely too much. And the grifter slop… oh, my God the grifter slop! Hey Dave, I came across your now-defunct company Luro. Design systems is an interesting space. Would you like to increase Luro users by six billion percent through spam email blasts? We can help. Chad, CEO I will never reply to this and– Dave, I thought I’d follow up in case you missed the last email. I’m a fucking robot obviously but we can have a conversation about this. Chad, CEO Grifters, spend six minutes crafting a real email and I promise you it will convert better. Or have an actual fucking product that is so good I hear about it from other people. You are one of five hundred anonymous Chads in my inbox, this pickup line doesn’t work anymore. Along those lines… Democrats, sending text messages doesn’t work anymore. I got AOC, Nancy Pelosi, Beto, and even Raphael Warnock’s grandma texting me every other day and it makes you look broke and desperate like a friend I’m gonna need to bail out of jail again. Capture me with ideas. That’s how I feel about email. And text messages. And phone calls. And social media. Once upon a time it had utility, now it’s all noise and grifter slop demanding our attention and giving us to-dos. Be hotter, be smarter, be more liberal, be more productive, be more, be more, be more. Oh, and I underpaid my taxes and owe the government $5400. That was the email I missed.
Dave RupertTop Front-end Bloggersread at source
Recursion is DEAD and HTML killed it
There’s a new web platform feature flighting in Chromium called Declarative Partial Updates . This proposal adds a new element which enables out-of-order streaming of content. Rather than the traditional way of building the HTML you need on the server, or sending JSON down to a JavaScript framework, you can staple HTML onto the initial response and use the syntax to inject that content into the placeholder. A simple example of this would be: ` syntax for ` ` becuase the HTML highlighter likes that better. Either syntax works. --> Hello world First post The late-arriving template will auto-magically get slurped up into the placeholder slot. Unlike the element though, once you use it’s done. You burned that card and it doesn’t work a second time. If you want to chain more than one post together and create a feed of posts, you can repeat the placeholder with the same at the bottom of the appended item. The essence of the feature is that you can open a server connection, never close it, and keep pumping more HTML onto the page. The element does the heavy lifting of taking that new HTML and putting it in the right place. There’s lots of potential here (e.g. my beloved HTML Imports?) but I think the canonical use case would be how an LLM chatbot could stream in content that generates at different speeds: Markdown formatted text goes here... Hi, sorry I took so long! Here's a whole-ass application. That will create some other problems like FOUC and LCP hurk-jerk you will have to manage –and that’s for another post– but lazy-loaded HTML rendering adds an interesting capability to the web platform. It took me a couple walks around the neighborhood to understand how I could potentially use and out-of-order rendering on a normal workday. Rendering a threaded response The rule of thumb I came up with is that might be a pattern to reach for any time where you want important content to show up first and then you kind of don’t care when other content shows up below the fold. A post with nested replies A product with reviews that show reviewer profile cards A gigantic pull request with nested comment threads A book with authors that have related books You get the gist, a classic relational data situation. The example I’m going to go with is a nested reply thread like a Reddit thread or Bluesky post. Building a nested thread of replies isn’t rocket science, but you have to do a lot of for-looping to first build up the tree of posts you’re going to render… → Fetch all root posts in thread → Loop through posts → Fetch replies for each root post → Loop through root post replies → Fetch replies for each reply → Add nested reply to parent reply → Add parent replies to root posts → Walk tree → Send HTML for all posts Assuming the data tree gets stitched together by the database or our server backend, your first attempt at rendering nested content will probably be the most literal approach: create a for-loop for each level of content… // pseudo-code const postsTree = postsDb.find(p => p.thread_id === 'abc123'); postsTree.map(post => html` ${post.username} ${post.body} ${post.replies?.map(reply => html` ${reply.username} ${reply.body} ${reply.replies?.map(nestedReply => html` ${nestedReply.username} ${nestedReply.body} ` ).join('')} ` ).join('')} ` ).join('') An obvious limitation here is that we’ve hard-coded ourselves to three levels of nesting. Adding more nesting would require backend and frontend work and break the classic “ Rule of three ” programming rule. Hard-coding your nesting is the “dumb way” to render nested content… but also the normal way. Making this a bit smarter and more infinite Philosophically-speaking, UI should be agnostic to the content it renders. If nested content is a possibility, a content renderer should theoretically support infinite nested content. Practically-speaking, most of us have learned that it doesn’t matter if the Product Manager’s Jira ticket says “The max is two nested items”, there will always be at least three. The smarter way to render this is to use recursion because nested for-loops are BAD and you are DUMB if you use them. But if you obfuscate the nesting into a function that calls itself, then that’s ✨recursion, baby✨ and you’re a GENIUS programmer. Jokes aside, recursion will clean up the template code and simplify getting HTML on the page. → Fetch all posts in thread → Loop through posts → Build a map → Loop through map → Build a tree → Walk tree → Send HTML for all posts Before we grabbed all posts, then all replies, then all nested replies. Walking that tree is a bit slow. We can make that faster by fetching a flat list of posts in the thread and then assemble a tree. You can do this with some crafty LEFT JOIN SQL statements but its generally easier on the server. buildPostTree : building a nested tree from a flat list of posts function buildPostTree(posts) { const postsById = new Map() // Loop through posts to build map for (const post of posts) { postsById.set(post.id, { ...post, replies: [], }) } const roots = [] // Loop through map values to build tree for (const post of postsById.values()) { // Parentless posts must be roots if (!post.parent_id) { roots.push(post); continue; } // Find this child's parents! const parent = postsById.get(post.parent_id) // Add child post to parent's replies parent.replies.push(post) } return roots } Then instead of hard-coding nested for-loops, we can recursively walk the tree to render the HTML for the posts. // pseudo-code const posts = postsDb.find(p => p.thread_id === 'abc123'); const postsTree = buildPostsTree(posts); const renderPost = (post) => html` ${post.username} ${post.body} ${ // Loop through post replies and render the HTML post.replies?.map(reply => renderPost(reply)).join('') } `; postsTree.map(post => renderPost(post)).join('') That’s a more robust and understandable way to handle infinitely nested content. Our code is more durable to future changes. Need more or less nesting? Sure, we don’t care. That’s a data concern not a component rendering concern. Even though we’ve simplified and reduced lines of code, both strategies have the same bottleneck: You need to build the entire tree before you can render the HTML . The user waits for us to stitch the data together and the user waits for us to walk the tree and render synchronously. That’s two render blocking operations to put a thread onto the page. The element gives us another path… Infinitely nested threads with out-of-order HTML First we setup our page to receive the HTML, like we did at the top of this post… And our rendering strategy gets a lot more simple… → Get all posts in thread → Loop through posts → Send HTML for post And code looks a little something like this… /* get all root posts */ const posts = postsDb.find(p => p.thread_id === 'abc123'); const renderPost = (post) => html` ${post.username} ${post.body} ` /* loop through posts */ posts.forEach(post => main.appendHTML(renderPost(post)) ); No tree building is happening whatsoever on the server. The microsecond after we generate the template in the middle of the for-loop we can stream/send the HTML directly to the client and let the page assemble itself. No recursion, no pre-assembly required. It all snap-fits together without us imperatively injecting content. A brief demo in CodePen. I recommend reading the HTML first, imagine that’s the order it streamed in, then viewing the assembled tree of nested comments. See the Pen LEFT JOIN with HTML by Dave Rupert ( @davatron5000 ) on CodePen . It’s worth noting a thread of 10K posts will crush the browser, so you’ll still have to load responsibly and think about a “Show more comments” buttons. I’m hand-waving the details here, but this gets easier too because your loadMoreComments() function doesn’t need to know anything about where to append those new comments, it can just staple them to the bottom of the page. I’m excited about the possibilities here of removing ancillary content or UI from the critical rendering path. I’m interested in HTML’s newfound ability to self-assemble content and relationships in a declarative way. And maybe… he says with a glimmer of hope in his eyes… HTML Imports will be resurrected and there will be peace on Earth.
Dave RupertTop Front-end Bloggersread at source
Tupo
“Triple Threat” Josh Collinsworth has a brand new daily puzzler game called Tupo . On it’s surface it looks like a simplified 4×4 Sudoku block but with colored squares. That is the foundation for a simple ruleset: Every row and column contains each number exactly once Every row and column contains each color exactly once Every number + color combination appears exactly once Mini Sudoku with colors. Got it. How hard could it be? Hahahahahahahahahaha. You naive bastard. It’s deceptively difficult and then right when you get the hang of it, it jumps to a 5×5 grid on Friday. Simple yet complex is a difficult design concept to achieve and Josh did a great job. I got a sneak peek of the game a couple weeks ago and it quickly earned the top spot in my daily puzzle playlist. Before Inkwell Games , before Clues by Sam , I play Tupo. If you’re a fan of daily puzzlers, try Tupo. It won’t disappoint. → Play Tupo
Dave RupertTop Front-end Bloggersread at source
How Many Dudes?
If I’ve been missing from the internet it’s because I’ve been spending my evenings grinding on a game from Butterscotch Shenanigans called How Many Dudes? Hot off the heels of the release of their massive multi-year sequel project Crashlands 2 , the family+ operation at BS turned a little game-jam style prototype into a “Featured on the Apple App Store” game in under a year. How Many Dudes? seeks to solve ancient internet riddles like: How many toddlers could you take on in a fight? Would you rather fight 100 duck-sized horses or 1 horse-sized duck? Who would win: 100 men vs 1 gorilla? The premise is pretty simple. Build a team of fighters with different abilities and power-ups and enter the arena to fight different enemies and bosses. For me this game has it all: Balatro -level of power creep Impactful decision making So much economies Great art style and hilarious copy A proper amount of gore Enjoyable difficulty curve RNG, but not too much Those characteristics all lead toward a game with great replay-ability. Mastering lower tiers unlocks characters and power-ups in higher tiers creating a perfect “loopback” system where I want to 100% the lower tiers before moving on. A full round takes around 20 minutes but you can stop and start it effortlessly like a good casual game. I could go on forever about the power stacking economies in this game but it’s the little bits that impress me the most. Like how the characters don’t have proper walk animations and instead rotate and bob as they shimmy their way across the screen like a paper cutout on a popsicle stick. This may seem like corner cutting but on a screen with 1000+ combatants, particle effects, blood splatter, and explosions… I bet limiting characters to a single low-frame combat animation cycle is a critical ingredient for good performance. Then there’s the characters themselves. With nods to other game franchises, movie tropes, and popular (but litigious) dungeon crawling tabletop systems, the game about memes builds on cultural memes to add depth. And even though these characters don’t talk, interact, and you don’t technically control them, the hilarious copy hidden within the power-ups and item pick-ups gives a game with a lot of characters a ton of character. I will confess some bias; Butterscotch Shenanigans is one of my favorite gamedev studios and I’m internet friends with their CEO Adam Coster ( he’s even been on my podcast ). It didn’t start like that. Before knowing Adam, I watched their GDC talks and mentioned their studio in a post I wrote about a GMTK video essay . I like their approach to prototyping a lot but what I like most about Butterscotch Shenanigans is that they have that old-world #indieweb ethos of sharing what you know . They have a blog for detailing their process , a podcast, and offer open source tools for GameMaker on GitHub. If I ever pivoted to gamedev, I’d want to build a studio like theirs. A big inspiration for me. Anyways, that’s my review of How Many Dudes? If you like deck-building rougelikes or button mashing power fantasies, I think you’ll like this game. And here’s my Dude tier list in case you’re wondering…