Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## [3.6.0] - 2022-04-09

- Added a new diff mode to output only flipped rules with the intention of using them in a separate stylesheet file to override the main stylesheet

## [3.5.4] - 2022-03-26

- Build the package bundle using rollup and created an ESM version of the package
Expand Down
60 changes: 48 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# PostCSS RTLCSS

[PostCSS] plugin to build Cascading Style Sheets (CSS) with Left-To-Right (LTR) and Right-To-Left (RTL) rules using [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 PostcCSS 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.

[![Build Status](https://travis-ci.com/elchininet/postcss-rtlcss.svg?branch=master)](https://app.travis-ci.com/elchininet/postcss-rtlcss)   [![Coverage Status](https://coveralls.io/repos/github/elchininet/postcss-rtlcss/badge.svg?branch=master)](https://coveralls.io/github/elchininet/postcss-rtlcss?branch=master)   [![npm version](https://badge.fury.io/js/postcss-rtlcss.svg)](https://badge.fury.io/js/postcss-rtlcss)

[PostCSS]: https://github.com/postcss/postcss
[RTLCSS]: https://rtlcss.com/

Demo
Playground Demo
---

https://elchininet.github.io/postcss-rtlcss/
Expand Down Expand Up @@ -131,7 +131,7 @@ Examples

#### 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 not need to override properties.
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.

```css
.test1, .test2 {
Expand Down Expand Up @@ -172,7 +172,7 @@ This is the recommended method, it will generate more CSS code but each directio

#### Output using the override mode

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

```css
.test1, .test2 {
Expand Down Expand Up @@ -206,12 +206,30 @@ This is the alternative method, it will generate less code because it lets the m
}
```

But this method has a disadvantage:
#### Output using the diff mode

<details><summary>Disadvantage of the override method</summary>
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.

```css
.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:

<details><summary>Disadvantage of the methods to override</summary>
<p>

Use this method carefully. It 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:
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:

```html
<div class="test1 test2">
Expand All @@ -231,7 +249,7 @@ Use this method carefully. It can override a property that is coming from anothe
}
```

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

```css
.test1 {
Expand All @@ -249,9 +267,9 @@ Using the combined method, the generated code will be the next one:
}
```

So, the `div` will have a padding of `20px 10px 20px 20px` in `LTR` and `20px 20px 20px 10px` in `RTL`.
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:
However, using the `override` method the generated code will be the next one:

```css
.test1 {
Expand All @@ -270,7 +288,16 @@ However, using the override method the generated code will be the next one:
}
```

Now the `div` has a padding of `20px 10px 20px 20px` in `LTR` and `20px 0 20px 10px` in `RTL`, because the override of the class `test2` doesn't take into account that this class could be used with `test1` having the same properties. The solution, in this case, is to provide the property that has been inherited:
And using the `diff` method the generated code will be the next one:

```css
.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:

```css
.test1 {
Expand All @@ -285,7 +312,7 @@ Now the `div` has a padding of `20px 10px 20px 20px` in `LTR` and `20px 0 20px 1
}
```

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

```css
.test1 {
Expand All @@ -305,6 +332,15 @@ So, the generated code will be:
}
```

And using the `diff` method the generated code will be:

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

</p>
</details>

Expand Down
3 changes: 2 additions & 1 deletion src/@types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { Rule, AtRule } from 'postcss';

export enum Mode {
combined = 'combined',
override = 'override'
override = 'override',
diff = 'diff'
}

export enum Source {
Expand Down
8 changes: 7 additions & 1 deletion src/data/store.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Rule } from 'postcss';
import { Rule, AtRule } from 'postcss';
import {
PluginOptions,
PluginOptionsNormalized,
Expand All @@ -24,10 +24,12 @@ import {
interface Store {
options: PluginOptionsNormalized;
keyframes: AtRulesObject[];
keyframesToRemove: AtRule[];
keyframesStringMap: AtRulesStringMap;
keyframesRegExp: RegExp;
rules: RulesObject[];
rulesAutoRename: Rule[];
rulesToRemove: Rule[];
rulesPrefixRegExp: RegExp;
}

Expand Down Expand Up @@ -146,10 +148,12 @@ const defaultOptions = (): PluginOptionsNormalized => ({
const store: Store = {
options: {...defaultOptions()},
keyframes: [],
keyframesToRemove: [],
keyframesStringMap: {},
keyframesRegExp: defaultRegExp,
rules: [],
rulesAutoRename: [],
rulesToRemove: [],
rulesPrefixRegExp: defaultRegExp
};

Expand Down Expand Up @@ -215,10 +219,12 @@ const normalizeOptions = (options: PluginOptions): PluginOptionsNormalized => {
const initStore = (options: PluginOptions): void => {
store.options = normalizeOptions(options);
store.keyframes = [];
store.keyframesToRemove = [];
store.keyframesStringMap = {};
store.keyframesRegExp = defaultRegExp;
store.rules = [];
store.rulesAutoRename = [];
store.rulesToRemove = [];
store.rulesPrefixRegExp = createRulesPrefixesRegExp(store.options);
};

Expand Down
8 changes: 7 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ import { PluginOptions } from '@types';
import { initStore } from '@data/store';
import { parseKeyFrames, parseAtRules } from '@parsers/atrules';
import { parseRules } from '@parsers/rules';
import { appendRules, appendKeyFrames, appendAutorenameRules } from '@utilities/rules';
import {
appendRules,
appendKeyFrames,
appendAutorenameRules
} from '@utilities/rules';
import { clean } from '@utilities/clean';

function postcssRTLCSS (options: PluginOptions = {}): Plugin {
return ({
Expand All @@ -16,6 +21,7 @@ function postcssRTLCSS (options: PluginOptions = {}): Plugin {
appendRules();
appendKeyFrames();
appendAutorenameRules();
clean(css);
}
});
};
Expand Down
31 changes: 26 additions & 5 deletions src/parsers/atrules.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import postcss, { Root, Container, Node, AtRule, Comment } from 'postcss';
import postcss, {
Root,
Container,
Node,
AtRule,
Comment
} from 'postcss';
import rtlcss from 'rtlcss';
import {
Source,
ControlDirective
ControlDirective,
Mode
} from '@types';
import {
AT_RULE_TYPE,
Expand Down Expand Up @@ -62,6 +69,12 @@ export const parseAtRules = (container: Container): void => {

};

const addToIgnoreKeyframesInDiffMode = (node: Node): void => {
if (store.options.mode === Mode.diff) {
store.keyframesToRemove.push(node as AtRule);
}
};

export const parseKeyFrames = (css: Root): void => {

const { source, processUrls, useCalc, stringMap, processKeyFrames } = store.options;
Expand All @@ -87,19 +100,27 @@ export const parseKeyFrames = (css: Root): void => {
(node: Node): void => {

if ( checkDirective(controlDirectives, CONTROL_DIRECTIVE.IGNORE) ) {
addToIgnoreKeyframesInDiffMode(node);
return;
}

if (node.type !== AT_RULE_TYPE) return;
if (node.type !== AT_RULE_TYPE) {
return;
}

const atRule = node as AtRule;

if (vendor.unprefixed(atRule.name) !== KEYFRAMES_NAME) return;
if (vendor.unprefixed(atRule.name) !== KEYFRAMES_NAME) {
return;
}

const atRuleString = atRule.toString();
const atRuleFlippedString = rtlcss.process(atRuleString, { processUrls, useCalc, stringMap });

if (atRuleString === atRuleFlippedString) return;
if (atRuleString === atRuleFlippedString) {
addToIgnoreKeyframesInDiffMode(atRule);
return;
}

const rootFlipped = postcss.parse(atRuleFlippedString);
const atRuleFlipped = rootFlipped.first as AtRule;
Expand Down
Loading