Skip to content

[Feature]: Adopt logical Tailwind utilities so the renderer is bidi-ready from day one #1771

@AsimNet

Description

@AsimNet

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I am describing a concrete problem or use case, not just a vague idea.

Area

apps/web renderer styling (Tailwind v4 utility usage + bidi handling in message bodies).

Problem or use case

apps/desktop is an Electron app rendering apps/web (React 19 + Tailwind v4), so the renderer is just Chromium and already knows how to lay out bidirectional text correctly. What's blocking RTL today is two things in the web codebase:

  1. The current Tailwind usage is dominantly physical, not logical. A quick scan of apps/web/src shows utilities like text-left, ml-auto, -ml-0.5, pl-[90px], pl-2, pr-1.5, etc. used throughout components such as Sidebar.tsx and contextMenuFallback.ts. These never mirror, regardless of the document direction.
  2. There is no bidi handling in message content. I couldn't find any uses of unicode-bidi, plaintext, or isolate anywhere in apps/web/src. That means when an assistant message contains Arabic (or any RTL text), Chromium falls back to the default LTR base direction and inline LTR tokens — code spans, mentions like @user, tool tags like pint, file paths — get visually shuffled inside the Arabic sentence. This is exactly the bug visible when using t3code on Arabic projects today.

Retrofitting RTL later is painful: every spacing, alignment, border, and positioning utility has to be revisited. It's much cheaper to standardize on logical utilities now, while the surface area is still small, so the renderer is bidi-ready by construction instead of needing a migration sprint later.

Proposed solution

Adopt logical Tailwind utilities + browser-native bidi handling as the convention in apps/web:

  1. Prefer logical Tailwind utilities over physical ones in components:
    • ml-* / mr-*ms-* / me-*
    • pl-* / pr-*ps-* / pe-*
    • left-* / right-*start-* / end-*
    • border-l-* / border-r-*border-s-* / border-e-*
    • text-left / text-righttext-start / text-end
    • rounded-l-* / rounded-r-*rounded-s-* / rounded-e-*
    • Tailwind v4 supports all of these out of the box, and in an LTR context they render identically to the physical versions, so the diff is behavior-preserving.
  2. Let the browser handle bidi text inside message bodies:
    • Set dir=\"auto\" on assistant/user message content containers so Chromium picks the base direction from the first strong character per element. No JS detection needed.
    • Apply unicode-bidi: plaintext to message paragraphs (e.g. via a small utility class or directly on the markdown renderer's wrapper) so each line resolves independently. This is what fixes inline pint / @user / URLs getting shuffled inside Arabic sentences.
  3. Pin code blocks, diffs, and file paths to LTR with dir=\"ltr\" + unicode-bidi: isolate, so source code stays LTR even when embedded inside an RTL message.
  4. Keep the app chrome LTR by default. No global "RTL mode" toggle. Only message bodies that actually contain RTL content flip themselves, which matches how GitHub and VS Code handle it and avoids a disruptive feature.
  5. (Optional, but cheap) Add a lint rule that flags physical Tailwind utilities in component class lists, so the convention doesn't drift back. There's prior art in the Tailwind community for this via custom ESLint rules / eslint-plugin-tailwindcss.

Why this matters

  • Cheap now, expensive later. Logical utilities are a drop-in replacement with identical behavior in LTR contexts, so adopting them today costs almost nothing. Doing it after the UI has grown means touching every component.
  • Unblocks Arabic / Hebrew / Persian / Urdu users for free. Once message containers use dir=\"auto\" and the chrome uses logical utilities, the renderer just works for RTL content without a dedicated feature.
  • Fixes a real bug visible today, where Arabic assistant output renders with inline code tokens floating to the wrong side of words.
  • Aligns with the platform. Chromium, the CSS Logical Properties spec, and Tailwind v4 itself all encourage this — it's the conventional way to write Tailwind in 2026.

Smallest useful scope

A first PR that:

  1. Wraps the message content container (the markdown renderer's outer element) with dir=\"auto\" and applies unicode-bidi: plaintext to its paragraphs.
  2. Wraps code blocks / inline code in dir=\"ltr\"; unicode-bidi: isolate.
  3. Migrates a single high-traffic component (e.g. Sidebar.tsx or the chat message component) from physical to logical Tailwind utilities as a reference for the rest of the codebase.

That alone makes Arabic conversations readable. The remaining components can be migrated incrementally, and a lint rule can be turned on once the codebase is clean.

Alternatives considered

  • Retrofit RTL support later as a feature. Strictly worse: more code to touch, more regressions, and users are blocked in the meantime.
  • Global "RTL mode" toggle that flips the whole app. Breaks mixed-language workflows (most users want LTR chrome with correctly-rendered RTL message bodies) and doesn't fix inline bidi issues on its own.
  • Manual per-message direction detection in JS. Reinvents what the browser already does correctly via dir=\"auto\" and unicode-bidi.

Risks or tradeoffs

  • A small number of cases (drag handles, animations, transforms) may still need physical values; those can stay physical and be called out explicitly.
  • Need to make sure code blocks, diffs, tables, and file paths stay LTR even when nested inside an RTL message — handled by step 3.
  • The migration touches a lot of files mechanically, but the diff is mostly find-and-replace and behavior in LTR is identical, so review risk is low.

Examples or references

Contribution

  • I would be open to helping implement this.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions