4 CSS layouts without using media queries
Tags: css, design, html, webdev
Published: 2020-01-29 13:24:39

Do you always need media queries to make a website responsive? With flexbox and grid you can make responsive layouts without having to define fixed breakpoints with media queries. [Adam Argyle](https://twitter.com/argyleink) recently showed us a tweet-sized example of this in action, where just 13 lines of CSS give you 4 separate layouts.

{% twitter 1217213431947747328 %}

How does it work? The HTML is surprisingly simple. No wrappers or extra elements:

```html
<form>
  <input type="text" placeholder="Name" />
  <input type="email" placeholder="Email Address" />
  <input type="submit" value="Subscribe" />
</form>
```

_If you want to use this in production, make sure all of these input fields have associated label elements._

The CSS that does the heavy lifting is very small too:

```scss
form {
  display: flex;
  flex-wrap: wrap;

  & > input {
    flex: 1 1 10ch;
    margin: 0.5rem;

    &[type='email'] {
      flex: 3 1 30ch;
    }
  }
}
```

That's 13 rows, blank rows included!

> _What's that `&` doing there?_
> Adam wrote this using [postcss-preset-env](https://preset-env.cssdb.org/), which is "Babel for CSS". It lets you write CSS using upcoming specs (like nesting) and compiles it to regular CSS.

## So what does this CSS do?

The styling for the form and input elements work in tandem to create the different layouts, so we'll go through their CSS properties one by one. Lets start with the `form` element.

### The form

The form is set to `display:flex` so has a flex layout. By default, that's a horizontal flex layout where all items fit a single row.

By adding `flex-wrap: wrap`, you specify that the flex layout can wrap to multiple rows if the elements don't fit a single row. Each of those rows will behave as their own flex layout, which this code uses to its advantage.

### The inputs

The meat of this technique lies in the `flex` property on the inputs. The `flex` property takes three values (e.g. `1 1 10ch;`) and is shorthand for three CSS properties:

- `flex-grow`
- `flex-shrink`
- `flex-basis`

`flex-grow` applies when there is more space in the surrounding element (the form in this case) than the elements need. It take a positive number (or 0). The number indicates what part of the total an element takes.

> The "part of the total" is best illustrated with an example. Say there are two elements, both having `flex-grow: 1`. They will both be equally wide at 1/2 of the total width. But if the second element has `flex-grow: 2`, the total number of parts is 1 + 2 = 3. The first item will then be 1/3rd of the total width, and the second element will be 2/3rd of the total width.

`flex-shrink` works much in the same way, except that it is used when there is _less_ space than the elements would normally take up. Here, a higher number means it ends up being smaller.

In flexbox terminology, if the elements combined take up more space than available, their `flexibility` is set to `shrink`. Otherwise it is set to `grow`. This is not something you set explicitly, but is useful to know when working with flex layouts.

In our 4-layout example `flex-wrap` is set to `wrap`, which means that the flexibility will never be set to shrink. Instead, elements will wrap to the next row if they don't fit.

_At this point, I have to admit that the calculation example I gave for the flex-grow value was a little simplified. It works that way only if the last property, `flex-basis`, is set to 0. Normally, flex-basis is set to `auto`, which makes things a little more complicated._

When `flex-basis` is set to `auto`, the number set for `flex-grow` and `flex-shrink` also take into account the width of the element, either the implicit width (called the 'content size') or an explicit `width`. So two elements that both have `flex-grow:1` might not end up being equally wide, if one of them has much more content.

You can also set `flex-basis` to a value. In that situation, the initial size of an element is set to that value. In this layout, the "name" field and the button both get a value of `10ch`, and the "email" field is set to `30ch`.

> _What's a `ch`?_ 
> One `ch` is equivalent to the width of the "0" character in a font.

The effect of this is that the name input and the button are initially 10ch wide, and the email field is 30ch wide. Because we are in a flex layout, that `flex-basis` value is used as the starting point for the `flex-grow` or `flex-shrink` value. Think of them as multipliers for the `flex-basis` value.

In this layout the "name" field and the button both have a `flex-grow` of 1, a `flex-shrink` of 1 and a `flex-basis` of `10ch`. The e-mail field has a `flex-grow` of 3, a `flex-shrink` of 1 and a `flex-basis` of 30ch.

**In other words**, the email field starts out 3 times as wide as the other field, _grows_ three times as fast but _shrinks_ at the same rate.

## How does that get us to 4 layouts?

That's where the `flex-wrap` comes into play. When elements do not naturally fit side-by-side, they go to the next row. Let's go layout-by-layout from small to large and see what happens.

### The smallest layout

![Screenshot of first layout](https://dev-to-uploads.s3.amazonaws.com/i/uxiuca5e5wy5hj78kdmk.png)

The three elements are stacked vertically. Here's what happens:

- There is not enough space to have all three elements side-by-side (their total is 50ch wide, 10 + 30 + 10), so elements will wrap to the next row.
- Two of them together (either name and email, or email and button) also don't fit, so the end result is that all of them are on their own row.
- We end up with three rows.
- Each row is it's own smaller flex layout, and each element on a single row is smaller than the total width.
- All of them have a positive `flex-grow` value, and don't share their row with other element, so all of them grow to the full width of their own row.

### The second layout

![Screenshot of second layout](https://dev-to-uploads.s3.amazonaws.com/i/osbw9rac46h1bf8yd8gx.png)


The name field takes the full width, and the email and subscribe button are on row together. Here's what happens:

- There is not enough space to have all three elements side-by-side, so elements will wrap to the next row.
- _Things get sneaky._ Even though the name field and the button have the same flex values, a `button` type element has a slightly different content size, so ends up being slightly smaller than a regular input field. _Browsers, right?_
- Name + email don't fit on a single row so email wraps to the next row. Because of the tiny difference, email + button _do_ fit on a single row.
- We end up with two flex rows.
- The first row has just the name, so that takes full width because of `flex-grow`.
- The second row is wider than both the email field and button combined, so flex-grow is used to make them fill up the width, the email field at a much bigger ratio compared to the button.

If the last element was not a button, this layout wouldn't be possible.

### The third layout

![Screenshot of third layout](https://dev-to-uploads.s3.amazonaws.com/i/t2itiw7tb0uqgv4r2uan.png)


The name field and email field are shown side by side, with the button below it. What happens:

- There is not enough space to have all three elements side-by-side so elements can wrap to the next row.
- The name and email fields fit side by side, so it's just the button that wraps to the next row.
- Name and email fill out the first row in their flex-grow/flex-basis ratio, and the button fills out the second row completely.

### The fourth layout

![Screenshot of fourth layout](https://dev-to-uploads.s3.amazonaws.com/i/hymh9xio1cass59c397k.png)

This is the simplest layout. The total width is wider than all elements, so they're distributed on a single row in their respective ratio.

But what about that button? You'd expect it to be as wide as the name field, since they have the same flex value. But here's where the sneaky thing from the second layout comes into play again: the content size of the button is less wide and so ends up being smaller.

## Responsive websites without media queries?

Recreating this layout with media queries is possible but would take you 3 breakpoints at set widths and only works when the form is the full width of the page.

With this flexbox solution you can place the form element anywhere and it will fit the element it's in, giving you even more flexibility than just media queries would give you.

Using modern layout algorithm like flexbox and grid give you a lot of extra flexibility when it comes to the way your elements are laid out without needing to explicitly define them. As you can see, they let you do some pretty powerful things!

---

Want to develop responsive designs (with or without media queries) with easy? Check out [Polypane](https://polypane.app), the browser/developer tool that I'm building that helps you build high quality websites and apps twice as fast.