CSS - Layout

Hello subgrid!

We have had Grid Layout in browsers for two years. Long enough for us to start to find the edges, and discover things we really wish it could do. The biggest missing feature from Level 1 was subgrid, which has become the main feature for Level 2 of the specification. In this talk I’ll introduce subgrid, with use cases, example code and some thoughts on where we might see Grid going in the future.

[…]

Resources

The following resources were mentioned during the presentation or are useful additional information.

Slides and code examples can be found in the source link.

Equal height rows in CSS Grid Layout

Great answer by Michael_B on Stack Overflow:

Short Answer

If the goal is to create a grid with equal height rows, where the tallest cell in the grid sets the height for all rows, here’s a quick and simple solution:

  • Set the container to grid-auto-rows: 1fr

How it works

Grid Layout provides a unit for establishing flexible lengths in a grid container. This is the fr unit. It is designed to distribute free space in the container and is somewhat analogous to the flex-grow property in flexbox.

If you set all rows in a grid container to 1fr, let’s say like this:

grid-auto-rows: 1fr;

… then all rows will be equal height.

It doesn’t really make sense off-the-bat because fr is supposed to distribute free space. And if several rows have content with different heights, then when the space is distributed, some rows would be proportionally smaller and taller.

Except, buried deep in the grid spec is this little nugget:

7.2.3. Flexible Lengths: the fr unit

When the available space is infinite (which happens when the grid container’s width or height is indefinite), flex-sized (fr) grid tracks are sized to their contents while retaining their respective proportions.

The used size of each flex-sized grid track is computed by determining the max-content size of each flex-sized grid track and dividing that size by the respective flex factor to determine a “hypothetical 1fr size”.

The maximum of those is used as the resolved 1fr length (the flex fraction), which is then multiplied by each grid track’s flex factor to determine its final size.

So, if I’m reading this correctly, when dealing with a dynamically-sized grid (e.g., the height is indefinite), grid tracks (rows, in this case) are sized to their contents.

The height of each row is determined by the tallest (max-content) grid item.

The maximum height of those rows becomes the length of 1fr.

That’s how 1fr creates equal height rows in a grid container.


Why flexbox isn’t an option

As noted in the question, equal height rows are not possible with flexbox.

Flex items can be equal height on the same row, but not across multiple rows.

This behavior is defined in the flexbox spec:

6. Flex Lines

In a multi-line flex container, the cross size of each line is the minimum size necessary to contain the flex items on the line.

In other words, when there are multiple lines in a row-based flex container, the height of each line (the “cross size”) is the minimum height necessary to contain the flex items on the line.

Breaking Out with CSS Grid Layout

If you’ve ever needed to have a content column with a maximum width, but have specific elements span the full width of the viewport, thus breaking out of the column, CSS Grid can do this really simply. First the grid on the container:

Code language: CSS

.content {
  display: grid;
  grid-template-columns: 
    [full-start] minmax(1em, 1fr) 
    [main-start] minmax(0, 40em) [main-end]
    minmax(1em, 1fr) [full-end];
}

This defines the columns, including the max width of the content, and a gutter. For normal content:

Code language: CSS

.content > * {
  grid-column: main;
}

Then for items to span the full viewport:

Code language: CSS

.content__full {
  grid-column: full;
}

See the source link for more details.

Breaking the Grid

Dave Rupert has encountered some confusing not-actually-bugs with grid item widths in CSS Grid, and provided this as a fix:

Code language: CSS

/*
 _______  ___   _______    _______  ______    ___   ______  
|       ||   | |       |  |       ||    _ |  |   | |      | 
|    ___||   | |_     _|  |    ___||   | ||  |   | |  _    |
|   |___ |   |   |   |    |   | __ |   |_||_ |   | | | |   |
|    ___||   |   |   |    |   ||  ||    __  ||   | | |_|   |
|   |    |   |   |   |    |   |_| ||   |  | ||   | |       |
|___|    |___|   |___|    |_______||___|  |_||___| |______| 
by Dave Rupert
Read More: https://daverupert.com/2017/09/breaking-the-grid/
*/
 
/* 
 * Remove `min-width: auto` from Grid Items
 * Fixes overflow-x items.
 */
.fit-grid > * { min-width: 0; }
 
/* Apply max-width to Replaced Elements and Form controls */
.fit-grid img,
.fit-grid video,
.fit-grid audio,
.fit-grid canvas,
.fit-grid input,
.fit-grid select,
.fit-grid button,
.fit-grid progress { max-width: 100%; }
 
/* Make file and submit inputs text-wrap */
.fit-grid input[type="file"],
.fit-grid input[type="submit"] { white-space: pre-wrap; }
 
/* Fix Progress and Range Inputs */
.fit-grid progress,
.fit-grid input[type="range"] { width: 100%; }
 
/* Fix Number Inputs in Firefox */
@supports (--moz-appearance: none) {
  .fit-grid input[type="number"] { width: 100%; }
}

Is it really safe to start using CSS Grid Layout?

The really short version? Absolutely. Slightly longer version:

Grid is new! Surely it has terrible browser support?

CSS Grid Layout shipped into Chrome, Firefox, Opera and Safari in March of this year. Microsoft Edge currently have an updated Version of Grid available behind a flag in Preview builds. At the time of writing, Can I Use indicates a global availability of CSS Grid Layout of 65.64%, rising to 70.75% if you include the prefixed version in IE10, 11 and current Edge. This is a rate of adoption we’ve never seen before for such a huge feature. It isn’t surprising that people don’t realise how many visitors will have support.

[…]

And non-supporting browsers?

