Back to gallery

Design engineering: a number flow like input

An animated number input component, inspired by the Family crypto wallet and Number Flow. Try typing a number below, when you’ll have some numbers typed in, select a single number and replace it with another number to see a barrel wheel effect like the one popularized by Number Flow.

$
Type in a value above

Features

Animated digits

  • Numbers are animated as they get inserted, we animate them up and animate their width at the same time so the input smoothly resizes as the user types.
  • Selecting and replacing a digit with another digit creates a barrel wheel effect, the direction of the wheel depends on whether the new number is larger or smaller than the old one.
  • Replacing a digit that is already in a barrel wheel animation smoothly interrupts and restart the barrel wheel so that it animates to the latest digit.
  • Accepted characters

    • Only digits, one decimal point, and one minus sign are allowed.
    • Uses a numeric keyboard on mobile devices.
    • The minus sign can only be placed at the beginning of the input.
    • When inserting a decimal point, the component can automatically add the leading 0 if the autoAddLeadingZero prop is set to true.
    • The component accepts an optional maxLength prop.

Implementation

The implementation of this component uses contentEditable in order to be able to animate the digits as they are typed. Relying on contentEditable allows to use markup, contrary to a regular input, but this comes with quite a few caveats.

React can’t help with content editable

Since the browser will insert content as the user types, the children of the content editable have to be managed outside of React. We are responsible for DOM manipulation, which can be a little tedious.

Preventing content editable rich text shortcuts

Content editable is designed to allow rich text formatting, and arbitrary markup to be inserted, for our input we need to prevent this. This is done via event.preventDefault(), simple yet effective.

Custom cursor handling

In rich text, cursor positions move between nodes, this means that when you insert a span the cursor position can be inside, or outside of the span. This means that there more than on logical cursor position mapping to the same visual position. Which means the user would have to press the arrow keys twice to move the cursor to the next digit. This is definitely not ideal.

We need to handle cursor positioning ourselves. This also means we need to implement things such as alt+arrow or cmd+arrow in order to move the cursor to the beginning or end of the input (depending on which arrow the user pressed).

Because of this, we also need to implement a custom selection, too.

Custom history (undo/redo)

Since we manipulate the content ourselves and manipulate the DOM directly, we can’t rely on the native history for the content editable, we have to implement history on our own. The implementation here is pretty naive, but it works and restore cursor position as the user undoes/redoes.

Custom clipboard

Since we have rich text markup in our input, and we want to copy a raw number to the clipboard, we also need to handle copy operations ourselves in order to clean the markup.

That’s a wrap

I wanted an input that animates in a similar way to the Number Flowcomponent, and the Family crypto wallet inputs. Number Flow is great, but it doesn’t really have an input mode. What they do in their examples is that they basically overlay the number flow component on top of an input, but I wanted more animation options than that. In the end, using content editable is quite tricky, because you have to re-implement many things that are a given with a regular input if you want it to feel right and provide a decent UX.

<--

Right before

Swipeable cards carousel

Up next

A split-flap display component
-->