Last fall, FiftyThree introduced Mix, a new way for users to collaborate on sketches and ideas in Paper. Though it’s only been a few months since launch, the Mix feature set has grown by leaps and bounds. One of the most requested features was the ability to search for friends. Unsurprisingly, Mix users wanted to find and collaborate with people they already followed on Tumblr, Twitter and Facebook. Just before the new year we launched creator search in Paper and on Mix.
The biggest challenge we faced while building our web-based search was implementing a fluid text input. A fluid text input has a width that is defined by its content. If you have experience with web programming, you’ll recall that HTML text inputs are not naturally fluid. This post explores how we implemented a text input with no explicit width.
No Detail Too Small
There are quite a few custom user interface features in our iPad app Paper, and text input is no different. When you edit the title of a Paper journal or enter text in the search field, you’ll notice that the type is centered in the UI. Not only are these text inputs visually appealing, they function to balance the on-screen elements.
The FiftyThree design and engineering teams recognize the importance of adhering to a design language that is intuitive, beautiful and, above all, consistent. Thus, keeping the Mix aesthetic and experience in step with all our products, especially Paper, has been a goal from the beginning.
Necessity is the Mother of Invention
As design-focused engineers and engineer-minded designers, we’re no strangers to constraints from both sides. Ultimately, well-defined constraints help guide us toward workable solutions and more quickly discard ill-conceived approaches. We had a general idea of what a fluid text input needed to do, but in order to differentiate between must-haves and nice-to-haves, we created a list to help gauge success:
- Must resize dynamically.
- Must allow for text selection.
- Must use placeholder copy consistently across browsers.
- Must work with pointer and touch events.
- Must behave predictably.
Let’s review each constraint in more detail.
1. Must resize dynamically.
Native HTML text fields must have a defined size. If you don’t provide a size, the browser will render the element at a default width. In the figure below, x represents the width of the text input. x can be defined in one of three ways:
- Percentage width (via CSS)
- Fixed width (via CSS)
- Size attribute (showing x number of characters)
This was the toughest constraint of all because we needed to “break the rules” when it came to the way that browsers rendered text fields. We wanted the native behavior of a standard text input with the flexibility of an inline element.
2. Must allow for text selection.
We understand how text fields work because we have been using search engines for years. So we knew if we were going to tinker with a native HTML text field, the final version would have to work exactly like we expected in regards to text selection. We’d have to be able to insert our cursor anywhere within the typed content, use the keyboard to move it, and use the shift and arrow keys to start/move the selection area.
3. Must use placeholder copy consistently across browsers.
placeholder attribute for text fields has had a rough upbringing. Initially, browser vendors implemented
placeholder behavior as they wished; some vendors didn’t support it at all. In the past few years browsers have been kinder to
placeholders and the behavior has normalized.
Although the interactions for
placeholder attributes have become consistent, we still wanted to ensure that the placeholder copy would behave similarly to those in Paper text inputs. For style consistency and to future-proof our implementation, we decided to create our own custom placeholder text.
4. Must work with pointer and touch events.
As one would probably expect, most Mix users also use Paper, which means that they own or have access to iPads. We know that usage of mobile devices is huge and will continue to grow. Thus, supporting touch screens and mobile devices has been a priority since the birth of Mix. Touch interactions are as important to us as mouse and keyboard interactions.
5. Must behave predictably.
This constraint may seem obvious, but for us it functioned as a catch-all. Predictable behavior included enforcing a single line of text (no line-breaks) and scrolling long text correctly. For example, a native text fields with overflowing text snaps back to the beginning of the string when keyboard/pointer focus leaves the field.
As mentioned previously, we needed our fluid text input to behave like an inline element. This led us to explore a span with a
contentEditable attribute. At first, an inline
contentEditable element seemed like a silver bullet because it dynamically resized based on content length. But once we considered the complications of line breaks and formatted content, the appeal quickly vanished.
contentEditable certainly was intriguing but not very practical.
We took a step back and remembered that native HTML text inputs strip formatting when you paste content. This was our breakthrough moment. We didn’t have to choose a native input over a span — we could use both. If our span mirrored the value of our text input and the text input sanitized the user’s input, we could experience the euphoria of an inline element without the
contentEditable hangover. Eureka!
Building Out a Solution with React
The Mix web app is authored using React. This “mirrored value” solution is not unique to React, but React makes it easy to implement. Here’s the basic idea:
- When a user changes the value in a text
<input>, React triggers the
- The updated value of the
<input>becomes a state of our
- The component is re-rendered based on the state change. The state is passed into
valueis part of the component state, it can also be used elsewhere in the component.
- Mirror the
<input>into the string inside a
<span>resizes fluidly based on content changes in the
In our final solution, the span acts as a spacer and the content inside is styled invisibly using CSS. As more characters are added to the input, the mirrored value inside the span pushes the search icon svg to the left and the clear icon svg to the right.
Adding a Coat of Paint
The above markup is nested inside a fieldset element. We used some crafty CSS to bring it all together.
- Allow the
<span>to flow “naturally” in the container.
- Set the
- Set the
text-align: centerfor the
<input>and set it to
- Set the
<svg>clear icon to
z-index: 1to “float” above the
<input>. This allows the
<svg>clear icon to receive touch and pointer events.
Remember Our Custom Placeholder?
The last piece of the puzzle was adding the “Search creators” placeholder. The process of rolling our own placeholder attribute was fairly straightforward: the value of the
<input> was “Search creators” until a user started typing. But there were a few wrinkles to iron out:
- A user could click anywhere in the field and the insertion cursor would be placed near the closest letter.
- A user could type anywhere within the custom placeholder text creating a situation like “Search creaHELLOtors”.
- A user could move the insertion cursor with the keyboard arrows.
- A user could select the placeholder text with a mouse and keyboard.
Preventing arrow key navigation was accomplished by intercepting (and discarding) the keypress events. To stop all the other unwanted behaviors, we needed to intercept taps/clicks and only allow the insertion cursor to start at the front of the placeholder text.
We implemented a mask using a
<div> and positioned it over the
<input> when the placeholder text was present. When a tap or click is intercepted by the mask, the cursor is placed at the front of the
setSelectionRange(0, 0). The mask is removed from the DOM when the user begins typing so that all normal touch and pointer behaviors are handled by the
And That’s Not All…
If you dig into the the markup and styles on mix.fiftythree.com/search you’ll find the production implementation to be a bit more complex than what we’ve outlined here. For example, we have a request (loading) indicator that replaces the svg clear icon when waiting for an outstanding API request. This indicator and the clear icon share a container and that markup is not represented in the diagrams and discussion above.
Send Out the Search Party
Just before the holidays, we launched creator search in Paper and on mix.fiftythree.com, enabling user search by first name, last name and email address. From design to usability, each new Mix feature presents its own set of challenges. If you’re someone who’s passionate about building delightful interfaces for touch devices and the web, let us know. We’re always searching for great people.