or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

accessibility.mdanimation.mdcore-utilities.mdform-controls.mdhooks.mdindex.mdinteractive-components.mdlayout-components.mdlist-components.mdmedia-components.mdplatform-apis.mdstylesheet.mdsystem-integration.mdtext-input.md

hooks.mddocs/

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

});