Skip to content
WANCRAFT

D.198Navigation & Menus

Link Hints

Vimium-style keyboard hints scoped to a container: press F to label every visible control, then type the letters to activate one.

NewReactCSSTailwindintermediate

Overview

Link Hints brings the Vimium browser extension's best idea into your app without taking over the page. Press the trigger key ("f" by default) while focus is anywhere inside the container, or use the visible Show hints button, and every visible focusable element inside it gets a short letter label. Type a label to activate that element: links and buttons are clicked, and text fields are focused. Backspace edits what you've typed and Escape cancels. The labels are prefix-free and as short as possible (one letter when there are few targets, two when there are many). They sit in a top-layer overlay positioned with getBoundingClientRect and follow scrolling and resizing. Elements that are hidden, inert, covered or scrolled out of view are skipped, and a polite live region announces "12 hints shown". It is never global: the trigger only works inside the container and never while you're typing in a field, and window listeners exist only while hints are showing.

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/link-hints.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/link-hints.json

Method 02 · Copy and paste

  1. 1. No extra dependencies — React and Tailwind CSS only.
  2. 2. Copy the source from the Code tab into components/ui/link-hints.tsx.
  3. 3. Import it and adjust props.
Dependencies
None
Peer
react ≥ 18, tailwindcss ≥ 4
Added
13 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { LinkHints } from "@/components/ui/link-hints";

export function LinkHintsExample() {
  return (
    <LinkHints aria-label="Project tools">
      <nav className="flex gap-3">
        <a href="/projects">Projects</a>
        <a href="/billing">Billing</a>
        <button type="button">New project</button>
      </nav>
    </LinkHints>
  );
}

03Props

PropTypeDefaultDescription
childrenReactNode—Required. The scoped content; its focusable elements get hints.
triggerKeystring"f"Key that shows hints while focus is inside the container (ignored in text fields and with modifiers).
alphabetstring"sadfjklewcmpgh"Characters for the labels (at least two).
action"activate" | "focus""activate"Click the element, or only focus it. Text fields are always just focused.
showButtonbooleantrueRender the Show hints button (an alternative to the key).
buttonLabelstring"Show hints"Text of that button.
uppercasebooleantrueDisplay labels in capitals; typing is case-insensitive.
variant"accent" | "contrast""accent"Accent-filled or inverted label pills.
selectorstringfocusable elementsCSS selector for elements that get hints.
onHint(element: HTMLElement) => void—Called after a hint was used.
accentstring"#FF7448"Label and focus colour.
classNamestring—Extra classes for the container.

04Features

  • 01Scoped trigger: the key only works while focus is in the container (or on its button), never in text fields or with modifiers
  • 02Prefix-free, minimal-length labels (Vimium's algorithm) from a configurable alphabet
  • 03Top-layer overlay positioned with getBoundingClientRect; it follows scroll and resize
  • 04Skips hidden, inert, aria-hidden, covered and scrolled-away elements
  • 05Type to narrow (matched letters dim), Backspace to edit, Escape to cancel; a key that matches nothing gives a small shake
  • 06Activate (focus + click) or focus-only, with text fields always just focused
input · keyboardinput · clickinput · focus

05Accessibility

  • A visible Show hints button (aria-pressed, aria-keyshortcuts) offers the same feature without knowing the key.
  • A polite live region announces "12 hints shown. Type the letters to choose, Escape to cancel." and "Hints hidden" on cancel; the labels themselves are aria-hidden.
  • Escape closes only the hints (it is consumed so a surrounding dialog stays open); the window keydown listener exists only while hints show.
  • The trigger never fires while typing in inputs, textareas, selects, contentEditable or textbox / combobox roles, and it never overrides Ctrl / ⌘ shortcuts like ⌘K.
  • Choosing a hint moves real focus to the element, so screen readers announce it and focus-visible styles apply.

06Limitations

  • Elements inside closed shadow roots and cross-origin iframes can't be hinted.