Show keystrokes on a website with the <show-keystrokes> custom element

Recording of the <show-keystrokes> Demo & Playground.

~

When recording screen casts or doing live demos, I typically rely on a native macOS app to visualize my keystrokes on screen. Recently, however, that app got blocked on my work machine by a corporate security policy. Additionally, the keystrokes visualized through that app don’t show up when screen sharing a specific window or tab.

Because I needed to demo a website that relies heavily on keyboard navigation — and thus really needed those keystrokes to be visible to the audience — I found myself in a bit of a pickle. So with the help of Gemini, I decided to build a web-native solution to fill that gap: <show-keystrokes>

~

# Embed it and you’re good to go

Using <show-keystrokes> is as simple as loading the module from npm (or a CDN) and dropping the custom element onto your page. Embed it on your site and you’re good to go:

<script type="module" src="https://cdn.jsdelivr.net/npm/show-keystrokes/index.js"></script>
<show-keystrokes></show-keystrokes>

Out of the box, <show-keystrokes> listens for keyboard events and displays keyboard shortcuts (such as ⌘ + A or ⇧ + ⌘ + T) and navigational keys (such as , , , , and ), while ignoring regular typing. If you do want to show every single key press, you can set the keystrokes attribute to all (or narrow it down to shortcuts or navigational).

<script type="module" src="https://cdn.jsdelivr.net/npm/show-keystrokes/index.js"></script>
<show-keystrokes keystrokes="all"></show-keystrokes>

The component automatically adapts to your platform (showing and on macOS versus CTRL and ALT on Windows/Linux), supports both symbol glyphs and text labels ( vs CMD), and ships with two built-in themes that respond to light and dark mode via light-dark(). And oh, it’s fully styleable through by targeting the exposed ::part()s and custom properties.

~

# Powered by the Popover API and CSS Anchor Positioning

Depending on what you are demoing, you might want the keycaps to appear in different places. Using the position attribute, you can easily position the component in a corner of the viewport (the default is viewport top right), right next to the cursor (position="pointer"), or just inline in normal document flow (position="normal").

<!-- Fixed in the bottom-center of the viewport -->
<show-keystrokes position="viewport bottom center"></show-keystrokes>

<!-- Follow the cursor -->
<show-keystrokes position="pointer"></show-keystrokes>

<!-- Render inline in normal document flow -->
<show-keystrokes position="normal"></show-keystrokes>

Under the hood, <show-keystrokes> leans heavily on modern web platform features to make this work seamlessly without any z-index battles or JavaScript bounding-box math.

First, the component always stays on top of everything else on the page thanks to the Popover API. Its internal container is promoted to the browser’s Top Layer using popover="manual", ensuring that no matter how complex the host page’s stacking contexts are, your keystrokes will never be clipped or hidden behind another element.

Second, the component positions and repositions itself thanks to CSS Anchor Positioning. In viewport mode, it anchors itself inside a full-viewport anchor element and aligns itself using position-area, align-self, and justify-self. In pointer mode, an invisible anchor tracks your cursor coordinates (via custom properties), and the popover anchors itself next to it using position-area. The position-try-fallbacks: flip-inline, flip-block, flip-inline flip-block are set by default, so the browser automatically flips the keystroke overlay to the other side of the cursor whenever you move it too close to the edge of the viewport. 🙂

~

# Run it on any site with the Bookmarklet or Chrome Extension

Because you don’t always control the source code of the site you want to demo, the project also comes with both a bookmarklet and a Chrome Extension so you can inject <show-keystrokes> onto any webpage on the fly.

The bookmarklet is a quick drag-and-drop button (available on the demo page) that dynamically imports the package from jsDelivr and calls the programmatic create() helper. For sites with a strict Content Security Policy (CSP) or when you want <show-keystrokes> to persist across page navigations, the Chrome Extension (currently pending review on the Chrome Web Store) has you covered. It bundles the component locally, lets you configure global defaults as well as per-origin overrides right from the popup, and includes a handy ⇧ + ⌘ + K (or ⇧ + CTRL + K) shortcut to toggle it on or off.

The Chrome Extension was a bit of an afterthought once I had version 1.0.0 of the component ready. But thanks to Modern Web Guidance and its Chrome Extensions skill, creating it was easy-peasy and done in no time (also see this guide) 🙂

~

# Links

~

# Spread the word

Feel free to reshare one of the following posts on social media to help spread the word:

~

🔥 Like what you see? Want to stay in the loop? Here's how:

I can also be found on 𝕏 Twitter and 🐘 Mastodon but only post there sporadically.

Published by Bramus!

Bramus is a frontend web developer from Belgium, working as a Chrome Developer Relations Engineer at Google. From the moment he discovered view-source at the age of 14 (way back in 1997), he fell in love with the web and has been tinkering with it ever since (more …)

Unless noted otherwise, the contents of this post are licensed under the Creative Commons Attribution 4.0 License and code samples are licensed under the MIT License

Join the Conversation

1 Comment

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.