Skip to content

bug: blog's global "/" and "s" search-focus shortcut hijacks the browser's Ctrl/Cmd+S save shortcut #1253

Description

@duyet

Severity: medium · Effort: S · Aspect: ux

Goal

The blog's "press / or s to focus search" keyboard shortcut must not fire when a modifier key (Ctrl/Cmd/Alt) is held, so it stops intercepting native browser shortcuts like Save Page (Ctrl/Cmd+S).

Context & Why

apps/blog's /search page (https://blog.duyet.net/search) registers a window-level keydown listener that focuses the search input whenever e.key === "/" or e.key === "s" is pressed, as long as focus isn't currently in an <input>/<textarea>. The handler never checks e.ctrlKey/e.metaKey/e.altKey. Since e.key for the S key is "s" regardless of whether Ctrl/Cmd is held, pressing Ctrl+S (Windows/Linux) or Cmd+S (macOS) to save the page — while focus is anywhere except a text field (e.g. focus is on the <body>, a link, or nothing in particular, which is the common case while reading) — gets preventDefault()'d and silently redirected into "focus the search box" instead of the browser's native Save dialog. This is a real, reproducible keyboard-shortcut hijack that will surprise end users who use the search page and try to save it, and is the kind of issue a software engineer reviewing keyboard-shortcut code should have modifier-guarded from the start.

Evidence

  • apps/blog/components/blog/search-bar.tsx:85-100:
    useEffect(() => {
      const handleKeyDown = (e: KeyboardEvent) => {
        if (
          e.target instanceof HTMLInputElement ||
          e.target instanceof HTMLTextAreaElement
        ) {
          return;
        }
        if (e.key === "/" || e.key === "s") {
          e.preventDefault();
          inputRef.current?.focus();
        }
      };
      window.addEventListener("keydown", handleKeyDown);
      return () => window.removeEventListener("keydown", handleKeyDown);
    }, []);
    No check for e.ctrlKey, e.metaKey, or e.altKey anywhere in this block.
  • SearchBar (which owns this listener) is mounted on the /search route via apps/blog/components/blog/search-client.tsx:139 (<SearchBar placeholder="Search by title, category, or tags..." />), so the hijack is live on https://blog.duyet.net/search whenever that page is open.

Suggested approach

Guard the shortcut with a modifier check, e.g.:

if ((e.key === "/" || e.key === "s") && !e.ctrlKey && !e.metaKey && !e.altKey) {
  e.preventDefault();
  inputRef.current?.focus();
}

Consider dropping the bare "s" shortcut entirely (it's an unusual choice — most sites only bind /) since a single unmodified letter key shortcut with no visible affordance is also easy to trigger accidentally while a user is, e.g., scrolling with the spacebar-adjacent keys or has focus on a non-form element that still processes text (e.g. a contenteditable outside of input/textarea, which this guard also misses).

Validation

On https://blog.duyet.net/search, click somewhere on the page that is not the search input (e.g. a heading or empty space) so focus is not in a text field, then press Ctrl+S (or Cmd+S on macOS). Confirm the browser's native "Save Page" dialog opens instead of the search input being focused.


Filed by automated multi-repo audit (2026-07-16) — aspect: ux (UI/UX and accessibility).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions