Rethinking responsive Webdesign
Tags: showdev, css, discuss, javascript
Published: 2020-06-09 21:14:37


### From idea to revolutionary web grid system ;)

-----

## How do we it do nowadays?


To be honest, most of us have never made a responsive web design, we all just make an adaptive webs (we only care about few specific screen sizes) and call it responsive!

![from: [https://4eps1j19gvc31lua5g49gnl7-wpengine.netdna-ssl.com/wp-content/uploads/2016/03/responsive-design.png](https://4eps1j19gvc31lua5g49gnl7-wpengine.netdna-ssl.com/wp-content/uploads/2016/03/responsive-design.png)](https://cdn-images-1.medium.com/max/2048/0*7QlneLKH895pUiv-.png)

Responsive means something more like the above. **But can we achieve it?**

After years of giving a UX lecture at university, showing this picture, and telling students, that this is a true responsive design, I realised, that **we cannot**. There is actually no  grid framework (known to me) fulfilling this assignment. 

As there is no ultimate solution for a responsive web design, most of us stick to something like Bootstrap grid system. Unfortunately, these kinds of grid systems are far from being responsive. And work badly with current FE tech stack.

*From now on, I'll often mention Bootstrap grid system. By doing so, I'll refer to a group of grid systems (Bootstrap, Foundation, Tailwind CSS and similar), not solely to Bootstrap.*



## What's wrong with Bootstrap-like grids?



Best to start with an example. Just to illustrate that there are more grid systems similar to bootstrap, lets check a Tailwind's grid:

```html
    <div class="flex flex-wrap">
      <div class="w-full sm:w-1/2 md:w-1/3 lg:w-1/4 xl:w-1/6 mb-4 bg-gray-500"></div>
      <div class="w-full sm:w-1/2 md:w-1/3 lg:w-1/4 xl:w-1/6 mb-4 bg-gray-400"></div>
      <div class="w-full sm:w-1/2 md:w-1/3 lg:w-1/4 xl:w-1/6 mb-4 bg-gray-500"></div>
      <div class="w-full sm:w-1/2 md:w-1/3 lg:w-1/4 xl:w-1/6 mb-4 bg-gray-400"></div>
      <div class="w-full sm:w-1/2 md:w-1/3 lg:w-1/2 xl:w-1/6 mb-4 bg-gray-500"></div>
      <div class="w-full sm:w-1/2 md:w-1/3 lg:w-1/2 xl:w-1/6 mb-4 bg-gray-400"></div>
    </div>
```

###What can a developer complain about the code above?

* It is a lot of code.
* It is hard to read. 
* It is hard to reason about. 

What a developer wants, is to get rid of this `w-full sm:w-1/2 md:w-1/3 lg:w-1/4 xl:w-1/6 mb-4 bg-gray-400` in favor of something like this: `col-2`


###What can a UX designer complain about the grid?

 1. It doesn't support huge screens.
 2. It doesn't work well with components.
 3. It lets you declare behaviour for certain conditions, instead of letting you declare rules for that behaviour.

Let's investigate the 3 points above in closer detail.


## 1) Wide screen support 


Just try a random Web of yours on 4k or 8k monitor ;) 

Bootstrap-like grids are adaptive. It means they have some screen size limits; they use breakpoints. The biggest —  xl, mostly stands for something around 1200px. Anything above is indifferent, and you need to deal with its responsiveness on your own. A lot of web pages break or look totally awkward on huge screens.


## 2) Grid vs Components


Bootstrap-like grids are almost useless inside components. Why?

Let say, you have 400px wide component with four 200px wide child elements. Obviously, its content needs to behave differently than in another instance of the same component, which has a width of 800px, right? In first case you prefer 2x2 grid, in other case you prefer 4x1 grid. Unfortunately, as the component code is the same for both instances and screen size is given, you are f..... 

#### Bootstrap example where a column width is defined by percents
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/9l25re067g1gpl0afy3e.png)

As we are already in a component era (React, Angular, Vue, Svelte, Web Components)… bootstrap-like grids seems to be doomed, but there is nothing to replace them, at least nothing generic that could be used across all front end frameworks.

