AMP is a way to build web pages that render with reliable and fast performance. AMP includes many responsive and interactive components, and resources such as quick-start templates to help developers create stunning AMP pages.

What are we going to be building?

In this codelab, you're going to build a fully-responsive, interactive and beautiful AMP page that incorporates many of AMP's features and extended components such as:

  • AMP Start
  • Responsive navigation
  • Fullpage Hero cover image
  • Responsive images
  • Video with autoplay
  • Embeds such as Instagram
  • Actions and selectors
  • Data binding with amp-bind
  • Visual effects with amp-fx-parallax and amp-animation

What you'll need

Tooling for serving content

We will use Node.js to run a local HTTP server to serve our AMP page. Check the Node.js website on how to install it on your Operating System.

Our tool of choice to serve content locally will be serve, a Node.js based static content server. To install it, run:

npm install -g serve

Download a template from AMPStart.com

AMP Start is a repository of quick-start AMP templates and components to help create modern, responsive AMP pages quickly.

Visit ampstart.com and download the code for the "Most Beautiful Photos of 2016" Page Template.

Running the template code

Extract the content of the ZIP file downloaded from ampstart.com

Run the command serve inside the article folder to serve the files locally.

Visit http://localhost:3000/templates/article.amp.html in your browser!

While we are at it, let's open the Chrome Devtools and toggle the Device mode as well!

Trim the template code

At this point we have a mostly functioning AMP page scaffolded by ampstart.com, but the purpose of this codelab is for you to learn and practice, so:

Delete everything inside the <body></body>!

Now we are left with an empty page which only includes some boilerplate code.

Throughout this codelab, you will add many components to this empty page, partially recreating the template with even more functionality.

An AMP page is a HTML page with some restrictions for reliable performance.

Though most tags in an AMP page are regular HTML tags, some HTML tags are replaced with AMP-specific tags. These custom elements, called AMP HTML components, make common patterns easy to implement in a performant way.

The simplest AMP HTML file looks like this (sometimes referred to as AMP boilerplate)

<!doctype html>
<html ⚡>
 <head>
   <meta charset="utf-8">
   <link rel="canonical" href="hello-world.html">
   <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
   <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
   <script async src="https://cdn.ampproject.org/v0.js"></script>
 </head>
 <body>Hello World!</body>
</html>

Take a look at the code of the empty page you created during setup, you will notice it includes this boilerplate and has a few extra additions, importantly, a <style amp-custom> tag that includes lots of minified CSS.

By default, AMP is not opinionated about design and does not enforce a particular set of styles. Most AMP components have very basic styling and it is left to the page authors to provide their custom CSS. That's where <style amp-custom> comes into play.

AMP Start however provides its own opinionated CSS styles that are beautifully-designed, cross-browser and responsive to help developers build elegant AMP pages quickly. The template code you have downloaded includes these opinionated CSS styles in <style amp-custom>.

We will start by adding back some of the components we have removed from the template to create a shell for our page, including navigation menu and page header image and title.

We will get help from AMP Start's UI Components page to do so, but we won't dig deep into their implementation details. Later steps in the codelab will provide plenty of opportunities to do so.

Add responsive navigation

Head to https://ampstart.com/components#navigation and copy/paste the HTML code provided for RESPONSIVE MENUBAR into body of your page.

The code provided by AMP Start includes the necessary HTML and CSS class structure to implement a responsive navigation bar for your page.

Try it out! Resize your window to see how it responds to different screen sizes.

This code uses CSS media queries and couple of AMP components, namely amp-sidebar and amp-accordion.

Add hero image and title

You guessed it! AMP Start provides ready-to-use snippets for beautiful, responsive hero image and title as well.

Head to https://ampstart.com/components#media and copy/paste the HTML code provided for Fullpage Hero into your code, right after the <!-- End Navbar --> comment in body.

Let's update the image and the title now.

As you may have noticed, there are two different amp-img tags in the code snippet. One is used for smaller widths and should point to a lower-resolution image and the other one for larger displays. They are toggled automatically based on the media attribute which AMP supports on all AMP elements.

Update the src, width and height to different images and the title to "Most Beautiful Hikes in the Pacific Northwest" by replacing the existing <figure>...</figure> with:

<figure class="ampstart-image-fullpage-hero m0 relative mb4">
    <amp-img width="600" height="900" layout="responsive" src="https://unsplash.it/600/900?image=1003" media="(max-width: 415px)"></amp-img>
    <amp-img height="1800" layout="fixed-height" src="https://unsplash.it/1200/1800?image=1003" media="(min-width: 416px)"></amp-img>
    <figcaption class="absolute top-0 right-0 bottom-0 left-0">
      <header class="p3">
        <h1 class="ampstart-fullpage-hero-heading mb3">
        <span class="ampstart-fullpage-hero-heading-text">
          Most Beautiful Hikes in the Pacific Northwest
        </span>
      </h1>
        <span class="ampstart-image-credit h4">
        By <a href="#" role="author" class="text-decoration-none">D.F. Roy</a>,<br> January 14, 2017
      </span>
      </header>
      <footer class="absolute left-0 right-0 bottom-0">
        <a class="ampstart-readmore py3 caps line-height-2 text-decoration-none center block h5" href="#content">Read more</a>
      </footer>
    </figcaption>
</figure>

Let's take a look at the page now:

Summary

The completed code for this section can be found here: http://codepen.io/aghassemi/pen/RpRdzV

In this section we will add responsive images, videos and embeds in addition to some text to our page.

Let's add a main element that will host the content of the page. We'll add it to the end of body:

<main id="content">

</main>

Add headings and paragraphs

Add the following inside main

<h2 class="px3 pt2 mb2">Photo Gallery</h2>
<p class="ampstart-dropcap mb4 px3">Vivamus viverra augue libero, vitae dapibus lectus accumsan eget. Pellentesque eget ipsum purus. Maecenas leo odio, ornare nec ex id, suscipit porta ipsum. Ut fringilla semper cursus.</p>

Since AMP is just HTML, there is nothing special about this code except for those CSS class names! What are px3, mb2, ampstart-dropcap, etc..? Where are they coming from?

These classes are not part of AMP HTML but provided by AMP Start boilerplate that we are using. Do you remember that minified CSS inside <style amp-custom>? The rules for these classes are defined there.

AMP Start uses Basscss to provide a low level CSS toolkit and adds additional AMP Start specific classes on top of it.

In this snippet, px3, mb2, etc... are defined by Basscss and translate to padding-left-right and margin-bottom respectively. ampstart-dropcap is provided by AMP Start and makes the first letter of a paragraph larger.

You can find documentation for these pre-defined CSS classes on http://basscss.com/ and https://ampstart.com/components.

Let's see how the page looks now:

Add an image

Making responsive pages is easy in AMP! In most cases as simple as adding layout="responsive" attribute to the AMP elements such as amp-img. Similar to HTML img tag, amp-img also supports srcset to specify different images for varying viewport widths and pixel densities.

Add an amp-img to main:

<amp-img 
  layout="responsive" width="1080" height="720"
  srcset="https://unsplash.it/1080/720?image=1043 1080w, https://unsplash.it/720/480?image=1043 720w" 
  alt="Photo of mountains and trees landscape">
</amp-img>

With this code, we are creating a responsive image by specifying layout="responsive" and providing width and height.

Why do I have to specify width and height when using responsive layout?

Two reasons:

  1. AMP uses width and height to calculate the aspect-ratio and maintain the correct height as width changes to fit its parent container.
  2. AMP enforces static sizing for all elements to ensure a good UX (no jumps in the page), and to determine each element's size and position to layout the page before resources are downloaded.

Let's take a look at the page now:

Add an autoplaying video

AMP supports many third-party video players such as YouTube and Vimeo and has its own version of HTML5 video element under amp-video extended component. Some of these video players, including amp-video and amp-youtube support muted autoplay on mobile as well.

Similar to amp-img, amp-video can become responsive by just adding layout="responsive"

Let's add an autoplaying video to our page.

Add another paragraph and the following amp-video element to main:

<p class="my2 px3">Vivamus viverra augue libero, vitae dapibus lectus accumsan eget. Pellentesque eget ipsum purus. Maecenas leo odio, ornare nec ex id, suscipit porta ipsum. Ut fringilla semper cursus.</p>

<amp-video 
  layout="responsive" width="1280" height="720"
  autoplay controls loop
  src="https://storage.googleapis.com/ampconf-76504.appspot.com/Bullfinch%20-%202797.mp4">
</amp-video>

Let's take a look:

Add an embed

