MCPcopy Index your code
hub / github.com/elchininet/postcss-rtlcss

github.com/elchininet/postcss-rtlcss @v6.0.0

Chat with this repo
repository ↗ · DeepWiki ↗ · release v6.0.0 ↗ · + Follow
6,288 symbols 16,437 edges 176 files 0 documented · 0% 7 cross-repo links
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

PostCSS RTLCSS

PostCSS plugin to build Cascading Style Sheets (CSS) with Left-To-Right (LTR) and Right-To-Left (RTL) rules using RTLCSS. RTLCSS allows one to flip an entire CSS file with the intention of using the original CSS for one direction and the new generated one for the other. What PostCSS RTLCSS does, is to create a single CSS file with both directions or to create a minimal CSS file only with the flipped rules with the intention of overriding the main one.

Deployment Status Test Coverage Status npm version downloads

Playground Demo

https://elchininet.github.io/postcss-rtlcss/

Install

npm

npm install postcss-rtlcss --save-dev

pnpm

pnpm add -D postcss-rtlcss

yarn

yarn add postcss-rtlcss -D

Basic usage

Usage with commonJS

const postcss = require('postcss');
const postcssRTLCSS = require('postcss-rtlcss');
const { Mode, Source } = require('postcss-rtlcss/options');

const options = { ... available options ... };
const result = postcss([
    postcssRTLCSS(options)
]).process(cssInput);

const rtlCSS = result.css;

Usage with ES6 modules

import postcss from 'postcss';
import postcssRTLCSS from 'postcss-rtlcss';
import { Mode, Source } from 'postcss-rtlcss/options';

const options = { ... available options ... };
const result = postcss([
    postcssRTLCSS(options)
]).process(cssInput);

const rtlCSS = result.css;

Usage in Webpack with postcss-loader

rules: [
    {
        test: /\.css$/,
        use: [
            { loader: 'style-loader' },
            { loader: 'css-loader' },
            {
                loader: 'postcss-loader',
                options: {
                    postcssOptions: {
                        plugins: [
                            postcssRTLCSS(options)
                        ]
                    }
                }
            }
        ]
    }
]

Examples

Input

.test1, .test2 {
    background-color: #FFF;
    background-position: 10px 20px;
    border-radius: 0 2px 0 8px;
    color: #666;
    padding-right: 20px;
    text-align: left;
    transform: translate(-50%, 50%);
    width: 100%;
}

.test3 {
    direction: ltr;
    margin: 1px 2px 3px;
    padding: 10px 20px;
    text-align: center;
}

Output using the combined mode (default and recommended)

This is the recommended method, it will generate more CSS code because each direction will have their specific prefixed rules but it is the safest option.

.test1, .test2 {
    background-color: #FFF;
    background-position: 10px 20px;
    color: #666;
    width: 100%;
}

[dir="ltr"] .test1, [dir="ltr"] .test2 {
    border-radius: 0 2px 0 8px;
    padding-right: 20px;
    text-align: left;
    transform: translate(-50%, 50%);
}

[dir="rtl"] .test1, [dir="rtl"] .test2 {
    border-radius: 2px 0 8px 0;
    padding-left: 20px;
    text-align: right;
    transform: translate(50%, 50%);
}

.test3 {
    margin: 1px 2px 3px;
    padding: 10px 20px;
    text-align: center;
}

[dir="ltr"] .test3 {
    direction: ltr;
}

[dir="rtl"] .test3 {
    direction: rtl;
}

Output using the override mode

[!IMPORTANT] This method is not recommended, check below why

This is one of the alternative methods to override. It will generate less code because it lets the main rule intact most of the time and generates shorter specific rules to override the properties that are affected by the direction of the text.

.test1, .test2 {
    background-color: #FFF;
    background-position: 10px 20px;
    border-radius: 0 2px 0 8px;
    color: #666;
    padding-right: 20px;
    text-align: left;
    transform: translate(-50%, 50%);
    width: 100%;
}

[dir="rtl"] .test1, [dir="rtl"] .test2 {
    border-radius: 2px 0 8px 0;
    padding-right: 0;
    padding-left: 20px;
    text-align: right;
    transform: translate(50%, 50%);
}

.test3 {
    direction: ltr;
    margin: 1px 2px 3px;
    padding: 10px 20px;
    text-align: center;
}

