Git Product home page Git Product logo

elchininet / postcss-rtlcss Goto Github PK

View Code? Open in Web Editor NEW
98.0 6.0 15.0 18.33 MB

PostCSS plugin to automatically build Cascading Style Sheets (CSS) with Left-To-Right (LTR) and Right-To-Left (RTL) rules using RTLCSS

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

License: Apache License 2.0

JavaScript 5.85% TypeScript 78.91% CSS 7.29% HTML 0.58% Handlebars 0.32% SCSS 6.90% Shell 0.16%
postcss postcss-plugin rtl ltr left-to-right right-to-left css postcss-rtlcss rtlcss direction rtl-plugin automatic-rtl plugin automatic-ltr arabic hebrew rtl-adaptivity

postcss-rtlcss's Introduction

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

## Latest version (postcss@^8.0.0)
npm install postcss-rtlcss --save-dev

## Latest legacy version (postcss@^7.0.0)
npm install postcss-rtlcss@legacy --save-dev

pnpm

## Latest version (postcss@^8.0.0)
pnpm add -D postcss-rtlcss

## Latest legacy version (postcss@^7.0.0)
pnpm add -D postcss-rtlcss@legacy

yarn

## Latest version (postcss@^8.0.0)
yarn add postcss-rtlcss -D

## Latest legacy version (postcss@^7.0.0)
yarn add postcss-rtlcss@legacy -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;
commonJS with the versions 1.x.x - 2.x.x
const { postcssRTLCSS, Mode, Source, Autorename } = require('postcss-rtlcss');

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;
ES6 modules with the versions 1.x.x - 2.x.x
import { postcssRTLCSS, Mode, Source, Autorename } from 'postcss-rtlcss';

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)

This is the recommended method, it will generate more CSS code but each direction will have their specific CSS declarations and there is no need of overriding properties.

.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

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

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;
}

But the two methods to override have a disadvantage:

Disadvantage of the override methods

Use these methods carefully. 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:

<div class="test1 test2">
    This is an example
</div>    
.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 solution, 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;
}

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

mode

Expand

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

Expand

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
[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: left;
}

[dir="rtl"] .test3,
.rtl .test3,
[dir="rtl"] .test4,
.rtl .test4 {
    text-align: right;
}


bothPrefix

Expand

This prefix will be used in some specific cases in which a ltr or rtl rule will override declarations located in the main rule due to specificity. Consider the next example using the option processUrls as true:

.test1 {
    background: url('icons/ltr/arrow.png');
    background-size: 10px 20px;
    width: 10px;
}

The generated CSS would be:

.test1 {
    background-size: 10px 20px;
    width: 10px;
}

[dir="ltr"] .test1 {
    background: url('icons/ltr/arrow.png');
}

[dir="rtl"] .test1 {
    background: url('icons/rtl/arrow.png');
}

In the previous case, the background-size property has been overridden by the background one. Even if we change the order of the rules, the last ones have a higher specificity, so they will rule over the first one.

To solve this, another rule will be created at the end using the bothPrefix parameter:

.test1 {
    width: 10px;
}

[dir="ltr"] .test1 {
    background: url('icons/ltr/arrow.png');
}

[dir="rtl"] .test1 {
    background: url('icons/rtl/arrow.png');
}

[dir] {
    background-size: 10px 20px;
}

And no matter the direction, the background-size property is respected.


prefixSelectorTransformer

Expand

This function will be used to transform the selectors and prefixing them at our will. The first parameter will be the prefix that will be used and the second the current selector:

Notes:

  • If the function doesnโ€˜t return a string, the default prefixing logic will be used.
  • If this function is used, be aware that rules using html, :root or :::view-transition will follow the custom prefixing logic. You should cover these cases.
input
.test1 {
    left: 10px;
    padding-right: 5px;
    padding-inline-end: 20px;
}

If the prefixSelectorTransformer is not sent (default):

output
[dir="ltr"] .test1 {
    left: 10px;
    padding-right: 5px;
}

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

[dir] .test1 {
    padding-inline-end: 20px;
}

Setting a prefixSelectorTransformer function

const options = {
    prefixSelectorTransformer: function (prefix, selector) {
        if (prefix === '[dir]') {
            return `.container > ${prefix} > ${selector}`;
        }
        return `${selector}${prefix}`;
    }
};
output
.test1[dir="ltr"] {
    left: 10px;
    padding-right: 5px;
}

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

.container > [dir] > .test1 {
    padding-inline-end: 20px;
}


safeBothPrefix

Expand

This option will add the boxPrefix option to those declarations that can be flipped, no matter if they are not overridden in the same rule. This avoids them being overridden by specificity of other flipped declarations contained in other rules. For example, let's consider that we have a div element with the next rules:

<div class="test1 test2">
    This is an example
</div> 
.test1 {
    color: #FFF;
    padding: 4px 10px 4px 20px;
    width: 100%;
}

.test2 {
    padding: 0;
}

The expecting result is that the padding of the element becomes 0 as it has been reset by test2. With safeBothPrefix in false, the generated CSS will be:

.test1 {
    color: #FFF;
    width: 100%;
}

[dir="ltr"] .test1 {
    padding: 4px 10px 4px 20px;
}

[dir="rtl"] .test1 {
    padding: 4px 20px 4px 10px;
}

.test2 {
    padding: 0;
}

The result is that the padding properties of test1 have more specificity than the same property in tes2, so it is not reset if both rules are applied at the same time. Let's check the result if safeBothPrefix is true:

.test1 {
    color: #FFF;
    width: 100%;
}

[dir="ltr"] .test1 {
    padding: 4px 10px 4px 20px;
}

[dir="rtl"] .test1 {
    padding: 4px 20px 4px 10px;
}

[dir] .test2 {
    padding: 0;
}

As test2 has the same level of specificity as test1, now the result is that the padding is reset if both rules are used at the same time.


ignorePrefixedRules

Expand

This option is to ignore the rules that have been prefixed with one of the prefixes contained in ltrPrefix, rtlPrefix, or bothPrefix:

input
[dir="ltr"] test {
    left: 10px;
}

[dir="rtl"] test {
    right: 10px;
}
ignorePrefixedRules true
const options = { ignorePrefixedRules: true }; // This is the default value
output
[dir="ltr"] test {
    left: 10px;
}

[dir="rtl"] test {
    right: 10px;
}
ignorePrefixedRules false
const options = { ignorePrefixedRules: false };
output
[dir="ltr"] [dir="ltr"] test {
    left: 10px;
}

[dir="rtl"] [dir="ltr"] test {
    right: 10px;
}

[dir="ltr"] [dir="rtl"] test {
    right: 10px;
}

[dir="rtl"] [dir="rtl"] test {
    left: 10px;
}


source

Expand

This option manages if the conversion will be from LTR to RTL or vice versa.

input
.test1, .test2 {
    left: 10px;
}
Using Source.ltr in combined mode
import { Mode, Source } from 'postcss-rtlcss/options';

const options = {
    mode: Mode.combined,
    source: Source.ltr // This is the default value
};
output
[dir="ltr"] .test1, [dir="ltr"] .test2 {
    left: 10px;
}

[dir="rtl"] .test1, [dir="rtl"] .test2 {
    right: 10px;
}
Using Source.rtl in override mode
import { Mode, Source } from 'postcss-rtlcss/options';

const options = {
    mode: Mode.override,
    source: Source.rtl
};
output
.test1, .test2 {
    left: 10px;
}

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


processUrls