AMP has extended components for many third-party embeds such as Twitter and Instagram, and for the ones we don't have a component for, there is always amp-iframe!

Now let's add an instagram embed to our page.

Unlike amp-img and amp-video, amp-instagram is not a built-in component and the import script tag for it must be included explicitly in head the AMP page before the component can be used.

The AMP Start boilerplate that we are using already has included several import script tags, look for them at the beginning of the head tag and ensure the following import script line is included:

<script custom-element="amp-instagram" src="https://cdn.ampproject.org/v0/amp-instagram-0.1.js" async></script>

Add another paragraph and the following amp-instagram element to main:

<p class="my2 px3">Vivamus viverra augue libero, vitae dapibus lectus accumsan eget. Pellentesque eget ipsum purus. Maecenas leo odio, ornare nec ex id, suscipit porta ipsum. Ut fringilla semper cursus.</p>
    
<amp-instagram 
   layout="responsive" width="566" height="708"
   data-shortcode="BRLbKh2hzMA">
</amp-instagram>

Let's take a look:

That's probably enough content for now!

Summary

The completed code for this section can be found here: http://codepen.io/aghassemi/pen/OpXGoa

So far we have only created static content for our page, in this section we will create an interactive photo gallery using components such as carousel, lightbox and AMP actions.

Although AMP does not support custom JavaScript, it still exposes several building blocks to receive and handle user actions.

Add a photo carousel

Having every image for our photo-focused AMP page visible on the page will not create a great user experience. Fortunately we can use amp-carousel to create a horizontally swipeable slides of photos.

First, let's make sure the script tag for amp-carousel is included in head:

<script custom-element="amp-carousel" src="https://cdn.ampproject.org/v0/amp-carousel-0.1.js" async></script>

Now let's add a responsive amp-carousel of type slides with several images to main

<p class="my2 px3">Vivamus viverra augue libero, vitae dapibus lectus accumsan eget. Pellentesque eget ipsum purus. Maecenas leo odio, ornare nec ex id, suscipit porta ipsum. Ut fringilla semper cursus.</p>

<amp-carousel 
  layout="responsive" width="1080" height="720"
  type="slides">

    <amp-img src="https://unsplash.it/1080/720?image=1037" layout="fill"></amp-img>
    <amp-img src="https://unsplash.it/1080/720?image=1038" layout="fill"></amp-img>
    <amp-img src="https://unsplash.it/1080/720?image=1039" layout="fill"></amp-img>
    <amp-img src="https://unsplash.it/1080/720?image=1040" layout="fill"></amp-img>
    <amp-img src="https://unsplash.it/1080/720?image=1041" layout="fill"></amp-img>
    <amp-img src="https://unsplash.it/1080/720?image=1042" layout="fill"></amp-img>
    <amp-img src="https://unsplash.it/1080/720?image=1043" layout="fill"></amp-img>
    <amp-img src="https://unsplash.it/1080/720?image=1044" layout="fill"></amp-img>
</amp-carousel>

type="slides" ensures only one image is visible at a time and allows users to swipe through them.

For the images inside the carousel, we use layout="fill" since slide carousel always fills its size with the child element, so there is no need to specify a different layout that requires width and height.

Let's try it out and see how it looks:

Add a thumbnails carousel

Now let's add a horizontally scrollable container to host the thumbnails for these images. We will use <amp-carousel> again but without type="slides" and with a fixed-height layout.

Add the following right after the previous amp-carousel element.

<amp-carousel layout="fixed-height" height="78" class="mt1">

    <amp-img src="https://unsplash.it/108/72?image=1037" layout="fixed" width="108" height="72"></amp-img>
    <amp-img src="https://unsplash.it/108/72?image=1038" layout="fixed" width="108" height="72"></amp-img>
    <amp-img src="https://unsplash.it/108/72?image=1039" layout="fixed" width="108" height="72"></amp-img>
    <amp-img src="https://unsplash.it/108/72?image=1040" layout="fixed" width="108" height="72"></amp-img>
    <amp-img src="https://unsplash.it/108/72?image=1041" layout="fixed" width="108" height="72"></amp-img>
    <amp-img src="https://unsplash.it/108/72?image=1042" layout="fixed" width="108" height="72"></amp-img>
    <amp-img src="https://unsplash.it/108/72?image=1043" layout="fixed" width="108" height="72"></amp-img>
    <amp-img src="https://unsplash.it/108/72?image=1044" layout="fixed" width="108" height="72"></amp-img>

