This blog post is part of the series “Web development for beginners” – which teaches people who have never programmed how to create web apps with JavaScript.
To download the projects, go to the GitHub repository learning-web-dev-code
and follow the instructions there.
I’m interested in feedback! If there is something you don’t understand, please write a comment at the end of this page.
CSS provides a variety of services for web content:
Almost all of the diagrams in this blog post were created via HTML and CSS. You can check out the originals here: html/css-layout.html
What does “laying out content” actually mean? Let’s look at two examples where want HTML elements to be placed on a page in particular fashion.
The first example is a horizontal list of links with gaps between them. The dashed gray border shows the (invisible) boundary of the <div>
that contains the links:
This is how we’d like to tell CSS what to do: The links should be placed in a horizontal row, with gaps between them.
CSS flexible box layout helps us with that: It arranges HTML elements in rows and columns.
The second example is a sidebar that occupies a fixed narrow space on the left of the page. The rest is taken up by the actual content:
This is how we’d like to tell CSS what to do: There is a grid with two columns and a single row. The first column has a fixed narrow width. The second column is as wide as fits the page. The row is as tall as is necessary for the content to fit.
CSS grid layout helps us with that: It lets us place HTML elements on a two-dimensional grid.
CSS layout works like this: An outer HTML element contains children that we want to lay out.
<div class="container">
<div class="item">A</div>
<div class="item">B</div>
<div class="item">C</div>
</div>
The outer HTML element is called the container. Its children are called items. We activate a layout via property display
– e.g.:
.container {
display: flex;
}
That changes how elements are arranged inside .container
.
We distinguish:
Axes matter for operations such as aligning.
CSS takes into consideration that not all writing systems go from left to right and from top to bottom. It distinguishes:
Inline axis: In which direction do inline HTML elements (such as <strong>
) flow? In English, they flow from left to right. The term row is a synonym for inline axis.
Block axis: In which direction do block HTML elements (such as <p>
) flow? In English, they flow from top to bottom. The term column is is a synonym for block axis.
The following table summarizes these terms:
Axis | Synonym | Dimension |
---|---|---|
inline axis | row | horizontal |
block axis | column | vertical |
CSS flexible box layout (short: flexbox layout) is a one-dimensional layout. Its most common use cases are:
We need the following CSS properties to use flexbox layout:
display: flex
activates flexbox layout.flex-direction
specifies in which direction the layout items flow inside the layout container. The corresponding axis is called the main axis. The other axis is called the cross-axis. It is perpendicular to the main axis.justify-content
distributes the items along the main axis.align-items
and align-self
align the items along the cross-axis.gap
inserts fixed gaps between items.content
means that a property affects groups of items. items
and self
means that a property affects items individually.
Note that all properties except display
are optional. If we don’t specify them, default values are used.
The following diagram visualizes some flexbox terms:
Note that this diagram only applies if the writing mode is (like) the one used by Latin-based languages and the flex direction is row
(which is the default).
display
and flex-direction
Consider the following HTML:
<div class="vertical">
Text without span
<span>Text inside span</span>
<a href="https://example.com">Link</a>
</div>
We are going to use flexbox to arrange the inline HTML elements inside .vertical
vertically:
.vertical {
display: flex;
flex-direction: column;
}
The result looks like this:
What do the two CSS properties do?
display
activates flexbox layout for the <div>
(because it has the class vertical
).flex-direction
tells CSS that the main axis is the block axis (vertical in this case).It’s interesting that flexbox doesn’t care if a child of .vertical
is an inline element or a block element. Even sequences of text are considered to be layout items (which are called invisible items because they are not HTML elements). However, if a sequence of text consists only of whitespace characters then it does not count as an item and is ignored. One example of that is the text between </span>
and <a>
.
flex-direction
can have the following values:
row
(default): The main axis is the inline axis.row-reverse
: The main axis is the inline axis, but with its direction reversed.column
: The main axis is the block axis.column-reverse
: The main axis is the block axis, but with its direction reversed.If there is no flex-direction
then row
is used as a default.
The following diagrams visualize how these values work:
justify-content
distributes items along main axis Let’s change the layout:
.vertical {
height: 7rem;
display: flex;
flex-direction: column;
justify-content: space-evenly;
}
Previously, the height of the container was derived from its items. Now we set a fixed height. With that height, there is extra vertical space left beyond what is needed for the items. We use justify-content
to tell CSS how to distribute that extra space along the main axis: We want it to be inserted before, after and between the items. The result looks like this:
justify-content
can have the following values:
flex-start
(default): The items are all put at the start of the main axis. They are followed by all extra space.flex-end
: The items are all put at the end of the main axis. They are preceded by all extra space.center
: The items are all put at the center of the main axis. They are preceded by half of any extra space and followed the other half.space-between
: Any extra space is distributed equally between the items.space-around
: Each item gets its own share of the space, before and after it. Therefore, there are spaces at the start and at the end – which are half as wide as the spaces between the items.space-evenly
: There are spaces at the start, between the items and at the end. They all have the same size.The following diagrams visualize how these values work:
gap
inserts fixed gaps between items Let’s use gap
to insert fixed spaces between the items:
.vertical {
display: flex;
flex-direction: column;
gap: 0.5em;
}
The height of the container is once again derived from the heights of its items. However, now there are vertical gaps between items that are 0.5em
high. Each gap increases the height of the container. The container now looks like this:
align-items
aligns all items along cross-axis Let’s center items horizontally (without gaps):
.vertical {
display: flex;
flex-direction: column;
align-items: center;
}
We use align-items
to center the items along the cross-axis. The result looks like this:
align-items
can have the following values:
stretch
(default) makes each item as long along the cross-axis as possible.center
centers each item along the cross-axis.flex-start
moves each item to the start of the cross-axis.flex-end
moves each item to the end of the cross-axis.baseline
makes sure that the baselines of all items line up.The following diagrams visualize how these values work:
align-self
aligns one item along cross-axis If present, align-items
provides a default value for the property align-self
of each item. We can override the former via the latter:
.vertical {
display: flex;
flex-direction: column;
align-items: flex-start;
}
a {
align-self: flex-end;
}
Now the HTML is displayed like this:
flex-wrap
wraps items By default, a flexbox consists of a single line and does not wrap its items. We can change that via property flex-wrap
. This property changes the cross-axis which determines the position and order of the lines. It does not affect the main axis (which determines the order of items within a line). Its values are:
nowrap
(default): No wrapping happens.wrap
: Items are wrapped. The cross-axis is the block axis. It determines the position and order of the lines. t does not change the axis.wrap-reverse
: Items are wrapped. The cross-axis is the block axis, but with its direction reversed.These diagrams visualize the values:
Note that with a fixed width, nowrap
leads to the fixed widths of the items being compressed so that they fit. CSS layouts normally don’t do that.
align-content
distributes wrapped lines along the cross-axis If a flexbox wraps then we can use align-content
to distribute the lines along the cross-axis. It can have these values:
stretch
(default)flex-start
flex-end
center
space-between
space-around
space-evenly
The following diagrams illustrate how these values work:
Flexbox has two more properties for gaps:
column-gap
is for specifying gaps along the inline axis (regardless of the main axis)row-gap
is for specifying gaps along the block axis (regardless of the main axis)Property gap
is actually a shorthand for these two properties:
gap: 1rem;
/* column-gap: 1rem; row-gap: 1rem; */
gap: 1rem 2rem;
/* column-gap: 1rem; row-gap: 2rem; */
The following diagrams illustrate how these properties work:
Notes:
row
diagrams use different items than the column
diagrams.gap
with a single value can be a good choice because then we don’t have to think about axes and “column” vs. “row”.For alignment properties, flexbox layout uses the following terminology:
justify-*
indicates that the property operates along the main axis.align-*
indicates that the property operates along the cross-axis.*-content
indicates that a property affects groups of interdependent items.*-self
indicates that a property affects individual items. It is therefore an item property – while all other properties are container properties.*-items
provides a default value for the *-self
properties of all items.The following table shows all alignment properties supported by flexbox layout:
content (C) |
items (C) |
self (I) |
|
---|---|---|---|
justify (main axis) |
justify-content |
||
align (cross-axis) |
align-content |
align-items |
align-self |
(requires flex-wrap ) |
Notes:
align-content
only has an effect if flex-wrap
is active.Flexbox layout and grid layout handle dimensions differently:
For alignment, the authors of the standards wanted to share as many property names as possible between flexbox and grid, therefore, they did not use the terms “inline” and “block” (which would have worked well for grid), but went with more abstract terms that were inspired by the names of two text alignment properties:
text-justify
. That property operates along the inline axis (which is the default main axis of flexbox).vertical-align
. This property operates along an axis that is perpendicular to the axis of text-justify
:
Turn the initial example into a horizontal list of links with gaps that wraps:
<section>
and the CSS inside the section
rule.The game “Flexbox Froggy” by Thomas Park may be too difficult for beginners, but you can check it out and see if you can make sense of it.
The following table summarizes the flexbox properties:
CSS property | Default | Element | Axis | Affects |
---|---|---|---|---|
display |
container | |||
flex-direction |
row |
container | ||
justify-content |
flex-start |
container | main | groups of items |
align-items |
stretch |
container | cross | single items |
align-self |
value of | item | cross | single items |
align-items |
||||
gap |
0px |
container | main |
Where flexbox is mostly used for one-dimensional layouts, grid is for two-dimensional layouts: The container defines a grid and the items are placed on it. That involves the following steps:
display: grid
To explore how grid layout works, we’ll create the following layout:
If we look at this layout, we can already see a grid with cells: Some HTML elements (header, left bar, content) occupy single cells, others (right bar, footer) more than one.
The following CSS sets up the grid:
.container {
display: grid;
grid-template-columns: 3.5rem 1fr 3.5rem;
grid-template-rows: 1.5rem 1fr 1.5rem;
/* ... */
}
We have defined the following columns and rows:
Columns and rows are called tracks. Each track is delimited by two lines. These are some of the values we can use to define the track sizes:
Tracks can have fixed sizes such as 3.5 rem
and 50px
.
1fr
stands for one fraction of the space that remains after all fixed tracks were allocated their shares. The number is called the flex factor. How many fractions there are, is determined by adding all flex factors. As an example: If there are only two tracks with flexible sizes, one with 1fr
and another one with 2fr
, then there are three fractions in total: The first track gets one third of the remaining space and the second track gets two thirds of it.
Length percentages such as 50%
are relative to the the inline dimension (columns) or the block dimension (rows) of the content box of the container.
min-content
tries to make cells as thin (narrow or short) as possible while the content still fits. It uses the size of its thickest cell as its size.
max-content
tries to make cells as thick (wide or tall) as possible while the content still fits. It uses the size of its thickest cell as its size.
minmax(min, max)
ensures that the size of a track is at least min
and at most max
. That has many uses. One example is dealing with fr
which can become arbitrarily thin and arbitrarily thick. minmax()
lets us constrain that:
minmax(3rem, 1fr)
takes up one fraction of the remaining space, but never less than 3rem
.minmax(1fr, 50px)
takes up one fraction of the remaining space, but never more than 50px
.repeat(n, track-def)
creates n
tracks, each of which has the definition track-def
– e.g.:
repeat(3, 2rem)
creates three tracks, each 2rem
thin.The following diagrams illustrate how min-content
and max-content
work. Notably, min-content
does not make a track narrower than the content allows.
Next, we have to tell grid layout where on the grid to place the grid items (lines have numbers):
We’ll place the following HTML elements:
<div class="container">
<div class="header">
header
</div>
<div class="leftbar">
left bar
</div>
<div class="content">
content
</div>
<div class="rightbar">
right bar
</div>
<div class="footer">
footer
</div>
</div>
Let’s look at three ways in which items can be placed:
The following CSS places the header:
.header {
grid-column-start: 2;
grid-column-end: 3;
grid-row-start: 1;
grid-row-end: 2;
}
We tell CSS grid layout that the header:
We could also have written the CSS like this:
.header {
grid-column: 2 / 3;
grid-row: 1 / 2;
}
Another option is to specify these properties via the HTML attribute style
:
<div style="grid-column: 2 / 3; grid-row: 1 / 2">
header
</div>
If we only mention one number then an item spans one cell – e.g., the following two declarations are equivalent:
grid-column: 4;
grid-column: 4 / 5;
A grid area is a region of the grid that is rectangular (delimited by two lines per axis). One way of defining regions is by writing words in a textual grid:
.container {
/* ... */
grid-template-areas:
". header rightbar"
"leftbar content rightbar"
"footer footer footer"
;
}
Notes:
.
) in a text grid cell means that the corresponding grid layout cell must remain empty.Now that we have defined the grid areas, we can use them to place items:
.header {
grid-area: header;
}
We can also specify the CSS property grid-area
via the HTML style
attribute:
<div class="header" style="grid-area: header">
header
</div>
If we don’t place items explicitly, grid layout places them for us – by default, one item per cell. It starts at the beginnings of the inline axis and the block axis. We can use property grid-auto-flow
to configure how it continues. These are two values (among others) that we can use:
row
(default) places along the inline axis first and the block axis second.column
(default) places along the block axis first and the inline axis second.The following diagrams show how that works:
For alignment properties, grid layout uses the following terminology:
justify-*
indicates that the property operates along the inline axis. This term was inspired by the text property text-justify
.align-*
indicates that the property operates along the block axis. This term was inspired by the text property vertical-align
.*-content
indicates that a property affects tracks (entities created by the container).*-self
indicates that a property affects individual items. It is therefore an item property – while all other properties are container properties.*-items
provides a default value for the *-self
properties of all items.The following table shows all alignment properties supported by grid layout:
content (C) |
items (C) |
self (I) |
|
---|---|---|---|
justify (inline axis) |
justify-content |
justify-items |
justify-self |
align (block axis) |
align-content |
align-items |
align-self |
Notes:
justify-items
and justify-self
Property justify-items
arranges items along the inline axis. It can have the following values:
justify-items
provides a default value for the justify-self
property of each item. Therefore, the latter can override the former.
align-items
and align-self
Property align-items
arranges items along the block axis. It can have the following values:
align-items
provides a default value for the align-self
property of each item. Therefore, the latter can override the former.
justify-content
If there is a resizable column such as 1fr
then a grid layout can always fill the complete inline axis of the container. If not then we can use property justify-content
to tell it how to distribute unused space:
align-content
align-content
specifies how to distribute unused space between rows:
column-gap
, row-gap
and gap
Grid layout supports two properties for adding gaps between items:
column-gap
is for specifying gaps along the inline axisrow-gap
is for specifying gaps along the block axisAdditionally we can use property gap
as a shorthand:
gap: 1rem;
/* column-gap: 1rem; row-gap: 1rem; */
gap: 1rem 2rem;
/* column-gap: 1rem; row-gap: 2rem; */
The following diagrams illustrate how these properties work:
Mostly follow the steps described in the first exercise and play with the grid layout example:
grid-template-areas
.How can we decide which CSS layout mechanism to use?
margin
, padding
and text-align
may already be enough.Often, we are not faced with a single layout use case but with multiple, nested ones – e.g., the top-level layout may be a grid but that grid may contain a column of elements – which are laid out via a nested flexbox. We’ll see an example of that later.
In this section we apply what we have learned to examples that are small but often occur in real-world projects.
Centering text horizontally is easy to do in CSS. As an example, take the following HTML:
<div class="container">
Centered
</div>
We can center the text horizontally like this:
.container {
text-align: center;
}
If the height of the <div>
is greater than the height of the text, we may additionally want to center the text vertically. That is more complicated. One elegant solution is to use flexbox:
.container {
display: flex;
justify-content: center;
align-items: center;
}
justify-content
centers along the inline axis.align-items
centers along the block axis.The result looks like this:
Given the following HTML:
<div class="row-of-inlines">
<span>Home</span>
|
<span>About</span>
|
<span>Follow</span>
|
<span>Archive</span>
</div>
In real projects, those spans are often links. We’d like the HTML to look like this:
To achieve that, we use a flexbox to insert gaps between the spans:
.row-of-inlines {
display: flex;
gap: 1rem;
}
This is a sequence of block elements:
<div class="column-of-blocks">
<div>A</div>
<div>B</div>
<div>C</div>
</div>
We’d like to arrange them like this:
We can use flexbox to do that:
.column-of-blocks {
display: flex;
flex-direction: column;
gap: 1rem;
}
Consider this sequence of blocks:
<div class="row-of-blocks">
<div>A</div>
<div>B</div>
<div>C</div>
<div>D</div>
<div>E</div>
</div>
We want to lay them out as follows:
To do that, we need this CSS:
.row-of-blocks {
flex-wrap: wrap;
gap: 1rem;
}
Consider the following user interface:
We can use <label>
and <input type="text">
to create it. However, if we want to use grid to align a label with its text field then we can’t nest the latter inside the former. Instead, we have to connect them via an ID and the HTML attribute for
:
<div class="container">
<label for="celsius">Celsius:</label>
<input type="text" id="celsius" size="5" value="100">
<label for="fahrenheit">Fahrenheit:</label>
<input type="text" id="fahrenheit" size="5" value="212">
</div>
The grid layout for this HTML looks like this:
.container {
display: grid;
grid-template-columns: min-content min-content;
grid-template-rows: min-content;
gap: 0.5rem;
}
We did not place the layout items outselves, we let grid layout place them automatically.
If we wanted to align the baselines of the smaller font used for the label and the larger font used for the text field, we could do that like this:
.container {
/* ... */
align-items: baseline;
}
During the early mobile web (in the late 2000s), there were often two versions of a web app:
The idea behind responsive design is to use a single design for both that flexibly adapts to screen sizes. In this section, we’ll look at two CSS features that help us with that:
calc()
In this section, we’ll need to perform some simple arithmetic with CSS lengths and the CSS function calc()
lets us do that. As an example, let’s assume we have:
article {
box-sizing: border-box;
padding-left: 1rem;
padding-right: 1rem;
/* ... */
}
And we want the content to take up 50% of its parent. Then we can use calc()
like this:
article {
/* ... */
width: calc(1rem + 50% + 1rem);
}
calc()
lets us use arithmetic operations such as addition (+
), subtraction (-
), multiplication (*
) and division (/
). And we can freely mix various CSS units.
Note that it’s often better to switch to content box sizing than to use calc()
but that’s not always possible.
The following user interface changes in response to how wide the viewport is:
Left-aligning the links in the sidebar was a deliberate decision; I find that more pleasant to read.
How can we switch between layouts in CSS? Via media queries:
@media «condition» {
/* Conditional declarations go here */
}
A media query contains declarations that are only active if the condition is true. The condition checks the medium the HTML is displayed on. Examples include:
/* Is the HTML displayed on a printed page? */
@media print { /* ... */ }
/* Is the HTML displayed on a screen? */
@media screen { /* ... */ }
/* Is the viewport less than 1024px wide? */
@media (width < 1024px) { /* ... */ }
/* Displayed on screen and viewport less than 1024px wide? */
@media screen and (width < 1024px) { /* ... */ }
Often the declarations of the media query override defaults that were set up earlier. As an example, the following CSS responsively changes between the two layouts shown above:
body {
display: grid;
grid-template-columns: 5rem 1fr;
grid-template-rows: min-content;
gap: 0.5rem;
grid-template-areas:
"sidebar content"
;
@media (width < calc(5rem + 0.5rem + 10rem)) {
grid-template-columns: 1fr;
grid-template-rows: min-content min-content;
grid-template-areas:
"content"
"sidebar"
;
}
}
Notes:
By default, we have two columns and the sidebar is to the left of the content.
The condition of the @media
at-rule only lets us refer to the total width of the viewport. We’d like the layout to change if the content is narrower than 10rem
. Therefore, we use calc()
to compute the sum of the width of the sidebar, the width of the gap and 10rem
. If the width of the viewport is less than that sum, we change the grid layout: There is a single column and the sidebar is below the content.
It would be nice if we could create a CSS variable for the calculated width. Alas, browsers currently don’t support that – even though the standard allows it.
The points at which the layout changes are called breakpoints. In the previous CSS, the result of calc()
is a breakpoint. This term was first used in 2010 (source, first appearance in the jQuery Mobile codebase). It’s not completely clear what it inspired that name. Two explanations seem plausible:
Mathematical functions can have breakpoints where they are not continuous.
Most non-responsive layouts “break” at a certain point when we make the viewport smaller. Then it’s time to switch to a different layout.
The following layout is similar to the previous one (the elements of the sidebar are also deliberately left-aligned):
Note the additional features:
In the first “screenshot”, we can see that the content has a maximum width. As a consequence, text lines won’t get too wide even if the viewport is very wide. We additionally center it horizontally, which also helps with wide viewports.
In the second “screenshot”, we can see that the flow of the elements of the sidebar changes if it is underneath the content.
In the third “screenshot”, we can see that the elements of the of the sidebar wrap if there is not enough horizontal space for them. We can also see that the content is padded – which prevents text from touching the edges of the viewport.
<div class="layout">
<div class="sidebar" style="grid-area: sidebar">
<div>1</div>
<div>2</div>
<!-- ... -->
</div>
<div class="content" style="grid-area: content">
<p>This is the content.</p>
<p>It contains multiple paragraphs.</p>
<p>They wrap automatically.</p>
</div>
</div>
The following CSS is basically the same as in the previous example:
.layout {
display: grid;
grid-template-columns: 2rem 1fr;
grid-template-rows: min-content;
grid-template-areas:
"sidebar content"
;
gap: 0.25rem;
@media (width < calc(2rem + 0.25rem + 10rem)) {
grid-template-columns: 1fr;
grid-template-rows: min-content min-content;
grid-template-areas:
"content"
"sidebar"
;
}
}
.sidebar
uses the same media query as .layout
in order to go from column flex direction to row flex direction. In the former mode, we don’t wrap because we want the elements to determine the height of the sidebar. In the latter mode, we enable flex-wrapping.
.sidebar {
display: flex;
flex-direction: column;
gap: 0.5rem;
@media (width < calc(2rem + 0.25rem + 10rem)) {
flex-direction: row;
flex-wrap: wrap;
}
}
This is the CSS for the content:
.content {
min-width: 5rem;
max-width: 14rem;
padding: 0 0.5rem;
justify-self: center;
}
0.5rem
prevents the content from ever touching the edges of the viewport.justify-self
to center the content horizontally.Where media queries react viewport size changes, container queries react to the size changes of an HTML element. That means that a single HTML element can become responsive. With container queries, two roles are important:
<div class="wrapper">
<div class="layout">
<!-- ... -->
</div>
</div>
.wrapper
is the container. We can query it as if it were a viewport. However, we can’t use queries to change the layout of .wrapper
, we can only do so in a nested HTML element such as .layout
.
Note that CSS re-uses the term container for this context:
.wrapper
is a query container..layout
is a layout container.The following CSS turns .wrapper
into a query container:
.wrapper {
container-type: size;
container-name: wrapper;
}
Property container-type
can have these values:
normal
: The HTML element is not a query container.size
: Enables container size queries for both the inline axis and the block axis.inline-size
: Only enables container size queries for the inline axis.Now we can use a container query for .layout
:
.layout {
/* Default declarations go here */
@container wrapper «condition» {
/* Conditional declarations go here */
}
}
As you can see, the syntax of container queries is very similar to the one of media queries. We can omit the name wrapper
after @container
. Then the query refers to the most recently declared container.
The following image shows a contact card at three different sizes:
The contact card adapts in two ways:
This is the HTML behind the contact card:
<div class="wrapper">
<div class="contact-card">
<img class="headshot"
src="css-layout/person.svg"
width="110" height="110"
>
<div class="name">
E. Scrooge
</div>
<div class="description">
The hard-working boss of our company. Profits matter!
</div>
</div>
</div>
Let’s look at the CSS that implements this behavior.
First, we enable container queries for .wrapper
:
.wrapper {
container-type: size;
container-name: wrapper;
}
Second, we set up the default layout:
.contact-card {
display: grid;
grid-template-columns: min-content 1fr;
grid-template-rows: min-content 1fr;
grid-template-areas:
"headshot name"
"headshot description"
;
gap: 0.5rem;
padding: 0.5rem;
/* ... */
}
Third, we use a container query to set up the layout that is used when we are inside the width breakpoint but not inside the height breakpoint.
.contact-card {
/* ... */
@container wrapper
(width < calc(0.5rem + 110px + 0.5rem + 5.5rem + 0.5rem)) and
(height >= calc(0.5rem + 110px + 0.5rem + 1.2rem + 0.5rem + 5rem + 0.5rem))
{
grid-template-columns: 1fr;
grid-template-rows: min-content min-content min-content;
grid-template-areas:
"headshot"
"name"
"description"
;
}
/* ... */
}
Explanation of the calc()
uses:
110px
is the fixed width of the image. 5.5rem
is the minimum width of name and description. The rest is padding or a gap.110px
is the fixed height of the image. 1.2rem
is the minimum height of the name. 5rem
is the minimum height of the description. The rest is padding or gaps.How does the layout change? The layout is now vertical and consists of a single column. The headshot comes first and is followed by the name and the description.
Fourth, we set up the layout that is used when we are inside the width breakpoint and inside the height breakpoint.
.contact-card {
/* ... */
@container wrapper
(width < calc(0.5rem + 110px + 0.5rem + 5.5rem + 0.5rem)) and
(height < calc(110px + 1rem))
{
grid-template-columns: 1fr;
grid-template-rows: min-content min-content;
grid-template-areas:
"headshot"
"name"
;
.description {
display: none;
}
}
}
How does the layout change? The description is not a template area anymore and hidden via the display
property.
The remaining CSS assigns grid areas to HTML elements. These remain the same regardless of what breakpoints were triggered.
.headshot {
grid-area: headshot;
}
.name {
grid-area: name;
font-weight: bold;
}
.description {
grid-area: description;
}
Mostly follow the steps described in the first exercise and play with the contact card example:
calc()
:
calc()
” of the W3C standard “CSS Values and Units Module Level 4”calc()
”We have taken a comprehensive look at CSS layout mechanism but there is more – these are a few interesting examples:
auto
and get a gap that grows as much as possible (more information).flex-grow
works similarly to 1fr
etc. in grid layout.Tip against feeling overwhelmed by the sheer amount of CSS features: For most of those features, it’s enough to be aware what a feature does. Then, when you actually need it, you can learn the details on the spot. That’s usually better than “learning ahead”.