DEV Community

Lola for Samsung Internet

Posted on

From Bootstrap to Grid

Contents

  1. The Bootstrap Way
  2. The Problem
  3. CSS-Grid
  4. display: flex
  5. Combining CSS-Grid with Flexbox
  6. Conclusion
  7. Further Watching

This post isn't going to be a CSS-grid tutorial, there are loads out there, Jo even wrote one and since, CSS Grid has emerged as vita tool to enable responsive design. This is a post about how to understand the difference between traditional CSS layout frameworks (Bootstrap, Tailwind, Foundation, etc) & CSS-grid.

I’ve been building things on the web for almost 8yrs & in that time, Bootstrap has been my layout framework of choice. When I began developing on the web, creating responsive layouts in vanilla CSS was intimidating, especially as a Ruby, predominately back-end, engineer but thankfully, CSS has continued to evolve in that time and now we have CSS-grid. Despite this though, many developers still opt to use frameworks which impact the load of their websites and web apps when they don’t need to. I know for me, CSS-grid was still intimidating, I didn’t understand when to use that vs when to use flexbox or if I could use them together. In this post I’m going to try to explain how CSS-grid (& flexbox) work if you’re coming from a framework such as Bootstrap.

To learn and understand CSS-grid I decided to create a grid for my website since the layout is fairly simple and there are few elements on the screen. I marked up the grid and any sub-grids I used to get a clear understanding of where the elements are positioned. The red dashed lines are are the main grid while the yellow marks the sub-grid.

the lolaodelola.dev site with red markings to show the current main grid and yellow markings to show the sub-grid

Without the use of any layout framework, the elements use the original HTML flow

the lolaodelola.dev site without any layout frameworks

I was able to recreate the layout, with only two changes (the positioning of the header) and make it fully responsive to width without any media queries.

a gif of the responsiveness of lolaodelola.dev using css-grid

The Bootstrap Way

Bootstrap typically work by defining a fixed, 12 column grid, while this number and the fixed width has historical context it’s mostly arbitrary, especially now considering the diversity in viewport dimensions. With this fixed width column system, you define how many columns your elements should take up at various viewport widths within a container e.g.

<div class="container">
    <div class="col-3-lg">
        ...
    </div>
</div>
Enter fullscreen mode Exit fullscreen mode

This example defines a container then says the child element should take up 3 columns on a large screen, which in Bootstrap is a screen width of ≥992px. Other frameworks approach this in a different way, for example Tailwind actually uses CSS-grid under the hood. Bootstrap is also mobile-first and in the most recent version, if you have child elements that should take up an equal amount of space, regardless of device width, you don’t have to specify a unit e.g.

<div class="container">
    <div class="col">
        ...
    </div>
    <div class="col">
        ...
    </div>
    <div class="col">
        ...
    </div>
</div>
Enter fullscreen mode Exit fullscreen mode

In the past Boostrap used CSS floats under the hood, however the current version, Bootstrap 5.1 uses flexbox (which I’ll go into more detail later).

The Problem

Bootstrap is a good way to quickly create a layout, especially if that layout is one of the few basic layouts that are used on the web. It has an extensive list of class definitions for developers to choose from, the documentation is robust and it’s free to use, there’s a place for Bootstrap. But choosing to use it should be intentional and not because you’re intimidated by CSS-grid.

Inflexiblity

In Ruby on Rails development, the methodology is “convention over configuration” which is to say it’s easier (read: better) to do things as they’re defined than it is to try and customise the framework to meet your needs. I think most frameworks, regardless of language, work in this way. While Bootstrap is robust, it isn’t flexible. The minute you want to create layouts that don’t follow the rules of a 12 column grid, or have elements doing interesting things like overlapping, or even decide to adjust the layout dependent on height instead of width, things begin to get complicated.

Obscures the CSS

The job of a framework is to create a sort of instruction manual of using a language in a certain way to build certain things. So, yes, frameworks will naturally obstruct the vanilla language being used to construct them however, Bootstrap goes an extra mile to redefine things that can be easily defined in one rule, for example:

.d-flex {
  display: flex !important;
}
Enter fullscreen mode Exit fullscreen mode

This is a rule that can easily be defined within the custom CSS, without the !important keyword.

On the topic of !important this is another place where CSS is obscured, the keyword is litered all through the framework and should you be using Bootstrap in conjuction with another stylesheet, especially on big projects, you will have to be careful and aware of the order of how your stylesheets are being loaded and where styles may be overwritten.

Overbloat

Because Bootstrap tries to cover many basis, there are inevitably rules and styles you won’t end up using but these styles will still be loaded into the site. Bootstrap also comes with a visual identity, there are styles for all kinds of elements, buttons, navs, leads, etc but even if you elect to just include the grid in your project, you won’t be using all aspects of the grid. In fact, it’s possible you may just use 3 or 4 grid properties but will still be loading a 53KB file (if only using bootstrap-grid.min.css ), my custom grid.css file for this project ended up being only 881bytes without being minified and is only 62 lines long.

CSS-Grid

Learning CSS-grid requires unlearning Bootstrap (or which framework you’re using if it’s not using grid under the hood). Before I get into the minutiae, it’s important to note that CSS-grid encourages you to have multiple grids on a page where as Bootstrap assumes you have one main grid which you nest elements within. CSS-Grid says apply the grid where you need it, which really encourages you to think about the default display of elements (which elements are block, which are inline and do you need to flex anything?).

What is a Column, What is a Row, What is a Cell?

Any grid framework or understanding works on a x-axis and a y-axis, it’s 2-dimensional in that way, this may seem like an obvious fact but is important to remember. Within Bootstrap, columns and rows work in the traditional way. A row is horizontal space while a column is a vertical space. In the image below the green dotted line around the perimeter of the elements indicates the row while the dotted line down the middle separates the columns. So there is one row and two columns.

the lolaodelola.dev site with the dev tools layout grid showing the columns in a green dashed line

This would be defined in the HTML as

<div class="row">
    <div class="col">
        <img>
    </div>
    <div class="col">
        <p></p>
    </div>
</div>
Enter fullscreen mode Exit fullscreen mode

Which is to say both the row and the column refer to the space between the lines of the grid, rather than the lines themselves. In other words, we define the row and column on the page and fill both with content.

CSS-grid also uses rows and columns but not in the same way. In grid, the columns and rows refer to the lines rather than the space between the lines.

the dev tools layout shows where the column and row lines are when the page has multiple columns

Although difficult to see, each line is numbered. The numbers in this image refer to the rows and columns, so the first vertical line is column 1, the second is column 2, and the first horizontal line is row 1 and the second is row 2. The space in between these lines, the cell, contains the image. Similarly, the block of text starts at horizontal line 2 i.e. column 2 and ends at horizontal line 3 i.e. column 3 and also starts at vertical line 1 i.e. row 1 and ends at vertical line 2, i.e. row 2.

And when the page collapses into a single column the image is still starts at column 1 and ends at column 2 and starts at row 1 and ends at row 2 but the block of text now also starts tarts at column 1 and ends at column 2 but sits between row 2 & 3.

The space between the rows and columns is called a cell. So the image sits in a cell that between two columns and two rows.

The CSS and HTML for this aren’t complex

<main>
      <!--Grid cell 1 -->
      <img>
      <!-- Grid cell 2 -->
      <div class="about">
      </div>
</main>
Enter fullscreen mode Exit fullscreen mode
main {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(50ch, 1fr));
}
Enter fullscreen mode Exit fullscreen mode

the dev tools layout shows where the column and row lines are when the page is in single column

I will go into detail about grid-template-columns a little later but the important thing here is that I’m defining a grid on the parent element (similarly to how you might define a container in bootstrap) and then telling the CSS how I want the grid to look i.e. how I want elements to occupy the available space.

If I want the elements to span multiple columns or rows, I can define that in the CSS


