# Request Hero (Moonarc)

A hero for an HTTP API: the headline beside a console where a request is written line by line, a line runs while it is sent, the status lands with the time it took and the region that answered, and the JSON response streams in under it. Two to four requests take their turn once and the last one rests; a tab above the console picks any of them with radios and :has(), and the console holds still while it is pointed at or holds focus. The code is highlighted at build by Shiki, each token colour moved until it reads on the console, which is dark on every page and every tone: the theme's deepest neutral. Zero JavaScript.

- Pro block. Install: `npx shadcn@latest add @moonarc-pro/hero-request` (license key required)
- Tier B · category section · trigger load, click
- Readout: `<HeroRequest host="api.…" requests={[{ method, path, body, status, time, response }]} />`
- Browser support: newly (Chrome 105 · Firefox 121 · Safari 15.4); elsewhere: without :has() the requests play their turns and the tabs do nothing
- Measured cost: 0 B JS of its own · uses Reveal + runtime (with dependencies 3.0 kB raw; CSS 18.6 kB raw)
- Page: https://moonarc.dev/components/hero-request/

## Props

| Prop | Type | Default | Description |
|---|---|---|---|
| `host` | `string` | none | The host every request goes to, printed in the console's bar and as the Host header. |
| `requests` | `{ label; method; path; headers?; body?; status; statusText?; time; region?; response }[]` | none | One to four requests, in the order they play. label is the tab's word; method is GET, POST, PUT, PATCH or DELETE; headers come after Host in order (mask a secret yourself); body and response are written as JSON when they are objects; time is in ms; statusText defaults to the usual reason phrase. |
| `headline` | `string` | none | The headline, split by word. Slot "headline" wins and may hold an <em>, set in the emphasis face and colour. |
| `eyebrow` | `string` | none | A short line above the headline. Slot "eyebrow" wins. |
| `copy` | `string` | none | A paragraph under the headline. |
| `primary` | `{ href: string; label: string }` | none | The main action. |
| `secondary` | `{ href: string; label: string }` | none | The second action. Slot "actions" replaces both. |
| `note` | `string` | none | A short line under the actions, in the mono face. |
| `label` | `string` | `'Example requests'` | The name of the console and of its tabs, read out before them. |
| `level` | `1 | 2` | `1` | Heading level of the headline: 1 on a page's own hero, 2 under another h1. |
| `media` | `'right' | 'left'` | `'right'` | Side of the console on a wide screen. |
| `tone` | `'surface' | 'muted' | 'ink' | 'brand'` | `unset (paints nothing)` | The hero's ground. The console keeps its deep neutral on every tone, edged by a hairline. |
| `density` | `'compact' | 'cozy' | 'roomy'` | `'cozy'` | Block padding ×0.8 / ×1 / ×1.25 through --ma-density. |

## Usage

```astro
---
import HeroRequest from '@/components/moonarc/pro/HeroRequest.astro';
---
<HeroRequest
  eyebrow="Email API"
  copy="One request sends it. Webhooks tell you when it lands."
  primary={{ href: '/docs/quickstart/', label: 'Get an API key' }}
  secondary={{ href: '/reference/', label: 'API reference' }}
  host="api.example.com"
  requests={[
    {
      label: 'Send',
      method: 'POST',
      path: '/v1/emails',
      headers: { Authorization: 'Bearer sk_live_••••' },
      body: { to: 'ada@example.com', subject: 'Your receipt' },
      status: 202,
      time: 84,
      region: 'fra1',
      response: { id: 'em_7Hq2', status: 'queued' },
    },
    {
      label: 'Check',
      method: 'GET',
      path: '/v1/emails/em_7Hq2',
      status: 200,
      time: 31,
      response: { id: 'em_7Hq2', status: 'delivered' },
    },
  ]}
>
  <span slot="headline">Email that <em>arrives</em></span>
</HeroRequest>
```

## Reduced motion

The first request is shown whole, with no turns and nothing streaming; the tabs still pick another.

## With ClientRouter

CSS only; a fresh page plays the turns from the start.

## Craft

- A request has a rhythm: its lines arrive 70 ms apart, the send line runs for 650 ms, the status lands, then the response streams faster (45 ms a line) and rests long enough to read before the next turn.
- Each line's moment is a custom property written by a Shiki transformer at build, so the timeline is data in the HTML and the animation is one keyframe of opacity and translate. No stepped keyframe, which WebKit drops past the first second of a document.
- The turns run once and the last request rests: content that keeps moving beside a headline is what WCAG 2.2.2 asks to be stoppable, and a tab picked stops the turns and plays only that request again from its start.
- The console holds still under the pointer and while it has focus, so a reader who stops to read is not overtaken by the next request.
- Every request is in the HTML whole, one after another, so a screen reader reads each in order without the console's timing.
- The console is dark on a light page and on a dark one, in every tone (the theme's deepest neutral, which a tone scope does not repaint), and --ma-dark is 1 inside it, so Shiki's dark colours are the ones painted and each was moved at build until it reads at 4.5 : 1.

## Replaces

- API playground heroes on developer landing pages
- Animated request/response mockups

## Source

Pro block. The source is served by the license-gated registry: `npx shadcn@latest add @moonarc-pro/hero-request` with a key in components.json (https://moonarc.dev/account/). Everything it composes is free: split-text, reveal.
