Using CSS transitions in CSS Grid Layout
Asked Answered
S

10

66

I am trying to get my sticky header to have a transition effect so it eases in rather than just a snap movement.

What am I doing wrong?

Here's my a working version:

http://codepen.io/juanmata/pen/RVMbmr

Basically the following code adds the class tiny to my wrapper class, this works fine.

$(window).on('load', function() {
    $(window).on("scroll touchmove", function () {
        $('.wrapper').toggleClass('tiny', $(document).scrollTop() > 0);
    });
});

Here's the CSS part:

.wrapper {
    grid-template-rows: 80px calc(75vh - 80px) 25vh 80px;
    -o-transition: all 0.5s;
    -moz-transition: all 0.5s;
    -webkit-transition: all 0.5s;
    transition: all 0.5s;
}
.tiny {
    grid-template-rows: 40px calc(75vh - 40px) 25vh 80px;
}

So the header does shrink as it should but there is no animation, have I missed something or does transitions simply not work with grid?

Here's a link to the css-grid docs.

https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout

$(window).on('load', function() {
  $(window).on("scroll touchmove", function() {
    $('.wrapper').toggleClass('tiny', $(document).scrollTop() > 0);
  });
});
* {
	margin:0;
	padding:0;
}

.wrapper {
	display: grid;
	grid-gap: 0px;
	grid-template-columns: 1fr 1fr 1fr 1fr;
	grid-template-rows: 80px calc(75vh - 80px) 25vh 80px;
	grid-template-areas:
		"head head head head"
		"main main main main"
		"leader leader leader leader"
		"foot foot foot foot";
	background-color: #fff;
	color: #444;
}
.tiny {
	grid-template-rows: 40px calc(75vh - 40px) 25vh 80px;
}
.box {
	background-color: #444;
	color: #fff;
	border-radius: 5px;
	font-size: 150%;
}
.box .box {
	background-color: lightcoral;
}

.head {
	grid-area: head;
	background-color: #999;
	z-index: 2;
	display: grid;
	grid-gap: 0px;
	grid-template-columns: 20% 1fr;
	-o-transition: all 0.5s;
	-moz-transition: all 0.5s;
	-webkit-transition: all 0.5s;
	transition: all 0.5s;
	position: sticky;
	top: 0;
}

.logo{
        height: inherit;
        grid-column: 1;
        grid-row: 1;
        background-color:red;
        position: relative;
        overflow: hidden;
    }
.logo img {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        display: block;
        max-width: 100%;
        height: auto;
    }
.main {
	grid-area: main;
	/* CSS Grid */
	z-index: 1;
	grid-column: head-start / head-end;
	grid-row: head-start / leader-start;
	background-color: red;
}
.leader {
	grid-area: leader;
	z-index:1;
	display: grid;
	grid-gap: 0px;
	grid-template-columns: repeat(4, 1fr  );
}
.foot {
	grid-area: foot;
	z-index:1;
}
<div class="wrapper">
  <div class="box head">
    <div class="box logo">
      <a href="#"><img src="https://unsplash.it/200/300/?random" /></a>
    </div>
    <div class="box nav">nav</div>
  </div>
  <div class="box main">main</div>
  <div class="box leader">
    <div class="box leader-1">1</div>
    <div class="box leader-2">2</div>
    <div class="box leader-3">3</div>
    <div class="box leader-4">4</div>
  </div>
  <div class="box foot">foot</div>
</div>
Selfpossession answered 11/5, 2017 at 9:45 Comment(0)
A
60

According to the spec, transitions should work on grid-template-columns and grid-template-rows.

7.2. Explicit Track Sizing: the grid-template-rows and grid-template-columns properties

Animatable: as a simple list of length, percentage, or calc, provided the only differences are the values of the length, percentage, or calc components in the list

So, if my interpretation is correct, as long as the only changes are to the values of the properties, with no changes to the structure of the rule, transitions should work. But they don't.


UPDATE

