react-native-adaptive-text
v1.0.1
Published
Picks a legible text color from a background (WCAG 2.1 + optional APCA). AdaptiveText, context theme, zero native code.
Maintainers
Readme
react-native-adaptive-text

Ever put text on a colored card and realize it’s hard to read, or you keep flipping between white and black by hand? react-native-adaptive-text does that thinking for you.
You give it the background color (and optionally a small list of brand colors). It picks a text color that stays readable, usually black or white, or whichever brand color scores best. Under the hood it uses the same kind of contrast math accessibility guidelines rely on (WCAG 2.1 luminance, plus optional APCA). You don’t need to be an accessibility expert to use it.
The library is pure JavaScript/TypeScript—no native modules, no extra pod install for this package—so it works in typical React Native and Expo setups the same way.
Why people reach for it
- Fewer “oops, can’t read that” moments on banners, buttons, chips, and colored headers.
- Brand friendly: you’re not locked to only black and white; pass a palette and let the best option win.
- Keeps RN simple: one import,
AdaptiveTextas a drop-inText, optionalAdaptiveTextThemeso you don’t repeatbackgroundColoron every line. - Icons and custom
Texttoo viauseAdaptiveForegroundColor, not only paragraphs—so the whole tile can match.
Try the demo (see it on screen)
If you already use React Native, open a terminal in this repo’s example folder and run:
cd example
npm install
npx pod-install # iOS only, first time
npm run android
# or
npm run iosYou’ll get a small sample app that walks through the main ideas (widget demo, palette, extension-style helpers, APCA)—no need to wire anything up first.
The table below uses iOS simulator captures from the example app (assets/demo_*.png).
Use it in your own app
1. Add the package:
npm install react-native-adaptive-text
# or
yarn add react-native-adaptive-text2. Import once:
import { AdaptiveText } from 'react-native-adaptive-text';3. Swap a line of text for something that “knows” the background:
<AdaptiveText backgroundColor="#1a237e" style={{ fontSize: 18 }}>
Hello
</AdaptiveText>That’s the happy path: the package chooses a foreground that tends to read well on that background. An explicit style.color still wins if you set it.
Optional: your brand colors instead of only black/white
<AdaptiveText
backgroundColor="#000000"
palette={['#ff9800', '#eeeeee', '#ffeb3b']}
>
Sale ends today
</AdaptiveText>Optional: set the background once for a whole section
import { AdaptiveTextTheme, AdaptiveText } from 'react-native-adaptive-text';
<AdaptiveTextTheme backgroundColor={cardColor} algorithm="wcag">
<AdaptiveText style={{ fontWeight: '700' }}>Title</AdaptiveText>
<AdaptiveText>Subtitle</AdaptiveText>
</AdaptiveTextTheme>You need React Native ≥ 0.71 and React ≥ 18. Run npm run build in the library root once if you install from a file: path so dist/ exists (see Development below).
A note for designers and product folks
This package answers: “What color should the letters be so people can actually read them?”
It does not replace a full design system, pick gradients for you, or read colors out of a photo. It works with solid ColorValues you already chose for UI surfaces (and respects an explicit text color when you set one).
Want the full technical map?
Types, exports, WCAG/APCA details, hooks, and parity with flutter_adaptive_text are documented in the sections below and in the source on GitHub:
- Repository & README source
- npm package page (when published)
License & source
MIT. See LICENSE.
Repository: github.com/iuzairaslam/react-native-adaptive-text
Detailed documentation
Table of contents
- Why this library?
- Features
- Architecture
- Requirements
- Installation
- Theme and overrides
- Standalone math and color helpers
- Hooks
- Context API
- Types
- Flutter API parity
- Development
- Platform notes
Why this library?
- Accessible contrast by default: foreground is chosen from luminance and WCAG-style contrast (or APCA when you opt in).
- Zero native setup: works on iOS and Android with the same JS bundle—no
pod installfor this package, no Android Gradle changes, no autolinking. - Composable: use
AdaptiveTextas a drop-inText, or wrap subtrees withAdaptiveTextThemeso you do not repeatbackgroundColoron every line. - Explicit color wins: if you set
style.color, it is respected (same rule as the Flutter widget).
Features
AdaptiveText:Textwith automatic foreground frombackgroundColorand/or theme.AdaptiveTextTheme: providebackgroundColor, optionalpalette, andalgorithm(wcag|apca) for descendantAdaptiveTextand hooks.- Hooks:
useAdaptiveForegroundColorforTextInput, icons, or customText;useAdaptiveTextThemeto read the current theme. - Core helpers: luminance, contrast ratio, WCAG checks, APCA,
getAdaptiveColoronRgb, and React NativeColorValue→ hex utilities. - Optional palette: choose the best color from a brand list instead of only black/white.
- Shipped as
dist/formain: Metro still resolves"react-native": "src/index.ts"for local development against the repo.
Architecture
| Layer | Role |
|--------|------|
| algorithm.ts | Pure sRGB math: luminance, WCAG contrast, APCA, getAdaptiveColor, meetsWcag, isLight / isDark. No react-native import. |
| colorUtils.ts | ColorValue parsing, hex helpers, adaptiveTextHex, style introspection for explicit color. |
| AdaptiveTextTheme.tsx | React context: AdaptiveTextTheme, useAdaptiveTextTheme. |
| AdaptiveText.tsx | Composes theme + utils and renders Text. |
| useAdaptiveForegroundColor.ts | Same resolution rules as AdaptiveText, returns a hex string for non-Text use cases. |
Nothing in this tree registers native view managers or TurboModules: it is JS-only, suitable for Expo and bare React Native alike.
Requirements
- React Native: ≥ 0.71
- React: ≥ 18
- iOS: ≥ 13 (typical RN baseline)
- Android: API ≥ 21
No native modules for this library: no
pod installchanges, no Android application edits, and no linking steps.
Installation
npm (recommended)
npm install react-native-adaptive-textGitHub Packages (optional)
Use only if you publish or consume the package from GitHub Packages under a scoped name (replace your-org with your GitHub org or username).
npm login --scope=@your-org --auth-type=legacy --registry=https://npm.pkg.github.com
npm install @your-org/react-native-adaptive-textLocal path (monorepo or sibling folder)
"react-native-adaptive-text": "file:../react-native-adaptive-text"Run npm run build in the library root once so dist/ exists. Metro resolves react-native to src/index.ts for local development.
Theme and overrides
import { AdaptiveTextTheme, AdaptiveText } from 'react-native-adaptive-text';
export function Card() {
return (
<AdaptiveTextTheme backgroundColor="#1a237e" algorithm="wcag">
<AdaptiveText style={{ fontWeight: '700' }}>Title</AdaptiveText>
<AdaptiveText>Subtitle</AdaptiveText>
</AdaptiveTextTheme>
);
}Palette + APCA
import { AdaptiveText, ContrastAlgorithm } from 'react-native-adaptive-text';
const brand = ['#ff9800', '#eeeeee', '#ffeb3b'];
export function BrandLine() {
return (
<AdaptiveText
backgroundColor="#000000"
palette={brand}
algorithm={ContrastAlgorithm.apca}
>
Brand text
</AdaptiveText>
);
}Standalone math and color helpers
import {
getAdaptiveColor,
getLuminance,
meetsWcag,
rgbToHex,
} from 'react-native-adaptive-text';
const bg = { r: 26, g: 35, b: 126 };
const rgb = getAdaptiveColor(bg);
const hex = rgbToHex(rgb);import { adaptiveTextHex, colorValueToRgb } from 'react-native-adaptive-text';
adaptiveTextHex('#1a237e');
adaptiveTextHex('rgb(0,0,0)', { palette: ['#ff0', '#fff'] });import { adaptiveRgb } from 'react-native-adaptive-text';
adaptiveRgb.adaptiveTextColor({ r: 0, g: 0, b: 0 });
adaptiveRgb.contrastRatioWith({ r: 0, g: 0, b: 0 }, { r: 255, g: 255, b: 255 });Hooks
import { Text } from 'react-native';
import {
useAdaptiveForegroundColor,
AdaptiveTextTheme,
} from 'react-native-adaptive-text';
function Subtitle() {
const color = useAdaptiveForegroundColor();
return <Text style={{ color }}>Using theme</Text>;
}
export function Screen() {
return (
<AdaptiveTextTheme backgroundColor="#333">
<Subtitle />
</AdaptiveTextTheme>
);
}Context API
import { useAdaptiveTextTheme } from 'react-native-adaptive-text';
const theme = useAdaptiveTextTheme();
// theme is AdaptiveTextThemeData | null
// { backgroundColor, palette?, algorithm }Types
import type { ColorValue, TextProps } from 'react-native';
type ContrastAlgorithm = 'wcag' | 'apca';
interface Rgb {
r: number;
g: number;
b: number;
}
interface AdaptiveTextThemeData {
backgroundColor: ColorValue;
palette?: ColorValue[] | null;
algorithm: ContrastAlgorithm;
}
interface AdaptiveTextProps extends TextProps {
backgroundColor?: ColorValue;
palette?: ColorValue[] | null;
algorithm?: ContrastAlgorithm;
}Flutter API parity
| Flutter | React Native |
|--------|----------------|
| getLuminance | getLuminance |
| getContrastRatio | getContrastRatio |
| getAdaptiveColor | getAdaptiveColor |
| getApcaContrast | getApcaContrast |
| isLight / isDark | isLight / isDark |
| meetsWcag | meetsWcag |
| ContrastAlgorithm | ContrastAlgorithm |
| AdaptiveText | AdaptiveText |
| AdaptiveTextTheme | AdaptiveTextTheme |
| BuildContext extension | useAdaptiveForegroundColor, useAdaptiveTextTheme |
| Color extension | adaptiveRgb, adaptiveTextHex, colorValueToRgb |
Development
npm install
npm run build # emits dist/
npm test # library unit tests
npm run test:all # library + example tests
npm run typescript # tsc --noEmitCommit messages: edit rnat-commit-msg.txt (subject + body at the top of the file), stage your changes, then either:
git add -A
npm run commitor git commit -F rnat-commit-msg.txt -c core.hooksPath=/dev/null. That reuses the same file each time and skips hooks that append unwanted Co-authored-by: trailers. Push as usual: git push origin main.
Platform notes
- Works on iOS and Android with the same JavaScript API.
- No optional native peer dependencies for this package.
