Introduced in 1996, CSS was designed to separate content from design. This separation allowed developers to maintain and update web styles more efficiently without altering the HTML structure. The early versions of CSS focused primarily on basic typography and color styling. As the web grew, so did the capabilities of CSS, expanding to include layouts, positioning, and dimensions.
Each iteration not only enhanced the visual capabilities of the web but also introduced more efficient, elegant solutions for common design challenges. This article explores key moments that have brought me those "aha" moments, revealing how simple CSS properties and techniques can be used in surprisingly effective ways.
The Art of Spacing: Paddings and Margins
Let's explore a common layout scenario: arranging blocks in columns and rows using CSS.
Padding and margin are two of the most commonly used properties that manage space in web design. Margin is the space around elements; it's external and used to separate the element from others.
Let's take a look at the first approach:
<div class="parent">
<div class="item">Block 1</div>
<div class="item">Block 2</div>
<div class="item">Block 3</div>
<div class="item">Block 4</div>
<div class="item">Block 5</div>
</div>
<p>next content</p>
.parent {
display: table;
width: 100%;
}
.item {
margin-left: 15px;
margin-bottom: 30px;
width: 280px;
float: left;
}
We use the display: table
property to dynamically adjust the .parent
block’s height according to the space occupied by its child elements. Consider the following example without this property:
Downside of this approach:
Adjusting the layout of the .item
element becomes complex under this approach, especially when the width of the parent container changes. The width
and margin-left
of the .item
element need continuous recalibration according to the formula: parentWidth — (itemWidth * 3) — ((gap * 4) / 3)
.
Applying padding to the parent element introduces further complexity:
- When padding exceeds the gap, apply
padding-left
andpadding-right
using the calculation:padding — gap
- When padding is less than the gap, recalculate the width of the
.item
elements
Another downside is the extra space that appears after the elements.
When Bootstrap emerged, I experienced a true ‘aha’ moment: the realization that we could use negative margins to adjust side paddings and gaps effectively. Here’s an example to illustrate this technique:
<div class="container">
<div class="parent">
<div class="item-wrap"><div class="item">Block 1</div></div>
<div class="item-wrap"><div class="item">Block 2</div></div>
<div class="item-wrap"><div class="item">Block 3</div></div>
<div class="item-wrap"><div class="item">Block 4</div></div>
<div class="item-wrap"><div class="item">Block 5</div></div>
</div>
</div>
.container {
box-sizing: border-box;
width: 900px;
margin-left: auto;
margin-right: auto;
padding-left: 15px;
padding-right: 15px;
}
.parent {
margin-left: -7.5px;
margin-right: -7.5px;
}
.parent::after {
display: block;
clear: both;
content: "";
}
Visually, the layout appears similar, but it improves significantly with the addition of padding to the container, adjustments to the item’s width, and the spacing between blocks.
Downsides:
- Utilizing the
::after
pseudo-element to adjust the parent’s height based on its inner elements is a makeshift solution that can lead to maintenance challenges. - The use of fractional pixel measurements, such as
7.5px
(half of a15px
gap), complicates precision in layout and can result in inconsistent rendering across different displays.
Obviously we could use flexboxes to replace the pseudo-element, float and extra DOM elements, it’s more interesting to focus on a key feature: CSS custom variables with the calc()
function.
<div class="container">
<div class="parent">
<div class="item-wrap"><div class="item">Block 1</div></div>
<div class="item-wrap"><div class="item">Block 2</div></div>
<div class="item-wrap"><div class="item">Block 3</div></div>
<div class="item-wrap"><div class="item">Block 4</div></div>
<div class="item-wrap"><div class="item">Block 5</div></div>
</div>
</div>
.container {
box-sizing: border-box;
width: 900px;
margin-left: auto;
margin-right: auto;
padding-left: 15px;
padding-right: 15px;
}
.parent {
--gap: 15px;
margin-left: calc(var(--gap) / -2);
margin-right: calc(var(--gap) / -2);
display: flex;
flex-wrap: wrap;
}
.item-wrap {
box-sizing: border-box;
margin-bottom: 30px;
padding-left: calc(var(--gap) / 2);
padding-right: calc(var(--gap) / 2);
width: calc(100% / 3);
}
This approach may appear outdated as modern grid systems provide simpler, more efficient solutions. Let's conclude this section by exploring a proper alternative:
<div class="container">
<div class="parent">
<div class="item">Block 1</div>
<div class="item">Block 2</div>
<div class="item">Block 3</div>
<div class="item">Block 4</div>
<div class="item">Block 5</div>
</div>
</div>
.container {
box-sizing: border-box;
width: 900px;
margin: 0 auto;
padding: 0 15px;
}
.parent {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 15px;
row-gap: 30px;
}
Mastering Layouts: Positioning of Block Elements
Another classic task frequently asked in interviews involves positioning a block at the center of the screen:
<h2>Variant with inline-block</h2>
<div class="container">
<div class="container__child container__child_v1">
<div class="box"></div>
<div class="box"></div>
</div>
</div>
<h3>inline-block without space</h3>
<div class="container">
<div class="container__child container__child_v1"><div class="box"></div><div class="box"></div></div>
</div>
<h2>Variant with margin auto</h2>
<div class="container">
<div class="container__child container__child_v2">
<div class="box"></div>
<div class="box"></div>
</div>
</div>
.container {
box-sizing: border-box;
width: 100%;
display: table;
}
.container__child {
height: 180px;
display: table-cell;
vertical-align: middle;
}
.container__child_v1 {
text-align: center;
}
.container__child_v1 .box {
display: inline-block;
}
.container__child_v2 .box {
margin-left: auto;
margin-right: auto;
}
.box {
width: 68px;
height: 68px;
}
Downsides:
For the first variant, adding another box will cause the subsequent element to align in a row, with extra space introduced due to the inline-block being treated similarly to text. It’s important to consider two different approaches when arranging elements in columns versus rows.
A minor yet noteworthy detail is the addition of an extra DOM element required to apply the vertical-align
rule.
Now, let’s explore another 'aha' moment that was truly mind-blowing for me the first time I encountered it:
<div class="container">
<div class="box"></div>
</div>
.container {
height: 240px;
position: relative;
}
.box {
position: absolute;
left: 50%;
top: 50%;
margin-left: -34px;
margin-top: -34px;
width: 68px;
height: 68px;
}
Indeed, it might seem like a simple layering detail, but the technique of positioning an element at 50% and then adjusting it with a negative margin was quite astonishing to me at the time.
The caveat, of course, is the need to meticulously monitor the width and height of the box to ensure proper alignment by negative margin. However, there’s a clever workaround for this issue:
.box {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 68px;
height: 68px;
}
The transform
property combined with translate
achieves a similar effect, but it simplifies the process by automatically calculating 50% of the element's width for the X-axis and 50% of its height for the Y-axis.
Top comments (0)