tv entry of appSurfaces, and list tv in your cskn.json platforms. Every kit component is focusable and works with TV navigation.
TvApp
The app’s frame. The rail opens over the content when the remote’s focus reaches it; the content doesn’t move. Back from the content goes to the rail, and Back on the rail leaves the app. Screens and overlays handle Back before either.The app’s sections, down the rail.
string
required
The section showing.
A section was chosen.
ReactNode
required
The screen: a
TvScreen with heroes, rows and grids.ReactNode
Your app’s mark at the top of the rail.
string
The rail’s last item, back to the TV shell. Left out when there is no shell.
ReactNode
A bar along the bottom, such as a media app’s
TvPlayerBar. The content gets room under it, and Down reaches it. Pass nothing while there’s nothing to show.--cskn-tv-* CSS variables, such as --cskn-tv-bg, to recolor it.
TvScreen
One screen of the app. When you arrive, focus goes back to where it was when you last left this screen (the card you opened), or else to the item markedautoFocus, or to the first item.
string
required
Names the screen, so returning to it restores focus.
() => void
Back on this screen, for example from a detail page to the list.
focusKey so the remembered focus survives the data reloading; otherwise their position is what’s remembered.
Content
TvCard:
title, onPress (required), subtitle?, image?, shape? (default landscape), width? (in a row, in units of a 1920-wide screen), badge? (over a corner, such as “LIVE”), progress? (0–1, a bar along the bottom), focusKey?, autoFocus?.
TvHero: title (required), backdrop?, logo? (a wordmark in place of the title), meta?, description?, actions? (usually TvButtons, the first with autoFocus), compact?.
TvListItem: title, onPress (required), subtitle?, image?, meta? (a duration or date), playing?, focusKey?, autoFocus?.
TvButton: label, onPress (required), icon?, variant?, iconOnly? (label becomes its accessible name), autoFocus?, disabled?.
Audio player
For music and podcast apps: a full-screen player and a compact bar that opens it. Your app owns the audio element; these draw the controls.TvAudioPlayer fills the screen with the artwork over a darkened wash of its colors. Back closes it and the audio keeps playing.
boolean / () => void
required
Visibility. Back calls
onClose.string
required
What’s playing.
boolean, number, number
required
Playback state. Times in seconds;
duration is 0 while unknown.() => void
required
Toggle playback.
(seconds: number) => void
Jump by seconds (negative is back). Without it, there are no skip buttons.
number
default:"15 / 30"
Seconds the skip buttons jump.
() => void, boolean
Previous and next buttons. Without the handlers, there are no such buttons.
string
The line under the title, the small line above it, and square art.
ReactNode
More round buttons after the transport (speed, like, queue):
TvButtons with iconOnly.ReactNode
Under the transport: up next (a
TvList), show notes.object
Accessible names:
play, pause, previous, next, skipBack, skipForward.TvPlayerBar takes the same playback props plus onExpand and expandLabel; its first button opens the full player. Put it in TvApp’s bottomBar.
useTvMediaKeys(handlers, enabled?) handles the remote’s media keys wherever focus is: onPlayPause, onPlay, onPause, onForward, onRewind, onNext, onPrevious, onStop. A key without a handler is left alone, and Play or Pause with only onPlayPause toggles.
useTvOverlayHost() returns the element full-screen layers like TvAudioPlayer render into, for your own overlays.
Clip feed
Short vertical videos, one at a time: the clip at full height in the middle over a blurred wash of itself. Up and Down change the clip, Left and Right change the feed, OK pauses, Back leaves the app. Every arrow already has a job, so useTvClipFeed instead of TvApp, not inside it.
{ key, label }[]
required
The feeds named along the top.
string / (key) => void
required
The feed showing. Each feed keeps its place, so switching away and back returns to the same clip.
TvClip[]
required
The current feed’s clips. Each has
key, title (who posted it), and optionally subtitle, avatar, description, poster and stats ({ icon, label, value }[]).(clip, { playing }) => ReactNode
required
The media for the clip on screen. Only one is ever mounted, because a TV has one or two hardware decoders; the clips on either side show their posters. A
<video> placed directly here is sized by the feed.boolean
Drawn only while there are no clips, so a reload keeps the current clip on screen.
{ title, hint? } / () => void
The empty state, and OK on it (try again).
() => void
Near the last clip: load the next page.
(clip) => void
The viewer stopped on a clip. Called once it settles, not for every clip flicked past.