[dir="rtl"] .test3 {
    direction: rtl;
}

Output using the diff mode

[!IMPORTANT] This method is not recommended, check below why

This is the second alternative method to override. It generates the minimum amount of code because it only outputs the rules that have been flipped and without prefixing them. The intention of this method is to generate a separate stylesheet file that will be loaded on top of the original one to override those rules that need to be flipped in certain direction.

.test1, .test2 {
    border-radius: 2px 0 8px 0;
    padding-right: 0;
    padding-left: 20px;
    text-align: right;
    transform: translate(50%, 50%);
}

.test3 {
    direction: rtl;
}

Disadvantages of the two methods to override

  1. Some directives as /*rtl:freeze*/, /*rtl:begin:freeze*/ and /*rtl:end:freeze*/ do not work with these methods
  2. They can override a property that is coming from another class if multiple classes are used at the same time. Take a look at the next HTML and CSS codes:



    This is an example



.test1 {
    background: #666;
    color: #FFF;
    padding: 20px;
}

.test2 {
    padding-right: 10px;
}

Using the combined method, the generated code will be the next one:

.test1 {
    background: #666;
    color: #FFF;
    padding: 20px;
}

[dir="ltr"] .test2 {
    padding-right: 10px;
}

[dir="rtl"] .test2 {
    padding-left: 10px;
}

So, the div will have a padding of 20px 10px 20px 20px in LTR and 20px 20px 20px 10px in RTL. Everything will work as expected here.

However, using the override method the generated code will be the next one:

.test1 {
    background: #666;
    color: #FFF;
    padding: 20px;
}

.test2 {
    padding-right: 10px;
}

[dir="rtl"] .test2 {
    padding-right: 0;
    padding-left: 10px;
}

And using the diff method the generated code will be the next one:

.test2 {
    padding-right: 0;
    padding-left: 10px;
}

Now the div has a padding of 20px 10px 20px 20px in LTR and 20px 0 20px 10px in RTL, because when the class test2 is overriden, it is not taken into account that it could be used with test1 having the same properties. The workaround, in this case, is to provide the property that has been inherited:

.test1 {
    background: #666;
    color: #FFF;
    padding: 20px;
}

.test2 {
    padding-left: 20px;
    padding-right: 10px;
}

So, using the override method the generated code will be:

.test1 {
    background: #666;
    color: #FFF;
    padding: 20px;
}

.test2 {
    padding-left: 20px;
    padding-right: 10px;
}

[dir="rtl"] .test2 {
    padding-right: 20px;
    padding-left: 10px;
}

And using the diff method the generated code will be:

.test2 {
    padding-right: 20px;
    padding-left: 10px;
}

Plugin Options

All the options are optional, and a default value will be used if any of them is omitted or the type or format of them is wrong

Option Type Default Description
mode Mode (string) Mode.combined Mode of generating the final CSS rules
ltrPrefix string or string[] [dir="ltr"] Prefix to use in the left-to-right CSS rules
rtlPrefix string or string[] [dir="rtl"] Prefix to use in the right-to-left CSS rules
bothPrefix string or string[] [dir] Prefix to create a new rule that affects both directions when the specificity of the ltr or rtl rules will override its declarations
prefixSelectorTransformer function null Transform function to have more control over the selectors prefixing logic
safeBothPrefix boolean false Add the bothPrefix to those declarations that can be affected by the direction to avoid them being overridden by specificity
ignorePrefixedRules boolean true Ignores rules that have been prefixed with some of the prefixes contained in ltrPrefix, rtlPrefix, or bothPrefix
source Source (string) Source.ltr The direction from which the final CSS will be generated
processUrls boolean false Change the strings in URLs using the string map
processRuleNames boolean false Swap two rules containing no directional properties if they match any entry in stringMap when the direction changes
processKeyFrames boolean false Flip keyframe animations
processEnv boolean true When processEnv is false, it prevents flipping agent-defined environment variables (safe-area-inset-left and safe-area-inset-right)
useCalc boolean false Flips background-position-x and transform-origin properties if they are expressed in length units using calc
stringMap PluginStringMap[] Check below An array of strings maps that will be used to make the replacements of the declarations' URLs and to match the names of the rules if processRuleNames is true
greedy boolean false When greedy is true, the matches of stringMap will not take into account word boundaries
aliases Record<string, string> {} A strings map to treat some declarations as others
processDeclarationPlugins DeclarationPlugin[] [] Plugins applied when processing CSS declarations
runOnExit boolean false Defines which visitor will be used to execute the plugin. If it is false (default value), Once will be used, but if it is true, OnceExit will be used instead.

