put.io design system

Tokens, primitives and component specs for put.io. Generic CSS / JSON here; platform apps adapt natively.
v1.0 · 2026-05 tokens.css ↗

Pipeline

Sourcetokens/*.tokens.json · canonical DTCG
Generatorscripts/build-tokens.ts
Outputsystem/tokens.css · generated
Basetokens/foundation.css · static layer
TargetsCSS now · Figma / iOS / Android via the same graph

Consume

CSS
DTCG JSON
Build
/* generated output — import in any page */
@import "system/tokens.css";

/* use vars as roles, never literals */
.btn-primary {
  background: var(--yellow-solid);
  color: var(--primary-foreground);
  border-radius: var(--radius);
}
// tokens/*.tokens.json · W3C DTCG — paths map 1:1 to CSS vars
{
  "yellow": {
    "solid": {
      "$value": "hsl(44.7, 97.9%, 63.1%)",
      "$type": "color",
      "$description": "Brand · #FDCE45 · → --yellow-solid"
    }
  },
  "radius": {
    "radius": {
      "$value": "6px", "$type": "dimension",
      "$description": "Default radius · → --radius"
    }
  }
}
// regenerate after editing tokens/*.tokens.json
$ node scripts/build-tokens.ts

// any tool that speaks DTCG can read the same graph
read("tokens/*.tokens.json")   // Style Dictionary, Tokens Studio, …

// platform adapters live in app repos, not here

House rules

  1. Utility is beautiful. Beauty comes from making file operations feel premium — not from decoration.
  2. Content-agnostic by default. No posters, album art, curated metadata, or “watched” state. Filename, size, date.
  3. Raw filenames stay raw. Never parse, rewrite or fake metadata from a filename. Render verbatim, mono.
  4. Clarity over cleverness. One verb per icon, one icon per verb. Download / Save / Stream never overlap.
  5. Type carries the weight. Three families — sans for UI, two monos for numerics and identifiers.
  6. One yellow at a time. #FDCE45 is the single CTA and the focus ring. Not decoration.
  7. TV is list-first. Without thumbnails, rows beat card walls. Don’t copy Apple TV or Plex.
  8. 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). Canonical source: tokens/*.tokens.jsontokens.css and every other artifact is generated from it.
A.01

Color

12-step scales, light + dark. One brand, three semantic, 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 (canonical) → tokens.css — light :root + .dark overlay
Usage
/* light + dark in one stylesheet — toggle .dark on <html> */
.btn { background: var(--yellow-solid); color: var(--primary-foreground); }
.row { background: var(--bg-secondary); color: var(--text); }
.row:hover { background: var(--list-item-bg-hover); }
Constraints
  • --yellow-solid is fill-only. For yellow text, use --yellow-text-secondary (AA on light bg).
  • No new color literals in component code
  • No semantic scale for decoration — status only
Brandstep 9 shifts between modes; everything else flips like a normal 12-step scale
BRAND
--yellow-solid
#FDCE45
Brand fill: CTAs, focus halos (35% alpha), folder icons, progress bars. Never as text on light.
+ hover
--yellow-solid-hover
#F3C435
CTA hover — slightly darker brand yellow. Yellow can't darken much without going olive; this is the most we can shift.
contrast
--primary-foreground
#292109
Label text on yellow-solid — warm dark, not pure black. yellow-contrast.
surface
--yellow-bg
step 1 · #FEFDFA / #000
Faintest tint backdrop in light; drops to pure black at the deepest step in dark.
Neutrals · dark (app default)12 steps · gray scale
--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 input fill.
--lime-3
in-motion accent
Transfer downloading / completing rows. put.io-specific.
specimens

Color · canonical previewspreview/color-*

