A mixed blessing

The otherwise excellent note-taking app Bear has an interesting bug that’s worth talking about while it’s still here.

When you’re around to-do items, you can press ⌘. (period) to toggle any task complete or incomplete. It’s actually a really fun shortcut in practice:

But when you have a larger selection with a mixed state (some checkmarks are on, others off), this is what happens:

This feels like an obvious thing to implement, and this is also where the code itself wants to go when left alone.

But this is not great. The rule is: When you have a mixed state, changing it should collapse (or: normalize) the entire selection to one state or the other, rather than perform individual inversion. Try ⌘B in your text editor on partially bolded text, and you can see that collapse in action:

It feels strange to recommend that, particularly as it seems like it loses data. So, what gives?

The first argument is “do not make the user jump through hoops” or maybe “respect a large selection.” If, as a user, I want to actually make sure all my tasks are done, the shortcut not being idempotent means that I now have to go through tasks one by one, and that’s a lot of work – especially since we’re talking about text selection, which is famously unpleasant.

The second reason is that even the UI layer has an opinion here. In the above bolding example, Pages collapsing the selection to bold when you press the B toggle, makes the toggle UI behave exactly as it normally would with a simple selection:

Elsewhere, in Figma, typing a number on top of a “mixed” state changes all the properties of relevant objects to that number:

Imagine how awful it would feel mechanically in both the above examples if your action would still leave the text in the “mixed” state. It would simply appear like the UI broke, since the change didn’t fully “stick” – kind of like those tiny hated moments when you close the door, but it doesn’t latch on and reopens on its own, or when you engage the turn signal stalk, but it refuses to stay put and snaps right back.

There is also one last reason. It’s the simplest one that I sometimes have to remind myself to put in my head before I jump too deep into the mechanics, or details, or technical nuances. Let’s say the toggles invert individually on a large selection. Who would ever benefit from it behaving this way?

“This is a common tell in web apps, and we did a lot of work to eliminate it.”

I have mixed feelings about Raycast announcing their move from the native interface to one powered by web tech (this is the same thing that made Photoshop’s dialogs so bad), but their blog post announcing the change has at least a useful list of some details that separate good native apps from bad web ones.

I think it’s worth checking out that list and internalizing it even if you’re nowhere near that kind of a decision, because some of these are universal requirements for a better-feeling interface:

  • No cursor: pointer on interactive controls. Desktop apps don’t do this. It’s small, but it immediately signals “this is a website.”
  • No hover highlights on most controls. On macOS, buttons and list items don’t highlight on hover the way they do on the web.
  • Settings open in a separate native window, not a modal or a side panel.
  • Popovers and tooltips render as native windows, not as DOM elements inside the WebView. They can extend beyond the window bounds, just like native popovers do.
  • On macOS Tahoe, we adopted Apple’s new Liquid Glass material so Raycast blends with the system’s updated visual language from day one.
  • No flickering when views appear or transition. This is a common tell in web apps, and we did a lot of work to eliminate it.

There is more in the blog post, and a lot more still left unsaid. Let me add one that I see all the time: accidental text selection. Web makes all text selectable by default, regardless of whether it makes sense for that text to be selected. On top of that, text selection heuristics on complex layouts are not that great. That means that surprisingly often you will see half a text on the page being selected in response to an accidental click or drag.

Here’s an example from YouTube I just spotted, where dragging a sidebar selects everything inside it:

It’s all solvable via the use of event cancellation and user-select, but requires someone to think about it happening.

Yes, there are moments where GUIs allow you to select text for a reason…

…but it’s always been a tricky proposition given the scarcity of affordances. It might be better to employ a pretty common “copy to clipboard” pattern instead.

What deserves a second chance

To follow up from yesterday’s post, in Figma, object selection actually goes onto the undo stack. This is because in a professional tool with objects in multiple levels of hierarchy, it might take a while to construct a selection to work on – and since selection is always just one accidental click away from being completely cleared, undoable selection is extra protection.

However, at the same time renaming a file – or changing settings like file access – is not undoable. This is in part because we didn’t feel people would understand they could cancel out their rename this way (Safari too used to have “reopen last tab” under ⌘Z, until it reverted to Chrome’s ⌘⇧T), but mostly because you could accidentally undo through a file rename during regular work if you were not careful, without noticing, and that felt like it’d have more profound consequences.

In some ways, it helped me to think of these not as “ineligible for undo” but rather “living outside of time.” The moment a file is renamed, it will always have been named that way. (For the purposes of undo, at least. You can acknowledge anything you want on the version history screen.)

I’m not saying these are universally correct choices – as a matter of fact, some users find undoable selection (at least initially) pretty confusing! – but mostly sharing these as examples of intentional thinking about what deserves undo, and what should be exempt from it and taken care of elsewhere.

“Naïve, simple, not good enough.”

This is a thoughtful post from Florian Schulz about designing a typeahead experience.

I liked the details both within the implementation – for example, making sure the kerning is preserved! – but also in the presentation. I particularly enjoyed Schulz making the component demo itself, rather than using prerecorded videos. (I was delighted to discover that even the first large “picture” of the component is actually interactive!)

A small comment to this bit:

Unfortunately, not all browsers expose the selection or accent color of an operating system. For example, if a user would set the accent color in macOS to pink, the special CSS keyword color “Highlight” will still result in a light blue color in Safari. In other browsers like Chrome, the color will match the user preference. But since this is an attack vector for user tracking / fingerprinting, Apple made the right choice to hide the user preference from developers.

From my understanding, this is not necessarily correct. For example, in theory, the purple visited link color can be used for fingerprinting, by building a profile of whether or not I visited one of the hundreds of popular websites, quietly in the background.

The way browsers solve this is to never expose the color programmatically back to JavaScript – if your code asks for a link color, it will be blue regardless of whether the link was visited or not. It seems to me that the Highlight color could be used the same way here. Given that CSS now supports things like color-mix(in srgb, Highlight 20%, white), it would even allow a designer to riff on the color without ever knowing what it is.

Deterministic vs. idempotent

Those terms confused me back in the day, and occasionally they still do, so I thought it might be nice to write it all out. This is how I understand them:

  • deterministic: whenever you do something, it gives you identical results as any previous or next time you do it
  • idempotent: if you do something twice, or thrice, or more times, it will be the same as doing it once

Or, in short:

  • deterministic: same every first time
  • idempotent: same every next time

This might be confusing. Outside of LLMs, computers are supposed to be deterministic, no? They’re famously bad at random numbers; if you memorize a pattern, you can beat Pac-Man every single time.

But “deterministic” in UI design might mean something more specific. Let’s take pressing ⌘B to bold, for example:

Every time you press ⌘B on an identical selection, it will behave the same. But, pressing ⌘B doesn’t guarantee something will get bold. If it already is bold, the command will reverse its meaning. In this sense, ⌘B is non-deterministic.

It’s not hard to imagine a determistic version of bolding. Make ⌘B bold the text, and make another shortcut – say, ⌘U – unbold it. This way, you can always press either and be absolutely confident you will get a predetermined result without worrying about anything else. It’s a boon for motor memory, but it is more complex to explain, and it adds more UI surface.

There is also another, more interesting way: you can make ⌘B always bold first, and unbold second. This way, your fingers can remember ⌘B is for bolding, and ⌘BB is for unbolding. But this also gets tricky: for already fully bolded text, it might seem the feature is broken, because the first keystroke does nothing!

Only the second of these three approaches is idempotent,meaning you can invoke it many times and it will always give the same result:

  • ⌘B toggles bolding from current state: non-deterministic, non-idempotent
  • ⌘B bolds, ⌘U unbolds: deterministic, idempotent
  • ⌘B toggles bolding, always starts from bold: deterministic, non-idempotent

One of my favorite idempotent concepts is the Clear key present on many calculators, and still on some larger Mac keyboards.

The idea behind Clear is simple. Let’s say you’re a professional keypad user – maybe an accountant? – typing in numbers for hours a day. You just made a mistake. Pressing Backspace will remove the last digit, but are you sure you only made a mistake on that last digit? What if your fingers brushed another key and you typed in two digits by accident?

Instead of using the non-idempotent Backspace key where you’d have to look at the screen to confirm, it’s easier just to press the idempotent Clear which will always remove the entire number, and then start from scratch without even having to look anywhere (as gamers would say, “no scope!”).

And, for people who are moving fast, it feels safe just to press the shortcut or a button instinctively, for ease of their mind, even if nothing has to be done. Some people might choose to press it a few times, just in case. The Esc key often has that property – isn’t it just nice to slam Esc many times? – and Jeff Jarvis in his 2014 essay talked about another shortcut that felt that way:

Since I don’t need ⌘S anymore, I can now appreciate how much it had become a part of my ritual of writing and even of thinking. I used to hit ⌘S not just as data insurance — hell, I’d often hit it after having not made a single change in my text since the last time I’d hit it. I hit ⌘S as a break, a psychic, semiotic semicolon. It gave me a moment to search for the right word, to plan the structure of where I would go next, to commit to what I’d written, or to wonder whether I had the courage to erase what I’d written and try again.

(In Figma, where ⌘S wasn’t necessary, we used to show this – but we only showed it once every fortnight, since some people would press the shortcut instinctively like Jarvis, and find the message distracting and maybe even patronizing.)

All of these options have pros and cons. The beauty of determinism and idempotence is that they free you from paying attention. I always get a bit nervous when someone tells me that in their country, you can press the elevator button again to unset it. Even if you don’t make a button a toggle, visually disabling it or showing a message (“Nothing to delete!”) when it has nothing to do could feel like a friendly gesture toward newcomers, but its non-idempotence will grate people who know what they’re doing. Determinism and idempotence are good for motor memory to develop, but – just like the above bolding example – might be initially more confusing.

The approaches can coexist; browsers give you ⌘⌥←→ to move between tabs (non-deterministic, non-idempotent) and ⌘1/2/3 to switch to a specific tab (deterministic, idempotent). Some places even offer a choice. In macOS, you can say whether you want clicking on a scrollbar chute to be deterministic or not:

…although usual choice-giving caveats should apply.

I think it’s good to think about those things, especially for interfaces used professionally. Magical things happen if you can trust your fingers and sometimes, if you worry too much about novice users, that might make it hard for pro users to emerge.