React Native text input styled using just Tailwind - scaling up for accessibility

ยท

3 min read

Creating nice styled text inputs in React Native is now easier than ever thanks to Tailwind, meaning we don't need to turn to large UI component libraries like Material any more. However, when ditching the libraries we must now consider all the accessibility features ourselves. Remembering to label everything is the bare minimum, what about if a user has their phone's font scaled up? Can our styles cope with large fonts?

I wanted to accurately create these inputs designed by Pixelz Studio

I knew I wanted to use Tailwind for the project, but these inputs were so similar to the Material UI's inputs that there was a temptation to use Material instead. Using Material brings a lot of UI elements with good customisation options. However, at some point there will always be something you have to fight with to customise. It is also not quite s good with react native as it is on the web in my opinion. I decided to give it a go with just Tailwind, and see if I could create something close to the designs that would be reusable and pass accessibility checks - including scaling up with the phone's font size setting

Here is a screenshot of my version, and you can test the finished example on Snack

Aim

Create text inputs with the styling of the inputs using just Tailwind. Features to include:

  • Label in the border on focus

  • Border and label should change colour with validation

  • Input should be easily reusable

  • Icons on left and right sides of input

  • Must pass accessibility checks:

    • Label read by screen readers

    • Screen readers should know what type of input it is

    • Should be able to scale font size via the phone's settings without affecting styling

Styling the label

The design includes a label which appears in the top border of the input once the input is focused. I wanted to attempt this feature using Tailwind. My first attempt used a hard-coded value for the translate-Y to move the text into the top of the input. This looked ok at normal font size, but of course, once the accessibility font scaling was turned up, the translation was no longer correct and it was not in position any more. A more dynamic solution was needed if this was to work with different font sizes.

Adding an `onLayout' function to store the height of the input allows us to translate the label's position dynamically:

  const measureInput = (e: LayoutChangeEvent) => {
    const height = e.nativeEvent.layout.height;
    if (inputHeight !== height) {
      setInputHeight(e.nativeEvent.layout.height); // Stored in state
    }
  };

  const translateValue = inputHeight / 2;

  return (
    <>
      {focused ? (
        <View
          style={[
            tw`bg-white p-1 ml-3 z-1 absolute`,
            {transform: [{translateY: -translateValue}]},
          ]}
        > ...

```

To test with the accessibility font scaling on IPhone, go to Settings->Accessibility->display & text sizes:

This is how it looks with the font scaled up now:

Limitations

The validation here is very basic - far too basic for any real-world scenario. React Hook Forms can provide good validation, but custom validation would probably still be needed. I'm not very happy with how the validation errors are displayed - I think having a message under each field is not the best practice for accessibility. If a user has multiple fields that are invalid and is using a screen reader, they have to go through the form to see the errors one by one. A better approach would be to have one alert which lists the errors and provides a link to each error. Unfortunately, this does not appear in many designs for mobile that I have seen, and is perhaps an area we need to improve by working with designers to consider this more.

Resources

ย