Semantic scalesopen ↗
Surface rolesopen ↗
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
tokens/*.tokens.json (type group) → tokens.css · brand fonts from put.io's font CDN, system fallbacks
Generated
CSS custom properties (--font-*, --fs-*)
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
96px · 900
put.io
--fs-3xl
64px · 700
Display heading
--fs-2xl
48px · 700
Display heading · small
--fs-xl
2rem · 28–30 · 700
Page title
--fs-lg
1.5rem · 21–22.5 · 500
Heading
--fs-md
1.125rem · ~16 · 500
Subheading · card title
--fs-base
1rem · 14–15 · 400
Body — app default (= root, matches production fontSize 1)
--fs-sm
0.875rem · 12–13 · 400
Caption · button label (production fontSize 0)
--fs-xs
0.75rem · ~11 · 500 · UP
Fine print / overline
--font-ui-mono
GT Mono · tabular
11.20 TB · 12.4 MB/s · 00:03:42
--font-mono
Berkeley · filenames
ubuntu-22.04.iso · sha1: e3b0c44…
specimens

Type · canonical previewspreview/type-*

Familiesopen ↗
Type scaleopen ↗
Specimenopen ↗
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)
Spacing in contextopen ↗
A.04

Radii & shadow

Five radii, four elevations. 6 px is the default for buttons, inputs, cards. Yellow halo is one of the shadows.
Purpose
Radii match component scale. Shadows define elevation roles — hover/focus, popover, modal — and a yellow focus halo that is the focus system.
Source of truth
tokens/*.tokens.jsontokens.css · --radius-* · --shadow-*
Generated
CSS custom properties
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
  • Only --shadow-focus ever uses yellow
  • 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
--shadow-md
popover, menu
0 4px 12px / 0.5
--shadow-lg
modal, sheet
0 16px 40px / 0.6
--shadow-focus
soft halo
0 0 0 3px / 0.35
Radii & shadows · canonical previewopen ↗
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-fast for hover, --dur-base for menus, --dur-slow for sheets.
Source of truth
tokens/*.tokens.jsontokens.css · --ease-* · --dur-*
Generated
CSS custom properties · 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-motion at the component level
  • No bounce / overshoot easings
Easing & duration scaleopen ↗
A · 2

Brand & primitivespreview/brand-* · focus-system · icon-set

A.07

Icons

Phosphor going forward. ph-fill ph-folder 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-fill ph-folder tinted --yellow-solid
  • No emoji as icons · no decorative SVG
  • No legacy Flaticons font in new code
A.08

Focus

Three input contexts, one ring. Keyboard / pointer / D-pad resolve to the same brand-yellow halo, scaled to the device.
Purpose
Unified focus visual across runtimes. Web/mobile = soft 3px brand-yellow halo (35% alpha). TV = 4px ring + scale(1.06) + drop shadow.
Source of truth
tokens/*.tokens.jsontokens.css · --shadow-focus
Generated
TV variant via tv-shell.css
Usage
/* web/mobile */
.btn:focus-visible { box-shadow: var(--shadow-focus); }
/* 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
  • Always yellow — focus is the one place yellow is mandatory
  • Use :focus-visible on web/mobile, never plain :focus
  • No outline removal without a visible replacement
Focus contextsopen ↗
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.01

Buttons

5 variants × 3 sizes. Primary is brand-yellow on a black ink. Only one primary per region — see one yellow at a time.
Purpose
Primary CTA + success + danger + info + invert + default + icon. All states (hover, active, focus-visible, disabled) lift from token roles.
Source of truth
Button
Generated
CSS class set · DTCG component tokens · TS prop types
Usage
<button class="btn btn-primary">Save</button>
<button class="btn btn-default">Cancel</button>
<button class="btn btn-info">Retry</button>
<button class="btn btn-danger">Delete</button>
Constraints
  • One btn-primary per surface region
  • Min-width 96px web · 44px hit-target mobile · 88px focus-target TV
  • No icon-only button without aria-label
B.02

Inputs

Text, search (with / hint), password, error. Yellow border + halo on :focus-visible.
Purpose
Text entry primitive. Search box, password field, validation states use the same shell.
Source of truth
Input
Generated
CSS class set · platform-native field shells
Usage
<input class="input" type="text" placeholder="Search files">
<input class="input is-error" aria-invalid="true">
Constraints
  • Label outside the field · placeholder is hint, not label
  • Error message uses --red-solid below the field
  • No placeholders as labels
Inputsopen ↗
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
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
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
Badge · Chip
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
Pills · chips · tagsopen ↗
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
ListItem
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
FileRow
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-fill ph-folder in --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
StorageBar
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
Disk usageopen ↗
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
TransferRow
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
EmptyState · ErrorSheet
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
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-full only
  • Bottom-sheet primary action at top; destructive in red
  • No nested overlays
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
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
Avatarsopen ↗
Breadcrumbsopen ↗
Dropzoneopen ↗
Notificationsopen ↗
Tooltipsopen ↗
Segmented controlopen ↗
Data tableopen ↗
Folder pickeropen ↗
Skeleton & loadingopen ↗
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/*.html specimens
Generated
CSS · DTCG JSON · flat JSON · TypeScript metadata · Figma export
Usage · per-platform consumption
// any web / JS runtime — install the CSS package
import "the generated CSS bundle";

// any tool that speaks DTCG JSON (Style Dictionary, Tokens Studio …)
read("tokens/*.tokens.json") // → 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 the iOS app / android
// distribution model
this design system     // tokens, component specs, flow specs
                       // ships: CSS · DTCG JSON · TS · Figma export

           // consumes the generated CSS bundle
the web apps           // desktop + smart-TV · same CSS
the native apps        // iOS / Android / Roku — build their OWN
                       // adapters from the DTCG JSON; this system
                       // doesn't ship Swift / Kotlin / BrightScript
B · 1

Web · desktop browserthe web app

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
the web app · main layout
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 the web app; cross-project canvases mirror them here.
v12 – v15 · linear-neutral
Pending source Files list · Light + Dark the web app · files feature
Pending source File detail / player Right-rail actions, breadcrumbs, Chromecast button. the web app · file detail
Pending source Command palette · ⌘K New surface. Compose from Inputs + List items.
Pending source Billing & plans Persona-shaped pricing for trial / casual / plus / power tiers. Composes from Buttons + List items.
B · 2

Mobile · iOS & AndroidiOS · 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 the component library primitives 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-solid text
  • 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 the iOS app & the Android app apps are being built spec-first.
Pending source iOS · Transfers queue Swipe-to-act rows. Compose from Transfer states + iOS shell.
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.08

Top navigation

Glass tabs + search affordance. Backdrop-blur where the runtime supports it; flat fallback where it doesn't.
C.09

Action menus

File-actions overlay. Same composition rules as the mobile bottom-sheet — different chrome, identical IA.
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. Each consuming platform repo owns its own engine, surface, player-ownership and token-mapping breakdown.
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.
Native consumers · out-of-treenot shipped from this repo
iOS
Apple TV · tvOS app
native app
native
AT
Android TV / Google TV
native app
native
RK
Roku channel
native app
native
SM
Smart-TV web · Tizen / webOS
web app
native