This does work but is so far only implemented in Firefox. Follow here for other browser updates. https://codepen.io/matuzo/post/animating-css-grid-layout-properties

~ a contribution in the comments by @bcbrian


Here's a test I created:

grid-container {
  display: inline-grid;
  grid-template-columns: 100px 20vw 200px;
  grid-template-rows: repeat(2, 100px);
  background-color: black;
  height: 230px;
  transition: 2s;
  
  /* non-essential */
  grid-gap: 10px;
  padding: 10px;
  box-sizing: border-box;
}

grid-container:hover {
  grid-template-columns: 50px 10vw 100px;
  grid-template-rows: repeat(2, 50px);
  background-color: red;
  height: 130px;
  transition: 2s;
}

grid-item {
  background-color: lightgreen;
}
<grid-container>
  <grid-item></grid-item>
  <grid-item></grid-item>
  <grid-item></grid-item>
  <grid-item></grid-item>
  <grid-item></grid-item>
  <grid-item></grid-item>
</grid-container>

jsFiddle demo

In the test, the transition works on the height and background color, but not on grid-template-rows or grid-template-columns.

Arterialize answered 11/5, 2017 at 17:7 Comment(10)
Hi Michael, thanks for the answer. Although your background is animated your grid boxes are not. I have managed to get the box animated by giving each the chosen div a height tag and basically overwriting the grid-template-columns for that div, it works but it's more of a hack.Selfpossession
Yes, the transition works only on the height and background color. That's in my answer (see the last sentence, below the code snippet).Arterialize
Missed that part. I'll mark it as answered as we can't go any further with it.Selfpossession
So is that to say that there wont be any animation for grid columns/rows in general or is that still a work in progress? If so how would anyone go about using css grid? If you cant have transitions so that you can make animated sidebars etc in one uniform solution whats the point? If its still on the works is there any article stating when they ll think this will be done or something? ThanksBusy
@GeorgeAvgoustis, all good questions worthy of research and complete answers. Consider posting a new question so that it can get full attention.Arterialize
This does work but is so far only implemented in Firefox. Follow here for other browser updates. codepen.io/matuzo/post/animating-css-grid-layout-propertiesClatter
@Michael_B I don't know if it's new behavior or not, but this suffers from the sometimes-seen glitching when you hover over the bottom or right edge of the default layout; as it animates out from under the cursor's boundary, it stops applying the hover styles, and jumps back to the start, but then immediately applies them again, etc.Silvester
is it possible to animate gap? i remember a while ago it wasnt, but maybe things have changed?Albuminoid
It is now possible to animate the transitions if the track changes (e.g. if you go from 1 to 2 columns) by using the page view transitions API. I know it isn't CSS and it only works in chromium, but this is a great way to animate grid-template-columns or grid-template-rows if you change the number of columns/rows. bram.us/2023/05/09/… This post shows up first when searching so it might be worth adding a small comment at the bottom of your answer @MichaelBenjaminAlguire
@patrickzdb, thanks for the note. Feel free to add to my answer, if you think it well help the broader community.Arterialize
C
14

As a workaround, you can work with the size of the grid items instead with grid-template-columns or grid-template-rows.

You could do:

grid-container {
  display: inline-grid;
  grid-template-columns: 100px 20vw 200px;
  background-color: black;
  height: 230px;
  transition: 2s;
  
  /* non-essential */
  grid-gap: 10px;
  padding: 10px;
  box-sizing: border-box;
}

grid-container:hover {
  background-color: red;
  height: 130px;
}

grid-item {
  height: 100px;
  transition: height 2s;
  background-color: lightgreen;
}

grid-container:hover .first-row {
  height: 25px;
}

grid-container:hover .last-row {
  height: 75px;
}
<grid-container>
  <grid-item class='first-row'></grid-item>
  <grid-item class='first-row'></grid-item>
  <grid-item class='first-row'></grid-item>
  <grid-item class='last-row'></grid-item>
  <grid-item class='last-row'></grid-item>
  <grid-item class='last-row'></grid-item>
