Skip to content


Web design is a study in accidental complexity. For example, while there’s no inherent aesthetic value in putting text on top of images, it’s a mainstay. Instead this kind of layering creates complexity. It constrains the text, the image, and sets up a duel for the user’s attention. Two dimensions ought to be enough.

Few habits complicate web design quite like undue layers. There are enough maligned modals to declare position: fixed forever broken — not technically, but in practice. We’ve permanently lost viewport height to a storm of sticky headers. And each layer burdens both design and code. Poor z-index.

Luckily, we have superior alternatives to every layer. Place text before or after images. Move modal content inline or extract it to a new page. Let headers remain on top. Instead of adding a loading overlay, make your site so fast that it can do without. And if the end result looks empty, redesign.

The web works best for designers, developers, and users alike when it is constrained to a single plane. We can shift the time spent on layering content to be used on improving the content itself. There is no need to compete with your own interface. Draw attention once per pixel, and keep form flat.

—   ¶


There’s a new notification about enabling notifications. The email client tells of three unread spam messages. The settings app has exactly one new system update. Twitter’s tab title counts five new tweets, as always. The home screen is littered with red badges. There’s no value in a predictable notification.

Unread counts are everywhere in modern interface design. They are tasked with retention and growth, often implemented without restraint, and typically end up ignored by most users. But just as in computer science, when it comes to what’s unread, there are only three amounts that count: zero, one, and some.

Most unread counts should stay at zero. This is the kind of feature that works best as opt-in. Better to err on the side of not-annoying. Even if important, many prefer to check counts themselves, at set intervals. And instead of counting to one, show a dot. But not a dangerous red dot. Make it blue, or green.

If there are some notifications, show a more prominent dot or even an ellipsis. There’s probably no actionable difference between 4 or 8 or 16 unread items. However, if there are always some notifications, just remove the indicator. Nobody needs another ignored attention grabber. Leave the user alone.

—   ¶


Software is a multiplier — it can empower users but it can’t induce behavior. Behind every successful app stands a pre-existing motivation. In other words, while you can lead users to your landing page, you can’t make them pay. Great software makes people better at things they already want to do.

However, on the road to great software, finding an idea that serves an existing motivation is just the first step. We may know which market to fit, but how do we product? How should each feature be designed to best help the user?

An early decision is whether or not the interface should mandate a workflow. This is the difference between a wizard and a spreadsheet, or a chatbot and a text editor. Wizards constrain users: first do this, then do that. It’s the same with chatbots: answer this question and I’ll ask you the next. On the other hand, spreadsheets mandate nothing: it’s a blank canvas — an empty slate.

If you know the domain better than the user, for example if the software is an interface to your organisation, go for a wizardly approach. Be a guide, provide hints, and correct mistakes along the way — all in as few steps as possible.

On the other hand, if your idea covers a process the user knows well, be a tool. Provide the features they need, monitor usage, iterate and improve. Aim for a flat set of orthogonal but complimentary features. Provide the tools as best you can, then get out of the way. Your users already know what they want to accomplish, because technology trails incentive.

—   ¶


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.

—   ¶


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.

—   ¶