<fieldset class="fieldset " aria-labelledby="rating-field-label">
    <div class="row">
        <legend id="rating-field-label" class="rating-rate-legend ">

        </legend>
        <div class="rating rating--rate  " role="listbox" aria-required="true" aria-labelledby="rating-field-label" tabindex="0">
            <div class="rating__rate-item " role="option" aria-selected="false">
                <label for="Rating_1" class="rating__star rating__star--rate " aria-label="Rate option, 1 of 5. Click to vote">
                    <span class="
                                rating__indicator
                                
                            "></span>
                </label>
                <input type="radio" name="ratings[1]" id="Rating_1" value="16" class="radio__field">
            </div>
            <div class="rating__rate-item " role="option" aria-selected="false">
                <label for="Rating_2" class="rating__star rating__star--rate " aria-label="Rate option, 2 of 5. Click to vote">
                    <span class="
                                rating__indicator
                                
                            "></span>
                </label>
                <input type="radio" name="ratings[2]" id="Rating_2" value="16" class="radio__field">
            </div>
            <div class="rating__rate-item " role="option" aria-selected="false">
                <label for="Rating_3" class="rating__star rating__star--rate " aria-label="Rate option, 3 of 5. Click to vote">
                    <span class="
                                rating__indicator
                                
                            "></span>
                </label>
                <input type="radio" name="ratings[3]" id="Rating_3" value="16" class="radio__field">
            </div>
            <div class="rating__rate-item " role="option" aria-selected="false">
                <label for="Rating_4" class="rating__star rating__star--rate " aria-label="Rate option, 4 of 5. Click to vote">
                    <span class="
                                rating__indicator
                                
                            "></span>
                </label>
                <input type="radio" name="ratings[4]" id="Rating_4" value="16" class="radio__field">
            </div>
            <div class="rating__rate-item " role="option" aria-selected="false">
                <label for="Rating_5" class="rating__star rating__star--rate " aria-label="Rate option, 5 of 5. Click to vote">
                    <span class="
                                rating__indicator
                                
                            "></span>
                </label>
                <input type="radio" name="ratings[5]" id="Rating_5" value="16" class="radio__field">
            </div>
        </div>
    </div>
</fieldset>
<script src="/components/raw/rating/rating-rate.js"></script>
<fieldset
    class="fieldset {{ fieldsetClass }}"
    aria-labelledby="rating-field-label"
