Dealing with responsiveness in mobile applications using REM

Article cover

When we work in the front-end area, it is immediately noticeable how responsiveness is something essential for us devs, but in the mobile world these implementations of responsive layouts are also extremely important and must be taken into account, thinking mainly about the range of possibilities of device sizes that we have today, both width and height, that is, we have devices with small width and large hight, or large width and large hight, tablets...

In addition to the wide variety of width and height measurements that vary greatly, there is also the pixel density that varies from device to device.

With that in mind, the purpose of this article is to introduce you to the REM measure and the power it has to help us with responsiveness, as well as a lib that I recently started using in React Native, which allows me to use REM and other relative measures in development of mobile applications.

Understanding the true size of a Pixel

The pixel, contrary to what many devs think, does not have an absolute measure, as it can assume different sizes, due to being directly related to the number of pixels per inch or ppi (pixel per inch). What happens is that the pixel value is influenced by the number of pixels that the device's resolution supports, that is, the higher the pixel density a device has, the more pixels per inch fit on its screen.

This concept started with the launch of the iPhone 4 through the retina screen, where Apple managed to make 4 pixels fit in a space where only 1 pixel would fit before, because the retina screen, in addition to being sharper, doubled the amount of pixels per inch displayed on the devices screen.

image

With this we realize that the pixel we declare in our styles (px) is not a pixel of the device screen (hardware), but a reference pixel (generally larger than the actual pixel).

Knowing the difference between absolute measurements and relative measurements

Absolute measurement units are those that do not depend on reference values, that is, if you define an element using the measure pt or pc for example, this element will have this value in all mobile or web versions where the application is being displayed, regardless of the screen size, and in the case of mobile phones, this absolute value will be multiplied by the value of the pixel density that the device offers, that is, a simple declaration of font-size: 16px, can result in a major responsiveness issue on other devices.

Relative measurement units are measurements that are based on a base value (or reference value), unlike absolute measurements, these are adapted to the context in which they are inserted.

We have many examples of relative measures such as EM, percentage %, REM, among others, but the focus of this article is to show a measure that helps me a lot when I need to be responsive on the screens I deliver, that is , I need the layout of the design to be faithful to what was presented and, in addition, to be able to adapt to as many devices as possible.

Knowing the relative measure REM

REM (whose initial letter "r" comes from "root": "root in"), that is, its size is based on the font attached to the root element.

Unlike EM, it has no nesting in the size values, and its base value is what is used in subsequent measurements.

Its base commonly has a value of 1rem equivalent to 16px (pixels), so to create a table of values in REM just use the value in pixel/16 (which is the value of 1rem):

1px = 1/16 = 0.0625rem


4px = 0.25rem
8px = 0.5rem
16px = 1rem (base REM value)
24px = 1.5rem
32px = 2rem

You might be wondering, okay, I understand the value of REM but I still don't understand how it will help me with code construction and responsiveness.

As explained above, mobile devices today have many formats, which change from the height and width of the screen, to the number of pixels they support, which ends up directly impacting their layout, because the greater the pixel density a device has, the more pixels per inch fit on your screen.

To exemplify this diversity of pixel density, I quote below the measures mentioned in the documentation of Pixel Ratio do ReactNative:

  • PixelRatio.get() === 1

    Android devices mdpi

  • PixelRatio.get() === 1.5

    android hdpi devices

  • PixelRatio.get() === 2

    iPhone SE, 6S, 7, 8 iPhone XR iPhone 11 Android xhdpi devices

  • PixelRatio.get() === 3

    iPhone 6S Plus, 7 Plus, 8 Plus iPhone X, XS, XS Max iPhone 11 Pro, 11 Pro Max Pixel, Pixel 2 Android devices xxhdpi

  • PixelRatio.get() === 3.5

    Nexus 6 Pixel XL, Pixel 2 XL Android devices xxxhdpi

How does this impact your code?

image

As shown in the image above, I'm emulating the code on 2 Android devices, but they have different pixel densities.

So if I declare the last image in my styles file using pixels, it will behave differently on devices that have different densities, as the pixel measurements will be multiplied by the ppi (pixel per inch).

That is, if I declare the image (width and height) cat with a size of 70px, this value will automatically be multiplied by the number of pixels that this device has, and in the case of the example above it would be:

70px x 3.5(left device aspect ratio) = 245px 70px x 2.75(right aspect ratio) = 192.5px

But the device on the right is showing that the pixel value is 193px, which also leads to another problem, the rounding, which can make us waste some time correcting the breakdowns in responsiveness, due to small rounding performed.

