npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

postcss-change-dir

v0.1.1

Published

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

Readme

PostCSS RTLCSS 是一个 [PostCSS] 插件,用于构建包含左到右(LTR)和右到左(RTL)规则的层叠样式表(CSS)。RTLCSS 允许用户翻转整个 CSS 文件,目的是使用原始 CSS 用于一个方向,而新生成的 CSS 用于另一个方向。PostCSS RTLCSS 的作用是创建一个包含两个方向的单一 CSS 文件,或者创建一个仅包含翻转规则的最小 CSS 文件,目的是覆盖主文件。

安装

npm

npm install postcss-change-dir --save-dev

pnpm

pnpm add -D postcss-change-dir

yarn

yarn add postcss-change-dir -D

基本用法

与 CommonJS 一起使用

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

const options = { ...可用选项... };
const result = postcss([
    postcssRTLCSS(options)
]).process(cssInput);

const rtlCSS = result.css;

与 ES6 模块一起使用

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

const options = { ...可用选项... };
const result = postcss([
    postcssRTLCSS(options)
]).process(cssInput);

const rtlCSS = result.css;

在 Webpack 中与 postcss-loader 一起使用

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

示例

输入

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

使用组合模式(默认推荐)

这是推荐的方法,它将生成更多的 CSS 代码,因为每个方向都将具有其特定的前缀规则,但这是最安全的选择。

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

使用覆盖模式输出

[!重要] 不推荐使用此方法,查看下面的原因

这是覆盖的另一种替代方法。它将生成较少的代码,因为它让主规则保持不变,并且生成较短的特定规则来覆盖受文本方向影响的属性。

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

使用差异模式输出

[!重要] 不推荐使用此方法,查看下面的原因

这是第二种替代的覆盖方法。它生成的代码量最少,因为它只输出被翻转的规则,而不加前缀。这种方法的目的是生成一个单独的样式表文件,该文件将被加载在原始文件之上,以覆盖需要在特定方向翻转的规则。

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

.test3 {
  direction: rtl;
}

两种覆盖方法的缺点

  1. 一些指令如 /*rtl:freeze*/, /*rtl:begin:freeze*//*rtl:end:freeze*/ 在这些方法中不起作用。
  2. 它们可能会覆盖来自另一个类的属性,如果同时使用多个类。查看下一个 HTMLCSS 代码:
<div class="test1 test2">这是一个示例</div>
.test1 {
  background: #666;
  color: #fff;
  padding: 20px;
}

.test2 {
  padding-right: 10px;
}

使用 combined 方法,生成的代码将是下一个:

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

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

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

所以,divLTR 中的 padding 将是 20px 10px 20px 20px,在 RTL 中将是 20px 20px 20px 10px。这里一切都按预期工作。

然而,使用 override 方法,生成的代码将是下一个:

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

.test2 {
  padding-right: 10px;
}

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

使用 diff 方法,生成的代码将是下一个:

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

现在 divLTR 中的 padding 将是 20px 10px 20px 20px,在 RTL 中将是 20px 0 20px 10px,因为当类 test2 被覆盖时,没有考虑到它可能与 test1 一起使用,具有相同的属性。在这种情况下的解决方案是提供被继承的属性:

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

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

所以,使用 override 方法,生成的代码将是:

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

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

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

使用 diff 方法,生成的代码将是:

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

插件选项

所有选项都是可选的,如果省略了任何一个选项或者它们的类型或格式错误,将使用默认值

