Operator feedback after the first refinement (PR #109): "It's hard
to see which one you're hovering" + asked for a fun mouse-tracking
radial gradient AND a subtle gradient on the active state. Three
targeted refinements in one PR.
1. Active state: subtle ember linear-gradient added.
- Kept the 1px ember border + ember text from PR #109's first
refinement (operator approved the toned-down read).
- Added a 135deg linear-gradient at 14% → 6% alpha (light) /
18% → 8% alpha (dark) so the active item has visible warmth
without returning to the heavy saturated pill from sub-convoy
#2. Background shorthand replaces the prior transparent value.
2. Hover state: base tint bolder.
- rgba(216, 67, 21, 0.08) → rgba(216, 67, 21, 0.18) on light
(per operator: "hard to see which one you're hovering")
- rgba(255, 138, 80, 0.10) → rgba(255, 138, 80, 0.22) on dark
- Also expanded the selector list to include .nav-item-hover
consistently with .nav-item and .nav-item-bottom (the three
hoverable nav surface classes).
3. Hover state: mouse-tracking radial spotlight overlay.
- New ::before pseudo-element on .nav-item / .nav-item-bottom /
.nav-item-hover. Uses a radial-gradient positioned via two
CSS custom properties --mouse-x and --mouse-y (default 50%).
- Light theme: rgba(255, 140, 30, 0.35) center → rgba(216, 67,
21, 0.18) at 35% → transparent at 70%. 120px radius.
- Dark theme: rgba(255, 160, 70, 0.40) center → rgba(255, 110,
0, 0.22) → transparent. Brighter for visibility against navy.
- Opacity transitions in/out via --motion-duration-quick.
- pointer-events: none so it never blocks clicks or focus.
- Content z-index: 1 so icons + labels paint above the gradient.
4. Layout.js: single delegated mousemove listener at document level.
- Cheaper than per-item React onMouseMove handlers (the
NavigationContent function has 10+ different nav-item render
paths). closest() returns null for non-nav events so the
listener cost is negligible.
- Writes --mouse-x / --mouse-y as percentages onto the closest
matching nav element. CSS consumes those vars to position the
radial-gradient via inheritance through the ::before.
- prefers-reduced-motion: respected by the existing global
0.01ms transition sweep — the spotlight opacity fade is
collapsed but the static gradient position update is a
positional change (not an animation), so it stays per WCAG
SC 2.3.3 without special-casing.
Tests:
- npm run test:run: 113/113
- npm run lint: clean (1 pre-existing unused-disable warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>