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 likeauto
, 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:
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";}
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;}
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;}