Eunomia v1.1.0
AllAngularReactNext.jsJavaScriptVue
EunomiaSalmonForestVioletOceanGoldFireCustom…
🇬🇧 English🇫🇷 Français

Rating

Rating lets someone give a score by filling a row of stars, such as reviewing a product, a course, or a stay. It also displays an existing score on its own, such as the average of every review a product received.

Dependencies

eun-icon eun-label · if it has a label
Overview API Examples Accessibility

When to use

Use a rating when someone scores something on a short, familiar scale, such as a product review, the feedback at the end of a course, or the satisfaction survey after a support request. Set display-only to show a score nobody edits, such as a product's average review on a listing page. For a score on a long or precise scale, or a choice between named levels rather than a score, see Alternatives below.

Install & usage

Pick a framework in the toolbar above and these snippets adapt.

npm install @eunomia/elements
import "@eunomia/elements/rating.js";
import "@eunomia/elements/icon.js";
import "@eunomia/elements/label.js";
<eun-rating name="stay" label="How was your stay?"></eun-rating>

Importing the file registers <eun-rating> as a custom element, with no further setup needed. It works with any framework, or none, since it's a standard web component. The stars are drawn by eun-icon, and the label by eun-label, so import both as well.

Alternatives

You want… Prefers
A score on a long or precise scale, such as a percentage eun-slider
A choice between named levels, such as Poor, Fair and Good eun-radio-list

Guidance

  • Set label to the question being scored, such as "How was your stay?", so the group is announced with it instead of the generic "Rating"
  • Keep the default five stars unless your audience already knows another scale
  • Set label on a display-only rating too whenever the score has a specific meaning, such as "Average customer rating"
  • Add clearable when giving a score is optional, since a picked rating can't be removed otherwise
  • Using a rating for anything other than a score, such as a quantity or a choice between named options: see Alternatives above
  • Giving icon and empty-icon the same shape, since the rating would then be told apart by color alone (see the Accessibility tab)
  • Lowering the contrast of the stars with --rating-color or --rating-empty-color below 3:1 against the background
  • Overriding colors with inline styles instead of the --rating-* CSS variables

Live testing

Properties

Rating <eun-rating>

Attributes

NameTypeDefaultDescription
default-valuenumber—The rating applied when the field connects, and restored on form reset
display-onlybooleanfalseOnly displays the rating, which can then be any decimal value, such as an average
clear-labelstring—The accessible label of the clear button. Defaults to the localized string for the current locale (English: "Clear rating")
empty-iconEunomiaIconName'star_outline'The icon drawn for the empty part of the rating. Keep its shape different from icon, so the rating doesn't rely on color alone
hide-errorbooleanfalseWhether the errors are hidden. The hint stays visible
maxnumber5The number of icons, which is also the highest rating
step1 | 0.51Whether a rating is picked by whole icons or by halves
clearablebooleanfalseShows a button that removes the rating, once one is picked
iconEunomiaIconName'star'The icon drawn for the filled part of the rating
valuenumber—The current rating. Empty when nothing is picked yet

Import the exact TypeScript type behind any property above, see Types.

Properties

JS-only — no matching HTML attribute, set these from a script or a template binding.

NameTypeDefaultDescription
labelstring—The title label of the field. While displayOnly is set, it isn't shown and starts the accessible name instead
instructionsstring—Instructions displayed below the label
hideLabelbooleanfalseWhether the label is visually hidden. It still gives the field its accessible name, and the instructions stay visible
namestring—The field name
localestring—The language of the accessible labels and of the decimal separator. Defaults to the shared default locale
disabledbooleanfalseWhether the field is disabled
readonlybooleanfalseWhether the field is read only
requiredbooleanfalseWhether a rating must be picked
hideErrorbooleanfalseWhether the errors are hidden. The hint stays visible
invalidbooleanfalseShows the field as invalid without any message, for an error displayed elsewhere
errorstring—An error computed outside the field, by the form or by the server. While set, the field is invalid and shows this message right away
hintstring—A short message displayed under the field
validatorsArray<Validators<number | undefined>>—The list of validation rules applied to the rating

Slots

NameDescription
labelCustom label content, falling back to the label property. Not rendered while displayOnly is set
instructionsCustom instructions content, falling back to the instructions property. Not rendered while displayOnly is set

Events

NameTypeDescription
eunchangeChangeEventFired whenever the rating is picked, changed or cleared

Every event above follows the same naming convention, covered in Events.

CSS custom properties

NameDescription
--rating-sizeSets the width and height of each icon
--rating-gapSets the space between two icons
--rating-colorSets the color of the filled icons
--rating-empty-colorSets the color of the empty icons
--rating-error-colorSets the color of the error message, and of the empty icons while in error
--rating-hint-colorSets the color of the hint message
--rating-focus-outline-colorSets the color of the focus outline drawn around the focused icon and the clear button
--field-label-font-sizeSets the font size of the label
--field-message-font-sizeSets the font size of the hint, error messages and instructions

Basic

Nothing is picked until someone clicks a star, or reaches the group with Tab and uses the arrow keys or Space.

