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…
Live specimen
Interact with the demo. Every part is real and numbered.
تقرير-الربع.pdf
↑↓ تتخطّى الفاصل
Anatomy: every part, named
Hover a row to locate it in the demo above.
Build prompt
How it behaves right-to-left
This section is ours alone.
The inset rule
mirrorsA 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 mirrorsA 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
mirrorsThe 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
mirrorsA 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
mirrorsA 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.
See also
Updated · 2026-08-19