You can still use bootstrap-like grids for page/app layout together with component oriented frameworks. But to be honest, when you see how much CSS code is generated for such grid system and how many classes you really use for a layout, it is better to get rid of it. You can always implement a custom solution.

#### Solution, fixing the Bootstrap example form above, seems to be definition of columns with min and max widths. 
Columns take desired widths and proportinally consume the leftovers.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/snfx6dzlo4po4h3cfyx8.png)


## 3) Grid's behaviour



Saying that bootstrap grid “lets you declare behaviour for certain conditions, instead of letting you declare rules for that behaviour” sounds bit philosophical. Translated to human language: 

Bootstrap grid lets you declare how big certain blocks should be at specific breakpoints (you need to cover all page-width cases with your own overly declarative code, as in the Tailwind example). Mostly, lot of code must be written to reach a simple goal: to make an element similarly big at any screen size. The additional value of bootstrap grid system is just aligning stuff to a grid.
What we really want, is to declare a set of rules and let the grid's content to flow, span, shrink and stretch based on them.

Could we skip all the declarations for all the screen sizes and make the alignment to a grid automatic? Yes we can!

*I know you can have hard time, arguing with your designers/product owners, when switching to a smart 'automatic' grid. You just need to explain to them, that even if they provide you with designs for mobile, table and desktop, there always will be some designs in between, which they don't have under control. Design should be specified on level of guidelines, component libraries and very basic understanding of layout rules, not on few pixel perfect mocks* ;)



## New Grid — design brainstorming



