tv-navigator
1
总安装量
1
周安装量
#47908
全站排名
安装命令
npx skills add https://github.com/dbobkov245-source/pwa-torserve --skill tv-navigator
Agent 安装分布
claude-code
1
Skill 文档
TV Navigator Skill
This skill provides expertise in creating “TV-First” interfaces using the useTVNavigation hook.
Your goal is to ensure every component is accessible via D-Pad (Arrow Keys) and handles focus states correctly.
ð§ Core Concepts
1. useTVNavigation Hook
Located in: client/src/hooks/useTVNavigation.js
Signature:
const {
focusedIndex, // Current active index (0..N)
setFocusedIndex, // Manually set focus
containerProps, // { onKeyDown, tabIndex } - spreads to parent container
isFocused // Helper: (index) => boolean
} = useTVNavigation({
itemCount: number, // Total items
columns: number, // 1 for List, >1 for Grid
itemRefs: React.RefObject, // { current: { [index]: HTMLElement } }
onSelect: (index) => void, // Enter/OK press
onBack: () => void, // Escape/Back press
loop: boolean, // Default: false
trapFocus: boolean, // true = Isolated (Modals), false = Global (HomeRow)
isActive: boolean // External control. If false, ignores all input.
})
2. Focus Visualization
- NEVER use
:hoverfor TV interfaces. - ALWAYS use the
.focusedstate logic or conditional rendering based onfocusedIndex. - For
focuseditems, apply:border,transform: scale(1.05), orbox-shadow.
3. Scroll Management
The hook automatically handles scrolling using scrollIntoView({ behavior: 'smooth', block: 'center' }).
You must attach refs to items:
<div ref={el => itemRefs.current[index] = el} ... >
4. Integration with activeArea
The hook must respect the isActive flag.
- If
isActive === false: The hook ignores ALL key presses. - This allows other UI areas (like the Sidebar) to take over control without unmounting the grid.
ð Common Patterns
Vertical List (Menu)
const { containerProps, isFocused } = useTVNavigation({
itemCount: items.length,
columns: 1
});
Grid (Posters)
const { containerProps } = useTVNavigation({
itemCount: items.length,
columns: 4 // or dynamic based on width
});
â ï¸ Anti-Patterns to Avoid
- Hidden Overflow: Avoid
overflow: hiddenon containers that need to scroll, unless you are implementing virtualized scrolling. - Missing TabIndex: The container MUST have
tabIndex={0}(provided bycontainerProps) to capture keyboard events. - Mouse Dependency: Do not rely on
onClick. Always maponSelect(Enter key) to the same handler.