Expand

This options manages if the strings of the URLs should be flipped taken into account the string map:

input
.test1, .test2 {
    background-image: url("./folder/subfolder/icons/ltr/chevron-left.png");
    left: 10px;
}
processUrls false
const options = { processUrls: false }; // This is the default value
output
.test1, .test2 {
    background-image: url("./folder/subfolder/icons/ltr/chevron-left.png");
}

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

[dir="rtl"] .test1, [dir="rtl"] .test2 {
    right: 10px;
}
processUrls true
const options = { processUrls: true };
output
[dir="ltr"] .test1, [dir="ltr"] .test2 {
    background-image: url("./folder/subfolder/icons/ltr/chevron-left.png");
    left: 10px;
}

[dir="rtl"] .test1, [dir="rtl"] .test2 {
    background-image: url("./folder/subfolder/icons/rtl/chevron-right.png");
    right: 10px;
}


processRuleNames

Expand

If it is true, it swaps two rules containing no directional properties if they match any entry in stringMap when the direction changes

Note that this option will not prefix those rules that have been processed already because they had directional properties.

input
.test1-ltr {
    color: #FFF;
}

.test2-left::before {
    content: "\f007";
}

.test2-right::before {
    content: "\f010";
}
processRuleNames true
const options = {
    processRuleNames: true
};
output
/* This selector will not be processed because it doesn't have a counterpart */
.test1-ltr {
    color: #FFF;
}

[dir="ltr"] .test2-left::before {
    content: "\f007";
}

[dir="rtl"] .test2-left::before {
    content: "\f010";
}

[dir="ltr"] .test2-right::before {
    content: "\f010";
}

[dir="rtl"] .test2-right::before {
    content: "\f007";
}


processKeyFrames

Expand

This option manages if the @keyframes animation rules should be flipped:

input
.test1 {
    animation: 5s flip 1s ease-in-out;
    color: #FFF;
}

@keyframes flip {
    from {
        transform: translateX(100px);
    }
    to {
        transform: translateX(0);
    }
}
processKeyFrames false
const options = { processKeyFrames: false }; // This is the default value
output
.test1 {
    animation: 5s flip 1s ease-in-out;
    color: #FFF;
}

@keyframes flip {
    from {
        transform: translateX(100px);
    }
    to {
        transform: translateX(0);
    }
}
processKeyFrames true
const options = { processKeyFrames: true };
output
.test1 {
    color: #FFF;
}

[dir="ltr"] .test1 {
    animation: 5s flip-ltr 1s ease-in-out;
}

[dir="rtl"] .test1 {
    animation: 5s flip-rtl 1s ease-in-out;
}

@keyframes flip-ltr {
    from {
        transform: translateX(100px);
    }
    to {
        transform: translateX(0);
    }
}

@keyframes flip-rtl {
    from {
        transform: translateX(-100px);
    }
    to {
        transform: translateX(0);
    }
}


processEnv

Expand

This options manages if the agent-defined environment variables should be flipped:

input
body {
    padding:
        env(safe-area-inset-top, 10px)
        env(safe-area-inset-right, 20px)
        env(safe-area-inset-bottom, 30px)
        env(safe-area-inset-left, 40px)
    ;
}

.test1 {
    margin-right: env(safe-area-inset-right, 10px);
    margin-left: env(safe-area-inset-left, 20px);
}
processEnv true
const options = { processEnv: true }; // This is the default value
output
[dir=\\"ltr\\"] body {
    padding:
        env(safe-area-inset-top, 10px)
        env(safe-area-inset-right, 20px)
        env(safe-area-inset-bottom, 30px)
        env(safe-area-inset-left, 40px)
    ;
}

[dir=\\"rtl\\"] body {
    padding:
        env(safe-area-inset-top, 10px)
        env(safe-area-inset-right, 40px)
        env(safe-area-inset-bottom, 30px)
        env(safe-area-inset-left, 20px);
}

[dir=\\"ltr\\"] .test1 {
    margin-right: env(safe-area-inset-right, 10px);
    margin-left: env(safe-area-inset-left, 20px);
}

[dir=\\"rtl\\"] .test1 {
    margin-left: env(safe-area-inset-left, 10px);
    margin-right: env(safe-area-inset-right, 20px);
}
processEnv false
const options = { processEnv: false };
output
[dir=\\"ltr\\"] body {
    padding:
        env(safe-area-inset-top, 10px)
        env(safe-area-inset-right, 20px)
        env(safe-area-inset-bottom, 30px)
        env(safe-area-inset-left, 40px)
    ;
}

[dir=\\"rtl\\"] body {
    padding:
        env(safe-area-inset-top, 10px)
        env(safe-area-inset-left, 40px)
        env(safe-area-inset-bottom, 30px)
        env(safe-area-inset-right, 20px);
}

[dir=\\"ltr\\"] .test1 {
    margin-right: env(safe-area-inset-right, 10px);
    margin-left: env(safe-area-inset-left, 20px);
}

[dir=\\"rtl\\"] .test1 {
    margin-left: env(safe-area-inset-right, 10px);
    margin-right: env(safe-area-inset-left, 20px);
}


useCalc

Expand

When this option is enabled, it flips background-position-x and transform-origin properties if they are expressed in length units using calc:

input
.test {
    background-image: url("./folder/subfolder/icons/ltr/chevron-left.png");
    background-position-x: 5px;
    left: 10px;
    transform-origin: 10px 20px;
    transform: scale(0.5, 0.5);
}
useCalc false
const options = { useCalc: false }; // This is the default value
output
.test {
    background-image: url("./folder/subfolder/icons/ltr/chevron-left.png");
    background-position-x: 5px;
    transform-origin: 10px 20px;
    transform: scale(0.5, 0.5);
}

[dir="ltr"] .test {
    left: 10px;
}

[dir="rtl"] .test {
    right: 10px;
}
useCalc true
const options = { useCalc: true };
output
.test {
    background-image: url("./folder/subfolder/icons/ltr/chevron-left.png");
    transform: scale(0.5, 0.5);
}

[dir="ltr"] .test {
    background-position-x: 5px;
    left: 10px;
    transform-origin: 10px 20px;
}

[dir="rtl"] .test {
    background-position-x: calc(100% - 5px);
    right: 10px;
    transform-origin: calc(100% - 10px) 20px;
}


stringMap

Expand

An array of strings maps that will be used to make the replacements of the declarations' URLs and to match rules selectors names if the processRuleNames option is true. The name parameter is optional, but if you want to override any of the default string maps, just add your own using the same name.

// This is the default string map object
const options = {
    stringMap: [
        {
            name: 'left-right',
            search : ['left', 'Left', 'LEFT'],
            replace : ['right', 'Right', 'RIGHT']
        },
        {
            name: 'ltr-rtl',
            search  : ['ltr', 'Ltr', 'LTR'],
            replace : ['rtl', 'Rtl', 'RTL'],
        }
    ]
};


greedy

Expand

When greedy is true, the matches of the stringMap will not take into account word boundaries.

input
.test1 {
    background: url("icon-left.png");
}

.test2 {
    background: url("icon-ultra.png");
}
greedy false
const options = {
    processUrls: true,
    greedy: false // This is the default value
};
output
[dir="ltr"] .test1 {
    background: url("icon-left.png");
}

[dir="rtl"] .test1 {
    background: url("icon-right.png");
}

.test2 {
    background: url("icon-ultra.png");
}
greedy true
const options = {
    processUrls: true,
    greedy: true
};
output
[dir="ltr"] .test1 {
    background: url("icon-left.png");
}

