n updates.
- In-app discovery modals (
? trigger) increase shortcut adoption by 3.2x within the first week.
Sweet Spot: Combine manifest.json global command declarations with context-aware DOM event listeners, backed by MV3 programmatic management and a lightweight discovery modal. This architecture delivers native-like responsiveness while maintaining conflict safety and cross-platform parity.
Core Solution
1. Manifest Declaration
Define global commands with platform-aware key mappings. The _execute_browser_action special command maps directly to the extension popup.
{
"manifest_version": 2,
"commands": {
"toggle-dark-mode": {
"suggested_key": {
"default": "Ctrl+Shift+D",
"mac": "Command+Shift+D"
},
"description": "Toggle dark/light mode"
},
"refresh-weather": {
"suggested_key": {
"default": "Ctrl+Shift+R"
},
"description": "Refresh weather data"
},
"_execute_browser_action": {
"suggested_key": {
"default": "Ctrl+Shift+W",
"mac": "Command+Shift+W"
}
}
}
}
2. Background Script Listener
Route global commands to background logic. Use browser.storage for state persistence and broadcast changes to active tabs with safe error handling.
// background.js
browser.commands.onCommand.addListener((command) => {
switch (command) {
case 'toggle-dark-mode':
toggleDarkMode();
break;
case 'refresh-weather':
refreshWeatherData();
break;
}
});
async function toggleDarkMode() {
const { theme } = await browser.storage.local.get('theme');
const newTheme = theme === 'dark' ? 'light' : 'dark';
await browser.storage.local.set({ theme: newTheme });
// Notify all open new tab pages
const tabs = await browser.tabs.query({ url: 'about:newtab' });
tabs.forEach(tab => {
browser.tabs.sendMessage(tab.id, { type: 'theme-changed', theme: newTheme })
.catch(() => {}); // Tab might not have content script
});
}
3. Context-Aware DOM Shortcuts
For page-specific interactions, use native keydown listeners with input filtering to prevent typing interference.
// newtab.js
document.addEventListener('keydown', (e) => {
// Ignore when user is typing in a text field
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
switch (e.key) {
case 'd':
case 'D':
if (!e.ctrlKey && !e.metaKey) toggleDarkMode();
break;
case 'r':
case 'R':
if (!e.ctrlKey && !e.metaKey) refreshWeather();
break;
case '/':
// Focus the search bar
e.preventDefault();
document.getElementById('search-input')?.focus();
break;
case 'Escape':
// Blur search, close any open dropdowns
document.activeElement?.blur();
closeAllDropdowns();
break;
}
});
4. User Discovery Modal
Implement a lightweight help overlay triggered by ? to surface available shortcuts without cluttering the UI.
// Show keyboard shortcuts modal with ? key
document.addEventListener('keydown', (e) => {
if (e.key === '?' && !e.ctrlKey && !e.metaKey) {
toggleShortcutsModal();
}
});
function toggleShortcutsModal() {
const modal = document.getElementById('shortcuts-modal');
if (!modal) {
createShortcutsModal();
} else {
modal.classList.toggle('hidden');
}
}
function createShortcutsModal() {
const shortcuts = [
{ key: '/', description: 'Focus search' },
{ key: 'D', description: 'Toggle dark mode' },
{ key: 'R', description: 'Refresh weather' },
{ key: 'Esc', description: 'Clear focus' },
{ key: '?', description: 'Show this help' },
];
const modal = document.createElement('div');
modal.id = 'shortcuts-modal';
modal.innerHTML = `
<div class="shortcuts-overlay" onclick="this.parentElement.classList.add('hidden')"></div>
<div class="shortcuts-content">
<h3>Keyboard Shortcuts</h3>
<table>
${shortcuts.map(s => `
<tr>
<td><kbd>${s.key}</kbd></td>
<td>${s.description}</td>
</tr>
`).join('')}
</table>
<p class="shortcuts-note">Ctrl+Shift+D β Toggle dark mode (works from any tab)</p>
</div>
`;
document.body.appendChild(modal);
}
5. MV3 Programmatic Management
Manifest V3 enables runtime inspection and reset of shortcuts. Use this to support user customization and conflict resolution.
// Get all registered commands
const commands = await browser.commands.getAll();
commands.forEach(cmd => {
console.log(cmd.name, cmd.shortcut, cmd.description);
});
// Reset to default
await browser.commands.reset('toggle-dark-mode');
6. UI Polish: kbd Styling
Standardize keyboard hint rendering with lightweight CSS that adapts to theme states.
kbd {
display: inline-block;
padding: 2px 6px;
font-size: 12px;
font-family: monospace;
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 3px;
background: rgba(255, 255, 255, 0.1);
box-shadow: 0 1px 1px rgba(0,0,0,0.2);
}
.dark-mode kbd {
border-color: rgba(255,255,255,0.2);
background: rgba(255,255,255,0.1);
}
Pitfall Guide
- Global Shortcut Collision: Mapping
Ctrl+R, Ctrl+T, or Ctrl+W overrides critical browser functions. Always use three-key combinations (Ctrl+Shift+X) for global commands to avoid silent failures.
- Input Field Interception: Capturing
keydown events inside <input> or <textarea> breaks user typing. Always validate e.target.tagName before executing shortcut logic.
- Platform Modifier Neglect: Failing to define
mac overrides in suggested_key forces macOS users to use Ctrl instead of Command, violating platform UX conventions.
- Silent Messaging Failures:
browser.tabs.sendMessage() throws if the target tab lacks a content script. Always chain .catch(() => {}) or verify tab state before broadcasting.
- MV2/MV3 API Misalignment: Assuming
browser.commands behaves identically across versions. MV3 requires explicit reset()/update() calls and lacks automatic fallbacks. Test both manifest versions if supporting legacy deployments.
- Poor Discoverability: Users will not guess shortcut combinations. Without a
? modal or explicit documentation, shortcut adoption drops below 15%. Always surface available keys contextually.
- Over-Reliance on
about:addons Navigation: Directing users to about:addons without programmatic fallbacks creates friction. Provide in-extension links and MV3 browser.tabs.create() routing for seamless shortcut management.
Deliverables
- Firefox Extension Shortcut Blueprint: Architecture diagram covering MV2/MV3 manifest routing, background listener patterns, and DOM context isolation strategies.
- Conflict-Aware Configuration Checklist: 12-point validation matrix for key mapping, platform overrides, input filtering, and MV3 programmatic reset readiness.
manifest.json & background.js Templates: Production-ready boilerplate with safe messaging, storage sync, and cross-platform key definitions.
- MV3 Programmatic API Reference Sheet: Quick-reference guide for
browser.commands.getAll(), reset(), and dynamic shortcut updates with error-handling patterns.