Products by Example

Fruit

Vegetable

More

View Demo View Source

Product

Experimental Mode

This example uses the following experimental feature: [amp-form-var-sub]
Enable them below and learn more here.

Enable Dev Channel

Introduction

This sample showcases how to build a product page in AMP HTML.

<!doctype html>
<html ⚡>
<head>
    <meta charset="utf-8">
    <script async src="https://cdn.ampproject.org/v0.js"></script>

Additionally used AMP components must be imported in the header.

    <script async custom-element="amp-carousel" src="https://cdn.ampproject.org/v0/amp-carousel-0.1.js"></script>
Show Hidden Code Hide Code
    <script async custom-element="amp-accordion" src="https://cdn.ampproject.org/v0/amp-accordion-0.1.js"></script>
    <script async custom-element="amp-list" src="https://cdn.ampproject.org/v0/amp-list-0.1.js"></script>
    <script async custom-template="amp-mustache" src="https://cdn.ampproject.org/v0/amp-mustache-0.1.js"></script>
    <script async custom-element="amp-social-share" src="https://cdn.ampproject.org/v0/amp-social-share-0.1.js"></script>
    <script async custom-element="amp-lightbox" src="https://cdn.ampproject.org/v0/amp-lightbox-0.1.js"></script>
    <script async custom-element="amp-youtube" src="https://cdn.ampproject.org/v0/amp-youtube-0.1.js"></script>
    <script async custom-element="amp-form" src="https://cdn.ampproject.org/v0/amp-form-0.1.js"></script>
    <script async custom-element="amp-sidebar" src="https://cdn.ampproject.org/v0/amp-sidebar-0.1.js"></script>

    <link rel="canonical" href="https://ampbyexample.com/samples_templates/product/">
    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
    <style amp-custom>
    /* general styling */
    article#preview {
      background-color: #f5f5f5;
    }
    amp-carousel {
      margin: 0;
    }
    /* header with logo and carousel */
    .header {
      position: relative;
      box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);
      padding-top: 52px;
    }
    .header {
      background-color: #607D8B;
    }
    .header #hamburger {
      position:absolute;
      top:0;
      left: 0;
      font-size: 18px;
      font-weight: 500;
      color: white;
      text-transform: uppercase;
      padding: 13px;
    }
    .header #logo {
      position:absolute;
      top:0;
      left:36px;
      font-size: 18px;
      font-weight: 400;
      color: white;
      text-transform: uppercase;
      margin: 16px;
      height: 32px;
    }
    /* search form */
    .header form {
      padding: 0;
    }
    .header input::-webkit-search-decoration,
    .header input::-webkit-search-cancel-button {
      display: none;
    }
    .header input[type=submit] {
      position:absolute;
      top:10px; right:8px;
      background-color: #eb407a;
      background-position: center;
      background-repeat: no-repeat;
      background-image: -webkit-image-set(
        url('/img/ic_search_white_1x_web_24dp.png') 1x,
        url('/img/ic_search_white_2x_web_24dp.png') 2x
      );
      border: 0 none;
      height: 32px;
      width: 36px;
      -webkit-appearance: none;
      border-radius: 0;
      -moz-border-top-right-radius: 4px;
      -moz-border-bottom-right-radius: 4px;
      -webkit-border-top-right-radius: 4px;
      -webkit-border-bottom-right-radius: 4px;
      border-top-right-radius: 4px;
      border-bottom-right-radius: 4px;
    }
    .header input[type=search] {
      position: absolute;
      top: 10px; 
      right: 24px;
      background: #ededed ;
      padding: 0 8px;
      width: 20vw;
      height: 32px;
      font-family: inherit;
      font-size: 100%;
      -moz-box-sizing: border-box;
      -webkit-box-sizing: border-box;
      box-sizing: border-box;
      -webkit-appearance: none;
      border: solid 0px #ccc;
      border-radius: 0;
      -moz-border-top-left-radius: 4px;
      -moz-border-bottom-left-radius: 4px;
      -webkit-border-top-left-radius: 4px;
      -webkit-border-bottom-left-radius: 4px;
      border-top-left-radius: 4px;
      border-bottom-left-radius: 4px;
      -webkit-transition: all .9s;
      -moz-transition: all .9s;
      transition: all .9s ease;
    }
    .header input[type=search]:focus {
      box-sizing: border-box;
      width: 80vw;
      max-width: 700px;
      height: 32px;
      background-color: #fff;
      border-color: #eb407a;
      -webkit-box-shadow: 0 0 5px rgba(109,207,246,.5);
      -moz-box-shadow: 0 0 5px rgba(109,207,246,.5);
      box-shadow: 0 0 5px rgba(109,207,246,.5);
      outline:none;
    }
    .header input:-moz-placeholder,
    .header input::-webkit-input-placeholder {
      color: #999;
    }
    .header input:focus + #logo {
      visibility: hidden;
      opacity: 0;
      transition: visibility 0s 0.7s, opacity 0.7s ease;
    }
    .header input + #logo {
      visibility: visible;
      opacity: 1;
      transition: opacity 1.5s ease;
    }
    @media (min-width: 600px) {
      .header input[type=search] {
        width: 100px;
      }
      .header input[type=search]:focus {
        width: 600px;
      }
    }
    /* items */
    .items,
    amp-list.items > div {
      display: flex;
      justify-content:space-around;
      flex-wrap: wrap;
    }
    .item {
      flex-grow: 1;
      flex-shrink: 1;
    }
    /* tiles */
    .tile {
      background-color: white;
      width: 120px;
      height: 200px;
      display: block;
      margin: 8px;
      -webkit-tap-highlight-color: #eee;
      max-width: 200px;
    }
    @media (max-width: 500px) {
      .items .tile {
        width: 150px;
        height: 200px;
      }
    }
    .tile:active {
      background-color: #eee;
    }
    .tile p {
      margin: 0;
      padding: 0 8px;
      font-size: 14px;
      line-height:20px;
    }
    .tile .name {
      margin-top: 8px;
    }
    .tile .price {
      margin-bottom: 8px;
    }
    .tile .price,
    .tile .star {
      color: black;
    }
    .call-to-action {
        display: flex;
        flex-direction: row;
        align-items: center;
        justify-content: space-between;
    }
    #product-description {
        clear: both;
    }
    amp-lightbox {
        background: rgba(0,0,0,0.8);
        width: 100%;
        height: 100%;
        position: absolute;
    }
    amp-accordion p, amp-accordion h4 {
        padding: 16px;
    }
    amp-accordion h4 {
        font-size: 18px;
    }
    .play {
        padding-left: 28px;
        background-position: left center;
        background-repeat: no-repeat;
        background-image: -webkit-image-set(url(/img/ic_play_circle_filled_black_1x_web_24dp.png) 1x,url(/img/ic_play_circle_filled_black_2x_web_24dp.png) 2x);
    }
    .amp-youtube-container {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 100%;
        height: 100%;
    }
    #video-container, #social, form .button {
        margin: 16px;
    }
    .video {
        flex: 1;
    }
    .price-description {
        color: grey;
        font-weight: 400;
    }
    form {
        flex-wrap: nowrap;
    }
    form.amp-form-submit-success > #add-to-cart {
        display: none;
    }
    form.amp-form-submitting > #add-to-cart {
        color: #919191;
    }
    form .button {
        min-width: 110px;
        padding: 0px;
    }
    .quantity {
        margin: 16px 0;
    }
    </style>

