Personal Webpage of Blake Watson https://blakewatson.com The personal webpage of Blake Watson Tue, 19 Sep 2023 17:13:34 +0000 en-US hourly 1 https://wordpress.org/?v=6.3.1 167038248 Fancy forEach with functional programming in JavaScript https://blakewatson.com/journal/fancy-foreach-with-functional-programming-in-javascript/ https://blakewatson.com/journal/fancy-foreach-with-functional-programming-in-javascript/#respond Tue, 19 Sep 2023 04:56:38 +0000 https://blakewatson.com/?p=1299 I’m not a functional programming guru by any means, but I have a passing curiosity and I’ve been trying to learn more about it. I recently dealt with some annoying, repetitive code at work1 by applying some functional programming concepts.

Here’s a boiled down example that’s similar to what I ran into.

First, suppose you have an API that gives you a list of TV shows. We’ll store them in an array like this.

const tvShows = [
  { id: 100, name: 'Better Call Saul' },
  { id: 101, name: 'Fringe' },
  { id: 102, name: 'Stargate SG-1' },
  { id: 103, name: 'Ted Lasso' },
  { id: 104, name: 'The Office' }
];

Now assume that a separate API call gives us a user’s watch list. It’s an array of IDs corresponding to TV shows they would like to watch at some point.

const watchList = [101, 102, 104, 105];

It is just an array of numbers. But look again and you may notice a problem. We received the ID 105 in our watchList, but we don’t have a TV show in our dataset with that ID.

Now, in a real world app you would probably need to deal with this discrepancy in some kind of nice way. For our purposes, however, we’re following the rule that if we receive an ID that our program doesn’t know about, we can just ignore it.

With that in mind, let’s think about how we would retrieve a list of TV show names, based on the user’s watch list. Here’s one way we could do it.

const names = [];

for (const id of watchList) {
  const show = tvShows.find(s => s.id === id);

  if (show) {
    names.push(show.name);
  }
}

We’re given the ID, so we can do a lookup based on that, then add the name of the show to the list.

This isn’t terrible, but because we have to deal with potentially nonexistent IDs, we have to check the result of the lookup to make sure it exists before trying to add its name to our list.

We can tighten this up a bit by chaining together a few of JavaScript’s array methods. This results in more concise and FP-friendly code.

const names = watchList
  .map(id => tvShows.find(s => s.id === id)?.name)
  .filter(s => s);

The Array object’s map method2 returns a new array based on the original. The difference is that it lets us run a function on each item in the array. Whatever we return from that function ends up in the new array.

We’re first mapping the watch list. For each ID, we do a lookup with find and return the name of the show. We’re taking advantage of JavaScript’s optional chaining feature (notice the ?.). If the show doesn’t exist, this causes the expression to short circuit and return undefined.

Finally, we add a quick filter on the end to throw out any undefined items in our list of names.

This is better. But if we need to do this often, it’s kind of annoying that we have to do the lookup and the filter every time.

There are multiple ways to solve this, of course. For example, it would probably be smart to run this loop one time and store the results in an array for future usage. Let’s assume that there’s an external reason preventing us from doing that.

Instead, let’s make a new function that does the work for us.

const mapWatchList = fn => watchList
  .map(id => tvShows.find(s => s.id === id))
  .filter(s => s)
  .map(fn);

In this example, we’ve created a function called mapWatchList. It receives a function as its only parameter. It then maps the watch list, returning the corresponding show for each ID. It then filters out any instances where the show doesn’t exist. Finally, it maps over the new list of shows using the callback function it received.

This means that we can now access the watch list, not as IDs, but as TV show objects.

const names = mapWatchList(show => show.name);
console.log(names)
// [ 'Fringe', 'Stargate SG-1', 'The Office' ]

There is one problem, though. While our code does work, it is a bit inefficient. It must loop over all of the TV shows, then loop over the results in order to filter out nonexistent shows. Then, finally, loop over the results of that, invoking the callback function for each item. That’s three loops. Ideally, we’d only loop through the TV show list one time.

Let’s refactor our mapWatchList function so that it works the same way but only performs one loop over the list. For this, we will be returning to our performant friend,3 the for...of loop.

const mapWatchList = fn => {
  const mappedShows = [];

  for (const id of watchList) {
    const show = tvShows.find(s => s.id === id);

    if (show) {
      mappedShows.push(fn(show));
    }
  }

  return mappedShows;
}

Again we are accepting a function as the only parameter. Remember, the goal of this function is to use it like a map. That means it should invoke our callback function on each item of the watch list and use the return value of the callback as an item in the new array.

We create an empty array that will hold all of our mapped shows. Then we iterate through the watchList. For each ID, we do a lookup to get the full show object. If we find one, we invoke the callback function and store the result in the mappedShows array.

After the loop is complete, we return the results.

This works great, and we could stop here.

But…

It’s kind of not great that we are accessing variables that live outside our function (tvShows and watchList). And what if our application needs to do the same thing but for other entities besides TV shows?

For our final trick, let’s go up one more level and make a function that can make the mapWatchList function.

To do this, we need to think generically. Instead of a list of TV shows and list of TV show IDs, let’s think of it as being a list and ids. Now in our case we are matching based on 'id', but let’s make it such that you can match on whatever primitive value you want. We will call that the identifier, and the consumer of our function will need to pass that in.

So we’ll ask for these three things:

  • list: this is the full array of objects
  • ids: this is an array of identifiers
  • identifier: this is a string, such as 'id', that we will use for the lookup

Here we goooooo (Mario voice):

const mapThingsWithIds = (list, ids, identifier) => fn => {
  const mappedThings = [];

  for (const id of ids) {
    const thing = list.find(s => s[identifier] === id);

    if (thing) {
      mappedThings.push(fn(thing));
    }
  }

  return mappedThings;
};

Let’s go from the inside out. We are still returning a function that loops over a list of IDs, does a lookup from a list of objects, then invokes a callback for each item it finds. But rather than access specific arrays, we wrap our function inside another function that accepts the information we need.

We use it like this:

const mapWatchList = mapThingsWithIds(tvShows, watchList, 'id');
const names = mapWatchList(show => show.name);

If this is breaking your brain, I get it. Creating a function that returns a function that returns a function can get a little trippy to think about. But what we’ve done is make our work reusable and flexible. We are no longer limited to TV shows.

We can use our new function with any other kind of entity, and match using a property other than 'id':

const mapReadingList = mapThingsWithIds(books, readingList, 'isbn');
const titles = mapReadingList(book => book.title);

See? Nice, efficient, and reusable!

But I have a confession to make. In my case, I didn’t go this far. I stopped after making the first helper function. The point of this little tutorial is just to show you what you can do. Even turning a little bit of procedural code into more declarative, functional code can be helpful. This only scratches the surface of interesting things you can do with a functional programming style. If you want more on this, check out these resources:

]]>
https://blakewatson.com/journal/fancy-foreach-with-functional-programming-in-javascript/feed/ 0 1299
100 ways to generate a story idea https://blakewatson.com/journal/100-ways-to-generate-a-story-idea/ https://blakewatson.com/journal/100-ways-to-generate-a-story-idea/#respond Sat, 19 Aug 2023 21:09:30 +0000 https://blakewatson.com/?p=1272 In late October 2019, I desperately scoured the NaNoWriMo forums searching for creative inspiration ahead of November’s big writing event.1 At some point, I decided to write down as many ways to generate a story as I could think of. This was the result, which I just rediscovered in my notes.

  1. Steal the plot of another story
  2. Adopt a plot on the NaNo forums
  3. Combine a fairy tale with a different genre or time period
  4. Use the titles of an album or playlist as plot points/chapter titles
  5. Snowflake method
  6. Free write
  7. Fanfiction
  8. Based on real events
  9. Based on an interesting person you know in real life
  10. Use an alternative history
  11. Use a list of random words as plot points
  12. Write a sequel to an existing story
  13. Write a Coen brothers-esque story
  14. Take inspiration from a particular time period
  15. Take an existing story and change its style or genre
  16. Take an existing conflict from current events and switch up its details/style/genre
  17. Ask a series of what if questions
  18. Write a prequel or origin story to an existing story
  19. Use characters and events from previous, unfinished stories
  20. Write what you know
  21. Write what you don’t know
  22. Write what you wish you knew
  23. Based on a board game
  24. Based on D&D
  25. Use a writing prompt
  26. Make a list of 100 plot ideas
  27. Rewrite an existing story (original or otherwise)
  28. Write a Stanley Kubrick-esque story
  29. Write from the perspective of a dead historical figure
  30. Write about a character who’s traveling through different existing stories like the Energizer bunny
  31. Use the Writer Emergency Pack
  32. Use story dice (or whatever they’re called)
  33. Make up some different characters and then see how you can relate them to each other
  34. Turn a movie or TV show into a novel
  35. Write an original story story that takes place in an existing story’s world
  36. Look through old plot idea notes
  37. Turn your friends into characters in a story
  38. Turn a poem into a full story
  39. Turn a short film (like the ones from Dust on YT) into a story
  40. Based on a video game
  41. Based on an obscure/odd profession
  42. Based on a fictional profession
  43. Based on a fictional sport
  44. Imagine the villain is suing the hero or vice-versa
  45. Make up one-sentence story ideas
  46. Ask someone else what kind of story they would like to read
  47. Based on a particular aesthetic
  48. Based on Biblical stories or characters
  49. Write a fictional religious text
  50. Write a manifesto
  51. Write non-fiction
  52. Based on a comic book
  53. Based on a TV show
  54. Take the problems of one historical character and have a different historical character experience them
  55. Based on a web comic (eg, xkcd)
  56. Based on characters from popular toy franchises (eg, GI Joe)
  57. Write from the perspective of a journalist chasing a story
  58. Interview with hero
  59. Interview with the villain
  60. Based on a rare medical condition
  61. Based on a major catastrophe
  62. Take the villain (or hero) from one story and insert them into another
  63. Write about Donald Trump if he were in a different profession (eg, private investigator)
  64. Turn a Greek epic into a contemporary story
  65. Use a plot generator
  66. Use a character generator
  67. Draw a storyboard
  68. Pick different stock photos and relate them together
  69. What if a fact or assumption everyone agreed on turned out to be wrong?
  70. Write about an industry as if it were a different industry (eg, what if Apple and Microsoft were hospitals?)
  71. Use a selection of tropes as storytelling mechanisms
  72. Write about a weird or scary time in your own life
  73. Build a world first, then add the characters
  74. Build a detailed character first and then develop the plot
  75. Write about online communities as if they were IRL institutions (eg, Redditors Guild)
  76. Start with a visceral scene or concept and work outward from there
  77. Start with the ending and work backward
  78. Write some dialog between two characters with opposing wants and see if an idea is born out of that
  79. Start with a main idea/character/setting and mindmap out from there
  80. Use the life of an animal as inspiration for a story (eg, ants are like militant colonists)
  81. Start with a question and ask more questions
  82. Write about something you wish would happen
  83. Based on song lyrics
  84. Based on a sidekick to a popular hero
  85. Write about possible near-future events
  86. Write a short story first, then turn it into a novel
  87. Turn a play into a novel
  88. Create a story from a mad-lib-style template
  89. Create random characters from a generator and try to make a story with them
  90. Roll a D&D character (or two) and make a story out of them
  91. Write a story based on a published D&D adventure
  92. Write the plot of a Western into a modern day story
  93. Take the plot of a sci-fi and turn it into a Western
  94. Sit outside and see where your mind takes you
  95. Create a magic system or game mechanic and then build a story out of it
  96. Write a story where each chapter is based on a joke
  97. Create a character that has an obscure phobia
  98. What’s the scariest thing you can possibly imagine? Create a story about that
  99. Create a story that’s preachy about a particular religious or political point
  100. Create a series of short stories that are tied together by theme or setting or something else (eg, they all take place in the same world)

  1. NaNoWriMo (or National Novel Writing Month) is a writing event that happens every November. Writers all over the world scramble to write a 50k-word novel by the end of the month. 
]]>
https://blakewatson.com/journal/100-ways-to-generate-a-story-idea/feed/ 0 1272
Multi-browser workflow on macOS with Choosy https://blakewatson.com/journal/multi-browser-workflow-on-macos-with-choosy/ Mon, 14 Aug 2023 03:24:20 +0000 https://blakewatson.com/?p=1252 One web browser is plenty for most folks but you might find yourself needing more for various reasons. For instance, here is a handful of activities I can think of that you might want to perform in separate browsers:

  • Work activity. Maybe you have a time-tracking extension for work. Or you are logged into your work Google account. Etc.
  • Development. Maybe you love to develop in a particular browser because of its devtools.
  • Social media. Maybe you want to contain your social media activity to one browser as a way to mitigate tracking and targeted ads.
  • Picky apps. it shouldn’t be this way but sometimes you need Chrome for that one app that refuses to work in Firefox or Safari.

Okay that’s all I can think of off the top of my head but there are probably other reasons.

Much of this could probably be done with browser profiles. That’s cool too!

The problem

If you are using multiple browsers, you’ve probably hit the classic multi-browser annoyance—opening a link in the wrong browser.

This is super annoying. But this problem can be mitigated by using a specialized browser made for opening other browsers.

The solution

There are several of these browser-opening browsers but the one I’ve landed on is Choosy.1 Make Choosy your default browser and it will do the work of opening your links in the right browser or profile.

There are a ton of features and different ways you can use Choosy. Check out the website for all the details, but at a high level, there are two ways Choosy picks which browser to use:

  • Prompting you with a choice
  • Choosing automatically based on rules you have provided

The second item is where the magic is at for me. I’m able to set up rules to open links in my work browser2 profile if any of the following are true:

  • The URL matches my work’s GitLab instance
  • The URL includes the name of our cloud platform
  • The URL matches one of the handful of URLs we use when serving our app locally for development

And I just want to reiterate, the links not only open in the specified browser, but also the specified profile.3