mode

The mode option has been explained in the Output using the combined mode, the Output using the override mode, and the Output using the diff mode sections. To avoid using magic strings, the package exposes an object with these values, but it is possible to use strings values anyway:

import postcss from 'postcss';
import postcssRTLCSS from 'postcss-rtlcss';
import { Mode } from 'postcss-rtlcss/options';

const input = '... css code ...';
const optionsCombined = { mode: Mode.combined }; // This is the default value
const optionsOverride = { mode: Mode.override };
const optionsDiff = { mode: Mode.diff };

const outputCombined = postcss([
    postcssRTLCSS(optionsCombined)
]).process(input);

const outputOverride = postcss([
    postcssRTLCSS(optionsOverride)
]).process(input);

const outputDiff = postcss([
    postcssRTLCSS(optionsDiff)
]).process(input);

ltrPrefix and rtlPrefix

These two options manage the prefix strings for each direction. They can be strings or arrays of strings:

input
.test1, .test2 {
    left: 10px;
}

.test3,
.test4 {
    text-align: left;
}
Using strings
const options = {
    ltrPrefix: '.ltr',
    rtlPrefix: '.rtl'
};
output
.ltr .test1, .ltr .test2 {
    left: 10px;
}

.rtl .test1, .rtl .test2 {
    right: 10px;
}

.ltr .test3,
.ltr .test4 {
    text-align: left;
}

.rtl .test3,
.rtl .test4 {
    text-align: right;
}
Using arrays of strings
const options = {
    ltrPrefix: ['[dir="ltr"]', '.ltr'],
    rtlPrefix: ['[dir="rtl"]', '.rtl']
};
output

```css [dir="ltr"] .test1, .ltr .test1, [dir="ltr"] .test2, .ltr .test2 { left: 10px; }

[dir="rtl"] .test1, .rtl .test1, [dir="rtl"] .test2, .rtl .test2 { right: 10px; }

[dir="ltr"] .test3, .ltr .test3, [dir="ltr"] .test4, .ltr .test4 { text-align: le

Extension points exported contracts — how you extend this code

Store (Interface)
(no doc)
src/data/store.ts
StringMapOptions (Interface)
(no doc)
src/@types/index.ts
Attributes (Interface)
(no doc)
playground/src/components/SwitchGroup/SwitchGroup.tsx
StringMap (Interface)
(no doc)
src/@types/index.ts
SwitchGroupProps (Interface)
(no doc)
playground/src/components/SwitchGroup/SwitchGroup.tsx
PluginStringMap (Interface)
(no doc)
src/@types/index.ts
CSSPanelProps (Interface)
(no doc)
playground/src/components/CSSPanel/CSSPanel.tsx
DeclarationPluginProcessor (Interface)
(no doc)
src/@types/index.ts

Core symbols most depended-on inside this repo

n
called by 691
docs/scripts/924.index.js
Jt
called by 657
docs/editor.worker.js
Yt
called by 657
docs/css.worker.js
en
called by 657
docs/scripts/4464.index.js
push
called by 485
docs/scripts/7257.index.js
finish
called by 285
docs/css.worker.js
finish
called by 285
docs/scripts/924.index.js
push
called by 248
docs/css.worker.js

Shape

Method 4,160
Class 1,328
Function 766
Interface 28
Enum 6

Languages

TypeScript100%

Modules by API surface

docs/css.worker.js2,164 symbols
docs/scripts/924.index.js1,138 symbols
docs/editor.worker.js1,036 symbols
docs/scripts/4464.index.js1,032 symbols
docs/scripts/7596.index.js283 symbols
docs/scripts/5985.index.js183 symbols
docs/scripts/8386.index.js98 symbols
docs/scripts/2018.index.js87 symbols
docs/scripts/9527.index.js44 symbols
docs/scripts/7257.index.js37 symbols
playground/src/components/AppProvider/AppProvider.tsx16 symbols
docs/scripts/2641.index.js16 symbols

For agents

$ claude mcp add postcss-rtlcss \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page