webdevpuneet.com

App Switcher Overlay — Free HTML CSS JS Snippet

App Switcher Overlay · Navigation · Plain HTML, CSS & JS · Live preview

CategoryNavigation

Copy & paste

Source Code

<div class="demo-page">
  <div class="demo-hint">
    <p>Press <kbd>Alt</kbd> + <kbd>Tab</kbd> (or click below) to open the switcher, then <kbd>Tab</kbd> / arrow keys to cycle and <kbd>Enter</kbd> to select.</p>
    <button class="open-btn" id="open-btn">Open app switcher</button>
  </div>
  <p class="active-app">Active app: <strong id="active-app-name">Dashboard</strong></p>
</div>

<div class="switcher-overlay" id="switcher" hidden>
  <div class="switcher-panel">
    <div class="switcher-grid" id="switcher-grid"></div>
    <p class="switcher-hint">Tab to cycle &middot; Enter to open &middot; Esc to cancel</p>
  </div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Genuine hold-to-cycle interaction: keydown advances focus while Alt+Tab is held, keyup on Alt triggers selection
Full two-dimensional arrow-key navigation (left/right by one, up/down by a full row) over a flat data array
Index wrapping via modulo arithmetic so navigation past either end of the list cycles around cleanly
Opens pre-focused on the next item after the currently active app, not the current app itself
Mouse hover and keyboard navigation share one setFocus() function, keeping visual state always consistent
Click and Enter/Alt-release selection paths share one selectApp() function
Escape key and backdrop click both close the overlay without changing the active app
Self-contained overlay with a blurred backdrop — no dependency on a specific app framework or router

About this UI Snippet

App Switcher Overlay — Hold-to-Cycle Key Handling and 2D Arrow-Key Grid Navigation

Screenshot of the App Switcher Overlay snippet rendered live

Desktop operating systems and many productivity suites share a familiar interaction: hold a modifier key, tap Tab repeatedly to cycle through open apps or recent items in a floating grid, and release the modifier to jump to whichever one is highlighted. This snippet implements that exact interaction model as a self-contained overlay — a grid of app cards, full keyboard cycling in two dimensions, and a genuine hold-and-release selection flow — for use inside a web app's own command surface rather than the OS-level switcher.

Hold-to-cycle via keydown and keyup working together

The core interaction spans two separate event listeners cooperating on shared state. A keydown listener detects Alt held together with Tab: on the first press it opens the overlay via openSwitcher(), and on every subsequent press while still open it advances focusedIndex by one (or backward with Shift) via setFocus() — exactly mirroring how holding Alt and tapping Tab repeatedly cycles further through a real OS switcher without closing it. A separate keyup listener watches specifically for the Alt key being released, and when it is, calls selectApp(focusedIndex) immediately — so releasing the modifier key is itself the selection gesture, not a separate click or Enter press, matching the muscle memory of the real OS pattern.

Two-dimensional arrow-key navigation over a 1D data array

The apps are stored as a flat array, but rendered into a four-column CSS grid, so up/down arrow navigation needs to move by a full row rather than by one array index. ArrowDown/ArrowUp add or subtract 4 (the fixed column count) from focusedIndex inside setFocus(), while ArrowLeft/ArrowRight and Tab/Shift+Tab move by exactly one. setFocus() wraps the resulting index with (i + APPS.length) % APPS.length, so navigating past either end of the flat array wraps around to the opposite end cleanly, regardless of whether the movement came from a row-jump or a single-step arrow key.

Opening pre-focused on the next item, not the current app

When the switcher opens, openSwitcher() deliberately does not focus the currently active app first — it looks up the active app's index and immediately focuses the *next* one ((startIdx + 1) % APPS.length). This mirrors real app switchers, where the very first Tab-hold already lands you on the second-most-recent item rather than re-highlighting the app you're already in, since selecting the app you're already using would be a no-op.

Mouse and keyboard sharing one focus model

Every .switcher-card also has a mouseenter listener wired to the same setFocus(i) function the keyboard uses, and a click listener wired to the same selectApp(i) function that Enter and Alt-release trigger. Because both input methods drive identical shared functions rather than separate parallel logic, the visual "focused" state (a highlighted border and background) and the selection behavior stay perfectly consistent regardless of whether the user is navigating with a mouse, a trackpad, or purely the keyboard.