Anything not matching the work rules I set up will fallback to my preferred default browser, Firefox.

I’m so used to it—because it operates so smoothly—that I take Choosy for granted. And that’s why I decided to write about it. If you’re dealing with multiple browsers, Choosy can make it a much nicer experience.


  1. Another one I’ve tried is LinCastor Browser. It works a bit differently but ultimately can open a browser according to specified rules. It’s serviceable, but I find Choosy to be easier to use and understand. 
  2. Previously Google Chrome, but recently trying out Microsoft Edge. 
  3. I’ve done it with Chromium-based browsers that have profiles. I haven’t tried it with Firefox profiles, as that browser does profiles a bit differently. From what I can tell, this feature only works with Chromium-based browsers. 
]]>
1252
Homebrew package for setting specified audio input device https://blakewatson.com/journal/homebrew-package-for-setting-specified-audio-input-device/ https://blakewatson.com/journal/homebrew-package-for-setting-specified-audio-input-device/#respond Tue, 18 Jul 2023 17:10:30 +0000 https://blakewatson.com/?p=1181 I had an annoying situation where, when my AirPods connected to my Mac, they would take over as the active input device (ie, microphone). I didn’t want that because I already have a nice microphone setup. So I set out to find a way to prevent that behavior.

I failed.

The only solution I’ve been able to find is to change the input device back to my good microphone after AirPods connect. The easiest way to automate this is to use a package called switchaudio-osx and the ToothFairy app (also available via Setapp).

Install switchaudio-osx with:

brew install switchaudio-osx

ToothFairy can run a shell script after AirPods connect. So I’m using that to run:

SwitchAudioSource -t input -s "MICROPHONE NAME HERE"
]]>
https://blakewatson.com/journal/homebrew-package-for-setting-specified-audio-input-device/feed/ 0 1181
My journey with the Chubon keyboard layout https://blakewatson.com/journal/my-journey-with-the-chubon-keyboard-layout/ https://blakewatson.com/journal/my-journey-with-the-chubon-keyboard-layout/#respond Sat, 15 Jul 2023 23:06:07 +0000 https://blakewatson.com/?p=1153 In high school, I still had the ability to handwrite, albeit with some difficulty. Toward the end of high school in the early aughts, I was given use of an ancient computer for completing some of my assignments. At that time, typing on a keyboard was easier than handwriting.

After starting college in fall 2003, I continued to experience muscular atrophy in my hands. I was still able to type, but it was slow. I needed a laptop-sized keyboard because full sized ones were too difficult.1

Over the course of the late 2000s, SMA swallowed the remaining strength in my hands. After a creative-but-doomed last ditch effort, I was forced to transition to alternate forms of input. I played around with dictation but was frustrated by it. My main form of input became the onscreen keyboard (abbreviated OSK).

Windows has a built-in onscreen keyboard:

Screenshot of the default onscreen keyboard in Windows. Standard QWERTY keyboard layout.

Macs come with an extremely powerful onscreen keyboard now, but they didn’t until the mid-2010s. Prior to that, they had something called the Keyboard Viewer which was a barely functional onscreen keyboard that was originally designed (I’m assuming) for displaying the alternate functions of keys when various modifiers are held down.

Fortunately, Mac users at the time had a good third party option.

AssistiveWare KeyStrokes

Animated GIF of KeyStrokes, the onscreen keyboard. It depicts a mouse typing by clicking on keys and predictive text suggestions. It’s a standard QWERTY layout with some extra keys for common actions like copy and paste.
This was the marketing image on the KeyStrokes product page for years.

KeyStrokes was a third party onscreen keyboard for Mac. It had a number of huge improvements over the Keyboard Viewer and even Windows’ OSK.

  • Word and phrase prediction (the Windows OSK has this now)
  • Custom keyboard layouts
  • Custom buttons, which could emulate other keys or even run complex macros
  • Dwelling, an input mode that lets you activate a key by hovering the mouse pointer over it for a specified amount of time
  • Fully resizable
  • Honestly, all sorts of other things I can’t remember

While I still loathed typing on an OSK, I was glad to have it.

But my average words-per-minute plummeted. There was something so slow and tedious about typing with a mouse. The most annoying part was the jumping around the keyboard. Why couldn’t all of the common letters be in one spot?

Then I noticed one of the custom keyboard layouts that KeyStrokes came with: Chubon.

I had no idea what that word meant, Chubon. It would be sometime before I realized it was a name.

It was strange, having been used to QWERTY. But being a nerd and a programmer, I had often heard of people changing their keyboard layout to Dvorak. This was the same thing. Not the same layout, but a layout made to be more efficient than QWERTY.

The difference was that it was optimized for people typing with a single finger or a typing stick. This was exactly what I needed. A mouse pointer may as well be a finger.

I made the switch and taught myself the new layout by playing typing games on the web.

My custom KeyStrokes keyboard panel. It’s the Chubon keyboard layout and has many custom buttons for text navigation, common actions like copy, paste, and undo, and buttons especially for web development, such as a browser refresh button.
My custom KeyStrokes panel

The Chubon layout

I became obsessed with this keyboard layout. I wanted to know more about it. Where did it come from? Was Chubon a person? If so, who were they?

It turns out that my beloved keyboard layout was designed during a research project in the eighties by Dr. Robert Chubon. He was interested in optimizing a layout for single-digit entry because he typed using a typing stick.

Dr. Chubon (Bob) was paralyzed as a high schooler during a trampoline accident. In the mid-eighties, while working as a professor at the University of South Carolina, he had so much typing work that needed to be done that he was becoming overwhelmed. He sought a way to increase his words per minute. This is what he came up with.

Photo of Dr. Chubon's keyboard. It’s using his layout on a hardware keyboard that has been remapped. The number keys and alpha keys are color-coded.
Photo of Dr. Chubon’s personal keyboard, as seen in his self-published autobiography

You can read all about it in his journal article if you are so inclined, but I will give you the quick rundown.

It’s based on letter frequencies and is arranged in a target-like layout with the most common letters being clustered in the center. The E serves as the bullseye (he even color coded the E key red). Lesser used characters are pushed to the periphery.

The Chubon layout reduces the amount of movement the typist needs to make in order to bounce around the keyboard spelling words. This might seem like a minor improvement, but I can say from experience that the difference is huge. It’s quantifiably better in terms of typing speed but it is also qualitatively better in terms of reducing tedium and making typing more enjoyable.

Writing had become such a chore, but once I got the hang of this keyboard layout, I could see the light at the end of the tunnel—typing wouldn’t always be a terrible slog.

I would soon be using a form of single-digit entry that wasn’t a mouse pointer. It was a method of typing much more mainstream than an OSK. It was called iPhone.

Bringing the Chubon layout to iPhone

I had been spoiled by using the Chubon layout on my Mac. I was not taking very kindly to having to use QWERTY again on my iPhone.

Specifically, I found that a good way to type on my computer was to use the iPhone as an external keyboard. This was possible thanks to a category of apps than I call “remote apps.” There were a handful of these at the time. But the one I was using most was called HippoRemote.