<eun-rating name="course" label="Rate this course"></eun-rating>

Display only

display-only shows a score nobody edits. Its value then keeps every decimal, such as an average of 4.3, and partly fills the last star. The label isn't shown, and starts the name the score is announced with instead: "Average customer rating: 4.3 out of 5" here.

4.3 · 128 reviews
<div class="rating-summary">
  <eun-rating
    display-only
    value="4.3"
    label="Average customer rating"
  ></eun-rating>
  <span>4.3 · 128 reviews</span>
</div>
.rating-summary {
  display: flex;
  align-items: center;
  gap: 8px;
}

Configuration

Half stars

step="0.5" lets someone pick half a star: the first half of a star picks the half, its second half the whole star. The arrow keys then move by halves too.

<eun-rating
  name="film"
  label="Rate this film"
  step="0.5"
  value="3.5"
></eun-rating>

Number of stars

max sets how many stars are drawn, which is also the highest score.

<eun-rating name="difficulty" label="Difficulty" max="3" value="2"></eun-rating>

Clearable

clearable adds a button after the stars once a score is picked, to remove it. Focus then moves back to the stars, since the button disappears with the score.

<eun-rating
  name="delivery"
  label="Rate the delivery (optional)"
  clearable
  value="2"
></eun-rating>

Icons

icon sets the icon drawn for the filled part of the rating, and empty-icon the one drawn for the empty part. Pick two different shapes, such as a filled icon and its outline, so the score stays readable without relying on color.

<eun-rating
  name="recipe"
  label="Did you like this recipe?"
  icon="favorite"
  empty-icon="favorite_outline"
  value="3"
  style="--rating-color: #be185d;"
></eun-rating>

States

Disabled

Disabled ratings remain visible but can't be changed, and are excluded from the tab order and from form submission.

<eun-rating name="stay" label="Disabled" value="3" disabled></eun-rating>

Readonly

A readonly rating stays focusable and its value still submits, but it can't be changed. Unlike disabled, its score remains easy to read.

<eun-rating name="stay" label="Readonly" value="4" readonly></eun-rating>

Required

A required rating needs a score to be valid.

<eun-rating name="stay" label="How was your stay?" required></eun-rating>

With hint

hint renders a short message under the stars, replaced by the error message once the rating becomes invalid (see Error below).

<eun-rating
  name="stay"
  label="How was your stay?"
  hint="1 is poor, 5 is excellent"
></eun-rating>

Error

The error shows once a score is picked, once focus leaves the stars, or when the form is submitted. The empty stars then take the error color, and the message replaces the hint. validators add any rule of your own.

<eun-rating
  name="support"
  label="How helpful was our support?"
  required
  hint="1 is not helpful, 5 is very helpful"
></eun-rating>
document.querySelector("eun-rating").validators = [
  {
    isValid: (value?: number) => value === undefined || value >= 2,
    message: "Tell us more in the comment below",
  },
];

Integration

Forms

Like every other Eunomia field, eun-rating is a real form-associated custom element, so it takes part in a native <form>'s submission and reset. The score is submitted as a number, such as 3.5, and nothing is submitted while no score is picked. A reset restores default-value, or removes the score when there's none.

Submit Reset
{}
<form id="my-form">
  <eun-rating name="food" label="Food" default-value="4"></eun-rating>
  <eun-rating name="service" label="Service" step="0.5" clearable></eun-rating>
  <button type="submit">Submit</button>
  <button type="reset">Reset</button>
</form>
const form = document.querySelector("#my-form");

// Fires whenever a score is picked, changed or cleared.
form.addEventListener("eunchange", () => {
  console.log(Object.fromEntries(new FormData(form)));
});

// Fires on reset (native form event), not on eunchange. `setTimeout` lets
// the ratings' own update settle first.
form.addEventListener("reset", () => {
  setTimeout(() => console.log(Object.fromEntries(new FormData(form))));
});

Language

locale sets the language of every label read by screen readers, and of the decimal separator: in French, a star is announced as "2,5 sur 5" and the clear button as "Effacer la note". It defaults to the language set for the whole page with setDefaultLocale().

<eun-rating
  name="sejour"
  label="Comment s'est passé votre séjour ?"
  locale="fr"
  step="0.5"
  clearable
  value="4.5"
></eun-rating>

Server-side rendering

The stars are drawn inside the rating's shadow root, so a server-rendered rating stays empty until the element upgrades, then pushes the content around it. Link rating-ssr.css in the page's <head> to draw the label and the stars from the first paint:

<link
  rel="stylesheet"
  href="node_modules/@eunomia/elements/dist/ssr/rating-ssr.css"
/>

A bundler that resolves package exports can reach it as @eunomia/elements/rating-ssr.css. It reads value or default-value, max up to 10, display-only, readonly, disabled, clearable and the --rating-* custom properties, and fills the stars up to the tenth, such as 4.3. Write those attributes in the server-rendered markup for them to apply. A few things only show with the upgrade, in the space already reserved for them: the required asterisk, the clear button, and stars drawn with a custom icon or empty-icon. instructions and hint appear with the upgrade too, and do take up new space.

