Skip to content

Typography

A production-ready typography component built entirely on createComponent primitives — variants for scale and weight, color mode at the base, font families in the weight variant, and a thin wrapper for accessibility. Copy and adapt to your design system.

Typography.tsx
import React from 'react';
import { Text as RNText } from 'react-native';
import { createComponent } from 'react-native-small-ui';
// ─── Base ─────────────────────────────────────────────────────────────────────
// createComponent handles scale, weight, font families, and color mode.
// Typography is kept internal — Text is what consumers use.
const Typography = createComponent(RNText, {
variants: {
preset: {
h1: { fontSize: 34, lineHeight: 41 },
h2: { fontSize: 28, lineHeight: 34 },
h3: { fontSize: 22, lineHeight: 28 },
h4: { fontSize: 20, lineHeight: 28 },
h5: { fontSize: 17, lineHeight: 24 },
h6: { fontSize: 14, lineHeight: 18 },
body: { fontSize: 16, lineHeight: 24 },
caption: { fontSize: 12, lineHeight: 18 },
small: { fontSize: 10, lineHeight: 14 },
},
weight: {
thin: { fontWeight: '200', fontFamily: 'Inter-Thin' },
light: { fontWeight: '300', fontFamily: 'Inter-Light' },
normal: { fontWeight: '400', fontFamily: 'Inter' },
semibold: { fontWeight: '600', fontFamily: 'Inter-SemiBold' },
bold: { fontWeight: '700', fontFamily: 'Inter-Bold' },
extrabold: { fontWeight: '900', fontFamily: 'Inter-ExtraBold' },
},
},
defaultVariants: {
preset: 'body',
weight: 'normal',
},
// Global text color — every instance inherits this unless overridden at the call site.
_light: { color: '#1a1a1a' },
_dark: { color: '#f0f0f0' },
});
// ─── Thin wrapper — handles the one concern createComponent can't ──────────────
// accessibilityRole is a behavioral prop, not a style. Three lines, fully transparent:
// all library features (style props, _light/_dark, .extend()) reach through via ...props.
const HEADING_PRESETS = new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6']);
type TypographyProps = React.ComponentProps<typeof Typography>;
export function Text({ preset = 'body', weight = 'normal', ...props }: TypographyProps) {
return (
<Typography
preset={preset}
weight={weight}
accessibilityRole={HEADING_PRESETS.has(preset) ? 'header' : undefined}
{...props}
/>
);
}

// Defaults apply — body size, normal weight, color mode from base
<Text>Some paragraph content</Text>
// Heading — accessibilityRole="header" set automatically
<Text preset="h1" weight="bold">Welcome Back</Text>
<Text preset="h3" weight="semibold">Section Title</Text>
// Per-instance color override — _light/_dark work on Text just like any createComponent output
<Text preset="caption" weight="semibold" _light={{ color: '#666' }} _dark={{ color: '#aaa' }}>
Last updated today
</Text>

All preset values — h1 through small. The accessibilityRole="header" wrapper applies automatically for h1–h6.


Concern Where it lives Why
Size scale preset variant Typed prop, autocompleted, no manual spread
Font weight + family weight variant Weight and face are always paired — one prop selects both
Text color Base _light/_dark Every instance inherits it; callers override per-instance
accessibilityRole Thin wrapper Behavioral prop, not a style — can’t live in a variant

Typography (the createComponent output) is internal. Text is what the rest of the app imports — it has the same API surface as any SmallUIComponent, plus the accessibility behaviour baked in.