Remote apps let you use your phone as an external keyboard and mouse for your computer. This was of particular interest to me because at the time I had plenty of hand strength to type rather quickly on a phone. The ability to transfer that to my computer was a huge win in my continual search for a better way to type.

At the time, there was no way to change out the standard iOS keyboard.2 But HippoRemote had a concept of custom panels, typically used for controlling media playback. As a result, it had an interesting feature that would let you create your own custom key panel.

It was a bit clumsy, due to some limitations with the feature, but I managed to put together a Chubon keyboard panel for HippoRemote.

After a couple of years of using this serviceable but awkward keyboard panel, I set out to make a real remote Chubon keyboard. One that didn’t need a whole extra set of keys just for capital letters. One that behaved more like an iPhone keyboard.

I put together a little app3 that had a proper Chubon layout4 and behaved more like a remote keyboard. It ran as a homescreen app on my iPhone and enabled me to use the Chubon layout on the phone to type on the Mac.

It was my greatest typing achievement since my transition away from physical keyboards. But it wouldn’t last. As I once said of SMA:

It keeps taking and taking. Its hunger is never filled nor its thirst quenched.

My hand weakened to the point where I could no longer use my creation. I transitioned to using KeyStrokes full-time. But there was a problem. KeyStrokes was aging software. The developer couldn’t justify a large overhaul because he was focused on other, more popular accessibility apps. KeyStrokes was on life support, in danger of breaking down completely with each new release of macOS.

But Apple worked with AssistiveWare and something incredible happened.

The macOS Accessibility Keyboard

Apple recreated much of KeyStrokes’ capabilities in the form of a new built-in OSK called the Accessibility Keyboard. The first iteration was a bit janky, but after a few releases it was capable enough for me to jump ship from KeyStrokes, which ceased to work not long after.

The essential features of the Accessibility Keyboard were, for me, the ability to create a custom layout and the ability to add custom buttons to execute key combo presses. I was able to create the Chubon layout and use the Accessibility Keyboard full-time.

Screenshot of the current iteration of my Chubon panel for the macOS Accessibility Keyboard. It features the Chubon layout with an adjacent number pad. There is an entire row dedicated to special characters and about 42 custom buttons that support everything from text navigation to app-specific shortcuts.
I kept the red coloring of the “E” key as homage to the original hardware design.

Bob

I corresponded with Dr. Chubon several times in the 2010s (he signed his name, Bob, in emails to me). He seemed delighted that his work from the eighties lived on, coming to new platforms he could have never imagined in the eighties.

Dr. Chubon impressed me. I consider him as a sort of disability renaissance man. His academic field was rehabilitation counseling, but he also picked up programming and playwriting. All of this while facing the challenges of a twentieth-century society that had only just begun to be more accepting of people with disabilities.

Dr. Chubon self-published an autobiography in 2010 and it’s a delightful little read. As a fellow disabled person I can relate to much of his story. We hate to use the i-word but I was nonetheless inspired by what he accomplished and how he used his skills and abilities to help others.

I was feeling overwhelmed with gratitude and nostalgia late one night this week and decided to email him again to express my thanks and appreciation for his work on the Chubon layout. I was saddened to discover that he had died in April of 2022, and his wife had died in May of this year.

I left a note on his obituary page and watched a video his family had made of spreading his ashes on family land.

I didn’t know him closely, but he affected my life greatly. It’s hard to imagine that I would have accomplished what I have if not for his keyboard layout. That sounds like hyperbole—and maybe it is—but it’s how I feel.

Resources

I hope to put up a one-page site on all things Chubon layout5, but until then I can offer some links to existing stuff.

I don’t know if Dr. Chubon’s website will remain online so I’m going to link to documents that I’m hosting myself. But if it still works, you can visit his webpage here:

http://www.chubon.org/bob/

Dr. Chubon’s work

Stuff I’ve written about the Chubon layout

Further research

The Chubon layout was developed for hardware keyboards. In particular, it was intended to be implemented by remapping the keys of off-the-shelf keyboards, so as to be practical and affordable for people with disabilities.

It was also primarily developed around typing-stick typing, which introduces some different variables that aren’t present on virtual keyboards—keys on the bottom row being easier to physically access, for example.

Further research has been done and hypothetically faster layouts have been developed. I’m not sure if I’ll be switching to something else anytime soon but I love that work being done in this area.

Speech recognition

If you can’t use a physical keyboard, another typing option is speech recognition. There have been big developments in this area and I’ve covered one of them, Talon, several times.

I’m not always in an environment where I can use speech recognition and sometimes I just prefer the onscreen keyboard. But I mention it here because it can be a powerful tool for people who need it.


  1. Part of the appeal of compact, laptop keyboards was the fact that the keys were usually flatter and flush, allowing my fingers to slide across them without lifting them up. 
  2. It wasn’t even called iOS at the time. It was called iPhone OS. 
  3. For the dev nerds, I made a tiny desktop app in Python using Tkinter. It’s only purpose was to give me a button that would fire up a PHP server that served a single-page app over the local network. That app passed encoded key commands over the wire where the PHP server would parse them and execute them with AppleScript. Yes, I realize how ridiculous that sounds. 
  4. Ok I actually used a variant of the Chubon layout that moved the “J” key to the end of the top row. I also have never used his numbers layout as I prefer a numpad layout for those. 
  5. I’ve already secured the domain name, which means 90% of the work is done and there’s no excuse for the near-certain procrastination of this project, which will cause me shame and regret annually when the domain comes up for renewal. 
]]>
https://blakewatson.com/journal/my-journey-with-the-chubon-keyboard-layout/feed/ 0 1153
The fragile nature of my life’s work https://blakewatson.com/journal/the-fragile-nature-of-my-lifes-work/ https://blakewatson.com/journal/the-fragile-nature-of-my-lifes-work/#respond Fri, 23 Jun 2023 04:59:45 +0000 https://blakewatson.com/?p=1114 On June 16, 2023 a storm rolled through Mississippi. Violent straight-line winds and, possibly, a tornado caused several telephone poles in my subdivision to snap and fall, knocking out the power and blocking the only way in and out of the subdivision.

A photograph depicting a telephone poll that has apparently snapped in half, the transformer having shattered to pieces and the power lines having been brought down.

Unfortunately, whole swathes of Mississippi’s power grid failed, overwhelming repair crews and leaving many residents without air conditioning during a Mississippi summer.

I’m somewhat prepared for short power outages—those lasting less than a day. I charge my wheelchair every night. My patient lift—a thing I use to transfer to and from my wheelchair and bed—is usually charging when not in use. My MacBook Pro is usually charged and is a newer model with great battery life. My desktop Mac is attached to an uninterruptible power supply (UPS) so that I have time to save my work and shut down properly. My modem and router are also connected to that UPS. That keeps my internet connection powered for several hours.

But this was not a short outage. The June 16 outage lasted (for me) four grueling days until the evening of June 19. My internet connection held up long enough to communicate with my fellow remote coworkers to let them know what was going on. I submitted my end-of-pay-period timecard form just before my UPS died.