main {
    display: grid;
        ...
}

img {
    grid-column: 1 / 3;
}

img {
    grid-row: 1 / 3;
}
Enter fullscreen mode Exit fullscreen mode

This will cause the image to start at column 1/row1 and end at column 3/row3, which will occupy 2 cells. If I wanted to have an element occupy 2 cells with Bootstrap, I’d give the element a .col-2 however, remember that Bootstrap works on a fixed-width grid and CSS-grid doesn’t. Play with this on MDN to really get to grips with it.

You can also define a grid-area which is a combination of the rows and columns.

Fractions (aka FR)

With CSS-grid comes a new unit to define how much space elements should take up. While it’s entirely fine to use px, ch, em, rem, %, etc fr (fraction) is a little more flexible in the context of grid.

main {
    display: grid;
    grid-template-columns: 1fr 1fr;
}
Enter fullscreen mode Exit fullscreen mode

As mentioned earlier, the grid-template-columns defines the vertical space the elements in my grid should occupy. I can define the same for rows with grid-template-rows but for now let’s focus on columns. In the CSS above, I’ve said that each column should take one fraction of the available grid space. Regardless of the viewport width, the grid cells will only take up 1 fraction of space in the grid.

When the screen width is too small, the text becomes very cramped and the image very small side by side

So even on smaller devices the elements will still try to take up 1 fraction of the available grid. Of course, you can use any fraction here, 2fr, 7fr, etc and mix and match. You can also tailor to consider how many cells will be in the grid and define those too, so if my grid had 4 cells I could say grid-template-columns: 1fr 1fr 1fr 1fr to say I want 4 cells and each should take up 1 fraction of the available grid space. You can also mix and match different units to get your ideal layout, have a go on MDN.

Gaps aka Gutters

The grid gutter in Bootstrap is the space between columns and rows, in the image below each column has a box around the perimeter and a space in the middle which is the gutter.

the two columns have boxes around them with a gap between them

While the gutters in Bootstrap can be redefined, most users use the default gutters and don’t have to consider them. With grid, gutters are called gaps and you also may not have to define gaps on grid dependent on the units you choose to define the columns but implementing gaps is straight-forward. You have the option of defining the column-gap, row-gap or for if you want both the vertical and horizontal gaps to be the same then gap.

Gaps can also be used to define the space within flex elements, so if you plan to nest grids within flex elements, and are defining gaps on both the grid and flex element, you may find your gaps are slightly wider than you anticipate. The solution is to adjust them as you see fit. You can play with gaps on MDN.

Get Responsive without Media Queries

As I mentioned earlier, I was able to recreate the responsiveness of my original site without using media queries. This requires a combination of rules to create the fluidity of a responsive web page.

First up, repeat

main {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
}
Enter fullscreen mode Exit fullscreen mode

This says create 2 columns each taking up 1 fraction of space in the grid, the issue here is that on smaller viewports, there will be 2 squished cells which isn’t want we want.

To overcome this hurdle, we can define the minimum width for the columns using minmax

main {
    display: grid;
    grid-template-columns: repeat(2, minmax(50ch, 1fr));
}
Enter fullscreen mode Exit fullscreen mode

Here we’re saying we want 2 columns, with a minimum of 50ch width, taking up 1fr of the available space.

when the screen width is too small, the column to the right overflows

This presents a new problem, when we’re on devices with a viewport width that’s smaller than the available row space, the content will overflow. This is also unideal, so there’s one more part to enable the cells collapsing like we expect.

auto-fit/ auto-fill:

main {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(50ch, 1fr));
}
Enter fullscreen mode Exit fullscreen mode

I’ve decided to use auto-fit here which collapses the columns into rows as we expect. auto-fit and auto-fill have similar functions in that they both look at the available space in a row and assign column space to the available cells. Sara Soueidan explains the difference as:

auto-fill FILLS the row with as many columns as it can fit. So it creates implicit columns whenever a new column can fit, because it’s trying to FILL the row with as many columns as it can. The newly added columns can and may be empty, but they will still occupy a designated space in the row.
auto-fit FITS the CURRENTLY AVAILABLE columns into the space by expanding them so that they take up any available space. The browser does that after FILLING that extra space with extra columns (as with auto-fill ) and then collapsing the empty ones.

I recommend reading the full explainer to really get your head around the difference.

The combination of repeat, minmax and auto-fit/ auto-fill creates a layout that’s responsive to width without media queries and in one line.

display: flex

Flexbox deserves its own post so I won’t go into too much detail here but it’s important to know how it fits in with grid. Many people aren’t only intimidated by CSS-grid, but also by Flexbox and the difference is between the two. Coming from Bootstrap, it’s tricky to know when to use either. The good news is you can use both but they do serve different purposes.

As I mentioned before CSS-grid works along the X-axis and Y-axis, it’s 2-dimensional. Flexbox only works along the X-axis, which is the horizontal axis and so it’s 1-dimensional. Adding elements to a flex element will cause the elements to flow horizontally.

The flex container for the nav. Each item is in its own box with the surrounding space shaded in.

In the example above, I have a ul element which usually stacks its list items vertically, however, since I’ve given the element a display: flex the elements are lined up horizontally. It’s possible to allow flex elements to stack vertically to avoid overflow with flex-wrap: wrap as I’ve done for the list of buttons here:

A gif of the responsive buttons created using flex

The CSS for this is two lines

ul {
    display: flex;
    flex-wrap: wrap;
}
Enter fullscreen mode Exit fullscreen mode

Justify & Align Content

The super power of Flexbox is being able to justify and align content the way we want. Aligning content positions the element along its own y-axis, while justify position an element along its own x-axis.

Combining CSS-Grid with Flexbox

Okay, so we can kinda grasp CSS-Grid and Flexbox how and why would we put them together? Let’s revisit my very first image with the Bootstrap grid outlines:

the lolaodelola.dev site with red markings to show the current main grid and yellow markings to show the sub-grid

To recap, the red dashed lines are the main grid and the yellow lines are the subgrid. Breaking this down I can see that I think I want 2 columns and 3 rows for the main grid, however CSS-Grid forces us to only use the grid where necessary. This is where personal preference comes into play, there aren’t any concrete rules here, it’s entirely up to me and how I want to lay this out. Bootstrap’s approach to this would be to make all the columns flex elements and wrap them as necessary and not use the grid at all.

The method I ended up going with was a combination of CSS-Grid and Flexbox.

I changed my mind about the layout to be a little more accesible. The previous layout would have screenreaders reading the image before the title which isn’t ideal. I’ve moved the title into a header with the nav, and since neither the header or the footer needs to work on a 2-dimensional level (or can just without changing the native display properties of the elements), I’ve decided to only apply the grid to the main content. The red dashed line shows 3 columns, 2 rows which create space for 2 cells.

the new lolaodelola.dev layout with red dashed lines outlining the grid

I’ve moved the title into a header with the nav, and since neither the header or the footer needs to work on a 2-dimensional level (or can just without changing the native display properties of the elements), I’ve decided to only apply the grid to the main content. The red dashed line shows 3 columns, 2 rows which create space for 2 cells.

I’ve decided to use flex for elements which normally have a display: block so that they can flow horizontally, wrap if necessary and are justified and aligned as expected on all screen sizes.

the new lolaodelola.dev layout with yellow dashed lines outlining the the flex elements, some of which are a sub-grid

Conclusion

CSS-Grid is very robust and there are definitions that I didn’t cover here, such as grid-template-areas which allows you to define sections of a page with different grids to create a layout, it also allows for empty cells (without defining empty HTML elements). Or grid ordering which lets you define the order of cells within a grid in the CSS.

One of the qualities of Bootstrap is that there’s the option to create sub-grids fairly easily and while it’s possible to nest grids with CSS-grid, there is a dedicated sub-grid ruleset on its way.

Further Watching

Top comments (0)