Skip to main content

Using Fixed CSS Grid Layout

Prequisites

The guide assumes that the grid is fixed CSS grid layout and uses 12 columns for each breakpoint. Learn more about configuring your grid here.

styles/assets/configuration/layout/_grid.scss
$grid-type: 'grid';
$grid-fluid: false;
$grid-layouts: (
'xs': (
'container': 100%,
'columns': 12,
'gap': 15px,
'margin': 15px,
),
'sm': (
'container': 540px,
),
'md': (
'container': 720px,
),
'lg': (
'container': 960px,
),
'xl': (
'container': 1140px,
),
'xxl': (
'container': 1560px,
),
);

Introduction

Fixed CSS Grid differs from flexbox solution in that the layout is created in the SCSS, not twig or html files. It utilizes the CSS Grid in order to create the layout for each website's component. It brings the benefit of cleaner structure and readability of .twig and .html files and minimises the chance of repeating the same code.

Layouts

The layout is a visual representation of grid elements written in the order they appear to the user. It is similar to the native CSS 3's grid-template-areas property, but much more extensible, giving you ability to repeat certain element in shorter way. Also, you can create multiple layouts in one declaration of the grid() mixin!

<div class="parent">
<div class="parent__element">
<p>Element</p>
</div>
</div>

The problem

Imagine you have 12 columns layout, in your website, and want to extend the element over all of them. In native CSS you would do like so:

CSS approach
  .parent {
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-template-areas:
'element element element element element element element element element element element';

&__element {
grid-area: element;
}
}

It does not seem very readable, and it's hard to tell exactly how many columns does the .parent__element take. You could potentially use the grid-column property and grid-row properties on the .parent__element itself, but it prevents you from controlling the .parent__element from the .parent scope.

The solution

Loonar comes with a handy grid() mixin, which allows you to create a grid of multiple layouts in one declaration. It utilises both .parent scope and grid-column / grid-row properties on the .parent__element itself. Here's an example, how this layout would look like in Loonar!

The grid() mixin
.parent {
@include grid((
xs: (
'12(element)',
)
));
}

The only thing you need to remember is to follow the BEM methodology, no need to pass the parent in the name of the element. It'll be automatically added by Loonar.

Manipulating the layout based on viewport size

As you have seen in the previous example, we created a template for the xs breakpoint. This breakpoint name is extracted from the breakpoint you have already defined in the $breakpoints map, when configuring the project. Since it's the smallest breakpoint it will be triggered initially, without any @media queries.

Your layout will change many times, depending on the design of your website. Whenever it changes, and you want to re-position the elements, you can do so by simply providing the template for higher breakpoint.

In this example, we'll have another element in our layout. Both of them should be placed next to each other whenever the viewport reaches $md breakpoint, but initially they should be placed below each other.

<div class="parent">
<div class="parent__element-one">
<p>Element one</p>
</div>
<div class="parent__element-two">
<p>Element two</p>
</div>
</div>
Different template for different breakpoints
.parent {
@include grid((
xs: (
'12(element-one)',
'12(element-two)',
),
md: (
'6(element-one) 6(element-two)',
),
));
}

Spacing between elements

Sometimes, you'd like to increase or decrease the gap between elements. Some designs may have 0px gap between elements, or maybe you have the need to push certain element by a column or two.

The grid() mixin allows you to do that by adding a . (dot). You only need to remember, not to exceed the total number of columns.

Grid gap (offset)
.parent {
@include grid((
xs: (
'12(element-one)',
'12(element-two)',
),
md: (
'6(element-one) . 5(element-two)',
),
));
}

If you are in need to push the element by more than one column, you can do so the same way as specifying the number of columns for an element.

Grid gap (repeating offset)
.parent {
@include grid((
// ...
md: (
'5(element-one) 2(.) 5(element-two)',
),
));
}

Nested layouts

In some cases, you might need to include layout in another layout. These are nested layouts. Because they are not specifically related to the .parent, the scope now changes to the .parent__element itself. To make the nested layout work, you need to pass the scope name as the second argument to the grid() mixin.

<div class="parent">
<div class="parent__element-one">
<p>Element one</p>
</div>
<div class="parent__element-two">
<div class="element-two__child-one">
<p>Child one</p>
</div>
<div class="element-two__child-two">
<p>Child two</p>
</div>
</div>
</div>
Grid gap (offset)
.parent {
@include grid((
xs: (
'12(element-one)',
'12(element-two)', // element-two has 12 columns initially
),ą
md: (
'6(element-one) . 5(element-two)', // element-two has 5 columns when reached `md`
),
));

&__element-two {
@include grid((
xs: (
'6(child-one) 6(child-two)', // 6 + 6 = 12 initially
),
md: (
'3(child-one) 2(child-two)', // 3 + 2 = 5 when reached `md`
),
), '.element-two');
}
}