| 选项 | 类型 | 默认值 | 描述 | | ------------------------------------------------------- | ------------------------ | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | mode | Mode (string) | Mode.combined | 生成最终 CSS 规则的模式 | | ltrPrefix | string or string[] | [dir="ltr"] | 左到右 CSS 规则中使用的前缀 | | rtlPrefix | string or string[] | [dir="rtl"] | 右到左 CSS 规则中使用的前缀 | | bothPrefix | string or string[] | [dir] | 创建一个新规则,当 ltr 或 rtl 规则的特异性覆盖其声明时,影响两个方向 | | prefixSelectorTransformer | function | null | 转换函数,以更控制选择器前缀逻辑 | | safeBothPrefix | boolean | false | 将 bothPrefix 添加到可能受方向影响的声明中,以避免被特异性覆盖 | | ignorePrefixedRules | boolean | true | 忽略已经被 ltrPrefix, rtlPrefix, 或 bothPrefix 中的一些前缀标记的规则 | | source | Source (string) | Source.ltr | 从哪个方向生成最终 CSS | | processUrls | boolean | false | 使用字符串映射更改 URL 中的字符串 | | processRuleNames | boolean | false | 如果它们与 stringMap 中的任何条目匹配,则在方向变化时交换两个规则,即使它们不包含方向属性 | | processKeyFrames | boolean | false | 翻转关键帧动画 | | processEnv | boolean | true | 当 processEnv 为 false 时,防止翻转代理定义的环境变量(safe-area-inset-leftsafe-area-inset-right) | | useCalc | boolean | false | 如果它们使用 calc 表示长度单位,则翻转 background-position-xtransform-origin 属性 | | stringMap | PluginStringMap[] | 查看下面 | 用于替换声明的 URL 和匹配规则选择器名称的字符串映射数组,如果 processRuleNamestrue | | greedy | boolean | false | 当 greedy 为 true 时,stringMap 的匹配不考虑单词边界 | | aliases | Record<string, string> | {} | 字符串映射,将一些声明视为其他声明 | | processDeclarationPlugins | DeclarationPlugin[] | [] | 处理 CSS 声明时应用的插件 |


mode

mode 选项已在 使用组合模式输出, 使用覆盖模式输出, 和 使用差异模式输出 部分中解释。为了避免使用魔术字符串,该包暴露了一个具有这些值的对象,但无论如何都可以使用字符串值:

import postcss from 'postcss'
import postcssRTLCSS from 'postcss-change-dir'
import { Mode } from 'postcss-change-dir/options'

const input = '... css 代码 ...'
const optionsCombined = { mode: Mode.combined } // 这是默认值
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 和 rtlPrefix

这两个选项管理每个方向的前缀字符串。它们可以是字符串或字符串数组:

输入
.test1,
.test2 {
  left: 10px;
}

.test3,
.test4 {
  text-align: left;
}
使用字符串
const options = {
  ltrPrefix: '.ltr',
  rtlPrefix: '.rtl'
}
输出
.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;
}
使用字符串数组
const options = {
  ltrPrefix: ['[dir="ltr"]', '.ltr'],
  rtlPrefix: ['[dir="rtl"]', '.rtl']
}
输出
[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

这个前缀将在一些特定情况下使用,其中一个 ltr 或 rtl 规则将由于特异性覆盖主规则中位于的声明。考虑使用 processUrls 选项作为 true 的下一个示例:

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

生成的 CSS 将是:

.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');
}

在之前的案例中,background-size 属性被 background 属性覆盖。即使我们改变规则的顺序,最后一组规则具有更高的特异性,因此它们将覆盖第一组规则。

为了解决这个问题,最后将使用 bothPrefix 参数创建另一个规则:

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

不管怎样的方向,background-size 属性都受到尊重。


prefixSelectorTransformer

这个函数将用于转换选择器,并根据我们的意愿前缀它们。第一个参数将是使用的前缀,第二个是当前选择器:

[!注意]

  • 如果该函数不返回字符串,则将使用默认的前缀逻辑。
  • 如果使用这个函数,请确保 html, :root::view-transition 使用自定义前缀逻辑。您应该涵盖这些情况。
输入
.test1 {
  left: 10px;
  padding-right: 5px;
  padding-inline-end: 20px;
}

如果 prefixSelectorTransformer 没有发送(默认):

输出
[dir='ltr'] .test1 {
  left: 10px;
  padding-right: 5px;
}

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

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

设置 prefixSelectorTransformer 函数

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

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

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

safeBothPrefix

这个选项将 boxPrefix 选项添加到可以翻转的声明中,不管它们是否在同一规则中被覆盖。这避免了它们被其他规则中包含的翻转声明的特异性覆盖。例如,让我们考虑一个 div 元素具有以下规则:

<div class="test1 test2">这是一个示例</div>
.test1 {
  color: #fff;
  padding: 4px 10px 4px 20px;
  width: 100%;
}

.test2 {
  padding: 0;
}

预期的结果是元素的 padding 变为 0,因为它被 test2 重置了。使用 safeBothPrefixfalse 时,生成的 CSS 将是:

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

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

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

.test2 {
  padding: 0;
}

结果是 test1padding 属性的特异性高于 test2 中的同一属性,所以如果同时应用两个规则,则不会被重置。让我们检查如果 safeBothPrefixtrue 的结果:

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

由于 test2 具有与 test1 相同的特异性水平,现在结果是如果同时使用两个规则,则 padding 将被重置。


