惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
V2EX
C
Check Point Blog
博客园 - Franky
月光博客
月光博客
T
Tenable Blog
博客园 - 聂微东
Cyberwarzone
Cyberwarzone
The GitHub Blog
The GitHub Blog
C
CERT Recently Published Vulnerability Notes
Scott Helme
Scott Helme
IT之家
IT之家
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
C
CXSECURITY Database RSS Feed - CXSecurity.com
F
Full Disclosure
博客园 - 司徒正美
Project Zero
Project Zero
Y
Y Combinator Blog
A
Arctic Wolf
美团技术团队
博客园 - 叶小钗
S
Securelist
F
Fortinet All Blogs
T
Threatpost
T
Threat Research - Cisco Blogs
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
酷 壳 – CoolShell
酷 壳 – CoolShell
MyScale Blog
MyScale Blog
大猫的无限游戏
大猫的无限游戏
Recorded Future
Recorded Future
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
S
Schneier on Security
Apple Machine Learning Research
Apple Machine Learning Research
L
LangChain Blog
P
Privacy International News Feed
博客园_首页
S
SegmentFault 最新的问题
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
腾讯CDC
P
Proofpoint News Feed
Cisco Talos Blog
Cisco Talos Blog
AWS News Blog
AWS News Blog
阮一峰的网络日志
阮一峰的网络日志
G
Google Developers Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Simon Willison's Weblog
Simon Willison's Weblog
雷峰网
雷峰网
P
Proofpoint News Feed
Latest news
Latest news
G
GRAHAM CLULEY

Ahmad Shadeed

Better fluid sizing with round() Use Cases for Field Sizing The Basics of Anchor Positioning Item Flow CSS Relative Colors Balancing Text In CSS Should masonry be part of CSS grid? CSS Grid Areas CSS Cap Unit An Interactive Guide to CSS Container Queries CSS :has() Interactive Guide CSS Nesting UX in DevTools CSS Nesting Future CSS: State Container Queries Rebuilding a comment component with modern CSS Conditional CSS with :has and :nth-last-child CSS Text balancing with text-wrap:balance CSS Masking Do we need CSS flex-wrap detection? My CSS Wishlist Conditional CSS CSS Style Queries Inside the mind of a frontend developer: Article layout Inside the mind of a frontend developer: Hero section CSS container queries are finally here The CSS behind Figma First Look At The CSS object-view-box Property Learn CSS Subgrid CSS :has Parent Selector Aligning Content In Different Wrappers Flexbox Dynamic Line Separator Hello, CSS Cascade Layers Building UI Components With SVG and CSS A Deep CSS Dive Into Radial And Conic Gradients Defensive CSS Building Real-life Components: Facebook Messenger Conditional Border Radius In CSS CSS Container Query Units Less Absolute Positioning With Modern CSS Aligning a Button Label Vertically Comparing Design Mockups To Code Result Using HSL Colors In CSS Custom Scrollbars In CSS Let CSS Container Queries For Designers The State of CSS Cross-Browser Development Overflow Issues In CSS Inspect Element As A Way To Increase Your Curiosity Handling Text Over Images in CSS Digging Into CSS Logical Properties Clipping Scrollable Areas On The inline-start Side Understanding Clip Path in CSS The Art of Building Real-life Components Handling Short And Long Content In CSS CSS Scroll Snap A Deep Dive Into CSS Grid minmax() CSS Variables 101 Finding The Root Cause of a CSS Bug Learn CSS centering How to detect browser support for Flexbox Gap CSS Mistakes While On Autopilot Digging Into the Flex Property Understanding CSS Multiple Backgrounds Aligning Logo Images in CSS Grid for layout, Flexbox for components Colors in CSS Thinking About The In-between Design Cases min(), max(), and clamp() CSS Functions Image Techniques On The Web Everything About Auto in CSS Learn Box Alignment Let Learn CSS Positioning Intrinsic Sizing In CSS CSS Grid Template Areas In Action Hiding Elements On The Web Creating a Variable Color Font From Scratch Building a Football Ticket With CSS and SVG Blending Modes in CSS CSS Variables With Inline Styles Implementing Dark Mode For My Website Rebuilding Apple Music Header in HTML & CSS Accessible Checkbox Layout Flickering On Browser Resize Enhancing The Clickable Area Size Custom Underlines with SVG Part 3: The Process of Implementing A UI Design From Scratch Part 2: The Process of Implementing A UI Design From Scratch Building An Old Nav Design CSS Flexbox: 5 Real World Use Cases I Used CSS Inline Flex For The First Time The Process of Implementing A UI Design From Scratch Common CSS Issues For Front-End Projects Handling Long and Unexpected Content in CSS How to Build Web Form Layouts With CSS Grid Grid Layout Ah-ha Moment Enhancing Our Components with CSS :empty Building Resizeable Components with Relative CSS Units CSS Writing Mode The Journey of Learning Front End Web Development on a Daily Basis
CSS display contents
Ahmad Shadeed · 2024-09-05 · via Ahmad Shadeed

