Medium-Style Page Transition

An article on how to achieve Medium’s next page transition effect—an effect that can be seen by clicking anywhere on the “Read Next” footer at the bottom of the page. This effect is characterized by the lower article easing upward as the current article fades up and out.

From our sponsor: Try Mailchimp today.

Medium, a blogging platform which has gained popularity over the past several months, has one of the smoothest, most polished user interfaces on the web. As you click and touch the interface, you’ll notice that great attention has been paid to transitions, white space, color, fonts, imagery, and iconography.

In this article, I will outline how to achieve Medium’s page transition effect—an effect that can be seen by clicking anywhere on the “Read Next” footer at the bottom of the page. This effect is characterized by the lower article easing upward as the current article fades up and out. See the animation below for an illustration of this effect.



In this demo, the page first loads with barebones HTML, which we’ll use as a template that will be filled in later with Ajax’d-in data. Below is what our <body> looks like on initial page load. One main <article> tag. Pretty simple, eh?

  <article class='page hidden'>
    <div class='big-image'></div>
    <div class='content'></div>

Once the content is Ajax’d-in, the <body> looks something like so:

  <article class='page current'><!--other HTML --></article>
  <article class='page next '><!--other HTML --></article>

The page currently being viewed has a class of current, and the next article has a class of next. The next article only has its large image being shown at the bottom of the page, which, when clicked on, brings it into focus.


The styles in this demo which control the article transitions are both applied dynamically via jQuery’s css() method, as well as by applying classes to the <article> elements using jQuery’s addClass() method.

Here’s a rundown of the pertinent classes used:

        display: none 
    } .content { 
        display: none 

    article.fade-up-out {
        opacity: 0;
        transform: scale(0.8) translate3d(0, -10%, 0);
        transition: all 450ms cubic-bezier(0.165, 0.840, 0.440, 1.000);

    article.easing-upward {
        transition: all 450ms cubic-bezier(0.165, 0.840, 0.440, 1.000);


Before getting into the Javascript code, I want to first outline the algorithm used to transition the “next” article upward, and transition the “current” article up and away.

User clicks on big image of next article:

  1. Disable scrolling on the page
  2. Fade current article to opacity of 0, a scale of 0.8, and move it upward by 10%.
  3. Show the next article’s content, give it smooth transitions, then move it upward to the top of the window
  4. After 500ms:
    1. Remove the current article from the DOM
    2. Remove smooth transitions from next article
    3. Scroll to top of page programmatically.
    4. Make next article the current article
    5. Enable scroll on the page
    6. Make Ajax request for next article’s content

The Code

  ArticleAnimator.animatePage = function(callback){
  var self              = this;
  var translationValue  = this.$next.get(0).getBoundingClientRect().top;
  this.canScroll        = false;


  this.$next.removeClass('content-hidden next')
       .css({ "transform": "translate3d(0, -"+ translationValue +"px, 0)" });


      self.$next.css({ "transform": "" });
          self.$current = self.$next.addClass('current');

      self.canScroll = true;
      self.currentPostIndex = self.nextPostIndex( self.currentPostIndex );

  }, self.animationDuration + 300 );

Throughout the CSS & Javascript code, you’ll notice that, in order to achieve fluid animations, I am using transform: translate3d(x,y,z) to move my DOM elements. By doing this, we “hardware accelerate” the DOM elements movement. This method is preferred over animating an element using top/left or transform: translate(x,y,z), which are not hardware accelerated by default.

Side notes

As outlined earlier, the page is populated by making Ajax requests to static json files. Page state is managed using the PushState API and location.hash.

Photos in the demo were used courtesy of Unsplash. The page font is set in PT Serif and the headings in Source Sans.

Tagged with:

Brian Gonzalez

Brian is a web developer and designer based in Los Angeles. He's one of the web’s biggest proponents, and enjoys using the web to build things that bring life to products and the people that use them.

Stay up to date with the latest web design and development news and relevant updates from Codrops.

Feedback 90

Comments are closed.
  1. Hey guys!
    I read trough the comments and saw that certain ppl had the sam issue as me, then I downloaded the updated example from gitHub BUT it’s still not working. I still get a ‘white’ page with:
    – Published by
    – Read Next

    There are no images displaying (even if I detect them with MOZ/Firebug), no text, Links not working ecc.

    Resume: The entire page isn’t working as it should! Tried it with Firefox, Chrome, Safari. Tried also hosting it, doesn’t change anything.


    BIG Thanks in advance!!

    • In your next update, can you please add a “previous article/post link” also.
      oh and great work by the way.

  2. awesome script, things are working fine in firefox but page don’t show in chrome or IE. anyone knows how to fix it?

    file:///C:/Main/post_1.json?_=1386317103426 No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘null’ is therefore not allowed access. jquery.js:6
    XMLHttpRequest cannot load file:///C:/Main/post_1.json?_=1386317103426. No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘null’ is therefore not allowed access.

    • Hi !

      Have you found the solution for this ? I have the same problem. I solved by placing in server. ( MAMP )

  3. Any ideas how I could modify this work with post numbers that increment by more than 1? For example, if my files were named post_3.json, post_7.json, post_15.json, could I make it ignore the missing posts?

  4. Hi Brian,

    … + thaks for this, so cool!

    Is it somehow possible to implement some php into this?

    I tryed this:

    “content”: “”

    … but it didn’t seem to work : )

    very best,

  5. Thank you for this code, it helped me a lot! But what if I want to had a link to another post in a post? The window.location changes well but nothing happened on the page (which seems logical, because we juste change the hash). Do I have to handle those links in JS as well or is their any other solutions ?
    And second important question, what about the url rewritting? I don’t think it’s possible with the hash. (But medium doesn’t have URLs with #1 🙂 )

    Thank you!

  6. Hi guys! I like this! But I have a problem. I want to write russian texts, but when I change it, it looks like ??????????. HTML works good in Russian, but .json files don’t. How can I change encoding from UTF-8 to chcp-1251?

  7. Thank you for this code. It’s a great help for me especially working in my own site. Thank you for sharing this interesting tutorial.

  8. Couldn’t get it working myself…

    Anyway you can post it to codepen? It’s a lot simpler for some of us to figure things out, when it’s listed in that format.


  9. Hi guys!

    Thanks for great code but I have question. Is some code example like that with normal content in HTML (no JSON, because if I debug the code, I see empty elements and that I don’t want)?

    For link or any more I will happy! Thanks so much guys!

  10. Hi,
    thank you for this awesome script.
    I have a problem
    I set up this script on a wordpress based website, and the posts are the wordpress posts that I bring back in JSON instead of the .json files used in this example.

    It’s working fine… on firefox.
    On Chrome and Safari I have a display issue: after clicking a “read next” link, all the content is perfectly loaded, but after this load, the page automatically scroll down to the bottom of the page.
    Can you help me please ?

    You can check the page Here, and if it’s necessary I can provide you the code I modified.

    Thank you

  11. Freaking Amazing! I love this.
    Has anyone been able to convert this into PHP though? I think this would make a killer personal blog if I can run the data on PHP.

  12. For some reason everything comes up blank. Big grey white space up top, grey block on the footer. Just downloaded it, opened it up and got that. Any ideas on how to make this thing work? Not sure what’s up…