CSS has the solution for you. To start with, defined in the Grid and Flexbox specifications are exactly how those specifications overwrite older layout methods.

Therefore if you want to use floats, inline-block, multiple-column layout, flexbox or even display: table as a fallback for your grid layout then the spec has you covered. You can overwrite those methods in a safe and predictable way. I made a cheatsheet explaining the fallbacks. I also cover several of these in my talk which was recorded at Render Conference earlier this year.

CSS also has Feature Queries. These have really great browser support, and the nice thing about Feature Queries is that you don’t need to concern yourself with the browsers that don’t support feature queries. There is no browser supporting Grid Layout and not supporting Feature Queries.

[…]

Generally you will then have a few things in the fallback CSS that will “leak through” to the grid layout. This is often widths on items as we need to assign widths to items in legacy layout to fake something that looks like it is using a grid. Therefore we use a simple feature query, checking for support of Grid Layout, and there we perhaps set widths back to auto.

Old browsers are not your fault, but they are your responsibility

The fact old browsers exist is not your fault. Don’t start these discussions by acting as if it is your failing that you can’t get the site looking identical in all browsers released in the last 10 years, while using technology only released this year. It’s not your fault, but it is your problem. It is your problem, your responsibility as a web professional to get yourself into a position where you can take the right course of action for each project.

Using CSS Transitions on Auto Dimensions

We’ve all been there. You’ve got an element you want to be able to collapse and expand smoothly using CSS transitions, but its expanded size needs to be content-dependent. You’ve set transition: height 0.2s ease-out. You’ve created a collapsed CSS class that applies height: 0. You try it out, and… the height doesn’t transition. It snaps between the two sizes as if transition had never been set. After some fiddling, you figure out that this problem only happens when the height starts out or ends up as auto. Percentages, pixel values, any absolute units work as expected. But all of those require hard coding a specific height beforehand, rather than allowing it to naturally result from the size of the element content.

[…]

If you were hoping I had a magical, complete solution to this problem, I’m sorry to disappoint you. There’s no one solution that achieves the desired effect without downsides. There are, however, multiple workarounds that each come with a different set of advantages and disadvantages, and in most use cases at least one of them will get the job done in an acceptable manner. I’ll outline the major ones, and list out their ups and downs so you can hopefully pick the best one for your situation.

[…]

Technique 1: max-height

If you web search this problem, the max-height approach will probably be mentioned in all of the first five to ten results. It’s actually pretty unideal, but I thought it was worth including here for the sake of comparison.

It works like this: CSS values can only be transitioned to and from fixed unit values. But imagine we have an element whose height is set to auto, but whose max-height is set to a fixed value; say, 1000px. We can’t transition height, but we can transition max-height, since it has an explicit value. At any given moment, the actual height of the element will be the maximum of the height and the max-height. So as long as max-height’s value is greater than what auto comes out to, we can just transition max-height and achieve a version of the desired effect.

[…]

Technique 2: transform: scaleY()

[…]

Implementation works like this: we set a transition for the element’s transform property, then toggle between transform: scaleY(1) and transform: scaleY(0). These mean, respectively, “render this element at the same scale (on the y axis) that it starts out at” and “render this element at a scale of 0 (on the y axis)”. Transitioning between these two states will neatly “squish” the element to and from its natural, content-based size.

Technique 3: JavaScript

Managing a CSS transition in CSS would be ideal, but as we’re learning, sometimes it just isn’t entirely possible.

If you absolutely have to have smoothly collapsing sections, whose expanded size is completely driven by their content, and which other elements on the page will flow around as they transition, you can achieve that with some JavaScript.

The basic strategy is to manually do what the browser refuses to: calculate the full size of the element’s contents, then CSS transition the element to that explicit pixel size.

CSS Grid Guides on MDN

A few weeks ago I was approached by the good people over at MDN, asking if I would be interested in writing a comprehensive resource for MDN on CSS Grid Layout. A series of guides to explain the grid specification with examples - ready for Grid shipping in Firefox 52 today.

I think that MDN is the best place for references and increasingly for learning web development, therefore I was very keen to add to that material and to create a learning resource for grid. Over the last couple of weeks I have written a book-worth of words and it is all available on MDN right now.

[…]

  1. Basics concepts of grid layout
  2. Relationship to other layout methods
  3. Line-based placement
  4. Grid template areas
  5. Layout using named grid lines
  6. Auto-placement in grid layout
  7. Box alignment in grid layout
  8. Grids, logical values and writing modes
  9. CSS Grid Layout and Accessibility
  10. CSS Grid Layout and Progressive Enhancement
  11. Realizing common layouts using grids

Expand last row of wrapped flex items to fill entire row

Thanks to Jonathan Snook, I’ve learnt that we don’t need quantity queries to create a balanced grid. Quantity queries are very powerful, but so is Flexbox. If we just want all the items in the last row to fill the space, regardless of how many there are, then Flexbox can take care of this. But if we want to add additional styles to the items, we still need quantity queries.

This is the grid we want to achieve:

Here’s the Flexbox magic.

The container needs to have the property display: flex; and the items need to be wrapped using flex-wrap: wrap;

Code language: CSS

.list {
    display: flex;
    flex-wrap: wrap;
}

Now here’s the clever bit. We can set an initial width to the items (in this case flex-basis: 23%;) so that each item will always get a width of 23% unless otherwise stated in the CSS. flex-grow: 1; tells the items to grow and fill the space in the row.

Code language: CSS

.list-item {
    ...
    flex-basis: 23%;
    flex-grow: 1;
}

So, thanks to flex-grow, no matter how many items are in the last row, they will always fill the space. Works like magic! It’s amazing how much can be achieved with so little CSS.