After my UPS died, taking with it my internet connection, I tried to switch to my phone’s personal hotspot. I couldn’t get it to work and wouldn’t discover until after the fact that the problem was that my particular wireless plan didn’t include the feature. 1

I’m much more capable using a Mac than a mobile device. My hands aren’t strong enough to use a touch screen. So I try to maintain internet access on the Mac as long as I can. But after fighting with the hotspot and also learning that the outage was gonna be a bad one, I decided that I needed to shut down the MacBook Pro and use it as a power bank for our phones. I’m extremely limited on what I can accomplish on a phone, but I’m not totally helpless thanks to Apple’s Voice Control feature.

More on technology later.

The harsh realities of a power outage began to set in after about 12 hours—the house warmed to an uncomfortable temperature, food in the fridge was no longer being properly cooled, the house was dark.

Mississippi has hot, humid summers. We jumped back and forth on whether opening the windows helped or harmed us. I had to sleep without my BiPAP, which meant I had trouble breathing at night. That prevented my body from resting like it needed to.

As we rolled into Saturday morning it became apparent that repair crews were busy elsewhere. And there was another problem. Those downed telephone poles were blocking the only way in or out of our subdivision. Ours and our neighbors’ only way to get out of the subdivision was going offroad through an uneven patch of land belonging to a neighbor.

For all intents and purposes, we were stuck at home. Dealing with power outages is already difficult. Having a significant physical disability makes it even tougher. It was hard on me and my brother, but also on our mother and caregivers.

Overall we did okay. We were never really in physical danger. Had the power outage continued further, we would have had more significant problems—batteries in our durable medical equipment would have eventually died. At a certain point, we would have been forced to set up a generator or either figure out how to stay in a hotel (which is a tricky accessibility problem).

But that four days without power gave me time to think. There wasn’t much else I could do. As SMA has continually weakened my muscles, my interface with the world around me is increasingly computer-based.

I’m a web developer by trade. I make websites. Websites are just computer files. If the world was suddenly without power, websites would more or less cease to exist. The things that I have spent the majority of my adult life learning how to build would be nonexistent and meaningless. All the skills I’ve developed in programming and digital graphic design would become useless.

When the power is on, I’m worth a competitive web developer’s salary, despite only being able to move a few fingers. When the power is off, there is nothing productive I can offer the world on my own.

Many people have this problem. Not just me. What does it mean that the livelihoods of so many people rely on an intangible world of our creation?

But it goes beyond just working. All of my hobbies and interests are either computer-based inherently, or either I interface with them via a computer. I am a side project enthusiast and I run several websites and apps. Without power, I can’t work on those at all.

I’m also a writer. I write for this website, but I also write fiction as a hobby and have participated in National Novel Writing Month for many years. There was a time when I had enough strength in my hand to write with pen and paper. But that time has long since passed. I require a computer to be able to write. I’m writing this very article on two different computers—a Mac Studio on which I’m using an onscreen keyboard, and a MacBook Pro on which I’m using voice recognition software.

My life has become increasingly screen-based, but as a generally optimistic technologist, I’ve embraced it.2 But being without power for the better part of a week made me acutely aware of how detached from the physical world I have become.

During the power outage I started listening to We Are Legion (We Are Bob), the current read that my book club at work is on. Hilariously, it is about a man who has his mind uploaded into a self-replicating spacecraft. That is about as reliant on technology, and detached from the physical world, as it gets. So I still have a ways to go.

I guess what I’m saying is I need to get out more, as soon as I figure out what that means.


  1. I don’t understand why this requires a separate charge. I’m already paying for the data. My iPhone supports personal hotspots. What is justifying the extra cost from the carrier. Feels like a sleazy cash grab. 
  2. I don’t mean “optimistic technologist” in the technology-as-a-god futurism sense. Just that I go easy on myself when it comes to screen time and using technology. For me, computers and the internet have a huge upside. It’s by no means the only way I could live, but it’s the way I choose to, given my interests and level of physical ability. 
]]>
https://blakewatson.com/journal/the-fragile-nature-of-my-lifes-work/feed/ 0 1114
I made a web-based version of Sawayama Solitaire https://blakewatson.com/journal/i-made-a-web-based-version-of-sawayama-solitaire/ https://blakewatson.com/journal/i-made-a-web-based-version-of-sawayama-solitaire/#respond Thu, 18 May 2023 04:10:22 +0000 https://blakewatson.com/?p=1043 One of my favorite game developers, Zachtronics, released their final game, Last Call BBS, a little while back. And in true Zachtronics fashion it fuses programming, retro futurism, and nostalgia to great effect, resulting in a fun, educational, and visually enticing game.

Last Call BBS is interesting because it’s a set of mini-games. The game boots up like a retro computer and you dial in to a bulletin board system (that’s the BBS) to download several games.

I’ve played about half of the games and they are pretty fun. In particular, a programming game about recreating 20th century food court food for people in the far future pushed all the right buttons for me.

But, in my opinion, one game stole the show. And it wasn’t even one of the mini games. It’s really a mini-mini game. Your Sawayama computer system does have one game pre-installed—Sawayama Solitaire.

A screenshot of Sawayama Solitaire in its beginning state. It looks like a retro OS desktop. The graphics are low resolution. The cards are dealt similar to Klondike but all of them are face up. The entire screenshot is slightly warped said that it looks like a CRT monitor.

It’s a variant of Klondike with a few rules changes:

  • The tableau is face-up but, like Klondike, only cards on top or cards in a sequence can be moved.
  • Any card (not just kings) can go into an open cell.
  • The deck can only be drawn through once. After all cards have been drawn, it does not reset.
  • After all the cards in the deck are gone, the spot where the deck sat becomes a free cell.

These changes have an exciting effect on the game. It suddenly becomes a lot more strategical than before. Since you can see all the cards, you can start thinking several moves ahead—and you are going to need to because you only get to go through the deck once. There is much less luck involved (but still a good amount). They claim that their variant of solitaire is often more winnable than Klondike and, anecdotally, I have found that to be true.

Aside from the rules, the execution of the game—the graphics, the sound effects, the music—is flawless. If you have childhood memories of playing solitaire on an old computer than you will appreciate the game design on this one.

I lost a bunch at first. But then I started winning. And then I became hooked. I would catch a few minutes of downtime during my day and want to play a few rounds, but I didn’t want to have to open Steam every time. I just wanted to be able to pop a browser tab open and play a little.

I’m no Zachtronics, but I’ve been slowly learning game development with PixiJS. So I set out to create a web-based variant of Sawayama Solitaire. I wasn’t trying to copy the retro vibe or anything else about the game other than the ruleset. I’m certainly not trying to take any business away from Zachtronics. I hope I bring more attention to their amazing game, if anything.

Some notes about my version of the game

This is the most interaction-heavy PixiJS game I’ve made so far. I’m sure I did a lot of things very badly code-wise. But it works. For the most part.

My version of the game look similar but not as cool.

