# Blocks — Model listing

The **model overview** grid. It shows a count, then Model list cards in one to three columns,
grouped under category headings with an optional description pill. Card skeletons show while
loading. Rebuilt from production's new `ModelListing` Storyblok component. The Blocks → Model
listing screen emits this exact markup.

## Metadata

- **Category:** Blocks
- **Family:** Listings
- **Status:** Partial. Filters, sorting, the mobile frame selector and per-category content
  blocks are not covered yet; the filter column is reserved.
- **Storyblok component:** `ModelListing`
- **Production source:** `gazelle-int-website/app/storyblok/ModelListing.vue` on
  `origin/staging` (`e1acfea4`, epic `mop-pop-compare`) → `ModelListCard.vue`,
  `catalog/CardSkeletonGrid.vue`
- **Source:** `docs/blocks-registry.js` (canonical markup) composing
  `docs/molecules-registry.js` (Model list card, Card skeleton)

## When to use

- The "all models" page: every model, grouped by category, filterable.

## When not to use

- For individual bikes, use the **Product listing** or **Bike listing**.
- For a few editorial model rows, use **Model card** molecules.

## Fields

| Field | Type | Covered | Notes |
| --- | --- | --- | --- |
| *(categories view)* | runtime | Yes | **Group by category**: production shows categories unless a filter is active, then one flat grid |
| *(category description)* | translation per category | Yes | Pill next to the category heading |
| *(state)* | runtime | Yes | Results / Loading (six skeletons) / No results |
| `ModelListContentBlock` | per category | No | The promo tile that can join a category grid |
| Filters, sort, frame selector | `CatalogSidebar`, `CatalogSortResults`, `CatalogFrameSelector` | No | The filter column is reserved from laptop |

## Anatomy

```html
<section class="bg-surface-oyster">
  <div class="mx-auto max-w-content laptop:grid laptop:grid-cols-8 laptop:gap-8 px-gutter pt-8 pb-8 tablet:pt-11 laptop:pt-16 laptop:pb-20">
    <aside class="hidden laptop:col-span-2 laptop:block" aria-label="Filters" data-slot="Filters"></aside>
    <div class="laptop:col-span-6">
      <p class="mb-8 type-caption-lg text-fg-primary desktop:type-body-sm" role="status">3 models<span class="hidden tablet:inline"> (9 bikes)</span></p>
      <div class="mb-8 flex items-center gap-3.5 text-fg-primary tablet:mb-10 tablet:gap-4">  <!-- + mt-12 after the first -->
        <h2 class="type-h2-sm tablet:type-h2-lg">Comfort</h2>
        <p class="inline-flex h-10 items-center rounded-full rounded-bl-none bg-oyster-800 px-4 type-caption-lg tablet:h-11 tablet:px-4.5 tablet:type-body-sm">Upright, stable and easy to step on</p>
      </div>
      <ul class="grid grid-cols-1 gap-4 tablet:grid-cols-2 desktop:grid-cols-3">
        <li class="flex flex-col"><!-- Model list card --></li>
      </ul>
    </div>
  </div>
</section>
```

## Tokens

- Layout: `max-w-content`, `px-gutter`, `pt-8 tablet:pt-11 laptop:pt-16`, `pb-8 laptop:pb-20`,
  `laptop:grid-cols-8 laptop:gap-8`, grid `gap-4`, `desktop:grid-cols-3`.
- Colour: `bg-surface-oyster`, pill `bg-oyster-800`.
- Type: count **Caption Large → Body Small** (from desktop), category **H2** (32 → 40px), pill
  **Caption Large → Body Small**.
- Shape: pill `rounded-full rounded-bl-none` (a speech-bubble corner).

## Production mapping

| Part | Production | Design system | Decision |
| --- | --- | --- | --- |
| Section | `bg-main-oyster-500`, `px-05 pb-07 laptop:pb-11 pt-07 tablet:pt-[44px] laptop:pt-10` | `bg-surface-oyster`, `px-gutter pb-8 laptop:pb-20 pt-8 tablet:pt-11 laptop:pt-16` | Exact |
| Columns | `laptop:grid-cols-8 laptop:gap-07`, sidebar `col-span-2`, results `col-span-6` | Same, `laptop:gap-8` | Exact |
| Count | `text-xs desktop:text-sm font-light tracking-[-0.4px]`, `mb-07` | `type-caption-lg desktop:type-body-sm`, `mb-8` | Exact sizes |
| Category heading | `<span class="text-xl tablet:text-lg-3 tracking-[-1.6px] tablet:tracking-[-2px] font-light">` | `<h2 class="type-h2-sm tablet:type-h2-lg">` | Exact sizes (32 → 40px); a real heading |
| Category row | `gap-[14px] tablet:gap-04 mb-07 tablet:mb-08`, `mt-09` after the first | `gap-3.5 tablet:gap-4 mb-8 tablet:mb-10`, `mt-12` | Exact |
| Pill | `h-[40px] tablet:h-[44px] text-xs tablet:text-sm font-light bg-main-oyster-800 px-04 tablet:px-[18px] rounded-[20px] rounded-bl-[0px]` | `h-10 tablet:h-11 type-caption-lg tablet:type-body-sm bg-oyster-800 px-4 tablet:px-4.5 rounded-full rounded-bl-none` | Exact; `rounded-full` gives the same pill at these heights |
| Grid | `grid-cols-1 tablet:grid-cols-2 desktop:grid-cols-3 gap-04` | Same on a `<ul>` | Exact |
| Skeletons | `CatalogCardSkeletonGrid variant="models"` (6) | Six Card skeletons in the same grid | Exact |

## Cross-references

- `specs/molecules/model-list-card.md`, `specs/molecules/card-skeleton.md`
- `specs/blocks/product-listing.md`: the same page frame for bikes
