GRID PILE: Stacking CSS Grids for Impossible Layouts

GRID PILE: Stacking CSS Grids for Impossible Layouts

CSS Grid is a revolutionary new layout module which allows us to work with the browser viewport as a true grid-based layout and design surface for the first time.

The immediate challenge all designers and developers face when working with CSS Grid is p.t. only the direct first-level descendants of a grid container can be placed on the grid. That means if you want to place an item within a grid, you have to make that item a first-level descendant of the container. This in turn can lead to aggressive "flattening" of the semantic markup which is bad for accessibility, semantics, and the web as a whole.

The good news is there are some nifty ways around this problem. One I've been experimenting with over the past 6 months is what I refer to as a "grid pile" - literally stacking multiple matching grids on top of one another to create the appearance of a single grid where multiple grids are at play. Go to CodePen to see a live demo.

The Challenge

We'll start with a standard semantic HTML document with clear hierarchical structure:

<a class="skip-link screen-reader-text" href="#content">Skip to content</a>

<header class="masthead">
  <div class="logo">GRID PILE</div>
  <h2 class="site-title">Stacked CSS Grid Effect</h2>
</header><!-- .masthead -->

<main id="content" class="main-area">
  <figure class="feature">
    <img src="https://source.unsplash.com/w3lQVmuK8fw/1200x600" alt="Fox.">
  </figure>
?
  <section class="splash">
    <div class="splash-content">
      <h2 class="content-title">Magical content restructuring with CSS Grid stacks</h2>
      <div class="splash-text">
        <p>This example demonstrates how to reposition content in a view by stacking multiple grids on top of one another. The goal of the example is to retain a logical markup structure while still allowing content to be split up and positioned on the grid. Case in point: From a HTML semantics standpoint, the main area should come before the sidebar area, and both should live on the same level without extra wrappers. This demo shows how stacking two grids allows the sidebar items to appear as if they are placed on the same grid as the main content while they are in reality placed in a separate grid positioned within and superimposed on top of the original grid.</p>
      </div><!-- .splash-text -->
    </div><!-- .splash-content -->
  </section><!-- .splash -->

  <section class="more">
    <div class="more-content">
      <h2 class="content-title">Subgrid could be nice here</h2>
      <p>The solution provided here is limited by the lack of support for subgrids. If subgrids were available, the solution would be more refined. That said, the lack of subgrid was what brought me to this solution, and it has practical applications well outside of the current demo. So maybe not having subgrid has opened the door to other more interesting opportunities?<p>
    </div><!-- .more-content -->
  </section><!-- .more -->

</main>

<aside class="sidebar">
  <div class="twin">
    <h2 class="content-title">First Sidebar Item</h2>
    <p>This is the first of two sidebar sections. It appears as the first of two items in the sidebar <code>aside</code> element which is located after the <code>main</code> element in the HTML source order. On narrow screens it visually appears after the <code>main</code> element. On medium screens it appears at the top of the left-hand sidebar. On wide screens it appears at the top of the left-hand sidebar.</p>
  </div>
  <div class="twin">
    <h2 class="content-title">Second Sidebar Item</h2>
    <p>This is the second of two sidebar sections. It appears as the second of two items in the sidebar <code>aside</code> element which is located after the <code>main</code> element in the HTML source order. On narrow screens it visually appears after the first sidebar item. On medium screens it appears as the second item of the left-hand sidebar. On wide screens it appears at the top of the right-hand sidebar.</p>
  </div>
</aside><!-- .sidebar -->

<footer class="colophon">
  <aside>All photos: <a href="https://unsplash.com/@andozo" target="_blank" rel="nofollow">Andreas R?nningen</a>.</aside>
  <aside>Content, layout, design: <a href="https://twitter.com/mor10" target="_blank" rel="nofollow">Morten Rand-Hendriksen</a>.</aside>
</footer>

Within each of the core elements (header, main, aside, footer) there may be multiple sub-items. Of relevance to this example are two sidebar items, one with a blue background, one with a peach background.

In the design for this layout, the sidebar items are positioned on the opposite sides of the main content on wider screens.

This design seems a perfect candidate for CSS Grid, but there's a problem: The sidebar items are descendants of the aside with the class "sidebar" and can therefore not be independently placed on the main grid. As far as the grid is concerned, they are just content inside the sidebar grid item.

Here's what the main grid CSS looks like as we get started:

/*--------------------------------------------------------------
CSS Grid layout for modern browsers:
--------------------------------------------------------------*/

@supports (grid-area: auto) {

	@media screen and (min-width: 65em) {

		.site {
			max-width: none;
			display: grid;
			grid-template-columns: 15em auto 15em;
		}

		.masthead {
			grid-column: 1/4;
		}

		.main-area {
			grid-column: 2/3;
		}

		.sidebar {
			grid-column: 1/2;
			grid-row: 2/4;
		}

		.colophon {
			grid-column: 1/4;
		}

	}

}

The Solution

To resolve this challenge we have to make it appear as if the sidebar items are placed on the main grid even though they are not. To do that we turn the sidebar into a grid container, match the column layout of the main container, and superimpose the sidebar container on top of the main grid. Let's break that down.

First, make sidebar a grid container inheriting the grid layout of its parent:

.sidebar {
  display: inherit;
  grid-template-columns: inherit;
}

Next, position the sidebar items in the first and last columns on the new grid (the first item positions itself automatically so we only need to worry about the second one):

.sidebar {
  display: inherit;
  grid-template-columns: inherit;
}

.twin:last-of-type {
  grid-column: 3/4;
}

Then position the sidebar itself before the main content in the main grid:

.sidebar {
  display: inherit;
  grid-template-columns: inherit;
  grid-column: 1/4;
  grid-row: 2/3;
}

.twin:last-of-type {
  grid-column: 3/4;
}

At this point the sidebar items appear to be placed correctly, but the main content is being pushed down to the row below the sidebar element. To fix this, move the main container up to the same row as the sidebar:

.main-area {
  grid-row: 2/4;
}

.sidebar {
  display: inherit;
  grid-template-columns: inherit;
  grid-column: 1/4;
  grid-row: 2/3;
}

.twin:last-of-type {
  grid-column: 3/4;
}

Here we take advantage of CSS Grid's unique ability to place several different items in the same grid cell. From the browser's standpoint we now have a center cell containing both the main element and the sidebar element, and because the center cell of the sidebar element is empty, the main element shines through. Which leaves us with one problem to fix: The main content is not selectable. This is because the main element is earlier in the source order of the HTML document and therefore lower in the stack - in other words the sidebar is sitting on top of it. To fix this we simply move the main element up in the stack using z-index:

.main-area {
  grid-row: 2/4;
  z-index: 1;
}

.sidebar {
  display: inherit;
  grid-template-columns: inherit;
  grid-column: 1/4;
  grid-row: 2/3;
}

.twin:last-of-type {
  grid-column: 3/4;
}

Result

Go to CodePen to see a live version of this demo.

What we end up with here is something rather interesting: Because the sidebar and main content both sit in the same row, the height of one will automatically be applied to the other. The result is an equal-height column layout where the items in the different columns are not actually related, they just appear to be. Meanwhile the source order and semantic structure of the original document is maintained so the reordering is purely visual.

That last part begs and obvious question: Isn't one of the rules of CSS Grid to not break accessibility by messing around with the visual source order of elements? The answer is yes, and I wouldn't necessarily recommend using this example on a production site. This example was put together to demonstrate an extreme reordering situation, but the approach used - of stacking multiple grids and using empty grid cells to reveal the content underneath - has many practical real-world applications.

--

Morten Rand-Hendriksen is a Senior Staff Instructor at LinkedIn Learning and Lynda.com focussing on front-end web development and a sessional instructor of Interaction Design at Emily Carr University of Art and Design.

Peter Kristensen

Digital Designs baked on a foundation of UX coated with UI, decorated with beautiful visuals and delightful experiences.

6 年

Nice, have you tried messing around with the for form of an tag like skewing and such, and then stacking a top of each other?

回复
Dario Passariello

Senior Solution Architect | dreamer and more...

6 年

my website https://www.dariopassariello.it with a massive use of css... made by me!

回复
Ibtihal Ouedghiri

Motion designer og Illustrator som studerer p? Interaktivt design DMJX i K?benhavn.

7 年

awesome :D

Leonardo J. Caballero G.

Speaker | Python | Plone | Odoo | Developer | Software Integrator

7 年
Zine-el abidine Radoua

Développeur front-end chez VOID | Expert en Css, Js, React, Design Systems, Drupal theming, Accessibilité Web (WCAG), Web performance

7 年

Nice hack!!

要查看或添加评论,请登录

社区洞察

其他会员也浏览了