Rating
This library is a work-in-progress. We are releasing it early to gather feedback, but it is not ready for production.
The Rating component provides a way for users to view and set a rating, typically represented by stars. It's highly customizable, supporting different sizes, colors, and precision.
Examples
Live Preview
Default Rating
A basic rating component.
Half Precision
Allows for half-star ratings.
Readonly
A non-interactive rating display.
Allow Clear
Clicking the current rating will clear it to 0.
Sizes
Three size options: small, medium (default), and large.
Variants
Color variants for different contexts.
Event Handling
Listen for `rating-change` and `rating-hover` events.
Current Value: 3
Hover Value: 0
Last Event:
Form Control Features
Rating supports labels, helper text, validation, and label positioning for form integration.
CSS Shadow Parts Customization
Use CSS Shadow Parts to customize the component's appearance.
View Vue Code
<template>
<section>
<div class="mbe4">
<h2>Default Rating</h2>
<p class="mbs2 mbe3">A basic rating component.</p>
<VueRating :value="3" />
</div>
<div class="mbe4">
<h2>Half Precision</h2>
<p class="mbs2 mbe3">Allows for half-star ratings.</p>
<VueRating
:value="2.5"
precision="half"
/>
</div>
<div class="mbe4">
<h2>Readonly</h2>
<p class="mbs2 mbe3">A non-interactive rating display.</p>
<VueRating
:value="4"
:readonly="true"
/>
</div>
<div class="mbe4">
<h2>Allow Clear</h2>
<p class="mbs2 mbe3">Clicking the current rating will clear it to 0.</p>
<VueRating
:value="3"
:allow-clear="true"
/>
</div>
<div class="mbe4">
<h2>Sizes</h2>
<p class="mbs2 mbe3">Three size options: small, medium (default), and large.</p>
<div
class="mbe4"
style="display: flex; flex-direction: column; gap: 1rem; align-items: flex-start;"
>
<VueRating
size="sm"
:value="3"
/>
<VueRating
size="md"
:value="3"
/>
<VueRating
size="lg"
:value="3"
/>
</div>
</div>
<div class="mbe4">
<h2>Variants</h2>
<p class="mbs2 mbe3">Color variants for different contexts.</p>
<div
class="mbe4"
style="display: flex; flex-direction: column; gap: 1rem; align-items: flex-start;"
>
<VueRating
variant="primary"
:value="3"
/>
<VueRating
variant="secondary"
:value="3"
/>
<VueRating
variant="success"
:value="3"
/>
<VueRating
variant="warning"
:value="3"
/>
<VueRating
variant="danger"
:value="3"
/>
<VueRating
variant="monochrome"
:value="3"
/>
</div>
</div>
<div class="mbe4">
<h2>Event Handling</h2>
<p class="mbs2 mbe3">Listen for `rating-change` and `rating-hover` events.</p>
<div>
<VueRating
:value="eventValue"
@rating-change="handleRatingChange"
@rating-hover="handleRatingHover"
/>
<div style="margin-top: 1rem; font-family: monospace; font-size: 0.875rem;">
<p>Current Value: {{ eventValue }}</p>
<p>Hover Value: {{ hoverValue }}</p>
<p>Last Event: {{ lastEvent }}</p>
</div>
</div>
</div>
<div class="mbe4">
<h2>Form Control Features</h2>
<p class="mbs2 mbe3">
Rating supports labels, helper text, validation, and label positioning for form integration.
</p>
<div style="max-width: 600px; display: flex; flex-direction: column; gap: 1.5rem;">
<!-- Basic label -->
<VueRating
label="Product Rating"
:value="3"
/>
<!-- With help text -->
<VueRating
label="Service Quality"
help-text="Rate the quality of service you received"
:value="4"
/>
<!-- Required field -->
<VueRating
label="Overall Experience"
:required="true"
help-text="This rating is required to submit your feedback"
:value="0"
/>
<!-- With error validation -->
<VueRating
label="Customer Support"
:required="true"
:invalid="true"
error-message="Please provide a rating before submitting"
:value="0"
/>
<!-- Label positioning -->
<VueRating
label="Start Position"
label-position="start"
:value="3"
/>
<VueRating
label="End Position"
label-position="end"
:value="3"
/>
</div>
</div>
<div class="mbe4">
<h2>CSS Shadow Parts Customization</h2>
<p class="mbs2 mbe3">
Use CSS Shadow Parts to customize the component's appearance.
</p>
<VueRating
:value="4"
class="custom-rating"
/>
</div>
</section>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
import { VueRating } from "agnosticui-core/rating/vue";
import type {
RatingChangeEvent,
RatingHoverEvent,
} from "agnosticui-core/rating/vue";
export default defineComponent({
name: "RatingExamples",
components: {
VueRating,
},
setup() {
const eventValue = ref(3);
const hoverValue = ref(0);
const lastEvent = ref("");
const handleRatingChange = (e: RatingChangeEvent) => {
eventValue.value = e.detail.newValue;
lastEvent.value = `rating-change: oldValue=${e.detail.oldValue}, newValue=${e.detail.newValue}`;
};
const handleRatingHover = (e: RatingHoverEvent) => {
hoverValue.value = e.detail.value;
lastEvent.value = `rating-hover: phase=${e.detail.phase}, value=${e.detail.value}`;
};
return {
eventValue,
hoverValue,
lastEvent,
handleRatingChange,
handleRatingHover,
};
},
});
</script>
<style>
/* CSS Shadow Parts customization examples */
.custom-rating::part(star-button) {
padding: 0.25rem;
border-radius: 50%;
transition: background-color 0.2s ease;
}
.custom-rating::part(star-button):hover {
background-color: rgba(0, 0, 0, 0.1);
}
.custom-rating::part(star) {
opacity: 0.8;
transition: opacity 0.2s ease;
}
.custom-rating::part(star):hover {
opacity: 1;
}
</style>
Live Preview
View Lit / Web Component Code
import { LitElement, html } from 'lit';
import 'agnosticui-core/rating';
export class RatingLitExamples extends LitElement {
static properties = {
eventValue: { type: Number },
hoverValue: { type: Number },
lastEvent: { type: String }
};
constructor() {
super();
this.eventValue = 3;
this.hoverValue = 0;
this.lastEvent = '';
}
// Render in light DOM to access global utility classes
createRenderRoot() {
return this;
}
handleRatingChange(e) {
this.eventValue = e.detail.newValue;
this.lastEvent = `rating-change: oldValue=${e.detail.oldValue}, newValue=${e.detail.newValue}`;
}
handleRatingHover(e) {
this.hoverValue = e.detail.value;
this.lastEvent = `rating-hover: phase=${e.detail.phase}, value=${e.detail.value}`;
}
render() {
return html`
<section>
<div class="mbe4">
<h2>Default Rating</h2>
<p class="mbs2 mbe3">A basic rating component.</p>
<ag-rating .value=${3}></ag-rating>
</div>
<div class="mbe4">
<h2>Half Precision</h2>
<p class="mbs2 mbe3">Allows for half-star ratings.</p>
<ag-rating
.value=${2.5}
precision="half"
></ag-rating>
</div>
<div class="mbe4">
<h2>Readonly</h2>
<p class="mbs2 mbe3">A non-interactive rating display.</p>
<ag-rating
.value=${4}
.readonly=${true}
></ag-rating>
</div>
<div class="mbe4">
<h2>Allow Clear</h2>
<p class="mbs2 mbe3">Clicking the current rating will clear it to 0.</p>
<ag-rating
.value=${3}
.allowClear=${true}
></ag-rating>
</div>
<div class="mbe4">
<h2>Sizes</h2>
<p class="mbs2 mbe3">Three size options: small, medium (default), and large.</p>
<div
class="mbe4"
style="display: flex; flex-direction: column; gap: 1rem; align-items: flex-start;"
>
<ag-rating
size="sm"
.value=${3}
></ag-rating>
<ag-rating
size="md"
.value=${3}
></ag-rating>
<ag-rating
size="lg"
.value=${3}
></ag-rating>
</div>
</div>
<div class="mbe4">
<h2>Variants</h2>
<p class="mbs2 mbe3">Color variants for different contexts.</p>
<div
class="mbe4"
style="display: flex; flex-direction: column; gap: 1rem; align-items: flex-start;"
>
<ag-rating
variant="primary"
.value=${3}
></ag-rating>
<ag-rating
variant="secondary"
.value=${3}
></ag-rating>
<ag-rating
variant="success"
.value=${3}
></ag-rating>
<ag-rating
variant="warning"
.value=${3}
></ag-rating>
<ag-rating
variant="danger"
.value=${3}
></ag-rating>
<ag-rating
variant="monochrome"
.value=${3}
></ag-rating>
</div>
</div>
<div class="mbe4">
<h2>Event Handling</h2>
<p class="mbs2 mbe3">Listen for <code>rating-change</code> and <code>rating-hover</code> events.</p>
<div>
<ag-rating
.value=${this.eventValue}
@rating-change=${this.handleRatingChange}
@rating-hover=${this.handleRatingHover}
></ag-rating>
<div style="margin-top: 1rem; font-family: monospace; font-size: 0.875rem;">
<p>Current Value: ${this.eventValue}</p>
<p>Hover Value: ${this.hoverValue}</p>
<p>Last Event: ${this.lastEvent}</p>
</div>
</div>
</div>
<div class="mbe4">
<h2>Form Control Features</h2>
<p class="mbs2 mbe3">
Rating supports labels, helper text, validation, and label positioning for form integration.
</p>
<div style="max-width: 600px; display: flex; flex-direction: column; gap: 1.5rem;">
<!-- Basic label -->
<ag-rating
label="Product Rating"
.value=${3}
></ag-rating>
<!-- With help text -->
<ag-rating
label="Service Quality"
help-text="Rate the quality of service you received"
.value=${4}
></ag-rating>
<!-- Required field -->
<ag-rating
label="Overall Experience"
.required=${true}
help-text="This rating is required to submit your feedback"
.value=${0}
></ag-rating>
<!-- With error validation -->
<ag-rating
label="Customer Support"
.required=${true}
.invalid=${true}
error-message="Please provide a rating before submitting"
.value=${0}
></ag-rating>
<!-- Label positioning -->
<ag-rating
label="Start Position"
label-position="start"
.value=${3}
></ag-rating>
<ag-rating
label="End Position"
label-position="end"
.value=${3}
></ag-rating>
</div>
</div>
<div class="mbe4">
<h2>CSS Shadow Parts Customization</h2>
<p class="mbs2 mbe3">
Use CSS Shadow Parts to customize the component's appearance.
</p>
<ag-rating
.value=${4}
class="custom-rating"
></ag-rating>
</div>
</section>
<style>
/* CSS Shadow Parts customization examples */
.custom-rating::part(star-button) {
padding: 0.25rem;
border-radius: 50%;
transition: background-color 0.2s ease;
}
.custom-rating::part(star-button):hover {
background-color: rgba(0, 0, 0, 0.1);
}
.custom-rating::part(star) {
opacity: 0.8;
transition: opacity 0.2s ease;
}
.custom-rating::part(star):hover {
opacity: 1;
}
</style>
`;
}
}
// Register the custom element
customElements.define('rating-lit-examples', RatingLitExamples);
Interactive Preview: Click the "Open in StackBlitz" button below to see this example running live in an interactive playground.
View React Code
import { useState } from "react";
import { ReactRating } from "agnosticui-core/rating/react";
export default function RatingReactExamples() {
const [eventValue, setEventValue] = useState(3);
const [hoverValue, setHoverValue] = useState(0);
const [lastEvent, setLastEvent] = useState("");
const handleRatingChange = (e) => {
setEventValue(e.detail.newValue);
setLastEvent(`rating-change: oldValue=${e.detail.oldValue}, newValue=${e.detail.newValue}`);
};
const handleRatingHover = (e) => {
setHoverValue(e.detail.value);
setLastEvent(`rating-hover: phase=${e.detail.phase}, value=${e.detail.value}`);
};
return (
<section>
<div className="mbe4">
<h2>Default Rating</h2>
<p className="mbs2 mbe3">A basic rating component.</p>
<ReactRating value={3} />
</div>
<div className="mbe4">
<h2>Half Precision</h2>
<p className="mbs2 mbe3">Allows for half-star ratings.</p>
<ReactRating value={2.5} precision="half" />
</div>
<div className="mbe4">
<h2>Readonly</h2>
<p className="mbs2 mbe3">A non-interactive rating display.</p>
<ReactRating value={4} readonly={true} />
</div>
<div className="mbe4">
<h2>Allow Clear</h2>
<p className="mbs2 mbe3">Clicking the current rating will clear it to 0.</p>
<ReactRating value={3} allowClear={true} />
</div>
<div className="mbe4">
<h2>Sizes</h2>
<p className="mbs2 mbe3">Three size options: small, medium (default), and large.</p>
<div
className="mbe4"
style={{ display: "flex", flexDirection: "column", gap: "1rem", alignItems: "flex-start" }}
>
<ReactRating size="sm" value={3} />
<ReactRating size="md" value={3} />
<ReactRating size="lg" value={3} />
</div>
</div>
<div className="mbe4">
<h2>Variants</h2>
<p className="mbs2 mbe3">Color variants for different contexts.</p>
<div
className="mbe4"
style={{ display: "flex", flexDirection: "column", gap: "1rem", alignItems: "flex-start" }}
>
<ReactRating variant="primary" value={3} />
<ReactRating variant="secondary" value={3} />
<ReactRating variant="success" value={3} />
<ReactRating variant="warning" value={3} />
<ReactRating variant="danger" value={3} />
<ReactRating variant="monochrome" value={3} />
</div>
</div>
<div className="mbe4">
<h2>Event Handling</h2>
<p className="mbs2 mbe3">
Listen for <code>rating-change</code> and <code>rating-hover</code> events.
</p>
<div>
<ReactRating
value={eventValue}
onRatingChange={handleRatingChange}
onRatingHover={handleRatingHover}
/>
<div style={{ marginTop: "1rem", fontFamily: "monospace", fontSize: "0.875rem" }}>
<p>Current Value: {eventValue}</p>
<p>Hover Value: {hoverValue}</p>
<p>Last Event: {lastEvent}</p>
</div>
</div>
</div>
<div className="mbe4">
<h2>Form Control Features</h2>
<p className="mbs2 mbe3">
Rating supports labels, helper text, validation, and label positioning for form
integration.
</p>
<div style={{ maxWidth: "600px", display: "flex", flexDirection: "column", gap: "1.5rem" }}>
{/* Basic label */}
<ReactRating label="Product Rating" value={3} />
{/* With help text */}
<ReactRating
label="Service Quality"
helpText="Rate the quality of service you received"
value={4}
/>
{/* Required field */}
<ReactRating
label="Overall Experience"
required={true}
helpText="This rating is required to submit your feedback"
value={0}
/>
{/* With error validation */}
<ReactRating
label="Customer Support"
required={true}
invalid={true}
errorMessage="Please provide a rating before submitting"
value={0}
/>
{/* Label positioning */}
<ReactRating label="Start Position" labelPosition="start" value={3} />
<ReactRating label="End Position" labelPosition="end" value={3} />
</div>
</div>
<div className="mbe4">
<h2>CSS Shadow Parts Customization</h2>
<p className="mbs2 mbe3">
Use CSS Shadow Parts to customize the component's appearance.
</p>
<ReactRating value={4} className="custom-rating" />
</div>
</section>
);
}
Usage
TIP
The framework examples below import AgnosticUI as an npm package. Alternatively, you can use the CLI for complete control, AI/LLM visibility, and full code ownership:
npx ag init --framework FRAMEWORK # react, vue, lit, svelte, etc.
npx ag add RatingThe CLI copies source code directly into your project, giving you full visibility and control. After running npx ag add, you'll receive exact import instructions.
Vue
<template>
<section>
<VueRating :value="3" />
<VueRating :value="2.5" precision="half" />
<VueRating :value="4" variant="success" />
</section>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { VueRating } from 'agnosticui-core/rating/vue';
export default defineComponent({
components: { VueRating }
});
</script>React
import { ReactRating } from 'agnosticui-core/rating/react';
export default function Example() {
return (
<section>
<ReactRating value={3} />
<ReactRating value={2.5} precision="half" />
<ReactRating value={4} variant="success" />
</section>
);
}Lit (Web Components)
<script type="module">
import 'agnosticui-core/rating';
</script>
<section>
<ag-rating value="3"></ag-rating>
<ag-rating value="2.5" precision="half"></ag-rating>
<ag-rating value="4" variant="success"></ag-rating>
</section>Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | 0 | The current rating value. Can be a float if precision is half. |
max | number | 5 | The maximum rating value (total number of stars). |
precision | 'whole' | 'half' | 'whole' | The precision of the rating, allowing for whole or half-star increments. |
readonly | boolean | false | If true, the rating is display-only and cannot be changed by the user. |
allowClear | boolean | false | If true, clicking on the currently selected rating value will set the rating to 0. |
size | 'sm' | 'md' | 'lg' | 'md' | The size of the rating stars. |
variant | '' | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | '' | The color variant of the rating. Defaults to yellow if empty. |
Events
| Event | Framework | Detail | Description |
|---|---|---|---|
rating-change | Vue: @rating-changeReact: onRatingChangeLit: @rating-change | { oldValue: number, newValue: number } | Fired when the user selects a new rating value. |
rating-hover | Vue: @rating-hoverReact: onRatingHoverLit: @rating-hover | { phase: 'start' | 'move' | 'end', value: number } | Fired as the user's pointer moves over the component. phase indicates the state of the hover interaction. |
CSS Shadow Parts
| Part | Description |
|---|---|
base | The main container div element wrapping the stars. |
star-button | The <span> element that acts as the clickable area for each star. |
star | The <span> element that contains the star's SVG icon. |
Customization Example
ag-rating.custom-rating::part(star-button) {
padding: 0.25rem;
border-radius: 50%;
transition: background-color 0.2s ease;
}
ag-rating.custom-rating::part(star-button):hover {
background-color: rgba(0, 0, 0, 0.1);
}
ag-rating.custom-rating::part(star) {
opacity: 0.8;
transition: opacity 0.2s ease;
}
ag-rating.custom-rating::part(star):hover {
opacity: 1;
}Accessibility
- The component is implemented as a
role="slider"for screen reader compatibility. aria-valuenow,aria-valuemin, andaria-valuemaxare used to convey the current, minimum, and maximum values.- The component is keyboard navigable using arrow keys, Home, and End.
- A visually hidden
<span>witharia-live="polite"announces the rating to screen readers. - The component is focusable and has a visible focus ring.
Best Practices
- For ratings that are not interactive, always use the
readonlyprop. - Ensure sufficient color contrast for the star colors, especially when using
variant. - When used in a form, consider associating it with a label for better context.