A Hammerspoon script that toggles Chrome's native vertical tab sidebar via the macOS Accessibility API
Keyboard shortcut, mouse edge trigger, or both — your choice.
简体中文 · 繁體中文 · 日本語 · 한국어 · Español · Português · Русский · Français · Deutsch
Chrome has a built-in vertical tab sidebar, but no keyboard shortcut to toggle it. This script solves that with two versions:
init.lua— supports three selectable schemes (keyboard / mouse edge / both)init-keyboard-only.lua— keyboard shortcut only, no mouse detection
It works by traversing Chrome's Accessibility tree (AXUIElement) to find the "Expand Tabs" / "Collapse Tabs" button and pressing it via AXPress. Same approach as ChromeSidebarToggleRaycast.
demo.mov
The script matches sidebar button labels from Chrome's accessibility tree. It works out of the box with any of these locales:
| Language | Locale | Expand Tabs | Collapse Tabs |
|---|---|---|---|
| English | en |
Expand tabs | Collapse tabs |
| Traditional Chinese | zh-TW / zh-HK |
展開分頁 | 收合分頁 |
| Simplified Chinese | zh-CN |
展开标签页 | 收起标签页 |
| Japanese | ja |
タブを開く | タブを閉じる |
| Korean | ko |
탭 펼치기 | 탭 접기 |
| German | de |
Tabs maximieren | Tabs minimieren |
| Spanish | es |
Mostrar pestañas | Ocultar pestañas |
| French | fr |
Développer les onglets | Réduire les onglets |
| Portuguese (Brazil) | pt-BR |
Mostrar guias | Ocultar guias |
| Russian | ru |
Развернуть вкладки | Свернуть вкладки |
To add another language, find the button label in your Chrome locale and append it to the SIDEBAR_LABELS table in init.lua.
Chrome may append a keyboard shortcut to the accessibility label, for example Expand Tabs (⇧⌘L). The matcher accepts the configured label with or without a trailing parenthesized shortcut.
- macOS 13+
- Hammerspoon
- Google Chrome with vertical tab sidebar enabled
- Accessibility permission granted to Hammerspoon
The vertical tab sidebar is not enabled by default. To turn it on:
- Type
chrome://flags/#vertical-tabsin the address bar - Set Vertical tabs to Enabled
- Click Relaunch to restart Chrome
- After restart, right-click an empty area in the tab bar to see the option
-
Install Hammerspoon:
brew install --cask hammerspoon
-
Choose a version and copy to your Hammerspoon config:
Scheme version (three modes, default):
cp init.lua ~/.hammerspoon/init.luaKeyboard-only version:
cp init-keyboard-only.lua ~/.hammerspoon/init.luaIf you already have a
~/.hammerspoon/init.lua, append the contents instead. -
Grant Accessibility permission:
- System Settings → Privacy & Security → Accessibility
- Add and enable Hammerspoon
-
Reload Hammerspoon config (click menu bar icon → Reload Config)
-
(Optional) Add Hammerspoon to login items so it starts automatically:
- System Settings → General → Login Items
- Add Hammerspoon
Edit the SCHEME variable at the top of init.lua to choose a mode:
| Scheme | Value | Triggers |
|---|---|---|
| Keyboard only | 1 |
Cmd+S toggles sidebar |
| Mouse edge only | 2 |
Hover left edge to expand, move beyond 380px to collapse |
| Keyboard + Mouse | 3 |
Both triggers active (default) |
local SCHEME = 3 -- 1 = Keyboard, 2 = Mouse edge, 3 = BothAll triggers are automatically disabled when Chrome is not the frontmost app.
| Trigger | Action | Scheme |
|---|---|---|
Cmd+S |
Toggle sidebar | 1 & 3 |
| Mouse hover at left edge (0-2px) for 0.15s | Expand sidebar | 2 & 3 |
| Mouse moves beyond 380px from left edge | Collapse sidebar | 2 & 3 |
| Shortcut | Action |
|---|---|
Cmd+Alt+D |
Show service status |
Cmd+Alt+B |
Dump all Chrome AX buttons to Console |
Cmd+Alt+R |
Force restart all services |
local SCHEME = 3 -- 1 = Keyboard, 2 = Mouse edge, 3 = Bothlocal EDGE_THRESHOLD = 2 -- px from left edge to trigger hover
local EXIT_THRESHOLD = 380 -- px from left edge to trigger collapse
local WAIT_TIME = 0.15 -- seconds to hover before triggering
local MOUSE_POLL_INTERVAL = 0.05 -- seconds between mouse position checkslocal DEBUG = true -- print debug messages to ConsoleAvailable in both init.lua and init-keyboard-only.lua. The default shortcut is Cmd+S, which overrides Chrome's native "Save page" shortcut. To change it, edit the key check in the createKeyTap function:
-- Cmd+S -> toggle sidebar
if flags.cmd and not flags.ctrl and not flags.alt and not flags.shift
and keyCode == keycodes.map["s"] thenChange the flags.* conditions to set your desired modifier combination:
| Modifier | Flag | Example |
|---|---|---|
| Cmd | flags.cmd |
flags.cmd and not flags.ctrl |
| Ctrl | flags.ctrl |
flags.ctrl and not flags.cmd |
| Alt/Option | flags.alt |
flags.alt |
| Shift | flags.shift |
flags.shift |
Set the flag to true to require it, not flags.xxx to exclude it.
Change keycodes.map["s"] to any key name. Common examples:
keycodes.map["s"] -- S
keycodes.map["b"] -- B
keycodes.map["/"] -- /
keycodes.map["return"] -- Return/Enter
keycodes.map["space"] -- Space
keycodes.map["f1"] -- F1Full key name list: run hs.keycodes.map in Hammerspoon Console.
Ctrl+Shift+B:
if flags.ctrl and not flags.cmd and flags.shift and not flags.alt
and keyCode == keycodes.map["b"] thenCmd+Alt+/:
if flags.cmd and not flags.ctrl and flags.alt and not flags.shift
and keyCode == keycodes.map["/"] thenCmd+Shift+Return:
if flags.cmd and not flags.ctrl and not flags.alt and flags.shift
and keyCode == keycodes.map["return"] thenAfter editing, reload Hammerspoon config to apply.
- An
eventtapinterceptsCmd+Swhen Chrome is frontmost (schemes 1 & 3) - A mouse position poller (50Hz) detects left-edge hover and exit (schemes 2 & 3)
- Both triggers call
toggleSidebar()which:- Gets Chrome's
AXUIElementroot viahs.axuielement.applicationElement() - Searches windows for a button whose
AXTitleorAXDescriptionmatches a configured label, with or without a trailing parenthesized keyboard shortcut - Calls
performAction("AXPress")on the found button
- Gets Chrome's
- A watchdog detects if the mouse poller dies and auto-restarts (schemes 2 & 3)
- Grace periods prevent false triggers during app switching
| File | Description |
|---|---|
init.lua |
Three-scheme version (keyboard / mouse / both) |
init-keyboard-only.lua |
Keyboard-only version, no mouse detection |
- Original concept: ChromeSidebarToggleRaycast by RotulPlastik
- Adapted for Hammerspoon with mouse-edge trigger support
MIT
