
Company News
Jerod Santo Joins Socket as Head of Media
Allow myself to introduce... myself.
Find layout that breaks in right-to-left languages: physical CSS properties, directional Tailwind utilities, and missing dir attributes.
Find the layout that breaks when your app switches to Arabic, Hebrew, Persian, or Urdu — before a user does.
ml-4 always pushes away from the left edge. In an RTL locale that is the wrong edge, so the icon that sat neatly beside the search field jumps to the far side of it. These bugs never show up in an English screenshot, and there is nothing in your test suite that catches them.
rtl-lint reads your stylesheets and components and reports every place that is pinned to a physical side, with the logical replacement for each one.
npm install -D rtl-lint
Or run it once without installing:
npx rtl-lint src
rtl-lint src
src/screens/POS.tsx
60:51 error `right-3` is fixed to one side and will not flip in RTL. tailwind-directional
→ Use `end-3` (Tailwind 3.3+).
62:32 error `pr-10` is fixed to one side and will not flip in RTL. tailwind-directional
→ Use `pe-10` (Tailwind 3.3+).
src/components/Sidebar.css
14:3 error `margin-left` is fixed to one side and will not flip in RTL. css-physical-property
→ Use `margin-inline-start`.
3 errors, 0 warnings in 2 of 50 files
| Option | Effect |
|---|---|
--base-dir <ltr|rtl> | The document's base direction. Decides whether left maps to start or to end. Default ltr |
--fix | Rewrite what can be repaired automatically |
--dry-run | With --fix, print the diff instead of writing |
--json | Machine-readable output for CI or an editor integration |
--strict | Exit non-zero on warnings too, not just errors |
--ignore <name> | Skip a directory by name; repeatable |
--no-color | Plain output (also honours NO_COLOR) |
Exit code is 1 when errors are found, 0 when clean, 2 on bad usage — so it drops straight into CI:
- run: npx rtl-lint src
| Rule | Severity | Catches | Suggests |
|---|---|---|---|
css-physical-property | error | margin-left, padding-right, border-left-width, border-top-left-radius, left, right, and the scroll-* variants — in CSS, SCSS, Less, and camelCased in CSS-in-JS | margin-inline-start, padding-inline-end, inset-inline-start, … |
css-physical-value | error | text-align: left, float: right, clear: left | text-align: start, float: inline-end, … |
tailwind-directional | error | ml-* mr-* pl-* pr-* left-* right-* text-left text-right float-left rounded-l-* border-r-* scroll-ml-* and friends, including variants (md:hover:-ml-2) and !important | ms-*, me-*, ps-*, pe-*, start-*, end-*, text-start, … |
missing-dir | error | <html> with no dir attribute | Set dir from the active locale |
hardcoded-dir | warning | dir="ltr" written into the markup | Derive it from the locale |
directional-transform | warning | rotate(90deg), skewX(…) — chevrons and arrows built this way keep pointing the same way in RTL | Check by eye; mirror under [dir='rtl'] if it conveys direction |
Comments are masked before any rule runs, so a property inside /* … */ is never reported — and line numbers still match the original file exactly.
Tailwind's logical utilities (ms-*, text-start, rounded-s-*) need Tailwind CSS 3.3 or newer.
--base-dir rtlWhich logical side a physical one maps to depends on the base direction of the document:
base ltr | base rtl | |
|---|---|---|
left | start | end |
right | end | start |
In an English-first app, text-right means "the end side" and becomes text-end. In an app whose root is <html dir="rtl">, the same class means "the natural reading side" and becomes text-start. Take the default and you mirror a working layout — worse than leaving it alone.
rtl-lint src --base-dir rtl --fix --dry-run
ltr:left-3 rtl:right-3 is a correct, deliberate pair: you have already told the browser what to do in each direction. Nothing with an ltr: or rtl: variant is reported or rewritten.
rtl-lint src --fix --dry-run # show the diff
rtl-lint src --fix # write it
Always look at the diff first. Physical is sometimes deliberate — a logo pinned to the left of a bilingual header is meant to stay there — and no linter can tell that apart from a mistake.
Only mechanical findings are rewritten. A missing dir, a hardcoded dir="ltr", a rotated chevron: those are reported and left alone, because guessing at intent is how an autofixer loses people's trust.
Fixes replace one token at a time. Whitespace, quoting and line count come out exactly as the author wrote them, and running --fix twice changes nothing the second time.
Put one of these in a comment, in whatever syntax the file already uses:
/* rtl-lint-disable-next-line */
.logo { margin-left: 16px; }
| Directive | Scope |
|---|---|
rtl-lint-disable-file | the whole file |
rtl-lint-disable-line | the line the comment is on |
rtl-lint-disable-next-line | the line after |
Being honest about the edges, because a linter you cannot trust is worse than none:
class/className attributes, including JSX expressions and clsx(...) calls. Classes assembled at runtime from variables are invisible to it.[&:hover]:ml-4 are skipped rather than half-parsed.import { lintFiles, fixFiles, lintSource } from "rtl-lint";
const { findings, errors, warnings } = await lintFiles("src");
// Repair in place; pass { dryRun: true } to get diffs back instead.
const { fixed, remaining } = await fixFiles("src", { baseDir: "rtl" });
// Or lint a string directly — the filename only picks which rules apply.
const issues = lintSource('<div class="ml-4">', "Button.tsx");
Each finding is { rule, severity, line, column, message, suggestion }, plus file when it came from lintFiles, and fix: { start, end, text } when it can be repaired automatically.
Node.js 18.18 or newer.
MIT © Khalel Hawary
FAQs
Find layout that breaks in right-to-left languages: physical CSS properties, directional Tailwind utilities, and missing dir attributes.
The npm package rtl-lint receives a total of 11 weekly downloads. As such, rtl-lint popularity was classified as not popular.
We found that rtl-lint demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Company News
Allow myself to introduce... myself.

Research
/Security News
A Twitch browser extension on Chrome and Firefox forwards users’ live OAuth session tokens through proxies controlled by a Russian bot service.

Security News
Anthropic found biased reasoning and recklessness drove Claude Mythos 5 to publish malware on PyPI and compromise a security vendor.