|
| 1 | +--- |
| 2 | + |
| 3 | +title: Rating |
| 4 | +description: A star-rating input where the user selects a score, with support for fractional values. |
| 5 | +name: rating |
| 6 | +aria: https://www.w3.org/WAI/ARIA/apg/patterns/radio |
| 7 | +--- |
| 8 | + |
| 9 | +# Rating |
| 10 | + |
| 11 | +<Description> |
| 12 | +A star-rating input where the user selects a score, with support for fractional values. |
| 13 | +</Description> |
| 14 | + |
| 15 | +<ComponentPreview name="Rating" /> |
| 16 | + |
| 17 | +## Features |
| 18 | + |
| 19 | +<Highlights |
| 20 | + :features="[ |
| 21 | + 'Can be controlled or uncontrolled.', |
| 22 | + 'Supports fractional ratings (half, quarter, etc.) via the step prop.', |
| 23 | + 'Previews the value under the pointer on hover.', |
| 24 | + 'Can be cleared by clicking the active value again.', |
| 25 | + 'Built on top of Radio Group — full keyboard navigation and form support.', |
| 26 | + 'Supports Right to Left direction.', |
| 27 | + 'Exposes CSS variables for rendering partial steps.', |
| 28 | + ]" |
| 29 | +/> |
| 30 | + |
| 31 | +## Installation |
| 32 | + |
| 33 | +Install the component from your command line. |
| 34 | + |
| 35 | +<InstallationTabs value="reka-ui" /> |
| 36 | + |
| 37 | +## Anatomy |
| 38 | + |
| 39 | +Import all parts and piece them together. `RatingRoot` exposes the list of `items`, and each `RatingItem` exposes the `steps` it is made of, so you render an indicator per step. |
| 40 | + |
| 41 | +```vue |
| 42 | +<script setup> |
| 43 | +import { RatingItem, RatingItemIndicator, RatingRoot } from 'reka-ui' |
| 44 | +</script> |
| 45 | +
|
| 46 | +<template> |
| 47 | + <RatingRoot v-slot="{ items }"> |
| 48 | + <RatingItem |
| 49 | + v-for="item in items" |
| 50 | + :key="item" |
| 51 | + v-slot="{ steps }" |
| 52 | + :item="item" |
| 53 | + > |
| 54 | + <RatingItemIndicator |
| 55 | + v-for="step in steps" |
| 56 | + :key="step" |
| 57 | + :step="step" |
| 58 | + /> |
| 59 | + </RatingItem> |
| 60 | + </RatingRoot> |
| 61 | +</template> |
| 62 | +``` |
| 63 | + |
| 64 | +## API Reference |
| 65 | + |
| 66 | +### Root |
| 67 | + |
| 68 | +Contains all the parts of a rating and provides the rating state. Renders a [Radio Group](/docs/components/radio-group) under the hood, so it supports form submission and keyboard navigation. |
| 69 | + |
| 70 | +<!-- @include: @/meta/RatingRoot.md --> |
| 71 | + |
| 72 | +<DataAttributesTable |
| 73 | + :data="[ |
| 74 | + { |
| 75 | + attribute: '[data-disabled]', |
| 76 | + values: 'Present when disabled', |
| 77 | + }, |
| 78 | + { |
| 79 | + attribute: '[data-orientation]', |
| 80 | + values: ['vertical', 'horizontal'], |
| 81 | + }, |
| 82 | + ]" |
| 83 | +/> |
| 84 | + |
| 85 | +### Item |
| 86 | + |
| 87 | +Wraps a single rating value (e.g. a single star). It computes the list of `steps` that compose this item based on the root's `step` prop, exposed via the default slot. Renders a `label` by default. |
| 88 | + |
| 89 | +<!-- @include: @/meta/RatingItem.md --> |
| 90 | + |
| 91 | +### ItemIndicator |
| 92 | + |
| 93 | +The interactive indicator rendered for each step of an item. It reflects whether the step is active based on the current (or hovered) value. |
| 94 | + |
| 95 | +<!-- @include: @/meta/RatingItemIndicator.md --> |
| 96 | + |
| 97 | +<DataAttributesTable |
| 98 | + :data="[ |
| 99 | + { |
| 100 | + attribute: '[data-state]', |
| 101 | + values: ['active'], |
| 102 | + }, |
| 103 | + { |
| 104 | + attribute: '[data-disabled]', |
| 105 | + values: 'Present when disabled', |
| 106 | + }, |
| 107 | + ]" |
| 108 | +/> |
| 109 | + |
| 110 | +To render partial steps (for `step` values below `1`), `RatingItemIndicator` exposes the following CSS variables: |
| 111 | + |
| 112 | +| CSS Variable | Description | |
| 113 | +| ---------------------------------- | ------------------------------------------------------------------------------------ | |
| 114 | +| `--reka-rating-item-step-width` | The width this step should occupy within the item, e.g. `50%` for a half step. | |
| 115 | +| `--reka-rating-item-step-opacity` | `1` when the step should be visible, `0` otherwise (used to stack overlapping steps). | |
| 116 | +| `--reka-rating-item-step-z-index` | The stacking order of the step so smaller steps render above larger ones. | |
| 117 | + |
| 118 | +A typical fractional indicator clips its width and stacks steps using these variables: |
| 119 | + |
| 120 | +```vue |
| 121 | +<RatingItemIndicator |
| 122 | + :step="step" |
| 123 | + class="absolute overflow-hidden w-[var(--reka-rating-item-step-width)] opacity-[var(--reka-rating-item-step-opacity)] z-[var(--reka-rating-item-step-z-index)]" |
| 124 | +/> |
| 125 | +``` |
| 126 | + |
| 127 | +## Examples |
| 128 | + |
| 129 | +### Fractional rating |
| 130 | + |
| 131 | +Use the `step` prop to allow values smaller than `1`. Each `RatingItem` is then split into multiple `steps`, and each step renders its own indicator clipped with the exposed CSS variables. |
| 132 | + |
| 133 | +```vue line=10 |
| 134 | +<script setup> |
| 135 | +import { RatingItem, RatingItemIndicator, RatingRoot } from 'reka-ui' |
| 136 | +import { ref } from 'vue' |
| 137 | +
|
| 138 | +const rating = ref(2.5) |
| 139 | +</script> |
| 140 | +
|
| 141 | +<template> |
| 142 | + <RatingRoot |
| 143 | + v-slot="{ items }" |
| 144 | + v-model="rating" |
| 145 | + :step="0.5" |
| 146 | + > |
| 147 | + <RatingItem |
| 148 | + v-for="item in items" |
| 149 | + :key="item" |
| 150 | + v-slot="{ steps }" |
| 151 | + :item="item" |
| 152 | + class="relative" |
| 153 | + > |
| 154 | + <RatingItemIndicator |
| 155 | + v-for="step in steps" |
| 156 | + :key="step" |
| 157 | + :step="step" |
| 158 | + class="absolute overflow-hidden w-[var(--reka-rating-item-step-width)] opacity-[var(--reka-rating-item-step-opacity)] z-[var(--reka-rating-item-step-z-index)]" |
| 159 | + /> |
| 160 | + </RatingItem> |
| 161 | + </RatingRoot> |
| 162 | +</template> |
| 163 | +``` |
| 164 | + |
| 165 | +### Clearable |
| 166 | + |
| 167 | +Use the `clearable` prop to let users reset the rating to `0` by clicking the currently selected value again. |
| 168 | + |
| 169 | +```vue line=4 |
| 170 | +<template> |
| 171 | + <RatingRoot |
| 172 | + v-model="rating" |
| 173 | + clearable |
| 174 | + > |
| 175 | + <!-- ... --> |
| 176 | + </RatingRoot> |
| 177 | +</template> |
| 178 | +``` |
| 179 | + |
| 180 | +### Hover preview |
| 181 | + |
| 182 | +Use the `hoverable` prop to preview the value under the pointer before committing to it. The `RatingItemIndicator` exposes `data-state="active"` for every step at or below the hovered value. |
| 183 | + |
| 184 | +```vue line=4 |
| 185 | +<template> |
| 186 | + <RatingRoot |
| 187 | + v-model="rating" |
| 188 | + hoverable |
| 189 | + > |
| 190 | + <!-- ... --> |
| 191 | + </RatingRoot> |
| 192 | +</template> |
| 193 | +``` |
| 194 | + |
| 195 | +### Custom length |
| 196 | + |
| 197 | +Use the `length` prop to change how many items are rendered. It defaults to `5`. |
| 198 | + |
| 199 | +```vue line=4 |
| 200 | +<template> |
| 201 | + <RatingRoot |
| 202 | + v-model="rating" |
| 203 | + :length="10" |
| 204 | + > |
| 205 | + <!-- ... --> |
| 206 | + </RatingRoot> |
| 207 | +</template> |
| 208 | +``` |
| 209 | + |
| 210 | +### Read-only / disabled |
| 211 | + |
| 212 | +Use the `disabled` prop to prevent interaction, for example when displaying an average rating. |
| 213 | + |
| 214 | +```vue line=4 |
| 215 | +<template> |
| 216 | + <RatingRoot |
| 217 | + :default-value="4" |
| 218 | + disabled |
| 219 | + > |
| 220 | + <!-- ... --> |
| 221 | + </RatingRoot> |
| 222 | +</template> |
| 223 | +``` |
| 224 | + |
| 225 | +## Accessibility |
| 226 | + |
| 227 | +Built on top of the [Radio Group](/docs/components/radio-group) primitive and adheres to the [Radio Group WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/radio). Provide an accessible label for each step via `aria-label` so screen reader users understand the value each indicator represents. |
| 228 | + |
| 229 | +### Keyboard Interactions |
| 230 | + |
| 231 | +<KeyboardTable |
| 232 | + :data="[ |
| 233 | + { |
| 234 | + keys: ['Tab'], |
| 235 | + description: 'Moves focus to either the checked item or the first item in the rating.', |
| 236 | + }, |
| 237 | + { |
| 238 | + keys: ['Space'], |
| 239 | + description: 'When focus is on an unchecked item, selects that value.', |
| 240 | + }, |
| 241 | + { |
| 242 | + keys: ['ArrowDown'], |
| 243 | + description: 'Moves focus and selection to the next item.', |
| 244 | + }, |
| 245 | + { |
| 246 | + keys: ['ArrowRight'], |
| 247 | + description: 'Moves focus and selection to the next item.', |
| 248 | + }, |
| 249 | + { |
| 250 | + keys: ['ArrowUp'], |
| 251 | + description: 'Moves focus and selection to the previous item.', |
| 252 | + }, |
| 253 | + { |
| 254 | + keys: ['ArrowLeft'], |
| 255 | + description: 'Moves focus and selection to the previous item.', |
| 256 | + }, |
| 257 | + ]" |
| 258 | +/> |
0 commit comments