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 上的示例