Search code examples
reactjsreact-nativeexpofocusreact-native-web

React Native Web/Expo: How to emulate focus-visible on a Pressable?


I'm trying to manually recreate the focus-visible behaviour on a Pressable component in React Native Web. This artificial behaviour will be such that when you click the Pressable component, it will have one set of styles, but if you focus it with tab on the keyboard, it will have a different set of styles.

Thankfully Pressable on react-native-web has pressed, hovered, and focused variants built in. But the issue is, whenever Pressable is pressed, focused remains true thereafter.

  1. Is there someway to make focused not true after being pressed?
  2. Alternatively, is there some other trick to artificually recreate the focus-visible behaviour?
<Pressable 
  style={({pressed, hovered, focused}) => [
      {
        backgroundColor: pressed ? "orange" : hovered ? "green" : focused ? "red" : "lightgray", 
        padding: 20 
      }
]}>
    {
      ({pressed, hovered, focused}) => (
        <Text>
          {pressed ? "Pressed" : hovered ? "Hovered" : focused ? "Focused" : "Default"}
        </Text>
      )
    }
</Pressable>

Here is the Expo Snack. Not sure why hover doesn't work on this snack, but upon clicking, it stays red (focused). Any way to make it back to gray after clicking?


Solution

  • Figured it out.

    Based on what you want, you need to use a combination of outline styles and box shadow.

    return (
    <Pressable style={(state) => [styles.default, state.focused && styles.focusOutline]}>
      <Text style={{fontWeight: "bold"}}>
        Outline styles only
      </Text>
      <Text>Tab: Colored offset outline</Text>
      <Text>Press: n/a</Text>
    </Pressable>
    )
    
    // styles
    const styles = StyleSheet.create({
      default: {
        paddingHorizontal: 15,
        paddingVertical: 10,
        backgroundColor: "powderblue",
        alignItems: "center",
        borderRadius: 20,
      },
      focusOutline: {
        outlineStyle: "solid",
        outlineWidth: 4,
        outlineColor: "skyblue", 
        outlineOffset: 2,
      }
    });
    

    Expo Snack