</amp-carousel>

Note that for the thumbnail images we simply used layout="fixed" and low-resolution versions of the same photos.

Let's take a look:

Change the image when user taps a thumbnail

To do this, we need to tie events such as "tap" to actions such as "change the slide to n".

event: We can use the on attribute to install event handlers on an element and the tap event is supported on all elements.

action: amp-carousel exposes a goToSlide(index=INTEGER) action that we can call from the tap event handler of each thumbnail image.

Now that we know about event and action, let's tie them together.

First we need to give the slides carousel an id so we can reference it from the tap event handler on the thumbnails.

Modify your existing code to add an id attribute to the slides carousel (first one):

<amp-carousel 
  id="imageSlides"
  type="slides"

  ....

Now let's install the event handler (on="tap:imageSlides.goToSlide(index=<slideNumber>)")" on each thumbnail image:

<amp-img on="tap:imageSlides.goToSlide(index=0)" role="button" tabindex="1" layout="fixed" ...
<amp-img on="tap:imageSlides.goToSlide(index=1)" role="button" tabindex="1" layout="fixed" ...
<amp-img on="tap:imageSlides.goToSlide(index=2)" role="button" tabindex="1" layout="fixed" ...
...

Note that we must also give it a tabindex and set the ARIA role for accessibility.

That's it. Now tapping each thumbnail image shows the corresponding image inside the slides carousel!

Highlight the thumbnail when user taps it

Can we do this? There doesn't seem to be any actions to change CSS classes for an element to call from the tap event handlers. So how can we highlight the selected thumbnail?

<amp-selector> to rescue!

amp-selector is different than components we have used so far. It is not a presentation component as it does not affect the layout of the page, but rather is a building block that allows the AMP page to know what option user has selected.

What amp-selector does is fairly simple yet powerful. In short:

Let's see how this helps us accomplish the task in hand.

Add the script tag for amp-selector to head:

<script custom-element="amp-selector" src="https://cdn.ampproject.org/v0/amp-selector-0.1.js" async></script>
  1. Wrap the thumbnail carousel in a amp-selector
  2. Make every thumbnail an option by adding option=<value> attribute.
  3. Make the first thumbnail selected by default by adding the selected attribute as well.
<amp-selector>

  <amp-carousel layout="fixed-height" height="78" class="mt1">

    <amp-img option=0 selected on="tap:imageSlides.goToSlide(index=0)" ...
      
    <amp-img option=1 on="tap:imageSlides.goToSlide(index=1)" ...

    ...

  </amp-carousel>

</amp-selector>

Now we need to add styling to highlight the selected thumbnail.

Add the following custom CSS in <style amp-custom> right after the minified CSS boilerplate from AMP Start

<style amp-custom>
...

/* low opacity for non-selected thumbnails */
amp-selector amp-img[option] {
  opacity: 0.4;
}

/* normal opacity for the selected thumbnail */
amp-selector amp-img[option][selected] {
  opacity: 1;
}

</style>

Let's take a look:

Looks good, but did you notice a bug?

If user swipes the slides carousel, the selected thumbnail does not update to reflect that! How can we bind the current slide in the carousel with the selected thumbnail?

In the next section, we will learn how!

Summary

The completed code for this section can be found here: http://codepen.io/aghassemi/pen/gmMJMy

In this section we will use an experimental feature of AMP called amp-bind to improve the interactivity of the image gallery from the previous section.

What is amp-bind?

It is a new extension that supports custom interactivity via data binding and expressions.

It has three key parts:

  1. State
  2. Binding
  3. Mutation

State is just a shared variable (can be as simple as a single value or a whole data structure). All components can read and write to this shared variable.

Binding is an expression that ties the state to an HTML attribute or text of an element (For both AMP and HTML elements).

Mutation is the action of changing the value of the state as result of some user action or event.

The magic of amp-bind starts when a mutation happens: All components that have a binding to that state will be notified and will update themselves automatically to reflect the new state!

Let's see it in action!

Use amp-bind to reimplement the image gallery

Previously we used AMP actions (e.g. goToSlide()) to tie the full-image slides carousel with tap event on the thumbnail images and used amp-selector to highlight the selected thumbnail.

Let's see how we can completely reimplement this code using amp-bind's approach to data binding.

But before we start coding, let's design our approach:

(1) What is our state?

Fairly simple in our case, the only value we care about is what the current slide number is. So, selectedSlide is our state.

(2) What are our bindings?

Let's put it this way, "What needs to change when selectedSlide changes?"

  1. The visible slide of the full-image carousel:
<amp-carousel [slide]="selectedSlide" ...
  1. The selected item in amp-selector needs to change too. This will fix the bug we ran into in the previous section!
<amp-selector [selected]="selectedSlide" ...

(3) What are our mutation?

Let's put it this way, "When does selectedSlide need to change? "

  1. When the user changes to a new slide in the full-image carousel by swiping:
<amp-carousel on="slideChange:AMP.setState(selectedSlide=event.index)" ...
  1. When the user selects a thumbnail:
<amp-selector on="select:AMP.setState(selectedSlide=event.targetOption)" ...

Let's use AMP.setState to trigger a mutation, which means that we no longer need all the on="tap:imageSlides.goToSlide(index=n)" code we had on thumbnail anymore!

Let's put it all together:

1) Add the script tag for amp-bind to head:

<script custom-element="amp-bind" src="https://cdn.ampproject.org/v0/amp-bind-0.1.js" async></script>

2) Replace the existing gallery code with the new approach:

<amp-carousel [slide]="selectedSlide" on="slideChange:AMP.setState(selectedSlide=event.index)" type="slides" id="imageSlides" layout="responsive" width="1080" height="720">

  <amp-img src="https://unsplash.it/1080/720?image=1037" layout="fill"></amp-img>
  <amp-img src="https://unsplash.it/1080/720?image=1038" layout="fill"></amp-img>
  <amp-img src="https://unsplash.it/1080/720?image=1039" layout="fill"></amp-img>
  <amp-img src="https://unsplash.it/1080/720?image=1040" layout="fill"></amp-img>
  <amp-img src="https://unsplash.it/1080/720?image=1041" layout="fill"></amp-img>
  <amp-img src="https://unsplash.it/1080/720?image=1042" layout="fill"></amp-img>
  <amp-img src="https://unsplash.it/1080/720?image=1043" layout="fill"></amp-img>
  <amp-img src="https://unsplash.it/1080/720?image=1044" layout="fill"></amp-img>

</amp-carousel>


<amp-selector [selected]="selectedSlide" on="select:AMP.setState(selectedSlide=event.targetOption)">

  <amp-carousel layout="fixed-height" height="78" class="mt1">

    <amp-img option=0 selected src="https://unsplash.it/108/72?image=1037" layout="fixed" width="108" height="72"></amp-img>
    <amp-img option=1 src="https://unsplash.it/108/72?image=1038" layout="fixed" width="108" height="72"></amp-img>
    <amp-img option=2 src="https://unsplash.it/108/72?image=1039" layout="fixed" width="108" height="72"></amp-img>
    <amp-img option=3 src="https://unsplash.it/108/72?image=1040" layout="fixed" width="108" height="72"></amp-img>
    <amp-img option=4 src="https://unsplash.it/108/72?image=1041" layout="fixed" width="108" height="72"></amp-img>
    <amp-img option=5 src="https://unsplash.it/108/72?image=1042" layout="fixed" width="108" height="72"></amp-img>
    <amp-img option=6 src="https://unsplash.it/108/72?image=1043" layout="fixed" width="108" height="72"></amp-img>
    <amp-img option=7 src="https://unsplash.it/108/72?image=1044" layout="fixed" width="108" height="72"></amp-img>

  </amp-carousel>

</amp-selector>

Let's test it out! Tap a thumbnail and image slides will change, swipe the image slides and the highlighted thumbnail will change!

Add "Image x/of y" text to the gallery

We have already done the heavy work to define and mutate a state for our current slide. Now we can easily provide additional bindings to update other pieces of information based on the current slides number.

Let's add "Image x/of y" text to our gallery:

Add the following code above the slides carousel element:

<div class="px3">Image <span [text]="1*selectedSlide + 1">1</span> of 8</div>

This time we are binding to the inner text of an element using [text]= instead of binding to an HTML attribute.

Let's try it out:

Summary

The completed code for this section can be found here: http://codepen.io/aghassemi/pen/MpeMdL

In this section we will use two new experimental features to add animation to our page.

Add parallax effect to the title

amp-fx-parallax is an experimental feature that allows an element to move as if it is nearer or farther relative to the foreground of the page content.

