Vitral 0.2
Customisation

Direction

Left to right unless you say otherwise: one attribute turns the layout, the keyboard and the icons that mean "next" round together.

Arabic, Hebrew, Persian and Urdu read right to left, and so does everything laid out for them: the sidebar starts on the right, a chevron that means “next” points left, and the Right arrow key moves backwards. Vitral is left to right by default and turns round on one attribute, because the browser already knows how to do most of it.

Set dir

index.html
<html lang="ar" dir="rtl">

That is the whole of the layout. Every stylesheet in the library is written in logical properties — padding-inline-start, inset-inline-end, border-inline-start — so the boxes, the paddings, the borders and the corners follow dir without a second stylesheet to ship or a build step to run. There is no vitral.rtl.css, and there is nothing to keep in sync.

Set it on the element you mount in, not only the CSS direction property: dir is what the components measure when they work out which arrow key moves forward, and what the icons key off.

Tell the configuration too

main.ts
import { Vitral } from '@vitral/vue';

createApp(App).use(Vitral, { direction: 'rtl' });

A popup is teleported to <body>, outside whatever dir your application carries, so it would open the wrong way round. The library gives each one the direction of the thing it belongs to, and falls back to this setting for the ones that belong to the page rather than to an anchor — a dialog, a toast. It is also what the server assumes, where there is no layout to measure.

nuxt.config.ts
export default defineNuxtConfig({
    modules: ['@vitral/nuxt'],
    vitral: { direction: 'rtl', locale: 'ar' }
});

In Nuxt the module puts dir on <html> as it renders, so the page arrives the right way round instead of turning over on hydration.

Part of a page

A direction is not an application-wide decision; it is a property of a piece of text. Nest it where it belongs:

template
<!-- One panel the other way round, inside a left-to-right page. -->
<div dir="rtl">
    <Menubar :model="items" />
    <DataGrid :value="rows" />
</div>

Components read the direction they are actually laid out in, so a right-to-left panel inside a left-to-right page behaves, keyboard included.

The keyboard

Reading right to left, Left is forward. Every component that moves with the arrow keys already knows: the tabs, menus, menubar, toolbar, stepper, rating, splitter, carousel, task board, schedule and the charts all swap Left and Right, and a swipe goes the other way with them. Overlays flip with it too — a menu that opens to the end of its trigger opens to the left.

Icons

An icon that means a direction has to turn round; an icon that means a side must not. chevronRight on a “next” button means the way the reader is travelling, so it is mirrored; alignLeft means the left of the paragraph in every language, so it is not. The set answers for itself and you can overrule it:

template
<!-- Ours know: a chevron that means "next" turns round, alignLeft does not. -->
<Button icon="chevronRight" label="Next" />

<!-- An icon from somewhere else cannot be asked, so tell it. -->
<Icon :icon="ArrowRightIcon" mirrored />

<!-- Or disagree about one of ours. -->
<Icon icon="undo" :mirrored="false" />

Media transport (play, rewind, fastForward) is deliberately left alone, because a timeline runs left to right everywhere, and so is rotation, which is clockwise or it is not. An icon of your own says which it is:

icons.ts
import { registerIcons } from '@vitral/icons';

registerIcons([{ name: 'nextStage', body: '<path d="M4 12h16M14 6l6 6-6 6"/>', mirrored: true }]);

The flip is CSS keyed off dir, not script: it costs nothing, it is the same on the server, and it follows a dir changed at runtime without anything re-rendering. renderSvg(def, {}, { direction: 'rtl' }) does the same for an icon drawn outside a component.

At runtime

ts
const { direction, isRtl, setDirection } = useDirection();

setDirection('rtl');
document.documentElement.dir = 'rtl';   // the layout follows this one

useDirection() is the reactive handle: the configured direction, whether it is right to left, and a setter that every component follows — the same shape as useLocale(), which you will usually be changing at the same moment. Moving the layout is still the dir attribute's job.