ignorePrefixedRules

这个选项用于忽略已经被 ltrPrefix, rtlPrefix, 或 bothPrefix 中的一些前缀标记的规则:

输入
[dir='ltr'] test {
  left: 10px;
}

[dir='rtl'] test {
  right: 10px;
}
ignorePrefixedRules 为 true
const options = { ignorePrefixedRules: true } // 这是默认值
输出
[dir='ltr'] test {
  left: 10px;
}

[dir='rtl'] test {
  right: 10px;
}
ignorePrefixedRules 为 false
const options = { ignorePrefixedRules: false }
输出
[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

这个选项管理转换是从 LTRRTL 还是反之。

输入
.test1,
.test2 {
  left: 10px;
}
使用 Source.ltr 在组合模式
import { Mode, Source } from 'postcss-change-dir/options'

const options = {
  mode: Mode.combined,
  source: Source.ltr // 这是默认值
}
输出
[dir='ltr'] .test1,
[dir='ltr'] .test2 {
  left: 10px;
}

[dir='rtl'] .test1,
[dir='rtl'] .test2 {
  right: 10px;
}
使用 Source.rtl 在覆盖模式
import { Mode, Source } from 'postcss-change-dir/options'

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

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

processUrls

这个选项管理是否应该翻转 URL 中的字符串,考虑到字符串映射:

输入
.test1,
.test2 {
  background-image: url('./folder/subfolder/icons/ltr/chevron-left.png');
  left: 10px;
}
processUrls 为 false
const options = { processUrls: false } // 这是默认值
输出
.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 }
输出
[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

如果为 true,则在方向变化时,如果它们与 stringMap 中的任何条目匹配,则交换两个不包含方向属性的规则

[!重要] 这个选项不会前缀已经被处理过的规则,因为它们具有方向属性。

输入
.test1-ltr {
  color: #fff;
}

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

.test2-right::before {
  content: '\f010';
}
processRuleNames 为 true
const options = {
  processRuleNames: true
}
输出
/* 这个选择器将不会被处理,因为它没有对应的 */
.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

这个选项管理是否应该翻转 @keyframes 动画规则:

输入
.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 } // 这是默认值
输出
.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 }
输出
.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

这个选项管理是否应该翻转代理定义的环境变量:

输入
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 } // 这是默认值
输出
[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 }
输出
[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

当这个选项启用时,如果它们使用 calc 表示长度单位,则翻转 background-position-xtransform-origin 属性:

输入
.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 } // 这是默认值
输出
.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 }
输出
.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

一个字符串映射数组,用于替换声明的 URL 和匹配规则选择器名称,如果 processRuleNames 选项为 true。名称参数是可选的,但如果您想覆盖任何默认字符串映射,只需使用相同的名称添加您自己的。

// 这是默认的字符串映射对象
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

greedytrue 时,stringMap 的匹配不考虑单词边界。

输入
.test1 {
  background: url('icon-left.png');
}