Metadata

The page indexing requires schema.org markup for one of the following types: Type, AggregateRating, Offers. Learn more.

    <script type="application/ld+json">
    {
        "@context": "http://schema.org/",
        "@type": "Product",
        "name": "Apple",
        "image": "http://www.ampbyexample.com/img/product1_1024x682.jpg",
        "description": "Lorem ipsum",
        "mpn": "925872",
        "brand": {
            "@type": "Fruit",
            "name": "Apple"
        },
        "aggregateRating": {
            "@type": "AggregateRating",
            "ratingValue": "4.4",
            "reviewCount": "88"
        },
        "offers": {
            "@type": "Offer",
            "priceCurrency": "USD",
            "price": "1.99",
            "priceValidUntil": "2020-11-05",
            "itemCondition": "http://schema.org/UsedCondition",
            "availability": "http://schema.org/InStock",
            "seller": {
                "@type": "Retail",
                "name": "AMP by Example"
            }
        }
    }
    </script>
Show Hidden Code Hide Code

    <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>
    </head>
<body>

Use the amp-sidebar component to give customers the chance to quickly jump to any of your product categories.

  <amp-sidebar id="drawermenu"
      layout="nodisplay">
    <div class="topheader">
      <a href="/"
          class="home">Products by Example</a>
    </div>
    <hr/>
    <h4 class="category expanded">
      <a href="/samples_templates/product_listing/preview/">Fruit</a>
    </h4>
    <h4 class="category">
      <a href="/samples_templates/product_listing/preview/">Vegetable</a>
    </h4>
    <h4 class="category">
      <a href="/samples_templates/product_listing/preview/">More</a>
    </h4>
  </amp-sidebar>

