0
# Hooks
1
2
React Native's utility hooks adapted for web with comprehensive support for window dimensions, color scheme preferences, and locale context management for building responsive, theme-aware applications.
3
4
## useWindowDimensions
5
6
Hook for accessing current window dimensions with automatic updates on resize, providing responsive layout capabilities for web applications.
7
8
```javascript { .api }
9
const useWindowDimensions: () => DisplayMetrics;
10
```
11
12
Returns current window dimensions that update automatically when the window is resized.
13
14
```javascript { .api }
15
interface DisplayMetrics {
16
fontScale: number;
17
height: number;
18
scale: number;
19
width: number;
20
}
21
```
22
23
**Usage:**
24
```javascript
25
import { useWindowDimensions } from "react-native-web";
26
27
function ResponsiveComponent() {
28
const { width, height, scale, fontScale } = useWindowDimensions();
29
30
// Responsive breakpoints
31
const isTablet = width >= 768;
32
const isDesktop = width >= 1024;
33
const isMobile = width < 768;
34
35
// Calculate responsive values
36
const padding = isDesktop ? 32 : isTablet ? 24 : 16;
37
const fontSize = isDesktop ? 18 : isTablet ? 16 : 14;
38
const columns = isDesktop ? 3 : isTablet ? 2 : 1;
39
40
return (
41
<View style={styles.container}>
42
<Text style={styles.info}>
43
Window: {width} × {height} (scale: {scale}, fontScale: {fontScale})
44
</Text>
45
46
<View style={[styles.content, { padding }]}>
47
<Text style={[styles.title, { fontSize }]}>
48
Responsive Design
49
</Text>
50
51
<Text style={styles.deviceType}>
52
Device Type: {isDesktop ? 'Desktop' : isTablet ? 'Tablet' : 'Mobile'}
53
</Text>
54
55
{/* Responsive grid */}
56
<View style={[styles.grid, { flexDirection: columns === 1 ? 'column' : 'row' }]}>
57
{Array.from({ length: 6 }, (_, i) => (
58
<View
59
key={i}
60
style={[
61
styles.gridItem,
62
{
63
width: columns === 1 ? '100%' : columns === 2 ? '50%' : '33.33%'
64
}
65
]}
66
>
67
<Text>Item {i + 1}</Text>
68
</View>
69
))}
70
</View>
71
</View>
72
</View>
73
);
74
}
75
76
// Custom responsive hooks
77
function useResponsiveValue(mobileValue, tabletValue, desktopValue) {
78
const { width } = useWindowDimensions();
79
80
return React.useMemo(() => {
81
if (width >= 1024) return desktopValue;
82
if (width >= 768) return tabletValue;
83
return mobileValue;
84
}, [width, mobileValue, tabletValue, desktopValue]);
85
}
86
87
function useBreakpoints() {
88
const { width, height } = useWindowDimensions();
89
90
return React.useMemo(() => ({
91
isMobile: width < 768,
92
isTablet: width >= 768 && width < 1024,
93
isDesktop: width >= 1024,
94
isLandscape: width > height,
95
isPortrait: height > width,
96
aspectRatio: width / height
97
}), [width, height]);
98
}
99
100
function useMediaQuery(query) {
101
const [matches, setMatches] = React.useState(false);
102
103
React.useEffect(() => {
104
if (typeof window === 'undefined') return;
105
106
const mediaQuery = window.matchMedia(query);
107
setMatches(mediaQuery.matches);
108
109
const handler = (event) => setMatches(event.matches);
110
mediaQuery.addEventListener('change', handler);
111
112
return () => mediaQuery.removeEventListener('change', handler);
113
}, [query]);
114
115
return matches;
116
}
117
118
// Advanced responsive layout component
119
function AdaptiveLayout({ children }) {
120
const { width } = useWindowDimensions();
121
const breakpoints = useBreakpoints();
122
123
// Calculate sidebar width based on screen size
124
const sidebarWidth = useResponsiveValue(0, 250, 300);
125
const showSidebar = breakpoints.isTablet || breakpoints.isDesktop;
126
127
return (
128
<View style={styles.adaptiveContainer}>
129
{showSidebar && (
130
<View style={[styles.sidebar, { width: sidebarWidth }]}>
131
<Text style={styles.sidebarTitle}>Navigation</Text>
132
{/* Navigation items */}
133
</View>
134
)}
135
136
<View style={[
137
styles.mainContent,
138
{ marginLeft: showSidebar ? sidebarWidth : 0 }
139
]}>
140
{children}
141
</View>
142
143
{/* Mobile navigation overlay */}
144
{breakpoints.isMobile && (
145
<TouchableOpacity style={styles.mobileMenuButton}>
146
<Text>☰</Text>
147
</TouchableOpacity>
148
)}
149
</View>
150
);
151
}
152
```
153
154
## useColorScheme
155
156
Hook for detecting and responding to system color scheme preferences with automatic updates when the user changes their theme settings.
157
158
```javascript { .api }
159
const useColorScheme: () => ColorSchemeName;
160
```
161
162
Returns the current color scheme preference from the system.
163
164
```javascript { .api }
165
type ColorSchemeName = 'light' | 'dark' | null;
166
```
167
168
**Usage:**
169
```javascript
170
import { useColorScheme } from "react-native-web";
171
172
function ThemedComponent() {
173
const colorScheme = useColorScheme();
174
const isDark = colorScheme === 'dark';
175
176
// Theme-aware styles
177
const theme = React.useMemo(() => ({
178
backgroundColor: isDark ? '#1a1a1a' : '#ffffff',
179
textColor: isDark ? '#ffffff' : '#000000',
180
borderColor: isDark ? '#333333' : '#e0e0e0',
181
cardBackground: isDark ? '#2d2d2d' : '#f8f9fa',
182
primaryColor: isDark ? '#4a9eff' : '#007AFF',
183
secondaryColor: isDark ? '#ff6b6b' : '#ff3b30'
184
}), [isDark]);
185
186
return (
187
<View style={[styles.container, { backgroundColor: theme.backgroundColor }]}>
188
<Text style={[styles.title, { color: theme.textColor }]}>
189
Current Theme: {colorScheme || 'system default'}
190
</Text>
191
192
<View style={[styles.card, {
193
backgroundColor: theme.cardBackground,
194
borderColor: theme.borderColor
195
}]}>
196
<Text style={[styles.cardText, { color: theme.textColor }]}>
197
This card adapts to your system theme preference
198
</Text>
199
200
<TouchableOpacity
201
style={[styles.button, { backgroundColor: theme.primaryColor }]}
202
>
203
<Text style={styles.buttonText}>Primary Button</Text>
204
</TouchableOpacity>
205
</View>
206
</View>
207
);
208
}
209
210
// Theme provider using color scheme
211
function ThemeProvider({ children }) {
212
const colorScheme = useColorScheme();
213
214
const theme = React.useMemo(() => {
215
const isDark = colorScheme === 'dark';
216
217
return {
218
colors: {
219
// Background colors
220
background: isDark ? '#000000' : '#ffffff',
221
surface: isDark ? '#1c1c1e' : '#f2f2f7',
222
card: isDark ? '#2c2c2e' : '#ffffff',
223
224
// Text colors
225
text: isDark ? '#ffffff' : '#000000',
226
textSecondary: isDark ? '#8e8e93' : '#6d6d70',
227
228
// UI colors
229
primary: isDark ? '#0a84ff' : '#007aff',
230
secondary: isDark ? '#ff453a' : '#ff3b30',
231
success: isDark ? '#32d74b' : '#34c759',
232
warning: isDark ? '#ff9f0a' : '#ff9500',
233
error: isDark ? '#ff453a' : '#ff3b30',
234
235
// Border colors
236
border: isDark ? '#38383a' : '#c6c6c8',
237
separator: isDark ? '#38383a' : '#c6c6c8'
238
},
239
spacing: {
240
xs: 4,
241
sm: 8,
242
md: 16,
243
lg: 24,
244
xl: 32
245
},
246
borderRadius: {
247
sm: 4,
248
md: 8,
249
lg: 12,
250
xl: 16
251
},
252
isDark,
253
colorScheme
254
};
255
}, [colorScheme]);
256
257
return (
258
<ThemeContext.Provider value={theme}>
259
{children}
260
</ThemeContext.Provider>
261
);
262
}
263
264
const ThemeContext = React.createContext(null);
265
266
function useTheme() {
267
const theme = React.useContext(ThemeContext);
268
if (!theme) {
269
throw new Error('useTheme must be used within a ThemeProvider');
270
}
271
return theme;
272
}
273
274
// Animated theme transitions
275
function AnimatedThemeComponent() {
276
const colorScheme = useColorScheme();
277
const [animatedValues] = React.useState({
278
backgroundColor: new Animated.Value(0),
279
textColor: new Animated.Value(0)
280
});
281
282
React.useEffect(() => {
283
const isDark = colorScheme === 'dark';
284
const targetValue = isDark ? 1 : 0;
285
286
Animated.parallel([
287
Animated.timing(animatedValues.backgroundColor, {
288
toValue: targetValue,
289
duration: 300,
290
useNativeDriver: false
291
}),
292
Animated.timing(animatedValues.textColor, {
293
toValue: targetValue,
294
duration: 300,
295
useNativeDriver: false
296
})
297
]).start();
298
}, [colorScheme]);
299
300
const animatedBackgroundColor = animatedValues.backgroundColor.interpolate({
301
inputRange: [0, 1],
302
outputRange: ['#ffffff', '#1a1a1a']
303
});
304
305
const animatedTextColor = animatedValues.textColor.interpolate({
306
inputRange: [0, 1],
307
outputRange: ['#000000', '#ffffff']
308
});
309
310
return (
311
<Animated.View style={[
312
styles.animatedContainer,
313
{ backgroundColor: animatedBackgroundColor }
314
]}>
315
<Animated.Text style={[
316
styles.animatedText,
317
{ color: animatedTextColor }
318
]}>
319
Smoothly animated theme transitions
320
</Animated.Text>
321
</Animated.View>
322
);
323
}
324
325
// Custom theme detection
326
function useSystemTheme() {
327
const [systemTheme, setSystemTheme] = React.useState('light');
328
329
React.useEffect(() => {
330
if (typeof window === 'undefined') return;
331
332
const darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)');
333
334
const updateTheme = (e) => {
335
setSystemTheme(e.matches ? 'dark' : 'light');
336
};
337
338
// Set initial value
339
updateTheme(darkModeQuery);
340
341
// Listen for changes
342
darkModeQuery.addEventListener('change', updateTheme);
343
344
return () => darkModeQuery.removeEventListener('change', updateTheme);
345
}, []);
346
347
return systemTheme;
348
}
349
```
350
351
## useLocaleContext
352
353
Hook for accessing locale information including language preference and text direction for internationalization support.
354
355
```javascript { .api }
356
const useLocaleContext: () => LocaleValue;
357
```
358
359
Returns the current locale context with direction and language information.
360
361
```javascript { .api }
362
interface LocaleValue {
363
direction: 'ltr' | 'rtl';
364
locale: string | null;
365
}
366
```
367
368
**Usage:**
369
```javascript
370
import { useLocaleContext } from "react-native-web";
371
372
function LocalizedComponent() {
373
const { direction, locale } = useLocaleContext();
374
const isRTL = direction === 'rtl';
375
376
return (
377
<View style={[styles.container, isRTL && styles.rtlContainer]}>
378
<Text style={[styles.title, isRTL && styles.rtlText]}>
379
Current Locale: {locale || 'Default'}
380
</Text>
381
382
<Text style={[styles.direction, isRTL && styles.rtlText]}>
383
Text Direction: {direction.toUpperCase()}
384
</Text>
385
386
{/* RTL-aware layout */}
387
<View style={[styles.row, isRTL && styles.rtlRow]}>
388
<View style={styles.startItem}>
389
<Text>Start Item</Text>
390
</View>
391
<View style={styles.centerItem}>
392
<Text>Center Item</Text>
393
</View>
394
<View style={styles.endItem}>
395
<Text>End Item</Text>
396
</View>
397
</View>
398
399
{/* RTL-aware text alignment */}
400
<Text style={[
401
styles.paragraph,
402
{ textAlign: isRTL ? 'right' : 'left' }
403
]}>
404
This paragraph text aligns according to the current text direction.
405
In RTL locales, it will be right-aligned, and in LTR locales, it will be left-aligned.
406
</Text>
407
</View>
408
);
409
}
410
411
// Locale provider usage
412
function App() {
413
const [currentLocale, setCurrentLocale] = React.useState('en-US');
414
415
return (
416
<LocaleProvider
417
locale={currentLocale}
418
direction={getLocaleDirection(currentLocale)}
419
>
420
<View style={styles.app}>
421
<LocaleSelector
422
currentLocale={currentLocale}
423
onLocaleChange={setCurrentLocale}
424
/>
425
<LocalizedComponent />
426
</View>
427
</LocaleProvider>
428
);
429
}
430
431
function LocaleSelector({ currentLocale, onLocaleChange }) {
432
const locales = [
433
{ code: 'en-US', name: 'English (US)', direction: 'ltr' },
434
{ code: 'ar-SA', name: 'العربية', direction: 'rtl' },
435
{ code: 'he-IL', name: 'עברית', direction: 'rtl' },
436
{ code: 'es-ES', name: 'Español', direction: 'ltr' },
437
{ code: 'fr-FR', name: 'Français', direction: 'ltr' },
438
{ code: 'ja-JP', name: '日本語', direction: 'ltr' }
439
];
440
441
return (
442
<View style={styles.localeSelector}>
443
<Text style={styles.selectorTitle}>Select Language:</Text>
444
445
{locales.map(({ code, name, direction }) => (
446
<TouchableOpacity
447
key={code}
448
style={[
449
styles.localeOption,
450
currentLocale === code && styles.selectedLocale
451
]}
452
onPress={() => onLocaleChange(code)}
453
>
454
<Text style={[
455
styles.localeName,
456
{ textAlign: direction === 'rtl' ? 'right' : 'left' }
457
]}>
458
{name}
459
</Text>
460
<Text style={styles.localeCode}>
461
{code} ({direction.toUpperCase()})
462
</Text>
463
</TouchableOpacity>
464
))}
465
</View>
466
);
467
}
468
469
// RTL-aware styling utilities
470
function useRTLStyles() {
471
const { direction } = useLocaleContext();
472
const isRTL = direction === 'rtl';
473
474
return React.useCallback((styles) => {
475
if (!isRTL) return styles;
476
477
const rtlStyles = { ...styles };
478
479
// Flip horizontal margins
480
if (styles.marginLeft !== undefined) {
481
rtlStyles.marginRight = styles.marginLeft;
482
rtlStyles.marginLeft = styles.marginRight || 0;
483
}
484
485
// Flip horizontal padding
486
if (styles.paddingLeft !== undefined) {
487
rtlStyles.paddingRight = styles.paddingLeft;
488
rtlStyles.paddingLeft = styles.paddingRight || 0;
489
}
490
491
// Flip text alignment
492
if (styles.textAlign === 'left') {
493
rtlStyles.textAlign = 'right';
494
} else if (styles.textAlign === 'right') {
495
rtlStyles.textAlign = 'left';
496
}
497
498
// Flip flex direction
499
if (styles.flexDirection === 'row') {
500
rtlStyles.flexDirection = 'row-reverse';
501
} else if (styles.flexDirection === 'row-reverse') {
502
rtlStyles.flexDirection = 'row';
503
}
504
505
return rtlStyles;
506
}, [isRTL]);
507
}
508
509
// Locale-aware formatting utilities
510
function useLocaleFormatting() {
511
const { locale } = useLocaleContext();
512
513
return React.useMemo(() => ({
514
formatNumber: (number, options = {}) => {
515
try {
516
return new Intl.NumberFormat(locale, options).format(number);
517
} catch {
518
return number.toString();
519
}
520
},
521
522
formatCurrency: (amount, currency = 'USD') => {
523
try {
524
return new Intl.NumberFormat(locale, {
525
style: 'currency',
526
currency
527
}).format(amount);
528
} catch {
529
return `${currency} ${amount}`;
530
}
531
},
532
533
formatDate: (date, options = {}) => {
534
try {
535
return new Intl.DateTimeFormat(locale, options).format(date);
536
} catch {
537
return date.toLocaleDateString();
538
}
539
},
540
541
formatRelativeTime: (value, unit = 'day') => {
542
try {
543
return new Intl.RelativeTimeFormat(locale).format(value, unit);
544
} catch {
545
return `${value} ${unit}${Math.abs(value) !== 1 ? 's' : ''} ago`;
546
}
547
}
548
}), [locale]);
549
}
550
551
function FormattingDemo() {
552
const formatting = useLocaleFormatting();
553
const { locale } = useLocaleContext();
554
555
const sampleData = {
556
number: 1234567.89,
557
currency: 299.99,
558
date: new Date(),
559
relativeTime: -3
560
};
561
562
return (
563
<View style={styles.formattingDemo}>
564
<Text style={styles.demoTitle}>
565
Locale Formatting ({locale})
566
</Text>
567
568
<View style={styles.formatExample}>
569
<Text style={styles.label}>Number:</Text>
570
<Text style={styles.value}>
571
{formatting.formatNumber(sampleData.number)}
572
</Text>
573
</View>
574
575
<View style={styles.formatExample}>
576
<Text style={styles.label}>Currency:</Text>
577
<Text style={styles.value}>
578
{formatting.formatCurrency(sampleData.currency)}
579
</Text>
580
</View>
581
582
<View style={styles.formatExample}>
583
<Text style={styles.label}>Date:</Text>
584
<Text style={styles.value}>
585
{formatting.formatDate(sampleData.date, {
586
weekday: 'long',
587
year: 'numeric',
588
month: 'long',
589
day: 'numeric'
590
})}
591
</Text>
592
</View>
593
594
<View style={styles.formatExample}>
595
<Text style={styles.label}>Relative Time:</Text>
596
<Text style={styles.value}>
597
{formatting.formatRelativeTime(sampleData.relativeTime)}
598
</Text>
599
</View>
600
</View>
601
);
602
}
603
```
604
605
## Combined Usage Patterns
606
607
Leverage multiple hooks together for comprehensive responsive and accessible applications.
608
609
```javascript
610
function ResponsiveThemedApp() {
611
const dimensions = useWindowDimensions();
612
const colorScheme = useColorScheme();
613
const { direction, locale } = useLocaleContext();
614
615
// Combined responsive and theme-aware styling
616
const styles = React.useMemo(() => {
617
const isTablet = dimensions.width >= 768;
618
const isDesktop = dimensions.width >= 1024;
619
const isDark = colorScheme === 'dark';
620
const isRTL = direction === 'rtl';
621
622
return StyleSheet.create({
623
container: {
624
flex: 1,
625
backgroundColor: isDark ? '#000' : '#fff',
626
padding: isDesktop ? 32 : isTablet ? 24 : 16,
627
flexDirection: isRTL ? 'row-reverse' : 'row'
628
},
629
630
sidebar: {
631
width: isDesktop ? 300 : isTablet ? 250 : 0,
632
backgroundColor: isDark ? '#1a1a1a' : '#f8f9fa',
633
display: isTablet ? 'flex' : 'none'
634
},
635
636
content: {
637
flex: 1,
638
marginLeft: !isRTL && isTablet ? 16 : 0,
639
marginRight: isRTL && isTablet ? 16 : 0
640
},
641
642
title: {
643
fontSize: isDesktop ? 28 : isTablet ? 24 : 20,
644
color: isDark ? '#fff' : '#000',
645
textAlign: isRTL ? 'right' : 'left',
646
fontFamily: locale?.startsWith('ar') ? 'Noto Sans Arabic' : 'system'
647
}
648
});
649
}, [dimensions, colorScheme, direction, locale]);
650
651
return (
652
<View style={styles.container}>
653
<View style={styles.sidebar}>
654
<Text>Navigation Sidebar</Text>
655
</View>
656
657
<View style={styles.content}>
658
<Text style={styles.title}>
659
Responsive, Themed, and Localized Content
660
</Text>
661
662
<View>
663
<Text>Screen: {dimensions.width} × {dimensions.height}</Text>
664
<Text>Theme: {colorScheme}</Text>
665
<Text>Direction: {direction}</Text>
666
<Text>Locale: {locale}</Text>
667
</View>
668
</View>
669
</View>
670
);
671
}
672
673
// Hook composition for common patterns
674
function useAppPreferences() {
675
const dimensions = useWindowDimensions();
676
const colorScheme = useColorScheme();
677
const locale = useLocaleContext();
678
679
return React.useMemo(() => ({
680
// Device info
681
...dimensions,
682
isMobile: dimensions.width < 768,
683
isTablet: dimensions.width >= 768 && dimensions.width < 1024,
684
isDesktop: dimensions.width >= 1024,
685
686
// Theme info
687
colorScheme,
688
isDark: colorScheme === 'dark',
689
isLight: colorScheme === 'light',
690
691
// Locale info
692
...locale,
693
isRTL: locale.direction === 'rtl',
694
isLTR: locale.direction === 'ltr'
695
}), [dimensions, colorScheme, locale]);
696
}
697
```
698
699
## Web-Specific Implementation
700
701
React Native Web's hooks leverage modern web APIs and standards:
702
703
**useWindowDimensions:**
704
- Uses `window.innerWidth` and `window.innerHeight`
705
- Listens to `resize` events for automatic updates
706
- Provides `devicePixelRatio` as the scale value
707
- FontScale defaults to 1 (can be customized based on user preferences)
708
709
**useColorScheme:**
710
- Uses CSS media query `(prefers-color-scheme: dark)`
711
- Automatically updates when system preference changes
712
- Falls back to 'light' if preference is not available
713
- Compatible with all modern browsers
714
715
**useLocaleContext:**
716
- Integrates with `LocaleProvider` context
717
- Can detect RTL languages automatically
718
- Supports browser language detection via `navigator.language`
719
- Provides utilities for locale-aware formatting
720
721
## Types
722
723
```javascript { .api }
724
interface DisplayMetrics {
725
fontScale: number;
726
height: number;
727
scale: number;
728
width: number;
729
}
730
731
type ColorSchemeName = 'light' | 'dark' | null;
732
733
interface LocaleValue {
734
direction: 'ltr' | 'rtl';
735
locale: string | null;
736
}
737
738
type WritingDirection = 'ltr' | 'rtl';
739
740
interface LocaleProviderProps {
741
direction?: WritingDirection;
742
locale?: string;
743
children: React.ReactNode;
744
}
745
746
// Hook return types
747
type UseWindowDimensionsReturn = DisplayMetrics;
748
type UseColorSchemeReturn = ColorSchemeName;
749
type UseLocaleContextReturn = LocaleValue;
750
```
751
752
const styles = StyleSheet.create({
753
container: {
754
flex: 1,
755
padding: 20
756
},
757
info: {
758
fontSize: 14,
759
marginBottom: 16,
760
color: '#666'
761
},
762
content: {
763
flex: 1
764
},
765
title: {
766
fontWeight: 'bold',
767
marginBottom: 16
768
},
769
deviceType: {
770
fontSize: 16,
771
marginBottom: 20,
772
fontWeight: '600'
773
},
774
grid: {
775
flexWrap: 'wrap'
776
},
777
gridItem: {
778
padding: 16,
779
backgroundColor: '#f0f0f0',
780
margin: 4,
781
borderRadius: 8,
782
alignItems: 'center'
783
},
784
785
// Adaptive layout styles
786
adaptiveContainer: {
787
flex: 1,
788
flexDirection: 'row'
789
},
790
sidebar: {
791
backgroundColor: '#f8f9fa',
792
padding: 20,
793
borderRightWidth: 1,
794
borderRightColor: '#e0e0e0'
795
},
796
sidebarTitle: {
797
fontSize: 18,
798
fontWeight: 'bold',
799
marginBottom: 16
800
},
801
mainContent: {
802
flex: 1,
803
padding: 20
804
},
805
mobileMenuButton: {
806
position: 'absolute',
807
top: 20,
808
left: 20,
809
width: 44,
810
height: 44,
811
backgroundColor: '#007AFF',
812
borderRadius: 22,
813
justifyContent: 'center',
814
alignItems: 'center'
815
},
816
817
// Theme styles
818
card: {
819
padding: 20,
820
borderRadius: 12,
821
borderWidth: 1,
822
marginVertical: 16
823
},
824
cardText: {
825
fontSize: 16,
826
marginBottom: 16
827
},
828
button: {
829
paddingVertical: 12,
830
paddingHorizontal: 24,
831
borderRadius: 8,
832
alignItems: 'center'
833
},
834
buttonText: {
835
color: 'white',
836
fontWeight: '600'
837
},
838
839
// Animated theme styles
840
animatedContainer: {
841
padding: 20,
842
borderRadius: 12,
843
margin: 20
844
},
845
animatedText: {
846
fontSize: 16,
847
textAlign: 'center'
848
},
849
850
// Locale styles
851
rtlContainer: {
852
flexDirection: 'row-reverse'
853
},
854
rtlText: {
855
textAlign: 'right'
856
},
857
rtlRow: {
858
flexDirection: 'row-reverse'
859
},
860
row: {
861
flexDirection: 'row',
862
marginVertical: 16
863
},
864
startItem: {
865
flex: 1,
866
backgroundColor: '#e3f2fd',
867
padding: 16,
868
marginHorizontal: 4,
869
borderRadius: 8,
870
alignItems: 'center'
871
},
872
centerItem: {
873
flex: 1,
874
backgroundColor: '#f3e5f5',
875
padding: 16,
876
marginHorizontal: 4,
877
borderRadius: 8,
878
alignItems: 'center'
879
},
880
endItem: {
881
flex: 1,
882
backgroundColor: '#e8f5e8',
883
padding: 16,
884
marginHorizontal: 4,
885
borderRadius: 8,
886
alignItems: 'center'
887
},
888
paragraph: {
889
fontSize: 16,
890
lineHeight: 24,
891
marginVertical: 16
892
},
893
894
// Locale selector styles
895
localeSelector: {
896
marginBottom: 24
897
},
898
selectorTitle: {
899
fontSize: 18,
900
fontWeight: 'bold',
901
marginBottom: 12
902
},
903
localeOption: {
904
padding: 12,
905
borderWidth: 1,
906
borderColor: '#e0e0e0',
907
borderRadius: 8,
908
marginVertical: 4
909
},
910
selectedLocale: {
911
backgroundColor: '#e3f2fd',
912
borderColor: '#2196f3'
913
},
914
localeName: {
915
fontSize: 16,
916
fontWeight: '600',
917
marginBottom: 4
918
},
919
localeCode: {
920
fontSize: 12,
921
color: '#666'
922
},
923
924
// Formatting demo styles
925
formattingDemo: {
926
padding: 16,
927
backgroundColor: '#f8f9fa',
928
borderRadius: 8,
929
marginTop: 16
930
},
931
demoTitle: {
932
fontSize: 18,
933
fontWeight: 'bold',
934
marginBottom: 16
935
},
936
formatExample: {
937
flexDirection: 'row',
938
justifyContent: 'space-between',
939
marginVertical: 8,
940
paddingVertical: 8,
941
borderBottomWidth: 1,
942
borderBottomColor: '#e0e0e0'
943
},
944
label: {
945
fontWeight: '600',
946
color: '#333'
947
},
948
value: {
949
color: '#666'
950
}
951
});