If we want to build real responsive components, it is often no longer enough to let something slide into the next line. Sometimes it is necessary to change the structure of a component and this can quickly become complicated. I would like to use the following example to show how the problem can be solved with Flexbox and Grid.
The Example
Let's assume that we want to implement the following card:
The card consists of an avatar, a title and a short description text.
In the mobile view the avatar and the title should be in the same line and the description should take up the whole line below.
For larger viewports, we want the avatar to be larger and the description to be next to the avatar as well.
Ok, let's see how we can implement this.
Flex solution
We start with a flexbox implementation, because I always start with flexbox.
An initial implementation of the mobile variant could look like this:
<section>
<div className="flex items-center gap-4">
<Avatar className="h-16 w-16" />
<h3 className="text-2xl font-bold">Title</h3>
</div>
<p className="mt-5">Description</p>
</section>
But when we try to implement the larger viewports, it gets difficult quickly.
The problem is that the structure changes.
The only solution I can think of here is to duplicate the description:
<section>
<div className="flex items-center gap-4">
<Avatar className="h-16 w-16 flex-shrink-0 sm:h-24 sm:w-24" />
<div className="w-full">
<h3 className="text-2xl font-bold">Title</h3>
<p className="mt-5 hidden sm:block">Description</p>
</div>
</div>
<p className="mt-5 sm:hidden">Description</p>
</section>
To solve the problem we had to group the title and description with a div
,
hide the description (hidden
) and
only show it when we are in a larger viewport (sm:block
).
The description for the small viewport is displayed by default and
is hidden when we are in a larger viewport (sm:hidden
).
But we need to do more.
The grouping of the title can cause the avatar to be squeezed,
so we need to add a w-full
to the grouping div
and a flex-shrink-0
to the avatar.
This solution is far from optimal.
We had to include more markup and we had to duplicate content.
There is probably a better way to solve this problem with Flexbox,
but I couldn't think of one.
If you know of a better solution, please leave a comment
Grid to the rescue
Lets try to implement the same layout with grid instead of flexbox.
We start again with the mobile view:
<section className="grid grid-cols-[5rem,1fr] items-center">
<Avatar className="h-16 w-16" />
<h3 className="text-2xl font-bold">Title</h3>
<p className="col-span-2 mt-5">Description</p>
</section>
Except for the arbitrary value for grid-cols, the whole thing is pretty simple.
We create a grid with 2 columns, the first is 5rem wide (1rem wider than the avatar) and the second is 1fr wide (grid grid-cols-[5rem,1fr]
).
In the first column of the first row is the avatar,
in the second column is the title.
We have extended the description to two columns (col-span-2
) and
it is in the second row.
In the case of the larger viewport,
we actually only need to draw the avatar over 2 lines and
take back the col span of the description.
The whole thing could look like this:
<section className="grid grid-cols-[5rem,1fr] items-center sm:grid-cols-[7rem,1fr]">
<Avatar className="row-span-2 h-16 w-16 sm:h-24 sm:w-24" />
<h3 className="text-2xl font-bold">Title</h3>
<p className="col-span-2 mt-5 sm:col-span-1">Description</p>
</section>
With the row-span-2
we could draw the avatar over both lines and
with sm:col-span-1
we reset the col span of the description.
We also had to reserve more space for the avatar in the first column of the grid (sm:grid-cols-[7rem,1fr]
).
Conclusion
With the grid solution we needed much less markup.
We also didn't need duplication anymore and it's also much easier to understand.
Oldest comments (0)