The Layout Maestro

I spent years teaching CSS layout on this blog. I put everything I know into The Layout Maestro course: 70+ lessons and 150+ interactive examples that teach you how to think CSS layouts, not just memorize syntax.

Get the course

The problem

When building a layout in CSS, we apply a layout module like flexbox or grid to the container element, and its children will get affected. That is how it works normally.

Say that one of those child items is also a container with multiple items with their layout system. At some point, we might need the children of this child element to join their siblings in the upper container.

Let’s take an example to see how display: content works.

Example 1

In this example, I have a page header with a title and description.

<div class="page-header">
  <h1>Recent articles</h1>
  <p>A look at my recent articles about CSS</p>
</div>

When I change the display to contents for the parent element, the box element of the .page-header will be removed.

.page-header {
  display: contents;
}

When applying the above, the div isn’t there at all.

<!-- <div class="page-header"> -->
<h1>Recent articles</h1>
<p>A look at my recent articles about CSS</p>
<!-- </div> -->

Play with the following demo and activate the checkbox. Notice how the background, border, and padding are gone.

Recent articles

A look at my recent articles about CSS

Interesting, right?

You might be wondering, why this is even useful. Well, that’s why I wrote this article. Let’s extend the previous example to showcase why display: contents is useful.

Example 2

In the following example, we have a page header that contains a title and a link, then we have a description text underneath.

Here is the HTML markup:

<div class="page-header">
  <div class="page-header-row">
    <h2>Recent articles</h2>
    <a class="link" href="#">View all</a>
  </div>
  <p>A look at my recent articles about CSS</p>
</div>
.page-header-row {
  display: flex;
  justify-content: space-between;
}

Let’s suppose that we want to move the link to be directly under the description so the visual order in the UI will be like this:

  • Title
  • Description text
  • Link

Something like this:

One idea is to use position: absolute. The default design will be that all elements are stacked. However, this isn’t a scalable solution.

.link {
  position: aboslute;
  right: 0;
  top: 0;
}

Enter display: contents, it can solve the issue perfectly. What we need to do is to add display: contents to the main element that contains the title and link.

Try the demo below and activate the toggle.

<div class="page-header">

<div class="page-header-row">

<h2>Recent articles</h2>

<a class="link" href="#">View all</a>

</div>

<p>A look at my recent articles about CSS</p>

</div>

.page-header-row {

display: flex;

}

.link {

order: 2;

}

We have flattened the layout. Now the title and link belong to the .page-header. Here is the CSS:

