# RTL Web Layout Auditor (`ammarcodes/rtl-auditor`) Actor

Automatically audit any website for RTL layout issues. Detects missing dir/lang attributes, physical CSS properties that break in RTL, bidirectional text problems, Arabic typography issues, and hardcoded positioning. Get a scored report with actionable fixes per page.

- **URL**: https://apify.com/ammarcodes/rtl-auditor.md
- **Developed by:** [Ammar Alakkad](https://apify.com/ammarcodes) (community)
- **Categories:** Developer tools, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-usage

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## RTL Web Layout Auditor

Crawl websites and audit for RTL (right-to-left) layout issues. Built for Arabic, Hebrew, Persian, Urdu, and other RTL scripts.

### Why this exists

Most web tooling assumes left-to-right. When you launch a site in Arabic, Hebrew, or Persian, layout bugs hide in plain sight — `margin-left` that doesn't flip, `text-align: left` that breaks reading order, missing `dir` attributes that confuse browsers. These issues ship to production because nobody has an automated way to catch them.

This Actor crawls your site and surfaces every RTL-specific issue, with a selector, a code snippet, and the exact fix.

### Who it's for

- **Agencies** launching Arabic versions of client sites
- **E-commerce teams** with RTL storefronts (Noon, Jumia, local marketplaces)
- **Frontend engineers** doing i18n audits before launch
- **QA teams** who need automated RTL coverage in CI

### What it checks

- **HTML attributes** — Missing `dir` and `lang` on `<html>`, incorrect `dir="auto"` usage
- **CSS properties** — Physical properties (`margin-left`, `padding-right`, `border-left`) that should use logical equivalents (`margin-inline-start`, etc.)
- **Bidirectional text** — Mixed Arabic/English content without `<bdi>` isolation, directional spillover
- **Typography** — Missing Arabic fonts, small font sizes, `letter-spacing` on Arabic text (breaks cursive connection), `px`-based font sizing
- **Float & positioning** — `float: left/right`, `text-align: left/right`, hardcoded `left`/`right` positioning in RTL layouts

### Example output

```json
{
  "url": "https://example.com/ar",
  "score": 72,
  "summary": { "errors": 1, "warnings": 4, "info": 2 },
  "issues": [
    {
      "id": "rtl-physical-css-inline",
      "severity": "warning",
      "title": "Physical CSS property \"margin-left\" in inline style",
      "selector": "div.sidebar",
      "snippet": "margin-left:20px;padding-right:10px",
      "fix": "Replace margin-left with margin-inline-start."
    },
    {
      "id": "rtl-letter-spacing",
      "severity": "error",
      "title": "Letter spacing applied to Arabic text",
      "selector": "p.arabic-content",
      "snippet": "letter-spacing: 2px",
      "fix": "Remove letter-spacing from elements that contain Arabic text."
    }
  ],
  "pageInfo": {
    "htmlDir": "rtl",
    "htmlLang": "ar",
    "detectedDirection": "rtl",
    "detectedLanguage": "ar"
  }
}
````

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `startUrls` | array | *required* | List of URLs to audit |
| `maxPagesPerSite` | integer | 10 | Maximum pages to crawl per starting URL |
| `maxCrawlDepth` | integer | 1 | How many links deep to follow (0 = starting URLs only) |
| `auditCssProperties` | boolean | true | Check for physical CSS properties |
| `auditHtmlAttributes` | boolean | true | Check dir/lang attributes |
| `auditBidirectionalText` | boolean | true | Check for bidirectional text issues |
| `auditTypography` | boolean | true | Check Arabic typography issues |
| `auditFloatAndPosition` | boolean | true | Check float/position usage |

### Scoring

- 100 = no unique issue types found
- Per unique issue type: errors -10, warnings -3, info 0
- Repeated findings of the same type don't stack (e.g. 20 instances of `margin-left` count as one warning)
- Score floors at 0

### Usage

```bash
## Via Apify CLI
apify call your-username/rtl-auditor -i '{
  "startUrls": [{"url": "https://example.com/ar"}],
  "maxPagesPerSite": 5,
  "maxCrawlDepth": 0
}'
```

```javascript
// Via API client
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('your-username/rtl-auditor').call({
  startUrls: [{ url: 'https://example.com/ar' }],
  maxPagesPerSite: 5,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

### Limitations

- Cross-origin stylesheets cannot be fully analyzed (browser security restriction)
- Dynamically injected styles after page load may be missed
- Single-page apps (SPAs) — only the initial render is audited; set `maxCrawlDepth: 0` and audit each route URL individually

# Actor input Schema

## `startUrls` (type: `array`):

List of URLs to audit for RTL layout issues.

## `maxPagesPerSite` (type: `integer`):

Maximum number of pages to crawl per starting URL. Set higher for deeper audits.

## `maxCrawlDepth` (type: `integer`):

How many links deep to follow from each start URL. 0 = only audit the start URLs.

## `auditCssProperties` (type: `boolean`):

Check for physical CSS properties that should use logical equivalents (e.g. margin-left -> margin-inline-start).

## `auditHtmlAttributes` (type: `boolean`):

Check for missing dir and lang attributes on the html element.

## `auditBidirectionalText` (type: `boolean`):

Check for LTR text in RTL contexts and missing bdi/bdo elements.

## `auditTypography` (type: `boolean`):

Check for Arabic font stack issues, missing Arabic-friendly fonts, and typography problems.

## `auditFloatAndPosition` (type: `boolean`):

Check for float:left/right and hardcoded left/right positioning in RTL layouts.

## `proxyConfiguration` (type: `object`):

Proxy settings. Use automatic for production.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://example.com/ar"
    }
  ],
  "maxPagesPerSite": 10,
  "maxCrawlDepth": 1,
  "auditCssProperties": true,
  "auditHtmlAttributes": true,
  "auditBidirectionalText": true,
  "auditTypography": true,
  "auditFloatAndPosition": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrls": [
        {
            "url": "https://example.com/ar"
        }
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("ammarcodes/rtl-auditor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "startUrls": [{ "url": "https://example.com/ar" }],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("ammarcodes/rtl-auditor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    {
      "url": "https://example.com/ar"
    }
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call ammarcodes/rtl-auditor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=ammarcodes/rtl-auditor",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "RTL Web Layout Auditor",
        "description": "Automatically audit any website for RTL layout issues. Detects missing dir/lang attributes, physical CSS properties that break in RTL, bidirectional text problems, Arabic typography issues, and hardcoded positioning. Get a scored report with actionable fixes per page.",
        "version": "0.1",
        "x-build-id": "avqbbJFhyBxEFX9HG"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/ammarcodes~rtl-auditor/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-ammarcodes-rtl-auditor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/ammarcodes~rtl-auditor/runs": {
            "post": {
                "operationId": "runs-sync-ammarcodes-rtl-auditor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/ammarcodes~rtl-auditor/run-sync": {
            "post": {
                "operationId": "run-sync-ammarcodes-rtl-auditor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "startUrls"
                ],
                "properties": {
                    "startUrls": {
                        "title": "Start URLs",
                        "type": "array",
                        "description": "List of URLs to audit for RTL layout issues.",
                        "items": {
                            "type": "object",
                            "required": [
                                "url"
                            ],
                            "properties": {
                                "url": {
                                    "type": "string",
                                    "title": "URL of a web page",
                                    "format": "uri"
                                }
                            }
                        }
                    },
                    "maxPagesPerSite": {
                        "title": "Max pages per site",
                        "minimum": 1,
                        "maximum": 500,
                        "type": "integer",
                        "description": "Maximum number of pages to crawl per starting URL. Set higher for deeper audits.",
                        "default": 10
                    },
                    "maxCrawlDepth": {
                        "title": "Max crawl depth",
                        "minimum": 0,
                        "maximum": 10,
                        "type": "integer",
                        "description": "How many links deep to follow from each start URL. 0 = only audit the start URLs.",
                        "default": 1
                    },
                    "auditCssProperties": {
                        "title": "Audit CSS properties",
                        "type": "boolean",
                        "description": "Check for physical CSS properties that should use logical equivalents (e.g. margin-left -> margin-inline-start).",
                        "default": true
                    },
                    "auditHtmlAttributes": {
                        "title": "Audit HTML attributes",
                        "type": "boolean",
                        "description": "Check for missing dir and lang attributes on the html element.",
                        "default": true
                    },
                    "auditBidirectionalText": {
                        "title": "Audit bidirectional text",
                        "type": "boolean",
                        "description": "Check for LTR text in RTL contexts and missing bdi/bdo elements.",
                        "default": true
                    },
                    "auditTypography": {
                        "title": "Audit typography",
                        "type": "boolean",
                        "description": "Check for Arabic font stack issues, missing Arabic-friendly fonts, and typography problems.",
                        "default": true
                    },
                    "auditFloatAndPosition": {
                        "title": "Audit float and position",
                        "type": "boolean",
                        "description": "Check for float:left/right and hardcoded left/right positioning in RTL layouts.",
                        "default": true
                    },
                    "proxyConfiguration": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Proxy settings. Use automatic for production."
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