.test2 {
  background: url('icon-ultra.png');
}
greedy 为 false
const options = {
  processUrls: true,
  greedy: false // 这是默认值
}
输出
[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
}
输出
[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

这个属性由字符串映射组成,将一些声明视为其他声明,这对于翻转 CSS 变量 的值非常有用。

输入
:root {
  --my-padding: 1rem 1rem 1.5rem 1.5rem;
}

.test {
  padding: var(--my-padding);
}
没有别名字符串映射(默认)
输出
:root {
  --my-padding: 1rem 1rem 1.5rem 1.5rem;
}

.test {
  padding: var(--my-padding);
}
设置别名字符串映射
const options = {
  aliases: {
    '--my-padding': 'padding'
  }
}
输出
[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

processDeclarationPlugins 选项的目的是处理声明,以扩展或覆盖 RTLCSS 功能。例如,我们可以避免自动翻转 background-position

输入
.test {
  background-position: 0 100%;
}
0 转换为 100%(默认)
输出
.test {
  background-position: 100% 100%;
}
设置插件以避免翻转
const options = {
  processDeclarationPlugins: [
    {
      name: 'avoid-flipping-background',
      priority: 99, // 高于核心 RTLCSS 插件的优先值 100
      processors: [
        {
          expr: /(background|object)(-position(-x)?|-image)?$/i,
          action: (prop, value) => ({ prop, value })
        }
      ]
    }
  ]
}
输出
.test {
  background-position: 0 100%;
}

控制指令

控制指令放置在规则或声明之间。它们可以针对单个节点或一组节点。

[!重要] 块指令(以 begin 开始和以 end 结束的那些)应该放置在规则外部以应用到多个规则,或放置在规则内部以应用到多个声明。您不应将指令的开始放置在规则外部,将结束放置在内部(或反之),否则将得到不期望的结果。

| 指令 | 描述 | | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | | /*rtl:ignore*/ | 忽略处理以下规则或声明 | | /*rtl:begin:ignore*/ | 开始忽略块 | | /*rtl:end:ignore*/ | 结束忽略块 | | /*rtl:freeze*/ | 在当前方向冻结规则或声明 | | /*rtl:begin:freeze*/ | 开始冻结块 | | /*rtl:end:freeze*/ | 结束冻结块 | | /*rtl:urls*/ | 此指令在下一个声明或下一个规则的声明中将 processUrls 选项设置为 true,不管全局 processUrls 选项的值如何 | | /*rtl:begin:urls*/ | 开始 processUrls 块 | | /*rtl:end:urls*/ | 结束 processUrls 块 | | /*rtl:rules*/ | 此指令在下一个规则中将 processRuleNames 选项设置为 true,不管全局 processRuleNames 选项的值如何 | | /*rtl:begin:rules*/ | 开始 processRuleNames 块 | | /*rtl:end:rules*/ | 结束 processRuleNames 块 | | /*rtl:source:{source}*/ | 设置规则或声明的源,不管 source 属性的值如何 | | /*rtl:begin:source:{source}*/ | 开始源块 | | /*rtl:end:source*/ | 结束源块 | | /*rtl:raw:{CSS}*/ | 解析 CSS 参数并插入到其位置。根据 source 参数,解析的 CSS 将被视为 rtlltr |


/*rtl:ignore*/

这个指令忽略了对以下规则或声明的处理。在下一个块中,整个声明将被忽略。

输入
/*rtl:ignore*/
.test1,
.test2 {
  text-align: left;
  left: 10px;
}
输出
.test1,
.test2 {
  text-align: left;
  left: 10px;
}

在下一个块中,只有 left 属性将被忽略:

输入
.test3,
.test4 {
  text-align: left;
  /*rtl:ignore*/
  left: 10px;
}
输出
.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*/ 和 /*rtl:end:ignore*/

这两个指令应该一起使用,它们将提供忽略规则或声明的开始和结束。

[!注意] 插入在这些块之间的指令将被忽略,并保留在最终输出中。

忽略多个规则:

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

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

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

忽略多个声明:

输入
.test1,
.test2 {
  left: 10px;
  /*rtl:begin:ignore*/
  margin-left: 4em;
  padding: 1px 2px 3px 4px;
  /*rtl:end:ignore*/
  text-align: left;
}
输出
.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:freeze*/

[!重要] 此指令仅在 combined 模式下工作。如果您在 overridediff 模式下使用它,它将被忽略。

这个指令在当前方向冻结规则或声明,但对 counterpart direction 如果有可翻转声明则不采取任何操作。当用于规则时,即使它不包含可翻转声明,也会在当前方向冻结它。当它用于声明时,即使它不可翻转,也会在当前方向冻结该声明。

输入
/*rtl:freeze*/
.test1,
.test2 {
  color: red;
  text-align: left;
  left: 10px;
}

.test3 {
  /*rtl:freeze*/
  text-align: center;
  /*rtl:freeze*/
  padding: 10px 20px 30px 40px;
  margin: 1px 2px 3px 4px;
}
输出
[dir='ltr'] .test1,
[dir='ltr'] .test2 {
  color: red;
  text-align: left;
  left: 10px;
}

[dir='ltr'] .test3 {
  text-align: center;
  padding: 10px 40px 30px 20px;
  margin: 1px 4px 3px 2px;
}

[dir='rtl'] .test3 {
  margin: 1px 4px 3px 2px;
}

/*rtl:begin:freeze*/ 和 /*rtl:end:freeze*/

[!重要] 此指令仅在 combined 模式下工作。如果您在 overridediff 模式下使用它,它将被忽略。

这两个指令应该一起使用,它们将提供冻结规则或声明的开始和结束。这些块之间的规则或声明,将即使没有涉及可翻转声明,也会在当前方向冻结。

冻结多个规则:

输入
/*rtl:begin:freeze*/
.test1,
.test2 {
  color: #fff;
  left: 10px;
  text-align: left;
}

.test3 {
  padding: 1px 2px 3px 4px;
}
/*rtl:end:freeze*/
输出
[dir='ltr'] .test1,
[dir='ltr'] .test2 {
  color: #fff;
  left: 10px;
  text-align: left;
}

[dir='ltr'] .test3 {
  padding: 1px 2px 3px 4px;
}

冻结多个声明:

输入
.test1,
.test2 {
  color: red;
  left: 10px;
  /*rtl:begin:freeze*/
  margin-left: 4em;
  padding: 1px 2px 3px 4px;
  /*rtl:end:freeze*/
  text-align: left;
}
输出
.test1,
.test2 {
  color: red;
}

[dir='ltr'] .test1,
[dir='ltr'] .test2 {
  left: 10px;
  margin-left: 4em;
  padding: 1px 2px 3px 4px;
  text-align: left;
}

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

/*rtl:urls*/

这个指令在下一个声明或下一个规则的声明中将 processUrls 选项设置为 true,不管全局 processUrls 选项的值如何:

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

.test2 {
  /*rtl:urls*/
  background-image: url('/icons/icon-left.png');
}
输出
[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*/ 和 /*rtl:end:urls*/

这两个指令应该一起使用,它们将提供 processUrls 块的开始和结束。

输入
/*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*/
}
输出
[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*/

这个指令在下一个规则中将 processRuleNames 选项设置为 true,不管全局 processRuleNames 选项的值如何:

输入
/*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';
}
输出
[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');
}

/* 这些选择器将不会被处理,因为只有其中一个有 rtl:rules 指令 */
.test2-left::before {
  content: '\f007';
}

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

/*rtl:begin:rules*/ 和 /*rtl:end:rules*/

这两个指令应该一起使用,它们将提供 processRuleNames 块的开始和结束。

输入
.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:end:rules*/
输出
.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}*/

这个指令设置规则或指令的源,忽略 source 属性的值:

输入
/*rtl:source:rtl*/
.test {
  color: #fff;
  border-left: 1px solid #666;
  padding: 10px 5px 10px 20px;
  text-align: left;
  width: 100%;
}
输出
.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}*/ 和 /*rtl:end:source*/

这两个指令应该一起使用,它们将提供规则或声明的源块的开始和结束:

输入
.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%;
}
输出
.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}*/

