import MaskedView from '@react-native-masked-view/masked-view'; import { LinearGradient } from 'expo-linear-gradient'; import React, { useCallback, useState } from 'react'; import type { AccessibilityProps, LayoutChangeEvent } from 'react-native'; import { View } from 'react-native'; import { useTheme } from '../../../theme'; interface GradientTextProps extends AccessibilityProps { children: React.ReactNode; } const GradientText = ({ children, ...accessibilityProps }: GradientTextProps) => { const theme = useTheme(); const gradient = theme.colors.gradients.aiDiagonal; const [size, setSize] = useState<{ width: number; height: number } | null>( null ); const onLayout = useCallback((event: LayoutChangeEvent) => { const { width, height } = event.nativeEvent.layout; setSize((prevSize) => { if (prevSize && prevSize.width === width && prevSize.height === height) { return prevSize; } return { width, height }; }); }, []); return ( {/* * MaskedView is entirely hidden from the accessibility tree. * It is purely visual: the mask defines the text clip shape and * the content shows either a gradient or a visible fallback. * Screen readers must not traverse into either branch here. */} {children} } > {size ? ( ) : ( // Render children as fallback until layout is measured, so text // is visible immediately rather than blank on the first frame. children )} {/* * Visually hidden but accessible: the single Text node that * screen readers announce. Kept outside MaskedView so it is * always present and never duplicated. */} {children} ); }; export default GradientText;