UIDex

Divider

الخطّ الفاصل

<hr> / role="separator"

also calledseparator، horizontal rule، hr، menu separator، thematic break، hairline، ديفايدر، سيبراتور، فاصل، خطّ أفقي، فاصل القائمة، حدّ فاصل

A divider is a thin line marking the boundary between two blocks of an interface, and three lines that look identical on screen carry three different meanings in code. An <hr> is a thematic break in the content, so what follows is about something else; its implicit role is separator, and it reaches the accessibility tree with no ARIA at all. A role="separator" inside role="menu" or role="toolbar" divides one group of controls from the next and says nothing about the content, while a purely decorative rule exists for visual rhythm and has to disappear from the accessibility tree completely. A divider is also not a CSS border: a border is a property of a box and creates no node in the tree, so it is never announced however heavy it looks on screen. It is not a heading either, because a heading names the section that follows and lands in the list a screen reader jumps through, while a separator only reports that something changed here. And the moment the line takes focus and the user can drag it, it stops being structure and becomes a splitter carrying aria-valuenow. Two questions settle any line in front of you: does it need a node in the accessibility tree, and can the user focus it.

If you called it…

"the thin grey line between two sections""the line with the word or sitting in the middle of it""the little line that splits menu options into groups""the vertical bar between two toolbar buttons""the line that stops short of the edge of the card"

Live specimen

Interact with the demo. Every part is real and numbered.

تقرير-الربع.pdf

↑↓ تتخطّى الفاصل


RTLالخطّ المُزاح يبدأ بعد الأيقونة في الاتجاهين، بلا margin-left

Anatomy: every part, named

Hover a row to locate it in the demo above.

Build prompt

Build dividers for an Arabic interface, and decide what kind each line is before you draw it. Use <hr> when what follows is about a different subject, and leave it without ARIA because the implicit separator role is enough, but zero the border and paint a single border-block-start, since the default look is a box with inset borders rather than a line. Inside role="menu" or role="toolbar", use an element with role="separator" to divide the groups, and keep it out of the focus order, keep arrow keys stepping over it, and keep it out of aria-setsize and aria-posinset. Hide every decorative line with aria-hidden="true", or skip the element entirely and paint it as a border-block-end on the row. For a labelled divider: a container with role="separator" holding two aria-hidden lines around the word, plus aria-label carrying the same word, because the role names from author and not from its content, and never generate the word with CSS content. Give every vertical rule an explicit aria-orientation="vertical", since the default is horizontal. Use logical properties throughout: margin-inline for the inset, border-inline-start for the vertical rule between two groups, inset-inline-start in place of left, and never write margin-left or border-right. Give any fading gradient an explicit rule under [dir="rtl"], because linear-gradient accepts no logical keywords. If the separator can be dragged, give it tabindex="0" with aria-valuenow, aria-valuemin, and aria-valuemax, and multiply the pointer delta by a direction sign before updating the value.

How it behaves right-to-left

This section is ours alone.

The inset rule

mirrors

A rule that begins after the row icon has to follow that icon when the page flips. Write margin-inline-start: 56px, never margin-left: 56px, or the inset stays on the left in Arabic while the icon moves to the right and the line looks cut from the wrong end. A symmetric inset is one value, margin-inline: 16px, and needs no flip because both ends already match. Note that the UA stylesheet already writes the default <hr> margins logically, as margin-inline: auto, so physical code here is a step back from the browser default.

The labelled divider

never mirrors

A divider with a word in the middle needs no mirroring when it is built as a flex row with flex: 1 on the two lines flanking the word: the inline axis orders itself, and centring is symmetric in both directions. A divider whose word starts at the beginning of the line is set with text-align: start or with flex ordering, and never with text-align: left. The one construction that breaks here draws the two lines with ::before and ::after pinned by left: 0 and right: 0, so move those to inset-inline-start and inset-inline-end.

The vertical rule between controls

mirrors

The vertical rule that separates two button groups in a toolbar is usually painted as a border on the second group. Write .group + .group { border-inline-start: 1px solid var(--line) }: the + combinator follows DOM order, the inline-start edge follows reading direction, and the two agree in Arabic exactly as they agree in English. border-left instead moves after the flip to the outer edge of the last group, adding a stray line at the end of the toolbar and deleting the one that used to sit between the groups. If you draw the rule as its own 1px-wide element inside a flex row, it is direction neutral and needs no change at all.

The fading gradient

mirrors

A rule that fades toward its end is painted with linear-gradient, and that function has no logical keywords: it takes to right, to left, and angles in degrees, with no to inline-end anywhere. A line written to right therefore fades against the reading direction in Arabic. Fix it with an explicit [dir="rtl"] .fade { background-image: linear-gradient(to left, …) }, or with a symmetric gradient that softens at both ends so direction stops mattering. The same limit applies word for word to mask-image if that is how you fade the end of the line.

The draggable splitter

mirrors

A separator that takes focus and can be moved is a widget rather than structure, so aria-valuenow becomes required on it, while aria-valuemin and aria-valuemax carry implicit values of 0 and 100. Pointer events hand you a purely physical delta: clientX grows toward the right whatever the page direction, so in Arabic a drag to the right has to shrink the first pane rather than grow it. Multiply the delta by a sign read from getComputedStyle(el).direction === "rtl" ? -1 : 1 before updating the value, and apply the same flip to ArrowLeft and ArrowRight so the handle keeps moving visually toward the arrow the user pressed. Have aria-valuenow describe the pane at the logical start, not the pane on the left.

In code

Each row is one framework's word for the same thing. Take the row your project speaks.

HTML<hr>A thematic break in the content, with an implicit separator role, and a void element that takes no children.
ARIArole="separator"A structure role for as long as it cannot be focused, and it names from aria-label rather than from its visible text.
Radix UI<Separator.Root orientation="vertical" />Radix emits aria-orientation for the vertical case only, since horizontal is already the role default.
ARIArole="separator" + aria-valuenowOnce the separator takes focus and can be dragged it is a splitter, with a value the user can read and change.
CSSborder-inline-startThe logical property that keeps a vertical rule between the two groups after the page flips.
shadcn/ui<Separator decorative={false} />The wrapper defaults decorative to true, which ships role="none", so no separator is announced until you pass false.
Material UI<Divider variant="inset" />The inset is a built-in variant, and it is what tells a row divider apart from a section divider.

See also

Updated · 2026-08-19