[dir="rtl"] .test1 {
    background: url("icon-right.png");
}

[dir="ltr"] .test2 {
    background: url("icon-ultra.png");
}

[dir="rtl"] .test2 {
    background: url("icon-urtla.png");
}


aliases

Expand

This property consists of a string map to treat some declarations as others, very useful to flip the values of CSS variables.

Note: This property is not available in the legacy version of the package

input
:root {
    --my-padding: 1rem 1rem 1.5rem 1.5rem;
}

.test {
    padding: var(--my-padding);
}
No aliases string map (default)
output
:root {
    --my-padding: 1rem 1rem 1.5rem 1.5rem;
}

.test {
    padding: var(--my-padding);
}
Set an aliases string map
const options = {
    aliases: {
        '--my-padding': 'padding'
    }
};
output
[dir="ltr"]:root {
    --my-padding: 1rem 1rem 1.5rem 1.5rem;
}

[dir="rtl"]:root {
    --my-padding: 1rem 1.5rem 1.5rem 1rem;
}

.test {
    padding: var(--my-padding);
}


processDeclarationPlugins

Expand

The intention of the processDeclarationPlugins option is to process the declarations to extend or override RTLCSS functionality. For example, we can avoid automatically flipping of background-potion.

input
.test {
    background-position: 0 100%;
}
Convert 0 to 100% (default)
output
.test {
    background-position: 100% 100%;
}
Set a plugin to avoid flipping
const options = {
	processDeclarationPlugins: [
        {
            name: 'avoid-flipping-background',
            priority: 99, // above the core RTLCSS plugin which has a priority value of 100
            processors: [{
                expr: /(background|object)(-position(-x)?|-image)?$/i,
                action: (prop, value) => ({prop, value})}
            ]
        }
    ]
};
output
.test {
    background-position: 0 100%;
}


Control Directives

Control directives are placed between rules or declarations. They can target a single node or a set of nodes.

Note: block directives (the ones that start with begin and end with end) should be placed outside rules to apply the directive to multiple rules or inside a rule to apply the directive to multiple declarations. You should not place the begin of a directive outside a rule and the end inside one (or vice versa) or you will get undesired results.

Directive Description
/*rtl:ignore*/ Ignores processing of the following rule or declaration
/*rtl:begin:ignore*/ Starts an ignoring block
/*rtl:end:ignore*/ Ends an ignoring block
/*rtl:urls*/ This directive set the processUrls option to true in the next declaration or in the declarations of the next rule no mattering the value of the global processUrls option
/*rtl:begin:urls*/ Starts a processUrls block block
/*rtl:end:urls*/ Ends a processUrls block block
/*rtl:rules*/ This directive set the processRuleNames option to true in the next rule no mattering the value of the global processRuleNames option
/*rtl:begin:rules*/ Starts a processRuleNames block block
/*rtl:end:rules*/ Ends a processRuleNames block block
/*rtl:source:{source}*/ Set the source of a rule or a declaration no mattering the value of the source property
/*rtl:begin:source:{source}*/ Starts a source block
/*rtl:end:source*/ Ends a source block
/*rtl:raw:{CSS}*/ Parses the CSS parameter and inserts it in its place. Depending on the source parameter the parsed CSS will be treated as rtl or ltr

/*rtl:ignore*/

Expand

This directive ignores processing of the following rule or declaration. In the next block the whole declaration will be ignored.

input
/*rtl:ignore*/
.test1, .test2 {
    text-align: left;
    left: 10px;
}
output
.test1, .test2 {
    text-align: left;
    left: 10px;
}

In the next block only the left property will be ignored:

input
.test3, .test4 {
    text-align: left;
    /*rtl:ignore*/
    left: 10px;
}
output
.test3, .test4 {
    left: 10px;
}

[dir="ltr"] .test3, [dir="ltr"] .test4 {
    text-align: left;
}

[dir="rtl"] .test3, [dir="rtl"] .test4 {
    text-align: right;
}


/*rtl:begin:ignore*/ and /*rtl:end:ignore*/

Expand

These directives should be used together, they will provide the beginning and the end for ignoring rules or declarations.

Note: The directives inserted between these blocks will be ignored and maintained in the final output.

Ignoring multiple rules:

input
/*rtl:begin:ignore*/
.test1, .test2 {
    left: 10px;
    text-align: left;
}

.test3 {
    padding: 1px 2px 3px 4px;
}
/*rtl:end:ignore*/
output
.test1, .test2 {
    left: 10px;
    text-align: left;
}

.test3 {
    padding: 1px 2px 3px 4px;
}

Ignoring multiple declarations:

input
.test1, .test2 {
    left: 10px;
    /*rtl:begin:ignore*/
    margin-left: 4em;
    padding: 1px 2px 3px 4px;
    /*rtl:end:ignore*/
    text-align: left;
}
output
.test1, .test2 {
    margin-left: 4em;
    padding: 1px 2px 3px 4px;
}

[dir="ltr"] .test1, [dir="ltr"] .test2 {
    left: 10px;
    text-align: left;
}

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


/*rtl:urls*/

Expand

This directive set the processUrls option to true in the next declaration or in the declarations of the next rule no mattering the value of the global processUrls option:

input
/*rtl:urls*/
.test1 {
    background-image: url("/buttons/button-ltr.png");
}

.test2 {
    /*rtl:urls*/
    background-image: url("/icons/icon-left.png");
}
output
[dir="ltr"] .test1 {
    background-image: url("/buttons/button-ltr.png");
}

[dir="rtl"] .test1 {
    background-image: url("/buttons/button-rtl.png");
}

[dir="ltr"] .test2 {
    background-image: url("/icons/icon-left.png");
}

[dir="rtl"] .test2 {
    background-image: url("/icons/icon-right.png");
}


/*rtl:begin:urls*/ and /*rtl:end:urls*/

Expand

These directives should be used together, they will provide the beginning and the end for processUrls blocks.

input
/*rtl:begin:urls*/
.test1 {
    background-image: url("/buttons/button-ltr.png");
}

.test2 {
    background-image: url("/icons/icon-left.png");
}
/*rtl:end:urls*/

.test3 {
    /*rtl:begin:urls*/
    background-image: url("/images/background-left.png");
    cursor: url("/images/cursor-ltr.png");
    /*rtl:end:urls*/
}
output
[dir="ltr"] .test1 {
    background-image: url("/buttons/button-ltr.png");
}

[dir="rtl"] .test1 {
    background-image: url("/buttons/button-rtl.png");
}

[dir="ltr"] .test2 {
    background-image: url("/icons/icon-left.png");
}

[dir="rtl"] .test2 {
    background-image: url("/icons/icon-right.png");
}

[dir="ltr"] .test3 {
    background-image: url("/images/background-left.png");
    cursor: url("/images/cursor-ltr.png");
}

[dir="rtl"] .test3 {
    background-image: url("/images/background-right.png");
    cursor: url("/images/cursor-rtl.png");
}


/*rtl:rules*/

Expand

This directive set the processRuleNames option to true in the next rule no mattering the value of the global processRuleNames option:

input
/*rtl:rules*/
.test1-ltr {
    background-image: url('/images/test1-l.png');
}

/*rtl:rules*/
.test1-rtl {
    background-image: url('/images/test1-r.png');
}

/*rtl:rules*/
.test2-left::before {
    content: "\f007";
}

