Documentation
Guides

Right-to-left layouts

Run a layout in a right-to-left page, where start is on the right and splitters, arrow keys, drops and borders follow the screen.

A layout follows the direction of the page it renders in. Set dir="rtl" on <html> (or on any ancestor of Dockable.Root) and start is the right side: the first tabset of a row, the first tab of a strip and the start border are on the right, and every interaction lands where the pointer or the key points.

Right-to-left layout
Gallery

Turning it on

Nothing in the layout changes: the same JSON, the same primitives, the same commands. The rows and the tab strips are CSS flex, so they mirror with the page. The engine reads the computed direction of its root when it attaches (and when a dir above it changes), and works from the start side in RTL:

<html lang="ar" dir="rtl">
    …
    <Dockable.Root model={model}>…</Dockable.Root>
</html>

engine.get("direction") says which one it read, "ltr" or "rtl" (take the engine from useDockable). A popout window takes the direction of the layout it came from (its <html> gets that dir, wherever the page sets it), and its own engine reads its root there.

What follows the direction

partin RTL
splitter, pointerthe splitter follows the pointer
splitter, keysArrowLeft moves it left, ArrowRight right: the way they point, as in LTR
splitter, ARIAaria-valuenow is the start child's share of the row: the child on the right
border splittergrows its panel towards the layout: ArrowLeft for the start border, ArrowRight for the end one
side dropsthe half of a tabset nearer the right edge is start
edge dropsthe right edge band is start, and Dockable.EdgeIndicator edge="start" is drawn there
strip dropsthe slots run from the right: a drop at the right of the first tab is index 0
overlay bordersa start overlay sits on the right (engine.get("overlay-placement-by") returns right: 0)
tab keysin a horizontal strip, ArrowLeft moves focus to the next tab, ArrowRight to the previous one
drop outlineDockable.DropIndicator is placed on the side the drop goes to

The names never change with the direction: a drop on the right of a tabset is still data-drop-location="start", and the command it runs is the same tab.move with location: "start". Top and bottom stay physical (vertical writing modes are not supported).

Flipping the direction at runtime

The engine observes the dir attribute of its root and of every ancestor of it: changing it on <html> or on a wrapper repositions the panels by itself, with the tabsets, and the popout windows take the new direction too. A dir inside the layout's content (an editor's paragraph) is not the layout's, and does not re-measure it.

<div dir={rtl ? "rtl" : "ltr"}>
    <Dockable.Root model={model}>…</Dockable.Root>
</div>

A direction changed by CSS alone (a class that sets direction: rtl, with no dir attribute) moves the tabsets without the engine noticing: run engine.run("measure-and-position") after it, which reads the direction again.

Borders

The start border's strip comes first in the frame's flex row, so it renders on the right. How its side labels read is yours: data-tab-direction says what the tabDirection prop asks for, and the engine does not change it with the page. On the right, labels usually read down, so pass tabDirection="down" there ("up" stays the default):

<Dockable.Border node={border} tabDirection={rtl ? "down" : "up"}>…</Dockable.Border>

Your styles then turn the labels from the attribute, the same way in both directions (see Borders).

Styling

The engine writes physical inline styles (left, width) for the panels and the outline, mapped from the start side. Your styles mirror with the page when they are logical: Tailwind's ps-, pe-, start-, end-, border-s, border-e, or CSS padding-inline-start, inset-inline-end. What has no logical form needs an rtl: variant (or :dir(rtl)):

  • an icon that points along the row, such as an edge indicator's arrow: rtl:-scale-x-100;
  • a transform that centres a splitter's grab area from its start: rtl:data-[orientation=vertical]:after:translate-x-1/2 (see Splitters);
  • a shadow or a gradient drawn on one side.

Text in another direction than the page's, such as English in an RTL page, keeps its punctuation in place with dir="auto" on its element.