<script setup lang="ts">
import { computed, ref } from "vue";
import { Button } from "@dicehub/kappa/components/button";
import { DirectionProvider } from "@dicehub/kappa/components/direction-provider";
import { Radio } from "@dicehub/kappa/components/radio";
type DemoLocale = "en-US" | "ar";
const messages = {
"en-US": {
direction: "ltr",
label: "Message priority",
options: [
{ value: "low", label: "Low" },
{ value: "normal", label: "Normal" },
{ value: "high", label: "High" },
],
},
ar: {
direction: "rtl",
label: "أولوية الرسالة",
options: [
{ value: "low", label: "منخفضة" },
{ value: "normal", label: "عادية" },
{ value: "high", label: "عالية" },
],
},
} as const;
const locale = ref<DemoLocale>("en-US");
const priority = ref("normal");
const content = computed(() => messages[locale.value]);
</script>
<template>
<section class="direction-example">
<div class="direction-example__languages" role="group" aria-label="Interface language">
<Button
size="sm"
:variant="locale === 'en-US' ? 'primary' : 'outline'"
:aria-pressed="locale === 'en-US'"
@click="locale = 'en-US'"
>
English
</Button>
<Button
size="sm"
:variant="locale === 'ar' ? 'primary' : 'outline'"
:aria-pressed="locale === 'ar'"
@click="locale = 'ar'"
>
العربية
</Button>
</div>
<section class="direction-example__surface" :dir="content.direction" :lang="locale">
<DirectionProvider :locale="locale">
<Radio.Root
v-model="priority"
name="direction-preview-priority"
orientation="horizontal"
>
<Radio.Label>{{ content.label }}</Radio.Label>
<Radio.Item
v-for="option in content.options"
:key="option.value"
:value="option.value"
>
<Radio.ItemControl />
<Radio.ItemText>{{ option.label }}</Radio.ItemText>
</Radio.Item>
</Radio.Root>
</DirectionProvider>
</section>
</section>
</template>
<style scoped>
.direction-example {
display: grid;
inline-size: min(100%, 32rem);
gap: 1rem;
}
.direction-example__languages {
display: flex;
justify-content: center;
gap: 0.5rem;
direction: ltr;
}
.direction-example__surface {
padding: 1rem;
border: 1px solid var(--kappa-line);
border-radius: 0.75rem;
background: var(--kappa-control);
}
</style>Installation
Direction Provider is part of the main Kappa package. It composes Ark UI's locale utility and adds no dependency beyond the existing Ark UI runtime.
Barrel
import { DirectionProvider } from "@dicehub/kappa";Granular
import { DirectionProvider } from "@dicehub/kappa/components/direction-provider";Usage
Pass a BCP 47 locale. Ark UI derives the direction from that locale. Also set matching lang and dir attributes on the document or containing region. The provider controls component behavior; the native attributes control text layout and assistive-technology language.
<script setup lang="ts">
import { DirectionProvider } from "@dicehub/kappa/components/direction-provider";
import { Radio } from "@dicehub/kappa/components/radio";
</script>
<template>
<section class="direction-scope" dir="rtl" lang="ar">
<DirectionProvider locale="ar">
<Radio.Root default-value="list" name="direction-usage-view" orientation="horizontal">
<Radio.Label>طريقة العرض</Radio.Label>
<Radio.Item value="list">
<Radio.ItemControl />
<Radio.ItemText>قائمة</Radio.ItemText>
</Radio.Item>
<Radio.Item value="grid">
<Radio.ItemControl />
<Radio.ItemText>شبكة</Radio.ItemText>
</Radio.Item>
</Radio.Root>
</DirectionProvider>
</section>
</template>
<style scoped>
.direction-scope {
inline-size: min(100%, 32rem);
padding: 1rem;
border: 1px solid var(--kappa-line);
border-radius: 0.75rem;
background: var(--kappa-control);
}
</style>Composition
DirectionProvider (Ark UI LocaleProvider; no DOM element)
└── default slotDirection Provider renders no DOM element. It passes Ark UI locale context to every descendant. Components with their own dir prop can still override the inherited direction locally.
Reference Behavior
Kappa follows Ark UI's Locale Provider, which derives direction from one locale. The locale-based API keeps Ark UI positioning, keyboard movement, filtering, and formatting on one consistent context.
Examples
Nested Providers
Use a nested provider when one region has a different locale and direction.
<script setup lang="ts">
import { DirectionProvider } from "@dicehub/kappa/components/direction-provider";
import { Radio } from "@dicehub/kappa/components/radio";
</script>
<template>
<section class="direction-scopes" dir="rtl" lang="ar">
<DirectionProvider locale="ar">
<Radio.Root default-value="recent" name="direction-outer-sort" orientation="horizontal">
<Radio.Label>ترتيب النتائج</Radio.Label>
<Radio.Item value="recent">
<Radio.ItemControl />
<Radio.ItemText>الأحدث</Radio.ItemText>
</Radio.Item>
<Radio.Item value="name">
<Radio.ItemControl />
<Radio.ItemText>الاسم</Radio.ItemText>
</Radio.Item>
</Radio.Root>
<section class="direction-scopes__nested" dir="ltr" lang="en">
<DirectionProvider locale="en-US">
<Radio.Root default-value="recent" name="direction-inner-sort" orientation="horizontal">
<Radio.Label>Result order</Radio.Label>
<Radio.Item value="recent">
<Radio.ItemControl />
<Radio.ItemText>Most recent</Radio.ItemText>
</Radio.Item>
<Radio.Item value="name">
<Radio.ItemControl />
<Radio.ItemText>Name</Radio.ItemText>
</Radio.Item>
</Radio.Root>
</DirectionProvider>
</section>
</DirectionProvider>
</section>
</template>
<style scoped>
.direction-scopes {
display: grid;
inline-size: min(100%, 32rem);
gap: 1rem;
padding: 1rem;
border: 1px solid var(--kappa-line);
border-radius: 0.75rem;
background: var(--kappa-control);
}
.direction-scopes__nested {
padding: 0.875rem;
border: 1px solid var(--kappa-line);
border-radius: 0.5rem;
background: var(--kappa-tint);
}
</style>useDirection
Call useDirection() in a descendant component when custom behavior must follow the active direction. It returns a computed ltr orrtl value and falls back to ltr outside a provider.
<!-- DirectionValue.vue -->
<script setup lang="ts">
import { useDirection } from "@dicehub/kappa/components/direction-provider";
const direction = useDirection();
</script>
<template>
<output class="direction-value" data-direction-value aria-live="polite">
Current direction: <strong>{{ direction.toUpperCase() }}</strong>
</output>
</template>
<!-- App.vue -->
<script setup lang="ts">
import { DirectionProvider } from "@dicehub/kappa/components/direction-provider";
import DirectionValue from "./DirectionValue.vue";
</script>
<template>
<section class="direction-scope direction-scope--value" dir="rtl" lang="he">
<DirectionProvider locale="he">
<span>עברית</span>
<DirectionValue />
</DirectionProvider>
</section>
</template>
<style scoped>
.direction-scope {
inline-size: min(100%, 32rem);
padding: 1rem;
border: 1px solid var(--kappa-line);
border-radius: 0.75rem;
background: var(--kappa-control);
}
.direction-scope--value {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}
.direction-value {
color: var(--kappa-subtle);
font-size: 0.8125rem;
}
</style>Accessibility
- Set both
langanddiron the document or scoped DOM region. - Use a locale that matches the written language. Do not use an RTL locale only to mirror a layout.
- Keep the DOM direction and provider locale synchronized during runtime language changes.
- Nested providers are valid for embedded content, but each nested DOM region also needs matching native attributes.
- Ark UI uses the context for directional arrow keys, placement, and locale-sensitive behavior.
API Reference
DirectionProvider
| Prop | Type | Default | Description |
|---|---|---|---|
locale | string | "en-US" | BCP 47 locale from which Ark UI derives direction and locale behavior. |
Slots
| Slot | Description |
|---|---|
default | Application or region content that receives the Ark UI locale context. |
Exports
| Export | Description |
|---|---|
DirectionProvider | Headless Ark UI locale-context provider. |
useDirection | Returns a computed ltr or rtl value from the nearest provider. |
Direction | The ltr and rtl direction union. |
DirectionProviderProps | Public locale prop contract. |
DirectionProviderSlots | Default content slot contract. |
DIRECTION_PROVIDER_DEFAULT_LOCALE | The en-US fallback locale. |