# useColor

> A hook that returns the appropriate color based on the current theme, with support for prop overrides and fallback to predefined color palettes.

**BNA UI** — a React Native / Expo component library.
These components render through `react-native`, not the DOM: there are no HTML
elements, no Tailwind classes and no Radix primitives. Source is copied into your
project and imported through `@/components/ui/*`, `@/components/charts/*`,
`@/hooks/*` and `@/theme/*`. Colours come from the `useColor` hook rather than
hardcoded hex; sizing tokens (`HEIGHT`, `FONT_SIZE`, `BORDER_RADIUS`, `CORNERS`)
come from `@/theme/globals`.

- Docs: https://ui.ahmedbna.com/docs/hooks/useColor
- Markdown: https://ui.ahmedbna.com/docs/hooks/useColor.md
- Structured JSON (props, usage, source, examples): https://ui.ahmedbna.com/r/ai/useColor.json
- Install payload (source plus every file it imports): https://ui.ahmedbna.com/r/useColor.json
- Install: `npx bna-ui add useColor`
- Registry dependencies: `mode-provider`, `useColorScheme`, `colors`

---

## Installation

### CLI

```bash
npx bna-ui add useColor
```

### Manual

**1.** This hook requires the useColorScheme hook and Colors theme. Install
dependencies first:

```bash
npx bna-ui add useColorScheme
```

**2.** Copy and paste the following code into your project.

```ts
// hooks/useColor.ts
import { useColorScheme } from '@/hooks/useColorScheme';
import { Colors } from '@/theme/colors';

export function useColor(
  colorName: keyof typeof Colors.light & keyof typeof Colors.dark,
  props?: { light?: string; dark?: string }
) {
  const theme = useColorScheme() ?? 'light';
  const colorFromProps = props?.[theme];

  if (colorFromProps) {
    return colorFromProps;
  } else {
    return Colors[theme][colorName];
  }
}
```

**3.** Ensure you have a Colors theme file with light and dark variants.

```ts
// theme/colors.ts
const lightColors = {
  // Base colors
  background: '#FFFFFF',
  foreground: '#000000',

  // Card colors
  card: '#F2F2F7',
  cardForeground: '#000000',

  // Popover colors
  popover: '#F2F2F7',
  popoverForeground: '#000000',

  // Primary colors
  primary: '#18181b',
  primaryForeground: '#FFFFFF',

  // Secondary colors
  secondary: '#F2F2F7',
  secondaryForeground: '#18181b',

  // Muted colors
  muted: '#78788033',
  mutedForeground: '#71717a',

  // Accent colors
  accent: '#F2F2F7',
  accentForeground: '#18181b',

  // Destructive colors
  destructive: '#ef4444',
  destructiveForeground: '#FFFFFF',

  // Border and input
  border: '#C6C6C8',
  input: '#e4e4e7',
  ring: '#a1a1aa',

  // Text colors
  text: '#000000',
  textMuted: '#71717a',

  // Legacy support for existing components
  tint: '#18181b',
  icon: '#71717a',
  tabIconDefault: '#71717a',
  tabIconSelected: '#18181b',

  // Default buttons, links, Send button, selected tabs
  blue: '#007AFF',

  // Success states, FaceTime buttons, completed tasks
  green: '#34C759',

  // Delete buttons, error states, critical alerts
  red: '#FF3B30',

  // VoiceOver highlights, warning states
  orange: '#FF9500',

  // Notes app accent, Reminders highlights
  yellow: '#FFCC00',

  // Pink accent color for various UI elements
  pink: '#FF2D92',

  // Purple accent for creative apps and features
  purple: '#AF52DE',

  // Teal accent for communication features
  teal: '#5AC8FA',

  // Indigo accent for system features
  indigo: '#5856D6',

  // Semantic states
  success: '#22c55e',
  successForeground: '#ffffff',
  warning: '#f59e0b',
  warningForeground: '#ffffff',
  info: '#3b82f6',
  infoForeground: '#ffffff',
  error: '#ef4444',
  errorForeground: '#ffffff',
};

const darkColors = {
  // Base colors
  background: '#000000',
  foreground: '#FFFFFF',

  // Card colors
  card: '#1C1C1E',
  cardForeground: '#FFFFFF',

  // Popover colors
  popover: '#18181b',
  popoverForeground: '#FFFFFF',

  // Primary colors
  primary: '#e4e4e7',
  primaryForeground: '#18181b',

  // Secondary colors
  secondary: '#1C1C1E',
  secondaryForeground: '#FFFFFF',

  // Muted colors
  muted: '#78788033',
  mutedForeground: '#a1a1aa',

  // Accent colors
  accent: '#1C1C1E',
  accentForeground: '#FFFFFF',

  // Destructive colors
  destructive: '#dc2626',
  destructiveForeground: '#FFFFFF',

  // Border and input - using alpha values for better blending
  border: '#38383A',
  input: 'rgba(255, 255, 255, 0.15)',
  ring: '#71717a',

  // Text colors
  text: '#FFFFFF',
  textMuted: '#a1a1aa',

  // Legacy support for existing components
  tint: '#FFFFFF',
  icon: '#a1a1aa',
  tabIconDefault: '#a1a1aa',
  tabIconSelected: '#FFFFFF',

  // Default buttons, links, Send button, selected tabs
  blue: '#0A84FF',

  // Success states, FaceTime buttons, completed tasks
  green: '#30D158',

  // Delete buttons, error states, critical alerts
  red: '#FF453A',

  // VoiceOver highlights, warning states
  orange: '#FF9F0A',

  // Notes app accent, Reminders highlights
  yellow: '#FFD60A',

  // Pink accent color for various UI elements
  pink: '#FF375F',

  // Purple accent for creative apps and features
  purple: '#BF5AF2',

  // Teal accent for communication features
  teal: '#64D2FF',

  // Indigo accent for system features
  indigo: '#5E5CE6',

  // Semantic states
  success: '#16a34a',
  successForeground: '#ffffff',
  warning: '#d97706',
  warningForeground: '#ffffff',
  info: '#2563eb',
  infoForeground: '#ffffff',
  error: '#dc2626',
  errorForeground: '#ffffff',
};

export const Colors = {
  light: lightColors,
  dark: darkColors,
};

// Export individual color schemes for easier access
export { darkColors, lightColors };

// Utility type for color keys
export type ColorKeys = keyof typeof lightColors;

// Helper function to get color with opacity (useful for React Native)
export const withOpacity = (color: string, opacity: number) => {
  // Handle rgba colors
  if (color.startsWith('rgba')) {
    return color;
  }

  // Handle hex colors
  if (color.startsWith('#')) {
    const hex = color.replace('#', '');
    const r = parseInt(hex.substr(0, 2), 16);
    const g = parseInt(hex.substr(2, 2), 16);
    const b = parseInt(hex.substr(4, 2), 16);
    return `rgba(${r}, ${g}, ${b}, ${opacity})`;
  }

  return color;
};
```

