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!
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:
<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?
- It doesn't support huge screens.
- It doesn't work well with components.
- 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
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.
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?
- To be independent from the screen size (to be usable in components).
- Grid should contain elements aligned to a virtual grid (grid gaps should align precisely in all directions)
- A child of a grid container could span across several virtual columns (optimally also across rows)
- Columns should have optimal sizes, given in units like
px
orrem
, 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. - 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.
- 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:
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
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:
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:
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:
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 ;)
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.
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
Top comments (15)
OK - now that I can see it working; it looks interesting... but I think it would help to have some comparisons - e.g. with a pure grid and pure flexbox approaches - to get a better idea of the shortcomings you're trying to address. A solution with a hard JS dependency is a hard sell over pure CSS ;)
True, unfortunately there is no way to do it without JS. I tried hard, read tons of articles about it. This article illustrates well some of the issues of pure css Grid solution: css-tricks.com/responsive-grid-mag... .
I am planning another article, more focused on grid usage explanation, real world examples.. and as you well pointed out, it probably also should contain comparison with other current solutions.
Thx for feedback
Thanks Tomas, excellent information.
On Android Firefox I see:
You are right, that browser doesn't support Resize Observer Api caniuse.com/#search=ResizeObserver
Will that work with a resize observer polyfill? I'm using that in a few places - think it works off mutationobserver
I am affraid that even if it works, there is another api (Web Components), which is not supported much better. But I am considering to make kind of framework specific plugins for react, angular, vue, svelte frameworks. It would enable me to skip web component api and use something like this library: github.com/marcj/css-element-queries nstead of the ResizeObserver. It would make it bit more expensive, slightly bigger in size, but with way better support. The functinality should remain the same.
Yeah I was looking at the polyfill for custom elements - not sure if that would work or not. I think this is very important work, but won't work for me unless it has IE11 support "somehow" as I write B2B software and 20%+ of my users are IE11 (don't trust caniuse stats for browsers in that environment...)
I do use ResizeObserver polyfilled and that is fine.
Maybe I try to experiment first with some polyfills. It is always better to maintain one project, than 5 of them, for different frameworks. Will see if some of them fit to my needs. Anyway, I wanted to get some feedback first, before investing time to this project ;)
thanks for feedback btw
Count me in, anything I can do to help let me know.
Currently the biggest help would be to try to use the grid in some real world markup (can be some random webpage template) and bring some feedback based on it. For instance, it could help me with a dilemma — whether the grid needs classes for sticky content (telling an element to be always aligned to the left/right side of the container).. It can be done now with the nesting of grids, but maybe people would appreciate being able to control it without nesting. I did not include that so far, as I wanted to keep it as small as possible for the beginning (sticky thing would need to generate quite lot of css rules based on element queries)
Ok sure, when I get a moment I'll go rework me recently written page to use it, could benefit from ultrawide having columns etc.
Maybe this could benefit from: dev.to/adam_cyclones/javascript-en...
Really interesting work!
It's time to go ultrawide