> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cskn.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# TV navigation

> Make your app usable with a TV remote: arrow keys, OK and Back.

A TV has no touch screen and no pointer. When your app runs on TV, `UIProvider` starts a spatial navigator: the arrow keys move real DOM focus to the nearest focusable element in that direction, OK clicks it, and Back walks a stack of handlers (close the open sheet, then the next overlay, then leave the app).

Every kit component (`Pressable`, `Button`, `Input`, `ListItem`, `TabBar`…) is already focusable. You only need the tools below for your own clickable elements.

## Detection

| How the app is loaded | What happens |
| - | - |
| Inside the TV shell | The shell, or the SDK's iframe relay, drives the remote. @cskn/ui detects this and stays passive, so the remote is never driven twice. |
| Directly in a TV browser (webOS, Tizen) | There is no bridge, but @cskn/ui recognizes the TV from the user agent and starts its own navigator. |
| Forced | `<UIProvider env="tv">`, `?cskn-tv=1` in the URL, or `window.__csknForceTv = true`. Useful for testing in a desktop browser. |

## Making your own elements focusable

```tsx theme={null}
import { useFocusable } from '@cskn/ui';

function PosterTile({ onPlay, first }: Props) {
  const focusable = useFocusable({ autoFocus: first });
  return <div {...focusable} role="button" onClick={onPlay}>▶</div>;
}
```

<ParamField path="autoFocus" type="boolean">Preferred first focus when its screen or overlay becomes active. Use at most one per scope.</ParamField>
<ParamField path="disabled" type="boolean">Remove it from the navigation order.</ParamField>
<ParamField path="tabIndex" type="number">Override the tab order.</ParamField>

The hook returns `data-focus` and `tabIndex`. Elements with `data-focus` get the theme's focus ring (`ring` color).

## Scopes and Back

```tsx theme={null}
import { FocusScope } from '@cskn/ui';

<FocusScope trap autoFocus onBack={close}>
  <PlayerControls />
</FocusScope>
```

<ParamField path="trap" type="boolean">Keep the arrow keys inside this subtree. Use it for overlays.</ParamField>
<ParamField path="autoFocus" type="boolean">Focus the first focusable element on mount.</ParamField>
<ParamField path="onBack" type="() => void">Called on Back or Escape while this scope is on top.</ParamField>

The kit's overlays already use scopes. For custom flows: `pushBackHandler(fn)` adds a Back handler and returns its removal, `focusFirst(root?)` moves focus into a container, and `startSpatialNav()` / `stopSpatialNav()` control the navigator directly.

## Everywhere else

`data-focus` and the focus ring also make your app keyboard-accessible on web and desktop. On touch devices the ring is not shown, so it is safe to apply everywhere.

<Tip>
  Combine `useUI().isTV` with larger type and spacing, and a `Sidebar` with `expandOnFocus`, for a layout that reads from across the room.
</Tip>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.