Skip to content

A web of hardware

Posted January 2018 in #design

When designing for the web it’s easy to forget that cross-browser testing is only half the story. Variations in hardware and operating system can have just as much to say. There’s a panoply of possible platforms — all with their own inputs and outputs, quirks and constraints. Sometimes the only way to handle the “other”-slice of the hardware pie is a healthy dose of technical restraint.

For example, consider the differences between a 27″ desktop monitor with 2560 × 1440 px at 144 Hz, and a 13″ HiDPI laptop screen with 1440 × 900 px at 60 Hz. While both may be amazing displays, neither can handle any design.

When it comes to animations, the desktop monitor has the upper hand. Its high refresh rate and low DPI will render any hardware accelerated animations buttery smooth. On the other hand, a laptop with a high DPI screen and an integrated GPU will often turn non-trivial animations into spinning fans.

When it comes to text, a high DPI laptop running macOS can make any font look good. However, many of these same fonts will not be optimized for the low DPI of the desktop monitor. When combined with how Windows renders fonts the result can look a weight off, a shade off, or even like anti-aliasing is turned off.

When it comes to viewport space, the desktop has plenty. But few sites have good support for a 2560 px wide window. On a 1440 × 900 px laptop you’re left with an effective viewport height of about 800 px after the operating system and browser have taken their toll. There’s little room left for a fixed header.

Critically, you cannot get a feel for these differences in a virtual machine. You need the actual hardware. So, if resources for cross-hardware testing are limited, keep it simple. Constrain keyframes to transform and opacity. Remember that font-family: system-ui is always an option. And when it comes to viewport size, make sure to be responsive in both width and height.

Invention fights intuition

Posted December 2017 in #design

Years ago there was a discussion on how web apps should address their users. Should the link to a user’s profile say Your profile or My profile? On the one hand, if the app is perceived as a tool — an extension of the user’s own faculties — then My profile fits the bill. On the other hand, if the app is seen as an interface to a service, then Your profile makes more sense.

Since the preferred nomenclature will differ between users, the best solution is probably to avoid the issue altogether. Just say Profile and move on down the backlog. Where this model really shines however, is in user experience design.

There will be parts of the experience that should belong to the user (as tools) and other parts that belong to the app (as the interface). Knowing the difference makes it clear when to be traditional and when to be bold. Critically, an interface should never make unexpected changes to otherwise intuitive tools.

Scrolling in a web browser is a good example. We expect it to work consistently across sites. It is a tool intrinsic to the browsing experience. So in cases where the interface changes how scrolling works our intuition goes out the window.

A more interesting example is a browser’s viewport. While it’s fine for an interface to respond to the viewport size, resizing the viewport itself is a tool of the user. And while web apps seldom change the window size they often come close by limiting the usable viewport through modals and fixed elements.

Assertions for Go

Posted November 2017 in #dev

It has long been difficult to write test helpers in Go because you’ve had to keep track of file names and line numbers to exclude helpers from output. This has resulted in some impressive but complicated assertion libraries. Recently, Go 1.9 improved the situation greatly by introducing the Helper method:

The new Helper method, added to both testing.T and testing.B, marks the calling function as a test helper function. When the testing package prints file and line information, it shows the location of the call to a helper function instead of a line in the helper function itself. — The Go Blog

To try this out, I wrote myself a little assertion library. And it’s great! Go can finally have test helpers that tick every box: intuitive signatures, minimal additional complexity, and accurate reporting of file names and line numbers.

package example

import (
	"errors"
	"testing"

	"github.com/olav/wat/is"
)

func TestExample(t *testing.T) {
	is.Equal(t, 1, 1)
	is.Nil(t, nil)
	is.Zero(t, "")
	is.Error(t, errors.New("error"))

	is.Above(t, 1, 3)
	is.Slice(t, []int{})
	is.Element(t, []int{1, 2, 3}, 3)
	is.Substring(t, "example", "ample")
}

See the project page for more details.

The end of frontend

Posted October 2017 in #dev

The ultimate goal of frontend development, as a specialized field that sits between digital design and backend development, should be to disappear — to innovate itself out of existence. And I think we’re getting close.

In time, more frontend tasks will be done by designers, though not in code. Prototypes are reaching a fidelity comparable to the finished product. It won’t be long until design tools learn to export complete components as actual code. Picture a tool between Sketch and Unity — an integrated design environment.

When design tools export trees of connected components, developers won’t have to infer intent from static mockups. We’ll no longer need the lossy translation step called a handover. What’s left is connecting endpoints to components, components to routes, and serving the resulting app to users.

While serving the content far from simple, it’s always the same: compile, optimize, minify, compress, distribute, localize, cache, serve. These are tasks to be solved once, by a generic proxy or an external service. But what about the rest of frontend — the wiring glue between routes, components, and endpoints?

These wires are just another layer of code, another step of the stack. It’s a layer as complex as any other, but not one that warrants its own field of development. Any technique unique to frontend should change to match the rest of the stack. Practices should apply across layers, and layers are better when built together.