.test2-right::before {
    content: "\f010";
}
output
[dir="ltr"] .test1-ltr {
    background-image: url('/images/test1-l.png');
}

[dir="rtl"] .test1-ltr {
    background-image: url('/images/test1-r.png');
}

[dir="ltr"] .test1-rtl {
    background-image: url('/images/test1-r.png');
}

[dir="rtl"] .test1-rtl {
    background-image: url('/images/test1-l.png');
}

/* These selectors will not be processed because only one of them has the rtl:rules directive */
.test2-left::before {
    content: "\f007";
}

.test2-right::before {
    content: "\f010";
}


/*rtl:begin:rules*/ and /*rtl:end:rules*/

Expand

These directives should be used together, they will provide the beginning and the end for processRuleNames blocks.

input
.test1-ltr {
    background-image: url('/images/test1-l.png');
}

.test1-rtl {
    background-image: url('/images/test1-r.png');
}

/*rtl:begin:rules*/
.test2-left::before {
    content: "\f007";
}

.test2-right::before {
    content: "\f010";
}
/*rtl:begin:rules*/
output
.test1-ltr {
    background-image: url('/images/test1-l.png');
}

.test1-rtl {
    background-image: url('/images/test1-r.png');
}

[dir="ltr"] .test2-left::before {
    content: "\f007";
}

[dir="rtl"] .test2-left::before {
    content: "\f010";
}

[dir="ltr"] .test2-right::before {
    content: "\f010";
}

[dir="rtl"] .test2-right::before {
    content: "\f007";
}


/*rtl:source:{source}*/

Expand

This directive sets the source of a rule or a directive ignoring the value of the source property:

input
/*rtl:source:rtl*/
.test {
    color: #FFF;
    border-left: 1px solid #666;
    padding: 10px 5px 10px 20px;
    text-align: left;
    width: 100%;
}
output
.test {
    color: #FFF;
    width: 100%;
}

[dir="ltr"] .test {
    border-right: 1px solid #666;
    padding: 10px 20px 10px 5px;
    text-align: right;
}

[dir="rtl"] .test {
    border-left: 1px solid #666;
    padding: 10px 5px 10px 20px;
    text-align: left;
}


/*rtl:begin:source:{source}*/ and /*rtl:end:source*/

Expand

These directives should be used together, they will provide the beginning and the end of source blocks for rules or declarations:

input
.test {
    color: #FFF;
    border-left: 1px solid #666;
    /*rtl:begin:source:rtl*/
    padding: 10px 5px 10px 20px;
    text-align: left;
    /*rtl:end:source*/
    width: 100%;
}
output
.test {
    color: #FFF;
    width: 100%;
}

[dir="ltr"] .test {
    border-left: 1px solid #666;
    padding: 10px 20px 10px 5px;
    text-align: right;
}

[dir="rtl"] .test {
    border-right: 1px solid #666;
    padding: 10px 5px 10px 20px;
    text-align: left;
}


/*rtl:raw:{CSS}*/

Expand

Parses the CSS parameter and inserts it in its place. Depending on the source parameter the parsed CSS will be treated as rtl or ltr:

input
.test1 {
    color: #EFEFEF;
    left: 10px;
    /*rtl:raw:
    height: 50px;
    width: 100px;*/
}

/*rtl:raw:.test2 {
    color: #EFEFEF;
    left: 10px;
    width: 100%;    
}

.test3 {
    transform: translate(10px, 20px);
}
*/
output
.test1 {
    color: #EFEFEF;
}

[dir="ltr"] .test1 {
    left: 10px;
}

[dir="rtl"] .test1 {
    right: 10px;
    height: 50px;
    width: 100px;
}

[dir="rtl"] .test2 {
    color: #EFEFEF;
    left: 10px;
    width: 100%;    
}

[dir="rtl"] .test3 {
    transform: translate(10px, 20px);
}


Value Directives

Value directives are placed anywhere inside the declaration value. They target the containing declaration node.

Directive Description
/*rtl:ignore*/ Ignores processing of the declaration
/*rtl:append{value}*/ Appends {value} to the end of the declaration value
/*rtl:insert:{value}*/ Inserts {value} to where the directive is located inside the declaration value
/*rtl:prepend:{value}*/ Prepends {value} to the begining of the declaration value
/*rtl:{value}*/ Replaces the declaration value with {value}

/*rtl:ignore*/

Expand

This directive ignores processing of the current declaration:

input
.test1, .test2 {
    text-align: left /*rtl:ignore*/;
    left: 10px;
}
output
.test1, .test2 {
    text-align: left;
}

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

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


/*rtl:append{value}*/

Expand

This directive appends {value} to the end of the declaration value:

input
.test1, .test2 {
    padding: 10px /*rtl:append20px*/;
    left: 10px;
}
output
[dir="ltr"] .test1, [dir="ltr"] .test2 {
    padding: 10px;
    left: 10px;
}

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


/*rtl:insert:{value}*/

Expand

This directive inserts {value} to where the directive is located inside the declaration value:

input
.test1, .test2 {
    padding: 10px/*rtl:insert 20px*/ 5px;
    left: 10px;
}
output
[dir="ltr"] .test1, [dir="ltr"] .test2 {
    padding: 10px 5px;
    left: 10px;
}

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


/*rtl:prepend:{value}*/

Expand

This directive prepends {value} to the begining of the declaration value:

input
.test1, .test2 {
    font-family: Arial, Helvetica/*rtl:prepend:"Droid Arabic Kufi", */;
    left: 10px;
}
output
[dir="ltr"] .test1, [dir="ltr"] .test2 {
    font-family: Arial, Helvetica;
    left: 10px;
}

[dir="rtl"] .test1, [dir="rtl"] .test2 {
    font-family: "Droid Arabic Kufi", Arial, Helvetica;
    right: 10px;
}


/*rtl:{value}*/

Expand

This directive replaces the declaration value with {value}:

input
.test1, .test2 {
    font-family: Arial, Helvetica/*rtl:"Droid Arabic Kufi"*/;
    left: 10px;
}
output
[dir="ltr"] .test1, [dir="ltr"] .test2 {
    font-family: Arial, Helvetica;
    left: 10px;
}

[dir="rtl"] .test1, [dir="rtl"] .test2 {
    font-family: "Droid Arabic Kufi";
    right: 10px;
}


If you do not use PostCSS, add it according to official docs and set this plugin in settings.

postcss-rtlcss's People

Contributors

aleen42 avatar armanozak avatar dependabot[bot] avatar elchininet avatar julien-deramond avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

postcss-rtlcss's Issues

Not work with less

My configuration is as follows:

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

Then it reported an error:

ERROR in ./src/pages/haha/container/plan-md.less (./node_modules/css-loader!./node_modules/less-loader/dist/cjs.js!./node_modules/postcss-loader/src??ref--7-3!./src/pages/okex/container/plan-md.less)
Module build failed (from ./node_modules/postcss-loader/src/index.js):
SyntaxError

