CSS and Layout skill 10 of 12

CSS Logical Properties SKILL.md: Fix RTL Layouts and Physical CSS

The CSS Logical Properties skill makes Claude, Cursor or another AI agent find physical-direction CSS such as margin-left, padding-right, left: 0 and Tailwind ml-4, and convert it so layouts mirror in right-to-left languages. A scanner reports each case by file and line, the agent edits only flagged lines, and you approve each batch.

css-logical-properties/SKILL.md, version 1.0.0, 2,599 words, plus scripts/logical_check.py, scripts/rtl_shots.py and three reference files.

Free to use and adapt with credit to [Afeez Adebayo](https://afeezadebayo.com) ([LinkedIn](https://www.linkedin.com/in/afeez-adebayo/)). Download: https://afeezadebayo.com/wp-content/uploads/skills-md/frontend-development/css-layout/css-logical-properties/SKILL.md

In this skill

  1. When to use it
  2. Guardrails
  3. Inputs
  4. Workflow (steps 1 to 9)
  5. Worked example
  6. Templates
  7. Failure modes
  8. Quality bar
  9. Cost
  10. Handoffs, appendix, sources

1. When to use it

Use it when a stylesheet, component library or Tailwind codebase must work in right-to-left languages (Arabic, Hebrew, Persian, Urdu) or in vertical writing modes, and the code has margin-left, padding-right, left: 0, text-align: right, float: left, ml-4 or pr-2 in it. Use it to review AI-generated CSS before it ships to an RTL market.

Do not use it for: translating strings, choosing fonts, redesigning icons, number and date formatting, converting width/height/min-*/max-* or media-query widths (they stay physical unless the user asks), or deciding whether a market needs RTL. Hand those back to the user.

Raw SKILL.md file

.md

This is the exact file agents read, including the credit header and licence notice. Save it as css-logical-properties/SKILL.md.

---
name: css-logical-properties
description: "Audits and fixes physical-direction CSS so layouts mirror in right-to-left languages and vertical writing modes. Runs a scanner, then swaps margin-left, padding-right, left, text-align and Tailwind ml-/pr- for logical forms in approved batches, and reviews RTL overrides, transforms and shadows by hand. Use for 'RTL support', 'Arabic layout', 'margin-inline-start', 'logical properties', 'mirror layout'. Do NOT use for translation, font choice, icon redesign or width/height conversion."
license: CC-BY-4.0
metadata:
  author: Afeez Adebayo
  author_url: https://afeezadebayo.com
  author_linkedin: https://www.linkedin.com/in/afeez-adebayo/
  author_x: https://x.com/za_icon
  source: https://afeezadebayo.com/skills-md/frontend-development/css-layout/css-logical-properties/
  category: frontend-development
  subcategory: css-layout
  version: "1.0.0"
  created: "2026-11-07"
  updated: "2026-11-07"
---

# CSS Logical Properties

Free to use and adapt with credit to [Afeez Adebayo](https://afeezadebayo.com) ([LinkedIn](https://www.linkedin.com/in/afeez-adebayo/)). Download: https://afeezadebayo.com/wp-content/uploads/skills-md/frontend-development/css-layout/css-logical-properties/SKILL.md

## In this skill

1. When to use it
2. Guardrails
3. Inputs
4. Workflow (steps 1 to 9)
5. Worked example
6. Templates
7. Failure modes
8. Quality bar
9. Cost
10. Handoffs, appendix, sources

## 1. When to use it

Use it when a stylesheet, component library or Tailwind codebase must work in right-to-left languages (Arabic, Hebrew, Persian, Urdu) or in vertical writing modes, and the code has `margin-left`, `padding-right`, `left: 0`, `text-align: right`, `float: left`, `ml-4` or `pr-2` in it. Use it to review AI-generated CSS before it ships to an RTL market.

Do not use it for: translating strings, choosing fonts, redesigning icons, number and date formatting, converting `width`/`height`/`min-*`/`max-*` or media-query widths (they stay physical unless the user asks), or deciding whether a market needs RTL. Hand those back to the user.

## 2. Guardrails

- Edits are bulk edits. Work on a branch, write nothing until the user approves a batch, and never run `--fix` on `main`. Default to a report.
- Never auto-convert a declaration inside an RTL override block (codes `O`). Those values are deliberately mirrored. Converting them flips them back.
- Never choose a browser support floor. Ask for it (Input 3). Support figures change; verify on MDN or caniuse before relying on any version number. This skill carries none.
- Data touched: source files only, matched by extension (`.css .scss .sass .less .html .vue .svelte .astro .php .js .jsx .ts .tsx .mdx`). The scanner prints file, line, code and the declaration it matched, never other source text. Do not paste whole files, `.env` files, tokens or customer text into the prompt. Skip `node_modules`, build output and minified files (the scanner already does).
- RTL quality is not provable from code. Layout parity can be checked by script and screenshot. Wording, numerals and bidirectional text need a native reader. Say so in the report; this is not a localisation sign-off or a legal or accessibility conformance statement.
- Edit only flagged lines. Do not reformat, reorder or rewrite whole files, and do not echo unchanged code back.

## 3. Inputs

Ask for anything missing, in one message. Do not assume.

| # | Input | Ask |
|---|---|---|
| 1 | Paths to scan | "Which folders hold the CSS, components and templates?" |
| 2 | Scope | "RTL only, or vertical writing modes too?" Vertical too means pass `--block`. |
| 3 | Browser support floor | "Which browsers and versions must work?" If a stated browser may lack a property used here, stop and tell the user to check MDN or caniuse. Do not guess a version. |
| 4 | How direction is set | "Is `dir` on `<html>`, per component, or not set?" |
| 5 | Stack | "Plain CSS, Sass, CSS-in-JS, Tailwind, React Native?" Read `references/frameworks.md` only for Tailwind, CSS-in-JS or React Native. |
| 6 | Edit permission | "Branch name, and do you approve edits in batches of up to 40?" |
| 7 | A way to see it | "Dev server URL or screenshot tooling?" If none, mark every visual check untested. |

## 4. Workflow

Run the script from the skill folder. Read its output only. Do not read the script or redo its checks by hand.

### Step 1: summary scan

```
python3 scripts/logical_check.py <paths> --summary
```

Exit 0: report "no physical-direction CSS found" plus any `I` lines, and stop. Exit 1: continue. Exit 2: fix the arguments.

Codes:

| Code | Meaning | Fixed by |
|---|---|---|
| A01 A02 A03 A04 | Longhand or value swap (`margin-left`, `text-align: left`, `marginLeft`) | `--fix`, step 4 |
| T01 | Tailwind class swap (`ml-4` to `ms-4`) | `--fix`, step 4 |
| M01 M02 M03 | Four-value shorthand with different left and right | By hand, step 5 |
| R01 to R05 | Physical-only: transform, background position, gradient, shadow, origin | By hand, step 6 |
| O01 O02 | Same swaps, but inside an RTL override | By hand, step 3 |
| I01 to I04 | Information: `direction` in CSS, RTL selectors, react-native file, `<html>` without `dir` | Step 7 |

### Step 2: choose batches

Read the totals line. Use these defaults (they are this skill's defaults, not a standard):

| Auto (A+T) count | Plan |
|---|---|
| 1 to 40 | One batch |
| 41 to 300 | One batch per directory, at most 40 edits each |
| Over 300 | Stop. Tell the user the count and ask whether to proceed by directory or by component |

Before any edit, capture LTR baseline screenshots (step 8) so the fix can be proved a no-op in LTR. If you cannot, say that parity is untested.

### Step 3: review RTL overrides first

Run `python3 scripts/logical_check.py <paths> --max 0` and read only `O` and `I02` lines. For each override block:

- If it only swaps the left and right of the base rule (the base has `margin-left: 16px`, the override has `margin-left: 0; margin-right: 16px`): the base rule will become `margin-inline-start: 16px`, so delete the override block.
- If it differs for any other reason (a different size, a different component state): keep it, leave its physical values untouched, and note it in the report.
- Never edit an override before its base rule is converted, or you cannot tell which is which.

### Step 4: apply the auto swaps

After approval for the batch:

```
python3 scripts/logical_check.py <batch-path> --fix
git diff --stat
```

The swap keeps declaration order, so cascade behaviour is unchanged. The W3C module says a logical property shares its computed value with the physical counterpart, and cascade order decides which wins. Check that the number of edits written matches the A+T count for that path. A mismatch means a file changed under you: stop and re-scan.

### Step 5: manual shorthands (M codes)

| Before | After |
|---|---|
| `margin: T R B L` (R differs from L) | `margin-block: T B; margin-inline: L R` |
| `padding: T R B L` | `padding-block: T B; padding-inline: L R` |
| `border-radius: TL TR BR BL` (left and right differ) | `border-start-start-radius: TL; border-start-end-radius: TR; border-end-end-radius: BR; border-end-start-radius: BL` |

Two and three value shorthands with a symmetric inline pair are already direction-neutral. The scanner does not flag them. Leave them.

### Step 6: physical-only values (R codes)

No logical property exists for these. The W3C Logical module defines flow-relative versions of box properties, not of transforms, shadows or images. Decide per code:

| Code | Decision rule |
|---|---|
| R01 `translateX`, `translate`, `rotate`, `scaleX`, `skewX` | If it moves an element sideways, multiply by a direction variable: `--flip: 1` on `:root`, `--flip: -1` on `[dir="rtl"]`, then `translateX(calc(var(--flip) * 6px))`. If it points an arrow, flip with `[dir="rtl"] .icon { transform: scaleX(-1); }` and only for directional icons. |
| R02 `background-position`, `object-position`, `mask-position` | No logical form (Ishadeed, 2021). Keep the physical value and add one `[dir="rtl"]` override, or use a mirrored asset. |
| R03 gradient `to left` or `to right` | Add a `[dir="rtl"]` override with the opposite keyword, unless the gradient is symmetric. |
| R04 `box-shadow` with a non-zero x offset | Override with the negated x offset, or use `calc(var(--flip) * 4px)`. |
| R05 `transform-origin`, `perspective-origin` with `left` or `right` | Override under `[dir="rtl"]`. |

Put all new overrides in one place per stylesheet. Each one needs a reason in the report.

### Step 7: markup and information lines

- `I04`: add `dir` to `<html>` only if the user's answer to Input 4 says it is missing. Use the language's direction (`rtl` for Arabic, Hebrew, Persian, Urdu). Keep `lang` as it is.
- `I01`: if CSS sets `direction`, propose moving it to a `dir` attribute. Do not move it without approval.
- `I02`: once steps 3 to 6 are done, count the remaining `[dir="rtl"]` selectors. Each must be justified in the report or removed.
- `I03`: react-native files use `marginStart` and `paddingEnd`, not web logical names. Read `references/frameworks.md`.
- Mixed-script text that is user-generated (names, usernames) goes in `<bdi>` or on an element with `dir="auto"`. Directional icons flip, non-directional icons (search, home, settings) stay (rule set in the skillverse RTL rules).

### Step 8: verify

1. Re-run `python3 scripts/logical_check.py <paths> --summary`. Expect no A or T codes. Every remaining M, R, O line must be in the report with an action or a reason.
2. LTR parity: screenshots after the fix must match the baseline at three widths (default 360, 768, 1280). A difference means a swap changed behaviour: revert that file and investigate.
3. RTL check: load the page with `dir="rtl"` on `<html>`, same widths, and look for: sidebars and drawers on the wrong side, arrows, progress bars and sliders running the wrong way, shadows, hover slide-ins, and text clipped on the wrong edge. With Playwright for Python installed, run `python3 scripts/rtl_shots.py <url> <outdir> --dir both`. Read `references/verify.md` only if you have a browser tool.
4. Anything not viewed is listed as untested. Do not write "RTL supported".

### Step 9: report

Use the report template (Templates). One row per file and code, not per line, unless the user asks.

## 5. Worked example

Input: an invented `card.css`, 10 rules (full file in `references/verify.md`). Scope RTL only. Floor confirmed by the user. Scan:

```
$ python3 scripts/logical_check.py card.css --summary
card.css A01x6 A02x2 I01x1 I02x1 M01x1 M03x1 O01x2 O02x1 R01x1 R02x1 R03x1 R04x1
1 files scanned: 8 auto, 2 manual, 4 physical-only, 3 rtl-override, 2 info
```

Step 3: the `[dir="rtl"] .card` block (`margin-left: 0; margin-right: 16px; text-align: right`) only mirrors the base rule, so it is deleted. Step 4: `--fix` writes 8 edits (`margin-left` to `margin-inline-start`, `padding-right` to `padding-inline-end`, `border-left` to `border-inline-start`, `left: 0` to `inset-inline-start: 0`, `text-align: left` to `start`, `float: left` to `inline-start`, and two more). Step 5: `margin: 4px 8px 4px 0` becomes `margin-block: 4px; margin-inline: 0 8px`. The radius `8px 0 0 8px` becomes `border-start-start-radius: 8px; border-end-start-radius: 8px`. Step 6: `translateX(4px)` becomes `calc(var(--flip) * 4px)`; the gradient and the 4px shadow get one RTL override each; `background-position: 8px center` gets an override. Step 8: after steps 3 and 4 alone, the re-run prints `0 auto, 2 manual, 4 physical-only` and the LTR screenshots at 360, 768 and 1280 are byte-identical to the baseline (measured on this file). Steps 5 and 6 are then applied by hand and checked the same way. The RTL view stays untested unless someone looks at it.

## 6. Templates

Starting prompt:

```
Use the css-logical-properties skill on <paths>. Scope: <RTL only | RTL and vertical>. Browser floor: <floor>. Work on branch <name>. Run the scanner first and show me the summary. Do not edit until I approve a batch.
```

Report:

```
Scanned: <n> files. Before: <A+T> auto, <M> manual, <R> physical-only, <O> overrides.
After:   <A+T> auto, <M> manual, <R> physical-only, <O> overrides.
| File | Code | Action | Reason if left |
LTR parity: <matched at 360/768/1280 | untested>
RTL view:   <checked at 360/768/1280 | untested>
Needs a human: wording, numerals and bidirectional text with a native reader.
```

Direction variable:

```css
:root { --flip: 1; }
[dir="rtl"] { --flip: -1; }
.slide-in:hover { transform: translateX(calc(var(--flip) * 4px)); }
```

## 7. Failure modes

| Failure | How to spot it | Do instead |
|---|---|---|
| Converting inside RTL overrides | `O` codes; after the fix, an RTL page has margins on the wrong side | Step 3: delete mirror-only overrides, never convert them |
| Treating logical properties as complete | Arrows, shadows, slide-ins and gradients point the wrong way in RTL | Step 6: the R codes need a decision each |
| Converting size and media queries | A diff full of `inline-size` and no visible gain | Leave `width`, `height`, `min-*`, `max-*` and `@media (min-width)` alone unless asked; pass `--block` only for vertical scope |
| Flipping every icon | Search and settings icons mirrored | Flip directional icons only (step 7) |
| Declaring success from one direction | Only LTR screenshots exist | Step 8 parity and RTL views, or write "untested" |

## 8. Quality bar

Run each check before handing over.

1. Step 8.1 re-run shows no A or T codes.
2. Every M, R and O line from step 1 appears in the report with an action or reason.
3. `git diff --stat` shows only files the scanner flagged, and edit counts match.
4. No `width`, `height`, `min-*`, `max-*` or media-query change unless the user asked.
5. Step 8.2 parity result is stated as matched or untested.
6. Step 8.3 RTL result is stated as checked or untested.
7. The browser floor in the report is the user's, not yours.
8. Edits sit on a named branch and each batch was approved.

## 9. Cost

All figures are estimates (words x 1.35 for prose, characters / 3.5 for code), not from a model tokeniser.

| Item | Tokens | Read when |
|---|---|---|
| Description | 88 | Every session with the skill installed |
| This body | 3,611 | When the skill fires |
| `references/frameworks.md` | 647 | Tailwind, CSS-in-JS or React Native only |
| `references/verify.md` | 508 | Step 8 with a browser tool only |
| `references/appendix.md` | 1,152 | Support notes, tool notes or sources only |
| Typical run | 4,149 | Description, body, 25 lines of output |
| Worst case | 7,326 | All three references, 60 lines of output |

Cheaper path: run `--summary`, approve one batch, `--fix`, re-run, skip every reference. On a 220-rule test stylesheet (18,256 characters, about 5,200 tokens to read) the summary prints one line of about 40 tokens. That is a measured fixture comparison, not a saving on your code.

## 10. Handoffs, appendix, sources

Handoffs: [css-nesting](https://afeezadebayo.com/skills-md/frontend-development/css-layout/css-nesting/) for rewriting the converted rules, [flexbox-grid-layout](https://afeezadebayo.com/skills-md/frontend-development/css-layout/flexbox-grid-layout/) when a layout still breaks in RTL, [localisation-translation](https://afeezadebayo.com/skills-md/marketing/international-seo/localisation-translation/) for the wording.

Tool appendix (outside the tool-agnostic core): `references/frameworks.md` for Tailwind, CSS-in-JS and React Native, and `references/appendix.md` for tool notes, source notes and the rival comparison.

Sources (last verified 7 November 2026):

- W3C, CSS Logical Properties and Values Level 1, Working Draft 4 December 2025: https://www.w3.org/TR/css-logical-1/
- Ahmad Shadeed, CSS Logical Properties, 10 March 2021: https://ishadeed.com/article/css-logical-properties/
- Anthropic, Agent Skills best practices (body under 500 lines, 1,024 character description): https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices
- Anthropic, Equipping agents for the real world with Agent Skills: https://www.anthropic.com/research/equipping-agents-for-the-real-world-with-agent-skills
- Front-End Checklist, logical-properties skill: https://github.com/thedaviddias/Front-End-Checklist
- skillverse, RTL development rules: https://skillverse.pages.dev/skills/rtl-right-to-left-i18n
- skills.sh, rtl-css: https://skills.sh/dvorkinguy/claude-skills-agents/rtl-css

---
Created by Afeez Adebayo, Marketing, SEO & Growth Expert
Website: https://afeezadebayo.com
LinkedIn: https://www.linkedin.com/in/afeez-adebayo/
X: https://x.com/za_icon
Source: https://afeezadebayo.com/skills-md/frontend-development/css-layout/css-logical-properties/

Licence: CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/). You may copy, share and adapt this skill, including for commercial use, as long as you credit Afeez Adebayo and link to the source URL above. Keep this notice in any copy or adapted version.

2. Guardrails

  • Edits are bulk edits. Work on a branch, write nothing until the user approves a batch, and never run --fix on main. Default to a report.
  • Never auto-convert a declaration inside an RTL override block (codes O). Those values are deliberately mirrored. Converting them flips them back.
  • Never choose a browser support floor. Ask for it (Input 3). Support figures change; verify on MDN or caniuse before relying on any version number. This skill carries none.
  • Data touched: source files only, matched by extension (.css .scss .sass .less .html .vue .svelte .astro .php .js .jsx .ts .tsx .mdx). The scanner prints file, line, code and the declaration it matched, never other source text. Do not paste whole files, .env files, tokens or customer text into the prompt. Skip node_modules, build output and minified files (the scanner already does).
  • RTL quality is not provable from code. Layout parity can be checked by script and screenshot. Wording, numerals and bidirectional text need a native reader. Say so in the report; this is not a localisation sign-off or a legal or accessibility conformance statement.
  • Edit only flagged lines. Do not reformat, reorder or rewrite whole files, and do not echo unchanged code back.

3. Inputs

Ask for anything missing, in one message. Do not assume.

#InputAsk
1Paths to scan"Which folders hold the CSS, components and templates?"
2Scope"RTL only, or vertical writing modes too?" Vertical too means pass --block.
3Browser support floor"Which browsers and versions must work?" If a stated browser may lack a property used here, stop and tell the user to check MDN or caniuse. Do not guess a version.
4How direction is set"Is dir on <html>, per component, or not set?"
5Stack"Plain CSS, Sass, CSS-in-JS, Tailwind, React Native?" Read references/frameworks.md only for Tailwind, CSS-in-JS or React Native.
6Edit permission"Branch name, and do you approve edits in batches of up to 40?"
7A way to see it"Dev server URL or screenshot tooling?" If none, mark every visual check untested.

4. Workflow

Run the script from the skill folder. Read its output only. Do not read the script or redo its checks by hand.

Step 1: summary scan

python3 scripts/logical_check.py <paths> --summary

Exit 0: report "no physical-direction CSS found" plus any I lines, and stop. Exit 1: continue. Exit 2: fix the arguments.

Codes:

CodeMeaningFixed by
A01 A02 A03 A04Longhand or value swap (margin-left, text-align: left, marginLeft)--fix, step 4
T01Tailwind class swap (ml-4 to ms-4)--fix, step 4
M01 M02 M03Four-value shorthand with different left and rightBy hand, step 5
R01 to R05Physical-only: transform, background position, gradient, shadow, originBy hand, step 6
O01 O02Same swaps, but inside an RTL overrideBy hand, step 3
I01 to I04Information: direction in CSS, RTL selectors, react-native file, <html> without dirStep 7

Step 2: choose batches

Read the totals line. Use these defaults (they are this skill's defaults, not a standard):

Auto (A+T) countPlan
1 to 40One batch
41 to 300One batch per directory, at most 40 edits each
Over 300Stop. Tell the user the count and ask whether to proceed by directory or by component

Before any edit, capture LTR baseline screenshots (step 8) so the fix can be proved a no-op in LTR. If you cannot, say that parity is untested.

Step 3: review RTL overrides first

Run python3 scripts/logical_check.py <paths> --max 0 and read only O and I02 lines. For each override block:

  • If it only swaps the left and right of the base rule (the base has margin-left: 16px, the override has margin-left: 0; margin-right: 16px): the base rule will become margin-inline-start: 16px, so delete the override block.
  • If it differs for any other reason (a different size, a different component state): keep it, leave its physical values untouched, and note it in the report.
  • Never edit an override before its base rule is converted, or you cannot tell which is which.

Step 4: apply the auto swaps

After approval for the batch:

python3 scripts/logical_check.py <batch-path> --fix
git diff --stat

The swap keeps declaration order, so cascade behaviour is unchanged. The W3C module says a logical property shares its computed value with the physical counterpart, and cascade order decides which wins. Check that the number of edits written matches the A+T count for that path. A mismatch means a file changed under you: stop and re-scan.

Step 5: manual shorthands (M codes)

BeforeAfter
margin: T R B L (R differs from L)margin-block: T B; margin-inline: L R
padding: T R B Lpadding-block: T B; padding-inline: L R
border-radius: TL TR BR BL (left and right differ)border-start-start-radius: TL; border-start-end-radius: TR; border-end-end-radius: BR; border-end-start-radius: BL

Two and three value shorthands with a symmetric inline pair are already direction-neutral. The scanner does not flag them. Leave them.

Step 6: physical-only values (R codes)

No logical property exists for these. The W3C Logical module defines flow-relative versions of box properties, not of transforms, shadows or images. Decide per code:

CodeDecision rule
R01 translateX, translate, rotate, scaleX, skewXIf it moves an element sideways, multiply by a direction variable: --flip: 1 on :root, --flip: -1 on [dir="rtl"], then translateX(calc(var(--flip) * 6px)). If it points an arrow, flip with [dir="rtl"] .icon { transform: scaleX(-1); } and only for directional icons.
R02 background-position, object-position, mask-positionNo logical form (Ishadeed, 2021). Keep the physical value and add one [dir="rtl"] override, or use a mirrored asset.
R03 gradient to left or to rightAdd a [dir="rtl"] override with the opposite keyword, unless the gradient is symmetric.
R04 box-shadow with a non-zero x offsetOverride with the negated x offset, or use calc(var(--flip) * 4px).
R05 transform-origin, perspective-origin with left or rightOverride under [dir="rtl"].

Put all new overrides in one place per stylesheet. Each one needs a reason in the report.

Step 7: markup and information lines

  • I04: add dir to <html> only if the user's answer to Input 4 says it is missing. Use the language's direction (rtl for Arabic, Hebrew, Persian, Urdu). Keep lang as it is.
  • I01: if CSS sets direction, propose moving it to a dir attribute. Do not move it without approval.
  • I02: once steps 3 to 6 are done, count the remaining [dir="rtl"] selectors. Each must be justified in the report or removed.
  • I03: react-native files use marginStart and paddingEnd, not web logical names. Read references/frameworks.md.
  • Mixed-script text that is user-generated (names, usernames) goes in <bdi> or on an element with dir="auto". Directional icons flip, non-directional icons (search, home, settings) stay (rule set in the skillverse RTL rules).

Step 8: verify

  1. Re-run python3 scripts/logical_check.py <paths> --summary. Expect no A or T codes. Every remaining M, R, O line must be in the report with an action or a reason.
  2. LTR parity: screenshots after the fix must match the baseline at three widths (default 360, 768, 1280). A difference means a swap changed behaviour: revert that file and investigate.
  3. RTL check: load the page with dir="rtl" on <html>, same widths, and look for: sidebars and drawers on the wrong side, arrows, progress bars and sliders running the wrong way, shadows, hover slide-ins, and text clipped on the wrong edge. With Playwright for Python installed, run python3 scripts/rtl_shots.py <url> <outdir> --dir both. Read references/verify.md only if you have a browser tool.
  4. Anything not viewed is listed as untested. Do not write "RTL supported".

Step 9: report

Use the report template (Templates). One row per file and code, not per line, unless the user asks.

5. Worked example

Input: an invented card.css, 10 rules (full file in references/verify.md). Scope RTL only. Floor confirmed by the user. Scan:

$ python3 scripts/logical_check.py card.css --summary
card.css A01x6 A02x2 I01x1 I02x1 M01x1 M03x1 O01x2 O02x1 R01x1 R02x1 R03x1 R04x1
1 files scanned: 8 auto, 2 manual, 4 physical-only, 3 rtl-override, 2 info

Step 3: the [dir="rtl"] .card block (margin-left: 0; margin-right: 16px; text-align: right) only mirrors the base rule, so it is deleted. Step 4: --fix writes 8 edits (margin-left to margin-inline-start, padding-right to padding-inline-end, border-left to border-inline-start, left: 0 to inset-inline-start: 0, text-align: left to start, float: left to inline-start, and two more). Step 5: margin: 4px 8px 4px 0 becomes margin-block: 4px; margin-inline: 0 8px. The radius 8px 0 0 8px becomes border-start-start-radius: 8px; border-end-start-radius: 8px. Step 6: translateX(4px) becomes calc(var(--flip) * 4px); the gradient and the 4px shadow get one RTL override each; background-position: 8px center gets an override. Step 8: after steps 3 and 4 alone, the re-run prints 0 auto, 2 manual, 4 physical-only and the LTR screenshots at 360, 768 and 1280 are byte-identical to the baseline (measured on this file). Steps 5 and 6 are then applied by hand and checked the same way. The RTL view stays untested unless someone looks at it.

6. Templates

Starting prompt:

Use the css-logical-properties skill on <paths>. Scope: <RTL only | RTL and vertical>. Browser floor: <floor>. Work on branch <name>. Run the scanner first and show me the summary. Do not edit until I approve a batch.

Report:

Scanned: <n> files. Before: <A+T> auto, <M> manual, <R> physical-only, <O> overrides.
After:   <A+T> auto, <M> manual, <R> physical-only, <O> overrides.
| File | Code | Action | Reason if left |
LTR parity: <matched at 360/768/1280 | untested>
RTL view:   <checked at 360/768/1280 | untested>
Needs a human: wording, numerals and bidirectional text with a native reader.

Direction variable:

:root { --flip: 1; }
[dir="rtl"] { --flip: -1; }
.slide-in:hover { transform: translateX(calc(var(--flip) * 4px)); }

7. Failure modes

FailureHow to spot itDo instead
Converting inside RTL overridesO codes; after the fix, an RTL page has margins on the wrong sideStep 3: delete mirror-only overrides, never convert them
Treating logical properties as completeArrows, shadows, slide-ins and gradients point the wrong way in RTLStep 6: the R codes need a decision each
Converting size and media queriesA diff full of inline-size and no visible gainLeave width, height, min-*, max-* and @media (min-width) alone unless asked; pass --block only for vertical scope
Flipping every iconSearch and settings icons mirroredFlip directional icons only (step 7)
Declaring success from one directionOnly LTR screenshots existStep 8 parity and RTL views, or write "untested"

8. Quality bar

Run each check before handing over.

  1. Step 8.1 re-run shows no A or T codes.
  2. Every M, R and O line from step 1 appears in the report with an action or reason.
  3. git diff --stat shows only files the scanner flagged, and edit counts match.
  4. No width, height, min-*, max-* or media-query change unless the user asked.
  5. Step 8.2 parity result is stated as matched or untested.
  6. Step 8.3 RTL result is stated as checked or untested.
  7. The browser floor in the report is the user's, not yours.
  8. Edits sit on a named branch and each batch was approved.

9. Cost

All figures are estimates (words x 1.35 for prose, characters / 3.5 for code), not from a model tokeniser.

ItemTokensRead when
Description88Every session with the skill installed
This body3,611When the skill fires
references/frameworks.md647Tailwind, CSS-in-JS or React Native only
references/verify.md508Step 8 with a browser tool only
references/appendix.md1,152Support notes, tool notes or sources only
Typical run4,149Description, body, 25 lines of output
Worst case7,326All three references, 60 lines of output

Cheaper path: run --summary, approve one batch, --fix, re-run, skip every reference. On a 220-rule test stylesheet (18,256 characters, about 5,200 tokens to read) the summary prints one line of about 40 tokens. That is a measured fixture comparison, not a saving on your code.

10. Handoffs, appendix, sources

Handoffs: [css-nesting](https://afeezadebayo.com/skills-md/frontend-development/css-layout/css-nesting/) for rewriting the converted rules, [flexbox-grid-layout](https://afeezadebayo.com/skills-md/frontend-development/css-layout/flexbox-grid-layout/) when a layout still breaks in RTL, [localisation-translation](https://afeezadebayo.com/skills-md/marketing/international-seo/localisation-translation/) for the wording.

Tool appendix (outside the tool-agnostic core): references/frameworks.md for Tailwind, CSS-in-JS and React Native, and references/appendix.md for tool notes, source notes and the rival comparison.

Sources (last verified 7 November 2026):

What this skill does

A stylesheet written for English hard-codes the left and the right. In Arabic, Hebrew, Persian and Urdu the page reads from the right, so a margin-left that pushed text away from an icon now pushes it the wrong way. Most guides say to replace every physical property with a logical one. That advice breaks two things: an [dir=rtl] override that already mirrors a value gets mirrored back, and a translateX, a shadow or a gradient has no logical form at all.

The skill runs scripts/logical_check.py, which reports file, line and code for 19 patterns. The agent applies the safe swaps with --fix on a branch, in batches you approve, then works through the rest by decision rule: four-value shorthands, RTL overrides, and values that need a direction variable or one override. It proves the left-to-right result is unchanged with screenshots and lists anything it could not view as untested. It asks you for the browser support floor instead of choosing one.

Who it is for

Use it if you are adding Arabic, Hebrew, Persian or Urdu to a product, inheriting a stylesheet full of margin-left and ml-4, or reviewing AI-generated CSS before it ships to an RTL market. It does not translate text, pick fonts, redraw icons or convert width and height, and it is not a localisation sign-off. A native reader must check wording, numerals and mixed-script text.

What is inside

The file is 2,599 words (version 1.0.0) with two scripts and three reference files. It holds:

  • Six guardrails, including edits on a branch only, no auto-conversion inside RTL overrides and no support floor chosen by the agent.
  • Seven inputs the agent must have first, including scope (RTL only or vertical writing modes) and how direction is set.
  • Nine workflow steps: scan, batches, RTL overrides, auto swaps, shorthands, physical-only values, markup, verify and report.
  • One scanner, scripts/logical_check.py, standard library only, with 19 codes. It prints file, line and a fixed message, and with --fix it rewrites only the safe swaps.
  • A worked example with real scanner output, a starting prompt and report template, five failure modes and an eight-item quality bar.
ItemTokensStatusRead when
Description88EstimateEvery session with the skill installed
SKILL.md body3,611EstimateWhen the skill fires
references/frameworks.md647EstimateOnly for Tailwind, CSS-in-JS or React Native
references/verify.md508EstimateStep 8, only with a browser tool
references/appendix.md1,152EstimateOnly for support notes, tool notes, source notes or the rival comparison
Typical run4,149EstimateDescription, body and scanner output
Worst case7,326EstimateAll three references and 60 lines of output
Cheaper path4,149EstimateRun the summary scan, approve one batch, run --fix, re-run, skip the references

The figures come from the skill’s own count (words × 1.35 for prose, characters ÷ 3.5 for code and tables), not from a model tokeniser. The three rivals I read have bodies of about 400, 135 (visible text only) and 405 tokens, and none ships a scanner, so each loads for fewer tokens. On a 220-rule test stylesheet the summary scan printed about 40 tokens, against about 5,200 to read the file.

How to install and use it in Claude, Cursor and other agents

Claude. Keep the folder as css-logical-properties/ with SKILL.md, scripts/ and references/ inside, and zip it. In Claude, open Customize, then Skills, then Upload a skill. In Claude Code, put the folder in ~/.claude/skills/ or .claude/skills/ in a project.

Cursor and other agents. Put the folder where your tool reads skills, or paste the body into its rules and keep the scripts beside your code. The scanner needs Python 3 and nothing else. The optional screenshot script needs Playwright for Python. Then send the starting prompt from the Templates section.

Before you install. Read the scanner first. It reads files and writes nothing unless you pass --fix. The skill tells the agent to work on a branch, to edit in batches you approve, never to convert inside an RTL override and never to choose your browser support floor. Install skills only from sources you trust.

A worked example

An invented 10-rule card.css has a margin, padding, border, float and inset on the left, a four-value margin, a badge rounded on the left, a translateX hover, a left-to-right gradient, a shadow with a 4px offset, a background image offset and an [dir=rtl] block that only mirrors the base card. The scanner prints 8 auto, 2 manual, 4 physical-only and 3 override lines, plus 2 information lines.

The agent deletes the override block, then --fix writes 8 edits. The re-run prints 0 auto, 2 manual and 4 physical-only, and the left-to-right screenshots at 360, 768 and 1280 pixels are byte-identical to the baseline. The margin and the radius are then rewritten by hand, the hover slide gets a --flip variable, and the gradient, the shadow and the background offset get one RTL override each. The right-to-left view is listed as untested until someone looks at it.

Common mistakes

The skill is built around five ways this goes wrong:

  • Converting inside RTL overrides. A value that was mirrored on purpose flips back. The scanner marks these as O codes and never auto-fixes them.
  • Treating logical properties as complete. Arrows, shadows, slide-ins and gradients still point the wrong way. Each R code gets a decision rule.
  • Converting width, height and media queries. The diff grows and nothing visible changes. They stay physical unless you ask.
  • Flipping every icon. Search and settings icons should not mirror. Only directional icons flip.
  • Declaring success from one direction. The skill requires left-to-right parity and right-to-left views, or the word untested.

This skill sits in Skills-md, Frontend Development and CSS and Layout. CSS Nesting covers rewriting the converted rules once they are native nested CSS. Flexbox and Grid Layout covers layouts that still overflow or misalign after the swap. CSS Custom Properties covers the --flip variable and registering it. Localisation and Translation covers the wording a native reader must check.

Frequently asked questions

They are CSS properties named by flow, not by screen side. margin-inline-start is the left margin in left-to-right text and the right margin in right-to-left text. The W3C CSS Logical Properties module (Working Draft, 4 December 2025) defines them, and says each shares its computed value with its physical counterpart, with cascade order deciding the winner.

Swap the property name and keep the value and the declaration order. The scanner reports each margin-left, padding-right, border-left and left: 0 by file and line, and with your approval –fix rewrites them on a branch. Four-value shorthands such as margin: 4px 8px 4px 0 are shown as margin-block and margin-inline pairs for you to apply by hand.

Convert direction-dependent properties to logical ones, set dir on the html element, and keep overrides only for what has no logical form. Ahmad Shadeed’s card example shrank from about 12 physical lines to about 6 logical ones. The skill also checks that left-to-right screenshots do not change and lists what you must still view in right-to-left.

No. The W3C module covers box properties and values, not translateX, box-shadow offsets, gradient keywords or background-position, which Shadeed notes has no logical form. The scanner reports these as R codes. The skill gives a rule for each: a direction variable for sideways movement, or one [dir=rtl] override.

Delete an override that only swaps the left and right of its base rule, because the converted base rule now mirrors itself. Keep one that differs for another reason. The scanner marks every declaration inside an RTL block as an O code and never auto-fixes it, since converting a deliberately mirrored value flips it back.

Use the logical utilities: ms-4 and me-4 for margins, ps and pe for padding, start and end for position, text-start and text-end for alignment. The scanner swaps ml-, mr-, pl-, pr-, left- and right- classes and keeps variants such as md:. Check that your installed Tailwind version has these utilities before running –fix. The skill states no minimum version.

The description is about 88 tokens and loads in every session. The SKILL.md body is about 3,611 tokens when the skill fires. A typical run is about 4,149 tokens with scanner output, and the worst case with all three references is about 7,326. All figures are estimates from words and characters, not from a model tokeniser.

Changelog

  • 1.0.0, 7 November 2026: first release.

Use, adapt and share this skill under CC BY 4.0: credit Afeez Adebayo and link to this page.