Theming (Level 1)
Customize colors, typography, and semantic text styles to match your brand.
Quick Start
import { ThemeProvider } from "@rocapine/react-native-onboarding-ui";
<ThemeProvider
customTheme={{
colors: { primary: "#FF5733" },
typography: {
fontFamily: { title: "Poppins-Bold" }
}
}}
>
<YourApp />
</ThemeProvider>
That's it! Your theme tokens are now applied across all onboarding screens.
Color Customization
Available Color Tokens
colors: {
// Main brand colors
primary: string, // Buttons, accents
secondary: string, // Secondary actions
disable: string, // Disabled states
// Tertiary/accent colors
tertiary: {
tertiary1: string,
tertiary2: string,
tertiary3: string,
},
// Neutral grays (highest = darkest, lowest = lightest)
neutral: {
highest: string,
higher: string,
high: string,
medium: string,
low: string,
lower: string,
lowest: string,
},
// Surface/background colors
surface: {
lowest: string, // Main background
lower: string,
low: string,
medium: string,
high: string,
higher: string,
highest: string,
opposite: string, // Opposite of main background
},
// Text colors
text: {
primary: string, // Main text
secondary: string, // Secondary text
tertiary: string, // Tertiary text
opposite: string, // Opposite of primary
disable: string, // Disabled text
}
}
Color Customization Examples
Override primary color:
<ThemeProvider
customTheme={{
colors: { primary: "#007AFF" }
}}
>
<YourApp />
</ThemeProvider>
Override multiple colors:
<ThemeProvider
customTheme={{
colors: {
primary: "#007AFF",
secondary: "#5856D6",
text: {
primary: "#1A1A1A",
secondary: "#666666"
}
}
}}
>
<YourApp />
</ThemeProvider>
Nested overrides:
<ThemeProvider
customTheme={{
colors: {
neutral: {
lowest: "#F5F5F5",
highest: "#1A1A1A"
},
surface: {
lowest: "#FFFFFF",
opposite: "#000000"
}
}
}}
>
<YourApp />
</ThemeProvider>
Light & Dark Mode
Customize themes for each mode separately:
<ThemeProvider
customLightTheme={{
colors: {
primary: "#007AFF",
surface: { lowest: "#FFFFFF" },
text: { primary: "#000000" }
}
}}
customDarkTheme={{
colors: {
primary: "#0A84FF",
surface: { lowest: "#000000" },
text: { primary: "#FFFFFF" }
}
}}
>
<YourApp />
</ThemeProvider>
Or use a single theme for both:
<ThemeProvider
customTheme={{
colors: { primary: "#FF5733" }
}}
>
<YourApp />
</ThemeProvider>
Switch modes programmatically:
import { useTheme } from "@rocapine/react-native-onboarding-ui";
function ThemeSwitcher() {
const { colorScheme, toggleTheme } = useTheme();
return (
<Button
title={`Switch to ${colorScheme === "light" ? "dark" : "light"}`}
onPress={toggleTheme}
/>
);
}
Typography Customization
Font Families
The SDK uses three font categories:
- title: Used for headings (heading1, heading2)
- text: Used for body text, buttons, labels
- tagline: Used for special emphasis text
There are two ways to load custom fonts:
- Bundle locally with
expo-font— for fonts shipped with the app binary. - Download at runtime via the
Onboarding.fontsmanifest — for fonts the CMS declares per onboarding (no native rebuild required).
Option A — Local fonts via expo-font
import { useFonts } from 'expo-font';
import { ThemeProvider } from "@rocapine/react-native-onboarding-ui";
export default function RootLayout() {
const [fontsLoaded] = useFonts({
'Poppins-Bold': require('./assets/fonts/Poppins-Bold.ttf'),
'Roboto-Regular': require('./assets/fonts/Roboto-Regular.ttf'),
});
if (!fontsLoaded) return null;
return (
<ThemeProvider
customTheme={{
typography: {
fontFamily: {
title: "Poppins-Bold",
text: "Roboto-Regular",
}
}
}}
>
<YourApp />
</ThemeProvider>
);
}
Option B — Runtime fonts via the Onboarding payload (since 1.17.0)
The backend can ship a font manifest in the onboarding response. The SDK
downloads each variant via expo-font (optional peer dep) and blocks render
until they are ready. ComposableScreen TextElement, ButtonElement, and
InputElement automatically resolve fontFamily + fontWeight to the right
registered variant — CMS authors keep using the family name they declared.
Backend Onboarding response shape:
{
"metadata": { /* ... */ },
"steps": [ /* ... */ ],
"configuration": { /* ... */ },
"fonts": {
"Inter": {
"regular": "https://cdn/.../Inter-Regular.ttf",
"medium": "https://cdn/.../Inter-Medium.ttf",
"bold": "https://cdn/.../Inter-Bold.ttf"
},
"Lobster": {
"regular": "https://cdn/.../Lobster-Regular.ttf"
}
}
}
Weight keys may be named (regular, medium, semibold, bold,
extrabold) or numeric (100…900); both are normalized internally.
When the requested weight is not registered, the resolver falls back to the
closest available weight (CSS-style font matching).
Show a fallback while fonts download:
import { OnboardingProvider } from "@rocapine/react-native-onboarding";
import { ActivityIndicator } from "react-native";
<OnboardingProvider
client={client}
fontsFallback={<ActivityIndicator />}
>
<YourApp />
</OnboardingProvider>
For non-OnboardingProvider hosts, mount fonts directly with FontLoaderGate:
import { FontLoaderGate, type FontsManifest } from "@rocapine/react-native-onboarding";
import { ActivityIndicator } from "react-native";
const fonts: FontsManifest = {
Inter: {
regular: "https://cdn/.../Inter-Regular.ttf",
bold: "https://cdn/.../Inter-Bold.ttf",
},
};
<FontLoaderGate fonts={fonts} fallback={<ActivityIndicator />}>
<YourScreen />
</FontLoaderGate>
If expo-font is not installed in the host app, registerFonts resolves to an
empty registry and logs a single warning — text falls back to system fonts.
Default Font Family (since 1.19.0)
typography.defaultFontFamily is the inherit target for ComposableScreen
Text, Button, and Input elements. When an element omits fontFamily or
sets it to the literal "inherit", the renderer falls back to this token.
Set it once at the theme level to brand every onboarding text element with a
single family — no need to patch each textStyles.*.fontFamily entry or
sprinkle fontFamily on every CMS payload element.
<ThemeProvider
customTheme={{
typography: {
defaultFontFamily: "Lobster",
},
}}
>
<YourApp />
</ThemeProvider>
CMS payload examples:
{ "type": "Text", "props": { "content": "Inherits Lobster" } }
{ "type": "Text", "props": { "content": "Also Lobster", "fontFamily": "inherit" } }
{ "type": "Text", "props": { "content": "Stays Inter", "fontFamily": "Inter" } }
Default value: "Inter". Set to undefined to fall back to the platform
system font when an element does not declare its own fontFamily.
Font Sizes
Override font size tokens:
<ThemeProvider
customTheme={{
typography: {
fontSize: {
xs: 10,
sm: 12,
md: 14,
lg: 18,
xl: 28, // heading2
"2xl": 36, // heading1
"3xl": 48,
"4xl": 72,
}
}
}}
>
<YourApp />
</ThemeProvider>
Default sizes:
xs: 12pxsm: 14pxmd: 16pxlg: 20pxxl: 24px2xl: 32px3xl: 40px4xl: 72px
Font Weights
Override font weight tokens:
<ThemeProvider
customTheme={{
typography: {
fontWeight: {
regular: "400",
medium: "500",
semibold: "700", // Make semibold bolder
bold: "700",
extrabold: "800",
}
}
}}
>
<YourApp />
</ThemeProvider>
Line Heights
Override line height tokens:
<ThemeProvider
customTheme={{
typography: {
lineHeight: {
tight: 1.2,
normal: 1.4,
relaxed: 1.6,
}
}
}}
>
<YourApp />
</ThemeProvider>
Semantic Text Styles
The SDK provides semantic text styles that match Figma specifications:
Default Semantic Styles
heading1: {
fontSize: 32, // 2xl token
fontWeight: "600", // semibold token
lineHeight: 1.25, // tight token
fontFamily: "title" // title font
}
heading2: {
fontSize: 24, // xl token
fontWeight: "600", // semibold token
lineHeight: 1.3, // normal token
fontFamily: "title" // title font
}
heading3: {
fontSize: 18, // lg token
fontWeight: "500", // medium token
lineHeight: 1.3, // normal token
fontFamily: "text" // text font
}
body: {
fontSize: 16, // md token
fontWeight: "400", // regular token
lineHeight: 1.3, // normal token
fontFamily: "text" // text font
}
bodyMedium: {
fontSize: 16, // md token
fontWeight: "500", // medium token
lineHeight: 1.3, // normal token
fontFamily: "text" // text font
}
label: {
fontSize: 14, // sm token
fontWeight: "500", // medium token
lineHeight: 1.3, // normal token
fontFamily: "text" // text font
}
caption: {
fontSize: 12, // xs token
fontWeight: "400", // regular token
lineHeight: 1.3, // normal token
fontFamily: "text" // text font
}
button: {
fontSize: 16, // md token
fontWeight: "500", // medium token
lineHeight: 1.5, // relaxed token
fontFamily: "text" // text font
}
Override Semantic Styles
<ThemeProvider
customTheme={{
typography: {
textStyles: {
heading1: {
fontSize: 40,
fontWeight: "700",
lineHeight: 1.2,
fontFamily: "title"
},
body: {
fontSize: 18,
fontWeight: "400",
lineHeight: 1.4,
fontFamily: "text"
}
}
}
}}
>
<YourApp />
</ThemeProvider>
Using Theme in Custom Code
Access Theme Tokens
import { useTheme } from "@rocapine/react-native-onboarding-ui";
function MyComponent() {
const { theme } = useTheme();
return (
<View style={{ backgroundColor: theme.colors.surface.lowest }}>
<Text style={{
color: theme.colors.text.primary,
fontSize: theme.typography.textStyles.heading3.fontSize,
}}>
Hello
</Text>
</View>
);
}
Use Semantic Text Styles
import { useTheme, getTextStyle } from "@rocapine/react-native-onboarding-ui";
function MyComponent() {
const { theme } = useTheme();
return (
<View>
<Text style={[
getTextStyle(theme, "heading1"),
{ color: theme.colors.text.primary }
]}>
Title
</Text>
<Text style={[
getTextStyle(theme, "body"),
{ color: theme.colors.text.secondary }
]}>
Body text
</Text>
</View>
);
}
Complete Example
import { useFonts } from 'expo-font';
import {
OnboardingProvider,
OnboardingStudioClient,
} from "@rocapine/react-native-onboarding";
import {
ThemeProvider,
ProgressBar,
} from "@rocapine/react-native-onboarding-ui";
const client = new OnboardingStudioClient("your-project-id", {
appVersion: "1.0.0",
});
export default function RootLayout() {
// Load custom fonts
const [fontsLoaded] = useFonts({
'CustomFont-Bold': require('./assets/fonts/CustomFont-Bold.ttf'),
'CustomFont-Regular': require('./assets/fonts/CustomFont-Regular.ttf'),
});
if (!fontsLoaded) return null;
return (
<OnboardingProvider
client={client}
locale="en"
customAudienceParams={{ onboardingId: "your-onboarding-id" }}
>
<ThemeProvider
customTheme={{
colors: {
primary: "#FF5733",
secondary: "#5856D6",
text: {
primary: "#1A1A1A",
secondary: "#666666"
},
surface: {
lowest: "#FFFFFF"
}
},
typography: {
fontFamily: {
title: "CustomFont-Bold",
text: "CustomFont-Regular",
},
fontSize: {
"2xl": 36,
xl: 26,
},
textStyles: {
heading1: {
fontSize: 36,
fontWeight: "700",
lineHeight: 1.2,
fontFamily: "title"
}
}
}
}}
>
<ProgressBar />
<YourApp />
</ThemeProvider>
</OnboardingProvider>
);
}
Default Theme Reference
Want to extend the default theme instead of overriding? Import default tokens:
import {
lightTokens,
darkTokens,
typography,
} from "@rocapine/react-native-onboarding-ui";
const myTheme = {
colors: {
...lightTokens.colors,
primary: "#FF5733",
},
typography: {
...typography,
fontFamily: {
...typography.fontFamily,
title: "CustomFont-Bold"
}
},
};
<ThemeProvider customTheme={myTheme}>
<YourApp />
</ThemeProvider>
Deep Merge Behavior
The SDK uses deep merging for theme tokens, so you only need to specify what changes:
// This merges with default tokens
<ThemeProvider
customTheme={{
colors: { primary: "#FF5733" } // Other colors remain default
}}
>
<YourApp />
</ThemeProvider>
You don't need to provide all tokens. Only specify the ones you want to override.
Next Steps
- 🔧 Custom Components - Replace specific UI components
- 🎭 Custom Renderers - Complete screen control
- 📘 API Reference - Full theme token reference