星級評分(React)
經典星星評分的 React 元件:支援半星、hover 預覽填色、方向鍵與數字鍵操作、點選時星星彈跳,唯讀模式可顯示平均分、評分人數與分佈長條;圖示、顏色與顆數皆可自訂。
補齊最基本卻最常用的星星評分:每顆星都是一顆按鈕,指到哪填到哪(半星模式指左半邊就是 .5),移開就還原成目前分數;點選當下那顆星會彈跳一下。填色用「底層灰、上層依百分比裁切」兩層圖示疊出來,所以不只半星,唯讀模式下平均 4.3 也會精準填三成。鍵盤可用方向鍵、Home/End 與數字鍵操作,readOnly 時不可聚焦、可搭配 count 顯示「4.5(1,234)」與 distribution 分佈長條;icon、color、max 都能換,用 Heart 就是愛心評分。
載入預覽⋯
npx shadcn@latest add https://webberui.com/r/star-rating.jsonPlayground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
5
<StarRating />
安裝
npx shadcn@latest add https://webberui.com/r/star-rating.json或在 components.json 設定 registries 後,改用 @webberui/star-rating 安裝。
使用
import { StarRating, ratingSummary } from "@/components/ui/star-rating";
import { Heart } from "lucide-react";
// 可互動:半星、再點一次清空、顯示讀數與文字標籤
<StarRating
allowHalf
allowClear
showValue
labels={["很差", "差", "普通", "好", "很好"]}
onChange={(value) => console.log(value)}
/>
// 唯讀平均分 + 評分人數 + 分佈長條(distribution 索引 0 為 1 星)
const distribution = [12, 25, 88, 340, 812];
const { average, total } = ratingSummary(distribution);
<StarRating value={average} readOnly showValue count={total} distribution={distribution} />
// 換圖示、顏色與顆數
<StarRating icon={Heart} color="#f43f5e" max={3} size="sm" defaultValue={2} />Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
value | number | — | 受控值(0–max;允許半星時可為 .5);不提供時為非受控模式 |
defaultValue | number | 0 | 非受控模式的初始值 |
onChange | (value: number) => void | — | 值變動時回呼(點選、鍵盤操作與清空都會觸發) |
max | number | 5 | 星星顆數 |
allowHalf | boolean | false | 允許半星:指到星星左半邊即為 .5,鍵盤以 0.5 為單位增減 |
readOnly | boolean | false | 唯讀:不可聚焦、不可操作,只呈現分數(可為任意小數,例如平均 4.3 會填三成) |
disabled | boolean | false | 停用:保留外觀但不可操作,整體降低透明度 |
size | "sm" | "md" | "lg" | number | "md" | 尺寸:sm 16px/md 24px/lg 32px,或直接給像素數 |
icon | LucideIcon | Star | 星星圖示(lucide-react 元件);換成 Heart 就是愛心評分 |
color | string | "#f59e0b" | 填色(任何 CSS 顏色值,如 #f59e0b、rgb(…)、var(--primary)),預設琥珀色 |
showValue | boolean | false | 在星星右側顯示分數讀數;hover 預覽時同步變化,提供 labels 時一併顯示該星的文字 |
count | number | — | 評分人數,顯示為「(1,234)」並納入無障礙播報;通常搭配 readOnly 呈現平均分 |
labels | string[] | — | 每顆星的文字說明(索引 0 對應 1 星),例如 ["很差","差","普通","好","很好"];播報為「3 星:普通」 |
allowClear | boolean | false | 再點一次目前的分數即清空為 0(鍵盤 Backspace/Delete/數字 0 亦可清空) |
distribution | number[] | — | 評分分佈:索引 0 為 1 星的則數、依序到 max 星;畫面由高星到低星排列長條 |
ariaLabel | string | "評分" | 評分群組的無障礙名稱 |
className | string | — | 透傳到最外層容器 |
另外具名匯出 ratingSummary(distribution)(回傳 { average, total },把後端的分佈直接換成平均分與總則數)、formatRatingValue(value)(讀數格式:整數不帶小數、其餘保留一位)與 StarRatingSize 型別。
細節
- 兩層圖示疊出任意比例:每顆星底層是整顆灰色圖示,上層是同一顆圖示、外框依百分比裁切寬度;半星就是 50%,唯讀模式下平均 4.3 的第五顆會填 30%,換成任何 lucide 圖示都適用
- 半星是兩顆按鈕:
allowHalf時每顆星的左右半邊各是一顆透明按鈕(值分別為 n−0.5 與 n),不做座標運算,觸控與滑鼠行為一致;互動模式的值一律對齊步進,才有唯一對應的按鈕可標記選中 - hover 只預覽、不改值:指到哪顆就填到哪並更新讀數,指標離開群組即還原;觸控不預覽,避免點一下閃兩次
- 點選彈跳只在互動時:被選中的那顆星(半星時為所屬整顆)以 keyframes 放大再回彈;用
useAnimationControls觸發,連續選同一顆也會重播 - 鍵盤操作:←/↓ 減一步、→/↑ 加一步(半星時一步為 0.5),Home 跳最低、End 跳滿分,數字鍵直接指定分數;
allowClear時 Backspace/Delete/0 清空。焦點會跟著新選中的星星走(roving tabindex) readOnly與互動模式互斥:唯讀完全不渲染按鈕,因此無法聚焦;disabled則保留按鈕但停用,整體降低透明度
可及性
- 互動模式為
role="radiogroup"(aria-label取自ariaLabel),每顆星(半星時為每半顆)是role="radio"的按鈕,aria-checked標記目前分數、aria-label播報「3 星:普通」或「2.5 星」;僅目前選中的按鈕在 Tab 序列中,其餘以方向鍵切換 - 唯讀模式整列為
role="img",標籤為「4.5 星,滿分 5 星,共 1,277 則評分」;分佈長條是語意化的ul,每列附有「5 星:812 則(64%)」的隱藏文字,評分本體再以aria-describedby指向它作為補充說明 - 停用時按鈕帶
disabled且群組標記aria-disabled;讀數與星星圖示皆aria-hidden,避免與 radio 播報重複 - 使用者系統開啟「減少動態效果」時:點選不彈跳、hover 不放大,只保留填色與讀數變化