As the user scrolls the page, the element scrolls faster or slower depending on the value assigned to the attribute.

The parallax effect can be enabled by adding the amp-fx-parallax=<factor> attribute to any HTML or AMP element.

Let's add parallax with a factor of 2.0 to our page title and see how it looks!

Add the script tag for amp-fx-parallax to head:

<script custom-element="amp-fx-parallax" src="https://cdn.ampproject.org/v0/amp-fx-parallax-0.1.js" async></script>

Now, find the existing header title element in the code and add the amp-fx-parallax="2.0" attribute to it:

<header amp-fx-parallax="2.0" class="p3">
  <h1 class="ampstart-fullpage-hero-heading mb3">
    <span class="ampstart-fullpage-hero-heading-text">
      Most Beautiful Hikes in the Pacific Northwest
    </span>
  </h1>
  <span class="ampstart-image-credit h4">
    By <a href="#" role="author" class="text-decoration-none">D.F. Roy</a>,<br> January 14, 2017
  </span>
</header>

Let's take a look at the result:

Title is now scrolling faster than the user, creating a slide-out-of-sight effect. Cool!

Add animation to the page

amp-animation is an experimental feature that brings the Web Animations API to AMP pages.

In this task we will use amp-animation to create a subtle zoom-in effect for the cover image.

Add the script tag for amp-animation to head:

<script custom-element="amp-animation" src="https://cdn.ampproject.org/v0/amp-animation-0.1.js" async></script>

Now we need to define our animation and the target element it applies to.

Animations are defined as JSON inside a top-level amp-animation tag.

Insert the following code directly below the opening body tag in your page.

<amp-animation trigger="visibility" layout="nodisplay">
    <script type="application/json">
      {
        "target": "heroimage",
        "duration": 30000,
        "delay": 0, 
        "fill": "forwards",
        "easing": "ease-out",
        "keyframes": {"transform":  "scale(1.3)"}
      }
      </script>
</amp-animation>

Here are we are defining an animation that runs for 30 seconds without delay and scales the image to be 30% larger.

We define a forwards fill to allow the image to stay zoomed in after the animation ends. target is the HTML id of the element that animation applies to.

Let's add an id to the hero image element in our page so amp-animation can act on it.

  1. Locate the existing hero image (the high-resolution one with layout="fixed-height") in your code and add id="heroimage" to the amp-img tag.
  2. For the sake of simplicity also delete media="(min-width: 416px)" and also remove the other low-resolution amp-img so we don't have to deal with multiple animations and media queries in amp-animation for now.
<figure class="ampstart-image-fullpage-hero m0 relative mb4">

    <amp-img id="heroimage" height="1800" layout="fixed-height" src="https://unsplash.it/1200/1800?image=1003"></amp-img>

    <figcaption class="absolute top-0 right-0 bottom-0 left-0">

...

As you may have noticed, scaling the image will make it overflow its parent, so we need to fix that by hiding the overflow.

Add the following CSS rule to the end of the existing <style amp-custom>

.ampstart-image-fullpage-hero {
  overflow: hidden;
}

Let's try it out and see how it looks:

Subtle!

But I could have done that with CSS anyway, what's the point of amp-animation?

Although true in this case, amp-animation brings extra functionality not possible with CSS. For instance animation can be triggered based on visibility (and pause based on visibility as well) or they can be triggered via an AMP action. amp-animation is also based on Web Animations API which itself has more features than CSS animations, specially around composability.

Summary

The completed code for this section can be found here: http://codepen.io/aghassemi/pen/OpXKzo

You've just finished creating a beautiful, interactive and canonical AMP page.

Let's celebrate by taking another look at what you have accomplished today.

Here is a link to the finished page: http://s.codepen.io/aghassemi/debug/OpXKzo and the final code: http://codepen.io/aghassemi/pen/OpXKzo

The collection of Codepens for this codelab can be found here: https://codepen.io/collection/XzKmNB/

What's Next?

This codelab only scratches the surface of what's possible in AMP. There are many resources and codelabs available to help you create amazing AMP pages:

If you have questions, or run into issues, please find us on AMP Slack Channel or create discussion/bug/feature issues on GitHub.

Oh! but before we go,

We forgot to check how our page looks on other form factors like tablet in landscape mode.

Let's see:

Phew!

Have a beautiful day.