Framework for transforming cascading style sheets (CSS) from left-to-right (LTR) to right-to-left (RTL)
—
RTLCSS provides comment-based directives for fine-grained control over CSS transformations. These directives allow developers to customize transformation behavior for specific CSS rules, declarations, or blocks.
Control directives modify the processing flow and behavior of RTLCSS transformations.
// Ignore directive - skip RTL processing for elements
/* rtl:ignore */
// Block ignore directive - ignore multiple elements
/* rtl:begin:ignore */
/* rtl:end:ignore */
// Rename directive - apply string mapping to selectors
/* rtl:rename */
// Raw directive - insert raw CSS
/* rtl:raw: .custom { direction: rtl; } */
// Remove directive - remove CSS elements
/* rtl:remove */
// Options directive - temporarily change configuration
/* rtl:begin:options: {"autoRename": true} */
/* rtl:end:options */Prevents RTLCSS from processing specific CSS elements.
/**
* Ignore directive syntax:
* - Single element: /* rtl:ignore * /
* - Block start: /* rtl:begin:ignore * /
* - Block end: /* rtl:end:ignore * /
*/Usage Examples:
/* Ignore single declaration */
.header {
float: left; /* rtl:ignore */
margin-right: 10px;
}
/* Output: .header { float: left; margin-left: 10px; } */
/* Ignore entire rule */
/* rtl:ignore */
.sidebar {
float: left;
margin-right: 20px;
}
/* Output: .sidebar { float: left; margin-right: 20px; } (unchanged) */
/* Block ignore multiple elements */
/* rtl:begin:ignore */
.nav-left {
float: left;
}
.nav-right {
float: right;
}
/* rtl:end:ignore */
/* Output: both rules remain unchanged */
/* Self-closing block ignore */
/* rtl:begin:ignore */
.component {
/* rtl:end:ignore */
text-align: left;
/* rtl:begin:ignore */
float: left;
}
/* rtl:end:ignore */
/* Output: only text-align is transformed */Applies string mapping transformations to CSS selectors.
/**
* Rename directive syntax:
* /* rtl:rename */
*/Usage Examples:
/* Apply string mapping to selector */
/* rtl:rename */
.nav-left {
color: blue;
}
/* Output: .nav-right { color: blue; } (if left-right string map is configured) */
/* Rename with custom string map */
/* With stringMap: [{ search: 'primary', replace: 'أساسي' }] */
/* rtl:rename */
.btn-primary {
background: blue;
}
/* Output: .btn-أساسي { background: blue; } */Inserts raw CSS content without processing.
/**
* Raw directive syntax:
* /* rtl:raw: CSS_CONTENT */
*/Usage Examples:
/* Insert raw RTL-specific CSS */
.container {
/* rtl:raw:
direction: rtl;
text-align: right;
*/
margin-left: 10px;
}
/* Output includes both the raw CSS and processed margin */
/* Complex raw insertion */
/* rtl:raw:
.rtl-only {
font-family: 'Arabic Font', sans-serif;
letter-spacing: 0;
}
.ltr-hidden {
display: none;
}
*/
.content {
text-align: left;
}
/* Raw CSS is inserted before the processed .content rule */Removes CSS elements from RTL output.
/**
* Remove directive syntax:
* /* rtl:remove */
*/Usage Examples:
/* Remove entire rule in RTL */
/* rtl:remove */
.ltr-only {
float: left;
text-align: left;
}
/* Output: rule is completely removed */
/* Remove specific declaration */
.header {
color: blue;
/* rtl:remove */
direction: ltr;
text-align: left;
}
/* Output: .header { color: blue; text-align: right; } */
/* Remove at-rule */
/* rtl:remove */
@media print {
.no-print { display: none; }
}
/* Output: entire @media rule is removed */Temporarily changes RTLCSS configuration options.
/**
* Options directive syntax:
* /* rtl:begin:options: JSON_OPTIONS */
* /* rtl:end:options */
*
* Self-closing:
* /* rtl:options: JSON_OPTIONS */
*/Usage Examples:
/* Temporarily enable autoRename */
/* rtl:begin:options: {"autoRename": true} */
.nav-left {
float: left;
}
.nav-right {
float: right;
}
/* rtl:end:options */
/* Both selector names and properties are transformed */
/* Self-closing options for single rule */
/* rtl:options: {"stringMap": [{"search": "brand", "replace": "علامة"}]} */
.brand-logo {
float: left;
}
/* Output: .علامة-logo { float: right; } */
/* Disable cleaning temporarily */
/* rtl:begin:options: {"clean": false} */
.debug {
/* rtl:ignore */
float: left; /* This comment will be preserved */
}
/* rtl:end:options */
/* Multiple option changes */
/* rtl:begin:options: {
"autoRename": true,
"greedy": true,
"stringMap": [
{
"name": "temp-map",
"search": "custom",
"replace": "مخصص",
"priority": 50
}
]
} */
.custom-header {
text-align: left;
}
/* rtl:end:options */Value directives transform CSS values inline using regular expressions.
/**
* Value directives are processed through plugin system
* They modify CSS property values during transformation
*/Custom Value Directive Example:
// Custom value directive plugin
const customValueDirective = {
name: 'custom-values',
priority: 100,
directives: {
value: [
{
name: 'flip',
action: (node, expr, context) => {
// Custom logic to flip values
const flipped = node.value.replace(/left/g, 'temp')
.replace(/right/g, 'left')
.replace(/temp/g, 'right');
node.value = flipped;
return true;
}
}
]
}
};
// Usage in CSS
.element {
/* rtl:flip */
text-align: left;
}
// Another practical example: prepend directive
const prependDirective = {
name: 'prepend-values',
priority: 100,
directives: {
value: [
{
name: 'prepend',
action: (node, expr, context) => {
const match = node.value.match(expr);
if (match && match[1]) {
node.value = match[1] + node.value;
return true;
}
return false;
}
}
]
}
};
// Usage in CSS with prepend directive
.rtl-prefix {
/* rtl:prepend: "rtl-" */
background-image: url(image.png);
}Directives are processed in the following order:
RTLCSS provides warnings for directive-related issues:
// Unclosed directive warning
/* rtl:begin:ignore */
.rule { float: left; }
// Missing /* rtl:end:ignore */ - generates warning
// Unmatched end directive
/* rtl:end:ignore */ // No matching begin - generates warning
// Invalid options JSON
/* rtl:options: {invalid json} */ // Generates error
// Blacklisted directive
// If 'ignore' is blacklisted, generates warning:
/* rtl:ignore */ // Warning: directive "rtlcss.ignore" is blacklistedUsage with Error Handling:
const postcss = require("postcss");
const rtlcss = require("rtlcss");
const processor = postcss([rtlcss()]);
processor.process(css)
.then(result => {
// Check for warnings
result.warnings().forEach(warn => {
console.warn(warn.toString());
});
console.log(result.css);
})
.catch(error => {
console.error('RTLCSS processing error:', error);
});Install with Tessl CLI
npx tessl i tessl/npm-rtlcss