.page-header {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

We can use order to place the link at the very end.

.link {
  order: 2;
}

Thinking of contents as ungrouping of elements

Try to right-click on the page header. This is what feels like to use display: contents.

Right-click on the page header and click ungroup.

Did you notice how the title and link are now ungrouped and not part of the box anymore? This is like providing us with a tool to do stuff we couldn’t do before while using CSS grid or flexbox.

Use cases for display contents

Styling generated HTML

In markdown, for example, when you add an image, the generated HTML looks like this:

<p><img src="image.jpg" alt="" /></p>

This can cause an issue if you want to style the <p>, like giving it a maximum width.

.content p {
  max-width: 60ch;
}

See the following example.

Coffee is one of the most beloved beverages in the world, cherished for its rich flavors and energizing qualities.

Cafés serve as hubs for conversation, creativity, and connection.

Noticed how the image is as equal as the paragraph? We can fix that!

Coffee is one of the most beloved beverages in the world, cherished for its rich flavors and energizing qualities.

Cafés serve as hubs for conversation, creativity, and connection.

<div class="content">

<p>

<img src="image.jpg" alt="" />

</p>

</div>

.content p:has(img) {

}

By using CSS :has(), I checked if there is an image inside a <p> and then used display: contents to fix that.

.content p:has(img) {
  display: contents;
}

Done.

Remove a container

In a header layout, we might need to contain the header in a fixed-width container. In the following HTML, the header’s content is in a container.

<div class="site-header">
  <div class="container">
    <h1 class="logo">Post title</h1>
    <div class="nav"><!-- Nav items --></div>
  </div>
</div>

To style the header layout, we should give the styles to the .container element.

.container {
  max-width: 1100px;
  margin-inline: auto;
}

.site-header .container {
  display: flex;
  justify-content: space-between;
}

What if we want to ignore the .container without altering the HTML? Well, that’s where display: contents comes in handy.

We can remove the container like so:

.site-header .container {
  display: contents;
}

Then move the layout for the .site-header element.

See the following interactive demo and notice how the HTML and CSS change when display: contents is active.

<div class="site-header">

<div class="container">

<h1 class="logo">Post title</h1>

<div class="nav">

</div>

</div>

</div>

.site-header .container {

display: flex;

justify-content: space-between;

}

.site-header {

}

In this example, we have a website header that contains:

  • a logo,
  • search,
  • and navigation.

Both the logo and search are grouped in a container.

Take a look at the HTML markup:

<header>
  <div class="header">
    <!-- Logo and Search -->
    <div class="logo-search">
      <a href="#" class="logo">iShadeed</a>
      <form action="#" class="search"></form>
    </div>

    <!-- Navigation -->
    <nav class="nav"></nav>
  </div>
</header>

At some point, we decided to reposition the search and center it. What are the options we have without display: contents?

.logo-search {
  flex: 1;
  display: flex;
  padding-right: 2rem;
}

.search {
  margin-left: auto;
}

Here is the result. While it looks “fine” at first glance, this can fail quickly. For example, if we increase the number of navigation items, it will be off-centered.

See the demo below and try to add or remove navigation items. Notice how it looks off-centered.

We can solve this by ungrouping the logo and search wrapper with display: contents.

.header {
  display: flex;
}

.logo,
.search,
.menu {
  flex: 1;
}

.logo-search {
  display: contents;
}

With that, the search now belongs to the header grid layout and it’s centered. Try to add or remove navigation items.

We can even reposition the search to be on the right side. Again, it now belongs to the header grid.

.search {
  order: 2;
}

It’s useful, isn’t it?

A grid of photos

In this example, we have a layout that consists of a figure with a group of photos and a caption. I’m using the <figure> element, see the following markup.

<figure>
  <div class="photo-group">
    <img src="thumb-1.jpg" alt="Thumbnail 1" />
    <img src="thumb-2.jpg" alt="Thumbnail 2" />
    <img src="thumb-3.jpg" alt="Thumbnail 3" />
  </div>
  <figcaption>At the summit...</figcaption>
</figure>

Here is how the layout looks.

At a certain viewport size, say that we want to achieve the following layout. How can we do that?

With the current HTML structure, it’s not possible to let the <figcaption> be part of the grid. Thanks to display: contents, we can ungroup the .photo-group element and make it work.

.photo-group {
  display: contents;
}

Now the HTML looks like this to the browser:

<figure>
  <!-- <div class="photo-group"> -->
  <img src="thumb-1.jpg" alt="Thumbnail 1" />
  <img src="thumb-2.jpg" alt="Thumbnail 2" />
  <img src="thumb-3.jpg" alt="Thumbnail 3" />
  <!-- </div> -->
  <figcaption>At the summit.</figcaption>
</figure>

We can then apply the new layout to the <figure> element.

figure {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.5rem;
}

Play with the demo below and toggle the display: contents checkbox. Keep your eye on the HTML and CSS and notice how they change accordingly.

<figure>

<div class="photo-group">

<img src="thumb-1.jpg" alt="Thumbnail 1">

<img src="thumb-2.jpg" alt="Thumbnail 2">

<img src="thumb-3.jpg" alt="Thumbnail 3">

</div>

<figcaption>Caption text goes here.</figcaption>

</figure>

.photo-group {

display: flex;

}

That’s it.

Alternating columns

I recently worked on a layout similar to this for a client project. At first glance, I thought it was a good candidate to use with CSS grid.

Personalized Plans

Get meal plans tailored to your goals and preferences.

Expert Recipes

Enjoy recipes crafted by nutritionists for balanced meals.

Easy Integration

Create shopping lists with a tap for easy ingredient buying.

Track Progress

Monitor your health journey with intuitive tracking tools.

Support

Connect with others, share experiences, and stay motivated.

Take a look at the HTML.

<section>
  <header>
    <h2>Why us?</h2>
  </header>
  <div class="features">
    <div class="features__item">
      <span class="icon"></span>
      <h3 class="title"></h3>
      <p class="desc"></p>
    </div>
    <!-- more items -->
  </div>
</section>

At first, I tried the following solution. The idea is to move the items on the right side down by 40px for each.

.features {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
}

.features__item:nth-child(2n) {
  transform: translateY(40px);
}

While this visually works, it can quickly fail if the content changes. In this variation, I made the content longer.

Notice how the grid items are stretched. This is a default behavior of CSS grid. I thought about changing the align-items for the main layout, but it didn’t work.

Personalized Plans

Get meal plans tailored to your goals and preferences.

Expert-Approved Recipes

Enjoy recipes crafted by nutritionists for balanced meals, designed to support your overall well-being daily. Enjoy recipes crafted by nutritionists for balanced meals, designed to support your overall well-being daily.

Easy Integration

Create shopping lists with a tap for easy ingredient buying.

Track Your Progress

Monitor your health journey with intuitive tracking tools, keeping you motivated and on the right path.

Support

Connect with others, share experiences, and stay motivated.

It’s not working. I thought about dividing the items into two groups, and later using display: contents to handle the layout.

<section>
  <header>
    <h2>Why us?</h2>
  </header>
  <div class="features">
    <div class="features__group"><!-- 3 items --></div>
    <div class="features__group"><!-- 2 items --></div>
  </div>
</section>

Try to increase the slider below, the items will still be vertically centered.

Personalized Plans

Get meal plans tailored to your goals and preferences.

Expert-Approved Recipes

Enjoy recipes crafted by nutritionists for balanced

Easy Integration

Create shopping lists with a tap for easy ingredient buying.

Track Your Progress

Monitor your health journey with intuitive tracking tools, keeping you motivated and on the right path.

Support

Connect with others, share experiences, and stay motivated.

On small sizes, we can simply ungroup the items and convert them to a normal grid.

.features {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 1rem;
}

.features__group {
  display: contents;
}

Try to resize the container below and notice how the items will ungroup at small sizes, thanks to display: contents.

Personalized Plans

Get meal plans tailored to your goals and preferences.

Expert Recipes

Enjoy recipes crafted by nutritionists for balanced meals.

Easy Integration

Create shopping lists with a tap for easy ingredient buying.

Track Progress

Monitor your health journey with intuitive tracking tools.

Support

Connect with others, share experiences, and stay motivated.

Quote

In this example, we have two separate layouts. One of a quote and the other contains a few features for a marketing website.

Coffee is more than just a drink; it’s an experience. From the first aromatic sip to the rich, lingering aftertaste, every cup tells a story of careful cultivation.

Rich Flavor Profile

Each cup offers a symphony of taste, from bold and robust.

Freshness Guaranteed

Our coffee is roasted to order, ensuring you enjoy the freshest beans.

Sustainable Sourcing

We are committed to ethical practices, sourcing our beans from farms that prioritize safety.

On small sizes, we want to reposition the quote and include it between the features.

Coffee is more than just a drink; it’s an experience. From the first aromatic sip to the rich, lingering aftertaste, every cup tells a story of careful cultivation.

Rich Flavor Profile

Each cup offers a symphony of taste, from bold and robust.

Freshness Guaranteed

Our coffee is roasted to order, ensuring you enjoy the freshest beans.

Sustainable Sourcing

We are committed to ethical practices, sourcing our beans from farms that prioritize safety.

Let’s learn how to do that. Here is the HTML markup:

<section class="layout">
  <div class="quote"></div>
  <div class="features-list">
    <!-- List of feature items -->
  </div>
</section>

First, we need to ungroup the features list by default.

/* Ungroup the items */
.features-list {
  display: contents;
}

.layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}

