# Services List (Moonarc)

What a studio or a freelancer does, as numbered rows at display size that open one at a time: each row names a service, and opens to a short text beside the list of deliverables. Every row is a native <details> (the browser owns the opening, the keyboard and the one-open rule through the name attribute), the height animates through ::details-content, and the name underlines on hover with UnderlineHover. Zero JavaScript of its own.

- Pro block. Install: `npx shadcn@latest add @moonarc-pro/services-list` (license key required)
- Tier B · category section · trigger click, hover
- Readout: `<ServicesList services={[{ title, body, deliverables }]} exclusive>`
- Browser support: newly (Chrome 131 · Firefox 143 · Safari 18.4); elsewhere: without ::details-content a row opens at once, without the height animation; without the name attribute (Firefox before 130) several rows can be open together
- Measured cost: 0 B JS (CSS 10.4 kB raw)
- Page: https://moonarc.dev/components/services-list/

## Props

| Prop | Type | Default | Description |
|---|---|---|---|
| `services` | `{ title: string; body: string; deliverables?: string[] }[]` | none | The rows, in order: a name, a short paragraph, and what the client receives, one line each. |
| `headline` | `string` | none | The section's heading. Slot "headline" wins. |
| `eyebrow` | `string` | none | A short line above the headline, in mono. Slot "eyebrow" wins. |
| `copy` | `string` | none | A paragraph under the headline. Slot "copy" wins. |
| `level` | `1 | 2` | `2` | Heading level of the headline: 2 under a page's h1, 1 when the list is the page's own heading. |
| `exclusive` | `boolean` | `true` | One row open at a time: opening one closes the others, through the details name attribute (no script). |
| `open` | `number` | none | Index of the row open on load; unset, all are closed. |
| `deliverablesLabel` | `string` | `'Deliverables'` | The small title over each row's deliverables. |
| `numbered` | `boolean` | `true` | Print 01, 02 … before each name, in mono. The numbers are aria-hidden: the order is the list's own. |
| `tone` | `'surface' | 'muted' | 'ink' | 'brand'` | `unset (paints nothing)` | The ground: the page surface, a muted band, an always-dark ink panel, or a deep brand panel. The hairlines follow the tone's --border, and an open row's plus inverts in the tone's own pair. |
| `density` | `'compact' | 'cozy' | 'roomy'` | `'cozy'` | Block padding and the rows' height ×0.8 / ×1 / ×1.25 through --ma-density. |

## Usage

```astro
---
import ServicesList from '@/components/moonarc/pro/ServicesList.astro';
---
<ServicesList
  headline="Services"
  services={[
    { title: 'Brand identity', body: 'A name, a mark and the rules that hold them together, for a company that is new or has outgrown its look.', deliverables: ['Logo and mark', 'Type and colour', 'Brand guidelines'] },
    { title: 'Websites', body: 'Sites designed and built in one team, from the first sketch to the code that ships.', deliverables: ['Design system', 'Astro build', 'Content model'] },
    { title: 'Motion', body: 'Animation for screens and campaigns: the logo in motion, product films, the interface itself.', deliverables: ['Motion identity', 'Product film', 'Interaction specs'] },
  ]}
  open={0}
/>
```

## Reduced motion

Rows open and close at once; the plus turns into a minus without the transition, and the underline appears without its sweep.

## With ClientRouter

Native <details>; nothing to rebind. A row open on the page a reader leaves is closed again on the next load, as the server rendered it.

## Craft

- The row is the summary, so the target is the full width of the list at display size: no chevron to aim for.
- A body that grows from 0fr to 1fr in ::details-content animates to its real height with nothing measured, the same technique as Accordion; a browser without the pseudo-element simply opens it.
- One open at a time is the details name attribute, the browser's own rule, so opening a row with the keyboard or by find-in-page closes the others the same way.
- The plus is two strokes: the vertical one scales to nothing as the row opens, so plus becomes minus. The circle around it fills with the text colour on hover and while open, and its strokes take the section's ground.
- Numbers in mono at the secondary strength carry the rhythm instead of an all-caps label over the heading.

## Replaces

- Services sections (Framer studio templates)
- Accordion services (Aceternity)
- Services list (Relume)

## Source

Pro block. The source is served by the license-gated registry: `npx shadcn@latest add @moonarc-pro/services-list` with a key in components.json (https://moonarc.dev/account/). Everything it composes is free: underline-hover.
