Skip to content

Colors (React Native)

React Native uses the same 38 semantic color tokens as Web, but accessed via the theme object instead of CSS custom properties. All values come from @grundtone/core — the single source of truth for both platforms.

Naming Convention

Tokens use shade-based naming (primaryLight, primaryDark) instead of state-based (primaryHover, primaryActive). Components decide which shade to use for hover, active, tint, etc.


Setup

Wrap your app with GrundtoneThemeProvider and pass light/dark themes from createTheme():

tsx
import { GrundtoneThemeProvider } from '@grundtone/react-native';
import { createTheme } from '@grundtone/core';

const { light, dark } = createTheme({
  light: {
    primary: '#0059b3',
    primaryLight: '#3381cc',
    primaryDark: '#003a7a',
    onPrimary: '#ffffff',
  },
  dark: {
    primary: '#4dabf7',
    primaryLight: '#74c0fc',
    primaryDark: '#339af0',
    onPrimary: '#121212',
  },
});

export default function App() {
  return (
    <GrundtoneThemeProvider light={light} dark={dark}>
      <RootNavigator />
    </GrundtoneThemeProvider>
  );
}

The provider automatically follows the system appearance (light/dark) via React Native's Appearance API. Override with the defaultMode or mode prop.


Using Colors

Access colors via the useGrundtoneTheme() hook:

tsx
import { useGrundtoneTheme } from '@grundtone/react-native';

function Card({ title, children }) {
  const { theme } = useGrundtoneTheme();

  return (
    <View
      style={{
        backgroundColor: theme.colors.surface,
        borderColor: theme.colors.borderLight,
        borderWidth: 1,
        borderRadius: 8,
      }}
    >
      <Text style={{ color: theme.colors.text }}>{title}</Text>
      <Text style={{ color: theme.colors.textSecondary }}>{children}</Text>
    </View>
  );
}

Brand

ColorTokenPurpose
#0059b3primaryPrimary brand color
#3381ccprimaryLightLighter shade (hover, tints)
#003a7aprimaryDarkDarker shade (active, pressed)
#ffffffonPrimaryText on primary background
#6c757dsecondarySecondary brand color
#868e96secondaryLightLighter secondary shade
#494f54secondaryDarkDarker secondary shade

Status

ColorTokenPurpose
#198754successSuccess state
#d1e7ddsuccessLightSuccess background tint
#146c43successDarkDarker success shade
#ffc107warningWarning state
#fff3cdwarningLightWarning background tint
#cc9a06warningDarkDarker warning shade
#dc3545errorError state
#f8d7daerrorLightError background tint
#b02a37errorDarkDarker error shade
#0dcaf0infoInfo state
#cff4fcinfoLightInfo background tint
#0aa2c0infoDarkDarker info shade

Surface

ColorTokenPurpose
#ffffffbackgroundPage background
#fafafabackgroundAltAlternate sections, zebra rows
#f8f9fasurfaceCards, panels
#f0f1f2surfaceAltHover state, zebra
#ffffffsurfaceRaisedModals, FABs
rgba(255,255,255,0.95)surfaceOverlaySemi-transparent overlay
rgba(0,0,0,0.5)modalBackdropModal backdrop scrim
tsx
// Modal backdrop example
function ModalBackdrop({ visible, children }) {
  const { theme } = useGrundtoneTheme();
  if (!visible) return null;

  return (
    <View style={[StyleSheet.absoluteFill, { backgroundColor: theme.colors.modalBackdrop }]}>
      {children}
    </View>
  );
}

Text

ColorTokenPurpose
#212529textPrimary text
#6c757dtextSecondarySecondary text
#adb5bdtextTertiaryTertiary / hint text
#fffffftextInverseText on dark backgrounds
#a3a3a3textPlaceholderInput placeholders
#d4d4d4textDisabledDisabled elements

Border

ColorTokenPurpose
#dee2e6borderLightSubtle dividers
#ced4daborderMediumDefault inputs
#adb5bdborderStrongFocus, emphasized
rgba(255,255,255,0.2)borderInverseOn dark backgrounds

Focus & Neutral

ColorTokenPurpose
#0059b3focusFocus indicator
rgba(0,89,179,0.25)focusRingFocus ring shadow
#6c757dneutralNeutral UI elements

Light vs Dark Comparison

Side-by-side comparison of all tokens across modes.

Brand

ColorTokenLightDark
#0059b3primary#0059b3#4dabf7
#3381ccprimaryLight#3381cc#74c0fc
#003a7aprimaryDark#003a7a#339af0
#ffffffonPrimary#ffffff#121212
#6c757dsecondary#6c757d#adb5bd
#868e96secondaryLight#868e96#ced4da
#494f54secondaryDark#494f54#868e96

Status

ColorTokenLightDark
#198754success#198754#51cf66
#d1e7ddsuccessLight#d1e7dd#1a3d20
#146c43successDark#146c43#40c057
#ffc107warning#ffc107#ffd43b
#fff3cdwarningLight#fff3cd#3d3a1a
#cc9a06warningDark#cc9a06#fab005
#dc3545error#dc3545#ff6b6b
#f8d7daerrorLight#f8d7da#3d1a1c
#b02a37errorDark#b02a37#fa5252
#0dcaf0info#0dcaf0#4dabf7
#cff4fcinfoLight#cff4fc#1a2e3d
#0aa2c0infoDark#0aa2c0#339af0

Surface

ColorTokenLightDark
#ffffffbackground#ffffff#121212
#fafafabackgroundAlt#fafafa#1a1a1a
#f8f9fasurface#f8f9fa#1e1e1e
#f0f1f2surfaceAlt#f0f1f2#252525
#ffffffsurfaceRaised#ffffff#2a2a2a
rgba(255,255,255,0.95)surfaceOverlayrgba(255,255,255,0.95)rgba(30,30,30,0.95)
rgba(0,0,0,0.5)modalBackdroprgba(0,0,0,0.5)rgba(0,0,0,0.7)

Text

ColorTokenLightDark
#212529text#212529#ffffff
#6c757dtextSecondary#6c757d#b0b0b0
#adb5bdtextTertiary#adb5bd#808080
#fffffftextInverse#ffffff#121212
#a3a3a3textPlaceholder#a3a3a3#666666
#d4d4d4textDisabled#d4d4d4#4a4a4a

Border

ColorTokenLightDark
#dee2e6borderLight#dee2e6#404040
#ced4daborderMedium#ced4da#505050
#adb5bdborderStrong#adb5bd#606060
rgba(255,255,255,0.2)borderInversergba(255,255,255,0.2)rgba(0,0,0,0.3)

Dark Mode Control

The hook provides isDark and setMode for manual control:

tsx
const { theme, isDark, setMode } = useGrundtoneTheme();

// Toggle
setMode(isDark ? 'light' : 'dark');

// Use in styles
<View style={{ backgroundColor: theme.colors.background }}>
  <Text style={{ color: theme.colors.text }}>
    {isDark ? 'Dark mode' : 'Light mode'}
  </Text>
</View>;

Overriding Colors

Override only what you need in createTheme() — the rest uses defaults:

tsx
const { light, dark } = createTheme({
  light: {
    primary: '#e91e63',
    primaryLight: '#f06292',
    primaryDark: '#c2185b',
    onPrimary: '#ffffff',
  },
  dark: {
    primary: '#f48fb1',
    primaryLight: '#f8bbd0',
    primaryDark: '#ec407a',
    onPrimary: '#121212',
  },
});

You can override any of the 38 tokens. Unspecified tokens keep their defaults.