I didn’t know much about keyboards going into this project. As a software engineer on Figma's Editor Usability team, my day-to-day typically focuses on the design editor experience. Turns out, keyboard shortcuts are a big part of that. These preset key combinations are essential for efficient, intuitive workflows and have a host of accessibility benefits—such as making it easier to access menus and functions. Yet Figma’s many shortcuts were originally designed based on US keyboards, which meant that they didn’t work for many other types of keyboards.
Our team began seeing a steady stream of shortcut-related bug reports as users encountered instructions in Figma like “press
⌘ + \ to toggle the UI”—despite their keyboards not having a
\ (back slash) key—or barring folks from cursor chat conversations because it was impossible for them to press
/ (forward slash) to initiate messages. Figma users are passionate about their workflows and, in turn, their keyboard shortcuts. We wanted to make sure that everyone, everywhere, could access the same keyboard efficiencies. We brought together a cross-functional team to begin working on making Figma shortcuts friendlier to international keyboards, a charter that would take us on a twisty journey through the next year.
When you press any key, the browser sends Figma standard
KeyboardEvents with information about which key was pressed. Our first step after receiving a
KeyboardEvent is to translate it into something that our editor can interpret. Separately, we specify possible keyboard shortcuts and the actions they trigger in a JSON file. These actions vary based on whether the shortcuts themselves are enabled or disabled, which depends on user and product state, aspects like preferences, which product they’re in, the operating system (OS), and more. Once we receive a user’s key press, we run it against that list of possible shortcuts. If there is a match, we execute the associated action (and if not, nothing happens). Et voila ✨ a frame is pasted, the UI is toggled, a cursor chat bubble appears.
At face value, it would seem that all we needed to do was add some new shortcut definitions to the JSON file. If a user has a Swedish keyboard, give them the mapping
Meta + Ä for “Bring forward” instead of
⌘ + ]. If a user has a Korean keyboard, give them the mapping
₩ for "Toggle UI" instead of
⌘ + \.
This is where the rabbit hole starts. 🐇🕳
On a German keyboard, the shortcut for “Decrease text weight” is
Meta + Alt + ß. Seems straightforward enough, yet this particular keyboard shortcut did not work when plugged into our keyboard shortcuts JSON. It turned out that when we tried to normalize shortcuts by capitalizing them, we saw that
SS, causing unexpected behavior when transforming a single key character
ß into two
SS. Most unintuitively, that means
"ß".toUpperCase().toLowerCase() does not give us
ß, the original character.
Beware! We worked around this by using the new uppercase eszett character,
ẞ, which thankfully remains the same when upper-cased.
What? A new capitalized letter? In 2017, the new capital eszett character was officially adopted by the German spelling council, though the character has existed in fonts before that. Coding languages have not entirely caught up yet, it seems.
With an overwhelming number of possible keyboard layouts, we wanted to start by supporting shortcuts for keyboard layouts that our users most commonly use. Gathering this information was easier via the desktop app, where we are able to detect the OS keyboard setting. However, on browsers, the best we could do was make guesses based on heuristics involving the experimental Keyboard API, which has its own limitations. Here’s how our heuristics work: This API tells us the character associated with each positional key code for the current user, which we then map to known keyboard layouts. For example, we might get data that the keyboard’s
Quote code has the
ä character, and, in combination with other such mappings, we can extrapolate that perhaps the user has a Swedish keyboard.
With keyboard detection logging in place, we saw over 2.5k distinct layouts used on Figma within 30 days! I didn't even know this many keyboard layouts existed, and it turns out that there have always been thousands of keyboard layouts. Marcin introduced us to these century-old Remington Typewriter layouts for Domestic, Swiss-Standard, and Swedish. Put a pin in this detail, because the struggle of keyboard detection comes back later.
Combining our detection data with forum feedback posts, we narrowed down our initial work to a (not so short) shortlist of standard keyboards: German, French AZERTY, Japanese, British, Swedish, Finnish, Danish, Norwegian, Italian, Spanish, Spanish LATAM, Chinese, Portuguese, and Korean.
With keyboards identified, the next step was auditing which keyboard shortcuts were problematic, either due to missing keys, physical inaccessibility, or conflicts with other shortcuts. Almost all of these shortcuts involved symbol characters (as opposed to roman letters), which cut down the number of shortcuts to investigate.
As a graphic tool and a text editor that exists within the realm of a browser (which also exists within an operating system)—Figma inherits all the shortcuts from each of these spaces, many of which are already in conflict with each other. Keeping a holistic view of where Figma fits within these various ecosystems is important when developing shortcuts that will impact a user’s workflow. For example, we want to maintain motor memory that users may already have from other tools, aligning our shortcuts to similar actions (say, copy, paste, zoom). At the same time, we need to avoid duplicate shortcut conflicts not only between existing Figma shortcuts, but also between shortcuts defined by the OS or other major desktop applications.
Additionally, there are many other considerations when adapting existing US QWERTY keyboard shortcuts to a swath of new keyboard layouts:
⌘key on Mac is usually used like the
Controlkey on Windows, but there is still a
Controlkey on Mac that is used for some shortcuts, adding edge cases and exceptions to this OS modifier key translation.
One of our next product questions was how to store users’ keyboard preferences. My initial intuition was that, like most preferences, keyboard layout preference should also be assigned per user. But keyboard preferences are not just a software artifact, they are indelibly tied to physical objects: keyboards themselves. And so, we had to consider the case where the same user might have multiple devices, perhaps one at work and one at home, with different keyboard layouts.
We decided to store keyboard layout preferences on device
localStorage, so that different devices can have different default Figma keyboard layouts—while also writing user preferences to a database. If a user clears their browser cache (which includes
localStorage), or logs in to a brand new device, we can still load their most recent preference from our backend. While this storage system was complex, we knew it would result in a better user experience.
To return to the challenge of remapping these shortcuts: On a French AZERTY keyboard, the shortcut for “Unindent text” is
Meta + ^. To be extra explicit, if a user presses
Meta + ^, we want their text to unindent, and not output a
^ character. Unfortunately,
^ is a diacritic key, meaning that it adds a glyph on top of another character. When
^ is pressed on an AZERTY keyboard, the browser tells us, “this is a dead key”* to indicate that we’re waiting to compose it with another key, and throws us a bunch of other KeyboardEvents that we need to handle in order to prevent
^ from appearing.
But of course, browsers are inconsistent, and in particular, Safari swaps the order of
compositionstart events for diacritic keys, leading us to emit the
^ character when we should be executing an action.
So, whereas Chrome orders events:
Safari orders them:
We rely on the keydown event to understand whether the shortcut is accepted or not, and based on that, will reject characters in the
input event listener. On Safari, with these events out of order, that logic breaks. Practically, this means that our shortcut for unindenting doesn’t work on Safari.
*An aside from our resident keyboard expert Marcin: “Dead keys” come from the world of typewriters, where a key would output an accent, but not advance the carriage—since they didn’t move, they were “dead.” The correct sequence of events was “assembling” a letter by typing a dead key first to put the accent on the page, and then a regular letter on top of it.
On all keyboards, “Zoom reset” is
Shift + 0. An innocuous shortcut, unless you extensively use the numpad on your keyboard. Pressing
Shift in combination with a numpad key overrides numlock, and perhaps surprisingly, means “Zoom reset” doesn’t work. Marcin tweeted about this bug quirk in 2021, the result of decisions made by other designers/manufacturers decades ago—which is basically the story of keyboards in a nutshell.
Once we finished supporting all these new keyboard shortcuts for all these keyboard layouts, we were still barely halfway done. We needed to notify non-QWERTY users that they have more keyboard shortcut coverage now, without bothering QWERTY users for whom nothing changed. Additionally, if users change their physical or OS keyboard layout, we didn’t want to add confusion as to why there might now be a mismatch between their Figma keyboard shortcuts and what they see at their fingertips. The team drew up several flowcharts for handling such cases, where the path branches based on Figma desktop app versus browser, the user’s existing Figma keyboard layout preference (if any), and the user’s system keyboard layout.
Re-enter the uncertainty of user keyboard layout detection.
On the Figma Desktop app, we can see users’ OS keyboard layout as their string names:
org.sil.ukelele.keyboardlayout.armenianphonetic.armenian-phonetic. Given this information, it’s not immediately clear what Figma keyboard layout we should recommend to the user. We have to just live with the uncertainty, where obvious matches—
com.apple.keylayout.US is a US QWERTY layout on Figma,
0000040C is a French AZERTY layout on Figma—can be used to help guide users’ Figma keyboard layout preference, but otherwise we fall back to doing nothing. (And even this has its pitfalls: In the middle of this project, Apple switched the name of some of its Japanese layouts, which broke our native internal mapping. Specifically,
com.apple.inputmethod.Kotoeri.KanaTyping.Japanese.Katakana. This caused us to switch to prefix-matching, which, while still imperfect, is better than exact string matching.)
On browsers, this decision tree gets even fuzzier because of the challenge in actually detecting what keyboard layout a user has. Not only do we get less coverage because the KeyboardLayout API is not available on all browsers, but bridging the gap between that data and an actual layout is an imperfect process. There are several layouts that we cannot detect at all via this method on browsers.
To add even more complexity, it is a common pattern to regularly and frequently toggle between a roman character keyboard layout and a non-roman character layout at the OS level. For Japanese users, for example, it’s common to switch between Japanese and ABC layouts multiple times in a session, while using the same physical keyboard. It’s ok for your keyboard shortcuts to be in a different language than your words; not all keyboard layout changes necessitate changing your Figma keyboard layout settings.
Here’s a secret: We came very close to launching a version of international keyboard shortcut support with an option that would auto-update a user’s keyboard layout when a system keyboard change was detected. But all the aforementioned challenges around how immediate our keyboard detection technology could be made us pause and think about what we were actually trying to accomplish. In the end, we abandoned the idea. At best, it would alert people when they switch keyboard layouts, and at worst, cause unintended changes to their settings. We scratched several iterations of our onboarding and keyboard detection decision tree, aiming to be as minimally intrusive as possible.
Our final design touch? Adding a visualization of keyboard layouts in the settings pane. There are tons of keyboard layouts (and customizable for those inclined), so it's not always easy to identify one by name alone. Our solution: Diagram all supported Figma keyboard layouts, thus giving people a way to visually that confirm their Figma keyboard matches their physical keyboard.
Alright, now let’s
Shift + - (“Zoom out”): In November, we shipped keyboard shortcut support for a set of non US QWERTY keyboards. We emerged from the other side of the rabbit hole. That being said, this work is never done! We continue to add new shortcuts, refine existing ones, and add guardrails to prevent regressions. Our keyboard layout coverage is far from comprehensive, and we welcome your requests via this Google form.
Much thanks to the entire team: Marcin Wichary (designer and resident keyboard expert), KC Oh (product manager), Rachel Miller (engineering manager), Sula Yang (product marketing manager), and Michael Feldstein (engineer).