Relationships
Implemented by 3
Informs 1
Related 13
Branches 3
Context
ADR-041 makes section the base that compositions place. The page-section runes (hero, cta, feature, pricing, reveal, tabs, accordion, steps) are expected to become compositions over it in time. Whatever section does with a leading paragraph or heading then becomes the header grammar of that whole family.
Today the family has two grammars, and they disagree:
| Content | Native page-section runes (pageSectionProperties, packages/runes/src/tags/common.ts) | section (packages/runes/src/tags/section.ts) |
|---|---|---|
| Paragraphs, no heading | the first paragraph is the blurb | the first paragraph is the eyebrow |
| Paragraph, then heading | eyebrow, then headline | eyebrow, then headline |
| Two headings | the first heading is the eyebrow | the second heading is body |
| Heading, then prose, then a paragraph | the first paragraph after the heading, anywhere, is the blurb | only a paragraph directly after the heading is the blurb |
Composition makes section's grammar read content that was never written as a header. A host runs its content model over the expanded template, so an author's paragraph arriving through a slot is matched by position like anything else:
- SPEC-169 D4's
stepsexample places each step with{% section %}{% slot /%}{% /section %}. A step's text is a paragraph with no heading before it, sosectionrenders it as an eyebrow: small, uppercase, in the primary colour. - A composed
testimonial(SPEC-161 D1) that places its quote and attribution slots in asectiongets the quote as the eyebrow and the attribution as the blurb. - The composed
character,realmandfactionescape only because their template places{% metablock %}between the heading and the description slot. Without it, the description's first paragraph would become a blurb. Nothing would report it.
A standalone section has the same problem, and WORK-652 makes it more common. With a media split, section becomes the general "region with media" primitive. An image beside two paragraphs then renders the first paragraph as an eyebrow.
Decision
The grammar is anchored to the headline. Eyebrow and blurb exist only relative to a headline. With no heading there is no header: all content is body. An eyebrow is a paragraph directly before the headline, and a blurb is a paragraph directly after it.
sectiontakesheader:auto(the default) reads eyebrow, headline and blurb as in decision 1;headlinelifts only the headline, so the paragraph after it stays body;nonereads no header at all: the region is all body.
The attribute applies wherever
sectionis used, standalone or placed by a composition. A composition sets it like any other attribute, for example{% section header="headline" %}.A slot that lands in a header role it is not named for is reported. At transform time, when content carrying a slot marker fills a header role and the slot's name is not that role's, the build warns, naming the composed rune, the slot, the role and both remedies. For example: "slot
descriptionofcharacteris read assection's blurb; name the fieldblurb, or setheader="headline"on the section." The warning changes nothing that renders.The page-section runes keep their native grammar until each one becomes a composition. A composed page-section rune parses its author's content with its own content model and places the result. It does not inherit
pageSectionProperties' rules throughsection.
Rationale
- The anchoring alone fixes every misreading found: the
stepsandtestimonialcases and the standalone region with media. None of them has a heading. - An attribute is visible and portable. It shows in the source, in
inspectand inreference, and means the same thing on a page and in a template. - The diagnostic is precise. The slot markers are already on the AST, so the check sees exactly which slot filled which role, with no static guessing about what a slot may contain.
- Deciding on
sectionnow is cheaper than later. Every composition placed on it after this inherits the grammar, and changing it then would move every one of them.
Consequences
section's output changes for content with no heading: a leading paragraph that rendered as an eyebrow is now body. The site's existingsectionexamples all have a heading, but the changeset names the change for third-party content.- SPEC-169 D4's
stepsexample renders its steps as body. headeris a new attribute onsection.referencedocuments it, and the editor offers it.
Resolved: a heading is never an eyebrow
This was left open: hero, cta and feature were documented as reading two leading headings as an eyebrow heading and a headline, and section's eyebrow is a paragraph only. The options were an eyebrow field that also takes a heading followed by another heading, or a composed hero that turns a heading eyebrow into a paragraph.
Decided on 2026-10-26: neither. An eyebrow is a paragraph directly before the headline (decision 1), with no heading form, in section and in every composition over it.
What was measured, rendering ## What's new in v2, # Build with refrakt.md and a paragraph:
| Rune | Today |
|---|---|
cta | <h2 data-name="eyebrow">, <h1 data-name="headline">, <p data-name="blurb">: works |
hero | <h2 data-name="headline">What's new in v2</h2>; the real headline and the blurb are dropped with no warning |
feature | the same as hero |
hero and feature read their content with a sequence whose eyebrow matches a paragraph only, so the second heading matches nothing and is discarded before pageSectionProperties runs (BUG-047). Of 43 hero, cta and feature blocks in the site, the plugins and the scaffolds, one writes a heading eyebrow: the documented example in site/content/docs/authoring/page-sections.md, which uses hero and so renders wrongly.
Why the form is dropped rather than carried into section:
- Nothing relies on it. Two of its three runes lose content when it is written, and its only example is one of those.
- It is the positional misreading this decision removes. Two headings in a row is also how an author writes a title followed by a subsection.
pageSectionPropertiesis looser still: it takes the first heading as an eyebrow whenever any second heading follows, after any amount of prose. - It is wrong in the outline. A heading eyebrow renders as a heading, an
h2ahead of theh1incta, so heading navigation lists a label as a section and out of order. An eyebrow is a label, and a paragraph is the element for it. - Both options cost more than they save. The first puts the misreading back into the grammar every composition inherits. The second still has to guess which heading is the headline.
Consequences:
- SPEC-160 D7's composed
heroandctaplace no heading eyebrow.ctais the one rune where the form works today: when it is composed, the first of two leading headings is the headline and the second is body. The changeset names this; no deprecation warning is planned, since no use is known. - Decision 4 is unchanged: the native page-section runes keep
pageSectionPropertiesuntil each is composed. - The docs stop describing the form (WORK-660).
References
- ADR-041: section is the composition base.
- SPEC-160: D9 records this decision; D7 carries the open question.
- SPEC-145: D10a (slot markers); the host's positional grammar reads the template's arrangement.
- SPEC-169: D4's
stepsexample. - SPEC-170: D2, one meaning for a snippet wherever it is pasted.
- WORK-659: the implementation.