All skills
callstackincubator avatar

/react-native-tv-best-practices

@fa0bad0 official

Reviews React Native TV apps for focus/D-pad navigation, 10-foot UI layout, TV playback/DRM integration, low-memory TV performance, and TV accessibility. Use when building, debugging, or reviewing react-native-tvos, Expo TV, Amazon Vega/Kepler, or React Native web TV targets where the issue depends on remote input, TV focus, TV packaging, TV hardware, or TV playback constraints.

Use this Skill: https://skilld.dev/gh/callstackincubator/agent-skills/react-native-tv-best-practices

This session only. Nothing lands on disk.

referencesa11y-implementation.md

≈1.1k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Accessibility Implementation in React Native TV

Quick Reference

  • For agent-run checks, load the agent-device skill first, then read agent-device help workflow
  • Use agent-device accessibility-tree evidence to inspect exposed labels, roles, states, focus, and modal behavior
  • Use manual screen-reader testing only for spoken-output timing, audio behavior, and platform quirks that automation cannot prove
  • Do not put accessible={true} high in a modal/body tree; it collapses focusable children into one item
  • Prefer TVFocusGuideView for shared focus paths; use nextFocus* only as a targeted override
  • Await AccessibilityInfo.isScreenReaderEnabled() before muting autoplay or changing announcements
  • Pair visual-only remote feedback with an accessibility announcement when state changes

Agent-Run Accessibility Smoke

Before planning or running device automation, load the agent-device skill and follow its setup/help flow. Do not duplicate command recipes here; use the installed agent-device help for current command shapes and platform limits.

An AI agent should not claim it heard VoiceOver, TalkBack, or VoiceView. Instead, use agent-device to inspect what the app exposes to platform accessibility. Check the accessibility-tree evidence for:

  • Interactive controls expose useful labels, roles, enabled/disabled state, and selected/checked state
  • The currently focused element is the expected remote target after each D-pad press
  • Modal opening moves focus inside the modal and hides or de-prioritizes background controls
  • Closing a modal restores focus to the invoking control or another predictable target
  • Dynamic state changes expose updated labels/state or a live-region/announcement path

Escalate to manual screen-reader testing for the parts agent-device cannot verify: exact spoken order, announcement timing, audio-description behavior, caption rendering, and platform-specific verbosity.

Accessible Modals

<Modal
  visible={isVisible}
  accessibilityViewIsModal={true}
  onRequestClose={handleClose}
>
  <View>
    {/* Don't wrap the body in `accessible` — it collapses children into one
        element and hides the buttons from focus. Label a header node instead. */}
    <Text accessibilityRole="header">Continue watching?</Text>
    <Text>Are you still watching?</Text>
    <Button title="Yes" onPress={handleContinue} />
    <Button title="No" onPress={handleExit} />
  </View>
</Modal>
  • Set initial focus inside modal
  • Trap focus within modal
  • Announce dialog opening

Autoplay Content

  • Include remote-focusable playback controls
  • AccessibilityInfo.isScreenReaderEnabled() returns a Promise — await it (or subscribe to the screenReaderChanged event) before deciding whether to mute; a bare synchronous if is always truthy:
    const srOn = await AccessibilityInfo.isScreenReaderEnabled();
    if (srOn) muteAutoplay();
  • Apple TV: respect system audio focus, integrate with AVAudioSession

Cross-Platform Focus

Prefer TVFocusGuideView over nextFocus* for shared layouts. destinations takes an array of resolved components (ref.current), not the ref objects and not string IDs. Don't build it inline — ref.current is null on the first render and mutating a ref triggers no re-render, so the guide would register nothing. Hoist the resolved components into state once the children have mounted:

const playRef = useRef(null);
const infoRef = useRef(null);
const [destinations, setDestinations] = useState([]);

// Children assign their refs after this component first renders; push the
// resolved nodes into state so a NEW array is passed and the guide updates.
useEffect(() => {
  setDestinations([playRef.current, infoRef.current].filter(Boolean));
}, []);

<TVFocusGuideView destinations={destinations}>
  <TouchableOpacity ref={playRef} accessibilityRole="button" accessibilityLabel="Play" />
  <TouchableOpacity ref={infoRef} accessibilityRole="button" accessibilityLabel="Info" />
</TVFocusGuideView>

See focus-management.md for when nextFocus* overrides are acceptable.

Related Skills

Source: SKILL.md on GitHub

No alerts1mo3 checks · Risk SAFE
  • Gen Agent Trust Hub1mo

    This skill provides comprehensive documentation and best practices for developing React Native applications for TV platforms. It contains technical guidelines for focus management, performance, and video playback without any malicious patterns, obfuscation, or unsafe code execution.

  • Socket1mo

    No alerts

  • Snyk1mo

    Risk: LOW · No issues

Signed by skilld at fa0bad0. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 weeks ago.

Activeupdated 2 months ago

README badge

README badge for callstackincubator/agent-skills/react-native-tv-best-practices