According to the Pixel Ratio documentation, which is a class that accesses the native density of devices, we have some ways of knowing these values, using, for example, the functions below:

PixelRatio.get( ) - the get() method provides access to the pixel density and font scale of the device.

PixelRatio.getPixelSizeForLayoutSize() - the getPixelSizeForLayoutSize() method converts a layout size (dp) to a pixel size (px).

Example of use in code, taken from the Pixel Ratio documentation that originated the screens above:

import React from 'react'
import { View, Text, Image, SafeAreaView, PixelRatio } from 'react-native'

const size = 70
const cat = {
  uri: 'https://reactnative.dev/docs/assets/p_cat1.png',
  width: size,
  height: size,
}

const ExampleREM: React.FC = () => {
  return (
    <SafeAreaView style={styles.safeAreaContainer}>
      <View style={styles.container}>
        <Text style={styles.text}>Current pixel aspect ratio is:</Text>
        <Text style={styles.value}>{PixelRatio.get()} </Text>
      </View>
      <View style={styles.container}>
        <Text style={styles.text}>
          Requires an image of a width in pixels of:
        </Text>
        <Text style={styles.value}>
          {PixelRatio.getPixelSizeForLayoutSize(size)} px
        </Text>

        {/* Transforming image size into REM */}
        <Image source={cat} style={styles.cat} />
      </View>
    </SafeAreaView>
  )
}

export default ExampleREM

As you can see, the images have different sizes, and we wouldn't want an app icon to have different sizes on different devices, we want it to be the default, that is, as real and similar as possible on "all devices".

Therefore, I used the REM measure as an alternative, as it is a relative measure that depends on a base measure. This base measure does not take into account the proportion of the devices, that is, the value transformed into pixels will not be multiplied by the density offered by the screen.

Linked to this, it is not possible until the date I am writing this post to insert 'rem' measurements using the StyleSheet offered by the 'react-native' lib, and to work around this situation, I recently started using the lib react-native-extended-stylesheet.

I'm still in the testing phase, but I basically use it to use REM and percentage, which help me a lot with responsiveness.

Code example using lib react-native-extended-stylesheet and REM together:

import React from 'react'
import { View, Text, Image, SafeAreaView, PixelRatio } from 'react-native'
import EStyleSheet from 'react-native-extended-stylesheet'

const size = 70
const cat = {
  uri: 'https://reactnative.dev/docs/assets/p_cat1.png',
  width: size,
  height: size,
}

const ExampleREM: React.FC = () => {
  return (
    <SafeAreaView style={styles.safeAreaContainer}>
      <View style={styles.container}>
        <Text style={styles.text}>Current pixel aspect ratio is:</Text>
        <Text style={styles.value}>{PixelRatio.get()} </Text>
      </View>

      <View style={styles.container}>
        <Text style={styles.text}>On this device, an image of width:</Text>
        <Text style={styles.value}>{size} px</Text>

        <Image source={cat} />
      </View>

      <View style={styles.container}>
        <Text style={styles.text}>
          Requires an image of a width in pixels of:
        </Text>
        <Text style={styles.value}>
          {PixelRatio.getPixelSizeForLayoutSize(size)} px
        </Text>

        {/* Transforming image size into REM */}
        <Image source={cat} style={styles.cat} />
      </View>
    </SafeAreaView>
  )
}

const styles = EStyleSheet.create({
  safeAreaContainer: {
    flex: 1,
    marginTop: '1rem',
  },

  container: {
    alignItems: 'center',
  },

  text: {
    fontSize: '1.2rem',
  },

  value: {
    fontSize: '1.5rem',
    marginBottom: '0.75rem',
    marginTop: '0.25rem',
  },

  // Creating a relative measurement for the image
  cat: {
    width: '8rem',
    height: '8rem',
  },
})

export default ExampleREM

Result of the above code:

image

Using REM, it is possible to see on the screen above that the images were practically the same size, unlike when we used the value in pixels.

I know the subject was extensive, but if you made it this far, I imagine you liked this way of declaring measurements, as this use on mobile with React Native is new to me too, but I have already noticed great gains, both in terms of usability and of productivity!

This was one more tip to help you become a dev that cares even more about the usability and responsiveness of your applications!

And do you already use this measure? If so, what is your favorite lib at the moment to use REM?

Finally, I'll leave you with some articles, and the Pixel Ratio documentation, in case you want to go deeper into this subject:

Articles to delve into:

A Pixel Identity Crisis

Guide to CSS units

Pixel Ratio documentation - React Native

Compatibility with different pixel densities - Android

See you in the next post! 👋🏻