一键导入
rn-theming
Theme token system: colors.ts and typography.ts structure, surface hierarchy, how to apply brand colors consistently
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Theme token system: colors.ts and typography.ts structure, surface hierarchy, how to apply brand colors consistently
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Template repo file structure: where screens, navigation, theme, data, and platform entries live in the monorepo
Android TV build and D-pad QA using Android CLI first, with bounded Gradle and ADB fallbacks
Expo Application Services cloud build configuration for TV app APK and IPA generation
Expo TV configuration: app.json plugins, prebuild settings, platform-specific config for react-native-tvos
Fire TV leanback manifest configuration: banner icons, LEANBACK_LAUNCHER intent filter, TV-specific Android settings
Gradle build commands: assembleDebug, assembleRelease, APK output paths, and emulator installation
| name | rn-theming |
| description | Theme token system: colors.ts and typography.ts structure, surface hierarchy, how to apply brand colors consistently |
| applies_to | ["phase_brand"] |
| load_when | applying `brand.json`, or any color/typography request |
Theme tokens live in
packages/shared-ui/theme/. A brand kit becomes a theme via mechanical token replacement, then a small set of judgment calls (contrast, accent placement, dark/light defaults).
packages/shared-ui/theme/
├── tokens.ts # The token table — single source of truth
├── ThemeProvider.tsx # Context provider, consumed by all components
├── tokens.android.ts # Optional per-platform override
├── tokens.ios.ts # Optional per-platform override
└── tokens.kepler.ts # Optional per-platform override (Vega)
tokens.ts exports a typed object roughly shaped like:
export const tokens = {
color: {
background: "#0B0B0F",
surface: "#15151C",
primary: "#E50914",
accent: "#FFC857",
text: "#FFFFFF",
textMuted: "#A0A0A8",
focusRing: "#FFFFFF",
},
type: {
family: "Inter",
size: { body: 24, h2: 36, h1: 56 },
weight: { regular: "400", bold: "700" },
},
radius: { sm: 4, md: 8, lg: 16 },
spacing: { 1: 4, 2: 8, 3: 12, 4: 16, 6: 24, 8: 32 },
};
brand.json → tokensbrand.json field | Goes into | Notes |
|---|---|---|
primary_color | color.primary | Used for CTAs, active states |
accent_color | color.accent | Used for focus rings, highlights |
background_color | color.background | App-wide background |
font_family | type.family | Must be loaded via expo-font if non-system |
logo_path | Not a token — copied to assets/logo.svg, referenced in Drawer | |
splash_path | Not a token — copied to apps/expo-multi-tv/assets/splash.png |
TV is viewed in dim rooms much more often than phones. Default dark unless the brief says otherwise. Test:
brand_kit.background_color lightness ≥ 0.85 → light mode is the intent.The planner's theme.mode field overrides this if explicitly set.
TV viewers sit 8–10 ft away. Minimum acceptable contrast ratios are stricter than WCAG mobile baselines:
If brand.primary_color fails contrast against background_color at body sizes, do not silently use it for text. Use it for CTAs and accents; use tokens.color.text for body.
The template uses white as a default focus ring. This works on dark backgrounds. On lighter brands:
#FFFFFF.accent_color if it contrasts ≥ 4.5:1, else darken accent_color.#111111).The contrast check is non-negotiable. A focus ring you can't see from across the room is a broken app.
body: 24 is the floor, not a suggestion.brand.font_family is non-system, register it via expo-font in the app's root layout. Verify it loads on all targets — fonts that work on iOS sometimes don't ship to Vega.Reach for tokens.ios.ts etc. only when the platform genuinely needs different values. Examples that warrant an override:
spacing there.type.family override.If you can't articulate why the override is needed, don't add one.
Generate (or accept user-provided):
If the brand kit only provides a logo, generate the rest:
background_color, with 30% margin.background_color, fitting 400×240.primary_color with 15% padding.useTheme(). If you write color: "#fff" in a component, you'll see it in the next bug report.brand.accent_color for body text. Accents are accents.