Components
Custom components built on top of the design system
This page documents custom components that extend the base design system.
StarRating
The StarRating component displays rating values using the design system's ember color palette, replacing emoji-based stars for consistency and accessibility.
Usage
import { StarRating } from "@/components/star-rating";
// Display a 4 out of 5 rating
<StarRating value={4} />
// Display with custom max (e.g., 3 stars)
<StarRating value={2} max={3} />
// Display with custom className
<StarRating value={5} className="text-lg" />Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | null | undefined | — | Number of filled stars (0–5) |
max | number | 5 | Total number of stars to render |
className | string | — | Additional CSS classes |
Behavior
- Null/Zero Handling: If
valueisnull,undefined, or0, renders a dash (—) in muted foreground color - Clamping: Values are clamped between
0andmax - Rounding: Non-integer values are rounded to nearest integer
- Design Tokens: Uses
--embercolor for filled stars,--muted-foregroundfor empty stars
Design System Compliance
This component addresses FINDING-007 from the design audit:
- ✅ Uses Lucide
StarIconinstead of emoji (⭐) - ✅ Applies design token
text-emberfor filled state - ✅ Applies
text-muted-foreground/30for empty state - ✅ Consistent sizing via
size-3.5(14px) - ✅ Fill state via
fill-emberandfill-transparent
Implementation
// Location: apps/app/src/app/(authenticated)/reports/components/star-rating.tsx
import { StarIcon } from "lucide-react";
import { cn } from "@repo/design-system/lib/utils";
interface StarRatingProps {
value: number | null | undefined;
max?: number;
className?: string;
}
export function StarRating({ value, max = 5, className }: StarRatingProps) {
const filled = Math.max(0, Math.min(Math.round(value ?? 0), max));
if (filled === 0) {
return <span className={cn("text-muted-foreground", className)}>—</span>;
}
return (
<span className={cn("inline-flex items-center gap-0.5", className)}>
{Array.from({ length: max }, (_, i) => (
<StarIcon
key={i}
className={cn(
"size-3.5",
i < filled
? "fill-ember text-ember"
: "fill-transparent text-muted-foreground/30"
)}
/>
))}
</span>
);
}Related Components
- Badge: For status chips and category labels
- Card: For containing rating displays in report cards
Interactive Primitives Showcase
Below you can preview and interactively play with some of the core UI primitives from @repo/design-system:
BUTTON PLAYGROUND
Variant
Size
BADGE PLAYGROUND
DEFAULT
Variant
SPINNER PLAYGROUND
Size
LOGO PLAYGROUND