Build your Responsive website without media query
Tags: html, css, webdev, beginners
Published: 2021-05-05 12:04:48

We cannot talk about web development without talking about Responsive Design. The latter is now a must and everyone will use Media Queries to build a responsive website.

Since the introduction of media queries (before 2000), CSS has evolved, and now (in 2021) there are a lot of tricks that can help you drastically reduce the usage of media queries and create an optimized code. I will even show you how to replace multiple media queries with only one CSS declaration. 

<sup>**PS:** you have to run all the examples outside DEV to better see the results since the embedded version is very small</sup>

---

I will start with the trivial examples that are widely used but still limited:


### `flex` & `flex-wrap`


**Demo**: https://codepen.io/t_afif/pen/zYNggoq 


`flex: 400px` will set a base width equal to `400px`. The items will then wrap if there isn't enough space for the `400px`. They will grow to fill the empty spaces and will shrink if the container width is bigger than `400px`.

✔️ Easy to use, only 2 lines of code are required
❌ We cannot control when the items will wrap
❌ We cannot control the number of items per row
❌ The items in the last row will have a different width

----

### `auto-fit` & `minmax`


**Demo**: https://codepen.io/t_afif/pen/wvgVVPN 


Similar to the previous example, the `repeat(auto-fit,minmax(400px,1fr))` will define the base width and we will have a similar wrapping behavior.

✔️ Easy to use, only 1 line of code is required
✔️ The items in the last row will keep the same width
❌ We cannot control when the items will wrap
❌ We cannot control the number of items per row
❌ We don't have the shrink effect of the flexbox so we may face overflow

---

We will try to optimize the above examples with some CSS tricks to overcome the drawbacks.

---


### Controlling the number of items

In our first example, let's change `flex: 400px` to `flex: max(400px, (100% - 20px)/3)`. Resize the screen and you will notice that each row will not have more than 3 items (even for a large screen width). 


**Demo**: https://codepen.io/t_afif/pen/abpeeeV 


The logic is easy. When the screen width increase, `100%/3` will be bigger than `400px` so it's the max value that will get used. We cannot have more than 3 items per row if all of them have a width equal to `100%/3`.

#### What the hell is the `20px`??

It's twice the gap we defined. For 3 items we will have 2 gaps so for N items we should use `max(400px, (100% - (N - 1)*gap)/N)`. 

We can still optimize the formula to remove the gap and use `max(400px, 100%/(N + 1) + 0.1%)`. We tell the browser that each item will be equal to `100%/(N + 1)` so `N + 1` items per row but we add a tiny percentage (the `0.1%`) thus one of the items will wrap and we end with only `N` items per row! 


**Demo**: https://codepen.io/t_afif/pen/wvJwzbL 


✔️ Now we can control the maximum number of items per row.

The same can also be applied to the CSS grid example: 


**Demo**: https://codepen.io/t_afif/pen/BaWBLge 


I have added CSS variables to easily control the different values.

----

### Controlling the shrink effect

Using CSS grid we may have an overflow if the base width is bigger than the container width unlike with Flexbox where we have the `flex-shrink`.

To overcome this we change `max(400px, 100%/(N + 1) + 0.1%)` to `clamp(100%/(N + 1) + 0.1%, 400px, 100%)`. 

* For a large screen width, the `100%/(N + 1) + 0.1%` will be bigger than `400px` and we will get our maximum number of items. 
* For a small screen width, the `100%` will be smaller than `400px` and our items will not exceed the container width.


**Demo**: https://codepen.io/t_afif/pen/ZEezBGL 


✔️ We have our shrink effect and no more overflow

---

### Controlling the wrap

In all the previous examples, we have no control over the wrap. We don't know when it will happen. It depends on the base width, the gap, the container width, etc 

To control this we will change our base width (the `400px`) with `(400px - 100vw)*1000` to get the following

```
clamp(100%/(N + 1) + 0.1%, (400px - 100vw)*1000, 100%)
```

It looks a bit strange but is easy to understand. The `100vw` is our screen width and logically this value will change on screen resize while the `400px` will remain fixed. This will lead us to the following logic:

* When `screen width (100vw) > 400px` the difference will be **negative** so it will get clamped to the `100%/(N + 1) + 0.1%` which is a positive value: We have N items per row

* When `screen width (100vw) < 400px` the difference will be **positive**, we multiply with a big value (the `1000`) so it will get clamped to the `100%`: We have 1 item per row


**Demo**: https://codepen.io/t_afif/pen/BaWBQqK 


We did our first media query!

We were able to move **from N columns to 1 column** without using `@media` and with only one CSS declaration. Our base width has become a breakpoint.

✔️ We can control when the items will wrap
✔️ We can control the number of items per row

---

### What about moving from N columns to M columns?

We simply update our `clamp()` function like below:

```
clamp(100%/(N + 1) + 0.1%, (400px - 100vw)*1000, 100%/(M + 1) + 0.1%)
```

I think everyone got the trick now. When the screen width is bigger than `400px` we fall into the first rule (N items per row). When the screen width is smaller than `400px` we fall into the second one (M items per row).


**Demo**: https://codepen.io/t_afif/pen/ZEezBgo 


We can easily control the number of items per row and we can decide when to change that number. All this using only one CSS declaration!

----

### What about moving from N columns to M columns to 1 column?

We can do this by nesting `clamp()` functions like the below:

```
clamp(clamp(100%/(N + 1) + 0.1%, (W1 - 100vw)*1000,100%/(M + 1) + 0.1%), (W2 - 100vw)*1000, 100%)
```

We have two breakpoints so we will logically need two widths (`W1` and `W2`). 

We can see our function like:

```
clamp(clamp( .. ), (W2 - 100vw)*1000, 100%)
```

* When the screen width is smaller than `W2` we fall into `100%`: one item per row
* When the screen width is bigger than `W2` we fall into the first `clamp()`: We do the logic there
  * when the screen width is smaller than `W1` we fall into `100%/(M + 1) + 0.1%)`: M items per row
  * when the screen width is bigger than `W1` we fall into `100%/(N + 1) + 0.1%)`: N items per row


Let's see this in play:

**Demo**: https://codepen.io/t_afif/pen/xxqKgZe 


We did 2 media queries using only one CSS declaration! Not only this, but we can easily adjust that declaration using CSS variables which means that we can update the logic for different containers easily


**Demo**: https://codepen.io/t_afif/pen/mdWbRRE


How many media queries until now? well, I stopped the count ...


Do you want more breakpoints? You simply nest another `clamp()` function and you have

### From N columns to M columns to P columns to 1 column

**Demo**: https://codepen.io/t_afif/pen/bGqbgYY 


We have our responsive design without any single media queries

✔️ Only one line of code
✔️ Easy to update using CSS variables
✔️ We can control the number of items per row
✔️ We can control when the items will wrap
✔️ We don't have an overflow on small screens
✔️ All the items have the same width
✔️ Each container can have its breakpoints

---

### Container Queries

Everyone is excited to use [this new feature](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Container_Queries) that considers the width of the element instead of the screen to create media queries but no need to wait for it. 

The trick I made already covers this feature. We simply change `100vw` with `100%` and all the logic we made previously will now consider the container width instead of the screen width.

Resize the below containers and see the magic in the play


**Demo**: https://codepen.io/t_afif/pen/gOmYmgz


---

### Bonus

I will end this post with a final trick that allows you to change the coloration of your items without using media queries as well.

```css
div {
  background:
   linear-gradient(purple 0 0) 0 /calc(var(--w3) - 100vw) 1px,
   linear-gradient(blue   0 0) 0 /calc(var(--w2) - 100vw) 1px,
   linear-gradient(green  0 0) 0 /calc(var(--w1) - 100vw) 1px,
   red;
}
```

We consider 3 gradient layers plus a `background-color`. The size of each gradient is defined using one of the breakpoints. If `calc()` is negative then the gradient will not show. If `calc()` is positive then the size will also be positive and thanks to the repeat feature, it will cover all the area.

The order is very important. Below is a table to better understand:

[0 W3[ |  [W3 W2[ | [W2 W1[  | [W1 infinity[
--- | --- | --- | ---
✔️purple | ❌purple | ❌purple | ❌purple
✔️blue   | ✔️blue   | ❌blue   | ❌blue   
✔️green  | ✔️green  | ✔️green  | ❌green  
✔️red| ✔️red| ✔️red| ✔️red


The red color is always shown and at each breakpoint one of the gradients is displayed covering the bottom layer.

Here is a demo with all the features together. Run at full screen and resize:


**Demo**: https://codepen.io/t_afif/pen/wvJwdRW 


To make the coloration work based on the container width, we update the code slightly and use a pseudo-element that we position relatively to the container and we clip the overflow


**Demo**: https://codepen.io/t_afif/pen/zYZOwQJ 

A related Stack Overflow question where I am using such a trick: [How to change the color of `<div>` Element depending on its height or width?](https://stackoverflow.com/q/56669587/8620333). I am also changing the text coloration and the borders based on the width or the height.


---

### That's it!

Now you have a good trick that allows you to control your responsive layout without using media queries and with only a few lines of code. Of course, this is not a replacement for media queries. It's an optimization that can help you reduce the amount of code.

----

### <center>You want to support me?</center>

[![buy me a coffee](https://cdn.buymeacoffee.com/buttons/default-orange.png)](https://www.buymeacoffee.com/afif)

### <center>OR</center>

[![Become a patron](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/db3jtgy2i5l4w8slkps5.png)](https://www.patreon.com/temani)