Once I had a playable alpha version of the game I started passing it around to a few people. Immediately, the most prominent feedback was that I needed to make a bouncy card animation after winning a game. Multiple people told me that this was required in a solitaire game and I agreed.

I was going to embed a video here of the animation, but I think it would be more fun if I challenge you to win the game and unlock the animation yourself. That’s your reward.

I’m not going to win any awards here, but I’m proud of how this one came out. For what it’s worth, the source code is on GitHub. Feel free to use it for learning purposes, but probably don’t follow my example too closely because I’m very much a beginner at this.

The game is published on me and my brother’s little web game site. I decided I’m not going to put it on Itch because Zachtronics already has their solitare collection for sell there and I don’t want to cause any confusion.

Links and stuff

]]>
https://blakewatson.com/journal/i-made-a-web-based-version-of-sawayama-solitaire/feed/ 0 1043
A home-cooked app for hiring personal caregivers https://blakewatson.com/journal/a-home-cooked-app-for-hiring-personal-caregivers/ https://blakewatson.com/journal/a-home-cooked-app-for-hiring-personal-caregivers/#respond Thu, 27 Apr 2023 03:33:49 +0000 https://blakewatson.com/?p=1009 I don’t talk about this often, but I use a team of caregivers to help with all of my daily personal care needs. I’m fortunate enough to be on a program in the state of Mississippi that provides me (and my brother, Matt) with a certain amount of hours of personal care assistance per day. The program requires that I find my own caregivers, which is great in one sense because I have control of who works with me. But it also means I bare the burden of recruiting.

Recruiting personal care assistants (PCAs) is tough. There has been a nationwide caregiver shortage since at least the 2020 pandemic, if not before. Add to that the fact that the economy has seen rising inflation and increased prices for just about everything. In response, many hospitals and healthcare agencies have risen the pay rate for CNAs and PCAs,1 some going as far as to offer sign on bonuses.

Unfortunately, the program I’m on doesn’t pay its workers very much. I’m not going to provide numbers here but for the area I live in, the hourly rate is below average, anecdotally speaking.2 The program is also somewhat slow to onboard people, which means it can take a week or more from when we find someone until they can start—and another two weeks until they get their first paycheck.

All of this means hiring a caregiver is challenging for me and and my brother. We typically approach this problem by paying for an ad on Indeed and processing applications to find people to interview and ultimately hire. This is a high turnover industry so we’re typically going through at least one round of hiring every year.

Despite the caregiver shortage we get a lot of applications, albeit many low quality ones. Processing these is its own problem and it’s one that, as developers, we could actually solve.

In which we reinvent the wheel because other wheels weren’t quite right

After years of using form providers like Google Forms (Matt) and Wufoo (me), we started using a self-hosted form made with WordPress and Gravity Forms. I was tired of paying so much for Wufoo and didn’t like reading Google Forms entries in a big spreadsheet. So the Gravity Forms setup was meant to save us money and give us more control.

It worked well enough but viewing Gravity Forms entries in the backend of WordPress left such a sour taste in my mouth that I finally got sick of it. And as full-featured as Gravity Forms is, posting new job forms proved to be tedious work. Here’s a short list of my grievances with the Gravity Forms setup:

  • Everything about it was slow
  • Viewing entries was slow
  • In some spots, leaving form instructions meant typing HTML into a tiny box.
  • Did I mention it was slow.

Finally, I’d had enough. Surely two professional web developers could do better. We could make a home-cooked app, just for us. So like developers are wont to do, we threw out all the perfectly adequate ready-made solutions and set out to build our own.

Boring is best

We turned to tried and true tech for this project:

  • PHP (Fat Free Framework a small one we’re both familiar with)
  • HTML forms
  • Classless CSS (awsm, but looks like that repo went private)
  • As little JavaScript as possible
  • SQLite
  • A basic VPS
  • Postmark for transactional email

The goal here was to build something that would require little maintenance, be fast, and make reviewing applications easy.

Because of these choices, we were able to build the app in a weekend, with another weekend for fixes and enhancements.

Job ads and application forms

For privacy reasons, I’m not going to link to the site here. It’s not hard to find though. But it’s not super interesting. It’s mostly just description of the job and what we’re looking for in a PCA.

The homepage shouldn’t be hit much but, if it is, visitors will see our current hiring status.

Screenshot of the homepage. It indicates that we are currently hiring and briefly explains who we are.

Click through to the job ad/description shows details about the job. Here’s a partial screenshot of an ad we’re running at the time of this writing.

Partial screenshot of the job description. It tells who we are and lists the shifts we’re looking to cover and describes some basic info about the job, such as the pay rate (redacted), and the program that provides the job, the Mississippi Department of Rehabilitation Services

The ad contains a few callouts to “Apply Now” which sends visitors to the main part of the app, the application form. Most people will land directly on this form because they are coming from our ad on Indeed.

For this page, we coded up an HTML form. The awsm CSS stylesheet has pretty good form styling. It looks something like this:

Screenshot showing a few basic text fields (name, email, phone) and a radio button question about a valid driver’s license.

Screenshot shows two text areas asking about previous experience and professional references.

There are a few more questions, a resume upload, then a submit button. Applications get submitted and saved to a SQLite database.

Matt and I get an email notification of the application and the applicant gets a confirmation email containing a copy of their responses.

Reviewing applications

Now the fun part. Each job we post has an ID associated with it. That lets us sort applications into what we call “campaigns.” Our admin area presents a list of campaigns. Clicking through to one of those shows a table of applications.

A table showing applications. Personal details have been redacted. Columns are Status, Notes, Name, Email, City, Date

This is a simple table view. It provides high-level info, most importantly the status. Matt and I will log in and review applications, marking the ones that we’d like to contact for an interview.

As an aside, I can see how the status “Nope” can come off as a little harsh. But look, I didn’t set out to be a PCA recruiter. It’s just something I have to do. I use a little humor to cope with it sometimes. This app isn’t for public consumption so the only audience the admin area targets is me and Matt. Applicants never see their status.

Clicking through to an application gives us a view of all the responses, plus a few editable fields like Status and contact info.

Screenshot of a single application, Test Person, with a status of Unreviewed (in a dropdown) and a few fake responses.

We also have the ability to leave internal notes on applications. We use for recording how an interview went or reasoning behind rejecting an application.

Screenshot of the bottom of an application showing a textarea where a note can be left.

Homemade is a feature

There really isn’t anything interesting about the architecture of the application I spent a whole article writing about. It performs a fairly simple task that I suspect many systems perform similarly—and probably do it better.

But ours it does it in the exact way we want. That’s the magic part. If we want to change it, we can. If we want it to stay the same for years, we can keep it that way. This is the best kept secret about being a developer—making your own stuff, just for you (okay it’s a poorly kept secret because developers are always blogging about their stuff).

