feat: gamepad navigation rewrite, focus styling, container grid system

- Rewrite useControllerNav.ts with clean console-style navigation:
  Sidebar (up/down wrap, right→containers, left→nothing),
  Container tile grid (spatial nav, no wrap at edges),
  Nav bar support (up from containers, down to grid),
  Inner controls (enter drills in, escape exits, trapped arrows)
- Add data-controller-container to Mesh, Fleet, Settings pages
- Fix Home.vue fragment (modals outside root div) causing Vue warnings
- Remove skip-to-content link (handled by controller nav)
- Orange ambient glow focus styling matching glass aesthetic
- Disable PWA service worker in dev mode (fixes HMR caching)
- Add gamepad-nav skill and GAMEPAD-NAV-MAP.md spec document
- 39 tests covering all navigation patterns

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-28 17:01:17 +00:00
co-authored by Claude Opus 4.6
parent 1444bcb0c4
commit aada19754d
13 changed files with 1327 additions and 772 deletions
+38 -17
View File
@@ -27,7 +27,8 @@
overflow: hidden;
z-index: 9999;
}
.skip-to-content:focus {
.skip-to-content:focus,
.skip-to-content:focus-visible {
position: fixed;
top: 12px;
left: 50%;
@@ -44,14 +45,32 @@
font-weight: 500;
text-decoration: none;
backdrop-filter: blur(12px);
outline: none;
}
/* Controller / keyboard navigation - orange border (Archipelago brand) */
/* Controller / keyboard navigation only for elements without their own focus styles.
Elements with existing hover/active styles (glass-button, sidebar-nav-item, etc.) keep theirs. */
*:focus-visible {
outline: none;
border-color: rgba(251, 146, 60, 0.8) !important;
box-shadow: 0 0 0 2px rgba(251, 146, 60, 0.7), 0 0 16px rgba(251, 146, 60, 0.25);
transition: box-shadow 0.2s ease, border-color 0.2s ease;
box-shadow:
0 0 0 1px rgba(251, 146, 60, 0.4),
0 0 12px rgba(251, 146, 60, 0.2),
0 0 24px rgba(251, 146, 60, 0.08);
transition: box-shadow 0.2s ease;
}
/* Elements with existing styles: suppress the global glow, let their own styles handle it */
.glass-button:focus-visible,
.glass-card:focus-visible,
.sidebar-nav-item:focus-visible,
.path-action-button:focus-visible,
.path-option-card:focus-visible,
.mode-switcher-btn:focus-visible,
.kiosk-app-tile:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible {
box-shadow: unset;
}
/* Mobile touch targets — ensure tappable elements meet 44px minimum */
@@ -99,13 +118,15 @@ input[type="radio"]:active + * {
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
/* Containers get subtle grow + orange glow when focused (gamepad selection) */
/* Containers: console-style focus — subtle lift + ambient glow through glass */
[data-controller-container]:focus-visible {
transform: scale(1.02);
outline: none;
transform: scale(1.01) translateZ(0);
box-shadow:
0 0 0 2px rgba(251, 146, 60, 0.7),
0 0 24px rgba(251, 146, 60, 0.2),
inset 0 0 24px rgba(255, 255, 255, 0.03);
0 0 0 1px rgba(251, 146, 60, 0.35),
0 4px 20px rgba(251, 146, 60, 0.12),
0 0 40px rgba(251, 146, 60, 0.06),
inset 0 1px 0 rgba(251, 146, 60, 0.1);
}
/* Global glassmorphism utilities */
@@ -977,11 +998,9 @@ input[type="radio"]:active + * {
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.sidebar-nav-item:focus-visible {
transform: scale(1.02) !important;
box-shadow:
0 0 0 2px rgba(251, 146, 60, 0.7),
0 0 24px rgba(251, 146, 60, 0.2),
inset 0 0 24px rgba(255, 255, 255, 0.03) !important;
outline: none !important;
background: rgba(255, 255, 255, 0.1) !important;
color: white !important;
}
}
@@ -1303,7 +1322,8 @@ html:has(body.video-background-active)::before {
background: rgba(255, 255, 255, 0.1);
}
.cloud-file-item:focus-visible {
box-shadow: 0 0 0 2px rgba(251, 146, 60, 0.7), 0 0 16px rgba(251, 146, 60, 0.25);
outline: none;
box-shadow: 0 0 0 1.5px rgba(251, 146, 60, 0.5), 0 0 16px rgba(251, 146, 60, 0.12);
}
.cloud-file-item-thumb {
@@ -1481,7 +1501,8 @@ html:has(body.video-background-active)::before {
transform: translateY(0);
}
.cloud-grid-card:focus-visible {
box-shadow: 0 0 0 2px rgba(251, 146, 60, 0.7), 0 0 16px rgba(251, 146, 60, 0.25);
outline: none;
box-shadow: 0 0 0 1.5px rgba(251, 146, 60, 0.5), 0 0 16px rgba(251, 146, 60, 0.12);
}
.cloud-grid-card-cover {