Customization
Customize native theme tokens by providing overrides to TacNativeProvider. All components consume tokens from the theme context via useTacNativeTheme().
How It Works
All Tac UI Native components consume design tokens via useTacNativeTheme(). Tokens are provided through TacNativeProvider and built from @tac-ui/tokens. You can override colors, spacing, border radius, motion, and more at the provider level.
Wrap your app in TacNativeProvider once. The default theme is built automatically from the tokens package. Use the theme prop to pass overrides — they are deep-merged with the defaults so you only specify what you want to change. Use useTacNativeTheme() anywhere in the tree to access the active theme object and switch modes.
import { TacNativeProvider, useTacNativeTheme } from '@tac-ui/native';
// Wrap once at the app root
export default function App() {
return (
<TacNativeProvider defaultMode="system">
<YourApp />
</TacNativeProvider>
);
}
// Access theme and mode controls anywhere in the tree
function ThemeToggle() {
const { mode, toggleMode, theme } = useTacNativeTheme();
return (
<Button onPress={toggleMode}>
{mode === 'dark' ? 'Switch to Light' : 'Switch to Dark'}
</Button>
);
}Theme Overrides
The simplest way to customize is to pass a theme prop to TacNativeProvider. The override object is deep-merged with the default theme, so only the values you specify are changed — all other tokens remain at their defaults.
import { TacNativeProvider } from '@tac-ui/native';
export default function App() {
return (
<TacNativeProvider
theme={{
colors: {
primary: '#059669',
primaryHover: '#047857',
primaryForeground: '#FFFFFF',
},
radius: {
m: 16,
lg: 20,
},
}}
>
<YourApp />
</TacNativeProvider>
);
}Per-Mode Overrides
To customize tokens differently for light and dark modes, use the lightTheme and darkTheme props on TacNativeProvider. These are merged on top of the base theme overrides for the matching mode.
import { TacNativeProvider } from '@tac-ui/native';
export default function App() {
return (
<TacNativeProvider
defaultMode="system"
lightTheme={{
colors: {
primary: '#2563EB',
background: '#FAFAFA',
border: '#D1D5DB',
},
}}
darkTheme={{
colors: {
primary: '#60A5FA',
background: '#0A0A0A',
border: '#27272A',
},
}}
>
<YourApp />
</TacNativeProvider>
);
}Per-Component Overrides
Every Tac UI Native component accepts a style prop for local overrides. For structural customization (padding, border radius, etc.) you can override the style directly. For color overrides that should follow the theme, use the component's dedicated style props.
import { Button, Card } from '@tac-ui/native';
import { useTacNativeTheme } from '@tac-ui/native';
function Example() {
const { theme } = useTacNativeTheme();
return (
<>
{/* Direct style override on a component */}
<Button
style={{
borderRadius: 999,
backgroundColor: '#059669',
}}
>
Custom Button
</Button>
{/* Use theme tokens for consistency */}
<Card
style={{
borderRadius: theme.radius.xl,
padding: theme.spacing.xl,
backgroundColor: theme.colors.card,
}}
>
<Text style={{ color: theme.colors.foreground }}>
Card with theme tokens
</Text>
</Card>
</>
);
}Token Reference
Colors
Core color tokens available via theme.colors.*
| Prop | Type | Default | Description |
|---|---|---|---|
| background | string | #FFFFFF | Page background color. |
| foreground | string | #0F172A | Default text color. |
| primary | string | #4E657E | Primary brand color for buttons and accents. |
| primaryHover | string | #3E5165 | Hover state of the primary color. |
| primaryForeground | string | #FFFFFF | Text color on primary backgrounds. |
| secondary | string | #F1F5F9 | Secondary surface color for subtle backgrounds. |
| border | string | #E2E8F0 | Default border color. |
| mutedForeground | string | #64748B | Subdued text (descriptions, placeholders). |
| point | string | #96784A | Accent color for highlights and callouts. |
Status Colors
Semantic colors for feedback states. Each has matching Bg and Foreground variants accessible via theme.colors.*
| Prop | Type | Default | Description |
|---|---|---|---|
| success | string | #10B981 | Success state color. |
| successBg | string | #D1FAE5 | Success background color. |
| successForeground | string | #065F46 | Text color on success backgrounds. |
| warning | string | #F59E0B | Warning state color. |
| warningBg | string | #FEF3C7 | Warning background color. |
| warningForeground | string | #92400E | Text color on warning backgrounds. |
| error | string | #EF4444 | Error/destructive state color. |
| errorBg | string | #FEE2E2 | Error background color. |
| errorForeground | string | #991B1B | Text color on error backgrounds. |
| info | string | #3B82F6 | Informational state color. |
| infoBg | string | #DBEAFE | Info background color. |
| infoForeground | string | #1E40AF | Text color on info backgrounds. |
Spacing
Spacing tokens available via theme.spacing.* (values in points).
| Prop | Type | Default | Description |
|---|---|---|---|
| xs | number | 4 | 4pt — Tight spacing (icon gaps). |
| sm | number | 8 | 8pt — Small spacing. |
| md | number | 12 | 12pt — Medium spacing. |
| lg | number | 16 | 16pt — Large spacing. |
| xl | number | 24 | 24pt — Extra large spacing. |
| 2xl | number | 32 | 32pt — Section-level spacing. |
Border Radius
Rounding scale from sharp to fully rounded. Available via theme.radius.* (values in points).
| Prop | Type | Default | Description |
|---|---|---|---|
| none | number | 0 | No rounding. |
| sm | number | 4 | 4pt — Subtle rounding (badges, chips). |
| m | number | 8 | 8pt — Default rounding (inputs, buttons). |
| lg | number | 12 | 12pt — Larger rounding (cards). |
| xl | number | 16 | 16pt — Extra large rounding. |
| pill | number | 9999 | 9999pt — Fully rounded (pills, avatars). |
Motion
Duration and spring config tokens for animations. Available via theme.motion.*
| Prop | Type | Default | Description |
|---|---|---|---|
| duration.instant | number | 50 | 50ms — Immediate feedback. |
| duration.fast | number | 150 | 150ms — Quick transitions (hover, focus). |
| duration.normal | number | 250 | 250ms — Standard animations. |
| duration.slow | number | 400 | 400ms — Deliberate animations (modals, drawers). |
| duration.complex | number | 600 | 600ms — Complex multi-step animations. |
| easing.standard | string | bezier(0.4, 0, 0.2, 1) | Default easing curve. |
| easing.easeOut | string | bezier(0, 0, 0.2, 1) | Deceleration curve (enter animations). |
| spring.default | object | { damping: 20, stiffness: 300 } | Default spring config for Animated API. |
| spring.bouncy | object | { damping: 12, stiffness: 400 } | Bouncy spring for playful interactions. |
Programmatic Access
You can also access and generate tokens programmatically by importing directly from the @tac-ui/tokens package. This is useful for building custom theme objects or computing derived values at runtime.
import { semanticTokens, spacing, radius, motion } from '@tac-ui/tokens';
import { buildNativeTheme } from '@tac-ui/tokens/native';
// Access raw semantic token values
console.log(semanticTokens.light.primary); // '#4E657E'
console.log(semanticTokens.dark.primary); // '#8AA3B8'
// Access structural tokens
console.log(spacing.lg); // 16
console.log(radius.m); // 8
console.log(motion.duration.normal); // 250
// Build a complete native theme object from tokens
const customTheme = buildNativeTheme({
colors: { primary: '#059669' },
});Full Theme Structure
Complete reference of the theme object returned by useTacNativeTheme(). All values below are the built-in light mode defaults.
// theme object returned by useTacNativeTheme()
{
mode: 'light' | 'dark',
colors: {
// Core
background: '#FFFFFF',
backgroundSubtle: '#F8FAFC',
surface: '#FFFFFF',
foreground: '#0F172A',
primary: '#4E657E',
primaryHover: '#3E5165',
primaryForeground: '#FFFFFF',
secondary: '#F1F5F9',
secondaryForeground: '#0F172A',
muted: '#F1F5F9',
mutedForeground: '#64748B',
card: '#FFFFFF',
cardForeground: '#0F172A',
border: '#E2E8F0',
input: '#E2E8F0',
point: '#96784A',
pointHover: '#C0B56A',
pointForeground: '#FFFFFF',
pointSubtle: '#F5F0E8',
// Status
success: '#10B981',
successBg: '#D1FAE5',
successForeground: '#065F46',
warning: '#F59E0B',
warningBg: '#FEF3C7',
warningForeground: '#92400E',
error: '#EF4444',
errorBg: '#FEE2E2',
errorForeground: '#991B1B',
info: '#3B82F6',
infoBg: '#DBEAFE',
infoForeground: '#1E40AF',
// Gray scale
gray50: '#F8FAFC',
// ...gray100 through gray900
},
typography: {
fontFamily: { primary: 'System', secondary: 'System' },
display: { lg: { fontSize, lineHeight }, md: { fontSize, lineHeight } },
heading: { h1, h2, h3, h4 },
body: { lg, md, sm },
caption: { md, sm },
fontWeight: { normal: '400', medium: '500', semibold: '600', bold: '700' },
},
spacing: { xs: 4, sm: 8, md: 12, lg: 16, xl: 24, '2xl': 32 },
radius: { none: 0, sm: 4, m: 8, lg: 12, xl: 16, pill: 9999 },
elevation: {
sm: { shadowColor, shadowOffset, shadowOpacity, shadowRadius, elevation },
m: { shadowColor, shadowOffset, shadowOpacity, shadowRadius, elevation },
lg: { shadowColor, shadowOffset, shadowOpacity, shadowRadius, elevation },
},
motion: {
duration: { instant: 50, fast: 150, normal: 250, slow: 400, complex: 600 },
easing: { standard, easeIn, easeOut, easeInOut, bounce, spring, elastic },
spring: {
default: { damping: 20, stiffness: 300 },
bouncy: { damping: 12, stiffness: 400 },
slow: { damping: 30, stiffness: 200 },
},
},
}