解析 CSS 参数并插入到其位置。根据 source 参数,解析的 CSS 将被视为 rtlltr

输入
.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);
}
*/
输出
.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);
}

值指令

值指令放置在声明值内的任何位置。它们针对包含的声明节点。

| 指令 | 描述 | | --------------------------------------------- | ------------------------------- | | /*rtl:ignore*/ | 忽略处理当前声明 | | /*rtl:append{value}*/ | 将 {value} 追加到声明值的末尾 | | /*rtl:insert:{value}*/ | 将 {value} 插入到指令所在位置 | | /*rtl:prepend:{value}*/ | 将 {value} 预置到声明值的开头 | | /*rtl:{value}*/ | 用 {value} 替换声明值 |


/*rtl:ignore*/

这个指令忽略了对当前声明的处理:

输入
.test1,
.test2 {
  text-align: left /*rtl:ignore*/;
  left: 10px;
}
输出
.test1,
.test2 {
  text-align: left;
}

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

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

/*rtl:append{value}*/

这个指令将 {value} 追加到声明值的末尾:

输入
.test1,
.test2 {
  padding: 10px /*rtl:append20px*/;
  left: 10px;
}
输出
[dir='ltr'] .test1,
[dir='ltr'] .test2 {
  padding: 10px;
  left: 10px;
}

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

/*rtl:insert:{value}*/

这个指令将 {value} 插入到指令所在位置的声明值内:

输入
.test1,
.test2 {
  padding: 10px /*rtl:insert 20px*/ 5px;
  left: 10px;
}
输出
[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}*/

这个指令将 {value} 预置到声明值的开头:

输入
.test1,
.test2 {
  font-family:
    Arial,
    Helvetica /*rtl:prepend:"Droid Arabic Kufi", */;
  left: 10px;
}
输出
[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}*/

这个指令用 {value} 替换声明值:

输入
.test1,
.test2 {
  font-family:
    Arial,
    Helvetica /*rtl:"Droid Arabic Kufi"*/;
  left: 10px;
}
输出
[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;
}

如果您不使用 PostCSS,请根据官方文档添加它并在此设置中配置此插件。