The Dark Side of the Grid (Part 2)

posted on

CSS Grid layout is powerful and flexible. It's great for our development experience, but it may come at the cost of user experience and accessibility if we don’t use it responsibly.

This article series gives you an overview of potential implementation pitfalls; or, in other words, the dark side of the grid.

Overview

  1. What’s CSS Grid Layout? (in part 1)
  2. Name and theme of this article (in part 1)
  3. Pink Floyd Fun Fact 1 (in part 1)
  4. Compromising on Semantics (in part 1)
  5. Pink Floyd Fun Fact 2 (in this article)
  6. Changing Visual Order (in this article)
  7. Cross Browser Support
  8. Pink Floyd Fun Fact 3
  9. Whose responsibility is it?
  10. Pink Floyd Fun Fact 4

Changing Visual Order

In part 1, I addressed the issue with flattening document structures. Before we talk about changing visual order, let me enlighten you with more Pink Floyd wisdom.

Pink Floyd Fun Fact #2

In 1975, Pink Floyd helped to finance the movie Monty Python and the Holy Grail by the comedy group Monty Python. Led Zeppelin and Genesis were also amongst the investors.

Visual order

The CSS Grid Layout specification provides us with many ways of changing visual order. This flexibility is a nice thing to have but bad for accessibility if we don’t use it consciously.
Before we look at what’s possible with Grid, let’s briefly talk about visual order.

  1. Both tab order and the order in which screen readers read content follow DOM order.
  2. Changing visual order with CSS has no effect on DOM order.

No matter where we place items with CSS, keyboard users will still encounter elements in the order in which they appear in the HTML document.

Disconnect between content and presentation

If the visual order and the DOM order don’t match, it can irritate and confuse users up to a point where the experience is so bad that the site is unusable.

  1. Visual order concerns keyboard users because they may have trouble predicting where focus will go next.
  2. It may irritate users of screen magnifiers if the enlarged portion of the screen skips around a lot.
  3. If a blind user is working with a sighted user, who reads the page in visual order, it may confuse them when they encounter information in different order.

Manipulating visual order

When you’re applying any property that changes order, you should especially pay attention to testing with a keyboard. Use the Tab and Shift + Tab keys to test if your website is usable without a mouse.

This applies to all properties, not just to those associated with CSS Grid. 
However, I'll focus on manipulating order in grids.

Explicit placement

One of the most exciting features in Grid is the ability to place grid items anywhere inside or outside of the grid.

Visualization of the lines in a 3 by 2 grid

You can place items explicitly by defining on which line they start or end. For vertical lines, there’s grid-column-start, grid-column-end, or the shorthand grid-column. For horizontal lines, grid-row-start, grid-row-end, and grid-row. These rules allow a line number (either positive or negative) or the span keyword.

<div class="grid">
  <div class="grid__item">Item 1</div>
  <div class="grid__item">Item 2</div>
  <div class="grid__item">Item 3</div>
  <div class="grid__item">Item 4</div>
</div>

The order in the DOM in this grid is Item 1 - 2 - 3 - 4.

.grid {
  /* Draw a grid...*/
  display: grid;
  /* with 2 200px columns. */
  grid-template-columns: repeat(2, 200px);
  /* Make all rows 100px high... */
  grid-auto-rows: 100px;
  /* and add 15px space between cells. */
  grid-gap: 15px;
}

/* Place item on the second horizontal and vertical line */
.grid__item:nth-child(2) {
  grid-column: 2;
  grid-row: 2;
}

/* Place item on the first horizontal and vertical line */
.grid__item:nth-child(4) {
  grid-column: 1;
  grid-row: 1;
}

The order in the DOM is still Item 1 - 2 - 3 - 4 but the visual order is now Item 4 - 1 - 3 - 2.

Explicit placement might create a mismatch between DOM order and visual order.

Auto flow

Explicitly placing items that have the same size is one thing, but placing differently sized items may have unexpected side effects with Grids default auto-placement algorithm. The combination of explicit and implicit placement sometimes results in unwanted gaps between grid items. This is because of the default placement algorithm only ever moving forward when placing items and never backtracking to fill holes.

.grid {
  display: grid;
  grid-template-columns: repeat(3, 100px);
  grid-auto-rows: 40px;
  grid-gap: 20px;
}

/* You can use the span keyword to make items span multiple columns or rows. */
.item:nth-child(1) {
  grid-row-end: span 3;
}
.item:nth-child(3) {
  grid-row-end: span 3;
}
.item:nth-child(4) {
  grid-column: span 2;
}
.item:nth-child(5) {
  grid-row: span 2;
}
.item:nth-child(7) {
  grid-column: 2 / span 2;
}
.item:nth-child(8) {
  grid-column: 2 / span 3;
}
.item:nth-child(9) {
  grid-row: span 2;
}

