1. Actively design constraints
Don’t design with static placeholder content. As a designer, it’s your responsibility to demonstrate the constraints that a layout should adapt to. If you show placeholder content that’s the same width for every title, and the same number of lines for every content block, that doesn’t help a front-end developer translate your intent into code.
Ask yourself: What constrains this content? What happens when it’s long, or when it’s short? Web content isn’t static — ragged, uneven content is the reality of working with a dynamic environment like the web.
2. Implement constraints with respect for content
Some layout constructs are based on grids, and there are fixed points at which the layout should change from beneath the content. This is the framework many developers think with when working with layout. However, we can’t rely on media queries and fixed breakpoints to make a layout responsive and resilient. Let’s define those terms for this purpose of this article:
- Responsive: Capable of being displayed within arbitrary viewport sizes.
- Resilient: Capable of holding arbitrary dynamic content without breaking down and showing cracks in the layout system.
When we look at these definitions, it’s pretty clear that drawing fixed lines at which the layout changes won’t address resilience to arbitrary content. To handle that, the constraints of a layout often need to be based on how much content is present — and how much can fit within a row.
The fundamental limitation of CSS layout is it doesn’t react to the size of content within a container. And the size of content is almost always dynamic. Whether dealing with dynamic content populated by a CMS, by user input in an interactive web app, or when dealing with localization of text to another language, content can change size. That means layouts can’t respond to the size of the content unless we carefully express constraints around the content such that the content is pushed and wraps into the appropriate place when it doesn’t fit where it would within a larger container.
Media queries allow us to change a layout based on the size of the viewport. In the near future, we may also be able to use them with respect to parent containers. But that doesn’t help us with content. Here’s the beautiful thing: If we manage to build content-first layouts, container agnosticism comes naturally. It’s like getting container queries for free.
It turns out negative margins on a parent, and equal but opposite positive margins on the children within are the answer. In this example, we have a row of items with the following styles:
.row.row--split.row > *
First itemSecond itemThird item
If there’s enough space for everything, we get the following:
Note that the container will visually fill whatever its container is — those negative margins and positive margins result in aligning the inner items to the edges of whatever contains the container. Our first item is split to the left, and our second item to the right. The second item is a row of its own, and contains two items.
If the content width exceeds the available width, the content will wrap as necessary:
The margin between the items on the left and the items on the right means the text within each set of items will never collide — they wrap as soon there’s not enough space for the content and the margin.
When I build layouts like this, I find they elegantly express the flow of content, handle dynamic content resiliently, and respond cleanly within different viewports and container sizes.