**4.** Update the import paths to match your project setup.

## Usage

```tsx
import { useColor } from '@/hooks/useColor';
```

```tsx
export function ThemedText({ style, ...props }) {
  const color = useColor('text', { light: '#000', dark: '#fff' });

  return <Text style={[{ color }, style]} {...props} />;
}
```

## API Reference

### useColor

Returns the appropriate color based on the current theme, with support for prop overrides.

#### Parameters

| Parameter   | Type                                | Description                                        |
| ----------- | ----------------------------------- | -------------------------------------------------- |
| `props`     | `{ light?: string; dark?: string }` | Optional color overrides for light and dark themes |
| `colorName` | `keyof Colors.light & Colors.dark`  | The color key from your Colors theme object        |

#### Returns

| Type     | Description                                                                                     |
| -------- | ----------------------------------------------------------------------------------------------- |
| `string` | The resolved color value - either from props override or from the Colors theme for current mode |

## Color Resolution Priority

The hook resolves colors in the following order:

1. **Prop Override**: If a color is provided in props for the current theme
2. **Theme Fallback**: The color from the Colors theme object for the current theme
3. **Light Default**: Falls back to light theme if current theme is null

```tsx
// Example resolution flow
const color = useColor(
  { light: '#custom-light', dark: '#custom-dark' },
  'primary'
);

// If current theme is 'dark':
// 1. Returns '#custom-dark' (prop override)
// 2. If no dark prop, returns Colors.dark.primary
// 3. If no Colors.dark, falls back to Colors.light.primary
```

## Theme Structure

Your Colors theme should follow this structure:

```tsx
// theme/colors.ts
export const Colors = {
  light: {
    text: '#000000',
    background: '#ffffff',
    primary: '#007AFF',
    secondary: '#8E8E93',
    border: '#E5E5E7',
    // ... other colors
  },
  dark: {
    text: '#ffffff',
    background: '#000000',
    primary: '#0A84FF',
    secondary: '#636366',
    border: '#38383A',
    // ... other colors
  },
};
```

## Use Cases

This hook is perfect for:

- Creating theme-aware components that respect system preferences
- Building consistent color systems across your application
- Allowing component-level color customization while maintaining theme consistency
- Creating reusable UI components with proper dark mode support
- Implementing accessible color schemes with proper contrast ratios

## Best Practices

### Component Design Patterns