It never gets old.


  1. A Certified Nursing Assistant (CNA) is a caregiver who has had a certain amount of training and has a license. For the program I’m on, PCAs can have a CNA license, but it’s not required. CNA’s typically make more than PCAs, generally speaking (but the program I’m on pays the same either way). 
  2. I think PCAs are underpaid as it is. They often don’t get the same work benefits that other jobs in the health industry do. But that’s a topic for another day. 
]]>
https://blakewatson.com/journal/a-home-cooked-app-for-hiring-personal-caregivers/feed/ 0 1009
Surveying the landscape of CSS micro-frameworks https://blakewatson.com/journal/surveying-the-landscape-of-css-micro-frameworks/ Mon, 13 Mar 2023 01:26:53 +0000 https://blakewatson.com/?p=983 Pretty soon after learning CSS and building my first few websites without table layouts (yes I’m old) I stumbled across the world of CSS frameworks. The oldest one I can remember using was the 960 Grid System. I guess calling it a framework is a stretch. It’s just a grid really. But at the time it was a breath of fresh air. I was trying to learn about design and grid systems and the 960 grid was so easy to use.

Then at some point Boostrap took over the world. A number of comprehensive frontend frameworks were born—CSS (probably Sass) and JavaScript included. And nowadays we have JavaScript libraries that people build frontend frameworks around, complete with fully-functional components—building blocks for making websites and web apps of any sort.

But with that kind of power comes complexity, bloat, performance issues, and dependency hell. So when beginning a new project, I like start by thinking about the bare minimum needed to get going. It’s also an excuse to waste time scouring the web for goodies as I am wont to do.

I’ve done everything from building with Vuetify to exclusively using my own CSS. But lately I’ve been enjoying the world of CSS micro-frameworks and classless CSS themes.

What is a CSS micro-framework?

I don’t know.1 So I’m defining it myself:

  • May include classes for building grids, components, etc. Typically limited, though, because of the next rule.
  • Under 10kb minified and gzipped. I feel like you can’t claim “micro” or “lightweight” beyond 10kb.
  • JavaScript is optional and is not supplied by the framework.

Under this definition, classless CSS themes also count as micro-frameworks, but I prefer to think of them separately because they have different goals. More on those later.

Here are a few of the micro-frameworks I could find. I’ve used Spectre, Chota, and Pure recently, but haven’t used the others. I’d like to try Pico in the future.

One thing I’ve noticed is that many of these appear to be somewhat dated, past their prime of peak activity. If I were to speculate, I’d say it’s because CSS is very good now and if you aren’t using one of the big players in the CSS framework space, you might just be writing your own styles. That’s just a guess, though.

Personally I would like to see a lot more of these. And I was a little bit surprised to see that the same half dozen frameworks kept popping up everywhere (which is a problem I suppose I’m contributing to now).

Why use a CSS micro-framework over a regular one like Bootstrap?

There could be many reasons, but here are reasons I’ve used them:

  • I would go for a micro-framework if I want to skip having a build step. They are usually easy to pull in and get started with.
  • I want to reduce complexity. I want fewer options and less documentation to have to wade through.
  • Performance. If it is a small project, maybe I can’t justify the larger sizes of other frameworks.

Just for fun, here is a size comparison of CSS micro-frameworks versus two larger ones, Bootstrap and Bulma. I came up with these numbers by starting with the minified version of each one and gzip-ing them from the command line.

Framework Size
Bootstrap 5 26.89kb
Bulma 26.95kb
Pico 9.94kb
Spectre 9.54kb
Picnic 7.26kb
Chota 3.56kb
Pure 3.51kb
Milligram 2.24kb
Skeleton 1.64kb

But what if you could go even more micro than micro-frameworks? Enter classless CSS themes.

What are classless CSS themes?

They are kind of like micro-frameworks except that they don’t provide classes for styling. Instead they aim to make regular semantic HTML look and work nicely by default. This typically means adding a max-width to the body (or <main> tag), implementing some good typography, and styling most or all of the default HTML elements.

Classless CSS themes differ from other CSS frameworks because they aren’t generally meant as a jumping off point for a full blown design. Frameworks are meant to be used to build something—a particular layout or multiple templates for a larger website. Classless CSS themes, while happy to be extended, represent something closer to the end result. These themes aren’t generally made to build multiple templates or layouts of a website. They often begin and end with a centered column of content, and that’s all they ever intend to be.

This is why I like to call them “themes” rather than “frameworks.” Because they aren’t really made for building so much as they are made for viewing.

Here are a few I have collected recently. I’ve used awsm, Water, and Holiday but I would like to try the others out soon.

I want to call out two of these. Water.css made waves (heh) on Hacker News a few years ago and introduced me to the concept of classless themes. It’s still one of my favorites.

GDCSS is modeled after GOV.UK, which I admire for putting simplicity and accessibility front and center. US government (especially local) could learn a thing or two from the UK here (although this looks like progress).

Some of these and more can be found at CSS Bed and this huge list of classless themes on GitHub.

I love these classless CSS themes and it is hard to say why exactly

The biggest appeal is that they are drop-in, meaning it doesn’t take any special coding or design thought to make it happen (other than including it). Just create some HTML and include the stylesheet and you are done. You have a styled webpage.

I remember the first time I stumbled across this concept. It was in the mid-2000s when I found the W3C Core Styles. These were meant to be (I believe) drop-in CSS styles that you could use immediately with mostly no addition of classes or anything else. This idea intrigued me.

In the years following my discovery of the Core Styles, the idea of reusable themes would be hammered into my psyche in the form of WordPress themes. And once I dove into WordPress, the idea of publishing my own theme became a years-long white whale that I never did capture.

Fast-forward to the present. I no longer find myself deep in WordPress work. My website still does use WordPress (as of the time of this writing), but I no longer think of WordPress themes as the dominant way to make a webpage (even if it still is).

I’m now thinking in terms of static sites made with generators like 11ty. So “theme” means something different to me now. A good CSS theme should not presuppose too much boilerplate, so as to be easier to apply to different sites with different markup.

That brings us back to the W3C Core Styles. We want some good default styles that don’t assume you are using any particular markup scheme beyond regular, semantic HTML. You can if you want, but you will need to add custom styles as needed.

The W3C Core Styles are a bit dated and they don’t style many of the newer elements that came with HTML5.

That’s where modern, classless CSS themes come in. They add just enough styling over the top of browser defaults to provide a fantastic base look without the need for specialized markup. There’s just something satisfying about the idea that I can write some HTML and then grab a nice pre-made theme that Just Works™. It’s hard to describe why I like this so much. In a way I suppose it’s nostalgic—these themes take me back to a time when I had first discovered HTML and CSS and learned to make simple webpages by hand (as opposed to using a site builder).

It gives me warm fuzzies to get back to the “bare metal” of the web. It’s the digital equivalent of cleaning your desk and having only the essential items at your fingertips. These classless themes feel fresh and exciting despite their minimalistic nature. They make content look great, which makes me want to write more content. We should be excited about writing content. It’s never been easier or cheaper or to write and publish to a worldwide audience.2

The framework-less framework

As we go deeper down the path of minimal CSS starters we end up at the logical conclusion—you might not need a framework at all. CSS has come a long way over the past decade. Many frameworks aren’t taking advantage of the cool stuff CSS can do now (eg, container queries, new sizing units, etc.).