>
    <div class="row">
        <legend
            id="rating-field-label"
            class="rating-rate-legend {{ legendClass }}"
        >
            {{ legendTitle }}
        </legend>
        <div
            class="rating rating--rate {{ class }} {{ ratingClass }}"
            role="listbox"
            aria-required="true"
            aria-labelledby="rating-field-label"
            {{{ attributes }}}
            tabindex="0"
        >
            {{#each rateItems as |item|}}
                <div
                    class="rating__rate-item {{ item.class }}"
                    role="option"
                    aria-selected="false"
                    {{{ item.attributes }}}
                >
                    <label
                        for="{{ item.id }}"
                        class="rating__star rating__star--rate {{ star.class }}"
                        aria-label="{{ labelText }}"
                        {{{ item.labelAttributes }}}
                    >
                        <span
                            class="
                                rating__indicator
                                {{ item.indicator.class }}
                            "
                            {{{ item.indicator.attributes }}}
                        ></span>
                    </label>
                    <input
                        type="radio"
                        name="{{ item.name }}"
                        id="{{ item.id }}"
                        value="16"
                        class="radio__field"
                    >
                </div>
            {{/each}}
        </div>
    </div>
</fieldset>
{{#if script}}
    <script src="{{static 'rating-rate.js' }}"></script>
{{/if}}
{
  "ariaLabel": "Average rating: 72%",
  "title": "4/5 sterren",
  "star": {
    "attributes": ""
  },
  "script": true,
  "rateItems": [
    {
      "id": "Rating_1",
      "name": "ratings[1]",
      "labelText": "Rate option, 1 of 5. Click to vote"
    },
    {
      "id": "Rating_2",
      "name": "ratings[2]",
      "labelText": "Rate option, 2 of 5. Click to vote"
    },
    {
      "id": "Rating_3",
      "name": "ratings[3]",
      "labelText": "Rate option, 3 of 5. Click to vote"
    },
    {
      "id": "Rating_4",
      "name": "ratings[4]",
      "labelText": "Rate option, 4 of 5. Click to vote"
    },
    {
      "id": "Rating_5",
      "name": "ratings[5]",
      "labelText": "Rate option, 5 of 5. Click to vote"
    }
  ]
}
  • Content:
    $rating__size                   : 75px !default;
    $rating__item-size              : $rating__size / 5 !default;
    $rating__size--rate             : 200px !default;
    $rating__margin--rate           : $spacer--medium !default;
    $rating__item-size--rate        : $rating__size--rate / 5 !default;
    $icon__active-color             : $orange !default;
    $icon__active-color--with-border: $orange !default;
    $icon__inactive-color           : $gray-light !default;
    $rating__error-margin-bottom    : $spacer !default;
    $rating__error-color            : $red !default;
    
    .rating {
        width: $rating__size;
        margin: 0 0 $spacer--medium;
    
        &:hover,
        &:focus {
            .rating__rate-item span:before {
                display: block;
            }
        }
    
        &__rate-item {
            position: relative;
            flex: 1 0 $rating__item-size;
            overflow: hidden;
    
            &:hover,
            &:focus {
                ~ .rating__rate-item span:before {
                    display: none;
                }
            }
        }
        &__star {
            position: relative;
            height: $rating__item-size;
            width: $rating__size;
            margin: 0;
    
            &:before { // inactive
                content: '';
                position: absolute;
                display: block;
                bottom: 0;
                left: 0;
                top: 0;
                right: 0;
                width: $rating__size;
                height: $rating__item-size;
                background-image: svg-uri("<svg viewBox='0 0 20 19' xmlns='http://www.w3.org/2000/svg'><path fill='none' d='M-2-2h24v24H-2z'/><path fill='#{$icon__inactive-color}' d='M20 7.24l-7.19-.62L10 0 7.19 6.63 0 7.24l5.46 4.73L3.82 19 10 15.27 16.18 19l-1.63-7.03L20 7.24zM10 13.4l-3.76 2.27 1-4.28-3.32-2.88 4.38-.38L10 4.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L10 13.4z'/></svg>");
                background-repeat: repeat-x;
                background-position: left center;
                background-size: $rating__item-size;
                text-indent: 10000px;
    
                @include isIE() {
                    height: 100%;
                    background-size: $rating__item-size 100%;
                }
            }
            &--single {
                width: $rating__item-size;
            }
        }
        &__indicator {
            left: 0;
            top: 0;
            display: block;
            height: $rating__item-size;
            width: $rating__size;
            overflow: hidden;
            text-indent: -10000px;
            &:before { // active
                content: '';
                position: absolute;
                display: block;
                left: 0;
                right: 0;
                top: 0;
                bottom: 0;
                width: auto;
                height: $rating__item-size;
                background-image: svg-uri("<svg viewBox='0 0 20 19' xmlns='http://www.w3.org/2000/svg'><path fill='none' d='M-2-2h24v24H-2z'/><path d='M10 15.27L16.18 19l-1.64-7.03L20 7.24l-7.19-.61L10 0 7.19 6.63 0 7.24l5.46 4.73L3.82 19 10 15.27z' fill='#{$icon__active-color}'/></svg>");
                background-repeat: repeat-x;
                background-position: left center;
                background-size: $rating__item-size;
                text-indent: 10000px;
    
                @include isIE() {
                    width: 100%;
                    height: 100%;
                    background-size: $rating__item-size 100%;
                }
    
                .rating__star--rate & {
                    display: none;
                }
                .rating__star--rate:hover &,
                .rating__star--rate:focus &,
                .rating__rate-item--active & {
                    display: block;
                }
            }
            &:after { //active star with border
                content: '';
                position: absolute;
                display: block;
                top: 0;
                right: 0;
                bottom: 0;
                left: 0;
                height: $rating__item-size;
                background-image: svg-uri("<svg viewBox='0 0 20 19' xmlns='http://www.w3.org/2000/svg'><path fill='none' d='M-2-2h24v24H-2z'/><path d='M10 15.27L16.18 19l-1.64-7.03L20 7.24l-7.19-.61L10 0 7.19 6.63 0 7.24l5.46 4.73L3.82 19 10 15.27z' fill='#{$icon__active-color--with-border}'/></svg>");
                background-repeat: repeat-x;
                background-size: $rating__item-size;
                background-position: left center;
    
                @include isIE() {
                    width: 100%;
                    height: 100%;
                    background-size: $rating__item-size 100%;
                }
    
                .rating__star--rate & {
                    display: none;
                    text-indent: 10000px;
                }
            }
        }
    
        &__title {
            font-family: $font-family-saira;
            font-size: $font-size-medium;
            color: rgba(52, 52, 52, 0.75);
            margin-top: 10px;
            margin-left: 2px;
        }
    
        &__error {
            display: none;
            color: $rating__error-color;
            margin-bottom: $rating__error-margin-bottom;
            margin-top: $spacer--1;
            flex: 1 0 100%;
            text-align: right;
    
            &--visible {
                display: block;
            }
        }
    
        .mage-error {
            display: none !important; // sass-lint:disable-line no-important
        }
        &--rate {
            display: flex;
            justify-content: center;
            align-items: center;
            width: $rating__size--rate;
            margin-bottom: 0;
    
            .rating__rate-item {
                flex: 1 0 $rating__item-size--rate;
            }
    
            .rating__star {
                height: $rating__item-size--rate;
                width: $rating__item-size--rate;
                border: 0;
                background-color: transparent;
                cursor: pointer;
    
                &:before {
                    width: $rating__item-size--rate;
                    height: $rating__item-size--rate;
                    background-size: $rating__item-size--rate - 10;
                    background-position: center;
                    background-repeat: no-repeat;
    
                    @include isIE() {
                        background-size: $rating__item-size--rate - 10 100%;
                    }
                }
    
                &--single {
                    width: $rating__item-size--rate;
                }
            }
            .radio__field {
                width: 1px;
                height: 1px;
                bottom: 0;
                &:focus {
                    opacity: 1;
                    height: 0;
                    outline: none;
                    appearance: none;
                    &:before {
                        content: '';
                        display: block;
                        position: absolute;
                        top: -$rating__item-size--rate;
                        width: $rating__item-size--rate;
                        height: $rating__item-size--rate;
                        border: $border-focus;
                    }
                }
            }
    
            .rating__indicator {
                height: $rating__item-size--rate;
                width: $rating__item-size--rate;
    
                &:before {
                    height: $rating__item-size--rate;
                    background-size: $rating__item-size--rate - 10;
                    background-position: center;
                    background-repeat: no-repeat;
                }
    
                &:after {
                    height: $rating__item-size--rate;
                    background-size: $rating__item-size--rate - 10;
                    background-position: center;
                    background-repeat: no-repeat;
                }
            }
        }
        &__label {
            margin: 0 0 3px;
            font-family: $font-family-saira;
        }
    }
    
  • URL: /components/raw/rating/_rating.scss
  • Filesystem Path: build/components/02-elements/rating/_rating.scss
  • Size: 7.4 KB
  • Content:
    'use strict'
    
    class Rating {
      constructor() {
        this.rating = document.querySelector('.rating--rate');
        this.activeItemClass = 'rating__rate-item--active';
        this.ratingElements = [...this.rating.querySelectorAll('.rating__rate-item')];
        this.optionChoosen = false;
    
        this.setListeners();
      }
      deleteOtherRates() {
        this.ratingElements.forEach(item => {
          item.classList.remove(this.activeItemClass);
        });
      }
      setListeners() {
        this.ratingElements.forEach(oneRank => {
          oneRank.addEventListener('click', () => {
            this.deleteOtherRates();
    
            oneRank.querySelector('.radio__field').checked = true;
            oneRank.classList.add(this.activeItemClass);
            this.optionChoosen = true;
            oneRank.setAttribute('aria-selected', true);
    
            for (let i = 0; i < this.ratingElements.length; i++) {
              if (!this.ratingElements[i].classList.contains(this.activeItemClass)) {
                this.ratingElements[i].classList.add(this.activeItemClass);
              }
              else {
                return;
              }
            }
          });
        });
      }
    }
    new Rating();
    
    
    
  • URL: /components/raw/rating/rating-rate.js
  • Filesystem Path: build/components/02-elements/rating/rating-rate.js
  • Size: 1.1 KB

Rating component

Accessible rating

  • Rating component (which shows the average rate) should be focusable (tabindex="0") and should have aria-label set with descriptive value and average rating value.
  • Rating–rate component has to be usable with keyboard. It acts like a small form with input type="button" behing the stars. moving focus we see that background changes and with space click we can set the field selected/star active.

What should be done to keep it working for AT:

  • keep ratio inputs in fieldset and add legend to set some label
  • use role="listbox" aria-required="true" and aria-labelledby="<use legend id>" to make list of options with required rating field
  • use role="option" & aria-selected on the input radio wrapper
  • use aria-label for input radio label elements