# Atoms — Rating

A **review score**: a sand star, the average score to one decimal and, optionally, the number of
reviews in muted text. Built from Gazelle's **Tailwind v4 utilities**. Screen readers hear one
sentence ("4.7 out of 5 stars, 96 reviews"). The visible pieces are `aria-hidden`. The copy field
on the Atoms → Rating screen emits this exact markup. A framework-free `.gz-rating` API in
`components/rating.css` mirrors the same design for projects not on Tailwind.

## Metadata

- **Category:** Atoms
- **Status:** Stable
- **Source:** `docs/atom-classes.js` (`GZ_ATOM_CLASSES.rating` + `GZ_ATOM_MARKUP.rating`, canonical
  markup); `components/rating.css` (`.gz-rating` framework-free compatibility API)
- **Production source:** `gazelle-int-website/app/components/review/ReviewRating.vue` (staging,
  `e1acfea4`)

## When to use

- Next to a product or model title on a card (Bike card, Model list card), aligned to the title's
  baseline and kept on one line (`shrink-0` from the card).

## When not to use

- Don't render it when there are no reviews. Production hides it unless `rating > 0` **and**
  `count > 0`. A "0.0" score is noise.
- Not as an input. This is a read-only summary, not a star picker.
- Not for review lists or Trustpilot widgets. Those have their own components.

## Anatomy

```html
<p class="inline-flex items-center gap-1 text-fg-primary type-caption-lg">
  <svg class="size-5.5 shrink-0 text-theme-sand-500" viewBox="0 0 24 24" fill="none" stroke-width="1.5" aria-hidden="true">…star…</svg>
  <span class="sr-only">4.7 out of 5 stars, 96 reviews</span>
  <span class="font-normal" aria-hidden="true">4.7</span>
  <span class="text-fg-muted" aria-hidden="true">(96)</span>
</p>
```

The star is Iconoir's `star` (solid body, filled and stroked with `currentColor`). Leave out the
count `<span>` (and ", 96 reviews" in the sentence) when the count is unknown.

## Class lists

| Part | Utilities |
| --- | --- |
| **Root** | `inline-flex items-center gap-1 text-fg-primary` + size |
| **Size — Small** (14px) | `type-caption-lg` |
| **Size — Large** (16px) | `type-body-sm` |
| **Step — `lg-tablet`** (Small → Large from tablet) | `tablet:type-body-sm` |
| **Star** | `size-5.5 shrink-0 text-theme-sand-500` (22px at every size) |
| **Score** | `font-normal` (400 over the role's light 300) |
| **Count** | `text-fg-muted` |

## Tokens

- Star `text-theme-sand-500`, text `text-fg-primary`, count `text-fg-muted`.
- Type: **Caption Large** → **Body Small**, with the score at weight `normal`.

## Production mapping

| Part | Production | Design system | Decision |
| --- | --- | --- | --- |
| Type | `text-xs tablet:text-sm leading-tight` (14 → 16px) | `type-caption-lg` + `tablet:type-body-sm` (14 → 16px) | Exact sizes; leading follows the roles (150) |
| Score | `font-regular text-main-black-500` | `font-normal` on `text-fg-primary` | Exact |
| Count | `font-light text-[#767676]` | `text-fg-muted` (`#606060`) | No raw hex. The muted foreground role is darker and has better contrast |
| Star | `nuxt-icon star-mall`, `text-theme-sand-500 text-lg-0` (22px) | Iconoir `star` solid, `size-5.5 text-theme-sand-500` | Same colour and size; glyph from the shared icon library |
| Gap | `gap-01` (4px) | `gap-1` | Exact |
| Accessibility | Score and count read separately ("4.7 (96)") | One sr-only sentence; visible parts `aria-hidden` | Improvement |

## Cross-references

- `specs/icons/icons.md` — the Iconoir library.
- `specs/molecules/bike-card.md`, `specs/molecules/model-list-card.md` — where the rating sits.
