ArenaSection.prompt.md

September 6, 2026 ยท View on GitHub

A named region of a page: a heading, what sits around it, and the group it names. The section register is the middle rung of the title ladder, under a page head and over a card.

<ArenaSection eyebrow="This week" title="Landed recently"
  description="Everything that cleared customs since Monday."
  action={<ArenaButton variant="ghost" iconRight="ph-bold ph-arrow-right">See all</ArenaButton>}>
  <ArenaGrid>{lots.map((lot) => <LotCard key={lot.id} lot={lot} />)}</ArenaGrid>
</ArenaSection>

Members, in contract order and under this layer's own names. * marks a required one.

MemberFormTypeDefaultWhat it is
title*primitivestringNames the region, both on screen and to assistive technology. Required, and guarded at runtime after trimming: a section is a heading over a group, and one with no heading is a stack, which css/rhythm.css already ships as a class. The guard trims first because the value it exists to catch is a present and useless one, not an absent one, which the type already refuses.
headingLevelenumArenaHeadingLevel"h2"Which rung of the document outline the title takes. Only the element changes: the title's class is the same at every value, so the render is identical and no appearance follows from it. It defaults to h2 because the section register is already a step under a page's title and a step over a card's, and this is that register said as structure rather than as a size. none is refused at runtime, the rule every component whose title is required follows: a title required because it names the thing it draws cannot also be told that the name is not one.
children*slotWhat the region holds. Required, and guarded at runtime: a section renders a heading naming a group, so a childless one renders a label for nothing. The guard counts the way the render path counts, so a child that is a false conditional counts as absent rather than as one.
eyebrowprimitivestringA line above the title saying which part of the page this is. Same register as every other eyebrow in the system, so a style plugin that takes them out of the console's mono capitals takes this one with them.
descriptionprimitivestringA line under the title, in the muted ink. It sits below the head row rather than beside the title, because a sentence and an action competing for the same row is what makes a head wrap on a narrow screen.
actionslotTrailing content in the head row, aligned to the end and to the title's own baseline. Arena draws the row; the consumer draws what sits in it. A link that leads to the whole of what the section shows a slice of is the ordinary case.
rhythmenumArenaSectionRhythm"md"How far the head stands from the body. The steps are the page rhythm scale itself, so sm reads as one unit, md as a head over its own content and lg as a head over a region of the page, and none closes the distance entirely for a section whose body carries its own top edge. Nothing here is a number this component chose.

title and children are both required, and both are guarded at runtime. A section is a heading over a group. With no heading it is a stack, which css/rhythm.css already ships as .arena-stack. With no children the heading names nothing. The title guard trims first, because the value it exists to catch is a present and useless one rather than an absent one.

The component renders a plain <section> and claims no landmark. A <section> becomes a region in the accessibility tree only once it has an accessible name. A page where every section announced itself would bury the two or three landmarks that matter. The heading is the structure a reader navigates by, and it is the platform's own.

A section nests, and headingLevel is how. The default is h2, the section rung of the title ladder and the right answer directly under a page head. A section inside another section takes headingLevel="h3", and the card register under it goes a rung further, or a reader walking headings meets peers where the page holds a hierarchy. Nothing on screen moves with the value. The title's class is the same at every rung, so the register a style plugin sets is untouched by the level the outline takes. none is refused here, since a section is a heading over a group.

rhythm is the distance from the head to the body, in the same three named steps the page rhythm scale carries, plus none. The padding is not the air between one section and the next. Arena draws no outer margin on anything, so that air stays yours to place with .arena-stack--section.

Do / Don't

  • Do let the description carry the sentence. Putting it in the action slot puts prose in a row sized for controls, and that row is what wraps first on a narrow screen.
  • Do nest a section inside a page that already has an ArenaPageHead. The two registers are a step apart on purpose, and a style plugin moves them together.
  • Don't reach for it when there is no title. The air between them is .arena-stack, and the guard says so.
  • Don't put a heading of your own inside the body at the level this one took. The section already opened one, and a reader walking headings will read two peers where there is one region.

The rules of the language hold in the code you write from this page. An Arena component is not a styling surface, so put no className of your own on it. Read every value through its token, never a raw colour and never a bare 16px. Never wrap it in your router's own link. arena-to-prod --audit reports these three in your sources. The rest are in ../../../../../skills/design/SKILL.md, which marks the ones it reports.