Custom

Override the --rating-* CSS variables, listed in full in the API tab.

<eun-rating class="custom-rating" label="Custom rating" value="3"></eun-rating>
.custom-rating {
  --rating-size: 32px;
  --rating-gap: 8px;
  --rating-color: #b7791f;
}

The custom properties that change colors, sizes and spacing, at a glance. The API tab lists every property of the component.

Variable Sets
--rating-size Sets the width and height of each icon
--rating-gap Sets the space between two icons
--rating-color Sets the color of the filled icons
--rating-empty-color Sets the color of the empty icons
--rating-error-color Sets the color of the error message, and of the empty icons while in error
--rating-hint-color Sets the color of the hint message
--rating-focus-outline-color Sets the color of the focus outline drawn around the focused icon and the clear button

Keyboard interactions

The stars form a single tab stop: the picked star, or the first one while nothing is picked. The arrow keys pick the score as they move, and never wrap around from the last star to the first.

Key Action
Tab / Shift+Tab Moves focus to the stars, then to the clear button when it's shown
ArrowRight / ArrowDown Raises the score by step, up to max. ArrowLeft in a right-to-left page
ArrowLeft / ArrowUp Lowers the score by step, down to the first star. ArrowRight in a right-to-left page
Home Picks the lowest score
End Picks the highest score
Space Picks the focused star while nothing is picked yet
Enter / Space on clear Removes the score and moves focus back to the stars

Semantics and screen readers

Aria attributes

Each possible score is a real <input type="radio">, so its role, its checked state and its position ("3 of 5") come from the browser itself. On top of that native baseline:

  • role="radiogroup" on the stars, named by label, or by the localized "Rating" when there's none
  • aria-label on each radio: its score, such as "3 out of 5", or "2.5 out of 5" with half stars, localized through locale
  • aria-describedby: points to the instructions, then to the hint or the error, whenever they're rendered
  • aria-required, aria-readonly and aria-disabled on the group, only present while required, readonly or disabled is set
  • aria-invalid on the group: kept in sync with valid, once disabled or hideError are also accounted for, and forced to true while invalid is set
  • aria-readonly and aria-disabled on the host as well, like every other field
  • aria-label on the clear button: clear-label, or the localized "Clear rating"

Every icon is aria-hidden, since the score is already carried by the radios.

Label accessibility

Set label to the question being scored, such as "How was your stay?". The group is announced with it, then each star with its score, so a screen reader user hears "How was your stay?, radio group, 4 out of 5, checked". Without a label, the group falls back to the generic "Rating", which doesn't say what is being scored.

Screen reader restitution

By default, screen readers read out the group's name, then the focused star's score, its checked state and its position, such as "How was your stay?, radio group, 4 out of 5, radio button, checked, 4 of 5". The order may vary depending on the screen reader and its configuration. The disabled state is rendered differently depending on the screen reader:

  • VoiceOver (macOS/iOS): dimmed
  • NVDA and JAWS: unavailable
  • Narrator and TalkBack: disabled

Display only

A display-only rating renders the stars as a single image (role="img"), named after the score: "Rating: 4.3 out of 5", or "Average customer rating: 4.3 out of 5" with a label. Screen readers read it in one go, and it never takes focus, since there's nothing to interact with. With no value, it reads "Rating: Not rated".

Field states

Disabled vs. readonly

disabled removes the stars from the tab order, blocks every change, and excludes the score from form submission. readonly keeps the picked star focusable and its score submitted, but blocks every change by pointer and keyboard, since the HTML readonly attribute has no effect on radios. It communicates "you can't change this right now", not "this doesn't apply". Prefer readonly whenever the score still matters to the user.

Required field

required means "a score must be picked". The error is announced through aria-invalid and the message linked with aria-describedby. Add a visible note near the top of the form as well, rather than relying on the error message alone.

Visual

Colors and shapes

The filled and the empty parts of the rating use two different shapes, a filled star and an outlined one, so the score stays readable with any color vision, in grayscale, and in Windows high contrast mode, where both are drawn in the system text color.

Graphics needed to understand content must reach a contrast of at least 3:1 against the page background. The empty stars always do, since their color is derived from the text and surface colors of the theme. The filled stars take the theme's primary color, which the theme generator keeps above that ratio in dark themes. In light themes, it's the brand color as is: the default theme reaches 4.3:1 on white, but a very light brand color falls short, in which case set --rating-color to a darker shade. The golden stars of the Custom example, #b7791f, reach 3.6:1 on white.

Target size

Each star is a single target of 24 by 24 pixels by default, even with half stars: where the pointer lands in the star decides between the half and the whole score, so no target is ever half as wide. Keep --rating-size at 24px or more.

Reference links

WAI-ARIA Authoring Practices: Radio Group Pattern
WCAG 2.2: Understanding Non-text Contrast
WCAG 2.2: Understanding Use of Color
WCAG 2.2: Understanding Target Size (Minimum)