@retray-dev/ui-kit
v13.0.0
Published
Personal UI Kit for React Native / Expo
Maintainers
Readme
📦 @retray-dev/ui-kit
A personal React Native / Expo UI component library with a built-in design system, dark mode support, haptic feedback, and smooth animations.
- 54 components across 9 categories (plus the deep-import
HolographicCard) - Light/dark theme with 12 public tokens (26 resolved) and full customization
- Apple HIG–compliant touch targets and haptic feedback
- Animated interactions: spring press, sliding tabs, accordion easing, animated progress
- Built with TypeScript — full type declarations included
Installation
# npm
npm install @retray-dev/ui-kit
# pnpm
pnpm add @retray-dev/ui-kitPeer dependencies
Install these in your app if not already present:
pnpm add expo-font expo-haptics expo-linear-gradient expo-image react-native-safe-area-context @gorhom/bottom-sheet react-native-reanimated react-native-gesture-handler react-native-worklets @react-native-picker/picker @react-native-community/slider @expo/vector-icons react-native-size-matters react-native-svg react-native-screens sonner-native pressto react-native-ease expo-image-pickerFor Expo projects, run npx expo install instead to get SDK-compatible versions.
pressto is required — it powers the press animations on every interactive component. Omitting it crashes the import. sonner-native is required for Toast.
Required: expo-image is required for Avatar, ImageUpload, ImageViewer, ListItem, and MediaCard image rendering. It replaces React Native's built-in Image with caching, blurhash placeholders, and cross-fade transitions.
Optional: for ImageUpload, add expo-image-picker. For the deep-import HolographicCard, add @shopify/react-native-skia expo-sensors.
Add the Worklets Babel plugin to babel.config.js (required by @gorhom/bottom-sheet):
module.exports = function (api) {
api.cache(true)
return {
presets: ['babel-preset-expo'],
plugins: ['react-native-worklets/plugin'],
}
}Typography
All components use Sohne font family. You must load the fonts at app root before rendering any component.
When you install @retray-dev/ui-kit, a postinstall script automatically copies 28 .otf font files to assets/fonts/sohne/ in your project. Then define SohneFonts locally in your App.tsx:
import { useFonts } from 'expo-font'
// Fonts copied to assets/fonts/sohne/ by @retray-dev/ui-kit postinstall
const SohneFonts = {
'Sohne-ExtraLight': require('./assets/fonts/sohne/Sohne-ExtraLight.otf'),
'Sohne-ExtraLightItalic': require('./assets/fonts/sohne/Sohne-ExtraLightItalic.otf'),
'Sohne-Light': require('./assets/fonts/sohne/Sohne-Light.otf'),
'Sohne-LightItalic': require('./assets/fonts/sohne/Sohne-LightItalic.otf'),
'Sohne-Regular': require('./assets/fonts/sohne/Sohne-Regular.otf'),
'Sohne-Italic': require('./assets/fonts/sohne/Sohne-Italic.otf'),
'Sohne-Medium': require('./assets/fonts/sohne/Sohne-Medium.otf'),
'Sohne-MediumItalic': require('./assets/fonts/sohne/Sohne-MediumItalic.otf'),
'Sohne-SemiBold': require('./assets/fonts/sohne/Sohne-SemiBold.otf'),
'Sohne-SemiBoldItalic': require('./assets/fonts/sohne/Sohne-SemiBoldItalic.otf'),
'Sohne-Bold': require('./assets/fonts/sohne/Sohne-Bold.otf'),
'Sohne-BoldItalic': require('./assets/fonts/sohne/Sohne-BoldItalic.otf'),
'Sohne-ExtraBold': require('./assets/fonts/sohne/Sohne-ExtraBold.otf'),
'Sohne-ExtraBoldItalic': require('./assets/fonts/sohne/Sohne-ExtraBoldItalic.otf'),
'SohneMono-ExtraLight': require('./assets/fonts/sohne/SohneMono-ExtraLight.otf'),
'SohneMono-ExtraLightItalic': require('./assets/fonts/sohne/SohneMono-ExtraLightItalic.otf'),
'SohneMono-Light': require('./assets/fonts/sohne/SohneMono-Light.otf'),
'SohneMono-LightItalic': require('./assets/fonts/sohne/SohneMono-LightItalic.otf'),
'SohneMono-Regular': require('./assets/fonts/sohne/SohneMono-Regular.otf'),
'SohneMono-Italic': require('./assets/fonts/sohne/SohneMono-Italic.otf'),
'SohneMono-Medium': require('./assets/fonts/sohne/SohneMono-Medium.otf'),
'SohneMono-MediumItalic': require('./assets/fonts/sohne/SohneMono-MediumItalic.otf'),
'SohneMono-SemiBold': require('./assets/fonts/sohne/SohneMono-SemiBold.otf'),
'SohneMono-SemiBoldItalic': require('./assets/fonts/sohne/SohneMono-SemiBoldItalic.otf'),
'SohneMono-Bold': require('./assets/fonts/sohne/SohneMono-Bold.otf'),
'SohneMono-BoldItalic': require('./assets/fonts/sohne/SohneMono-BoldItalic.otf'),
'SohneMono-ExtraBold': require('./assets/fonts/sohne/SohneMono-ExtraBold.otf'),
'SohneMono-ExtraBoldItalic': require('./assets/fonts/sohne/SohneMono-ExtraBoldItalic.otf'),
}
export default function App() {
const [fontsLoaded] = useFonts(SohneFonts)
if (!fontsLoaded) return null
return (
// ... providers and app
)
}.gitignore recommendation:
# Sohne fonts — copied by @retray-dev/ui-kit postinstall
assets/fonts/sohne/Setup
Wrap your app root with all required providers. Order matters.
import { SafeAreaProvider, initialWindowMetrics } from 'react-native-safe-area-context'
import { GestureHandlerRootView } from 'react-native-gesture-handler'
import { ThemeProvider, BottomSheetModalProvider, ToastProvider } from '@retray-dev/ui-kit'
export default function App() {
return (
<SafeAreaProvider initialMetrics={initialWindowMetrics}>
<GestureHandlerRootView style={{ flex: 1 }}>
<ThemeProvider colorScheme="system">
<BottomSheetModalProvider>
<ToastProvider>{/* your app */}</ToastProvider>
</BottomSheetModalProvider>
</ThemeProvider>
</GestureHandlerRootView>
</SafeAreaProvider>
)
}| Provider | Required by |
| -------------------------- | ------------------------------------------ |
| SafeAreaProvider | ToastProvider (uses useSafeAreaInsets) |
| GestureHandlerRootView | Sheet, ConfirmDialog (@gorhom/bottom-sheet) |
| ThemeProvider | All components |
| BottomSheetModalProvider | Sheet, ConfirmDialog |
| ToastProvider | useToast hook |
Theme
Customize any color token per scheme:
const myTheme = {
light: { primary: '#6366f1', primaryForeground: '#ffffff' },
dark: { primary: '#818cf8', primaryForeground: '#ffffff' },
}
<ThemeProvider theme={myTheme} colorScheme="system">Access theme colors anywhere inside the tree:
import { useTheme } from '@retray-dev/ui-kit'
const { colors, colorScheme } = useTheme()Public tokens (12 — override these): background, foreground, card, primary, primaryForeground, border, destructive, destructiveForeground, success, successForeground, warning, warningForeground. Optional: overlay, accent, accentForeground.
Derived tokens (read-only via useTheme()): foregroundSubtle, foregroundMuted, surface, surfaceStrong, skeleton, destructiveTint, destructiveBorder, successTint, successBorder, warningTint, warningBorder, ring, input, separator, overlay, accentResolved, accentForegroundResolved
Design Tokens
Static structural constants — no provider required:
import { SPACING, ICON_SIZES, RADIUS, SHADOWS, BREAKPOINTS, TYPOGRAPHY } from '@retray-dev/ui-kit'
<View style={{ gap: SPACING.md, padding: SPACING.base, borderRadius: RADIUS.md, ...SHADOWS.sm }} />| Token | Keys |
|-------|------|
| SPACING | xxs (2), xs (4), sm (8), md (12), base (16), lg (24), xl (32), xxl (48), section (64) |
| ICON_SIZES | sm (14), md (18), lg (22), xl (28), 2xl (32) |
| RADIUS | none (0), xs (4), sm (8), md (14), lg (20), xl (32), full (9999) |
| SHADOWS | sm, md, lg, xl — cross-platform shadow presets |
| BREAKPOINTS | wide (700) |
| TYPOGRAPHY | 16 variants: display-hero, display-xl, display-lg, display-md, display-sm, title-md, title-sm, body-md, body-sm, caption, caption-sm, badge-text, micro-label, uppercase-tag, button-lg, button-sm |
Color Utilities
withAlpha(hex: string, alpha: number)— takes a hex color string (e.g.,"#6366f1") and an opacity value (0–1), returns anrgba()string. Useful for semi-transparent overlays without adding a separate token.hexToRgb(hex: string)— returns{ r, g, b } | null. Converts hex color to RGB components.
Components
| Category | Components |
| ----------- | ----------------------------------------------------------------------------------------------- |
| Display | Text, Badge, Avatar, Separator, Spinner, Skeleton, Progress, CurrencyDisplay, Stats |
| Surfaces | Card, AlertBanner, EmptyState, MediaCard, PricingCard |
| Form | Form (+ Form.Field / Form.Section / Form.Footer), Button, ButtonGroup, IconButton, Input, CurrencyInput, Textarea, Checkbox, Switch, Toggle, RadioGroup, Select, Slider, SelectableGrid, SelectableCard (+ SelectableCardGroup), SheetSelect, ImageUpload, IconPicker, NumberStepper |
| Composition | Tabs, Accordion |
| Navigation | AppHeader, TabBar, PagerDots |
| Overlays | Sheet, ConfirmDialog, ImageViewer |
| Feedback | Toast / ToastProvider / useToast |
| Data | ListItem, ListGroup (+ .Header / .Footer), MenuItem, MenuGroup (+ .Header / .Footer), Chip / ChipGroup, LabelValue, MonthPicker, CategoryStrip, DetailRow |
| Utilities | Pressable, Icon, RetrayProvider, ErrorBoundary |
Deep-import only: HolographicCard — import { HolographicCard } from '@retray-dev/ui-kit/HolographicCard'.
Quick examples
import {
Button, Input, Badge, Card, CardHeader, CardTitle, CardContent,
Toast, useToast, Sheet, Select, Tabs, TabsContent,
} from '@retray-dev/ui-kit'
// Button
<Button label="Save" variant="primary" size="md" onPress={() => {}} />
// Toast
const { toast } = useToast()
toast({ title: 'Saved', variant: 'success' })
// Sheet (bottom sheet — auto-sizes to content)
<Sheet open={open} onClose={() => setOpen(false)} title="Options">
<Text>Sheet content</Text>
</Sheet>
// Select
<Select
value={value}
onValueChange={setValue}
options={[{ label: 'Option A', value: 'a' }, { label: 'Option B', value: 'b' }]}
placeholder="Pick one"
/>
// Color utilities
import { useTheme, withAlpha, hexToRgb } from '@retray-dev/ui-kit'
const { colors } = useTheme()
;<View style={{ backgroundColor: withAlpha(colors.primary, 0.15) }} />
// hexToRgb — converts hex to RGB components
const rgb = hexToRgb('#6366f1') // { r: 99, g: 102, b: 241 }Full props reference and more examples are available in COMPONENTS.md, which is also shipped inside the npm package for use with AI tools:
## UI Components
@./node_modules/@retray-dev/ui-kit/COMPONENTS.mdLicense
MIT © Julian Camilo Cruz Sanchez
