Tac UIv1.1.2

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.

tsx
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.

tsx
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.

tsx
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.

tsx
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.*

PropTypeDefaultDescription
backgroundstring#FFFFFFPage background color.
foregroundstring#0F172ADefault text color.
primarystring#4E657EPrimary brand color for buttons and accents.
primaryHoverstring#3E5165Hover state of the primary color.
primaryForegroundstring#FFFFFFText color on primary backgrounds.
secondarystring#F1F5F9Secondary surface color for subtle backgrounds.
borderstring#E2E8F0Default border color.
mutedForegroundstring#64748BSubdued text (descriptions, placeholders).
pointstring#96784AAccent color for highlights and callouts.

Status Colors

Semantic colors for feedback states. Each has matching Bg and Foreground variants accessible via theme.colors.*

PropTypeDefaultDescription
successstring#10B981Success state color.
successBgstring#D1FAE5Success background color.
successForegroundstring#065F46Text color on success backgrounds.
warningstring#F59E0BWarning state color.
warningBgstring#FEF3C7Warning background color.
warningForegroundstring#92400EText color on warning backgrounds.
errorstring#EF4444Error/destructive state color.
errorBgstring#FEE2E2Error background color.
errorForegroundstring#991B1BText color on error backgrounds.
infostring#3B82F6Informational state color.
infoBgstring#DBEAFEInfo background color.
infoForegroundstring#1E40AFText color on info backgrounds.

Spacing

Spacing tokens available via theme.spacing.* (values in points).

PropTypeDefaultDescription
xsnumber44pt — Tight spacing (icon gaps).
smnumber88pt — Small spacing.
mdnumber1212pt — Medium spacing.
lgnumber1616pt — Large spacing.
xlnumber2424pt — Extra large spacing.
2xlnumber3232pt — Section-level spacing.

Border Radius

Rounding scale from sharp to fully rounded. Available via theme.radius.* (values in points).

PropTypeDefaultDescription
nonenumber0No rounding.
smnumber44pt — Subtle rounding (badges, chips).
mnumber88pt — Default rounding (inputs, buttons).
lgnumber1212pt — Larger rounding (cards).
xlnumber1616pt — Extra large rounding.
pillnumber99999999pt — Fully rounded (pills, avatars).

Motion

Duration and spring config tokens for animations. Available via theme.motion.*

PropTypeDefaultDescription
duration.instantnumber5050ms — Immediate feedback.
duration.fastnumber150150ms — Quick transitions (hover, focus).
duration.normalnumber250250ms — Standard animations.
duration.slownumber400400ms — Deliberate animations (modals, drawers).
duration.complexnumber600600ms — Complex multi-step animations.
easing.standardstringbezier(0.4, 0, 0.2, 1)Default easing curve.
easing.easeOutstringbezier(0, 0, 0.2, 1)Deceleration curve (enter animations).
spring.defaultobject{ damping: 20, stiffness: 300 }Default spring config for Animated API.
spring.bouncyobject{ 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.

typescript
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.

typescript
// 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 },
    },
  },
}