Stars

Fractional star rating with count, filled by a clipped overlay.

ui
$npx shadcn@latest add @storekit/stars
stars

Live and interactive. Switch the brand in the frame header to see the same source under another token file.

Installation

$npx shadcn@latest add @storekit/stars
$pnpm dlx shadcn@latest add @storekit/stars
$yarn dlx shadcn@latest add @storekit/stars
$bunx --bun shadcn@latest add @storekit/stars

Source

components/storekit/ui/stars.tsx
47 lines
import { useId } from "react"
import { cn } from "@/components/storekit/lib/cn"

/* Fractional star rating. Each star clips its own fill to the fraction it
   earns, so 4.6 reads as four full stars and a 60% fifth. */

const PATH = "M12 2.5l2.9 6.1 6.7.8-4.9 4.6 1.3 6.6L12 17.3l-6 3.3 1.3-6.6L2.4 9.4l6.7-.8L12 2.5z"

export function Stars({
  value,
  count,
  size = 14,
  className,
  showValue = false,
}: {
  value: number
  count?: number
  size?: number
  className?: string
  showValue?: boolean
}) {
  const id = useId()
  return (
    <span className={cn("inline-flex items-center gap-2", className)} aria-label={`${value.toFixed(1)} out of 5`}>
      <span className="flex gap-0.5" aria-hidden>
        {Array.from({ length: 5 }).map((_, i) => {
          const frac = Math.max(0, Math.min(1, value - i))
          const clip = `${id}-${i}`
          return (
            <svg key={i} width={size} height={size} viewBox="0 0 24 24">
              <defs>
                <clipPath id={clip}>
                  <rect x="0" y="0" width={24 * frac} height="24" />
                </clipPath>
              </defs>
              <path d={PATH} className="fill-sk-ink/15" />
              {frac > 0 ? <path d={PATH} className="fill-sk-accent" clipPath={`url(#${clip})`} /> : null}
            </svg>
          )
        })}
      </span>
      {showValue ? <span className="text-sm font-medium tabular-nums text-sk-ink">{value.toFixed(1)}</span> : null}
      {typeof count === "number" ? <span className="text-sm tabular-nums text-sk-muted">({count.toLocaleString()})</span> : null}
    </span>
  )
}

Usage

Import from "@/components/storekit/ui/stars" and pass your product data. The demo above exercises the full prop surface with sample values.

Stars, StoreKit