npx astro add moonarcpnpm astro add moonarcbunx astro add moonarcA code block with tabs (for example npm, pnpm, bun) and a copy button per panel.
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.
Email API
One request sends it. Webhooks tell you when it lands, and every message keeps its own log.
Sample figures, made up for this demo
POST /v1/emails HTTP/1.1
Host: api.sitename.example
Authorization: Bearer sk_live_••••
Content-Type: application/json
{
"from": "hello@sitename.example",
"to": "ada@example.com",
"subject": "Your receipt",
"template": "receipt"
}202 Accepted84 msfra1
{
"id": "em_7Hq2kD9x",
"status": "queued",
"created_at": "2026-10-09T08:14:22Z"
}GET /v1/emails/em_7Hq2kD9x HTTP/1.1
Host: api.sitename.example
Authorization: Bearer sk_live_••••200 OK31 msfra1
{
"id": "em_7Hq2kD9x",
"status": "delivered",
"opened": false,
"events": [
{
"type": "delivered",
"at": "2026-10-09T08:14:25Z"
}
]
}GET /v1/domains HTTP/1.1
Host: api.sitename.example
Authorization: Bearer sk_live_••••200 OK27 msiad1
{
"data": [
{
"name": "sitename.example",
"status": "verified",
"region": "eu-west"
}
],
"has_more": false
}Live, from the library itself, at full size: scroll, hover, navigate away and back. The demo is never gated.
Variants · each row writes the attribute the prop writes; the section's own CSS answers
JavaScript of its own
0 B
Uses Reveal and the shared runtime (1.9 kB raw, once per site). With those included: 3.0 kB raw.
CSS 18.6 kB raw including the base tokens. Measured from a production build, and again in CI for every change that can move it.
Browser support
Chrome 105 · Firefox 121 · Safari 15.4; needs has. Elsewhere: without :has() the requests play their turns and the tabs do nothing.
The demo above is never gated; the source is. With a key in components.json (see account), the registry serves it like any other item:
npx shadcn@latest add @moonarc-pro/hero-request---
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>Without a key the registry answers 401 with an upgrade message. Everything this section composes is free: split-text, reveal.
| 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. |
The section reads the theme contract (shadcn's variable names with the base tokens as fallback), so a pasted theme or one --ma-brand recolours it. Its class names are the override surface and stay stable; a named slot replaces the matching prop with your own markup.
.ma-hreq.ma-hreq__actions.ma-hreq__bar.ma-hreq__code.ma-hreq__console.ma-hreq__copy.ma-hreq__eyebrow.ma-hreq__headline.ma-hreq__host.ma-hreq__inner.ma-hreq__line.ma-hreq__lock.ma-hreq__note.ma-hreq__panel.ma-hreq__panels.ma-hreq__primary.ma-hreq__radio.ma-hreq__req.ma-hreq__res.ma-hreq__secondary.ma-hreq__send.ma-hreq__status.ma-hreq__tab.ma-hreq__tabs.ma-hreq__text
slot="eyebrow"slot="headline"slot="actions"
The first request is shown whole, with no turns and nothing streaming; the tabs still pick another.
ClientRouterCSS only; a fresh page plays the turns from the start.
Replaces: API playground heroes on developer landing pages · Animated request/response mockups. See the migration table.
npx astro add moonarcpnpm astro add moonarcbunx astro add moonarcA code block with tabs (for example npm, pnpm, bun) and a copy button per panel.
Every request takes your key as a bearer token. Sample API, made up for this demo.
Base URLhttps://api.sitename.example/v1
Send an email, then follow it until it lands.
POST/emails
Queues one email. It is sent within seconds; follow it with GET /emails/{id} or a webhook.
Body
fromstringFormat: email.required
An address on a domain you have verified.
tostringFormat: email.required
subjectstringAt most 998 characters.required
textstringoptional
The plain-text body.
tagsarray of objectsoptional
Labels you can filter the logs by.
namestringoptional
valuestringoptional
Responses
202422Request
curl -X POST "https://api.sitename.example/v1/emails" \
-H "Authorization: Bearer $SITENAME_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "hello@sitename.example",
"to": "ada@example.com",
"subject": "Your receipt",
"text": "string",
"tags": [
{
"name": "kind",
"value": "receipt"
}
]
}'const res = await fetch('https://api.sitename.example/v1/emails', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SITENAME_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
from: 'hello@sitename.example',
to: 'ada@example.com',
subject: 'Your receipt',
text: 'string',
tags: [
{
name: 'kind',
value: 'receipt',
},
],
}),
});
const data = await res.json();import os
import requests
res = requests.post(
"https://api.sitename.example/v1/emails",
headers={"Authorization": f"Bearer {os.environ['SITENAME_KEY']}"},
json={
"from": "hello@sitename.example",
"to": "ada@example.com",
"subject": "Your receipt",
"text": "string",
"tags": [
{
"name": "kind",
"value": "receipt",
},
],
},
)
print(res.json()){
"id": "em_7Hq2kD9x",
"status": "queued",
"created_at": "2026-10-09T08:14:22Z"
}{
"error": "domain_not_verified",
"message": "Verify sitename.example before sending from it."
}GET/emails/{id}
Path parameters
idstringrequired
The id the send returned.
Responses
200Request
curl "https://api.sitename.example/v1/emails/em_7Hq2kD9x" \
-H "Authorization: Bearer $SITENAME_KEY"const res = await fetch('https://api.sitename.example/v1/emails/em_7Hq2kD9x', {
headers: {
Authorization: `Bearer ${process.env.SITENAME_KEY}`,
},
});
const data = await res.json();import os
import requests
res = requests.get(
"https://api.sitename.example/v1/emails/em_7Hq2kD9x",
headers={"Authorization": f"Bearer {os.environ['SITENAME_KEY']}"},
)
print(res.json()){
"id": "em_7Hq2kD9x",
"status": "queued",
"created_at": "2026-01-01T00:00:00Z"
}The domains you send from, and whether their records check out.
GET/domains
Query parameters
limitinteger1 to 100.optional
How many to return.
Default20
Responses
200Request
curl "https://api.sitename.example/v1/domains" \
-H "Authorization: Bearer $SITENAME_KEY"const res = await fetch('https://api.sitename.example/v1/domains', {
headers: {
Authorization: `Bearer ${process.env.SITENAME_KEY}`,
},
});
const data = await res.json();import os
import requests
res = requests.get(
"https://api.sitename.example/v1/domains",
headers={"Authorization": f"Bearer {os.environ['SITENAME_KEY']}"},
)
print(res.json()){
"data": [
{
"name": "sitename.example",
"status": "verified"
}
],
"has_more": false
}An API reference in one section: a sidebar of groups and their operations; for each operation its method and path, what it does, its path, query and header parameters, the fields of its body (nested fields under a disclosure, as deep as they go) and its responses; and beside it, sticky on a wide screen, the request in every language with a copy button and the response examples by status.
Add the MCP server once and the agent searches components by intent and byte budget, reads their craft notes and gets the command that installs them.
# in your project
claude mcp add moonarc -- npx -y @moonarc/mcp// .cursor/mcp.json
{
"mcpServers": {
"moonarc": { "command": "npx", "args": ["-y", "@moonarc/mcp"] }
}
}// .vscode/mcp.json
{
"servers": {
"moonarc": { "type": "stdio", "command": "npx", "args": ["-y", "@moonarc/mcp"] }
}
}# ~/.codex/config.toml
[mcp_servers.moonarc]
command = "npx"
args = ["-y", "@moonarc/mcp"]A feature section with code beside the copy: a headline, copy and a short list on one side, and CodeTabs on the other, one tab per client or language, each panel with its own copy button.
A hero for developer tools: split headline on the left, a terminal window on the right that types the install command and prints its output line by line, on a pulsing dot field.
Also: view transitions · how costs are measured · browser support · accessibility policy · five-minute setup · MCP for agents · this page as Markdown