put.io design system
Tokens, primitives and component specs for put.io. Generic
CSS / JSON here; platform apps adapt natively.
v0.6 · 2026-05
tokens.css ↗
Pipeline
Sourcetokens/**/*.tokens.json · canonical
Generatedsystem/tokens.css · build output
CSS@putdotio/design/css
DTCG@putdotio/design/tokens/dtcg
Flat JSON@putdotio/design/tokens
TS@putdotio/design/tokens/meta
Figma@putdotio/design/tokens/figma
Consume
CSS
DTCG JSON
TS
/* import the single source */ @import "@putdotio/design/css"; /* use vars as roles, never literals */ .btn-primary { background: var(--yellow-solid); color: #000; border-radius: var(--radius); }
// @putdotio/design/tokens/dtcg · W3C DTCG { "color": { "brand": { "yellow": { "$value": "hsl(44.7, 97.9%, 63.1%)", "$type": "color" } } }, "radius": { "base": { "$value": "6px", "$type": "dimension" } } }
// @putdotio/design/tokens/meta · typed metadata import { tokens } from "@putdotio/design/tokens/meta"; tokens["color.brand.yellow"] // "hsl(44.7, 97.9%, 63.1%)" tokens.radius.base // "6px" // platform adapters live in app repos, not here
House rules
- Utility is beautiful. Beauty comes from making file operations feel premium — not from decoration.
- Content-agnostic by default. No posters, album art, curated metadata, or “watched” state. Filename, size, date.
- Raw filenames stay raw. Never parse, rewrite or fake metadata from a filename. Render verbatim, mono.
- Clarity over cleverness. One verb per icon, one icon per verb. Download / Save / Stream never overlap.
- Type carries the weight. Three families — sans for UI, two monos for numerics and identifiers.
- One yellow at a time.
#FDCE45is the single CTA, button/nav/TV focus halo, and brand accent. Not decoration. - TV is list-first. Without thumbnails, rows beat card walls. Don’t copy Apple TV or Plex.
- Voice and restraint. Plain, short, English-first. Subtract before you add.
Part A
Tokens & primitives
Foundations (color, type, space, radii, motion) followed by brand primitives (logo, icons, focus ring). Single source:
tokens.css — every other artifact is generated from it.A.01
Color
Radix 12-step scales, dark + light. One brand, three semantic scales, one transfer-state lime.
- Purpose
- Role-based color tokens. Vars name the role (
--bg,--text-secondary), not the hex. Components consume roles, never literals. - Source of truth
tokens/**/*.tokens.json— generated intosystem/tokens.css- Usage
/* light + dark in one stylesheet — toggle .dark on <html> */ .btn { background: var(--yellow-solid); color: #000; } .row { background: var(--bg-secondary); color: var(--text); } .row:hover { background: var(--list-item-bg-hover); }
- Constraints
--yellow-solid= backgrounds, icons, text-on-dark only — 1.5:1 on light = fail- For yellow text on light, use
--yellow-text-secondary(4.98:1 AA) - No new color literals in component code
- No semantic scale for decoration — status only
Brand · sacrednever changes across modes or platforms
SACRED
--yellow-solid
hsl(44.7, 97.9%, 63.1%)
Brand fill: CTAs, button/nav/TV focus halos (35% alpha), folder icons, progress bars. Never as text on light.
hover · light
--yellow-solid-hover
hsl(44.7, 97.9%, 63.1%)
CTA hover keeps the sacred yellow value in light mode.
hover · dark
.dark --yellow-solid-hover
hsl(44.7, 97.9%, 63.1%)
CTA hover keeps the sacred yellow value in dark mode.
surface
--yellow-bg
hsl(45 100% 5.5%)
Faint brand tint backdrop (dark mode); near-white in light.
Neutrals · dark (app default)12 steps · radix gray-dark
--bg
hsl(0 0% 8.5%)
Page background — the dominant app surface. Sidebars, body, full-screen modals.
--bg-secondary
hsl(0 0% 11%)
Inset panel · quiet aside. Code blocks, reference panels, secondary nav.
--component-bg
hsl(0 0% 13.6%)
Card surface · default button fill · chips. Focal components.
--component-bg-hover
hsl(0 0% 15.8%)
Hover state for any
--component-bg surface.--component-bg-active
hsl(0 0% 17.9%)
Pressed / selected state · toggle on.
--line
hsl(0 0% 20.5%)
Hairline divider between rows, cells, sections.
--border
hsl(0 0% 24.3%)
Component border — inputs, buttons, cards. Resting state.
--border-hover
hsl(0 0% 31.2%)
Border on hover / focus-within.
--solid
hsl(0 0% 43.9%)
Solid neutral fill —
btn-info, mid-tone icons.--solid-hover
hsl(0 0% 49.4%)
Hover companion to
--solid.--text-secondary
hsl(0 0% 62.8%)
Meta copy — captions, hints, timestamps, secondary nav.
--text
hsl(0 0% 93%)
Primary text — body, headings, button labels.
Semantic · positive · destructive · in-motionstatus only, never decoration
--green-solid
positive
btn-success · check icons · progress complete.--green-bg-secondary
positive surface
Success callout backdrop · confirmation tint.
--red-solid
destructive
btn-danger · error icons · invalid input border.--red-bg-secondary
destructive surface
Error callout backdrop. Invalid fields keep
--field-bg.--lime-3
in-motion accent
Transfer downloading / completing rows. put.io-specific.
--yellow-solid
brand · status accent
Primary CTA fill · button/nav/TV focus halo · active step · brand mark.
specimens
Color · canonical previewspreview/color-*
A.02
Type
Three local families. GT America for UI / body / display. GT America Mono for numerics. Berkeley Mono for code & filenames.
- Purpose
- One sans + two monos do all the work. Mono families enforce raw filenames stay raw — filenames render in Berkeley Mono, numerics in GT Mono.
- Source of truth
system/tokens.css· font CSS hosted atstatic.put.io/fonts/- Generated
type.tokens.json· CSS custom properties · TS metadata- Usage
/* font roles */ .row { font-family: var(--font-sans); } /* GT America */ .row .size { font-family: var(--font-ui-mono); font-variant-numeric: tabular-nums; } .row .name { font-family: var(--font-mono); } /* Berkeley · filenames */
- Constraints
- Weights used: 400 · 500 · 700 · 900 (display only)
- Mobile body never below 14px · TV body never below 24px
- No display serif, no script, no system stack fallbacks for body
--fs-display
put.io
--fs-3xl
Display heading
--fs-2xl
Section heading
--fs-xl
Subheading · card title
--fs-lg
Lead copy for long-form
--fs-md
Subheading · dense UI
--fs-base
Body — app default
--fs-sm
Secondary body
--fs-xs
Label / caption
--font-ui-mono
11.20 TB · 12.4 MB/s · 00:03:42
--font-mono
ubuntu-22.04.iso · sha1: e3b0c44…
specimens
Type · canonical previewspreview/type-*
A.03
Spacing
4 px base, aggressive doubling. Reserved for layout — components handle inner padding via their own tokens.
- Purpose
- Layout rhythm.
--space-3(16) is the row gutter;--space-4(32) is the section gutter. - Generated
spacing.json· platform px / dp scales- Usage
.list { display: flex; flex-direction: column; gap: var(--space-1); } /* 4px between rows */ .section { padding: var(--space-4) var(--space-3); } /* 32 × 16 */
- Constraints
- Use scale tokens — no arbitrary px values in components
- No half-steps (no 6px / 12px / 24px)
A.04
Radii & shadow
Five radii, four elevations. 6 px is the default for buttons and fields. The yellow halo is reserved for button, link, nav, and TV focus.
- Purpose
- Radii match component scale. Shadows define elevation roles — popover, modal, raised panels — plus a yellow focus halo for non-field controls.
- Source of truth
system/tokens.css·--radius-*·--shadow-*- Generated
radii.json·shadows.json- Usage
.btn { border-radius: var(--radius); } /* 6px default */ .pill { border-radius: var(--radius-pill); } .btn:focus-visible { box-shadow: var(--shadow-focus); } /* yellow halo */
- Constraints
--radius= 6 is the canonical default--shadow-focususes yellow; form fields use--field-ring- No drop shadow on hover — use bg shift instead
--radius-sm
4px
--radius
6px · default
--radius-md
8px
--radius-lg
10px
--radius-pill
999px
--shadow-sm
hover / focus
0 1px 2px / 0.4
0 1px 2px / 0.4
--shadow-md
popover, menu
0 4px 12px / 0.5
0 4px 12px / 0.5
--shadow-lg
modal, sheet
0 16px 40px / 0.6
0 16px 40px / 0.6
--shadow-focus
yellow halo · 3px
0 0 0 3px / 0.35
0 0 0 3px / 0.35
A.05
Motion
Two easings. Three durations. If a transition doesn't fit one of these, it's wrong.
- Purpose
- Animation feels consistent across platforms.
--dur-fastfor hover,--dur-basefor menus,--dur-slowfor sheets. - Source of truth
tokens.css·--ease-*·--dur-*- Generated
motion.json· native easing curves- Usage
.btn { transition: background var(--dur-fast) var(--ease-out); } .sheet { transition: transform var(--dur-slow) var(--ease-in-out); }
- Constraints
- Respect
prefers-reduced-motionat the component level - No bounce / overshoot easings
- Respect
A · 2
Brand & primitivespreview/brand-* · focus-system · icon-set
A.06
Brand & logo
Logomark, app-icon family, empty-state catalogue. Kaomoji
ᕦ(ò_óˇ)ᕤ is part of the brand.- Purpose
- Brand identity assets. Logomark = wordmark + dot; app icon = mark on yellow square; empty-state = kaomoji + one-line copy + one yellow CTA.
- Source of truth
system/assets/logo.svg·logo-retro-{dark,light}.svg·app-icon-*.png- Generated
- iOS / Android / web favicons · app icons per platform
- Usage
<!-- always lowercase --> <span class="brand">put<span class="dot">.</span>io</span>
- Constraints
- Wordmark is put.io — never uppercase, title-case, or spaceless variants
- Public package names like
@putdotio/designare exempt - No mark in body text · no shrunk-then-cropped variants
A.07
Icons
Phosphor going forward. regular line weight.
ph-folder-fill tinted #FDCE45 is the signature.- Purpose
- One icon family across all platforms. Regular weight default · fill weight for active states. Inline SVG only — no icon fonts in new code.
- Source of truth
- Phosphor icons ·
phosphor-icons.com - Generated
- Inline-SVG icon set · per-icon metadata JSON
- Usage
<!-- inline SVG, 24×24, currentColor --> <svg width="24" height="24" viewBox="0 0 256 256"> <path fill="currentColor" d="…"/> </svg>
- Constraints
- 24×24 default, 20×20 in dense tables, 32×32 on TV
- Folder icon =
ph-folder-filltinted--yellow-solid - No emoji as icons · no decorative SVG
- No legacy Flaticons font in new code
A.08
Focus
Three input contexts, one focus contract. Buttons and nav get the brand halo; text entry gets the quiet field ring; TV focus scales for distance.
- Purpose
- Unified focus visual across runtimes. Buttons, links, and nav use the brand-yellow halo. Text fields and OTP slots use the quieter
--field-ring. TV adds scale + drop shadow. - Source of truth
tokens.css·--shadow-focus·--field-ring- Generated
- TV variant via
tv-shell.css - Usage
/* web/mobile */ .btn:focus-visible { box-shadow: var(--shadow-focus); } .field:focus-visible { box-shadow: var(--field-ring); } /* TV — overrides for 10ft viewing */ .tv .card:focus { transform: scale(1.06); box-shadow: 0 0 0 4px var(--yellow-solid), var(--shadow-lg); }
- Constraints
- Use yellow focus for buttons, links, nav, and TV focus
- Use
--field-ring, not yellow, for text entry controls - Use
:focus-visibleon web/mobile, never plain:focus - No outline removal without a visible replacement
Part B
Components
Reusable, composable, framework-agnostic specs. Each card answers purpose · source · artifact · usage · constraints, then shows the canonical specimen.
source · preview/components-*
preview/ ↗
B.02
Inputs
Text, search (with / hint), password, error. Fields use
--field-bg and a quiet --field-ring on focus.- Purpose
- Text entry primitive. Search box, password field, validation states use the same shell.
- Source of truth
@putdotio/ui · Input.tsx- Generated
- CSS class set · platform-native field shells
- Usage
<input class="field" type="text" placeholder="Search files"> <input class="field" aria-invalid="true" aria-describedby="field-error">
- Constraints
- Label outside the field · placeholder is hint, not label
- Invalid state is
aria-invalid="true": red border + red text, no red fill - No placeholders as labels
B.03
Form fields · extended
Date · stepper · slider · tag chips · autocomplete · file row · password strength. Plus the full validation vocabulary — error / warning / success / async / counted.
- Purpose
- Field types beyond plain text. Each inherits Input's shell + adds a behavior. Validation vocabulary is shared.
- Source of truth
@putdotio/ui · Field/*- Generated
- CSS variants · native pickers per platform
- Usage
<FieldDate name="due" value={d} onChange={set} /> <FieldSlider min={0} max={100} step={5} />
- Constraints
- Validation state is a prop, not a class — one source per field
- No custom-built date picker — defer to platform native on mobile
B.04
Form layouts
Seven canonical compositions. Settings row (label-left) · modal stacked · two-column · inline composer · wizard · sticky save bar · click-to-edit.
- Purpose
- Composition patterns. Pick the closest match — don't invent a new one.
- Source of truth
@putdotio/ui · FormLayout- Generated
- Grid templates · responsive breakpoints
- Usage
<FormLayout variant="settings-row"> ... </FormLayout> <FormLayout variant="two-column"> ... </FormLayout>
- Constraints
- Sticky save bar appears only on dirty state
- No mid-form modals — break into a wizard instead
B.05
Badges & chips
Lifecycle pills, filter chips, user tags. No codec / quality / "watched" badges — see content-agnostic.
- Purpose
- Status communication. Lifecycle (queued/in-progress/done/failed), filter chips, user tags.
- Source of truth
@putdotio/ui · Badge.tsx·Chip.tsx- Generated
- CSS classes · semantic color mapping
- Usage
<Badge tone="positive">Completed</Badge> <Badge tone="in-motion">Downloading</Badge> <Chip selected>Video</Chip>
- Constraints
- Tone maps to semantic scale (positive/destructive/in-motion)
- No codec / quality / poster badges on files
- No "watched" state — we don't track that
B.06
List items
The most-touched primitive. Sidebar nav · setting row · friend row · activity row — same skeleton, four shapes.
- Purpose
- Single horizontal row primitive. Composes leading icon · primary text · secondary text · trailing meta · trailing action.
- Source of truth
@putdotio/ui · ListItem.tsx- Generated
- Slot-based CSS · TS prop types
- Usage
<ListItem leading={icon} title="Files" trailing={count} /> <ListItem title="Theme" subtitle="Auto" layout="setting" />
- Constraints
- Min row height 44px (touch) · 48px (TV focus margin)
- Trailing meta uses
--font-ui-mono - No more than one trailing element
B.07
File row
The content-agnostic file primitive. Yellow folder icon, mono filename, mono size, mono date. Same skeleton in web tables, mobile lists, TV rows.
- Purpose
- Render any file/folder identically: icon · filename · size · date. No metadata enrichment, ever.
- Source of truth
@putdotio/ui · FileRow.tsx- Generated
- Web table / mobile cell / TV row variants
- Usage
<FileRow kind="folder" // folder | video | audio | doc | archive | other name="The.Wire.S03E04.1080p.x264-GROUP.mkv" // raw, verbatim size="1.42 GB" date="2026-05-25" />
- Constraints
- Filename always Berkeley Mono, ellipsis-on-overflow, hover reveals full
- Folder =
ph-folder-fillin--yellow-solid - No parsing, no marketing-group stripping
- No thumbnail · no codec badge · no resolution chip
B.08
Storage bar
Healthy (with file-type breakdown) · warn 80% · over 95%. Compact sidebar variant + plan-upsell card.
- Purpose
- Quota readout. Three thresholds (healthy / warn / over) drive color. Breakdown is by file-type, not metadata.
- Source of truth
@putdotio/ui · StorageBar.tsx- Generated
- Web sidebar + standalone card
- Usage
<StorageBar used={11_200_000_000_000} total={50_000_000_000_000} breakdown={["video", "audio", "doc"]} />
- Constraints
- Threshold colors: green ≤80, yellow 80-95, red >95
- No "junk file" or "biggest movie" labels — we don't know that
B.09
Transfer states
Queued → downloading → completed / seeding / failed. Health indicator replaces torrent jargon (seed ratio, peer count).
- Purpose
- The "is it working?" answer. One health dot (green/yellow/red) plus a one-line status. Power-user detail (ratio, peers) is in expand.
- Source of truth
@putdotio/ui · TransferRow.tsx- Generated
- Web + mobile + TV transfer-row variants
- Usage
<TransferRow state="downloading" // queued | downloading | seeding | completed | failed health="good" // good | warn | bad progress={0.62} name="ubuntu-22.04.iso" />
- Constraints
- Default view is plain-English: "Downloading · 62%"
- Lime accent (
--lime-3) for in-motion surface only - No "Seed ratio 2.00/10 days" in default view
B.10
Empty & error states
Kaomoji + one-line copy + one yellow CTA. Loading skeleton above 200ms wait. Red error sheet for real failures.
- Purpose
- Communicate "nothing here yet" or "something broke" with personality and a single clear next action.
- Source of truth
@putdotio/ui · EmptyState.tsx·ErrorSheet.tsx- Generated
- Per-platform empty illustrations as text/kaomoji
- Usage
<EmptyState kao="ᕦ(ò_óˇ)ᕤ" title="No transfers yet" cta={{ label: "Add a transfer", onClick: ... }} />
- Constraints
- One CTA · one kaomoji · one line of copy
- Loading skeleton kicks in at 200ms, not earlier
- No illustration art · no decorative SVG
B.11
Overlays
Dialog · sheet · bottom-sheet · confirm. One scrim, one elevation. Bottom-sheet is mobile-first; the others render anywhere.
- Purpose
- Modal surfaces. Dialog (centered) · sheet (right edge) · bottom-sheet (mobile) · confirm (small destructive).
- Source of truth
@putdotio/ui · Overlay/*- Generated
- CSS + ARIA primitives · native sheets on iOS/Android
- Usage
<Dialog open={o} onClose={c}> ... </Dialog> <BottomSheet open={o} onClose={c} actions={[ ... ]} />
- Constraints
- Scrim opacity from
--overlay-fullonly - Bottom-sheet primary action at top; destructive in red
- No nested overlays
- Scrim opacity from
B.12
Other primitives
Avatars · breadcrumbs · dropzone · notification · tabs · tooltip. Same token rules, same focus model.
- Purpose
- Smaller, less-touched primitives. Lifted verbatim from the same token roles as everything else.
- Source of truth
@putdotio/ui · Avatar / Breadcrumbs / Dropzone / Notification / Tabs / Tooltip- Generated
- Per-platform variants
- Usage
<Avatar name="kai" /> <Breadcrumbs path={["Files", "Movies"]} /> <Tabs items={t} /> <Tooltip content="…">…</Tooltip>
- Constraints
- Tooltips: keyboard-accessible, dismissible on Esc
- Notifications: max 3 stacked · auto-dismiss 4s for info, manual for destructive
Part C
Platforms
Where tokens and components compose into the surfaces users actually touch. This repo publishes generic CSS / JSON; platform repos adapt locally. Visual stays put.io.
C.00
Platform contract
What lives where. This repo = framework-agnostic source of truth. Each platform repo consumes the generated artifact and implements natively.
- Purpose
- Clarify the seam between generic spec (here) and platform implementation (in each app repo). Spec changes propagate via artifact regeneration, not copy-paste.
- Source of truth
system/tokens.css+preview/*.htmlspecimens- Generated
- CSS · DTCG JSON · flat JSON · TypeScript metadata · Figma export
- Usage · per-platform consumption
// any web / JS runtime — install the CSS package import "@putdotio/design/css"; // any tool that speaks DTCG JSON (Style Dictionary, Tokens Studio …) read("@putdotio/design/tokens/dtcg") // → consume in your own build // native app repos build their own adapter from the same JSON
- Constraints
- Platform repos may add — never override — token values
- Visual matches across platforms; engine (focus, blur, player) is local
- No platform-specific color in this repo
- No "TV-only" tokens here — TV adaptation lives in putio-ios / android
// repo split putio-design/ // THIS REPO — tokens, component specs, flow specs // ships: CSS · DTCG JSON · TS · Figma export product implementation// consumes @putdotio/design/css putio-tv-web/ // lightweight JS runtime · same CSS putio-{ios,android,roku}/ // build their OWN adapters from DTCG JSON // — this repo doesn't ship Swift / Kotlin / BrightScript
B · 1
Web · desktop browserputdotio/product implementation
C.01
App shell
Collapsible sidebar · breadcrumb top bar with /-search · selection toolbar · column-headed file table.
- Purpose
- Top-level web layout. Sidebar nav + main content + storage footer. Keyboard-first.
- Source of truth
product app shell- Generated
- React component · CSS grid template
- Usage
<AppShell sidebar={<Sidebar />} topbar={<TopBar />}> <FilesView /> </AppShell>
- Constraints
- Sidebar collapsible; persisted preference
- One yellow CTA on the topbar (New transfer)
- No second yellow button anywhere in the shell
C.02
Web references
Convergent ground-truth screens to lift from. Live in
putdotio/product implementation; cross-project canvases mirror them here.v12 – v15 · linear-neutral
Pending source
Files list · Light + Dark
putdotio/product implementation · product file browser surface
Pending source
File detail / player
Right-rail actions, breadcrumbs, Chromecast button.
product file-detail surface
Pending source
Billing & plans
Persona-shaped pricing for trial / casual / plus / power tiers. Composes from Buttons + List items.
B · 2
Mobile · iOS & Androidputio-ios · putio-android
C.03
iOS & Android shells
iOS Files screen · Android player chrome · shared pairing/activation flow used across iOS, Android, tvOS, Roku.
- Purpose
- Touch-first shells. iOS uses native nav stack; Android uses Material You scaffold tinted with brand yellow.
- Source of truth
- Platform repos (out-of-tree). Shells composed from
@putdotio/uiprimitives in each platform's idiom. - Generated
- Spec only — platform repos implement natively
- Usage · iOS
NavigationStack { FilesView() .toolbar { ToolbarItem(placement: .primaryAction) { AddTransferButton() } } }
- Constraints
- Long-press → bottom-sheet for file actions
- Destructive items use
--red-solidtext - No swipe-to-delete without confirm
C.04
Bottom-sheet actions
Mobile uses the bottom-sheet from Overlays for file actions. Long-press → bottom-sheet → tap; destructive in red.
C.05
Mobile references
Pending canonical sources — the new
putio-ios & putio-android apps are being built spec-first.
Pending source
iOS · Player + PiP
AVPlayer-backed. Lock Screen / Dynamic Island controls.
Pending source
Android · Files list
Material You scaffold, brand-yellow accent override.
Pending source
Settings · iOS / Android
Compose from List items — Account · Plan · Privacy · Notifications · Apps & integrations · Storage groups.
C · 3
TV · Apple TV · Android TV · Roku · Smart-TVplatform repos adapt locally
C.06
Foundations
Row anatomy · type scale at 10ft · surface materials · content rules. List-first — see house rules.
tv-shell.css
- Purpose
- 10-foot UI primitives. Filename-driven; no thumbnails, no posters. Rows of 56-72px focus targets.
- Source of truth
system/preview/tv-shell.css+ the TV specimen pages below- Generated
- CSS scope (
.tv) · DTCG TV-context tokens - Usage
/* TV body never below 24px */ .tv .row { font-size: 28px; line-height: 1.25; padding: 18px 32px; } .tv .row .name { font-family: var(--font-mono); } /* raw filename */
- Constraints
- List rows, not card grids · D-pad navigation only
- Min focus target 88px
- No card walls · no hero rails · no "Continue Watching" if we can't track it
C.07
Focus
scale(1.06) + 4px yellow ring + drop shadow. Same visual on every TV platform, different engines drawing it.C.10
Player chrome
Scrubber, audio/subtitle pickers, info overlay. Player engine is platform-specific (AVPlayer / libVLC / Roku / HTML5).
C.11
Platform matrix
Seven platforms × five differences (focus engine, surface material, player ownership, perf floor, token mapping).
Pending preview
Strategy & token mapping table
Web · iOS · Android · Apple TV · Android TV · Roku · Smart-TV web. Per-platform engine, surface, player-ownership and token-mapping breakdown lives in the platform repos.
C.12
Specimen file map
Every TV preview embedded above is a flat HTML file in
system/preview/. Native app repos consume the DTCG JSON for tokens and lift these specimens verbatim into their own idiom.system/preview/tv-*
The specimen is the contract. When implementing a TV component natively, open the matching
preview/tv-*.html + preview/tv-shell.css, read the exact values (font-size, weight, tracking, line-height, padding, radius, color tokens, shadow), then re-express them in your platform's native language. No native bundles ship from this repo.
TV
Foundations
spec
TV
Focus
spec
TV
Top navigation
spec
TV
Action menus
spec
TV
Player chrome
spec
TV
TV stylesheet
css
Native consumers · out-of-treenot shipped from this repo
iOS
Apple TV · tvOS app
native
AT
Android TV / Google TV
native
RK
Roku channel
native
SM
Smart-TV web · Tizen / webOS
native