AMP
  • websites

Star Rating

Introduction

This star rating widget is implemented using only CSS, given AMP's restriction on custom JavaScript. It still has the key features of star rating components:

  • touch, mouse and keyboard accessibility

  • stars change color when the user mouses over them

  • once a selection is made, it "sticks"

  • clean scalable vector icons

  • screen reader friendly

Setup

Import the amp-form component.

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

We also need the amp-mustache component.

<script async custom-template="amp-mustache" src="https://cdn.ampproject.org/v0/amp-mustache-0.2.js"></script>

CSS for the star rating

This star rating implementation overlays "☆" Unicode characters precisely over radio buttons. Inline comments below describe the rules in detail.

<style amp-custom>
  .rating {
    --star-size: 3;  /* use CSS variables to calculate dependent dimensions later */
    padding: 0;  /* to prevent flicker when mousing over padding */
    border: none;  /* to prevent flicker when mousing over border */
    unicode-bidi: bidi-override; direction: rtl;  /* for CSS-only style change on hover */
    text-align: left;  /* revert the RTL direction */
    user-select: none;  /* disable mouse/touch selection */
    font-size: 3em;  /* fallback - IE doesn't support CSS variables */
    font-size: calc(var(--star-size) * 1em);  /* because `var(--star-size)em` would be too good to be true */
    cursor: pointer;
    /* disable touch feedback on cursor: pointer - http://stackoverflow.com/q/25704650/1269037 */
    -webkit-tap-highlight-color: rgba(0,0,0,0);
    -webkit-tap-highlight-color: transparent;
    margin-bottom: 1em;
  }
  /* the stars */
  .rating > label {
    display: inline-block;
    position: relative;
    width: 1.1em;  /* magic number to overlap the radio buttons on top of the stars */
    width: calc(var(--star-size) / 3 * 1.1em);
  }
  .rating > *:hover,
  .rating > *:hover ~ label,
  .rating:not(:hover) > input:checked ~ label {
    color: transparent;  /* reveal the contour/white star from the HTML markup */
    cursor: inherit;  /* avoid a cursor transition from arrow/pointer to text selection */
  }
  .rating > *:hover:before,
  .rating > *:hover ~ label:before,
  .rating:not(:hover) > input:checked ~ label:before {
    content: "★";
    position: absolute;
    left: 0;
    color: gold;
  }
  .rating > input {
    position: relative;
    transform: scale(3);  /* make the radio buttons big; they don't inherit font-size */
    transform: scale(var(--star-size));
    /* the magic numbers below correlate with the font-size */
    top: -0.5em;  /* margin-top doesn't work */
    top: calc(var(--star-size) / 6 * -1em);
    margin-left: -2.5em;  /* overlap the radio buttons exactly under the stars */
    margin-left: calc(var(--star-size) / 6 * -5em);
    z-index: 2;  /* bring the button above the stars so it captures touches/clicks */
    opacity: 0;  /* comment to see where the radio buttons are */
    font-size: initial; /* reset to default */
  }
  form.amp-form-submit-error [submit-error] {
    color: red;
  }
</style>

Usage

While this star rating widget is accessible via the keyboard, that doesn't happen without quirks:

  • Click/tap the widget, then press up/down/left/right arrows. Unfortunately, in Firefox and Safari, keyboard arrows work backwards. This issue appears to be impossible to fix on non-Chrome-based browsers without using JavaScript, so stay tuned for the amp-rating component.
  • Mouse over the stars to see the style change.

We'll use a set of radio buttons to take user input for the star ratings, because they are keyboard-accessible. For desktop, it's nice to change the style of the previous stars when the user hovers their mouse over them. The only pure-CSS way to affect the style of previous elements onmouseover is to list them in reverse DOM order, which is why the <input> elements below run from 5 to 1.

We want the form to submit as soon as the user makes a selection, without a Submit button. To do that, we'll set the on attribute of the inputs to submit the form on change.

The initial rating is determined by which radio button has the checked attribute set. This is optional.

<form id="rating" method="post" action-xhr="https://amp.dev/documentation/examples/interactivity-dynamic-content/star_rating/set" target="_blank">
  <fieldset class="rating">
    <input name="rating" type="radio" id="rating5" value="5" on="change:rating.submit">
    <label for="rating5" title="5 stars"></label>

    <input name="rating" type="radio" id="rating4" value="4" on="change:rating.submit">
    <label for="rating4" title="4 stars"></label>

    <input name="rating" type="radio" id="rating3" value="3" on="change:rating.submit">
    <label for="rating3" title="3 stars"></label>

    <input name="rating" type="radio" id="rating2" value="2" on="change:rating.submit" checked="checked">
    <label for="rating2" title="2 stars"></label>

    <input name="rating" type="radio" id="rating1" value="1" on="change:rating.submit">
    <label for="rating1" title="1 stars"></label>
  </fieldset>
  <div submit-success>
    <template type="amp-mustache">
      <p>Thanks for rating {{rating}} star(s)!</p>
    </template>
  </div>
  <div submit-error>
    <template type="amp-mustache">
      Looks like something went wrong. Please try to rate again. {{error}}
    </template>
  </div>
</form>
자세한 설명이 필요하신가요?

이 페이지의 설명만으로 궁금한 점이 모두 해결되지 않는다면 다른 AMP 사용자에게 문의하여 구체적인 활용 사례를 논의해 보세요.

Stack Overflow로 이동
설명이 부족한 기능을 발견하셨나요?

AMP 프로젝트는 여러분의 참여와 기여를 적극 환영합니다! 오픈 소스 커뮤니티를 통해 지속적으로 활동해 주셔도 좋지만 관심 있는 주제에 한 번만 기여하셔도 큰 도움이 됩니다.

GitHub에서 샘플 수정하기