Separator
Static line separating sections of content, rendered as an hr and hidden from assistive technology unless the line itself is the only thing marking the boundary.
Selection
The criteria an agent checks before retrieving this component.
Use when
- Use when a visible line marks a break between sections of content (e.g., between entries in a feed, or between groups of settings on a preferences screen).
- Use when a line separates groups of items or controls inside a container (e.g., separating destructive actions from the rest of a menu).
Try a different component when
- Do not use when the divider can be moved by the user to resize the regions on either side of it, which is a focusable widget with a value and a keyboard model rather than a static line (use
splitter.basic). - Do not use when the line is a border belonging to a single element, such as a card edge, an input underline, or a rule between table cells. That is styling on that element rather than a break between two things, and it needs no separator semantics.
Must Haves
Non-negotiable structure. Every generated instance must satisfy these rules.
- A decorative separator is
<hr aria-hidden="true">. It is decorative when the boundary it draws is already carried by the surrounding markup, such as<section>or<article>elements, headings, or list items. - A meaningful separator is a plain
<hr>, which carries theseparatorrole implicitly and needs no ARIA. It is meaningful when the line is the only thing marking where one topic ends and the next begins. - A separator that is exposed and runs vertically carries
aria-orientation="vertical", becausehorizontalis the implicit value for this role. - A separator that is exposed is a graphical object the reader needs, and carries the contrast requirement in
global.non-text-contrast. A decorative one does not, because it conveys nothing that needs to be perceived. - A separator drawn as a
background-colorfill rather than aborderis restated under forced colors, where author backgrounds are replaced and the line disappears entirely. Seeglobal.forced-colors.
Donts
Avoid these accessibility and UX barriers.
- Do not use
<hr>as a child of<ul>,<ol>, or<menu>. Those elements accept only<li>and script-supporting elements, so an<hr>between list items is invalid markup. Use<li role="separator">instead. - Do not make a separator focusable. It has no interactive behavior, so a
tabindexproduces a tab stop that does nothing. - Do not place text, icons, or controls inside a separator. The role has presentational children, so that content is not announced.
- Do not give a decorative separator an accessible name. It produces an announcement carrying no information.
- Do not repeat separators to create rhythm or spacing. Each exposed one is announced, and spacing is a style concern.
- Do not rely on a separator alone to convey a grouping. Even when exposed it reports a boundary without saying what the groups are, so a reader who needs to know what changed still needs a heading or a labelled region.
Customizable
Alternatives and options that give the AI agent some room to move.
- Drawing the line as a CSS
borderon an element that is already in the markup, instead of adding an<hr>. This is equivalent to the decorative case and adds no node at all, which is often the cleaner result when the separator is purely visual. - Using
role="separator"on another element where the host element's content model does not permit<hr>. Between list items this is the only correct route, and it takes the form<li role="separator">. - Thickness, color, length, inset, and surrounding spacing are all at the engineer's discretion, subject to the contrast requirement above when the separator is exposed.
<hr>is fully styleable; reset its default border and margins first (e.g.,border: none; border-top: 1px solid; margin: 0;). - Whether a section break also carries a visible label (e.g., "Today" above a group of notifications). When it does, that label is a heading and the line beside it is decorative, because the heading already communicates the break.
Golden Pattern
The tested reference implementation. Agents start from this shape and adapt to the developer’s codebase and context.
export function SeparatorDemo() {
return (
<div>
{/* Decorative: each section has a heading, so the boundary is already in the markup. */}
<section>
<h2>Continue watching</h2>
<p>Signal Lost, Season 2</p>
</section>
<hr aria-hidden="true" />
<section>
<h2>Because you watched Night Dispatch</h2>
<p>The Quiet Hours, Static Sky</p>
</section>
{/* Meaningful: a flat run with no headings, so the line is the only marker of the break. */}
<p>The dispatcher works the overnight shift alone.</p>
<hr />
<p>Two years earlier, the same station had three people on nights.</p>
{/* Exposed and vertical, so the implicit horizontal orientation is overridden. */}
<div style={{ display: "flex", alignItems: "center", gap: 12 }}>
<p>Now playing</p>
<hr aria-orientation="vertical" style={{ alignSelf: "stretch" }} />
<p>Up next</p>
</div>
</div>
);
}
Acceptance Checks
The component’s test spec — an optional body of checks for verification.
Screen Reader
- Reading through the page does not announce a separator for any line whose boundary is already carried by a heading, a sectioning element, or a list item.
- Every line that is the only marker of a topic change is announced as a separator at the point the break occurs.
- No separator is announced with a name, a label, or any inner text.
Keyboard
- Tabbing through the page never lands on a separator.
Structure
- No
<hr>appears as a child of a<ul>,<ol>, or<menu>element. - Exposed vertical separators carry
aria-orientation="vertical". - With forced colors active, every separator that is still meant to be seen remains visible.