For best results, use this on a phone with vibration.

Pin Code with Haptic Feedback

Download

There is nothing quite like seeing an application on your phone that, although it works very well, still seems to miss that extra something you cannot put your finger on. I use a budgeting app to help me manage my finances, which has one of those pin code entry pages. It also looks very similar to their web site so I am guessing it is just a simple webview application.

When accessing my Android phone I need to enter a pin code. This interaction is very nice and feels better somehow compared to the budgeting apps pin code input. Looking into why this is the case, I found it was down to there being no force feedback, no response to the buttons being pressed. So I thought I would have a go at creating my own pin code page that vibrates the phone whenever the user presses one of the pin code buttons. It isn't as simple to do as you would first think, but each issue is a chance to learn something new.

Before we go through all the parts needed to get it working, why don't take a quick look at the final result. It is best to see this on your phone.

How it Works

I have created a simple web component to put everything together all in one place. You can download, change and use it to fit your needs. There are a number of things we need to take into account when making something like this, and below, I will go through some of them.

Vibration

There is a simple web API to handle vibration. You can make the phone vibrate on and off for different amounts of time. This is done by calling the navigator.vibrate function, but before you do, you need to make sure it exists first, because not all devices have vibration hardware.

// If vibrate if available
if (navigator.vibrate) {
  // Make a very small vibration
  navigator.vibrate(3);
}  

This code checks if the vibration API can be used, and if so, it then makes the phone vibrate for 3 milliseconds. The short amount of time is used to make the phone buzz a little. It feels like a real world button being pressed.

I had to think when was the best place to make this happen. Is it when the button is pressed down or when the finger is released? We have a number of events we can plug into, either pointerdown, pointerup or click. We need to look at this in slow motion and see what is happening when you press a button, how it should feel, what GUI changes need to happen, to make the user feel like something has happened. It felt more natural to make the phone vibrate when the button was pressed down, so I added it into the pointerdown event.

Animation

We want to give some visual feedback to the user that a button is being pressed. The normal approach is to change the background color of the button. However, on a phone, your finger is covering the button so you may not see the color change.

We could do a number of different things to indicate a button press. The one used by the Android login pin code screen is to change the button's space from a circle to a square with rounded corners. This is done using some CSS to transition between the two button shapes.

button {
  border: none;
  border-radius: 50%;
  background-color: #e2e2e2;
  color: #303030;
  transition:
    color 0.1s linear,
    background-color 0.1s linear,
    border-radius 0.1s ease-in-out;
}

button.active {
  border-radius: 2cqmin;
  color: white;
  background-color: #484ace;
}

When a button is pressed it is given the class active which as you can see changes the button's border radius, color and background color. These are transitioned (animated) between the two states. The end result is that when the button is pressed, the shape and colors change, giving the user a visual indication something is happening.

Screen Size

We want our login pin code page to fill the whole page, but screen sizes differ and the orientation could be portrait or landscape. We need to adjust the button sizes and their locations to keep the overall look. The pin code area contains a grid of 3x4 buttons and the inputted symbols at the top. This can fit into a 2 by 3 rectangle. All we need to do then, is make this 2 by 3 rectangle grow to fix the available screen space.

:host {
  position: absolute;
  inset: 0;
  aspect-ratio: 2 / 3;
  max-height: 100%;
  max-width: 100%;
  margin: auto;
  container-type: size;
}  

This is all that is needed to make the outer 2 by 3 rectangle fit the whole available parent area. We can then just fill in the inner pin code buttons without being concerned about the buttons looking out of place.

We are also using the “container-type: size;” CSS property, which allows us to set the width and height of the buttons, and the font size, to be dynamically set related to the overall size of the web component. The larger the screen, the larger the button and the text within them. We use the CSS measurement cqmin to help set the sizes of the inner parts.

Drag Down to Refresh

On a phone you can press and drag the whole page to make it refresh. This is not what we want to happen. Pressing a button in a way that the phone thinks is dragging can stop the button from activating. It may seem like you pressed a button but nothing happens.

To stop this from happening you need to add the CSS “touch-action: none;” property. You may also need to add this to the whole page too, maybe to the body element.

Language Direction

You may have never come across this before, but not all languages are written in the same way. In English the text is written from left to right. Also, all grid items are placed in the same order, starting on the left and then added along the right. But in other languages this is done in the opposite order, from right to left. If we did nothing, the top row of our pin code numbers would not be listed as 1, 2, 3, but as 3, 2, 1 instead.

We want the layout of the numbers to remain the same no matter what language the user is using. This can be done using the CSS “direction: ltr;” property.

Keyboard

Another important part to keep in mind is that not all users will be using the pin code page on a phone and not all of them can see very well. Therefore we need to allow a user to use the keyboard to enter the pin code.

You can tab through each of the buttons and interact with them by pressing the Space or Enter keys. You can also just type in the numeric keys on the keyboard and they will be shown on the page.

Other Stuff You Could Add

There are some other things you can do to improve the pin code page.

  • Dark mode
  • Better contrast coloring
  • Handle reduced motion preference
  • Handle invalid pin code
  • Add delete animation