.features-item:nth-child(2) {
  order: -1;
}

Then, on larger viewport sizes, we group them again and change the main layout.

@media (min-width: 700px) {
  .layout {
    grid-template-columns: 1fr 1fr;
  }

  .features-list {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
  }

  .features-item:nth-child(2) {
    order: initial;
  }
}

In this example, we have a footer layout. For design purposes, two of the columns are in the same group. That means they aren’t below the main grid.

See the following demo and try to resize the layout. Do you notice that the group looks odd in small sizes?

Here is the CSS:

.footer {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 1rem;
}

What I want to do is to ungroup the element that contains the two footer items and let the items belong to the main grid. I want that to happen on:

  • larger sizes (when there is more space)
  • smaller sizes (when there is less space)

All we need to do is this:

@container footer (min-width: 700px) {
  .footer-group {
    display: contents;
  }
}

Toggle display: contents and see it in action.

This is a similar example to the above but it’s a bit more complex. In this layout, we have the following key items to note:

  • The main footer layout
  • Footer row
  • Footer group

See the following figure that maps the footer’s layout:

What we want to achieve is to move the last footer row into the grid, and also to ungroup the current “Footer group” element.

Here is a visual that shows what we need to achieve:

To achieve that, we will need to use display: contents multiple times. Here is the default styling:

.footer {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2rem;
}

.footer-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  gap: 1rem;
}

To achieve the layout, we need to apply the following CSS:

@container footer (min-width: 1200px) {
  .footer-row.top,
  .footer-group {
    display: contents;
  }

  /* The Footer already has grid, I just changed the cols. */
  .footer {
    grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  }
}

Here is a demo that shows in action. Try to hover over the legends to see each item.

Subgrid alternative

CSS subgrid became stable in all browsers in Sep 2023, and I wrote about it two years ago. We can use display: contents to mimic CSS subgrid.

See the following example where we have a post layout that contains:

  • a title,
  • and post content

Here is the HTML markup:

<div class="page">
  <h1>Post title</h1>
  <div class="prose">
    <!-- Content here -->
    <img src="thumb.jpg" alt="" />
  </div>
</div>

What I want to do is to make the image break out of its container. Something like the following example:

This is a title for the post

Espresso is the heart of any coffee experience. It's rich, bold, and packed with flavor.

A perfect shot in every cup.

The beauty of espresso lies in its simplicity—a blend of finely ground coffee and hot water.

First, here is the main layout grid:

.page {
  display: grid;
  grid-template-columns: 1fr 50ch 1fr;
  gap: 1rem;
}

Using CSS subgrid will look like this:

.prose {
  /* [1] Making the prose element take the full width. */
  grid-column: 1 / -1;
  display: grid;
  /* [2] Applying subgrid, inheriting the columns from the .page element. */
  grid-template-columns: subgrid;
  gap: 1rem;

  /* [3] Every item in .prose is in the middle column. */
  > * {
    grid-column: 2 / 3;
  }

  /* [4] The image is taking the full width. */
  img {
    grid-column: 1 / -1;
  }
}

While that works, you might not be able to use subgrid. We can use display: contents instead. The following CSS, it’s similar to the previous one but without any new grid definition.

.prose {
  display: contents;

  > * {
    grid-column: 2 / 3;
  }

  img {
    grid-column: 1 / -1;
  }
}

It’s interesting, right? Below is the display: contents version.

display: contents

Show columns

This is a title for the post

Espresso is the heart of any coffee experience. It's rich, bold, and packed with flavor.

A perfect shot in every cup.

The beauty of espresso lies in its simplicity—a blend of finely ground coffee and hot water.

A note on accessbility

CSS display: contents is known to cause accessbility issues when used with HTML tables, headings, buttons, and lists. Please make sure to avoid using it on such elements. Here are some further resources:

Outro

That’s it. CSS display: contents is a useful feature when you don’t have control over some parts of the HTML and can make you achieve things that aren’t possible without markup change. That’s being said, it’s important to make sure that you test for accessbility when using it with HTML elements like <nav>, for example.

Enjoyed the read? If you'd like to support my work, consider buying me a coffee. This interactive guide took 6 weeks, which translates to 54 coffee cups. Thanks a latte!

Support me on Ko-fi