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.
By Afeez Adebayo, Marketing, SEO & Growth Expert with 10+ years of experience.
Created 7 November 2026. Updated 7 November 2026. Free to use under CC BY 4.0.
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
- When to use it
- Guardrails
- Inputs
- Workflow (steps 1 to 9)
- Worked example
- Templates
- Failure modes
- Quality bar
- Cost
- 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
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
--fixonmain. 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,.envfiles, tokens or customer text into the prompt. Skipnode_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> --summaryExit 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 hasmargin-left: 0; margin-right: 16px): the base rule will becomemargin-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 --statThe 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: adddirto<html>only if the user's answer to Input 4 says it is missing. Use the language's direction (rtlfor Arabic, Hebrew, Persian, Urdu). Keeplangas it is.I01: if CSS setsdirection, propose moving it to adirattribute. 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 usemarginStartandpaddingEnd, not web logical names. Readreferences/frameworks.md.- Mixed-script text that is user-generated (names, usernames) goes in
<bdi>or on an element withdir="auto". Directional icons flip, non-directional icons (search, home, settings) stay (rule set in the skillverse RTL rules).
Step 8: verify
- 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. - 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.
- 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, runpython3 scripts/rtl_shots.py <url> <outdir> --dir both. Readreferences/verify.mdonly if you have a browser tool. - 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 infoStep 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
| 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.
- Step 8.1 re-run shows no A or T codes.
- Every M, R and O line from step 1 appears in the report with an action or reason.
git diff --statshows only files the scanner flagged, and edit counts match.- No
width,height,min-*,max-*or media-query change unless the user asked. - Step 8.2 parity result is stated as matched or untested.
- Step 8.3 RTL result is stated as checked or untested.
- The browser floor in the report is the user's, not yours.
- 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
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--fixit 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.
| Item | Tokens | Status | Read when |
|---|---|---|---|
| Description | 88 | Estimate | Every session with the skill installed |
SKILL.md body | 3,611 | Estimate | When the skill fires |
references/frameworks.md | 647 | Estimate | Only for Tailwind, CSS-in-JS or React Native |
references/verify.md | 508 | Estimate | Step 8, only with a browser tool |
references/appendix.md | 1,152 | Estimate | Only for support notes, tool notes, source notes or the rival comparison |
| Typical run | 4,149 | Estimate | Description, body and scanner output |
| Worst case | 7,326 | Estimate | All three references and 60 lines of output |
| Cheaper path | 4,149 | Estimate | Run 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.
Related skills
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
What are CSS logical properties?
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.
How do I change margin-left to margin-inline-start?
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.
How do I support RTL in CSS without a separate stylesheet?
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.
Do logical properties flip transforms, shadows and background images?
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.
Should I delete my [dir=rtl] overrides after converting?
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.
How do I support RTL in Tailwind CSS?
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.
How many tokens does the CSS Logical Properties skill use?
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.