Things that used to be difficult are much easier now. If all you need is a sidebar and a main content area, look no further than:

.with-sidebar {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}

.sidebar {
  /* ↓ The width when the sidebar _is_ a sidebar */
  flex-basis: 20rem;
  flex-grow: 1;
}

.not-sidebar {
  /* ↓ Grow from nothing */
  flex-basis: 0;
  flex-grow: 999;
  /* ↓ Wrap when the elements are of equal width */
  min-inline-size: 50%;
}

Source: Every Layout

The takeaway

Less code is desirable and, for content on the web, simpler design is often better than complex design.3 Micro-frameworks and classless themes are great for getting up and running quickly without adding file size bloat, build steps, or reams of documentation to peruse. Classless CSS themes in particular look great by default and make for a handy go-to when you just need some HTML to be presentable.


  1. I asked ChatGPT what a micro CSS framework was and it was hilariously wrong, citing Bootstrap as its first example. 
  2. I realize that putting something on the web doesn’t automatically mean you have an audience, but this grandiose idea that you can write anything and publish it is part of the thrill of these classless themes to me so just let me pretend. 
  3. A good litmus test to know if your design is too complex is if your users are hitting the browsers reader mode (or using an extension that does something similar). 
]]>
983
Neglecting the scrollbar: a costly trend in UI design https://blakewatson.com/journal/neglecting-the-scrollbar-a-costly-trend-in-ui-design/ https://blakewatson.com/journal/neglecting-the-scrollbar-a-costly-trend-in-ui-design/#respond Fri, 17 Feb 2023 23:13:50 +0000 https://blakewatson.com/?p=948 I’ve noticed an alarming UI trend over the last five years or so. Apps are neglecting, misusing, or outright omitting the scrollbar from their interfaces. Notice isn’t the right word. I’ve been living this trend.

I have a physical disability that, among other things, makes it difficult for me to scroll by using a typical scroll wheel or touch surface. That means I often scroll by clicking and dragging the scrollbar.

It’s likely you haven’t thought about scrolling this way. Maybe you think of it as an old, outdated way of doing it. Or maybe you didn’t even know the scrollbar is draggable. But some people actually scroll that way! And it’s becoming more difficult than it used to be.

Benefits of the scrollbar

The scrollbar has been around for decades now and is an effective, interactive UI control. It gives you quite a bit of immediate visual feedback:

  • That the area is in fact scrollable.
  • About how long a given page of content is.
  • How much of that content you’re viewing right now.
  • Your current location on the page.

The scrollbar is a versatile control. It appears anywhere you might have a long area of content embedded in a window. That could be a single document—like a webpage—or something smaller like a menu of items. One window might have multiple embedded content areas and all of them could use a scrollbar.

It also provides some functionality:

  • You can click and drag a scrollbar to move long distances in a single motion.
  • You can click within the scroll track to either page up and down, or to jump to a specific point (depending on how the the scrollbar is implemented).

You might very well know what a scrollbar is and wonder why I’m rambling on about it. The point I’m trying to make is that this control is a useful, battle-tested one that is everywhere. We need to remind ourselves of that so that we know what we’re losing when we neglect it.

The cost of neglecting the scrollbar

With the proliferation of touch-based scrolling, the role of the scrollbar in modern interfaces has changed. Some might say it has evolved, but I think of it as neglect.

The scrollbar is often relegated to serving as a mere visual indicator. And in some UI patterns, like infinite scrolling, the scrollbar effectively loses all meaning or utility. As a result, they are typically styled to be thin, semitransparent, and sometimes auto-hiding. They’re optimized for being seen when the context is right (e.g., when the user is scrolling). But that comes at a cost.

The interactivity of the scrollbar is diminished when it’s made to be a visual indicator only.

  • Thin scrollbars are difficult to click.
  • Auto-hiding scrollbars can actively prevent scrolling if the only way to reveal them is to scroll the content.
  • Non-clickable scrollbars cause confusion because some users might expect them to be clickable.
  • Neglecting the interactivity of scrollbars takes away multiple methods of navigating a content area.

Example: the bad one

I want to offer a real world example of the struggle1 I go through. One of the worst offenders in my world is the Setapp app explorer (I don’t mean any ill will here—I love Setapp).

Here’s a quick video where I demonstrate the problem.

Here’s what’s happening in the video. I’m looking at the Setapp app’s main view. I’m an onscreen keyboard user so I have my heavily modified macOS Accessibility Keyboard in the frame so you can see it.

Notice that there’s no scrollbar control visible. Hovering my mouse over the rightmost area of the screen doesn’t reveal a scrollbar. I’m able to get the scrollbar to appear by pressing the down arrow key, which scrolls a few lines and causes the scrollbar to become visible. At that point, clicking and dragging works like it normally would.

After a few seconds, the scrollbar disappears. I’m back where I started.

If it wasn’t for that workaround using the arrow key, I’d be physically unable to scroll that view. 2

Example: the good one

One of the best scrolling experiences I have is with Visual Studio Code. As soon as you mouseover the code editor pane you can see the scrollbar. That’s a win right there—you get immediate visual feedback. You can click and drag it as you would expect. And because it’s a code editor, you get a code mini map which also functions as a scrollbar.

The best part is that the size of the scrollbar is configurable. In the video, I set the vertical scrollbar width to a nice, eminently clickable 28 pixels.

Example: the best one

One thing I adore about the web is its near infinite ability to be customized. It’s a foundational concept on the web and it enables powerful accessibility wins for users.

I use the incredible browser extension, ScrollAnywhere. It allows me to scroll webpages mobile-style by clicking and dragging anywhere on the page. This extension is a life saver for me. I want ScrollAnywhere everywhere now. It’s so good.

Considerations for developers

My pitch is simple. Scrollbars are awesome. Use them! I’ll wrap this up with a list of considerations to keep in mind when working with scrollbars, in no particular order.

  • Remember that scrollbars are interactive, not just visual indicators.
  • Don’t hide them without an obvious way to summon them.
  • On macOS, if the user has checked the global setting for always showing scrollbars (Settings > Appearance) don’t hide them at all.
  • Avoid thin scrollbars if you can help it.
  • If you’re customizing the look of a scrollbar, give it enough contrast to be visible.
  • Try clicking and dragging your scrollbar. If it’s difficult for you, the developer, it’s probably difficult for your users.
  • Try not to hijack the scroll. Yes scrolling hijacks make for cool Awwwards websites, but they can be quite difficult to deal with.
  • Don’t be afraid to make the scrollbar bigger!
  • Provide a way to scroll with the keyboard (e.g., up/down arrows). It’s often the expected behavior and can be a safety net for the user in case you screw up the scrollbar for whatever reason (but please don’t screw it up).

  1. There are a lot of struggles that are a much bigger deal than this one. I realize that. But we’re talking about UI here. So take it in context. 
  2. Well, almost. I’d probably launch Talon, for which I have set up a voice command for scrolling. 
]]>
https://blakewatson.com/journal/neglecting-the-scrollbar-a-costly-trend-in-ui-design/feed/ 0 948