</grid-container>

Here's another 2x2 example: https://codepen.io/erik127/pen/OvodQR

and here a more extensive example where you can add more columns or rows: https://codepen.io/erik127/pen/rdZbxL

Unfortunately it's a lot of javascript, it would be nice if grid-template-columns and grid-template-rows would be animatable.

Another alternative which might work in some usecases (if your grid items don't span multiple rows) is the use of flexbox together with a grid.

Carolinian answered 5/4, 2018 at 19:12 Comment(3)
Please don't post answers that only link to external sites; when those sites go down, your answer loses its usefulness to readers!Silvester
Thanks @TylerH, I added a codesnippet to the answer.Carolinian
2x2 example is the only right answer in this thread. BTW, instead of 1fr I used (everywhere) auto. And class toggling with transitions worked for me too.Orthopter
P
9

I used GSAP to animate the grid-template-columns and grid-template-rows properties:

function changeGridTemplateColumns(pattern) {
  TweenMax.to(".container",
    1, {
      gridTemplateColumns: pattern
    }
  );
}

function changeGridTemplateRows(pattern) {
  TweenMax.to(".container",
    1, {
      gridTemplateRows: pattern
    }
  );
}

$(document).ready(
  function() {
    $(".el-a,.el-b,.el-c").mouseenter(
      function() {
        changeGridTemplateRows("2fr 1fr");
      }
    );
    $(".el-d,.el-e,.el-f").mouseenter(
      function() {
        changeGridTemplateRows("1fr 2fr");
      }
    );

    $(".el-a,.el-d").mouseenter(
      function() {
        changeGridTemplateColumns("2fr 1fr 1fr");
      }
    );

    $(".el-b,.el-e").mouseenter(
      function() {
        changeGridTemplateColumns("1fr 2fr 1fr");
      }
    );

    $(".el-c,.el-f").mouseenter(
      function() {
        changeGridTemplateColumns("1fr 1fr 2fr");
      }
    );

    $(".container").mouseleave(
      function() {
        changeGridTemplateColumns("1fr 1fr 1fr");
        changeGridTemplateRows("1fr 1fr");
      }
    );
  }
);
.container {
  width: 50vw;
  height: 50vw;
  margin: auto;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(2, 1fr);
  grid-template-areas: "a b c" "d e f";
}

.el-a {
  grid-area: a;
  background-color: skyblue;
}

.el-b {
  grid-area: b;
  background-color: darkseagreen;
}

.el-c {
  grid-area: c;
  background-color: coral;
}

.el-d {
  grid-area: d;
  background-color: gold;
}

.el-e {
  grid-area: e;
  background-color: plum;
}

.el-f {
  grid-area: f;
  background-color: beige;
}
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.0.2/TweenMax.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div class="container">
  <div class="el-a"></div>
  <div class="el-d"></div>
  <div class="el-b"></div>
  <div class="el-e"></div>
  <div class="el-c"></div>
  <div class="el-f"></div>
</div>
Provender answered 29/10, 2018 at 19:30 Comment(2)
too bad their license isn't MITPianissimo
@Pianissimo If it was, then it'd be unmaintained like countless other tools :) GSAP has been around a long time thanks to its licensing. greensock.com/licensingVargo
C
7

For now the transition on the grid template is not working. But you can use transform like this:

jsFiddle

var button = document.querySelector("#btnToggle");
button.addEventListener("click",function(){
	var content = document.querySelector(".g-content");
  if(content.classList.contains("animate"))
    content.classList.remove("animate");
  else
	  content.classList.add("animate");
});
html,body{
  width:100%;
  height:100%;
  padding:0;
  margin:0;
}

.g-content{
  display:grid;
  height:100%;
  grid-template-columns: 200px 1fr;
  grid-template-rows:60px 1fr 30px;
  grid-template-areas: 
    "g-header g-header"
    "g-side g-main"
    "g-footer g-footer";
 overflow-x:hidden;
}

.g-header{
  grid-area:g-header;
  background:#2B4A6B;
  display:grid;
  grid-template-columns: max-content 1fr;
}

.g-header button{
  align-self:center;
  margin:0 5px;
}

.g-side{
  grid-area:g-side;
  background:#272B30;
  transition:all 0.5s;
}

.g-main{
  grid-area:g-main;
  background:#FFFFFA;
  transition:all 0.5s;
}

.g-footer{
  grid-area:g-footer;
  background:#7E827A
}

.animate .g-main{
  width:calc(100% + 200px);
  transform:translateX(-200px);
}

.animate .g-side{
  transform:translateX(-200px);
}  
<div class="g-content">
  <div class="g-header">
    <button id="btnToggle">
    Toggle
    </button>
  </div>
  <div class="g-side">
  </div>
  <div class="g-main">
  test
  </div>
  <div class="g-footer">
  </div>
</div>
Club answered 13/7, 2018 at 6:55 Comment(0)
T
7

In my case I was trying to open sidebar-menu by using this code:

.wrapper{
  display: grid;
  grid-template-columns: 0 100%;
  transition: all 1s;
  .sidebar{
    background-color: blue;
  }
  .content{
    background-color: red;
  }
}
.wrapper.sidebar-open{
  grid-template-columns: 300px 100%;
  transition: all 1s;
}

but transition was not working for grid-template-columns. This is my solution:

.wrapper{
  display: grid;
  grid-template-columns: auto 100%;
  .sidebar{
    width: 0;
    background-color: blue;
    transition: all 1s;
  }
  .content{
    background-color: red;
  }
}
.sidebar.sidebar-open{
  width: 300px;
  transition: all 1s;
}

Perhaps, it helps someone.

Tomasz answered 23/10, 2018 at 12:29 Comment(1)
Very helpful answer, as it focuses on the CSS which is the issue. The key is to set the grid-template-columns that you want to transition to auto, then set the width of the unopened element to a fixed pixel width (e.g. width: 0) and the width of the opened element to the desired open pixel width (e.g. width: 300px)Gundry
S
2

Another approach is to use transform. It actually might even be better because transform along w/ opacity can achieve 60fps because it's GPU accelerated instead of CPU accelerated (browser has to do less work).

here's an example: https://codepen.io/oneezy/pen/YabaoR

.sides {
  display: grid;
  grid-template-columns: 50vw 50vw;
}

.monkey { animation: 0.7s monkey cubic-bezier(.86,0,.07,1) 0.4s both; }
.robot { animation: 0.7s robot cubic-bezier(.86,0,.07,1) 0.4s both; }

@keyframes monkey {
  0% { transform: translate(-50vw); }
  100% { transform: 0; }
}

@keyframes robot {
  0% { transform: translate(50vw); }
  100% { transform: 0; }
}
Scrannel answered 13/4, 2018 at 11:40 Comment(0)
A
2

I just wanted to mention the Page View Transitions API, since this question appears very high on search results for transition grid-template-columns and no one has mentioned it on this page yet.

As mentioned by Michael Benjamin above, a transition when changing column/row count (i.e. changing the grid tracks) is not possible with pure CSS. So attempting to see a transition from:

grid-template-columns: repeat(1,minmax(0,1fr));

to:

grid-template-columns: repeat(2,minmax(0,1fr));

Would not work and the change would be instantaneous, regardless of having a css transition: applied.

However, it is now possible to animate the transition between column or row count if you use the Page View Transitions API and toggle the CSS selector with a tiny amount of JavaScript. The JS used is absolutely minimal and has a fallback for unsupported browsers with only a few lines of code. It isn't a heavy framework and currently works in all chromium browsers. I don't want the mention of it to put people off trying it out. It can be as simple as this:

.grid {
    display: grid;
    grid-template-columns: repeat(1,minmax(0,1fr));
}

.grid--2-cols {
    grid-template-columns: repeat(2,minmax(0,1fr));
}

Then in JS, you could add it on toggle of a button for e.g.:

const grid = document.querySelector( '.grid' );
const button = document.querySelector( 'button' );

button.addEventListener( 'click', () => {
    if ( ! document.startViewTransition ) {
      // view transitions not supported, just toggle the class and see the change instantly
      grid.classList.toggle( 'grid--2-columns' );
    } else {
      // view transitions supported! now they will fade/animate 8-)
      document.startViewTransition( () => grid.classList.toggle( 'grid--2-columns' ) ); 
    }
});

If you wanted to get a nice animation effect rather than the basic fade, you just need to add a separate view-transition-name property to the items in the grid. For example the post-id or item count of the item in the grid. The rule is that there can't be duplicate view-transition-name values on a page, otherwise the transition will fail and the effect will be instant again.

See a simple code snippet here:

const grid = document.querySelector( '.grid' );
const button = document.querySelector( '.toggle-pvt' );
const buttonNoPvt = document.querySelector( '.toggle-no-pvt' );

button.addEventListener( 'click', () => {
    if ( ! document.startViewTransition ) {
      // view transitions not supported, just toggle the class
      grid.classList.toggle( 'grid--2-columns' );
    } else {
      // view transitions supported! now they animate 8-)
      document.startViewTransition( () => grid.classList.toggle( 'grid--2-columns' ) ); 
    }
});

buttonNoPvt.addEventListener( 'click', () => {
    grid.classList.toggle( 'grid--2-columns' );
});
.grid {
  display: grid;
  grid-template-columns: repeat(1,minmax(0,1fr));
  gap: 1rem;
}

.grid--2-columns {
  grid-template-columns: repeat(2,minmax(0,1fr));
}

/* with individual view-transition-name's the browser will animate them changing */

.box--1 {
  view-transition-name: box-1;
}
.box--2 {
  view-transition-name: box-2;
}
.box--3 {
  view-transition-name: box-3;
}
.box--4 {
  view-transition-name: box-4;
}
.box--5 {
  view-transition-name: box-5;
}
.box--6 {
  view-transition-name: box-6;
}
.box--7 {
  view-transition-name: box-7;
}
.box--8 {
  view-transition-name: box-8;
}

/* comment out the box-- classes above to see the default Page View Transitions API fade effect */

/* basic styling */

.box {
  width: 100%;
  background: tomato;
  color: white;
  height: 2rem;
}

button {
  margin-bottom: 2rem;
}
<button class="toggle-pvt">Toggle 2 columns</button>
<button class="toggle-no-pvt">Toggle 2 columns without page view transitions</button>

<div class="grid">
  <div class="box box--1">Box 1</div>
  <div class="box box--2">Box 2</div>
  <div class="box box--3">Box 3</div>
  <div class="box box--4">Box 4</div>
  <div class="box box--5">Box 5</div>
  <div class="box box--6">Box 6</div>
  <div class="box box--7">Box 7</div>
  <div class="box box--8">Box 8</div>
</div>

I made a quick codepen where you can see the effect - Codepen: https://codepen.io/patrickwc/pen/jOdxdgP (view in Firefox and Chrome to see the difference).

The page view transitions API can be called any time you add or remove elements to the page, or even toggle a CSS class on or off (there are probably hundreds of other use cases).

I thought it should be referenced here to help people animate the web.

Currently only works in chromium browsers, https://caniuse.com/?search=View%20Transition%20API#:~:text=View%20Transitions%20API%20(single%2Ddocument)&text=Provides%20a%20mechanism%20for%20easily,document%20transitions%20is%20being%20planned.

Credit to https://www.bram.us/2023/05/09/rearrange-animate-css-grid-layouts-with-the-view-transition-api/ as I found his blog post before posting this answer. If you're interested in the Page View Transitions API check out the google link above for more info. This youtube video where they add page view transition support in 1-2hrs is also worth a look https://www.youtube.com/watch?v=5K5wNqCUrL8

Alguire answered 23/11, 2023 at 18:12 Comment(0)
L
0

I found a workaround, but it requires that at least one of the other columns fill up the width. The column to be transitioned must be auto in the grid-template-columns. And another one must be 1fr, otherwise the auto column will use this missing space. Also, the column to be transitioned must have absolute widths in both states.

Here it is:

CodePen

document.querySelector('.js-button').addEventListener('click', function() {
  document.querySelector('.js-grid').classList.toggle('grid--full')
})
.grid {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  grid-template-rows: 100px;
  grid-gap: 20px;
  transition: all 1s;
}

.item {
  background-color: #D73B38;
  color: #ffffff;
  padding: 10px;
  border-radius: 5px;
  border: none;
  transition: 1s;
}

.grid .item:nth-child(3n + 2) {
  width: 200px;
  background: blueviolet; 
}
.grid--full .item:nth-child(3n + 2) {
  width: 300px;
  background: cadetblue;
}

button {
  background-color: #123456;
  color: #ffffff;
  margin: 2rem 0;
  padding: 1.4rem;
  border: none;
  border-radius: 5px;
  text-transform: uppercase;
  font-size: 1.2rem;
}
<p>Fork from <a href="https://codepen.io/matuzo/pen/rmQvMG">Manuel Matuzovic</a></p>

<button class="js-button">Animate</button>

<div class="grid js-grid">
  <article class="item">
    <h2>Element 1</h2>
  </article>
  <article class="item">
    <h2>Element 2</h2>
  </article>
  <article class="item">
    <h2>Element 3</h2>
  </article>
  <article class="item">
    <h2>Element 4</h2>
  </article>
  <article class="item">
    <h2>Element 5</h2>
  </article>
  <article class="item">
    <h2>Element 6</h2>
  </article>
</div>

It uses the selector :nth-child(3n + 2) to do the trick in all columns of the grid. But also works if you want to change only one block of your grid.

Laszlo answered 22/9, 2020 at 13:37 Comment(0)
M
0

Sometimes getting rid of repeat and explicitly defining columns also helps.

Maledict answered 29/5 at 20:31 Comment(0)
M
-1

After hours of trial and error, I finally found a way to animate grid columns. This causes the column gaps to go to 0 and the hovered field to expand by its width.

Here is what i got so far:

$('.cards').each(function(){
  let cols = window.getComputedStyle($(this).get(0));
  let colGapAmount = cols.getPropertyValue("grid-template-columns").split(" ").length - 1;
  let card = $(this).find('.card');
  let standardColWidth = parseInt(card.css('width'), 10);
  let colGapSpace = colGapAmount * parseInt($(this).css('column-gap'), 10);
  let newSize = (standardColWidth + colGapSpace) + 'px';
  //Set Values
  $(this).closest('.cards').get(0).style.setProperty('--scaledSize', newSize);
  $(this).closest('.cards').get(0).style.setProperty('--normalSize', standardColWidth + 'px');
});

$('.card').hover(function(){
  $(this).closest('.cards').addClass('noGap');
},
function(){
  $(this).closest('.cards').removeClass('noGap');
});
.cards {
     --normaleSize: 100%;
     --scaledSize: unset;
     display: grid;
     grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
     column-gap: 20px;
     transition: column-gap 1s ease-in-out;
}
 .cards .card {
     transition: width 1s ease-in-out;
     border: 1px solid black;
     width: var(--normalSize);
}
 .cards .card:hover {
     width: var(--scaledSize);
}
 .cards.noGap {
     column-gap: 0px;
}
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section class="cards">
  <div class="card">1</div>
  <div class="card">2</div>
  <div class="card">3</div>
  <div class="card">4</div>
  <div class="card">5</div>
</section>

<section class="cards" style="margin-top: 3rem">
  <div class="card">1</div>
  <div class="card">2</div>
  <div class="card">3</div>
  <div class="card">4</div>
  <div class="card">5</div>
</section>
Mousebird answered 6/4, 2021 at 19:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.