What characteristics should a truly responsive grid system have?

 1. To be **independent from the screen size** (to be usable in components).
 2. Grid should contain elements **aligned to a virtual grid** (grid gaps should align precisely in all directions)
 3. A child of a grid container could span across several virtual columns (optimally also across rows)
 4. Columns should have optimal sizes, given in **units like `px` or `rem`**, **not in percents**. Problem of defining columns only in percents is, that it forces us to define element's behaviour for specific breakpoints -sm, md, lg. Percents represent different real sizes under different conditions. 
 5. Grid should be defined by columns, not vice versa as in Bootstrap. Bootstrap grid has always 12 columns; it is too much for mobiles and too little for UltraHD.
 6. Columns should adjust to container size (if column is wider than container, it should shrink itself to container's width).



## New grid — choosing a technology



What technology to use? Available technologies seems to be Flex-box and CSS Grid. They both seem to fit to most of our requirements, but not to all of them. Let's see what these approaches miss:

### Flex-box

In case we want to avoid definition of columns in percents of parent element, we would need to define grid elements by something like: 

    .col {min-width:100px; max-width:200px;}

Unfortunately this works only as long as last .col element is aligned to right edge of grid container. If first row has 3 .col elements and the second only has two .cols, then the elements no longer align to an imaginary grid. This behaviour cannot be fixed. That's a no go for Flex-box.

### Grid

**display: grid** performs bit better, we can use: 
```
    grid-template-columns: repeat(auto-fit, minmax($grid-col-width, 1fr));
```
`auto-fit` tells the grid to stretch existing columns, until there is space for a new one.
`minmax()` defines minimal and maximal width of a column. *Min* is the desired column width, *max* (1fr) tells the column to take 1/*number of columns*, in case there are not enough columns, which would fill the container with their minimal widths.

It does exactly the same as the flex-box example above, with the difference that it always fits to a grid, hurray! But it has another flaw. When you tell an element to span across three columns, but only two would fit the container. The spanned element leak outside the container. The only meaningful solution for this problem seems to be the long-wished Element Queries (queries where 'responsive conditions apply to elements on the page instead of the width or height of the browser.'). As they are still in a form of opened proposal, I had to program 'Element Queries' on my own. 

### Element Queries

I first tried to use some kind of polyfill, but those with good support were slow and relatively big (around thousand lines of code). As I was aiming for super small grid framework I had to find another way. Best fit was to use new 'ResizeObserver' and 'customElements' JS APIs. Their support is not optimal (about 70% of world market), but they are fast and exactly fit to what needs to be done. Element Queries for my grid can be done in something like 35 lines of code, which is awesome.

CSS and JS code required is 1KB (gzipped) and cover all the requirements form the brainstorming section. It actually does even more!

I now skip implementation details and show you the results, what my grid can do ;)



## The “Eq Grid”



In order to avoid serving JS and CSS separately, adding event listeners to DOM elements, listening to DOM mutations and so on, I created a 'Custom Element' which you just need to import and init. It generates all the required CSS classes based on supplied parameters. All you need is:


    npm i eq-grid --save


then in you main.js file:

```javascript
import { initEqGrid } from 'eq-grid';
initEqGrid(120, 10, 'px', 10); // ['column width', 'gap width', 'units', 'max-columns-span/collapse']
```

From that time on, you can use `<eq-grid>` element in your html templates, and all the magic is done in background.

It works literally everywhere, in pure JS/HTML, React, Angular, Vue, Svelte and other modern frameworks.

Let's see some examples… Just open following sandboxes in fullscreen mode and try to resize them.

### Grid used for content:
*Useful for something like article teasers, cards, thumbnails, etc*

{% codesandbox eq-grid-content-hq3x7 %}
In the example above, you can see classes `.eq-col-3-5 .eq-col-2-4`, which you can use to declaratively overwrite the automatic behaviour of the grid.


### Nested grids:

{% codesandbox eq-grid-nested-k5izt %}
By nesting, you can avoid some element breaking freely across 'rows'. It can be useful when creating layouts as shown below.

### Grid used for Layouts: 

{% codesandbox eq-grid-layout-0c9w7 %}
You can wonder, why the grid has so wide `.eq-col-1` on the right side. How is that possible? The root grid here has only two `.eq-col-1` columns, all the stuff on left is in a nested grid. Each column has min-width 100px and max 1fr (one part of parent's width). In this case the max value takes the lead. If there are many elements in the root grid, then min(100px) rule is used instead. This applies to the nested grid on the left side.

Remember that content of child grids has no influence on its parents.
 
This example has 3 nested grids, btw. With this nesting technique, you can be in better control of what , when and how should fold or stretch.
 It can be useful for big layouts.

## Eq Grid and rem

There is one more cool feature this grid can offer, when you set it to use `rem` units:

```javascript
initEqGrid(10, 1, 'rem');
```
Anything using `rem` units derives its size from font size of `HTML` element. It give us power to scale columns by media queries on the `HTML` element. If we use **poly fluid sizing** technique, we can linearly scale font up. Below I scale a little up to 1280px. Then I start scaling in same pace as the window grows.

```
html {
  font-size: 14px;
}

@media screen and (min-width: 320px) {
  html {
    font-size: calc(14px + 4 * ((100vw - 320px) / 960));
  }
}

@media screen and (min-width: 1280px) {
  html {
    font-size: calc(18px + 158 * ((100vw - 1280px) / 10000));
  }
}

@media screen and (min-width: 11280px) {
  html {
    font-size: 176px;
  }
}
```

Result of the above, together with eq-grid in `rems` is cool. Once window grows up to width over 1280px, everything (grid, fonts, etc) starts scaling up as if you zoom in. This way, you can see your web nicely on 8k monitor. You can set what the ratio between zooming and adding of new columns will be - simply by adjusting the font-size `18px + 158` and `176px`.

See an example here (you need to click the 'Open Sandbox button', otherwise it wont work). Then zoom out a lot to see how it works ;)

{% codesandbox eq-grid-poly-fluid-l5d0v %}

## Conclusion



I hope, the Eq Grid system is able to cover all common development/UX requirements. You can use it in very simple and automatic way and let the content flow like a water in the first picture. Or you can be more declarative and fine-tune how grid elements fold and shrink, based on the grid size. It is up to your requirements.

* It is truly responsive. 

* It can scale from zero to infinite without compromising UX. 

* It is also first implementation of my own idea, and it definitely can be improved, so... 

I'll appreciate if you leave a comment — what you think about it, what you miss, or what you like/dislike. Any feedback will be appreciated! Maybe we can make usage of grid systems, in era of components, viable again.


You can check eq-grid grid on npm: [eq-grid on npm](https://www.npmjs.com/package/eq-grid).
It is the place where all Eq-grid classes are described - what they do, and how to use them.
You can also check how the [grid is used inside a React app](https://codesandbox.io/s/eq-react-ijuzj)