This can give your designs a nice touch, but it can also annoy. Grid's default auto-placement algorithm can be changed by switching from a “sparse” to a “dense” packing mode using the grid-auto-flow property.

.grid {
  display: grid;
  grid-template-columns: repeat(3, 200px);
  grid-auto-rows: 70px;
  grid-gap: 20px;

  grid-auto-flow: dense;
}

This is a dream come true but the advantage of the default packing mode is that order stays intact which isn’t guaranteed with grid-auto-flow: dense;.

Play with the auto-flow demo on CodePen or read more about grid-auto-flow on MDN.

AaahhmmmImplicit, explicit, whatisit?

If you’re not sure what I mean by explicit and implicit, head over to CSS-Tricks and read about the difference between explicit and implicit grids.

Order

You might already be familiar with the order property because it has been around since Flexbox. order can be set to any integer value to change the ordering of flex items. This also works with grid items.

.grid {
  display: grid;
  grid-template-columns: repeat(2, 200px);
  grid-auto-rows: 100px;
  grid-gap: 1.4rem;
}

.grid__item:nth-child(2) {
  order: 4;
}

.grid__item:nth-child(4) {
  order: 1;
}

See the order property demo on CodePen or read more about the order property on MDN.

Absolute positioning

It’s possible to combine absolute positioning and explicit placement. I haven’t found a use case yet in any of my projects but it’s interesting how a positioned item behaves inside a grid.

In the following 3 by 2 grid with 6 items I’m moving the second item from line 2 to line 3 by applying grid-column: 3. Since I haven’t set the row property explicitly and I haven’t placed other items, all subsequent items move one cell with the explicitly placed item. DOM order still matches visual order.

ul {
  display: grid;
  grid-template-columns: repeat(3, 200px);
  grid-auto-rows: 100px;
  grid-gap: 1.4rem;
}

li:nth-child(2) {
  /* Move the second item to the third vertical line */
  grid-column: 3;
}

Now let’s see what happens if we add absolute positioning to the mix and position the item in the top left corner relative to its parent.

ul {
  position: relative;
}

li:nth-child(2) {
  grid-column: 3;

  position: absolute;
  left: 0;
  top: 0;
}

Positioned grid items lie on top of other items just like any absolute positioned element. They don’t affect the position of other items, and they’re completely ignored during auto-placement.\
This is where it gets interesting: setting left and top to 0 doesn’t place them in the top left corner of their parent item (the ul) but in the cell they’re placed in.

Again, this may cause a disconnect between content and presentation.

Areas

Time to make a confession: I’m deeply in love with Grid Areas ❤️. I’ll share my intense feelings about this property in another article soon. Areas are awesome, but nobody is perfect and since this article deals with the dark side of things, let’s see what may go wrong.

The grid-template-areas property does 2 things. 
First, it describes layouts in CSS visually. If you have a 2-column layout with a header, content, sidebar and a footer, this is what it may look like in CSS.

.layout {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-areas:
    'header header'
    'content sidebar'
    'footer footer';
}

Second, it defines named grid areas where any grid item may be placed inside.

Let’s take this example where the source order is wrong.

<body>
  <footer>Footer</footer>

  <header>Header</header>

  <main>Main</main>
</body>

The source order of these elements is footer - header - main, where it should be header - main - footer. The correct approach to fixing this is to change the order in HTML but with Areas we don’t have to.
All it takes is to define the correct layout in CSS and then place the elements in the respective area using the grid-area property.

body {
  display: grid;
  /* Three single column, single row areas */
  grid-template-areas:
    'header'
    'content'
    'footer';
}

footer {
  /* place the footer in the third row/area */
  grid-area: footer;
}

header {
  /* place the header in the first row/area */
  grid-area: header;
}

main {
  /* place the main element in the second row/area */
  grid-area: content;
}

This is so simple, so beautiful, yet so dangerous. It looks right, but the order is only correct on the surface because the source order is still the same.

Recap

None of these features are bad, but just they may affect an important part of the user experience negatively. If you’re changing visual order, be aware of the effects it may have.

  • Test your components with the keyboard by pressing Tab or Shift + Tab for the opposite direction.
  • Make sure that visual order is comprehensible and that it matches DOM order as good as in any way possible.
  • Test on different devices and screen sizes. People also use keyboards on mobile devices.
  • You can use a tool Like Accessibility Insights to visualize and better understand the tab order of your web pages.
Accessibility Insights tracks all the tab stops you make when you press the tab key.

Thanks for reading. ❤️

The last part of this series, "Cross Browser Support", will be published soon.

PS: Thanks to Juho and E.J. for proofreading.