Create themed components that accept color overrides:

```tsx
interface ThemedButtonProps {
  title: string;
  onPress: () => void;
  colors?: { light?: string; dark?: string };
  variant?: 'primary' | 'secondary';
}

export function ThemedButton({
  title,
  onPress,
  colors,
  variant = 'primary',
}: ThemedButtonProps) {
  const backgroundColor = useColor(
    colors || {},
    variant === 'primary' ? 'primary' : 'secondary'
  );

  const textColor = useColor('background');

  return (
    <TouchableOpacity
      style={{
        backgroundColor,
        padding: 16,
        borderRadius: 8,
        alignItems: 'center',
      }}
      onPress={onPress}
    >
      <Text style={{ color: textColor, fontWeight: 'bold' }}>{title}</Text>
    </TouchableOpacity>
  );
}
```

### Consistent Color Naming

Use consistent color names across your theme:

```tsx
// Good: Semantic color names
const Colors = {
  light: {
    text: '#000000',
    textSecondary: '#666666',
    background: '#ffffff',
    backgroundSecondary: '#f8f8f8',
    primary: '#007AFF',
    primaryLight: '#5AC8FA',
    danger: '#FF3B30',
    success: '#34C759',
    warning: '#FF9500',
  },
  // ... dark theme
};
```

### Performance Optimization

Memoize complex color calculations:

```tsx
import { useMemo } from 'react';

export function useThemedStyles() {
  const backgroundColor = useColor('background');
  const textColor = useColor('text');
  const borderColor = useColor('border');

  return useMemo(
    () => ({
      container: {
        backgroundColor,
        borderColor,
        borderWidth: 1,
        borderRadius: 8,
        padding: 16,
      },
      text: {
        color: textColor,
        fontSize: 16,
      },
    }),
    [backgroundColor, textColor, borderColor]
  );
}
```

### Type Safety

Ensure type safety with proper TypeScript definitions:

```tsx
// theme/colors.ts
export const Colors = {
  light: {
    text: '#000000',
    background: '#ffffff',
    primary: '#007AFF',
    // ... other colors
  },
  dark: {
    text: '#ffffff',
    background: '#000000',
    primary: '#0A84FF',
    // ... other colors
  },
} as const;

// This ensures colorName parameter is properly typed
export type ColorName = keyof typeof Colors.light & keyof typeof Colors.dark;
```

## Advanced Usage

### Contextual Color Variations

Create variations of colors based on context:

```tsx
export function useContextualColor(
  baseColor: keyof typeof Colors.light & keyof typeof Colors.dark,
  variant: 'default' | 'muted' | 'emphasis' = 'default'
) {
  const theme = useColorScheme() ?? 'light';
  const baseColorValue = Colors[theme][baseColor];

  // Apply contextual modifications
  switch (variant) {
    case 'muted':
      return theme === 'dark'
        ? `${baseColorValue}80` // Add opacity
        : `${baseColorValue}60`;
    case 'emphasis':
      return theme === 'dark'
        ? lighten(baseColorValue, 0.2)
        : darken(baseColorValue, 0.1);
    default:
      return baseColorValue;
  }
}
```

### Animated Color Transitions

Combine with animations for smooth theme transitions:

```tsx
import { useEffect, useRef } from 'react';
import { Animated } from 'react-native';

export function useAnimatedThemeColor(
  props: { light?: string; dark?: string },
  colorName: keyof typeof Colors.light & keyof typeof Colors.dark
) {
  const color = useColor(props, colorName);
  const animatedColor = useRef(new Animated.Value(0)).current;

  useEffect(() => {
    Animated.timing(animatedColor, {
      toValue: 1,
      duration: 300,
      useNativeDriver: false,
    }).start();
  }, [color]);

  return color; // In practice, you'd interpolate the animated value
}
```

## Dependencies

- `@/hooks/useColorScheme` - Required for theme detection
- `@/theme/colors` - Required for color palette definitions

## Accessibility

The hook supports accessibility by:

- Respecting system-level dark mode preferences
- Enabling proper color contrast ratios through theme definitions
- Supporting high contrast modes when defined in your color palette
- Maintaining consistent color relationships across themes

## Related Hooks

- [`useColorScheme`](/docs/hooks/useColorScheme) - Base hook for theme detection

## References

Learn more about implementing color schemes in Expo:

- [Expo Color Schemes Guide](https://docs.expo.dev/guides/color-schemes/)
- [React Native Appearance API](https://reactnative.dev/docs/appearance)
- [iOS Human Interface Guidelines - Dark Mode](https://developer.apple.com/design/human-interface-guidelines/dark-mode)
- [Material Design - Dark Theme](https://material.io/design/color/dark-theme.html)
