---
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.
