Fundamentals of layout creation with CSS

Fundamentals of layout creation with CSS

Learn CSS and how it works, to build complex layouts without many difficulties.


Box Model

HTML elements are divided into 4 parts:

  • Margin - Space between elements
  • Border - Border which colors can be applied
  • Padding - Internal space of the element
  • Content - Content of the element

By default the Box Model applies height and width to the Content Box. This causes inconsistencies in layout creation. Because when you declare the sizes of an element, it will behave differently for an element that has padding and border.

To fix this the first thing we have to do is add the following style:

* {
box-sizing: border-box;
}

In it we use the asterisk selector ” * ” to declare that all site elements must respect the border sizing model. Which comparing with the standard model looks like this:

Flow Layout

HTML is a markup language that was designed for documents, so the default placement of elements is vertical. This model is known as a Flow Layout, and in it elements float to the top left corner and adjust to take up the space of your content.

Because of this default model, both the html element and the body element do not fit 100% of the height of the screen, which jeopardizes the styling based on relative height values. So we have to add the following style:

html,
body {
width: 100%;
height: 100dvh;
}

With this, we guarantee the consistency of the width, by percentage, and the height, by the measured unit dvh (Dynamic Viewport Height).

The viewport is the technical term for your browser window, meaning, everything that is not a toolbar is a viewport. It is dynamic because on mobile the navigation bar changes as the user scrolls on the page.

Flexbox

Flexbox, or for the intimate flex, is a unidirectional model for building layouts. It defines two axes, main and cross.

Axes

The main axis is the one that crosses all child elements of a container. The cross axis is the one that crosses the elements individually.





By default the main axis is horizontal, but that can be changed with the following style:

.class {
display: flex;
flex-direction: column;
}




Horizontal and vertical alignment

To align the main axis elements, use the justify-items property.

.class {
display: flex;
justify-items: center;
}




To align the cross axis elements, use the align-items property.

.class {
display: flex;
align-items: center;
}




ATTENTION!! When you change the axis direction, the reference to what is vertical and horizontal also changes.

To align the cross axis elements, use the align-items property.

.class {
display: flex;
flex-direction: column;
align-items: center;
}




To align the main axis elements, use the justify-items property.

.class {
display: flex;
flex-direction: column;
justify-items: center;
}




Element positioning wrap

Another functionality of the flexbox model is the possibility of wrapping position of elements, in case there is not enough space in the parent container.

.class {
display: flex;
flex-wrap: wrap;
}

Space between elements

Another property, not exclusive to the flexbox model, is the gap, which is a space that can be added between elements.

.class {
display: flex;
gap: 1rem;
}

But then you might ask yourself, “Why not use a margin, to add space?”. This is because the margin would not work correctly with the flex-wrap positioning wrap.

.class {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}

Element growth factor

We don’t always want elements to scale based on their content, but using relative units of measurement, like percentages, to do this can be a headache. Even more involving element wrapping and vertical and horizontal alignment.

That’s why we have the flex-grow property, with which we can define element weights so that they adjust and grow according to our needs. Usually we add this property directly to the element that we want to grow with the value “1”.

.flex {
display: flex;
}
.grow {
flex-grow: 1;
}

Grid

Grid Layout is a bidirectional model for building layouts, meaning, unlike flex it focuses on more than one direction at the same time. With it screen regions are defined and elements can be manipulated to occupy one or more of those regions.

.class {
display: grid;
}

Space between elements

Another property, not exclusive to the flexbox model, is the gap, which is a space that can be added between elements.

.class {
display: grid;
gap: 1rem;
}

Rows and columns

Like a table, the grid also works with lines and columns, but the difference is that there is a special measure unit fractional (fr), which can be used together with other units.

.class {
display: grid;
gap: 1rem;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr;
}

Grid Layout Functions

As the grid layout introduces new concepts, it also provides some functions to fit some scenarios: repeat(), minmax() and fit-content().

  • repeat() as the name says is for repeating the sizes of rows or columns.
.class {
grid-template-rows: 1fr 1fr 1fr 1fr 1fr 1fr;
}

Using the repeat() function this would be the equivalent of the code above:

.class {
grid-template-rows: repeat(6, 1fr);
}
  • minmax() defines the maximum and minimum sizes of rows or columns.
.class {
grid-template-rows: minmax(20px, auto);
}
  • fit-content() works like auto, but setting a fixed size, which can be bigger than the element size.
.class {
grid-template-rows: fit-content(200px);
}

Grid area

As stated above, the grid works with rows and columns like a table. You must be wondering: “OK, but why not just use a table?”. This is because in the Grid Layout an element can occupy more than one cell.

There is more than one way to do this, but I recommend using grid area. With it we can define nicknames for our page’s containers, with the grid-area property, and create a one-to-one parallel with your layout, using the grid-template-areas property.

.grid {
display: grid;
gap: 1rem;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(3, 1fr);
grid-template-areas:
"GRID_ITEM"
"GRID_ITEM"
"GRID_ITEM";
}
.grid__item {
grid-area: GRID_ITEM;
}

In the example above we can see that in the .grid-item class the nickname is declared, while in the main container the spaces where GRID_ITEM should occupy. Containers whose grid-area where not defined, fit the default behavior of a cell.

Now picture that you have a screen with the following elements:

<header />
<main />
<footer />

Now let’s apply the grid areas in the format we want our layout will fit:

.header {
grid-area: HEADER;
}
.aside {
grid-area: ASIDE;
}
.main {
grid-area: MAIN;
}
.footer {
grid-area: FOOTER;
}
.grid {
display: grid;
gap: 1rem;
grid-template-areas:
"HEADER HEADER"
"ASIDE MAIN"
"FOOTER FOOTER";
}
<header />
<main />
<footer />

In this scenario I didn’t even need to declare the number of rows and columns, but I could define that the aside column has to fit automagically.

.grid {
display: grid;
gap: 1rem;
grid-template-areas:
"HEADER HEADER"
"ASIDE MAIN"
"FOOTER FOOTER";
grid-template-columns: auto 1fr;
}
<header />
<main />
<footer />

Grid span

Our containers do not always have semantic value to justify the use of the grid areas. So we can declare how many rows and columns our element will occupy, through the grid-row and grid-column properties, which together with the span keyword, we don’t need to define the starting or ending point of the element’s scope, taking the current position as a reference.

.grid {
display: grid;
gap: 1rem;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
}
.grid__item {
grid-column: span 2 / span 2;
grid-row: span 3 / span 3;
}