Every time I encounter a component that needs a floating layout, I ask myself: Is it really necessary to use a floating layout? I began to notice several instances where position: absolute was really unnecessary. I thought this was interesting because I decided to document a few examples of my front-end projects.

In this article, I’ll talk about a few examples where floating layouts are used, but not necessarily.

introduce

If we go back five years, CSS elastic layout was a relatively new concept and we tended to downgrade it a lot. CSS grid layouts were not even supported at the time. Ultimately, we will use CSS positioning to achieve the desired effect. Some of these examples, however, are already available today using elastic or grid layouts.

A few days ago, my team was developing a front-end landing page, and my colleague asked me a layout question. This problem happened to be caused by postion: absolute. I tried to help her, and finally we solved this problem without even using floating layout! It was at that moment that it inspired me: I should write about this kind of problem — and that’s exactly what I’m doing.

Some examples

Card to cover

When we have a card and need some text to float on it, the first thing we usually do is use position: Absolute to solve the problem. But with CSS Grid, we don’t need to do that anymore:

This is a typical “text floats on a card” style, and here is its HTML code:

<article class="card">
    <div class="card__thumb">
        <img src="assets/mini-cheesecake.jpg" alt="">
    </div>
    <div class="card__content">
        <h2><a href="#">Title</a></h2>
        <p>Subtitle</p>
    </div>
</article>
Copy the code

In order for the text content to float on the card, we need to float.card__content:

.card {
    position: relative;
}

