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
#e9ecefsecondarySecondary brand color
#f8f9fasecondaryLightLighter secondary shade
#dee2e6secondaryDarkDarker 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
#e9ecefsecondary#e9ecef#343a40
#f8f9fasecondaryLight#f8f9fa#495057
#dee2e6secondaryDark#dee2e6#2b3035

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.