More from Good Enough
Back in March we added Album Search in Album Whale. This was a very nice update because it meant you no longer had to first grab an album share link from a music service (e.g. Spotify, Apple Music, Bandcamp, etc) in order to save an album to a list. You can just head straight to Album Whale and do it all there. Today we’ve added another feature to Album Whale with a similar goal in mind, this time on the other end of the spectrum: now you can listen to an album right in Album Whale! No more needing to jump over to a music service first to do so. Hover over any album artwork and you’ll find a green ▶︎ play button, which opens a dialog containing music service embeds for that album, like this: While amazing for discovering new music quickly, I’ve also found it very useful for my own To Try list. Now this entire flow can happen within Album Whale after hearing of a new album I want to listen to at some point: Add it to my To Try list in Album Whale using search Listen to it right in Album Whale If I like it, copy it to another list in Album Whale (oh ya, this is another small feature we added!) A couple more notes: For albums added to Album Whale using a Bandcamp share link, those green play buttons just link back to Bandcamp. At least for Spotify, I found I needed to be logged into Spotify on the web to have their embeds in Album Whale play the whole album, not just a preview. This might also be true for other music services. We hope you discover many great new tunes this summer! Listen on Album Whale Reply by email
As we say repeatedly on its homepage, Pika is designed to help you focus on writing, not tinkering with your blog design. To that end Pika purposely doesn’t support complex liquid templating or other such code customization. However, today I thought I’d share how crazy powerful custom CSS can be in some circumstances where you want to augment Pika. Pika has a “Scroll to top” button floating in the bottom right of the Dashboard. Some people have asked if they can have this on their own blog. This may or may not be a feature we build right into Pika one day, but in the meantime, you can get close to a full implementation yourself in Pika. Here’s how: First, head to Pika’s Settings, scroll down to Site footer and add this to the bottom of the site footer editor: ↑ — This is an up arrow that is linking to #top, which will now be on every page of your Pika site. After saving that, head to Settings > Theme, scroll down to Additional options and check “Add custom CSS”. In the resulting code editor that appears, let’s add some CSS that targets that specific link, and designs it as a floating button: .user-site-footer a[href="#top"] { /* Float this in the bottom right of the page */ position: fixed; bottom: var(--space-S); right: var(--space-S); z-index: 1; /* Centered styling */ display: grid; place-content: center; /* Button styling with built-in Pika style variables */ background-color: var(--color-primary); border-radius: var(--radius-round); color: var(--color-txt-on-primary); font-family: var(--font-family); font-size: var(--font-L); height: var(--space-XL); width: var(--space-XL); text-decoration: none; } Depending on if you have other custom CSS, you might need to add !important to any of the style lines above. That’s it! Now you have a floating back to top button on your Pika blog, designed to look like other buttons on your site. But if you want to go the extra mile, here’s how you would make it so the back to top button only shows up after scrolling a bit (since it’s not so useful when you’re already at the top of the page): .user-site-footer a[href="#top"] { /* Float this in the bottom right of the page */ position: fixed; bottom: var(--space-S); right: var(--space-S); z-index: 1; /* Centered styling */ display: grid; place-content: center; /* Button styling with built-in Pika style variables */ background-color: var(--color-primary); border-radius: var(--radius-round); color: var(--color-txt-on-primary); font-family: var(--font-family); font-size: var(--font-L); height: var(--space-XL); width: var(--space-XL); text-decoration: none; /* Hide it until a little bit of page scroll */ transition: 200ms; opacity: 0; pointer-events: none; } .scrolled-a-bit .user-site-footer a[href="#top"] { opacity: 1; pointer-events: all; } Feel free to make this button your own, like adding a box-shadow (since it’s floating), or making it a rounded square, or whatever you’d like. If you’re really clever, you could probably get the button to say “Scroll to top” when you hover on it, though that would take quite a bit more HTML and CSS — but it’s possible! I leave that as an exercise for you. Reply by email
Did you know that we, Good Enough, make a little website called Album Whale where you can make beautiful lists of albums to share with your friends, the world, or just yourself? It’s true! We haven’t made an update there in quite some time (it’s pretty good enough as-is), but I was inspired this week. Adding an album to a list previously required you to first grab an album share link from a music service of your choice (e.g. Spotify, Apple Music, Bandcamp, etc). This has always been a bit cumbersome. I’m excited to share you can now just search for an album right away in Album Whale! Like this: 🔍 💿 🎉 No need to first go somewhere else, you can jump right to Album Whale when you want to save an album to a list. I’ve found this really made my private “To Try” list more useful. Behind-the-scenes we’re using MusicFetch, which supports most of the big music services. We aren’t sure, but there may be some albums that aren’t found via search? If so you can still paste in a link as you always could. Reply by email
Update 12/03/2025: Yay.Boo is staying alive! 🙌 Keep your eyes peeled to Yay.Boo for updates. We have built a lot of good products here at Good Enough. Whether you’re sharing an inbox with your team or avoiding social media with a blog, we’ve got you covered. Unfortunately, there are some products that, while very nice, have not had our attention for a long time. Two of those products are Yay.Boo and Ponder. Ponder, our take on small forum software, was one of the first things we built as a collective. Even today, it works really well for a small group of polite folks to talk about a shared interest. Unfortunately, not many small groups found Ponder, and our team hasn’t had the bandwidth to continue improving the software. Yay.Boo is a delightful tool with which to quickly throw some HTML online. On top of that, it has always been a playground to push the envelopes on just what a product homepage could look like. Unlike Ponder, Yay.Boo is even getting a decent amount of use. If you look closely you may see a shared risk that each of these products pose. To run both Yay.Boo and Ponder responsibly, a fair amount of time must be spent in moderation. Every Yay.Boo site update needs to be checked. We also don’t want any nasty content to end up hosted on Ponder, which is even trickier to moderate since the groups are private. Leaving those services online and not paying attention to them is something that we do not feel comfortable doing. Since our small team is not focused on these products, we need to do the responsible thing and move on from them. So, with heavy hearts, we’re going to shut down Ponder and Yay.Boo (see above note 👆). Signups are already turned off. On December 3rd we’ll delete all user data and flip the power to off. To those of you still using Yay.Boo and Ponder, we’re sorry. There are some good alternatives to Yay.Boo in: tiiny.host, Netlify Drop, and static.app. Ponder is a different beast and, sadly, we do not know of many products that fit a similar mold. Liminal is the only one we’ve come across that might be close. To anyone who was gracious enough to pay for a Yay.Boo account, double thank you! We have canceled all accounts and you won’t be billed again. If you have any questions about the shutdown or your data, please also don’t hesitate to get in touch. While it stinks to have to send this message, we hope you’ll understand that a lot of thought went into this decision. Through these years at Good Enough we’ve discovered which products really excite us; properly saying goodbye to Ponder and Yay.Boo will allow us to focus on those products instead. A final note: if you’re reading this and thinking that you’d be just the person to take on either of these softwares, do let us know. We haven’t 100% closed the door to passing these products on to a person or team that could care for them, and we’d be happy to listen to your pitch. Thank you and keep keeping the web weird!
More in technology
Here are the Vim forks I know about: Neovim: “a Vim-based text editor engineered for extensibility and usability, to encourage new applications and contributions.” Vim Classic: “a stable, long-term support fork of the ubiquitous text editor, maintained without the assistance of generative AI tools.” EVi: “a hard-fork of Vim v9.1.0 (Jan 2024) before AI was used in the project.” MacVim: “a macOS version of the Vim text editor, providing a graphical user interface for Vim, while tightly integrating with macOS and providing features specific to the platform.” This list doesn’t include Vim-inspired editors like Kakoune, Vim-style plugins for other editors like IdeaVim, or Vim “distributions” like LazyVim. Contact me if you know of other forks!
Thought I’d make a quick blog post about some weird behavior I’m seeing on iOS 27 and Dynamic Island live activities, that I initially thought was restricted to the Duo, but turns out affects everything. Yay. Maybe this post can save some others some time, and hopefully Apple can fix it. Long story short, ever since iOS 16.1 introduced “Live Activities”, where we can display handy little widgets in the Dynamic Island, there’s been a bunch of different configurations for showing things up there: something on the left, the right, when expanded, when two apps are showing things and yours gets a bit smaller, and on the lock screen. One interesting one is if you only need to show a small indicator in the Dynamic Island (screen recording indicator, electric vehicle charging status, etc.) you can do so, which just shows on the left on older iPhones, and on the Duo a single one below the camera cutout. Apple describes how to do this in their developer documentation (emphasis added): If you can’t find a solution to display views for leading and trailing compact presentations with limited width, provide a compact leading presentation that best represents your Live Activity, such as the view you use for the minimal presentation, and an empty view for the trailing compact presentation. The system hides the empty trailing view automatically. Unfortunately, in iOS 27 (and 27.1, and 27.2), this only half works, due to a new addition in iOS 27: isDynamicIslandLimitedInWidth. It’s a variable that basically tells you if the Dynamic Island is even narrower than normal, presumably because they knew the Duo was coming with its vertical Dynamic Island, but also because as of iOS 27, Live Activities now show in the Dynamic Island when the device is in landscape too. So it’s handy to know if you have a bit less space than usual. In this new narrow view however, it seems like they didn’t test all the configurations thoroughly, as if you provide EmptyView for that trailing compact presentation, it’s not empty at all, and in fact enforces a quite large minimum size so the Dynamic Island looks hilariously tall. In other words: The system hides the empty trailing view automatically Left: what it should look like. Right: reality You can imagine this is especially rough on the Duo, where navigation buttons, toolbar buttons, tab bar buttons, etc. are all competing for precious space on the right. But that’s only half true. It displays perfectly fine (hiding it) when isDynamicIslandLimitedInWidth is false (AKA how it always used to be), but in the new cases where it can be true, like the Duo and iPhones in landscape, that’s when it breaks. So tl;dr: In iOS 27, Dynamic Island Live Activities are improperly sized if you use the listed EmptyView configuration and isDynamicIslandLimitedInWidth is true. This makes it especially bad on the Duo, womp womp. Complications Now, you might be saying “That’s weird, I haven’t seen that behavior! This is nothing but hogwash!”. But it is not hogwash, just a little complicated. There’s basically three categories of apps. Apple apps Maps, for instance, displays this single, small Dynamic Island live activity perfectly well and sized, and has been beautifully compiled with the iOS 27.2 SDK. By all accounts it should show this bug, too! Apple apps don’t have to use the same APIs as us normies, so funnily enough through really cool tools like REA we can disassemble the iOS Simulator and see that it’s in fact true, the Apple Maps live activity just completely bypasses WidgetKit and instead just renders plain UIKit through MapsBannerViewController which shows the cute little cyan widget through a ManeuverAccessoryView and then for the hidden trailing view simply returns a DistanceAccessoryView with CGSizeZero, which iOS actually respects, unlike our paltry SwiftUI EmptyView. Third-party apps NOT updated to iOS 27 If your app hasn’t been compiled with any of the iOS 27 SDKs, it only knows about iOS 26, and thus doesn’t know about (or have to worry about) the new, troublesome isDynamicIslandLimitedInWidth property, so it just… displays perfectly well. Here’s the Rivian app, which hasn’t been updated for iOS 27 yet, which shows the Live Activity displaying perfectly sized on the Duo (indicating vehicle charging): (Technical disassembly detail for the nerds: iOS shows the app’s minimal config, a smaller config normally used when multiple apps are in the Dynamic Island together, rather than the normal compact config, so it bypasses this issue entirely.) Developers who updated their apps for iOS 27 Surprise! That picture from before? On the left is just me compiling with the old 26 SDK that works! Unfortunately if you are a good developer who updated their app for iOS 27 and all the new features, you kind of get punished through this bug, because your live activity displays the improperly sized way described above, and you can’t just use raw UIKit like Apple. So you’re kinda stuck where I am: do I ship a weirdly sized Dynamic Island experience for everyone? Do I shove something extraneous in the compactTrailing spot just so it doesn’t look as empty? Do I not update the app at all until Apple fixes this bug? Widgets are techincally a separate extension from your main app, so you could compile them with a different SDK (26) than your main app (27) which fixes this bug, but I’m not sure Apple approves of that. Apple On the bright side hopefully it’s an easy enough fix, treating EmptyView like you used to on iOS 26 and per the documentation (the part that is weirdly right beside the mention of the new API that introduces this craziness in the first place). Radar/feedback assistant ID: FB25301955 Easy reproduction code if you’re curious: import ActivityKit import SwiftUI import WidgetKit struct CircleAttributes: ActivityAttributes { struct ContentState: Codable, Hashable {} } struct BlueCircle: View { var body: some View { Circle() .fill(.blue) .frame(width: 20, height: 20) } } @main struct CircleLiveActivity: Widget { var body: some WidgetConfiguration { ActivityConfiguration(for: CircleAttributes.self) { _ in BlueCircle() } dynamicIsland: { _ in DynamicIsland { DynamicIslandExpandedRegion(.center) { BlueCircle() } } compactLeading: { BlueCircle() } compactTrailing: { EmptyView() // pls respect me } minimal: { BlueCircle() } } } }
Vicky, driving me home from O'Hare, got sixteen dollars for the best conversation I had in months. The last line is hers.
I’ve had “AI usage statements” in the back of my mind ever since I read Ethan Marcott’s blog post sharing a round-up of his favorite ones. I didn’t consider writing one for this site, since it had the smell of status-signaling. Like a quasi-political declaration of allegiance to some sort of in-group (be it pro-AI, anti-AI, etc). I couldn’t be less interested in that.1 But then something happened that caught me completely off-guard. I was reading a post on my friend’s personal blog. This was a guy that I knew personally and looked up to. I’ve been reading everything he had been writing for years. About two-thirds of the way through a long (and fairly insightful) blog post, I had a sudden realization that the post had been written by AI. I’m not the best at detecting this kind of thing, but once I saw it, I couldn’t unsee it. It was unmistakable. And the thing is, I didn’t feel indifferent or even disappointed, as I might have predicted. I felt betrayed. For somebody who considers themselves open-minded—even optimistic—towards the development of AI, I didn’t know where this feeling was coming from. I have theories… maybe it has something to do with the social contract between the writer and the reader. Whatever it is, I didn’t want anyone who was reading my blog (hi mom 👋) to feel the same thing. So for the sake of transparency, here is my AI usage statement: The content of this website is written by me. I do not use AI to generate the text you are reading because writing from scratch helps me process my thoughts (which is one of the main reasons I blog in the first place). I don’t currently use AI-powered writing tools (grammar-check, spell-check, dictation tools, etc), but I may experiment with them as long as they don’t undermine the thinking process. The code for this website was originally hand-written, but I have since used AI to make several useful updates. I expect to continue using AI to write code for this website and other personal coding projects. When I create a public project exclusively with AI, I mention that fact in the project README.2 I am experimenting and my AI usage may change. When it does, I’ll update the policy here on my /about page, which is where it will live long-term. To explain why, see Paul Graham's Keep your Identity Small. ↩ For an example, see s-tier ↩ Thanks for reading this post via RSS! Let me know your thoughts by leaving a comment on the original post or sending me an email.