CSS Grid Magic: A practical card example from my An Event Apart talk.
Morten Rand-Hendriksen
AI & Ethics & Rights & Justice | Educator | TEDx Speaker | Neurodivergent System Thinker | Dad
Today I’m speaking at An Event Apart Spring Summit, an online conference featuring some of the best and brightest in the web industry, and myself. In my talk, I’m discussing modern CSS layout modules and how to incorporate them into our practices and workflows. Part of the talk centers on a practical example I built to demonstrate just how different the world of web layouts has become now that we have CSS grid, and I thought I’d share the example and some more details on it here as well. To see the full talk, head on over to An Event Apart (since it’s an online conference, you can buy tickets even after the event and get immediate access to all the content.)
A card
The example is based on a post in a web design forum I follow. There was a card design and a question: How do I make this layout using CSS?
The standard answer to this is to add some extra nesting <div>s and use various layout hacks like negative margins, positioning, etc.
When I see examples like this, my first questions are always the same:
What is the simplest HTML representation of this content? Can I make this design happen using pure CSS without modifying the HTML?
This is the goal of web development after all: The complete separation of content (HTML) from its presentation (CSS).
Looking at the design, there is an image, and some text. One way of representing this in HTML is using a <figure> wrapped around an <img> and a <figcaption>. Here’s what that looks like:
<figure class="card">
? <img src="…" width="640" height="640" alt=""/>
? <figcaption>
? ? <blockquote>We shape our tools (…)</blockquote>
? ? <cite>Attributed to Winston Churchill, Marshall McLuhan(…)</cite>
? ? <p><strong>Caju Gomes</strong>, <a href="…">Unsplash</a></p>
? </figcaption>
</figure>
That solves the content part. Now for the presentation.
Layered layouts
The real challenge in this design is the many layers. There’s a faux shadow, then the card itself which serves as a background image. Then there is the image, the text which overlaps the image, and the image credit.?
Stacking elements one on top of the other using CSS is traditionally achieved through clever hacks using either positioning, negative margins, or both. However, CSS grid allows us to place any child element of the grid anywhere on the grid, including in the same cell or cells as other elements. So if we need to stack elements on top of one another like in this design, CSS grid lets us do that in a clean and controlled way.
The first step is to figure out what the grid should look like:
Next, mark up that grid in CSS. The <figure> becomes the grid parent:
figure {
? display: grid;
? grid-template-columns: 3em 9em 1fr 1fr 6em 3em;
? grid-template-rows: 3em 9em 1fr 6em 3em;
}
With the grid defined, place items on that grid. There are many ways of doing this, the easiest of which is to just position the times based on the grid line numbers. First the image:
img {
? grid-column: 4/5;
? grid-row: 3/4;
}
Then the text which sits inside the figcaption:
figcaption {
? grid-column: 3/5;
? grid-row: 3/4;
}
This puts the text in the correct general area of the grid, and on top of the image, but the design calls for more control over the exact placement of the text.
To achieve this, add a second grid for the <figcaption> element and position the items within on that grid:
figcaption {
? grid-column: 3/5;
? grid-row: 3/4;
? display: grid;
? grid-template-columns:?3fr 1fr 2fr;
}
That gives us the image and the text, but the design also calls for a background image and a faux shadow. What to do...
Pseudo elements as grid elements.
An interesting of both grid and flex is pseudo elements become first-level children of these containers. That means if you create a grid container around a single item and then define ::before and ::after pseudo elements for that item, the grid now has three grid items: the element itself and its before and after pseudo-elements, and each will be placed in its own grid cell. This opens up an interesting new possibilities, one of which can be used to solve the current layout problem.
The design calls for both a background image and a faux shadow.
Using pseudo elements both of these can be added in a non-destructive way that will gracefully degrade to older browsers without grid support.
The background image first. For this I’ll use ::before because it should appear before the actual card content in the DOM order. Place the new (pseudo) grid item on the existing grid, and then fill it with a background image:
figure::before {
? grid-column: 2/7;
? grid-row: 2/6;
? content: "";
? display: block;
? background: white;
? background-image: url(…);
? background-size: cover;
}
Next, the faux shadow. I’ll have to use ::after for this, which technically places the shadow after the card in the DOM order and therefore ends up on top of the card.
figure::after {
? grid-column: 1/6;
? grid-row: 1/5;?
? content: "";
? display: block;
? background: #e0dde4;
}
That’s easily solved by moving the item along the z-axis using z-index:
figure::after {
? grid-column: 1/6;
? grid-row: 1/5;
? z-index: -1; ?
? content: "";
? display: block;
? background: #e0dde4;
}
This last part is another important feature of CSS grid. Because grid allows us to stack things on top of each other, we are effectively working in the 3rd dimension; literally layering content on top of content like a stack of cards. This layering is done based on DOM order, so the last item ends up on top of the stack. We can use the z-index property to change this order to whatever we want. In other words, with Grid we are now for the first time able to lay out content along three axes: horizontal, vertical, and depth.
Backwards compatibility
At this point there’s a good chance you’re thinking something along the lines of “Sure, this is cool, but grid doesn’t work in older browsers, so you need to provide fallback options.”
Surprisingly, the answer is no.
This setup doesn’t require any accommodations for older browsers.
You can test it for yourself. Open the example in your favorite browser, go to the developer tools, and disable the two instances of display:grid.
What you get is the main image followed by the text. The faux shadow and background image disappear as if by magic, and the visitor gets only the content they are looking for, styled in a way that makes sense in their environment.
The “magic” at play here is basic progressive enhancement: Take advantage of whatever capabilities the current browser has without reducing the experience for those without those capabilities.?
Of course we could use @supports to detect grid support and build out custom styles for browsers without grid support. If I published this on a real website, I would wrap the ::before and ::after rules using @supports to prevent older browsers from downloading the background image. But, as you see this is not necessary unless you want to add specific fallback styles.
The standard argument against this approach is that the result of doing nothing will be different users having different experiences. From my perspective, that's an argument with little substance: We have used responsive web design since 2010, so for the past 10+ years we've been shipping websites that look different on different screens. This is no different, except the responsive axes have been expanded to include not only viewport width and height but also browser capability.
At the very onset of this project I defined semantic HTML, and I said I didn’t want to make any changes to the HTML to accommodate the style. Thus every browser will get semantic parsable content that makes sense. Then I use CSS to progressively enhance the experience when grid is available in the visiting browser.
So what happens to that faux shadow and background image? Inspect your code and you’ll see when grid is disabled, they each collapse to a height of 0px. Normally when we add pseudo elements using ::before and ::after we set their width and height which would result in them showing up when grid is not present, but since I’m using grid, that’s not necessary: Given no instructions, grid’s default treatment of grid items is to stretch them to fit the cell they are presented in. As a result, the faux shadow and background image automatically stretch to fill the grid cells, and collapse to a height of 0 when no grid is present.
CSS layouts are now a platform feature
For as long as I can remember, CSS layouts were always a collection of carefully crafted hacks. That time is now over. Between grid, flex, float, multicol, and table, we have all the layout features we need built into the platform to make the layouts we want without having to modify our markup or employ clever hacks. Now it's our job to realign our way of thinking about layouts to use these modules in our daily practice. I built this example to give you a brief glimpse of what is possible and how these new modules open doors we didn't even know existed before. I invite you to join me as we explore this new future of CSS layouts together.
For more examples, and talks from many of the leading voices of our industry, head on over to An Event Apart and sign up for the 2021 Spring Summit!
--
Morten Rand-Hendriksen is a Senior Staff Instructor at?LinkedIn Learning?(formerly Lynda.com) focusing on?front-end web development and the next generation of the web platform. He spends a lot of his time figuring out how to make complicated things easier using new technology.
The talk in Paris 2017 at WordCamp was amazing! :)
Student at University of Chittagong
3 年Useful article. ??
Reporter @ Washington Ledger | Aspiring Full Stack Developer (React) | WordPress
3 年I thought css grid has been around for a while?
Anv?ndarupplevelse och Produktdesigner | Kravanalytiker | Drivs av en passion f?r att skapa inkluderande och tillg?ngliga designl?sningar
3 年Thank You! Useful! ??
Corporate antimatter · Change Management at journaway
3 年I’m terribly behind on CSS because it isn’t part of my day job anymore, so thanks for this fascinating example! Progressive enhancement still is chronically undervalued imho. Explanations like this one hopefully will help to get the point across to a wider audience.