<noscript><div class="alert alert-danger"><p>To use this site, you'll need to enable JavaScript. If you aren't sure how, <a href="https://enable-javascript.com" target="_blank" class="alert-link">follow these instructions</a>.</p></div></noscript>
<a href="#main" onclick="document.getElementById('main').focus()">Skip to main content</a>
<div class="wrapper">
  <main role="main" id="main" tabindex="-1">
    <h1>Transition types</h1>
    <p>Terms: Element A = departing; Element B  = arriving; Scrim = Covering that is translucent, typically behind a modal; Curve = an arc that creates a more natural motion path when moving from point 1 to point 2 (probably not MVP, <a href="http://tobiasahlin.com/blog/curved-path-animations-in-css/">possible in CSS but requires an extra element</a>)</p>
    <p><em>PS: There are no easing methods, physics, or proper timelines applied currently. Only showing general types of interaction below.</em></p>
<ul>
  <li>
    <h2>Fade (aka. Dissolve)</h2>
    <div class="content">
      <div class="anim fade">
        <div class="b small"></div>
        <div class="scrim"></div>
      </div>
      <h4>Use for menus & overlays.</h4>
      <p>Appears without transitioning another object out.</p>
      <p>Options: Scrim, scale, position, animation curve</p>
    </div>
  </li>
  
    <li>
    <h2>Scale (aka. Pop / Explode)</h2>
    <div class="content">
      <div class="anim scale">
        <div class="b"></div>
      </div>
      <h4>Use for a modal or for a sub-item that needs a larger work area / becoming the new page focus.</h4>
      <p>A resizes from original size to a larger size (typically filling the screen).</p>
      <p>Options: Scrim, position, animation curve, shadow, cover %</p>
    </div>
  </li>
  
  <li>
    <h2>Crossfade (aka. Cross dissolve)</h2>
    <div class="content">
      <div class="anim fade">
        <div class="a"></div>
        <div class="b"></div>
      </div>
      <h4>Use for navigating to an unrelated page (aka. moving from a work item page to a main navigation page) AND <a href="https://css-tricks.com/introduction-reduced-motion-media-query/">is the reduced-motion page transition (and prob web) default</a>. </h4>
      <p>B fades from 0% to 100% directly over the top of A.</p>
    </div>
  </li>
  
  <li>
    <h2>Reveal (aka. Move in)</h2>
    <div class="content">
      <div class="anim reveal">
        <div class="a"></div>
        <div class="scrim"></div>
        <div class="b"></div>
      </div>
      <h4>Use for default page to sub-page transition. Use at non-100% cover for Peek panel. Mobile only.</h4>
      <p>B slides in ##% and can cover A entirely. B starts at 0% opacity.</p>
      <p>Options: Scrim, shadow, distance, cover %</p>
    </div>
  </li>
  
  <li>
    <h2>Slide</h2>
    <div class="content">
      <div class="anim slide">
        <div class="a"></div>
        <div class="b"></div>
      </div>
      <h4>Use for carousels & tab group swipes.</h4>
      <p>Both A & B move together 100% of viewable width.</p>
      <p>Options: Scrim</p>
    </div>
  </li>

    
  <li>
    <h2>Flow (aka. Grid)</h2>
    <div class="content">
      <div class="anim flow">
        <div class="a"></div>
        <div class="b"></div>
      </div>
      <h4>Used for moving from one completely different app experience to another.</h4>
      <p>A & B both scale back, revealing a background of some sort. They slide a direction so B is now centered. Once B is  centered, they zoom back in to cover the viewable area.</p>
      <p>Options: Animation curve</p>
    </div>
  </li>
    
  <li>
    <h2>Push</h2>
    <div class="content">
      <div class="anim push">
        <div class="a"></div>
        <div class="b"></div>
      </div>
      <p>B slides in 100% to cover A entirely. A is moving ⅓ that diffence at the same time. Use a scrim or shadow to convey depth. Push works great on small areas, but poorly on larger display areas. Warning: SLOW on older machines</p>
      <p>Options: Scrim, shadow</p>
    </div>
  </li>
  
  <li>
    <h2>Flip</h2>
    <div class="content">
      <div class="anim flip">
        <div class="a"></div>
        <div class="b"></div>
      </div>
      <p>A & B rotate on the same axis at the same time. A is hidden, B is revealed. Sometimes used for settings on back of a card, but is a little overkill.</p>
      <p>Options: Mask to add 3d lighting</p>
    </div>
  </li>

  <li class="cube">
    <h2>Cube</h2>
    <div class="content">
      <div class="anim cube">
        <div class="a"></div>
        <div class="b"></div>
      </div>
      <p>A variation on Flip. Makes cube-like animation instead of a card-like one. Non-performant to animate, since it relies on left/top values (it can't rely on translateX due to rotation decreasing its width).</p>
      <p>Options: Mask to add 3d lighting</p>
    </div>
  </li>
  
    <li class="expand-1">
    <h2>Single object expand</h2>
    <div class="content">
      <div class="anim expand-1">
        <div class="b"></div>
        <div class="a"></div>
      </div>
      <p>Revealing new objects in a stream</p>
    </div>
  </li>
  <li class="expand-2">
    <h2>Accordian expand</h2>
    <div class="content">
      <div class="anim expand-2">
        <div class="a"></div>
        <div class="b"></div>
      </div>
      <h4>Use for accordians.</h4>
      <p>Toggling between active sections</p>
    </div>
  </li>
  
  <li class="stacks">
    <h2>Stacks</h2>
    <div class="content">
      <div class="anim stacks">
        <div class="a"></div>
        <div class="b"></div>
        <div class="c"></div>
      </div>
      <p>Reduces height. For quick dismiss review. Should reduce stack unless its serving up N+ items. Yeah, I know, animation needs some smoothing out.</p>
    </div>
  </li>
  
  <!--li>
    <h2>Curl</h2>
    <div class="content">
      <div class="anim">😔</div>
      <p>A peels out, revealing B below. Not commonly used, and not easily replicated with CSS alone. Requires SVG clip-paths & JS for element measurements to reproduce. Probably non-performant to animate also.</p>
    </div>
  </li-->
  
</ul>






  </main>
</div>
<!-- a11y helpers: 1. Prepositions 2. Error log container -->
<div id="sr-helpers" class="sr-only">
  <div id="sr-prepositions">
    <div id="srp-for">for</div>
    <div id="srp-on">on</div>
  </div>
  <div id="sr-alerts" role="alert" aria-relevant="additions"></div>
</div>
// Vars
$pad: 8px;
$mobileW: 375px;
$ease: cubic-bezier(.4,.6,.1,1);
$time:3s;
$w:200px;
$h:150px;

// Keyframes
@keyframes Fade {
	0%,90%,100%{opacity:0}
	50%{opacity:1}
}
@keyframes Scale {
	0%,90%,100%{transform-origin: center;transform:scale(.33) translateX(25%) translateY(50%)}
	50%{transform-origin: center;transform:scale(1)}
}
@keyframes Expand1B {
	0%,90%,100%{max-height:0}
	50%{max-height:60px}
}
@keyframes Expand2A {
	0%,90%,100%{max-height:60px}
	50%{max-height:10px}
}
@keyframes Expand2B {
	0%,90%,100%{max-height:10px}
	50%{max-height:60px}
}
@keyframes StackA {
	0%,100%{z-index:10;transform:scale(.8)}
  25%{z-index:10;transform:scale(.8) translateX(150%);opacity:0}
  26%{z-index:1;transform:scale(.4) translateY(60%) translateX(0%)}
  50%{z-index:1;transform:scale(.4) translateY(60%) translateX(0%);opacity:1}
  51%{z-index:2}
  75%{transform:scale(.6) translateY(20.5%) translateX(0%);z-index:2}
  76%{z-index:10}
}
@keyframes StackB {
	0%,100%{z-index:2;transform:scale(.6) translateY(20.5%) translateX(0%);opacity:1}
  25%{z-index:10;transform:scale(.8);opacity:1}
  50%{z-index:10;transform:scale(.8) translateX(-150%);opacity:0}
  51%{z-index:1;transform:scale(.4) translateY(60%) translateX(0%);opacity:0}
  75%{z-index:1;transform:scale(.4) translateY(60%) translateX(0%);opacity:1}
}
@keyframes StackC {
	0%,100%{z-index:1;transform:scale(.4) translateY(60%) translateX(0%);opacity:1}
  1%{z-index:2}
  25%{transform:scale(.6) translateY(20.5%) translateX(0%);z-index:2}
  49%{z-index:10}
  50%{z-index:10;transform:scale(.8);opacity:1}
  75%{z-index:10;transform:scale(.8) translateX(150%);opacity:0}
  76%{z-index:1;transform:scale(.4) translateY(60%) translateX(0%);opacity:0}
}
@keyframes Reveal {
	0%,90%,100%{
    transform:translateX(30%);
    opacity:0}
	50%{transform:translateX(0%);
    opacity:1}
}
@keyframes PushA {
	0%,90%,100%{transform:translateX(0)}
	50%{transform:translateX(-33%)}
}
@keyframes PushB {
	0%,90%,100%{
    transform:translateX(102%)}
	50%{
    transform:translateX(0%);
    box-shadow:-4px 0 4px #000}
}
@keyframes SlideA {
	0%,90%,100%{transform:translateX(0)}
	50%{transform:translateX(-100%)}
}
@keyframes SlideB {
	0%,90%,100%{transform:translateX(100%)}
	50%{transform:translateX(00%)}
}
@keyframes FlowA {
	0%,90%,100%{transform:scale(1) translateX(0)}
  16%,77%{transform:scale(.5) translateX(0)} // Pull back
  33%,63%{transform:scale(.5) translateX(-110%)} // Move
	50%{transform:scale(1) translateX(-110%)}
}
@keyframes FlowB {
	0%,90%,100%{transform:scale(1) translateX(110%)}
  16%,77%{transform:scale(.5) translateX(110%)} // Pull back
  33%,63%{transform:scale(.5) translateX(0)} // Move
	50%{transform:scale(1) translateX(0)}
}
@keyframes FlipA {
	0%,90%,100%{transform: rotateY(0deg)}
	50%{transform: rotateY(180deg)}
}
@keyframes FlipB {
	0%,90%,100%{transform: rotateY(-180deg)}
	50%{transform: rotateY(00deg)}
}

@keyframes CubeA {
	0%,100%{transform: rotateY(0deg);left:0}
	50%{transform: rotateY(-90deg);left:$w*-1}
}
@keyframes CubeB {
	0%,100%{transform: rotateY(90deg);left:$w}
	50%{transform: rotateY(0deg);left:0}
}

// Base & helpers
:root {
	font-family:sans-serif;
  line-height:2.5ex;
	background: #f3f3f3;
	color: #111;
	box-sizing: border-box;
	cursor: default;
	-ms-overflow-style:-ms-autohiding-scrollbar;
}
*,::before,::after {
  animation-fill-mode:both;
	box-sizing: border-box;
	color: inherit;
  cursor: inherit;
	font: inherit;
	hyphens: auto;
	line-height: inherit;
  scroll-behavior: smooth;
  vertical-align: inherit;
  word-break: keep-all;
}
::before,::after {text-decoration: inherit}
[unselectable] {user-select: none}
.disabled,:disabled {
	cursor: not-allowed;
	pointer-events: none;
}
h1,h2,h3,h4,.h1,.h2,.h3,.h4,[aria-level],table caption,fieldset legend{
	font-weight:700;
	margin-bottom:0;
	text-align:left;
}
h1,.h1,[aria-level='h1']{font-size:200%}
h2,.h2,[aria-level='h2'],table caption{font-size:150%}
h3,.h3,[aria-level='h3'],fieldset legend{font-size:117%}
h4,.h4,[aria-level='h4']{font-size:100%}
textarea {resize: vertical}
.clearfix:after {display:block;clear:both}
.hyphenate{
	overflow-wrap: break-word;
	word-wrap: break-word;
	hyphens:auto;
}
.fx{transition: all .17s cubic-bezier(.2,.6,.36,1)}
.no-fx{transition: none}
:-ms-input-placeholder {color: rgba(0,0,0,.6)}
::placeholder {
	color: rgba(0,0,0,.6);
	opacity:1;
}
html,body,.wrapper{
  width:100%;
  height:100%;
  padding:0;
  margin:0;
}
em{font-style:italic}

// A11y
[aria-busy="true"] {cursor: progress}
[aria-controls] {cursor: pointer}
[aria-disabled] {cursor: default}
.sr-only {
	position: absolute !important;
	clip: rect(1px, 1px, 1px, 1px);
	padding:0 !important;
	border:0 !important;
	height: 1px !important;
	width: 1px !important;
	overflow: hidden;
}
body:hover .sr-only a,
body:hover .sr-only input,
body:hover .sr-only button {display: none !important}
a[href='#main']{
  color:#fff;
  display: inline-block;
  text-decoration: none;
  background: rgba(0,0,0,.8);
  line-height: 1;
  padding: .5em;
  position: absolute;
  top:-2em;
  z-index: 999;
}
a[href='#main']:focus{top:auto}


// Custom
body{padding:1em}
main{outline:none}
.anim{
  border:1px solid #666;
  width:$w;
  height:$h;
  background:linear-gradient(#7ad,#47a);
  margin-bottom:$pad;
  position:relative;
  overflow:hidden;
  perspective: 1000px;
  transform-style: preserve-3d;
  &,*,*:after{
    font-weight:700;
    font-size:72px;
    line-height:$h;
    text-align:center;
  }
}
.a,.b,.c,.scrim{
  background:#f99;
  color:#000;
  z-index:1;
  backface-visibility: hidden;
  &.small{
    transform:scale(.66);
  }
  &,&:after{
    position:absolute;
    width:$w;
    height:$h;
    content:"A";
  }
  &:after{
    top:0;
    left:0;
  }
}
.scrim{
  background:rgba(#000,.5);
  z-index:2;
  &:after{content:""}
}
.b{
  background:#bfb;
  z-index:3;
  &:after{content:"B"}
}
.c{
  background:#bbf;
  z-index:4;
  &:after{content:"C"}
}
ul{
  list-style:none;
  padding-left:0;
  display:flex;
  flex-wrap:wrap;
}
li{
  flex:1 1 auto;
  clear:both;
  max-width:18em;
  padding:$pad*2;
}
h2{height:60px}
a{color:blue}


// Animations
.anim.fade .b,.anim .scrim{animation:Fade $time infinite}
.anim.scale .b{animation:Scale $time infinite}
.anim.reveal .b{
    animation:Reveal $time infinite;
    box-shadow:-4px 0 4px #000;
}
.anim.push{
  .a{animation:PushA $time infinite}
  .b{animation:PushB $time infinite}
}
.anim.slide{
  .a{animation:SlideA $time infinite}
  .b{animation:SlideB $time infinite}
}
.anim.flow{
  .a{animation:FlowA $time*2 infinite}
  .b{animation:FlowB $time*2 infinite}
}
.anim.flip{
  .a{animation:FlipA $time infinite}
  .b{animation:FlipB $time infinite}
}

.anim.cube{
  .a{
    transform-origin: 100% 50%;
    animation:CubeA $time infinite}
  .b{
    transform-origin: 0% 50%;
    animation:CubeB $time infinite}
}

.anim.expand-1,.anim.expand-2{
  .a,.b{
    height:60px;
    width:80%;
    margin:7px auto;
    position:static;
    overflow:hidden;
    &,&::after{
      height:60px !important;
      line-height:60px !important;
      font-size:24px;
      position:static;
      overflow:hidden;
    }
  }
}

.anim.expand-1{
    .b{animation:Expand1B $time infinite}
}
.anim.expand-2{
  .a{
    margin-bottom:0 !important;
    animation:Expand2A $time infinite}
  .b{
    margin-top:0 !important;
    animation:Expand2B $time infinite}
}

.anim.stacks{
  .a{animation:StackA $time infinite}
  .b{animation:StackB $time infinite}
  .c{animation:StackC $time infinite}
}
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js