(10:5) Unknown word

   8 |   }
   9 |   .section-content {
> 10 |     .gg();
     |     ^
  11 |     margin-top: 24px;
  12 |     .content {

postcss-rtlcss does not support less? Who can help me

Webpack function error

When i used this library with Webpack, an error occurred

Module build failed (from ./node_modules/postcss-loader/src/index.js):
TypeError: PostCssRtlCssPlugin is not a function
Child

and here is my config file

/**
 * Main file of webpack config for RTL.
 * Please do not modified unless you know what to do
 */
const path = require("path");
const PostCssRtlCssPlugin = require('postcss-rtlcss')
const WebpackMessages = require("webpack-messages");
const del = require("del");

// theme name
const themeName = "metronic";
// global variables
const rootPath = path.resolve(__dirname);
const distPath = rootPath + "/src/assets";

const entries = {
  "css/style.vue": "./src/assets/css/style.vue.css"
};

const mainConfig = function () {
  return {
    mode: "development",
    stats: "errors-only",
    performance: {
      hints: false
    },
    entry: entries,
    output: {
      // main output path in assets folder
      path: distPath,
    },
    plugins: [
      // webpack log message
      new WebpackMessages({
        name: themeName,
        logger: str => console.log(`>> ${str}`)
      }),
      {
        apply: compiler => {
          // hook name
          compiler.hooks.afterEmit.tap("AfterEmitPlugin", () => {
            (async () => {
              await del.sync(distPath + "/css/*.js", { force: true });
            })();
          });
        }
      }
    ],
    module: {
      rules: [
        {
          test: /\.css$/,
          use: [
            "style-loader",
            "css-loader",
            {
              loader: "postcss-loader",
              options: {
                ident: "postcss",
                plugins: () => [PostCssRtlCssPlugin({
                  mode: "override"
                })]
              }
            }
          ]
        }
      ]
    }
  };
};

module.exports = function () {
  return [mainConfig()];
};

Do you have any idea?

None of the Directives works with next js

hey I have latest nextjs project this is my postcss file

module.exports = { plugins: { tailwindcss: {}, "postcss-flexbugs-fixes": {}, "postcss-preset-env": { autoprefixer: { flexbox: "no-2009", }, stage: 3, features: { "custom-properties": false, }, }, "postcss-rtlcss": {}, }, };

postcss-rtlcss works fine the only thing that's not working is the Directives for example i put this in my main css file

/* rtl:begin:ignore */
body {
background-color: blue;
}

/* rtl:end:ignore */

but it seems the Directives don't work the background is blue in both rtl and ltr

Incorrect output when dealing with duplicate property declarations in a selector

Playground reproduction of the issue: https://elchininet.github.io/postcss-rtlcss/#63dd66e7f0726

Input

.test1, .test2 {
    margin-right: 4px;
    margin-right: var(--hello);
}

Output

.test1, .test2 {
    margin-right: 4px;
}

[dir="ltr"] .test1, [dir="ltr"] .test2 {
    margin-right: var(--hello);
}

[dir="rtl"] .test1, [dir="rtl"] .test2 {
    margin-left: var(--hello);
}

Expected output

[dir="ltr"] .test1, [dir="ltr"] .test2 {
    margin-right: var(--hello);
}

[dir="rtl"] .test1, [dir="rtl"] .test2 {
    margin-left: var(--hello);
}

When I try to RTL a selector with duplicate property declarations, but different values, the library seems smart enough to know that the last declaration applies, but not all instances of the property are removed from the non-prefixed LTR / RTL selectors.

(In our particular use case, we are extending styles of components by merging styles together, resulting in duplicate property declarations with different values, and noticed that the RTL versions still had some LTR styling applied.)

Is there any requirement on the postCSS-Loader version?

The following code works:
"postcss": "^7.0.39", "postcss-loader": "^4.3.0", "postcss-rtlcss": "^1.7.2",

But The following code does not works:
"postcss": "^7.0.39", "postcss-loader": "^3.0.0", "postcss-rtlcss": "^1.7.2",

Example on how to add postcss-rtlcss to `postcss.config.js`

I'm looking to integrate this plugin using the postcss.config.js configuration file, though there's no documentation on how to go about doing that. I'm new to front-end development and I'm just starting to learn about bundlers so this type of things are not really second-nature to me.

Is it possible to use this plugin with Storybook?

Spent hours trying to come up with a setup that allowed me to run Storybook and my components and an enhanced version of my inlined CSS (Storybook seems to use Emotion) to have RTL support, but nothing that I tried worked.

Among other things, this is what I have in my package.json:

"@storybook/addon-postcss": "^2.0.0", // may or not be needed, tried with and without
"postcss": "^8.4.12",
"postcss-loader": "4.2.0",
"postcss-rtlcss": "^3.6.2",
"rtlcss": "^3.5.0",

I managed to load Storybook, but inlined styles do not contain a RTL version. Has anyone managed to have a setup w/ Storybook+postcss-rtlcss?

The plugin does not honour the stringMap directive.

As can be seen from the illustration, the string highlighted is the same after being processed, and the selector as well is not changed.
image

before

/* rtl:begin:options: {
  "stringMap":[ {
    "name"    : "prev-next",
    "search"  : "prev",
    "replace" : "next"
  } ]
} */
.carousel-control-prev-icon {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e");
}

.carousel-control-next-icon {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
}
/* rtl:end:options */

After

/* rtl:begin:options: {
  "stringMap":[ {
    "name"    : "prev-next",
    "search"  : "prev",
    "replace" : "next"
  } ]
} */
.carousel-control-prev-icon {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e");
}

.carousel-control-next-icon {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
}
/* rtl:end:options */

Error after upgrading from 3.6.0 to 3.6.1

ERROR in ./resources/css/vendor.css
Module build failed (from ./node_modules/mini-css-extract-plugin/dist/loader.js):
ModuleBuildError: Module build failed (from ./node_modules/postcss-loader/dist/cjs.js):
TypeError: Cannot read properties of undefined (reading 'some')
    at /Users/gr4376/Projects/webtrees/node_modules/bootstrap/dist/css/bootstrap.min.css:1:1
    at AtRule.some (/Users/gr4376/Projects/webtrees/node_modules/postcss/lib/container.js:267:23)
    at H (/Users/gr4376/Projects/webtrees/node_modules/postcss-rtlcss/index.js:1:5660)
    at /Users/gr4376/Projects/webtrees/node_modules/postcss-rtlcss/index.js:1:17440
    at /Users/gr4376/Projects/webtrees/node_modules/postcss/lib/container.js:55:18
    at Root.each (/Users/gr4376/Projects/webtrees/node_modules/postcss/lib/container.js:41:16)
    at Root.walk (/Users/gr4376/Projects/webtrees/node_modules/postcss/lib/container.js:52:17)
    at /Users/gr4376/Projects/webtrees/node_modules/postcss-rtlcss/index.js:1:17279
    at Object.Once (/Users/gr4376/Projects/webtrees/node_modules/postcss-rtlcss/index.js:1:17533)
    at LazyResult.runOnRoot (/Users/gr4376/Projects/webtrees/node_modules/postcss/lib/lazy-result.js:337:23)
    at LazyResult.runAsync (/Users/gr4376/Projects/webtrees/node_modules/postcss/lib/lazy-result.js:393:26)
    at processResult (/Users/gr4376/Projects/webtrees/node_modules/webpack/lib/NormalModule.js:751:19)
    at /Users/gr4376/Projects/webtrees/node_modules/webpack/lib/NormalModule.js:853:5
    at /Users/gr4376/Projects/webtrees/node_modules/loader-runner/lib/LoaderRunner.js:399:11
    at /Users/gr4376/Projects/webtrees/node_modules/loader-runner/lib/LoaderRunner.js:251:18
    at context.callback (/Users/gr4376/Projects/webtrees/node_modules/loader-runner/lib/LoaderRunner.js:124:13)
    at Object.loader (/Users/gr4376/Projects/webtrees/node_modules/postcss-loader/dist/index.js:142:7)

1 ERROR in child compilations (Use 'stats.children: true' resp. '--stats-children' for more details)
webpack compiled with 2 errors

My knowledge of npm build tools is fairly weak, so I'm not sure how to produce a minimal test case.

The problem is reproducable with my open-source project, if that helps - fisharebest/webtrees...

  • git clone, npm install, npm run production - works OK
  • Edit package.json to update from 3.6.0 to 3.6.1. npm install, npm run production - error as above.

TypeError: Cannot read properties of undefined (reading 'replace')

First of all, thank you very much for this plugin. It has saved us a lot of time.

The cleanRuleRawsBefore function replaces multiple new lines from the before property of a node's raws, but results in a type error same as the title of this issue when the property is missing.

The error is caused by this line: https://github.com/elchininet/postcss-rtlcss/blob/master/src/utilities/rules.ts#L165

The following change fixes the error:

node.raws.before = '\n\n' + (node.raws.before ? node.raws.before.replace(/\n/g, '') : '');

Please let me know if you would like me to create a PR.

Exclude some styles that comes from packages - Next.Js

Hello, I have installed this package React-Phone-Input-2 the package have a css file which I have to import it to my application, when going rtl mode for some reason the package's style brakes, I don't want to fix the break i'd rather want to the package stay with the style it currently have and I don't want rtl mode for this package at all, that why I want to postcss-rtlcss exclude this package.

Thanks

Not working in Next.js postcss

According to next.js docs, require() is not allowed to import the PostCSS Plugins. Plugins must be provided as strings.

My postcss.config.js

module.exports = {
    plugins: ["postcss-rtlcss"]
}

Error when running next.js development server

Error: [Object Object] is not a PostCSS plugin

What is the proper way to use postcss-rtlcss in next.js?

Flip variables values

Check this issue in postcss-rtl repo.

The value of the variables is not flipped, for example:

:root {
  --padding: 1rem 1rem 1.5rem 1.5rem;
}

.my-class {
  padding: var(--padding);
}

Is compiled to:

:root {
  --padding: 1rem 1rem 1.5rem 1.5rem;
}

.my-class {
  padding: var(--padding);
}

Warning with Vite 3

Hi,

We've recently migrated to Vite 3 and started seeing the following warning when we include postcss-rtlcss in our postcss.config file:

"A PostCSS plugin did not pass the from option to postcss.parse. This may cause imported assets to be incorrectly transformed. If you've recently added a PostCSS plugin that raised this warning, please contact the package author to fix the issue."

This is the PR that introduced this warning:
vitejs/vite#8183

Can this cause unexpected issues later on and can it be fixed?

Thanks!

Media query + text-align: center not given precedence

Hi @elchininet,

At below 600px, I want the text to be centered aligned but will in fact be left aligned due to specificity.
image

Codesandbox with more similar code that I'm encountering with my configuration
https://codesandbox.io/s/cranky-violet-bx0v2w?file=/index.html

Even with the config safeBothPrefix: true, I'm not getting the .test1 in a media query to have a .dir in front of it.

My full config is

    ltrPrefix: '.dir-ltr',
    rtlPrefix: '.dir-rtl',
    bothPrefix: '.dir',
    safeBothPrefix: true,
    prefixSelectorTransformer: (prefix, selector) => `${prefix}${selector}`,

5.1.1 release fails to install

Failure log:

npm ERR! code 254
npm ERR! path /home/runner/work/generator-jhipster/generator-jhipster/app/node_modules/postcss-rtlcss
npm ERR! command failed
npm ERR! command sh -c npx -y only-allow pnpm
npm ERR! npm ERR! code ENOENT
npm ERR! npm ERR! syscall lstat
npm ERR! npm ERR! path /home/runner/work/generator-jhipster/generator-jhipster/app/target/lib
npm ERR! npm ERR! errno -2
npm ERR! npm ERR! enoent ENOENT: no such file or directory, lstat '/home/runner/work/generator-jhipster/generator-jhipster/app/target/lib'
npm ERR! npm ERR! enoent This is related to npm not being able to find a file.
npm ERR! npm ERR! enoent 

Looks related to added preinstall script:

"preinstall": "npx -y only-allow pnpm",

jhipster/generator-jhipster#25836
jhipster/generator-jhipster#25825

Problem with bootstrap variable customization

Hello,
Thank you for developing and maintaining this plugin. I am trying to use this plugin with bootstrap 5 and having a problem. I am modifying the $font-family-sans-serif variable to include an Arabic font as shown in their documentation. In my scss I have the following:

$font-family-sans-serif:
  system-ui,
  ...
  #{"/*rtl:prepend:Tajawal,*/"} !default;

@import '~bootstrap/scss/bootstrap';

The output css will have the following:

[dir="ltr"] :root {
  --bs-font-sans-serif: system-ui, ...; }

[dir="rtl"] :root {
  --bs-font-sans-serif: Tajawal,system-ui, ...; }

but it does not work as expected. Previously I used postcss-rtl and the same css worked so I tried to see what the difference is and I found that the output is the same except it does not contain a space between the brackets and :root like this: [dir="ltr"]:root which works as expected. Do you think there is a bug here or is it supposed to be like this?

Problem when using in next.js

postcss.config.js like this
module.exports = { plugins: ['postcss-rtlcss'] }

and the packages version
"postcss": "^8.1.1", "postcss-loader": "^4.0.3", "postcss-rtlcss": "3.0.0",

it failed to compile as below:

`ValidationError: Invalid options object. PostCSS Loader has been initialized using an options object that does not match the API schema.

  • options has an unknown property 'config'. These properties are valid:
    object { postcssOptions?, execute?, sourceMap?, implementation? }

Build error occurred`

How can I use postcss-rtlcss in nextjs project properly?

rtl:raw not taken into account in my final build

Hello

I use

svg {
/*rtl:raw:
    transform: scaleX(-1);*/
}

and this is my webpack config for production build

{
		test: /\.scss$/,
		use: [
			MiniCssExtractPlugin.loader,
			{
				loader: "css-loader",
				options: {
					modules: "icss",
				},
			},
			{
				loader: "postcss-loader",
				options: {
					postcssOptions: {
						plugins: [
							"autoprefixer",
							enableRTL
								? postcssRTLCSS({
										mode: Mode.combined,
										})
								: "",
						],
					},
				},
			},
			{
				loader: "sass-loader",
				options: {
					additionalData: sassLoaderData,
					// Prefer `dart-sass`
					implementation: require("sass"),
				},
			},
		],
	},

The final build does not take the directive rtl:raw into account.

The transform css property is missing

could it be a bug ?

Example of how to setup postcss-rtlcss with react-scripts (and possibly craco)

I attempted and failed to setup postcss-rtlcss for processing bootstrap SCSS files and various custom .module.scss files in a create-react-app project where craco already is being used (and for completeness sake, we use react-bootstrap). I would love to have an example or recipe for enabling use of postcss-rtlcss that integrates well with the above, perhaps as a complement to the note about using postcss-rtlcss in bootstrap

react-bootstrap/react-bootstrap#6794

Cannot read properties of undefined (reading 'some') with sass like mixin on first line

Hello!
In my nuxt-2 project, I'm trying to replace node-sass with a set of postcss plugins.
This is my config part

         postcss: {
             plugins: {
                 'postcss-nested': {},
                 'postcss-simple-vars': {
                     variables: variablesPostCss
                 },
                 'postcss-mixins': {
                     mixins: mixinsPostCss,
                 },
                 'postcss-functions': {
                     functions: functionsPostCss
                 },
                 'postcss-rtlcss': {
                     processKeyFrames: true
                 },
             },
             preset: {
                 features: {
                     'logical-properties-and-values': false,
                 },
             },
         },

In general, everything works correctly, however, the sass-like mixin set by the first line breaks the build.

.draw-info {
   $fixbug: &; /* this code is workaround, without line build breaks */

   @mixin theme primary, -20;
   position: relative;
   padding: em($gutter);
   border-radius: $border_radius;
   text-align: center;

   &::before,
   &::after {

     position: absolute;
     top: em($gutter);
     bottom: em($gutter);
     width: em(3);

     content: "";
     background-color: getColor(accent);
   }
}

It seems that rtlcss accepts source code as input rather than accepting it sequentially.
This is also evidenced by the ability to repeat the same bug in the playground - https://elchininet.github.io/postcss-rtlcss/#644157320bad7

the error is located here rtlcss/utilities/rules.ts on function ruleHasChildren

With override mode turned on, some properties are still extracted

Here's the configuration from my project

 plugins: [postcssRTLCSS({ mode: Mode.override })],

Following this configuration, I reproduced the output at the playground

/* input */
.demo{
    background: linear-gradient(180deg, #fff27e 0%, #ffffff 100%);
    background-clip: text;
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    color: rgba(255, 242, 126, 1);
}
/* output*/
.demo{
    background: linear-gradient(180deg, #fff27e 0%, #ffffff 100%);
    -webkit-text-fill-color: transparent;
    color: rgba(255, 242, 126, 1);
}

[dir="rtl"] .demo{
    background: linear-gradient(-180deg, #fff27e 0%, #ffffff 100%);
}

[dir] .demo{
    background-clip: text;
    -webkit-background-clip: text;
}

I don't know why background-clip was extracted.Is this a bug?

If I remove the backgound, the output meets my expectations

romove background: linear-gradient(180deg, #fff27e 0%, #ffffff 100%);

/* input and output are the same  */
.demo{
    background-clip: text;
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    color: rgba(255, 242, 126, 1);
}

Release

Please, create github release for every version so that one can subscribe and be notified about new features.

add ignoreList to options

hi,
when we use postcss-rtlcss with other ui libraries like ant-design, we have not access to their css file to ignore what we want with directives e.g. with /*rtl:ignore*/ so it is good to have some option property to list all selectors we want to ignore:

postcss: {
  plugins: {
     'postcss-rtlcss': {
         ignoreList: ['.ignore-rtl', '#ignore-me']
     }
  }
}```

Types don't work with allowJs: true and require()

If I enable "allowJs": true and "checkJs": true in my tsconfig.json, TypeScript will do type checking in JavaScript files like postcss.confg.js. However, postcss-rtlcss's type definitions break in this case:

const postcssRtlcss = require( 'postcss-rtlcss' );
const autoprefixer = require( 'autoprefixer' );

module.exports = {
    plugins: [
        postcssRtlcss(),
        autoprefixer()
    ]
};

This results in the following TypeScript error:

postcss.config.js:12:3 - error TS2349: This expression is not callable.
  Type 'typeof import("/home/catrope/[...]/node_modules/postcss-rtlcss/index")' has no call signatures.

12   postcssRtlcss()
     ~~~~~~~~~~~~~

If I use require( 'postcss-rtlcss').default, then TypeScript is happy, but of course I get a runtime error because that property doesn't exist in reality.

For autoprefixer everything does work correctly. It looks like the critical difference is that postcss-rtlcss uses export default blah; but autoprefixer uses export = blah;.

Including postcss-rtlcss with Angular12 with SCSS base styles later compiled to CSS

I am trying to use this library in my Angular 12 project with a custom webpack. I am getting this error: "Module build failed (from ./node_modules/sass-loader/dist/cjs.js): SassError: Invalid CSS after "i": expected 1 selector or at-rule, was 'import api from "!.' on line 1 of src/app/... from sass-loader/dist/cjs.js file"

module.exports = (config, options) => {
    addRtlConfig = (config) => {
        return {
            ...config, *//has all the existing default configs*
            module: {
                ...config.module,
                rules: [
                    ...config.module.rules,
                    {
                    test: /\.scss$|\.sass$/,
                      use: [
                        'style-loader',
                        'css-loader',
                        {
                          'loader': 'postcss-loader',
                          'options': {
                            postcssOptions: {
                                plugins: () => [require('postcss-rtlcss')({})],
                            }
                          }
                        },
                        'sass-loader'
                      ]
                    },
                  ]
                }
            }
        }
    return addRtlConfig(config);
}

Please guide if I am using this incorrectly!

safeBothPrefix does not prefix unknown CSS properties

We are using a polyfill for some experimental CSS properties such as animation-timeline. (The specific polyfill doesn't matter here, but just for context, this specific polyfill requires that the new experimental CSS properties be next to the other animation properties.)

Reproduction: https://elchininet.github.io/postcss-rtlcss/#661086140c525

Input

.example1 {
    transform: translate3d(-21vh, 0, 0);

    view-timeline: --row-a block;
    animation-range: cover 0% cover 100%;
    
    animation-timing-function: var(--timing-function);;
    animation-name: slide-in;
} 

.example2 {
    transform: translate3d(-21vh, 0, 0);

    @media (prefers-reduced-motion: no-preference) {
      view-timeline: --row-a block;
      
      animation-timing-function: var(--timing-function);
      animation-name: slide-in;
    }
}

Output:

.example1 {

    view-timeline: --row-a block;
    animation-range: cover 0% cover 100%;
}

[dir] .example1 {
    
    animation-timing-function: var(--timing-function);;
    animation-name: slide-in;
}

[dir="ltr"] .example1 {
    transform: translate3d(-21vh, 0, 0);
}

[dir="rtl"] .example1 {
    transform: translate3d(21vh, 0, 0);
}

 .example2 {
    @media (prefers-reduced-motion: no-preference) {
      view-timeline: --row-a block;
      
      animation-timing-function: var(--timing-function);
      animation-name: slide-in;
    }
}

 [dir="ltr"] .example2 {
    transform: translate3d(-21vh, 0, 0)
}

 [dir="rtl"] .example2 {
    transform: translate3d(21vh, 0, 0)
}

In example1, notice how view-timeline is not prefixed with safeBothPrefix because โ€” if I'm reading the code correctly โ€” it is not a known CSS property listed here.

For some reason, in example2, wrapping the entire block with a media query seems to keep them together.

Would it be possible to support safeBothPrefix on unknown CSS properties?

Thanks!

Possible to support RTL-ing css variable values?

I noticed postcss-rtlcss does not support RTL-ing css variable values. Example

Input

.test1 {
    background: linear-gradient(to right,#000 0%,#FFF 100%);
}

.test2 {
    --gradient: linear-gradient(to right,#000 0%,#FFF 100%);
    background: var(--gradient);
}

Output

[dir="ltr"] .test1 {
    background: linear-gradient(to right,#000 0%,#FFF 100%);
}

[dir="rtl"] .test1 {
    background: linear-gradient(to left,#000 0%,#FFF 100%);
}

.test2 {
    --gradient: linear-gradient(to right,#000 0%,#FFF 100%);
    background: var(--gradient);
}

Ideal Output

[dir="ltr"] .test1 {
    background: linear-gradient(to right,#000 0%,#FFF 100%);
}

[dir="rtl"] .test1 {
    background: linear-gradient(to left,#000 0%,#FFF 100%);
}

.test2 { 
    background: var(--gradient);
}

[dir="ltr"] .test2 {
    --gradient: linear-gradient(to right,#000 0%,#FFF 100%);
}

[dir="rtl"] .test2 {
   --gradient: linear-gradient(to left,#000 0%,#FFF 100%);
}

Granted, css variable values can be anything and a single css variable might be used in both RTL/LTR contexts as well as static contexts:

.foo {
--someValue: 10px; // does not get RTL-ed
padding-left: var(--someValue);
font-size: var(--someValue);
}

But would it be possible to deduce the CSS variables that are only used in RTL/LTR contexts and RTL the values in such cases?

.bar {
   --gradient: linear-gradient(to left,#000 0%,#FFF 100%); // does get RTL-ed
   background: var(--gradient);
}

Thanks!

Issue with converting scss to rtlcss in Angular component styles

Hi, I have been trying to use the postcss-rtlcss plugin with postcss-loader in an Angular12 project.

All is well when styles are defined in the main styles.css file. However, the conversion does not work when styles are defined for a specific component. In such case Angular adds a [_ngcontent-] selector to each class of the component so it knows how the styles should be scoped.

So sass input:

.padding-class {
  padding-left: 250px;
  p {
    color: blue;
    margin-left: 100px;
  }
}

Is converted like in the image below:
image

As you can probably already see, the tag is also added after the generated [dir] selectors which causes the classes to not be applied.

Here is a simple project that reproduces the problem: https://github.com/MatasRut/rtlcss-example

Please advise on how this issue can be resolved.

Issue with padding 0 overrides in combined mode

Core issue in the playground:
image

The eventual output of the css will be 8px rather than 0px due to the specificity of the 8px being later. Is it possible to not drop the padding value even if it's 0?

expected result:

.test1 {
    padding-top: 4px;
    padding-bottom: 4px;
    padding-top: 0;
    padding-bottom: 0;
    padding-left: 0;
    padding-right: 0;
}

[dir="ltr"] .test1 {
    padding-left: 8px;
    padding-right: 8px;
    padding-left: 0;
    padding-right: 0;
}

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

When using `safeBothPrefix` the prefix `[dir]` is missing for styles using `padding-block`

When using padding-block and safeBothPrefix the output doesn't wrap the class using padding-block with [dir] that leads to specificity issues with other styles using padding.

Example

.test {
    padding-block: 10;
}

input {
    padding: 0;
}

Output:

.test {
    padding-block: 10;
}

[dir] input {
    padding: 0;
}

Expected:

[dir] .test {
    padding-block: 10;
}

[dir] input {
    padding: 0;
}

Playground: https://elchininet.github.io/postcss-rtlcss/#6503a1cc884f3

overriding properties with "unset" gives unexpected results

Hello.
When override mode is enable, some properties like border-left-width do not convert correctly to RTL, since they get overridden with the "unset" keyword, which allows the default values to be used. In the case of border-left-width, setting it to unset results in a computed value of 3px in FF

.cls {
  border-left-width: 1px;
}

gives:

.cls {
  border-left-width: 1px;
}

[dir="rtl"] .cls {
  border-left-width: unset; // this is 3px in Firefox
  border-right-width: 1px;
}

Missing styles with @media nesting (conflict with postcss-nested?)

So the problem:

Initial code:

.b-product-single {
 
  &__main {
    @media screen and (min-width: 991px) {
      display: grid;
      grid-template-columns: 1fr 250px;
      grid-column-gap: var(--large-margin);
    }
  }
  
  &__image-wrapper {
    text-align: right;
    overflow: hidden;

    img {
      max-width: 100%;
      height: auto;
      object-fit: scale-down;
    }
  }
}

results to by a postcss-nested plugin:

 @media screen and (min-width: 991px) {

  .b-product-single__main {
      display: grid;
      grid-template-columns: 1fr 250px;
      grid-column-gap: var(--large-margin)
  }

 
  .b-product-single__image-wrapper {
    text-align: right;
    overflow: hidden;
  }

  .b-product-single__image-wrapper img {
      max-width: 100%;
      height: auto;
      object-fit: scale-down;
    }
    }

Where .b-product-single__main just disappears after postcss-rtlcss.

If I replace initial code with stright result of postcss-nested I cannot see the problem.

My env:

node -v
v16.14.0

package.json:

    "autoprefixer": "^10.4.2",
    "cssnano": "^5.0.17",
    "postcss": "^8.4.6",
    "postcss-nested": "^5.0.6",
    "postcss-rtlcss": "^3.5.2",

    ...
   "postcss-loader": "^6.2.1",
   "webpack": "^5.65.0",
   "webpack-cli": "^4.9.2",

postcss.config.js:

const postcssRTLCSS = require('postcss-rtlcss')
const cssnano = require('cssnano')
const postcssNested = require('postcss-nested')

module.exports = (ctx) => {
  const isProduction = ctx.env === 'production'
  return {
    plugins: [
      postcssNested(),
      postcssRTLCSS({ mode: 'override'}),
      require('autoprefixer'),
      isProduction && cssnano(({
        preset: 'default',
      })),
    ]
  }
}

If I rollback to postcss-rtlcss@2 it works. So may be version 3.x has some conflicts with other postcss plugins?
And another way to make it work:

.b-product-single {
 
  &__main {
    /* Is to write any rule */
   display: block;
    @media screen and (min-width: 991px) {
      display: grid;
      grid-template-columns: 1fr 250px;
      grid-column-gap: var(--large-margin);
    }
  }

In this way the code results to:

.b-product-single__main {
    display: block;
  }

  @media screen and (min-width: 991px) {

  .b-product-single__main {
      display: grid;
      grid-template-columns: 1fr 250px;
      grid-column-gap: var(--large-margin)
  }
    }

And a last way to make it work is to remove all trailing left-right specific rules:

&__main {
    @media screen and (min-width: 991px) {
      display: grid;
      grid-template-columns: 1fr 250px;
      grid-column-gap: var(--large-margin);
    }
  }

  &__image-wrapper {
    /* We can see a text-align: right; here, so remove it and see what happens: */
    /* text-align: right; */
    overflow: hidden;

    img {
      max-width: 100%;
      height: auto;
      object-fit: scale-down;
    }
  }

Result:

@media screen and (min-width: 991px) {

  .b-product-single__main {
      display: grid;
      grid-template-columns: 1fr 250px;
      grid-column-gap: var(--large-margin)
  }
    }

  .b-product-single__image-wrapper {
    /* text-align: right; */
    overflow: hidden;
  }

  .b-product-single__image-wrapper img {
      max-width: 100%;
      height: auto;
      object-fit: scale-down;
    }

Well .b-product-single__main doesn't disappears now. Magic.

There is no errors due to a build process. I tested it with webpack and rollup, so loaders and bundlers doesn't matters.

Integrate better with CSS logical properties and values

It would be great if this plugin works better with CSS Logical Properties and Values. I have two use cases.

First, I would like a mode to convert physical direction and dimension to logical ones (like converting border-left to border-inline-start).

Second, when there are logical and physical rules in the same rule, the semantics of the transformed rule could be wrong. For example:

.test1, .test2 {
    padding-right: 20px;
    padding-inline-end: 30px;
}

is transformed to:

.test1, .test2 {
    padding-inline-end: 30px;
}

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

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

But padding-inline-end should overwrite padding-right in LTR (or padding-left in RTL).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.