Commit 41fe118
authored
feat(ui): add RTL support for tldraw UI (tldraw#8033)
Resurrects the approach from tldraw#6682 with review feedback addressed. In
order to support RTL languages like Arabic in the tldraw UI, this PR
adds a `useDirection()` hook and updates all Radix UI components and CSS
to respect the current locale's text direction.
Screenshot:
<img width="1430" height="658" alt="Screenshot 2026-03-09 at 10 19 14"
src="https://github.com/user-attachments/assets/9b0e91a8-74be-4be3-93b4-caa55662ecc9"
/>
### Key changes
- **`useDirection()` hook**: Returns `'ltr'` or `'rtl'` from the current
translation context. Exported publicly from `@tldraw/tldraw`.
- **Container `dir` attribute**: Set on `.tl-container` via `useEffect`
so CSS can use `.tl-container[dir='rtl']` selectors (review feedback:
set `dir` on container instead of scattering it everywhere).
- **Provider reordering**: `TldrawUiTranslationProvider` moved above
`TldrawUiTooltipProvider` so tooltips can access direction (review
feedback from tldraw#6682).
- **Radix `dir` prop**: All Radix UI root components use `dir={dir}`
instead of hardcoded `dir="ltr"` — dropdown menus, popovers, dialogs,
selects, sliders, tooltips, context menus. Context menu trigger keeps
`dir="ltr"` since the canvas is always LTR.
- **Submenu chevrons**: Flip to `chevron-left` in RTL mode.
- **CSS logical properties**: Converted physical properties
(`margin-left`, `padding-right`, `text-align: left`, `left`/`right`) to
logical equivalents (`margin-inline-start`, `padding-inline-end`,
`text-align: start`, `inset-inline-start`/`inset-inline-end`). Added
`.tl-container[dir='rtl']` overrides for gradients that can't use
logical properties.
### Review feedback from tldraw#6682 addressed
1. ✅ Set `dir` on `.tl-container` instead of scattering `dir` attributes
2. ✅ Don't call hooks inline as props — hooks called at component level
3. ✅ Keep popover `align = 'center'` default unchanged
4. ✅ No brittle `width: 15px` on `.tlui-kbd > span`
5. ✅ Use Radix `dir` props instead of CSS `!important` overrides
6. ✅ Context menu trigger uses `dir="ltr"` (canvas is always LTR), Root
uses `{dir}`
7. ✅ `TldrawUiTranslationProvider` moved above `TldrawUiTooltipProvider`
### Change type
- [x] `feature`
### Test plan
1. `yarn dev` → open localhost:5420
2. Switch language to Arabic (العربية) in user preferences
3. Verify menus open on the correct side
4. Verify context menu positions correctly
5. Verify keyboard shortcuts display correctly in menus
6. Verify page menu layout mirrors
7. Verify submenus have left chevrons
8. Verify dialog titles and buttons respect direction
- [x] Unit tests
### API changes
- Added `useDirection()` — returns current text direction (`'ltr'` |
`'rtl'`) based on locale
### Release notes
- Add RTL (right-to-left) support for the tldraw UI when using Arabic
and other RTL languages
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Touches a wide surface area of UI components and CSS, so regressions
in menu positioning, animations, and resizing behavior are possible,
especially in mixed LTR/RTL embedding scenarios. No auth or
data-handling logic changes.
>
> **Overview**
> Adds end-to-end RTL support by deriving a `dir` value from the active
translation/locale and propagating it through the UI.
>
> Introduces `useDirection()` (public) plus a shared `RTL_LANGUAGES`
set, sets `dir`/`lang` on the `.tl-container`, and updates Radix-based
primitives (menus, popovers, dialogs, selects, sliders, tooltips,
context menus) to use `dir={dir}` instead of hardcoded LTR, including
flipping submenu chevrons in RTL.
>
> Updates styling to be direction-aware via CSS logical properties and
targeted `.tl-container[dir='rtl']` overrides (e.g., sidebar/layout
positioning, toast animations/gradients, watermark placement, and dotcom
cookie/sidebar spacing), and reorders providers so tooltips can access
translation direction.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
5b11d76. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->1 parent 0666ccb commit 41fe118
31 files changed
Lines changed: 317 additions & 98 deletions
File tree
- apps/dotcom/client/src/tla
- components
- TlaSidebar
- dialogs
- layouts/TlaSidebarLayout
- providers
- styles
- packages
- editor/src/lib/license
- tldraw
- src
- lib
- ui
- components
- ContextMenu
- MainMenu
- ZoomMenu
- primitives
- context
- hooks/useTranslation
Lines changed: 31 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
18 | 25 | | |
19 | 26 | | |
20 | 27 | | |
| |||
59 | 66 | | |
60 | 67 | | |
61 | 68 | | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
62 | 78 | | |
63 | 79 | | |
64 | 80 | | |
| |||
73 | 89 | | |
74 | 90 | | |
75 | 91 | | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
76 | 101 | | |
77 | 102 | | |
78 | 103 | | |
| |||
261 | 286 | | |
262 | 287 | | |
263 | 288 | | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
264 | 294 | | |
265 | 295 | | |
266 | 296 | | |
| |||
550 | 580 | | |
551 | 581 | | |
552 | 582 | | |
553 | | - | |
| 583 | + | |
554 | 584 | | |
555 | 585 | | |
556 | 586 | | |
| |||
Lines changed: 10 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
50 | 50 | | |
51 | 51 | | |
52 | 52 | | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
53 | 58 | | |
54 | 59 | | |
55 | 60 | | |
| |||
153 | 158 | | |
154 | 159 | | |
155 | 160 | | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
156 | 166 | | |
157 | 167 | | |
158 | 168 | | |
| |||
Lines changed: 6 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
80 | 80 | | |
81 | 81 | | |
82 | 82 | | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
83 | 88 | | |
84 | 89 | | |
85 | | - | |
| 90 | + | |
86 | 91 | | |
87 | 92 | | |
88 | 93 | | |
| |||
Lines changed: 27 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
12 | 16 | | |
13 | 17 | | |
14 | 18 | | |
| |||
21 | 25 | | |
22 | 26 | | |
23 | 27 | | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
24 | 34 | | |
25 | 35 | | |
26 | 36 | | |
| |||
36 | 46 | | |
37 | 47 | | |
38 | 48 | | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
39 | 54 | | |
40 | 55 | | |
41 | 56 | | |
| |||
45 | 60 | | |
46 | 61 | | |
47 | 62 | | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
48 | 70 | | |
49 | 71 | | |
50 | 72 | | |
| |||
72 | 94 | | |
73 | 95 | | |
74 | 96 | | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| 13 | + | |
13 | 14 | | |
14 | 15 | | |
15 | 16 | | |
| |||
45 | 46 | | |
46 | 47 | | |
47 | 48 | | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
48 | 54 | | |
49 | 55 | | |
50 | 56 | | |
| |||
100 | 106 | | |
101 | 107 | | |
102 | 108 | | |
| 109 | + | |
103 | 110 | | |
104 | 111 | | |
105 | 112 | | |
106 | 113 | | |
| 114 | + | |
107 | 115 | | |
108 | 116 | | |
109 | 117 | | |
| |||
124 | 132 | | |
125 | 133 | | |
126 | 134 | | |
| 135 | + | |
127 | 136 | | |
128 | 137 | | |
129 | 138 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
243 | 243 | | |
244 | 244 | | |
245 | 245 | | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
246 | 250 | | |
247 | 251 | | |
248 | 252 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
191 | 191 | | |
192 | 192 | | |
193 | 193 | | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
194 | 204 | | |
195 | 205 | | |
196 | 206 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2773 | 2773 | | |
2774 | 2774 | | |
2775 | 2775 | | |
| 2776 | + | |
| 2777 | + | |
| 2778 | + | |
2776 | 2779 | | |
2777 | 2780 | | |
2778 | 2781 | | |
| |||
5566 | 5569 | | |
5567 | 5570 | | |
5568 | 5571 | | |
| 5572 | + | |
| 5573 | + | |
| 5574 | + | |
5569 | 5575 | | |
5570 | 5576 | | |
5571 | 5577 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
678 | 678 | | |
679 | 679 | | |
680 | 680 | | |
681 | | - | |
| 681 | + | |
682 | 682 | | |
683 | 683 | | |
| 684 | + | |
684 | 685 | | |
685 | 686 | | |
686 | 687 | | |
| |||
0 commit comments