AMP supports forms, which means you can directly integrate a product search into your AMPs. Try searching for "Apple".

AMP doesn't support custom Javascript, but you can use CSS3 animations to enrich your page. The expanding text field when the search box is focused is implemented with CSS only.

  <div class="header">
    <a id="hamburger"
        on="tap:drawermenu.toggle">
      <amp-img srcset="/img/ic_menu_white_1x_web_24dp.png 1x, /img/ic_menu_white_2x_web_24dp.png 2x"
          width="24"
          height="24"
          alt="navigation"></amp-img>
    </a>
    <form method="GET"
        action="https://ampbyexample.com/samples_templates/product_listing/search"
        target="_top">
      <input name="search"
          type="search"
          placeholder="Search">
      <a id="logo"
          href="/">Products by Example</a>
      <input type="submit"
          value="">
    </form>
  </div>

  <h2>Apple</h2>
  <p id="product-description">Lorem ipsum dolor sit amet, ut sed non vel mattis. Et nulla suscipit ante ligula nisl in. Ut venenatis et mauris, mauris porta vulputate tellus mauris integer facilisis.</p>
  <h5 class="price-description">From $1.99</h5>

Apple

Lorem ipsum dolor sit amet, ut sed non vel mattis. Et nulla suscipit ante ligula nisl in. Ut venenatis et mauris, mauris porta vulputate tellus mauris integer facilisis.

From $1.99

The amp-carousel works very well for product image galleries. Learn more about amp-carousel here.

  <amp-carousel width="512"
      height="384"
      layout="responsive"
      type="slides">
    <amp-img src="/img/product1_1024x682.jpg"
        width="1024"
        height="682"
        layout="responsive"></amp-img>
    <amp-img src="/img/product1_alt1_1024x682.jpg"
        width="1024"
        height="682"
        layout="responsive"></amp-img>
    <amp-img src="/img/product1_alt2_1024x682.jpg"
        width="1024"
        height="682"
        layout="responsive"></amp-img>
  </amp-carousel>

Product Quantity

The add to cart action is implemented using amp-form. In our sample we show a dropdown menu to select the item quantity. Pressing the ADD TO CART button adds the product to a shopping cart page using the quantity you have selected. This is realized by using a POST action which enables the button VIEW CART if there is no error.

Pressing the VIEW CART button leads to a shopping cart page. amp-form doesn't support automatic redirect after post (yet). Instead, we use a button to redirect the user to a shopping cart. Notice that the button URL contains the query clientId={{ClientId}}

We are using the CLIENT_ID variable to identify the user, this enables storing a shopping cart between repeated visits of an AMP page (either via the AMP Cache or the original host). This variable can be used inside an amp-form by declaring an hidden input value with default-value="CLIENT_ID(cart). Read more about variable substitution here.


  <div class="call-to-action">
    <form method="POST"
        action-xhr="https://ampbyexample.com/samples_templates/product/add_to_cart"
        target="_top">
      <select name="quantity"
          class="data-input quantity">
        <option>1</option>
        <option>2</option>
        <option>3</option>
      </select>
      <input type="submit"
          class="button button-primary"
          id="add-to-cart"
          value="ADD TO CART">
      <input type="hidden"
          name="name"
          value="Apple">
      <input type="hidden"
          name="price"
          value="$1.99">
      <input type="hidden"
          name="img"
          value="/img/product1_1024x682.jpg">
      <input name="clientId"
          type="hidden"
          value="CLIENT_ID(cart)"
          data-amp-replace="CLIENT_ID">
      <div class="submit-success"
          submit-success>
        <template type="amp-mustache">
          <a class="button button-primary"
              href="https://ampbyexample.com/shopping_cart/?clientId={{ClientId}}">view cart</a>
        </template>
      </div>
      <div submit-error>
        <template type="amp-mustache">
          Error! Looks like something went wrong with your shopping cart, please try to add an item again. {{error}}
        </template>
      </div>
    </form>
  </div>

Video

AMP supports a wide range of video platforms. Here we are using amp-youtube to show a product video. You can find an overview of all supported video platforms here.

In this sample we don't embed the video directly into the page, but instead show it in a lightbox using the amp-lightbox component. Learn more about amp-lightbox here.

  <div id="video-container">
    <a on="tap:watch-video-lightbox"
        role="button"
        class="button button-secondary play"
        tabindex="0">
      Show Video
    </a>

    <amp-lightbox id="watch-video-lightbox"
        layout="nodisplay">
      <div class="amp-youtube-container">
        <div class="video">
          <a on="tap:watch-video-lightbox.close"
              role="button"
              tabindex="0">
            Close
          </a>
          <amp-youtube width="480"
              height="270"
              layout=responsive
              data-videoid="lBTCB7yLs8Y">
          </amp-youtube>
        </div>
      </div>
    </amp-lightbox>
  </div>

Social

The Social Share extension provides a common interface for share buttons. Learn more about amp-social-share here.

  <div id="social">
    <amp-social-share type="twitter"
        width="30"
        height="22"></amp-social-share>
    <amp-social-share type="facebook"
        width="30"
        height="22"
        data-attribution=254325784911610></amp-social-share>
    <amp-social-share type="gplus"
        width="30"
        height="22"></amp-social-share>
    <amp-social-share type="email"
        width="30"
        height="22"></amp-social-share>
    <amp-social-share type="pinterest"
        width="33"
        height="22"></amp-social-share>
  </div>

Collapsibles/Accordion

Use amp-accordion to hide and show additional data about your product. Learn more about amp-accordion here.

  <div>
    <amp-accordion>
      <section>
        <h4>Description</h4>
        <p>Lorem ipsum dolor sit amet, mauris id, fermentum natoque ut et ullam dolore egestas. Vitae tortor maecenas nec consequat, nisl vestibulum est est curabitur. Mi eu in pellentesque lectus nostra ut. </p>
      </section>
      <section>
        <h4>Specification</h4>
        <p>Lorem ipsum dolor sit amet, mauris id, fermentum natoque ut et ullam dolore egestas. Vitae tortor maecenas nec consequat, nisl vestibulum est est curabitur. Mi eu in pellentesque lectus nostra ut. </p>
      </section>
      <section>
        <h4>Measurements</h4>
        <p>Lorem ipsum dolor sit amet, mauris id, fermentum natoque ut et ullam dolore egestas. Vitae tortor maecenas nec consequat, nisl vestibulum est est curabitur. Mi eu in pellentesque lectus nostra ut. </p>
      </section>
      <section>
        <h4>Other</h4>
        <p>Lorem ipsum dolor sit amet, mauris id, fermentum natoque ut et ullam dolore egestas. Vitae tortor maecenas nec consequat, nisl vestibulum est est curabitur. Mi eu in pellentesque lectus nostra ut. </p>
      </section>
    </amp-accordion>
  </div>

Description

Lorem ipsum dolor sit amet, mauris id, fermentum natoque ut et ullam dolore egestas. Vitae tortor maecenas nec consequat, nisl vestibulum est est curabitur. Mi eu in pellentesque lectus nostra ut.

Specification

Lorem ipsum dolor sit amet, mauris id, fermentum natoque ut et ullam dolore egestas. Vitae tortor maecenas nec consequat, nisl vestibulum est est curabitur. Mi eu in pellentesque lectus nostra ut.

Measurements

Lorem ipsum dolor sit amet, mauris id, fermentum natoque ut et ullam dolore egestas. Vitae tortor maecenas nec consequat, nisl vestibulum est est curabitur. Mi eu in pellentesque lectus nostra ut.

Other

Lorem ipsum dolor sit amet, mauris id, fermentum natoque ut et ullam dolore egestas. Vitae tortor maecenas nec consequat, nisl vestibulum est est curabitur. Mi eu in pellentesque lectus nostra ut.

  <h5 id="related-products">Related products</h5>

With AMP, you can easily pull in different latest offers or highlights without changing the page. To do so, just use amp-list to fire a CORS request to a JSON endpoint which supplies the list of related products. These are populated into an amp-mustache template on the client. Learn more about amp-list here

  <amp-list class="items"
      width=200
      height=100
      layout=responsive
      src="/json/related_products.json">
    <template type="amp-mustache">
      <a class="tile item"
          href="/samples_templates/product/preview/">
        <amp-img width="640"
            height="426"
            layout="responsive"
            alt="{{name}}"
            src="{{img}}"></amp-img>
        <p class="name">{{name}}</p>
        <p class="star">{{{stars}}}</p>
        <p class="price">${{price}}</p>
      </a>
    </template>
  </amp-list>

Analytics

Analytics must be configured in the body. Here we use Google Analytics to track pageviews.

  <amp-analytics type="googleanalytics">
    <script type="application/json">
      {
        "vars": {
          "account": "UA-73836974-1"
        },
        "triggers": {
          "default pageview": {
            "on": "visible",
            "request": "pageview",
            "vars": {
              "title": "Product"
            }
          }
        }
      }
    </script>
  </amp-analytics>

Next up:

</body>
</html>