.card__content {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    background: linear-gradient(to top, # 000.rgba(0.0.0.0) bottom/100% 60% no-repeat;
    padding: 1rem;
}
Copy the code

When we decided to use position: Absolute to achieve this effect, we did exactly the right thing. So is there an easier way? Let’s take a look:

The first step is to give the entire card component a display: grid. We don’t need to set any rows or columns yet.

.card {
    position: relative;
    display: grid;
}

.card__content {
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
}
Copy the code

CSS Grid layouts turn content into row elements by default. In the card component above, we have two main elements (background image and text content), so we have two lines.

To overlay content on top of an image, we need to place both elements in the same grid area.

.card__thumb..card__content {
    grid-column: 1/2;
    grid-row: 1/2;
}
Copy the code

We can usegrid-areaAbbreviate to implement it more elegantly:

.card__thumb..card__content {
    grid-area: 1/2;
}
Copy the code

Finally, we can use grid-area: 1/-1 instead. Because -1 represents the last row and column in a Grid layout, this abbreviation always finds the last row and column elements in the layout.

Card label

Based on the previous example, we now want to expand the demo. We want to add a “tag” to the top left corner of the card. To do this, we’ll use the same technique, grid-area: 1/-1, but as you can see below, we don’t want the tag to cover the container:

To fix this, we need to set some alignment properties for this element relative to the container:

.card__tag {
	align-self: start;
  justify-self: start;
  /* Other style attributes */
}
Copy the code

We can see that the tag is already floating based on its parent element, and we don’t use position: absolute at all.

Hero Section

Note: In web development, the Hero Section is a special term that refers to the first thing people see when they open your site.

Another perfect example: we often use the effect of text floating on the image in the Hero section.

This is a bit like the card example, but a bit different in the style code for the text content. Before we delve into modern CSS implementations, take a few minutes to think about how we might implement it with a floating layout.

Now we have three layers:

  • The picture
  • Transparent gradient mask
  • The text

There are several ways to do this. If the image is purely decorative, we can use background-image; otherwise, we use an tag.

.hero {
    position: relative;
    min-height: 500px;
}

.hero__thumb {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Superimposed elements */
.hero:after {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    background-color: # 000;
    opacity: 0.5;
}

.hero__content {
    position: absolute;
    left: 50%;
    top: 50%;
    z-index: 1;
    transform: translate(-50%, -50%);
    text-align: center;
}
Copy the code

Above is the code we used to implement the Hero section using a floating layout. But now that we want to do it in a more elegant way, let’s look at a more modern way.

First, we need to apply a display: grid property to the entire Hero element. Then, just as we did in the card example, we put a grid-area: 1/-1 on all the children of the Hero element (so that they are squeezed into a grid element).

Note that we now need to fix the height of the hero section for the.hero_thumb element to work. Hero_thumb is a child element that uses height: 100%, which is valid only if the parent element has a fixed height, whereas min-height: 100px; It’s not a fixed height.

.hero {
    display: grid;
    height: 500px;
}

.hero__content {
    z-index: 1; [1] / * * /
    grid-area: 1/-1;
    display: flex;
    flex-direction: column;
    margin: auto; [2] / * * /
    text-align: center;
}

.hero__thumb {
    grid-area: 1/-1;
    object-fit: cover; / * [3] * /
    width: 100%;
    height: 100%;
    min-height: 0; / * [4] * /
}

.hero:after {
    content: "";
    background-color: # 000;
    opacity: 0.5;
    grid-area: 1/-1;
}
Copy the code

I’d like to highlight the code on the lines of several comments I marked:

  1. We can use it directly on child elementsz-indexAttribute, which does not need to be added to the parent elementposition: relative!
  2. since.hero__contentIs a grid element that is usedmargin: autoTo center him horizontally and vertically.
  3. Don’t forget to add it to the imageobject-fit: cover.
  4. I used it on the picture of Beijingmin-height:0To ensure that if someone configures a large image for the Hero section, it does not exceed the scope of the Hero section. If you are interested in this detail, read my article: Minimum Content Size in CSS Grid [1].

Notice how much more elegant and clean our code is now.

CSS display: contents

I feel like this is the only place I’ve come across in real work where the display: content property works, so take a look at the layout below:

Here’s its HTML code:

<div class="hero">
    <div class="hero__content">
        <h2><! -- Title --></h2>
        <p><! -- Desc --></p>
        <a href="#">Order now</a>
    </div>
    <img src="recipe.jpg" alt="">
</div>
Copy the code

Nothing fancy so far, but on mobile, we want it to be responsive like this:

Notice that the picture on the layout is inserted between the title and description. If it were you, how would you solve the problem? The first thing that might come to mind is to change the HTML order:

<div class="hero">
    <div class="hero__content">
        <h2><! -- Title --></h2>
        <img src="recipe.jpg" alt="">
        <p><! -- Desc --></p>
        <a href="#">Order now</a>
    </div>
</div>
Copy the code

On the mobile end, the behavior of changing THE HTML succeeded. On the desktop, we float the tag to the right. But we actually have a more straightforward solution, which is to use display: contents.

Let’s go back to the original HTML code:

<div class="hero">
    <div class="hero__content">
        <h2><! -- Title --></h2>
        <p><! -- Desc --></p>
        <a href="#">Order now</a>
    </div>
    <img src="recipe.jpg" alt="">
</div>
Copy the code

Notice that our literal content is wrapped in a.hero__content container. How can we tell the browser that we want

,

, and to be siblings of ? That’s where display: Contents comes in.

.hero__content {
	display: contents;
}
Copy the code

The container node becomes a hidden ghost node by adding display: content to.hero_content. The browser actually parses the HTML as follows:

<div class="hero">
    <h2><! -- Title --></h2>
    <p><! -- Desc --></p>
    <a href="#">Order now</a>
    <img src="recipe.jpg" alt="">
</div>
Copy the code

Then we just need to add the following CSS:

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

.hero__content {
    display: contents;
}

.hero h2..hero img {
    order: -1;
}
Copy the code

If we use it right, display: contents can be a powerful technique for doing things that would have been a hassle just a few years ago.

Of course, we also need to deal with the desktop layout in the above example:

@media (min-width: 750px) {
    .hero {
        flex-direction: row;
    }
    
    .hero__content {
        display: initial;
    }
    
    .hero h2..hero img {
        order: initial; }}Copy the code

Rearrange the card components

In this example, we have different shapes of a card, one with the title above the picture and one with the title below the picture.

Let’s look at the HTML for this example:

<article class="card">
    <img src="thumb.jpg" alt="">
    <div class="card__content">
        <h3>Title</h3>
        <p>Description</p>
        <p>Actions</p>
    </div>
</article>
Copy the code

Notice that we only have two child elements, one image and one content text. Can we use this HTML code to put < H3 > at the top of the card component?

We can do this by floating it:

.card {
    position: relative;
    padding-top: 3rem;
    /* Leave room for the title */
}

.card h3 {
    position: absolute;
    left: 1rem;
    top: 1rem;
}
Copy the code

The problem is, when our title text is too long, this code needs to be adjusted again.

This problem exists because the header text is out of the normal flow of the document. So browsers don’t care if the title text is long or short when rendering component cards. At this point, we can solve the problem better by using diplay: contents:

.card {
    display: flex;
    flex-direction: column;
    padding: 1rem;
}

.card__content {
    display: contents;
}

.card h3 {
    order: -1;
}
Copy the code

This code allows us to control all the child elements and make them elastic, and then control the order in which they are displayed through the order attribute.

There is also a small problem, according to the design, our middle image should be attached to the border (covering the entire width), we solve this problem like this:

.card img {
	width: calc(100% + 2rem);
  margin-left: -1rem;
}
Copy the code

In the middle

When we talk about centralizing an element, you usually see a lot of jokes about it, which shows how complicated it can be. But now, it’s actually the easiest thing to center an element.

** Please do not use position: Absolute with transform. ** For an example, we can center a Flex element horizontally and vertically by using margin: Auto.

.card {
    display: flex;
}

.card__image {
    margin: auto;
}
Copy the code

I’ve written a complete guide in the middle before. [2]

Picture aspect ratio

The new CSS attribute aspect-ratio is already supported on most browsers. Sometimes, we use a hack technique like padding to make a container maintain a certain aspect ratio.

An 🌰 :

.card__thumb {
  position: relative;
  padding-top: 75%;
}

.card__thumb img {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
Copy the code

With aspect-ratio, it’s much easier:

.card__thumb {
  position: relative;
  aspect-ratio: 4/3;
}
Copy the code

You can also learn more about it in this article. [3]

Sometimes, withPosition: AbsoluteIt’s a better solution

Take a look at this example. We have a piece of content that needs to be superimposed on a card background. In short, there are two ways to achieve this:

  1. Used on card backgroundsposition: absolute.
  2. Give the content container a negative margin.

Let’s try each one to see what works best for this scenario:

Use a floating layout

Using this scheme, we can add a floating rectangle element, and then have the card content add a padding: 1rem.

.card__cover {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 50px;
}

.card__content {
    padding: 1rem;
}
Copy the code

Write it this way, even if the background is removed. We don’t need to change any other code, and the layout looks good:

Use negative margins

With this scheme, we don’t need to float any elements, just give the content element a negative margin:

.card__content {
    padding: 1rem;
    margin-top: -1rem;
}
Copy the code

When the background of the card is fully displayed, the effect is OK. But if the background is not loaded or removed, the whole card will be distorted:

You can see that the avatar is offset from its parent (the card container). To solve this problem, we need to change its CSS code when the background is not shown.

.card--no-cover .card__content {
    margin-top: 0;
}
Copy the code

It turns out that using position: Absolute is actually a better solution because it eliminates the need to write more compatibility code for additional scenarios.

Develop reading

  • Ryan Mulligan has written a great article on how to use CSS grids to superimpose content on elements. [4]
  • Stephanie Eckles also wrote a nice example of how to build a Hero section using a CSS Grid. [5]

🔗 Related links:

[1] : ishadeed.com/article/min…

[2] : ishadeed.com/article/lea…

[3] : ishadeed.com/article/css…

[4] : css-tricks.com/positioning…

[5] : moderncss. Dev / 3 – popular – w…