DEV Community

Weather Clock Dash
Weather Clock Dash

Posted on

Keyboard Shortcuts in Firefox Extensions: A Complete Guide

Keyboard Shortcuts in Firefox Extensions: A Complete Guide

Keyboard shortcuts make extensions feel native. A new tab extension that users can control without clicking feels much more polished. Here's how to add them to your Firefox extension.

Declaring Commands in manifest.json

{
  "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"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The special _execute_browser_action command opens your extension's popup.

Handling Commands in Background Script

// 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
  });
}
Enter fullscreen mode Exit fullscreen mode

Keyboard Shortcuts Within the New Tab Page

For shortcuts that only work within your new tab page, use regular DOM event listeners:

// 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;
  }
});
Enter fullscreen mode Exit fullscreen mode

Showing Available Shortcuts to Users

A quick help modal makes shortcuts discoverable:

// 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);
}
Enter fullscreen mode Exit fullscreen mode

CSS for the kbd element

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);
}
Enter fullscreen mode Exit fullscreen mode

Managing Commands in code (MV3)

In Manifest V3, you can update command shortcuts programmatically:

// 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');
Enter fullscreen mode Exit fullscreen mode

Conflict Avoidance Tips

  1. Avoid single letters as Ctrl+Letter shortcuts — many are already taken (Ctrl+R = refresh, Ctrl+T = new tab, etc.)
  2. Use three-key combos like Ctrl+Shift+D for global shortcuts
  3. Single letter shortcuts within the page (like / for search) are fine when you skip text inputs
  4. Check for conflicts at about:addons → Manage Extension Shortcuts

The Keyboard Shortcut Settings Page

Users can view and change extension shortcuts at:

about:addons → gear icon → Manage Extension Shortcuts
Enter fullscreen mode Exit fullscreen mode

Link directly to it from your extension's options page:

// Opens about:addons in a new tab, then user navigates to shortcuts
browser.tabs.create({ url: 'about:addons' });
Enter fullscreen mode Exit fullscreen mode

The Weather & Clock Dashboard uses the / shortcut to focus the search bar — one of the most-used features.

firefox #javascript #ux #browserextension #webdev

Top comments (0)