Beautiful, interactive, canonical AMP pages

AMP makes it easier to build reliable, responsive, performant web pages. AMP lets you create common site interactions without writing JavaScript. The amp.dev website includes predesigned, quick-start templates.

What you'll build

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

  • 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-collection and amp-animation

What you'll need

  • A modern web browser
  • Node.js and a text editor or access to CodePen or a similar online playground
  • Basic knowledge of HTML, CSS, JavaScript, and Google Chrome Developer Tools

Tooling for serving content

We'll use Node.js to run a local HTTP server to serve our AMP page. Check the Node.js website to learn how to install it.

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

npm install -g serve

Download a template from amp.dev

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

Visit AMP Templates and download the code for the "Simple Article" for "The Year's Best Animal Photos" template.

Running the template code

Extract the contents of the ZIP file.

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

Visit http://localhost:5000/templates/article.amp.html in your browser. (The port might be 3000 or a different number depending on the version of serve. Check the console for the exact address.)

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've scaffolded a mostly functioning AMP page, 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 containing only some boilerplate code:

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

An AMP page is an HTML page with extra tags and 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 that you created during setup, which includes this boilerplate and has a few additions⁠—importantly, a <style amp-custom> tag that includes lots of minified CSS.

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

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

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

We'll get help from the AMP Start UI components page, 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 and paste the HTML code provided for RESPONSIVE MENUBAR into the 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 the amp-sidebar and amp-accordion AMP components.

Add hero image and title

AMP Start provides ready-to-use snippets for beautiful, responsive hero images and titles, as well.

Head to https://ampstart.com/components#media and copy and 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 is 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

  • You created a shell for your page, including responsive navigation and a hero image and title.
  • You learned more about AMP Templates and used the AMP Start UI components to quickly put together a page shell.

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

In this section we'll add responsive images, videos, embeds, and 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, and ampstart-dropcap? Where are they coming from?

These classes are not part of AMP HTML. The AMP Start templates use Basscss to provide a low-level CSS toolkit and add classes specific to AMP Start.

In this snippet, px3 and mb2 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 many cases, making an AMP component responsive is as simple as adding a layout="responsive" attribute. Similar to the 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 user experience (no jumps in the page), and to determine each element's size and position to lay out the page before resources are downloaded.

Let's take a look at the page now:

Add an autoplaying video

AMP supports many video players, such as YouTube and Vimeo. AMP has its own version of the HTML5 video element under the 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 with the addition of 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. For embeds that lack an AMP component, there is always amp-iframe.

Let's add an Instagram embed to our page.

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

The AMP Start boilerplate that we are using includes several import script tags. Look for them at the beginning of the head tag and ensure that 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="BJ_sPxzAGyg">
</amp-instagram>

Let's take a look:

That's probably enough content for now.

Summary

  • You learned about responsive components in AMP.
  • You added different types of media and text content.

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'll create an interactive photo gallery using components such as a carousel, a lightbox, and AMP actions.

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

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 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 that only one image is visible at a time, and it allows users to swipe through them.

For the images inside the carousel, we use layout="fill", since a 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:

1.gif

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

Add the following 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 changing the slide.

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 the event and the 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 slide carousel.

2.gif

Highlight the thumbnail when the user taps it

Can we do this? There don'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 the rescue!

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

What amp-selector does is fairly simple yet powerful:

  • amp-selector can contain any arbitrary HTML elements or AMP components.
  • Any descendant element of amp-selector can become an option if it has an option=<value> attribute.
  • When a user taps an element that is an option, amp-selector just adds a selected attribute to that element (and removes it from other option elements in single-selection mode).
  • You can style the selected element in your custom CSS by targeting the selected attribute with a CSS attribute selector.

Let's see how this helps us accomplish the task at 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 an amp-selector
  2. Make every thumbnail an option by adding an option=<value> attribute.
  3. Make the first thumbnail selected by default by adding the selected attribute.
<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> 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:

3.gif

Looks good, but did you notice a bug?

If user swipes the slide 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'll learn how.

Summary

  • You learned about different types of carousels and how to use them.
  • You used AMP actions and events to change the visible slide in the image carousel when the user taps a thumbnail image.
  • You learned about amp-selector and how it can be used as a building block to implement interesting use-cases.

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

In this section, we'll use amp-bind to improve the interactivity of the image gallery from the previous section.

What is amp-bind?

The core AMP component amp-bind lets you create custom interactivity with data binding and expressions.

amp-bind has three key parts:

  1. State
  2. Binding
  3. Mutation

State is an application state variable, containing anything from a single value to a complex 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 the content of an element.

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

The power 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!

Previously, we used AMP actions (for example, goToSlide()) to tie the full-image slide carousel with a tap event on the thumbnail images and used amp-selector to highlight the selected thumbnail.

Let's see how we can completely re-implement this code using the amp-bind 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?

What needs to change when selectedSlide changes?

  • The visible slide of the full-image carousel:
<amp-carousel [slide]="selectedSlide" ...
  • 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 mutations?

When does selectedSlide need to change?

  • When the user changes to a new slide in the full-image carousel by swiping:
<amp-carousel on="slideChange:AMP.setState({selectedSlide:event.index})" ...
  • 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 of the on="tap:imageSlides.goToSlide(index=n)" code we had on thumbnail anymore!

Let's put it all together:

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>

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.

4.gif

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're binding to the inner text of an element using [text]= instead of binding to an HTML attribute.

Let's try it out:

5.gif

Summary

  • You learned about amp-bind.
  • You used amp-bind to implement an improved version of the image gallery.

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

In this section we'll use two new features to add animation to our page.

Add a parallax effect to the title

amp-fx-collection is an extension that provides a collection of preset visual effects such as parallax that can be easily enabled on any element with attributes.

With the parallax effect, 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" data-parallax-factor="<a decimal factor>" attribute to any HTML or AMP element.

  • A factor value greater than 1 makes the element scroll faster when the user scrolls down the page.
  • A factor value less than 1 makes the element scroll slower when the user scrolls down the page.

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

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

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

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

<header amp-fx="parallax" data-parallax-factor="1.5" 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:

6.gif

The title is now scrolling faster than the rest of the page. Cool!

Add animation to the page

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

In this section, we'll use amp-animation to create a subtle zoom-in effect for the cover image.

Add the script tag for amp-animation to the 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>

This code defines an animation that runs for 30 seconds without delay and scales the image to be 30% larger.

We define a forward fill to allow the image to stay zoomed in after the animation ends. target is the HTML id of the element that the 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:

7.gif

Subtle!

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

That's true in this case, but amp-animation enables extra functionality that can't be done with CSS alone. For example, animation can be triggered based on visibility (and pause based on visibility as well) or it can be triggered with an AMP action. amp-animation is also based on the Web Animations API, which itself has more features than CSS animations, especially around composability.

Summary

  • You learned about creating parallax effects with amp-fx-collection.
  • You learned about amp-animation.

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

You've just finished creating a beautiful, interactive 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

Untitled.gif

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

Oh, before we go...

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

Let's see:

Untitled.gif

Excellent!

Have a beautiful day.

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 to create amazing AMP pages:

If you have questions or run into issues, please find us on the AMP Slack channel or create discussions, bug reports, or feature requests on GitHub.