Step by step

How to Use

  1. 1
    Open the switcherClick "Open app switcher" or hold Alt and press Tab. openSwitcher() shows the overlay and focuses the item just after the currently active app.
  2. 2
    Keep holding Alt and tap Tab to cycleEach additional Tab press while Alt is held advances focusedIndex by one via setFocus(), exactly like a real OS app switcher — Shift+Tab moves backward.
  3. 3
    Or navigate with arrow keysLeft/Right move one card at a time; Up/Down jump by a full row (4 cards) since the grid is two-dimensional but the underlying data is a flat array.
  4. 4
    Release Alt, or press Enter, to selectReleasing the Alt key (detected via a keyup listener) immediately selects whichever card is focused, matching real hold-and-release switcher behavior. Enter does the same without requiring a held modifier.
  5. 5
    Click a card directly, or press Escape to cancelClicking any card selects it immediately regardless of keyboard focus. Escape, or clicking the dimmed backdrop, closes the overlay without changing the active app.
  6. 6
    Edit the apps listUpdate the APPS array with your own name, color, and glyph (a short initials string) for each entry — buildGrid() regenerates the full card grid from whatever entries exist.

Real-world uses

Common Use Cases

In-app workspace or module switcher
Give a multi-module product (dashboard, inbox, calendar, files) a fast, keyboard-first way to jump between sections, similar to the command palette but optimized for visual, icon-based recent-item switching instead of text search.
Recent documents or recent workspaces overlay
Reuse the same hold-to-cycle pattern for a "recent files" or "recent projects" switcher, swapping app icons for document thumbnails or project avatars.
Multi-account or multi-tenant switcher
Let users holding a shortcut cycle between connected accounts, organizations, or tenants in a SaaS product without leaving the keyboard.
Reference for hold-and-release keyboard interaction patterns
A clear example of coordinating keydown and keyup listeners to implement a modifier-key-held interaction, useful for any UI that needs "hold to preview, release to commit" behavior.
Power-user productivity tool onboarding
Demonstrating this pattern in a product tour helps power users discover a faster way to move around than clicking through a sidebar nav.
Related: Voice Command Navigation Menu
See the Voice Command Navigation Menu for a related navigation pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

A keydown listener opens the switcher and advances the focused index on repeated Alt+Tab presses while the key combination is held. A separate keyup listener specifically watches for the Alt key being released and, if the switcher is open, immediately calls selectApp() on whatever card is currently focused — so releasing the modifier key is the selection trigger, not a separate action.

The apps are stored as a flat one-dimensional array but rendered into a 4-column CSS grid. Moving up or down visually means jumping a full row, so ArrowUp and ArrowDown add or subtract 4 (the fixed column count) from the focused index rather than 1, which is what Left/Right and Tab use.

setFocus() wraps the resulting index using (i + APPS.length) % APPS.length, so moving past the last item cycles back to the first, and moving before the first item cycles to the last, regardless of whether the navigation came from an arrow key or a Tab press.

openSwitcher() intentionally focuses the item immediately after the currently active app's index, because selecting the app you are already in would be a no-op — this mirrors how real OS-level app switchers land on the next most relevant item rather than re-highlighting your current app.

Yes. Pressing Enter while the switcher is open calls the exact same selectApp() function that releasing Alt triggers, and clicking a card directly also calls it — the modifier-hold pattern is one of three equivalent ways to select an item, not the only one.

Yes. Use the JSX, Vue, Angular, or Tailwind export buttons on this page. In React, keep focusedIndex and isOpen in state, and wire the same keydown/keyup logic to window listeners inside a useEffect that adds and cleans up the listeners.

Yes. While the App Switcher Overlay snippet gives you one pattern to copy, the free Responsive Navbar Builder at webdevpuneet.com/navbar-builder/ designs a whole navigation bar: logo, links and dropdown menus, a mobile hamburger menu (dropdown panel, slide-in drawer or full screen) and a scroll effect (sticky, shrink, hide on scroll or transparent). It has six presets, a live preview that runs the real code, and exports HTML + CSS + vanilla JS, React, Tailwind, Vue 3 or Angular — or you can click Fork & Edit to keep editing it in My Code.