├── .editorconfig ├── .github └── workflows │ └── test.yml ├── .gitignore ├── .rollup.js ├── .tape.js ├── CHANGELOG.md ├── CONTRIBUTING.md ├── INSTALL.md ├── LICENSE.md ├── README.md ├── package.json ├── src ├── index.js └── lib │ ├── clone-decl.js │ ├── clone-rule.js │ ├── match-inset-prefix.js │ ├── match-side.js │ ├── match-size.js │ ├── reduce-values.js │ ├── split.js │ ├── transform-border-radius.js │ ├── transform-border.js │ ├── transform-directional-shorthands.js │ ├── transform-float.js │ ├── transform-inset.js │ ├── transform-resize.js │ ├── transform-side.js │ ├── transform-size.js │ ├── transform-text-align.js │ └── transform-transition.js └── test ├── border.css ├── border.expect.css ├── border.ltr.expect.css ├── border.preserve.expect.css ├── clear.css ├── clear.expect.css ├── clear.ltr.expect.css ├── float.css ├── float.expect.css ├── float.ltr.expect.css ├── inset.css ├── inset.expect.css ├── inset.ltr.expect.css ├── margin.css ├── margin.expect.css ├── margin.ltr.expect.css ├── padding.css ├── padding.expect.css ├── padding.ltr.expect.css ├── resize.css ├── resize.expect.css ├── size.css ├── size.expect.css ├── size.preserve.expect.css ├── text-align.css ├── text-align.expect.css ├── text-align.ltr.expect.css ├── transition.css ├── transition.expect.css ├── transition.ltr.expect.css └── transition.preserve.ltr.expect.css /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = tab 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | 13 | [*.{json,md,yml}] 14 | indent_size = 2 15 | indent_style = space 16 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | on: 3 | push: 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | node: [12, 16] 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions/setup-node@v2 14 | with: 15 | node-version: ${{ matrix.node }} 16 | 17 | - run: yarn install --ignore-scripts 18 | - run: yarn run test 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | index.*.* 3 | package-lock.json 4 | yarn.lock 5 | *.log* 6 | *.result.css 7 | .* 8 | !.editorconfig 9 | !.gitignore 10 | !.rollup.js 11 | !.tape.js 12 | !.github 13 | -------------------------------------------------------------------------------- /.rollup.js: -------------------------------------------------------------------------------- 1 | import babel from '@rollup/plugin-babel'; 2 | 3 | export default { 4 | input: 'src/index.js', 5 | output: [ 6 | { file: 'index.cjs.js', format: 'cjs', sourcemap: true, exports: 'default' }, 7 | { file: 'index.esm.mjs', format: 'esm', sourcemap: true, exports: 'default' } 8 | ], 9 | plugins: [ 10 | babel({ 11 | babelHelpers: 'bundled', 12 | presets: [ 13 | ['@babel/env', { modules: false, targets: { node: 10 } }] 14 | ] 15 | }) 16 | ] 17 | }; 18 | -------------------------------------------------------------------------------- /.tape.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'border': { 3 | message: 'supports logical "border" property values' 4 | }, 5 | 'border:ltr': { 6 | message: 'supports logical "border" property values with { dir: "ltr" }', 7 | options: { 8 | dir: 'ltr' 9 | } 10 | }, 11 | 'border:preserve': { 12 | message: 'supports logical "border" property values with { preserve: true }', 13 | options: { 14 | preserve: true 15 | } 16 | }, 17 | 'clear': { 18 | message: 'supports logical "clear" property values' 19 | }, 20 | 'clear:ltr': { 21 | message: 'supports logical "clear" property values with { dir: "ltr" }', 22 | options: { 23 | dir: 'ltr' 24 | } 25 | }, 26 | 'float': { 27 | message: 'supports logical "float" property values' 28 | }, 29 | 'float:ltr': { 30 | message: 'supports logical "float" property values with { dir: "ltr" }', 31 | options: { 32 | dir: 'ltr' 33 | } 34 | }, 35 | 'inset': { 36 | message: 'supports logical "inset" properties' 37 | }, 38 | 'inset:ltr': { 39 | message: 'supports logical "inset" properties with { dir: "ltr" }', 40 | options: { 41 | dir: 'ltr' 42 | } 43 | }, 44 | 'margin': { 45 | message: 'supports logical "margin" properties' 46 | }, 47 | 'margin:ltr': { 48 | message: 'supports logical "margin" properties with { dir: "ltr" }', 49 | options: { 50 | dir: 'ltr' 51 | } 52 | }, 53 | 'padding': { 54 | message: 'supports logical "padding" properties' 55 | }, 56 | 'padding:ltr': { 57 | message: 'supports logical "padding" properties with { dir: "ltr" }', 58 | options: { 59 | dir: 'ltr' 60 | } 61 | }, 62 | 'resize': { 63 | message: 'supports logical "resize" properties' 64 | }, 65 | 'size': { 66 | message: 'supports logical "size" properties' 67 | }, 68 | 'size:preserve': { 69 | message: 'supports logical "size" properties with { preserve: true }', 70 | options: { 71 | preserve: true 72 | } 73 | }, 74 | 'text-align': { 75 | message: 'supports logical "text-align" properties' 76 | }, 77 | 'text-align:ltr': { 78 | message: 'supports logical "text-align" properties with { dir: "ltr" }', 79 | options: { 80 | dir: 'ltr' 81 | } 82 | }, 83 | 'transition': { 84 | message: 'supports logical "transition" properties' 85 | }, 86 | 'transition:ltr': { 87 | message: 'supports logical "transition" properties with { dir: "ltr" }', 88 | options: { 89 | dir: 'ltr' 90 | } 91 | }, 92 | 'transition:preserve:ltr': { 93 | message: 'supports logical "transition" properties with { dir: "ltr", preserve: true }', 94 | options: { 95 | dir: 'ltr', 96 | preserve: true 97 | } 98 | } 99 | }; 100 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changes to PostCSS Logical Properties 2 | 3 | ### 5.0.0 (September 17, 2021) 4 | 5 | - Updated: Support for PostCS 8+ (major). 6 | - Updated: Support for Node 12+ (major). 7 | 8 | ### 4.0.2 (June 10, 2019) 9 | 10 | - Fixed: Restored transforms for `max-block-size`, `max-inline-size`, 11 | `min-block-size`, and `min-inline-size`. 12 | 13 | ### 4.0.1 (June 10, 2019) 14 | 15 | - Fixed: An issue with `block-size` and `inline-size` being miscalculated. 16 | 17 | ### 4.0.0 (June 5, 2019) 18 | 19 | - Added: Logical border-radius properties, which include 20 | `border-end-end-radius`, `border-end-start-radius`, `border-start-end-radius`, 21 | and `border-start-start-radius`. 22 | - Removed: All unknown logical properties, which include `border-end`, 23 | `border-end-color`, `border-end-style`, `border-end-width`, `border-start`, 24 | `border-start-color`, `border-start-style`, `border-start-width`, `inset-end`, 25 | `inset-start`, `margin-end`, `margin-start`, `padding-end`, `padding-start` 26 | and `border` with `logical`. 27 | - Updated: `transition` and `transition-property` to support the changes. 28 | - Updated: `postcss` to 7.0.16 (patch) 29 | - Updated: Node 8+ compatibility (major) 30 | 31 | ### 3.0.0 (September 20, 2018) 32 | 33 | - Added: Support for logical properties within `transition` and 34 | `transition-property`. 35 | - Changed: Physical rule fallbacks are written as full selectors rather than 36 | as nesting selectors. 37 | 38 | ### 2.0.0 (September 17, 2018) 39 | 40 | - Updated: Support for PostCSS v7+ 41 | - Updated: Support for Node v6+ 42 | 43 | ### 1.1.1 (March 21, 2017) 44 | 45 | - Fix `dir` option to allow falsey value 46 | 47 | ### 1.1.0 (March 20, 2017) 48 | 49 | - Add `preserve` option to preserve logical properties and values 50 | 51 | ### 1.0.2 (Aug 15, 2017) 52 | 53 | - Improve flow-relative clear support 54 | 55 | ### 1.0.1 (Aug 13, 2017) 56 | 57 | - Improve flow-relative border support 58 | 59 | ### 1.0.0 (Aug 8, 2017) 60 | 61 | - Initial version 62 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to PostCSS Logical Properties 2 | 3 | You want to help? You rock! Now, take a moment to be sure your contributions 4 | make sense to everyone else. 5 | 6 | ## Reporting Issues 7 | 8 | Found a problem? Want a new feature? 9 | 10 | - See if your issue or idea has [already been reported]. 11 | - Provide a [reduced test case] or a [live example]. 12 | 13 | Remember, a bug is a _demonstrable problem_ caused by _our_ code. 14 | 15 | ## Submitting Pull Requests 16 | 17 | Pull requests are the greatest contributions, so be sure they are focused in 18 | scope and avoid unrelated commits. 19 | 20 | 1. To begin; [fork this project], clone your fork, and add our upstream. 21 | ```bash 22 | # Clone your fork of the repo into the current directory 23 | git clone git@github.com:YOUR_USER/postcss-logical-properties.git 24 | 25 | # Navigate to the newly cloned directory 26 | cd postcss-logical-properties 27 | 28 | # Assign the original repo to a remote called "upstream" 29 | git remote add upstream git@github.com:csstools/postcss-logical-properties.git 30 | 31 | # Install the tools necessary for testing 32 | npm install 33 | ``` 34 | 35 | 2. Create a branch for your feature or fix: 36 | ```bash 37 | # Move into a new branch for your feature 38 | git checkout -b feature/thing 39 | ``` 40 | ```bash 41 | # Move into a new branch for your fix 42 | git checkout -b fix/something 43 | ``` 44 | 45 | 3. If your code follows our practices, then push your feature branch: 46 | ```bash 47 | # Test current code 48 | npm test 49 | ``` 50 | ```bash 51 | # Push the branch for your new feature 52 | git push origin feature/thing 53 | ``` 54 | ```bash 55 | # Or, push the branch for your update 56 | git push origin update/something 57 | ``` 58 | 59 | That’s it! Now [open a pull request] with a clear title and description. 60 | 61 | [already been reported]: issues 62 | [fork this project]: fork 63 | [live example]: https://codepen.io/pen 64 | [open a pull request]: https://help.github.com/articles/using-pull-requests/ 65 | [reduced test case]: https://css-tricks.com/reduced-test-cases/ 66 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | # Installing PostCSS 2 | 3 | [PostCSS Logical Properties and Values] runs in all Node environments, with 4 | special instructions for: 5 | 6 | | [Node](#node) | [PostCSS CLI](#postcss-cli) | [Webpack](#webpack) | [Create React App](#create-react-app) | [Gulp](#gulp) | [Grunt](#grunt) | 7 | | --- | --- | --- | --- | --- | --- | 8 | 9 | ## Node 10 | 11 | Add [PostCSS Logical Properties and Values] to your project: 12 | 13 | ```bash 14 | npm install postcss-logical --save-dev 15 | ``` 16 | 17 | Use [PostCSS Logical Properties and Values] to process your CSS: 18 | 19 | ```js 20 | const postcssLogical = require('postcss-logical'); 21 | 22 | postcssLogical.process(YOUR_CSS /*, processOptions, pluginOptions */); 23 | ``` 24 | 25 | Or use it as a [PostCSS] plugin: 26 | 27 | ```js 28 | const postcss = require('postcss'); 29 | const postcssLogical = require('postcss-logical'); 30 | 31 | postcss([ 32 | postcssLogical(/* pluginOptions */) 33 | ]).process(YOUR_CSS /*, processOptions */); 34 | ``` 35 | 36 | ## PostCSS CLI 37 | 38 | Add [PostCSS CLI] to your project: 39 | 40 | ```bash 41 | npm install postcss-cli --save-dev 42 | ``` 43 | 44 | Use [PostCSS Logical Properties and Values] in your `postcss.config.js` 45 | configuration file: 46 | 47 | ```js 48 | const postcssLogical = require('postcss-logical'); 49 | 50 | module.exports = { 51 | plugins: [ 52 | postcssLogical(/* pluginOptions */) 53 | ] 54 | } 55 | ``` 56 | 57 | ## Webpack 58 | 59 | Add [PostCSS Loader] to your project: 60 | 61 | ```bash 62 | npm install postcss-loader --save-dev 63 | ``` 64 | 65 | Use [PostCSS Logical Properties and Values] in your Webpack configuration: 66 | 67 | ```js 68 | const postcssLogical = require('postcss-logical'); 69 | 70 | module.exports = { 71 | module: { 72 | rules: [ 73 | { 74 | test: /\.css$/, 75 | use: [ 76 | 'style-loader', 77 | { loader: 'css-loader', options: { importLoaders: 1 } }, 78 | { loader: 'postcss-loader', options: { 79 | ident: 'postcss', 80 | plugins: () => [ 81 | postcssLogical(/* pluginOptions */) 82 | ] 83 | } } 84 | ] 85 | } 86 | ] 87 | } 88 | } 89 | ``` 90 | 91 | ## Create React App 92 | 93 | Add [React App Rewired] and [React App Rewire PostCSS] to your project: 94 | 95 | ```bash 96 | npm install react-app-rewired react-app-rewire-postcss --save-dev 97 | ``` 98 | 99 | Use [React App Rewire PostCSS] and [PostCSS Logical Properties and Values] in 100 | your `config-overrides.js` file: 101 | 102 | ```js 103 | const reactAppRewirePostcss = require('react-app-rewire-postcss'); 104 | const postcssLogical = require('postcss-logical'); 105 | 106 | module.exports = config => reactAppRewirePostcss(config, { 107 | plugins: () => [ 108 | postcssLogical(/* pluginOptions */) 109 | ] 110 | }); 111 | ``` 112 | 113 | ## Gulp 114 | 115 | Add [Gulp PostCSS] to your project: 116 | 117 | ```bash 118 | npm install gulp-postcss --save-dev 119 | ``` 120 | 121 | Use [PostCSS Logical Properties and Values] in your Gulpfile: 122 | 123 | ```js 124 | const postcss = require('gulp-postcss'); 125 | const postcssLogical = require('postcss-logical'); 126 | 127 | gulp.task('css', () => gulp.src('./src/*.css').pipe( 128 | postcss([ 129 | postcssLogical(/* pluginOptions */) 130 | ]) 131 | ).pipe( 132 | gulp.dest('.') 133 | )); 134 | ``` 135 | 136 | ## Grunt 137 | 138 | Add [Grunt PostCSS] to your project: 139 | 140 | ```bash 141 | npm install grunt-postcss --save-dev 142 | ``` 143 | 144 | Use [PostCSS Logical Properties and Values] in your Gruntfile: 145 | 146 | ```js 147 | const postcssLogical = require('postcss-logical'); 148 | 149 | grunt.loadNpmTasks('grunt-postcss'); 150 | 151 | grunt.initConfig({ 152 | postcss: { 153 | options: { 154 | use: [ 155 | postcssLogical(/* pluginOptions */) 156 | ] 157 | }, 158 | dist: { 159 | src: '*.css' 160 | } 161 | } 162 | }); 163 | ``` 164 | 165 | [Gulp PostCSS]: https://github.com/postcss/gulp-postcss 166 | [Grunt PostCSS]: https://github.com/nDmitry/grunt-postcss 167 | [PostCSS]: https://github.com/postcss/postcss 168 | [PostCSS CLI]: https://github.com/postcss/postcss-cli 169 | [PostCSS Loader]: https://github.com/postcss/postcss-loader 170 | [PostCSS Logical Properties and Values]: https://github.com/csstools/postcss-logical 171 | [React App Rewire PostCSS]: https://github.com/csstools/react-app-rewire-postcss 172 | [React App Rewired]: https://github.com/timarney/react-app-rewired 173 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # CC0 1.0 Universal 2 | 3 | ## Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an “owner”) of an original work of 8 | authorship and/or a database (each, a “Work”). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific works 12 | (“Commons”) that the public can reliably and without fear of later claims of 13 | infringement build upon, modify, incorporate in other works, reuse and 14 | redistribute as freely as possible in any form whatsoever and for any purposes, 15 | including without limitation commercial purposes. These owners may contribute 16 | to the Commons to promote the ideal of a free culture and the further 17 | production of creative, cultural and scientific works, or to gain reputation or 18 | greater distribution for their Work in part through the use and efforts of 19 | others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation of 22 | additional consideration or compensation, the person associating CC0 with a 23 | Work (the “Affirmer”), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and 25 | publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights (“Copyright and 31 | Related Rights”). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 1. the right to reproduce, adapt, distribute, perform, display, communicate, 34 | and translate a Work; 35 | 2. moral rights retained by the original author(s) and/or performer(s); 36 | 3. publicity and privacy rights pertaining to a person’s image or likeness 37 | depicted in a Work; 38 | 4. rights protecting against unfair competition in regards to a Work, 39 | subject to the limitations in paragraph 4(i), below; 40 | 5. rights protecting the extraction, dissemination, use and reuse of data in 41 | a Work; 42 | 6. database rights (such as those arising under Directive 96/9/EC of the 43 | European Parliament and of the Council of 11 March 1996 on the legal 44 | protection of databases, and under any national implementation thereof, 45 | including any amended or successor version of such directive); and 46 | 7. other similar, equivalent or corresponding rights throughout the world 47 | based on applicable law or treaty, and any national implementations 48 | thereof. 49 | 50 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 51 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 52 | unconditionally waives, abandons, and surrenders all of Affirmer’s Copyright 53 | and Related Rights and associated claims and causes of action, whether now 54 | known or unknown (including existing as well as future claims and causes of 55 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 56 | duration provided by applicable law or treaty (including future time 57 | extensions), (iii) in any current or future medium and for any number of 58 | copies, and (iv) for any purpose whatsoever, including without limitation 59 | commercial, advertising or promotional purposes (the “Waiver”). Affirmer 60 | makes the Waiver for the benefit of each member of the public at large and 61 | to the detriment of Affirmer’s heirs and successors, fully intending that 62 | such Waiver shall not be subject to revocation, rescission, cancellation, 63 | termination, or any other legal or equitable action to disrupt the quiet 64 | enjoyment of the Work by the public as contemplated by Affirmer’s express 65 | Statement of Purpose. 66 | 67 | 3. Public License Fallback. Should any part of the Waiver for any reason be 68 | judged legally invalid or ineffective under applicable law, then the Waiver 69 | shall be preserved to the maximum extent permitted taking into account 70 | Affirmer’s express Statement of Purpose. In addition, to the extent the 71 | Waiver is so judged Affirmer hereby grants to each affected person a 72 | royalty-free, non transferable, non sublicensable, non exclusive, 73 | irrevocable and unconditional license to exercise Affirmer’s Copyright and 74 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 75 | maximum duration provided by applicable law or treaty (including future time 76 | extensions), (iii) in any current or future medium and for any number of 77 | copies, and (iv) for any purpose whatsoever, including without limitation 78 | commercial, advertising or promotional purposes (the “License”). The License 79 | shall be deemed effective as of the date CC0 was applied by Affirmer to the 80 | Work. Should any part of the License for any reason be judged legally 81 | invalid or ineffective under applicable law, such partial invalidity or 82 | ineffectiveness shall not invalidate the remainder of the License, and in 83 | such case Affirmer hereby affirms that he or she will not (i) exercise any 84 | of his or her remaining Copyright and Related Rights in the Work or (ii) 85 | assert any associated claims and causes of action with respect to the Work, 86 | in either case contrary to Affirmer’s express Statement of Purpose. 87 | 88 | 4. Limitations and Disclaimers. 89 | 1. No trademark or patent rights held by Affirmer are waived, abandoned, 90 | surrendered, licensed or otherwise affected by this document. 91 | 2. Affirmer offers the Work as-is and makes no representations or warranties 92 | of any kind concerning the Work, express, implied, statutory or 93 | otherwise, including without limitation warranties of title, 94 | merchantability, fitness for a particular purpose, non infringement, or 95 | the absence of latent or other defects, accuracy, or the present or 96 | absence of errors, whether or not discoverable, all to the greatest 97 | extent permissible under applicable law. 98 | 3. Affirmer disclaims responsibility for clearing rights of other persons 99 | that may apply to the Work or any use thereof, including without 100 | limitation any person’s Copyright and Related Rights in the Work. 101 | Further, Affirmer disclaims responsibility for obtaining any necessary 102 | consents, permissions or other rights required for any use of the Work. 103 | 4. Affirmer understands and acknowledges that Creative Commons is not a 104 | party to this document and has no duty or obligation with respect to this 105 | CC0 or use of the Work. 106 | 107 | For more information, please see 108 | http://creativecommons.org/publicdomain/zero/1.0/. 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
⚠️ PostCSS Logical Properties and Values was moved to @csstools/postcss-plugins. ⚠️
2 | Read the announcement
3 | 4 | # PostCSS Logical Properties and Values [PostCSS Logo][postcss] 5 | 6 | [![NPM Version][npm-img]][npm-url] 7 | [![CSS Standard Status][css-img]][css-url] 8 | [![Build Status][cli-img]][cli-url] 9 | [![Support Chat][git-img]][git-url] 10 | 11 | [PostCSS Logical Properties and Values] lets you use logical, rather than 12 | physical, direction and dimension mappings in CSS, following the 13 | [CSS Logical Properties and Values] specification. 14 | 15 | [!['Can I use' table](https://caniuse.bitsofco.de/image/css-logical-props.png)](https://caniuse.com/#feat=css-logical-props) 16 | 17 | ```pcss 18 | .banner { 19 | color: #222222; 20 | inset: logical 0 5px 10px; 21 | padding-inline: 20px 40px; 22 | resize: block; 23 | transition: color 200ms; 24 | } 25 | 26 | /* becomes */ 27 | 28 | .banner:dir(ltr) { 29 | padding-left: 20px; padding-right: 40px; 30 | } 31 | 32 | .banner:dir(rtl) { 33 | padding-right: 20px; padding-left: 40px; 34 | } 35 | 36 | .banner { 37 | resize: vertical; 38 | transition: color 200ms; 39 | } 40 | 41 | /* or, when used with { dir: 'ltr' } */ 42 | 43 | .banner { 44 | color: #222222; 45 | top: 0; left: 5px; bottom: 10px; right: 5px; 46 | padding-left: 20px; padding-right: 40px; 47 | resize: vertical; 48 | transition: color 200ms; 49 | } 50 | 51 | /* or, when used with { preserve: true } */ 52 | 53 | .banner:dir(ltr) { 54 | padding-left: 20px; padding-right: 40px; 55 | } 56 | 57 | .banner:dir(rtl) { 58 | padding-right: 20px; padding-left: 40px; 59 | } 60 | 61 | .banner { 62 | color: #222222; 63 | top: 0; left: 5px; bottom: 10px; right: 5px; 64 | inset: logical 0 5px 10px; 65 | padding-inline: 20px 40px; 66 | resize: block; 67 | resize: vertical; 68 | transition: color 200ms; 69 | } 70 | ``` 71 | 72 | These shorthand properties set values for physical properties by default. 73 | Specifying the `logical` keyboard at the beginning of the property value will 74 | transform the flow-relative values afterward into both physical LTR and RTL 75 | properties: 76 | 77 | #### Logical Borders 78 | 79 | - `border`, `border-block`, `border-block-start`, `border-block-end`, 80 | `border-inline`, `border-inline-start`, `border-inline-end`, `border-start`, 81 | `border-end`, `border-color`, `border-block-color`, 82 | `border-block-start-color`, `border-block-end-color`, `border-inline-color`, 83 | `border-inline-start-color`, `border-inline-end-color`, `border-start-color`, 84 | `border-end-color`, `border-style`, `border-block-style`, 85 | `border-block-start-style`, `border-block-end-style`, `border-inline-style`, 86 | `border-inline-start-style`, `border-inline-end-style`, `border-start-style`, 87 | `border-end-style`, `border-width`, `border-block-width`, 88 | `border-block-start-width`, `border-block-end-width`, `border-inline-width`, 89 | `border-inline-start-width`, `border-inline-end-width`, `border-start-width`, 90 | `border-end-width`, `border-start-start-radius`, `border-start-end-radius`, 91 | `border-end-start-radius`, `border-end-end-radius` 92 | 93 | #### Logical Offsets 94 | 95 | - `inset`, `inset-block`, `inset-block-start`, `inset-block-end`, 96 | `inset-inline`, `inset-inline-start`, `inset-inline-end`, `inset-start`, 97 | `inset-end` 98 | 99 | #### Logical Margins 100 | 101 | - `margin`, `margin-block`, `margin-block-start`, `margin-block-end`, 102 | `margin-inline`, `margin-inline-start`, `margin-inline-end`, `margin-start`, 103 | `margin-end` 104 | 105 | #### Logical Paddings 106 | 107 | - `padding`, `padding-block`, `padding-block-start`, `padding-block-end`, 108 | `padding-inline`, `padding-inline-start`, `padding-inline-end`, 109 | `padding-start`, `padding-end` 110 | 111 | #### Logical Sizes 112 | 113 | - `block-size`, `max-block-size`, `min-block-size`, `inline-size`, `max-inline-size`, `min-inline-size` 114 | 115 | #### Flow-Relative Values 116 | 117 | - `clear: inline-start`, `clear: inline-end`, `float: inline-start`, 118 | `float: inline-end`, `text-align: start`, `text-align: end` 119 | 120 | --- 121 | 122 | By default, [PostCSS Logical Properties and Values] creates fallback selectors 123 | which require at least one `[dir]` attribute in your HTML. If you don’t have 124 | any `[dir]` attributes, consider using the following JavaScript: 125 | 126 | ```js 127 | // force at least one dir attribute (this can run at any time) 128 | document.documentElement.dir=document.documentElement.dir||'ltr'; 129 | ``` 130 | 131 | Otherwise, consider using the `dir` option to transform all logical properties 132 | and values to a specific direction. 133 | 134 | ```js 135 | require('postcss-logical')({ 136 | dir: 'ltr' 137 | }); 138 | ``` 139 | 140 | ## Usage 141 | 142 | Add [PostCSS Logical Properties and Values] to your project: 143 | 144 | ```bash 145 | npm install postcss-logical --save-dev 146 | ``` 147 | 148 | Use [PostCSS Logical Properties and Values] to process your CSS: 149 | 150 | ```js 151 | const postcssLogical = require('postcss-logical'); 152 | 153 | postcssLogical.process(YOUR_CSS /*, processOptions, pluginOptions */); 154 | ``` 155 | 156 | Or use it as a [PostCSS] plugin: 157 | 158 | ```js 159 | const postcss = require('postcss'); 160 | const postcssLogical = require('postcss-logical'); 161 | 162 | postcss([ 163 | postcssLogical(/* pluginOptions */) 164 | ]).process(YOUR_CSS /*, processOptions */); 165 | ``` 166 | 167 | [PostCSS Logical Properties and Values] runs in all Node environments, with 168 | special instructions for: 169 | 170 | | [Node](INSTALL.md#node) | [PostCSS CLI](INSTALL.md#postcss-cli) | [Webpack](INSTALL.md#webpack) | [Create React App](INSTALL.md#create-react-app) | [Gulp](INSTALL.md#gulp) | [Grunt](INSTALL.md#grunt) | 171 | | --- | --- | --- | --- | --- | --- | 172 | 173 | ## Options 174 | 175 | ### dir 176 | 177 | The `dir` option determines how directional fallbacks should be added to CSS. 178 | By default, fallbacks replace the logical declaration with nested `:dir` 179 | pseudo-classes. If `dir` is defined as `ltr` or `rtl` then only the left or 180 | right directional fallbacks will replace the logical declarations. If 181 | `preserve` is defined as `true`, then the `dir` option will be ignored. 182 | 183 | ### preserve 184 | 185 | The `preserve` option determines whether directional fallbacks should be added 186 | before logical declarations without replacing them. By default, directional 187 | fallbacks replace logical declaration. If `preserve` is defined as `true`, then 188 | the `dir` option will be ignored. 189 | 190 | [css-img]: https://cssdb.org/badge/logical-properties-and-values.svg 191 | [css-url]: https://cssdb.org/#logical-properties-and-values 192 | [cli-img]: https://github.com/csstools/postcss-logical/workflows/test/badge.svg 193 | [cli-url]: https://github.com/csstools/postcss-logical/actions/workflows/test.yml?query=workflow/test 194 | [git-img]: https://img.shields.io/badge/support-chat-blue.svg 195 | [git-url]: https://gitter.im/postcss/postcss 196 | [npm-img]: https://img.shields.io/npm/v/postcss-logical.svg 197 | [npm-url]: https://www.npmjs.com/package/postcss-logical 198 | 199 | [CSS Logical Properties and Values]: https://drafts.csswg.org/css-logical/ 200 | [Gulp PostCSS]: https://github.com/postcss/gulp-postcss 201 | [Grunt PostCSS]: https://github.com/nDmitry/grunt-postcss 202 | [PostCSS]: https://github.com/postcss/postcss 203 | [PostCSS Loader]: https://github.com/postcss/postcss-loader 204 | [PostCSS Logical Properties and Values]: https://github.com/csstools/postcss-logical 205 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postcss-logical", 3 | "version": "5.0.0", 4 | "description": "Use logical properties and values in CSS", 5 | "author": "Jonathan Neal ", 6 | "license": "CC0-1.0", 7 | "repository": "csstools/postcss-logical", 8 | "homepage": "https://github.com/csstools/postcss-logical#readme", 9 | "bugs": "https://github.com/csstools/postcss-logical/issues", 10 | "main": "index.cjs.js", 11 | "module": "index.esm.mjs", 12 | "files": [ 13 | "index.cjs.js", 14 | "index.cjs.js.map", 15 | "index.esm.mjs", 16 | "index.esm.mjs.map" 17 | ], 18 | "scripts": { 19 | "build": "rollup --config .rollup.js --silent", 20 | "prepublishOnly": "npm test", 21 | "pretest:tape": "npm run build", 22 | "test": "npm run test:js && npm run test:tape", 23 | "test:js": "eslint src/{*,**/*}.js --cache --ignore-path .gitignore --quiet", 24 | "test:tape": "postcss-tape" 25 | }, 26 | "engines": { 27 | "node": ">=12" 28 | }, 29 | "peerDependencies": { 30 | "postcss": "^8.3" 31 | }, 32 | "devDependencies": { 33 | "@babel/core": "7.15.5", 34 | "@babel/preset-env": "7.15.6", 35 | "@rollup/plugin-babel": "5.3.0", 36 | "eslint": "7.32.0", 37 | "postcss": "8.3.6", 38 | "postcss-tape": "6.0.1", 39 | "pre-commit": "1.2.2", 40 | "rollup": "2.56.3" 41 | }, 42 | "eslintConfig": { 43 | "env": { 44 | "browser": true, 45 | "es6": true, 46 | "node": true 47 | }, 48 | "extends": "eslint:recommended", 49 | "parserOptions": { 50 | "ecmaVersion": 2020, 51 | "impliedStrict": true, 52 | "sourceType": "module" 53 | }, 54 | "root": true 55 | }, 56 | "keywords": [ 57 | "postcss", 58 | "css", 59 | "postcss-plugin", 60 | "logical", 61 | "flow", 62 | "relative", 63 | "property", 64 | "properties", 65 | "values", 66 | "ltr", 67 | "rtl", 68 | "dir", 69 | "directions", 70 | "directional", 71 | "inline", 72 | "block", 73 | "start", 74 | "end", 75 | "align", 76 | "border", 77 | "clear", 78 | "float", 79 | "margin", 80 | "padding", 81 | "size", 82 | "text" 83 | ] 84 | } 85 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import transformBorder from './lib/transform-border'; 2 | import transformBorderRadius from './lib/transform-border-radius'; 3 | import transformDirectionalShorthands from './lib/transform-directional-shorthands'; 4 | import transformFloat from './lib/transform-float'; 5 | import transformInset from './lib/transform-inset'; 6 | import transformResize from './lib/transform-resize'; 7 | import transformSide from './lib/transform-side'; 8 | import transformSize from './lib/transform-size'; 9 | import transformTextAlign from './lib/transform-text-align'; 10 | import transformTransition from './lib/transform-transition'; 11 | import { splitBySpace } from './lib/split'; 12 | 13 | // plugin 14 | function postcssLogicalProperties(opts) { 15 | opts = Object(opts); 16 | 17 | const preserve = Boolean(opts.preserve); 18 | const dir = !preserve && typeof opts.dir === 'string' 19 | ? /^rtl$/i.test(opts.dir) 20 | ? 'rtl' 21 | : 'ltr' 22 | : false; 23 | 24 | const makeTransform = (transform) => { 25 | return (decl) => { 26 | const parent = decl.parent; 27 | const values = splitBySpace(decl.value, true); 28 | transform(decl, values, dir, preserve); 29 | if (!parent.nodes.length) { 30 | parent.remove(); 31 | } 32 | } 33 | } 34 | 35 | const makeTransformWithoutSplittingValues = (transform) => { 36 | return (decl) => { 37 | const parent = decl.parent; 38 | const values = [decl.value]; 39 | transform(decl, values, dir, preserve); 40 | if (!parent.nodes.length) { 41 | parent.remove(); 42 | } 43 | } 44 | } 45 | 46 | return { 47 | postcssPlugin: 'postcss-logical-properties', 48 | Declaration: { 49 | // Flow-Relative Values 50 | 'clear': makeTransform(transformFloat), 51 | 'float': makeTransform(transformFloat), 52 | 'resize': makeTransform(transformResize), 53 | 'text-align': makeTransform(transformTextAlign), 54 | 55 | // Logical Height and Logical Width 56 | 'block-size': makeTransform(transformSize), 57 | 'max-block-size': makeTransform(transformSize), 58 | 'min-block-size': makeTransform(transformSize), 59 | 'inline-size': makeTransform(transformSize), 60 | 'max-inline-size': makeTransform(transformSize), 61 | 'min-inline-size': makeTransform(transformSize), 62 | 63 | // Flow-relative Margins 64 | 'margin': makeTransform(transformDirectionalShorthands), 65 | 'margin-inline': makeTransform(transformSide['inline']), 66 | 'margin-inline-end': makeTransform(transformSide['inline-end']), 67 | 'margin-inline-start': makeTransform(transformSide['inline-start']), 68 | 'margin-block': makeTransform(transformSide['block']), 69 | 'margin-block-end': makeTransform(transformSide['block-end']), 70 | 'margin-block-start': makeTransform(transformSide['block-start']), 71 | 72 | // Flow-relative Offsets 73 | 'inset': makeTransform(transformInset), 74 | 'inset-inline': makeTransform(transformSide['inline']), 75 | 'inset-inline-end': makeTransform(transformSide['inline-end']), 76 | 'inset-inline-start': makeTransform(transformSide['inline-start']), 77 | 'inset-block': makeTransform(transformSide['block']), 78 | 'inset-block-end': makeTransform(transformSide['block-end']), 79 | 'inset-block-start': makeTransform(transformSide['block-start']), 80 | 81 | // Flow-relative Padding 82 | 'padding': makeTransform(transformDirectionalShorthands), 83 | 'padding-inline': makeTransform(transformSide['inline']), 84 | 'padding-inline-end': makeTransform(transformSide['inline-end']), 85 | 'padding-inline-start': makeTransform(transformSide['inline-start']), 86 | 'padding-block': makeTransform(transformSide['block']), 87 | 'padding-block-end': makeTransform(transformSide['block-end']), 88 | 'padding-block-start': makeTransform(transformSide['block-start']), 89 | 90 | // Flow-relative Borders 91 | 'border-block': makeTransformWithoutSplittingValues(transformBorder['border-block']), 92 | 'border-block-color': makeTransform(transformBorder['border-block']), 93 | 'border-block-style': makeTransform(transformBorder['border-block']), 94 | 'border-block-width': makeTransform(transformBorder['border-block']), 95 | 'border-block-end': makeTransformWithoutSplittingValues(transformBorder['border-block-end']), 96 | 'border-block-end-color': makeTransform(transformBorder['border-block-end']), 97 | 'border-block-end-style': makeTransform(transformBorder['border-block-end']), 98 | 'border-block-end-width': makeTransform(transformBorder['border-block-end']), 99 | 'border-block-start': makeTransformWithoutSplittingValues(transformBorder['border-block-start']), 100 | 'border-block-start-color': makeTransform(transformBorder['border-block-start']), 101 | 'border-block-start-style': makeTransform(transformBorder['border-block-start']), 102 | 'border-block-start-width': makeTransform(transformBorder['border-block-start']), 103 | 'border-inline': makeTransformWithoutSplittingValues(transformBorder['border-inline']), 104 | 'border-inline-color': makeTransform(transformBorder['border-inline']), 105 | 'border-inline-style': makeTransform(transformBorder['border-inline']), 106 | 'border-inline-width': makeTransform(transformBorder['border-inline']), 107 | 'border-inline-end': makeTransformWithoutSplittingValues(transformBorder['border-inline-end']), 108 | 'border-inline-end-color': makeTransform(transformBorder['border-inline-end']), 109 | 'border-inline-end-style': makeTransform(transformBorder['border-inline-end']), 110 | 'border-inline-end-width': makeTransform(transformBorder['border-inline-end']), 111 | 'border-inline-start': makeTransformWithoutSplittingValues(transformBorder['border-inline-start']), 112 | 'border-inline-start-color': makeTransform(transformBorder['border-inline-start']), 113 | 'border-inline-start-style': makeTransform(transformBorder['border-inline-start']), 114 | 'border-inline-start-width': makeTransform(transformBorder['border-inline-start']), 115 | 116 | // Flow-relative Corner Rounding 117 | 'border-end-end-radius': makeTransform(transformBorderRadius), 118 | 'border-end-start-radius': makeTransform(transformBorderRadius), 119 | 'border-start-end-radius': makeTransform(transformBorderRadius), 120 | 'border-start-start-radius': makeTransform(transformBorderRadius), 121 | 122 | // Four-Directional Shorthand Border Properties 123 | 'border-color': makeTransform(transformDirectionalShorthands), 124 | 'border-style': makeTransform(transformDirectionalShorthands), 125 | 'border-width': makeTransform(transformDirectionalShorthands), 126 | 127 | // Transition helpers 128 | 'transition': makeTransform(transformTransition), 129 | 'transition-property': makeTransform(transformTransition) 130 | } 131 | }; 132 | } 133 | postcssLogicalProperties.postcss = true; 134 | 135 | export default postcssLogicalProperties; 136 | -------------------------------------------------------------------------------- /src/lib/clone-decl.js: -------------------------------------------------------------------------------- 1 | import matchSide from './match-side'; 2 | import matchInsetPrefix from './match-inset-prefix'; 3 | 4 | export default (decl, suffix, value) => decl.cloneBefore({ 5 | prop: `${decl.prop.replace(matchSide, '$1')}${suffix}`.replace(matchInsetPrefix, ''), 6 | value 7 | }); 8 | -------------------------------------------------------------------------------- /src/lib/clone-rule.js: -------------------------------------------------------------------------------- 1 | import postcss from 'postcss'; 2 | 3 | export default (decl, dir) => { 4 | const rule = Object(decl.parent).type === 'rule' ? decl.parent.cloneBefore({ 5 | raws: {} 6 | }).removeAll() : postcss.rule({ selector: '&' }); 7 | 8 | rule.assign({'selectors': rule.selectors.map(selector => `${selector}:dir(${dir})`)}) 9 | 10 | return rule; 11 | }; 12 | -------------------------------------------------------------------------------- /src/lib/match-inset-prefix.js: -------------------------------------------------------------------------------- 1 | export default /^inset-/i; 2 | -------------------------------------------------------------------------------- /src/lib/match-side.js: -------------------------------------------------------------------------------- 1 | export default /^(inset|margin|padding)(?:-(block|block-start|block-end|inline|inline-start|inline-end|start|end))$/i; 2 | -------------------------------------------------------------------------------- /src/lib/match-size.js: -------------------------------------------------------------------------------- 1 | export default /^(min-|max-)?(block|inline)-(size)$/i; 2 | -------------------------------------------------------------------------------- /src/lib/reduce-values.js: -------------------------------------------------------------------------------- 1 | export default values => { 2 | const reducedValues = values.slice(); 3 | 4 | // reduce [A, B, C, B] to [A, B, C] 5 | if (reducedValues.length === 4 && reducedValues[3] === reducedValues[1]) { 6 | reducedValues.pop(); 7 | } 8 | 9 | // reduce [A, B, A] to [A, B] 10 | if (reducedValues.length === 3 && reducedValues[2] === reducedValues[0]) { 11 | reducedValues.pop(); 12 | } 13 | 14 | // reduce [A, A] to [A] 15 | if (reducedValues.length === 2 && reducedValues[1] === reducedValues[0]) { 16 | reducedValues.pop(); 17 | } 18 | 19 | return reducedValues; 20 | }; 21 | -------------------------------------------------------------------------------- /src/lib/split.js: -------------------------------------------------------------------------------- 1 | export function splitByComma(string, isTrimmed) { 2 | return splitByRegExp(string, /^,$/, isTrimmed); 3 | } 4 | 5 | export function splitBySpace(string, isTrimmed) { 6 | return splitByRegExp(string, /^\s$/, isTrimmed); 7 | } 8 | 9 | export function splitBySlash(string, isTrimmed) { 10 | return splitByRegExp(string, /^\/$/, isTrimmed); 11 | } 12 | 13 | function splitByRegExp(string, re, isTrimmed) { 14 | const array = []; 15 | let buffer = ''; 16 | let split = false; 17 | let func = 0; 18 | let i = -1; 19 | 20 | while (++i < string.length) { 21 | const char = string[i]; 22 | 23 | if (char === '(') { 24 | func += 1; 25 | } else if (char === ')') { 26 | if (func > 0) { 27 | func -= 1; 28 | } 29 | } else if (func === 0) { 30 | if (re.test(char)) { 31 | split = true; 32 | } 33 | } 34 | 35 | if (split) { 36 | if (!isTrimmed || buffer.trim()) { 37 | array.push(isTrimmed ? buffer.trim() : buffer); 38 | } 39 | 40 | if (!isTrimmed) { 41 | array.push(char); 42 | } 43 | 44 | buffer = ''; 45 | split = false; 46 | } else { 47 | buffer += char 48 | } 49 | } 50 | 51 | if (buffer !== '') { 52 | array.push(isTrimmed ? buffer.trim() : buffer); 53 | } 54 | 55 | return array; 56 | } 57 | -------------------------------------------------------------------------------- /src/lib/transform-border-radius.js: -------------------------------------------------------------------------------- 1 | import cloneRule from './clone-rule'; 2 | 3 | const logicalRadii = /^(border-)(end-end|end-start|start-end|start-start)(-radius)$/i; 4 | const ltrRadii = { 'end-end': 'bottom-right', 'end-start': 'bottom-left', 'start-end': 'top-right', 'start-start': 'top-left' }; 5 | const rtlRadii = { 'end-end': 'bottom-left', 'end-start': 'bottom-right', 'start-end': 'top-left', 'start-start': 'top-right' }; 6 | 7 | export default (decl, values, dir, preserve) => { 8 | if (dir === 'ltr') { 9 | lDecl(decl); 10 | clean(decl, preserve); 11 | return; 12 | } 13 | 14 | if (dir === 'rtl') { 15 | rDecl(decl); 16 | clean(decl, preserve); 17 | return; 18 | } 19 | 20 | cloneRule(decl, 'ltr').append(lDecl(decl)); 21 | cloneRule(decl, 'rtl').append(rDecl(decl)); 22 | clean(decl, preserve); 23 | }; 24 | 25 | function lDecl(decl) { 26 | return decl.cloneBefore({ 27 | prop: decl.prop.replace(logicalRadii, ($, prefix, direction, suffix) => `${prefix}${ltrRadii[direction]}${suffix}`) 28 | }); 29 | } 30 | 31 | function rDecl(decl) { 32 | return decl.cloneBefore({ 33 | prop: decl.prop.replace(logicalRadii, ($, prefix, direction, suffix) => `${prefix}${rtlRadii[direction]}${suffix}`) 34 | }); 35 | } 36 | 37 | function clean(decl, preserve) { 38 | if (!preserve) decl.remove(); 39 | } 40 | -------------------------------------------------------------------------------- /src/lib/transform-border.js: -------------------------------------------------------------------------------- 1 | import cloneRule from './clone-rule'; 2 | 3 | const matchLogicalBorderSide = /^border-(block|block-start|block-end|inline|inline-start|inline-end)(-(width|style|color))?$/i; 4 | 5 | export default { 6 | // border-block 7 | 'border-block': (decl, values, dir, preserve) => { 8 | decl.cloneBefore({ 9 | prop: `border-top${decl.prop.replace(matchLogicalBorderSide, '$2')}`, 10 | value: values[0] 11 | }); 12 | decl.cloneBefore({ 13 | prop: `border-bottom${decl.prop.replace(matchLogicalBorderSide, '$2')}`, 14 | value: values[1] || values[0] 15 | }); 16 | clean(decl, preserve); 17 | }, 18 | 19 | // border-block-start 20 | 'border-block-start': (decl, values, dir, preserve) => { 21 | decl.cloneBefore({ 22 | prop: `border-top${decl.prop.replace(matchLogicalBorderSide, '$2')}`, 23 | }); 24 | clean(decl, preserve); 25 | }, 26 | 27 | // border-block-end 28 | 'border-block-end': (decl, values, dir, preserve) => { 29 | decl.cloneBefore({ 30 | prop: `border-bottom${decl.prop.replace(matchLogicalBorderSide, '$2')}`, 31 | }); 32 | clean(decl, preserve); 33 | }, 34 | 35 | // border-inline 36 | 'border-inline': (decl, values, dir, preserve) => { 37 | const ltrDecls = () => { 38 | return [ 39 | decl.cloneBefore({ 40 | prop: `border-left${decl.prop.replace(matchLogicalBorderSide, '$2')}`, 41 | value: values[0] 42 | }), 43 | decl.cloneBefore({ 44 | prop: `border-right${decl.prop.replace(matchLogicalBorderSide, '$2')}`, 45 | value: values[1] || values[0] 46 | }) 47 | ]; 48 | }; 49 | 50 | const rtlDecls = () => { 51 | return [ 52 | decl.clone({ 53 | prop: `border-right${decl.prop.replace(matchLogicalBorderSide, '$2')}`, 54 | value: values[0] 55 | }), 56 | decl.clone({ 57 | prop: `border-left${decl.prop.replace(matchLogicalBorderSide, '$2')}`, 58 | value: values[1] || values[0] 59 | }) 60 | ]; 61 | }; 62 | 63 | const isLTR = 1 === values.length || 2 === values.length && values[0] === values[1]; 64 | if (isLTR || dir === 'ltr') { 65 | ltrDecls(); 66 | clean(decl, preserve); 67 | return; 68 | } else if (dir === 'rtl') { 69 | rtlDecls(); 70 | clean(decl, preserve); 71 | return; 72 | } else { 73 | cloneRule(decl, 'ltr').append(ltrDecls()); 74 | cloneRule(decl, 'rtl').append(rtlDecls()); 75 | clean(decl, preserve); 76 | return; 77 | } 78 | }, 79 | 80 | // border-inline-start 81 | 'border-inline-start': (decl, values, dir, preserve) => { 82 | const ltrDecl = () => { 83 | return decl.cloneBefore({ 84 | prop: `border-left${decl.prop.replace(matchLogicalBorderSide, '$2')}` 85 | }); 86 | }; 87 | 88 | const rtlDecl = () => { 89 | return decl.cloneBefore({ 90 | prop: `border-right${decl.prop.replace(matchLogicalBorderSide, '$2')}` 91 | }); 92 | }; 93 | 94 | if (dir === 'ltr') { 95 | ltrDecl(); 96 | clean(decl, preserve); 97 | return; 98 | } else if (dir === 'rtl') { 99 | rtlDecl(); 100 | clean(decl, preserve); 101 | return; 102 | } else { 103 | cloneRule(decl, 'ltr').append(ltrDecl()); 104 | cloneRule(decl, 'rtl').append(rtlDecl()); 105 | clean(decl, preserve); 106 | return; 107 | } 108 | }, 109 | 110 | // border-inline-end 111 | 'border-inline-end': (decl, values, dir, preserve) => { 112 | const ltrDecl = () => { 113 | return decl.cloneBefore({ 114 | prop: `border-right${decl.prop.replace(matchLogicalBorderSide, '$2')}` 115 | }); 116 | }; 117 | 118 | const rtlDecl = () => { 119 | return decl.cloneBefore({ 120 | prop: `border-left${decl.prop.replace(matchLogicalBorderSide, '$2')}` 121 | }); 122 | }; 123 | 124 | if (dir === 'ltr') { 125 | ltrDecl(); 126 | clean(decl, preserve); 127 | return; 128 | } else if (dir === 'rtl') { 129 | rtlDecl(); 130 | clean(decl, preserve); 131 | return; 132 | } else { 133 | cloneRule(decl, 'ltr').append(ltrDecl()); 134 | cloneRule(decl, 'rtl').append(rtlDecl()); 135 | clean(decl, preserve); 136 | return; 137 | } 138 | } 139 | }; 140 | 141 | function clean(decl, preserve) { 142 | if (!preserve) decl.remove(); 143 | } 144 | -------------------------------------------------------------------------------- /src/lib/transform-directional-shorthands.js: -------------------------------------------------------------------------------- 1 | import cloneRule from './clone-rule'; 2 | import reduceValues from './reduce-values'; 3 | 4 | export default (decl, values, dir, preserve) => { 5 | if ('logical' !== values[0]) { 6 | return null; 7 | } 8 | 9 | // get logical directions as all, inline, block-end, then inline-end 10 | const [, all, inline, blockEnd, inlineEnd ] = values; 11 | 12 | // get left-to-right relative directions from logical directions as: 13 | // → top from all 14 | // → right from inline-end, inline, or all 15 | // → bottom from block-end, block, or all 16 | // → left from inline, or all 17 | const ltrValues = reduceValues([ 18 | all, 19 | inlineEnd || inline || all, 20 | blockEnd || all, 21 | inline || all 22 | ]); 23 | 24 | const ltrDecl = () => { 25 | return decl.cloneBefore({ 26 | value: ltrValues.join(' ') 27 | }); 28 | }; 29 | 30 | // return the ltr values if the values are flow agnostic (where no second inline value was needed) 31 | const isFlowAgnostic = ltrValues.length < 4; 32 | 33 | if (isFlowAgnostic || dir === 'ltr') { 34 | ltrDecl(); 35 | clean(decl, preserve); 36 | return; 37 | } 38 | 39 | // get right-to-left relative directions from logical directions as: 40 | // → top from all 41 | // → right from inline, or all 42 | // → bottom from block-end, block, or all 43 | // → left from inline-end, inline, or all 44 | const rtlValues = reduceValues([ 45 | all, 46 | inline || all, 47 | blockEnd || all, 48 | inlineEnd || inline || all 49 | ]); 50 | 51 | const rtlDecl = () => { 52 | return decl.cloneBefore({ 53 | value: rtlValues.join(' ') 54 | }); 55 | }; 56 | 57 | if (dir === 'rtl') { 58 | rtlDecl(); 59 | clean(decl, preserve); 60 | return; 61 | } 62 | 63 | cloneRule(decl, 'ltr').append(ltrDecl()); 64 | cloneRule(decl, 'rtl').append(rtlDecl()); 65 | clean(decl, preserve); 66 | } 67 | 68 | function clean(decl, preserve) { 69 | if (!preserve) decl.remove(); 70 | } 71 | -------------------------------------------------------------------------------- /src/lib/transform-float.js: -------------------------------------------------------------------------------- 1 | import cloneRule from './clone-rule'; 2 | 3 | export default (decl, values, dir, preserve) => { 4 | if (/^inline-start$/i.test(decl.value)) { 5 | if (dir === 'ltr') { 6 | lDecl(decl); 7 | clean(decl, preserve); 8 | return; 9 | } else if (dir === 'rtl') { 10 | rDecl(decl); 11 | clean(decl, preserve); 12 | return; 13 | } else { 14 | cloneRule(decl, 'ltr').append(lDecl(decl)); 15 | cloneRule(decl, 'rtl').append(rDecl(decl)); 16 | clean(decl, preserve); 17 | return; 18 | } 19 | } if (/^inline-end$/i.test(decl.value)) { 20 | if (dir === 'ltr') { 21 | rDecl(decl); 22 | clean(decl, preserve); 23 | return; 24 | } else if (dir === 'rtl') { 25 | lDecl(decl); 26 | clean(decl, preserve); 27 | return; 28 | } else { 29 | cloneRule(decl, 'ltr').append(rDecl(decl)); 30 | cloneRule(decl, 'rtl').append(lDecl(decl)); 31 | clean(decl, preserve); 32 | return; 33 | } 34 | } 35 | }; 36 | 37 | function lDecl(decl) { 38 | return decl.cloneBefore({ value: 'left' }); 39 | } 40 | 41 | function rDecl(decl) { 42 | return decl.cloneBefore({ value: 'right' }); 43 | } 44 | 45 | function clean(decl, preserve) { 46 | if (!preserve) decl.remove(); 47 | } 48 | -------------------------------------------------------------------------------- /src/lib/transform-inset.js: -------------------------------------------------------------------------------- 1 | import cloneRule from './clone-rule'; 2 | 3 | export default (decl, values, dir, preserve) => { 4 | if ('logical' !== values[0]) { 5 | decl.cloneBefore({ prop: 'top', value: values[0] }); 6 | decl.cloneBefore({ prop: 'right', value: values[1] || values[0] }); 7 | decl.cloneBefore({ prop: 'bottom', value: values[2] || values[0] }); 8 | decl.cloneBefore({ prop: 'left', value: values[3] || values[1] || values[0] }); 9 | clean(decl, preserve); 10 | return; 11 | } 12 | 13 | const isLTR = !values[4] || values[4] === values[2]; 14 | 15 | if (isLTR || dir === 'ltr') { 16 | lDecl(decl, values); 17 | clean(decl, preserve); 18 | return; 19 | } else if (dir === 'rtl') { 20 | rDecl(decl, values); 21 | clean(decl, preserve); 22 | return; 23 | } else { 24 | cloneRule(decl, 'ltr').append(lDecl(decl, values)); 25 | cloneRule(decl, 'rtl').append(rDecl(decl, values)); 26 | clean(decl, preserve); 27 | return; 28 | } 29 | } 30 | 31 | function lDecl(decl, values) { 32 | return [ 33 | decl.cloneBefore({ prop: 'top', value: values[1] }), 34 | decl.cloneBefore({ prop: 'left', value: values[2] || values[1] }), 35 | decl.cloneBefore({ prop: 'bottom', value: values[3] || values[1] }), 36 | decl.cloneBefore({ prop: 'right', value: values[4] || values[2] || values[1] }) 37 | ]; 38 | } 39 | 40 | function rDecl(decl, values) { 41 | return [ 42 | decl.cloneBefore({ prop: 'top', value: values[1] }), 43 | decl.cloneBefore({ prop: 'right', value: values[2] || values[1] }), 44 | decl.cloneBefore({ prop: 'bottom', value: values[3] || values[1] }), 45 | decl.cloneBefore({ prop: 'left', value: values[4] || values[2] || values[1] }) 46 | ]; 47 | } 48 | 49 | function clean(decl, preserve) { 50 | if (!preserve) decl.remove(); 51 | } 52 | -------------------------------------------------------------------------------- /src/lib/transform-resize.js: -------------------------------------------------------------------------------- 1 | export default (decl, values, dir, preserve) => { 2 | if (/^block$/i.test(decl.value)) { 3 | decl.cloneBefore({ value: 'vertical' }); 4 | clean(decl, preserve); 5 | return; 6 | } else if (/^inline$/i.test(decl.value)) { 7 | decl.cloneBefore({ value: 'horizontal' }); 8 | clean(decl, preserve); 9 | return; 10 | } 11 | } 12 | 13 | function clean(decl, preserve) { 14 | if (!preserve) decl.remove(); 15 | } 16 | -------------------------------------------------------------------------------- /src/lib/transform-side.js: -------------------------------------------------------------------------------- 1 | import cloneDeclBefore from './clone-decl'; 2 | import cloneRule from './clone-rule'; 3 | import matchSide from './match-side'; 4 | 5 | import matchInsetPrefix from './match-inset-prefix'; 6 | 7 | export default { 8 | // inset-block, margin-block, padding-block 9 | 'block': (decl, values, dir, preserve) => { 10 | cloneDeclBefore(decl, '-top', values[0]); 11 | cloneDeclBefore(decl, '-bottom', values[1] || values[0]); 12 | clean(decl, preserve); 13 | }, 14 | 15 | // inset-block-start, margin-block-start, padding-block-start 16 | 'block-start': (decl, values, dir, preserve) => { 17 | decl.cloneBefore({ prop: decl.prop.replace(matchSide, '$1-top').replace(matchInsetPrefix, '') }); 18 | clean(decl, preserve); 19 | }, 20 | 21 | // inset-block-end, margin-block-end, padding-block-end 22 | 'block-end': (decl, values, dir, preserve) => { 23 | decl.cloneBefore({ prop: decl.prop.replace(matchSide, '$1-bottom').replace(matchInsetPrefix, '') }); 24 | clean(decl, preserve); 25 | }, 26 | 27 | // inset-inline, margin-inline, padding-inline 28 | 'inline': (decl, values, dir, preserve) => { 29 | const ltrDecls = () => { 30 | return [ 31 | cloneDeclBefore(decl, '-left', values[0]), 32 | cloneDeclBefore(decl, '-right', values[1] || values[0]) 33 | ]; 34 | }; 35 | 36 | const rtlDecls = () => { 37 | return [ 38 | cloneDeclBefore(decl, '-right', values[0]), 39 | cloneDeclBefore(decl, '-left', values[1] || values[0]) 40 | ]; 41 | }; 42 | 43 | const isLTR = 1 === values.length || 2 === values.length && values[0] === values[1]; 44 | if (isLTR || dir === 'ltr') { 45 | ltrDecls(); 46 | clean(decl, preserve); 47 | return; 48 | } else if (dir === 'rtl') { 49 | rtlDecls(); 50 | clean(decl, preserve); 51 | return; 52 | } else { 53 | cloneRule(decl, 'ltr').append(ltrDecls()); 54 | cloneRule(decl, 'rtl').append(rtlDecls()); 55 | clean(decl, preserve); 56 | return; 57 | } 58 | }, 59 | 60 | // inset-inline-start, margin-inline-start, padding-inline-start 61 | 'inline-start': (decl, values, dir, preserve) => { 62 | const ltrDecl = () => { 63 | return cloneDeclBefore(decl, '-left', decl.value); 64 | }; 65 | const rtlDecl = () => { 66 | return cloneDeclBefore(decl, '-right', decl.value); 67 | }; 68 | 69 | if (dir === 'ltr') { 70 | ltrDecl(); 71 | clean(decl, preserve); 72 | return; 73 | } else if (dir === 'rtl') { 74 | rtlDecl(); 75 | clean(decl, preserve); 76 | return; 77 | } else { 78 | cloneRule(decl, 'ltr').append(ltrDecl()); 79 | cloneRule(decl, 'rtl').append(rtlDecl()); 80 | clean(decl, preserve); 81 | return; 82 | } 83 | }, 84 | 85 | // inset-inline-end, margin-inline-end, padding-inline-end 86 | 'inline-end': (decl, values, dir, preserve) => { 87 | const ltrDecl = () => { 88 | return cloneDeclBefore(decl, '-right', decl.value); 89 | }; 90 | const rtlDecl = () => { 91 | return cloneDeclBefore(decl, '-left', decl.value); 92 | }; 93 | 94 | if (dir === 'ltr') { 95 | ltrDecl(); 96 | clean(decl, preserve); 97 | return; 98 | } else if (dir === 'rtl') { 99 | rtlDecl(); 100 | clean(decl, preserve); 101 | return; 102 | } else { 103 | cloneRule(decl, 'ltr').append(ltrDecl()); 104 | cloneRule(decl, 'rtl').append(rtlDecl()); 105 | clean(decl, preserve); 106 | return; 107 | } 108 | } 109 | } 110 | 111 | function clean(decl, preserve) { 112 | if (!preserve) decl.remove(); 113 | } 114 | -------------------------------------------------------------------------------- /src/lib/transform-size.js: -------------------------------------------------------------------------------- 1 | import matchSize from './match-size'; 2 | 3 | export default (decl, values, dir, preserve) => { 4 | decl.cloneBefore({ 5 | prop: decl.prop.replace( 6 | matchSize, 7 | ($0, minmax, flow) => `${minmax || ''}${'block' === flow ? 'height' : 'width'}` 8 | ) 9 | }); 10 | 11 | if (!preserve) { 12 | decl.remove(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/lib/transform-text-align.js: -------------------------------------------------------------------------------- 1 | import cloneRule from './clone-rule'; 2 | 3 | export default (decl, values, dir, preserve) => { 4 | if (/^start$/i.test(decl.value)) { 5 | if (dir === 'ltr') { 6 | lDecl(decl); 7 | clean(decl, preserve); 8 | return; 9 | } else if (dir === 'rtl') { 10 | rDecl(decl); 11 | clean(decl, preserve); 12 | return; 13 | } else { 14 | cloneRule(decl, 'ltr').append(lDecl(decl)); 15 | cloneRule(decl, 'rtl').append(rDecl(decl)); 16 | clean(decl, preserve); 17 | return; 18 | } 19 | } else if (/^end$/i.test(decl.value)) { 20 | if (dir === 'ltr') { 21 | rDecl(decl); 22 | clean(decl, preserve); 23 | return; 24 | } else if (dir === 'rtl') { 25 | lDecl(decl); 26 | clean(decl, preserve); 27 | return; 28 | } else { 29 | cloneRule(decl, 'ltr').append(rDecl(decl)); 30 | cloneRule(decl, 'rtl').append(lDecl(decl)); 31 | clean(decl, preserve); 32 | return; 33 | } 34 | } 35 | }; 36 | 37 | function lDecl(decl) { 38 | return decl.cloneBefore({ value: 'left' }); 39 | } 40 | 41 | function rDecl(decl) { 42 | return decl.cloneBefore({ value: 'right' }); 43 | } 44 | 45 | function clean(decl, preserve) { 46 | if (!preserve) decl.remove(); 47 | } 48 | -------------------------------------------------------------------------------- /src/lib/transform-transition.js: -------------------------------------------------------------------------------- 1 | import cloneRule from './clone-rule'; 2 | import { splitByComma, splitBySpace } from './split'; 3 | 4 | export default (decl, notValues, dir, preserve) => { 5 | const ltrValues = []; 6 | const rtlValues = []; 7 | 8 | splitByComma(decl.value).forEach(value => { 9 | let hasBeenSplit = false; 10 | 11 | splitBySpace(value).forEach((word, index, words) => { 12 | if (word in valueMap) { 13 | hasBeenSplit = true; 14 | 15 | valueMap[word].ltr.forEach(replacement => { 16 | const clone = words.slice(); 17 | 18 | clone.splice(index, 1, replacement); 19 | 20 | if (ltrValues.length && !/^,$/.test(ltrValues[ltrValues.length - 1])) { 21 | ltrValues.push(','); 22 | } 23 | 24 | ltrValues.push(clone.join('')); 25 | }); 26 | 27 | valueMap[word].rtl.forEach(replacement => { 28 | const clone = words.slice(); 29 | 30 | clone.splice(index, 1, replacement); 31 | 32 | if (rtlValues.length && !/^,$/.test(rtlValues[rtlValues.length - 1])) { 33 | rtlValues.push(','); 34 | } 35 | 36 | rtlValues.push(clone.join('')); 37 | }); 38 | } 39 | }); 40 | 41 | if (!hasBeenSplit) { 42 | ltrValues.push(value); 43 | rtlValues.push(value); 44 | } 45 | }); 46 | 47 | if (ltrValues.length && dir === 'ltr') { 48 | if (preserve) { 49 | decl.cloneBefore({}); 50 | } 51 | decl.assign({ value: ltrValues.join('') }); 52 | return; 53 | } else if (rtlValues.length && dir === 'rtl') { 54 | if (preserve) { 55 | decl.cloneBefore({}); 56 | } 57 | decl.assign({ value: rtlValues.join('') }); 58 | return; 59 | } else if (ltrValues.join('') !== rtlValues.join('')) { 60 | 61 | cloneRule(decl, 'ltr').append(decl.cloneBefore({ value: ltrValues.join('') })); 62 | cloneRule(decl, 'rtl').append(decl.cloneBefore({ value: rtlValues.join('') })); 63 | clean(decl, preserve); 64 | return; 65 | } 66 | }; 67 | 68 | function clean(decl, preserve) { 69 | if (!preserve) decl.remove(); 70 | } 71 | 72 | const valueMap = { 73 | // Logical Height and Logical Width 74 | 'block-size': { ltr: ['height'], rtl: ['height'] }, 75 | 'inline-size': { ltr: ['width'], rtl: ['width'] }, 76 | 77 | // Flow-relative Margins 78 | 'margin-block-end': { ltr: ['margin-bottom'], rtl: ['margin-bottom'] }, 79 | 'margin-block-start': { ltr: ['margin-top'], rtl: ['margin-top'] }, 80 | 'margin-block': { ltr: ['margin-top', 'margin-bottom'], rtl: ['margin-top', 'margin-bottom'] }, 81 | 'margin-inline-end': { ltr: ['margin-right'], rtl: ['margin-left'] }, 82 | 'margin-inline-start': { ltr: ['margin-left'], rtl: ['margin-right'] }, 83 | 'margin-inline': { ltr: ['margin-left', 'margin-right'], rtl: ['margin-left', 'margin-right'] }, 84 | 85 | // Flow-relative Offsets 86 | 'inset-block-end': { ltr: ['bottom'], rtl: ['bottom'] }, 87 | 'inset-block-start': { ltr: ['top'], rtl: ['top'] }, 88 | 'inset-block': { ltr: ['top', 'bottom'], rtl: ['top', 'bottom'] }, 89 | 'inset-inline-end': { ltr: ['right'], rtl: ['left'] }, 90 | 'inset-inline-start': { ltr: ['left'], rtl: ['right'] }, 91 | 'inset-inline': { ltr: ['left', 'right'], rtl: ['left', 'right'] }, 92 | 'inset': { ltr: ['top', 'right', 'bottom', 'left'], rtl: ['top', 'right', 'bottom', 'left'] }, 93 | 94 | // Flow-relative Padding 95 | 'padding-block-end': { ltr: ['padding-bottom'], rtl: ['padding-bottom'] }, 96 | 'padding-block-start': { ltr: ['padding-top'], rtl: ['padding-top'] }, 97 | 'padding-block': { ltr: ['padding-top', 'padding-bottom'], rtl: ['padding-top', 'padding-bottom'] }, 98 | 'padding-inline-end': { ltr: ['padding-right'], rtl: ['padding-left'] }, 99 | 'padding-inline-start': { ltr: ['padding-left'], rtl: ['padding-right'] }, 100 | 'padding-inline': { ltr: ['padding-left', 'padding-right'], rtl: ['padding-left', 'padding-right'] }, 101 | 102 | // Flow-relative Borders 103 | 'border-block-color': { ltr: ['border-top-color', 'border-bottom-color'], rtl: ['border-top-color', 'border-bottom-color'] }, 104 | 'border-block-end-color': { ltr: ['border-bottom-color'], rtl: ['border-bottom-color'] }, 105 | 'border-block-end-style': { ltr: ['border-bottom-style'], rtl: ['border-bottom-style'] }, 106 | 'border-block-end-width': { ltr: ['border-bottom-width'], rtl: ['border-bottom-width'] }, 107 | 'border-block-end': { ltr: ['border-bottom'], rtl: ['border-bottom'] }, 108 | 'border-block-start-color': { ltr: ['border-top-color'], rtl: ['border-top-color'] }, 109 | 'border-block-start-style': { ltr: ['border-top-style'], rtl: ['border-top-style'] }, 110 | 'border-block-start-width': { ltr: ['border-top-width'], rtl: ['border-top-width'] }, 111 | 'border-block-start': { ltr: ['border-top'], rtl: ['border-top'] }, 112 | 'border-block-style': { ltr: ['border-top-style', 'border-bottom-style'], rtl: ['border-top-style', 'border-bottom-style'] }, 113 | 'border-block-width': { ltr: ['border-top-width', 'border-bottom-width'], rtl: ['border-top-width', 'border-bottom-width'] }, 114 | 'border-block': { ltr: ['border-top', 'border-bottom'], rtl: ['border-top', 'border-bottom'] }, 115 | 'border-inline-color': { ltr: ['border-left-color', 'border-right-color'], rtl: ['border-left-color', 'border-right-color'] }, 116 | 'border-inline-end-color': { ltr: ['border-right-color'], rtl: ['border-left-color'] }, 117 | 'border-inline-end-style': { ltr: ['border-right-style'], rtl: ['border-left-style'] }, 118 | 'border-inline-end-width': { ltr: ['border-right-width'], rtl: ['border-left-width'] }, 119 | 'border-inline-end': { ltr: ['border-right'], rtl: ['border-left'] }, 120 | 'border-inline-start-color': { ltr: ['border-left-color'], rtl: ['border-right-color'] }, 121 | 'border-inline-start-style': { ltr: ['border-left-style'], rtl: ['border-right-style'] }, 122 | 'border-inline-start-width': { ltr: ['border-left-width'], rtl: ['border-right-width'] }, 123 | 'border-inline-start': { ltr: ['border-left'], rtl: ['border-right'] }, 124 | 'border-inline-style': { ltr: ['border-left-style', 'border-right-style'], rtl: ['border-left-style', 'border-right-style'] }, 125 | 'border-inline-width': { ltr: ['border-left-width', 'border-right-width'], rtl: ['border-left-width', 'border-right-width'] }, 126 | 'border-inline': { ltr: ['border-left', 'border-right'], rtl: ['border-left', 'border-right'] }, 127 | 128 | // Flow-relative Corner Rounding 129 | 'border-end-end-radius': { ltr: ['border-bottom-right-radius'], rtl: ['border-bottom-left-radius'] }, 130 | 'border-end-start-radius': { ltr: ['border-bottom-left-radius'], rtl: ['border-bottom-right-radius'] }, 131 | 'border-start-end-radius': { ltr: ['border-top-right-radius'], rtl: ['border-top-left-radius'] }, 132 | 'border-start-start-radius': { ltr: ['border-top-left-radius'], rtl: ['border-top-right-radius'] } 133 | }; 134 | -------------------------------------------------------------------------------- /test/border.css: -------------------------------------------------------------------------------- 1 | test-border { 2 | border: inherit; 3 | border-block: 1px solid black; 4 | border: inherit; 5 | border-block-start: 2px solid black; 6 | border: inherit; 7 | border-block-end: 3px solid black; 8 | border: inherit; 9 | border-inline: 4px solid black; 10 | border: inherit; 11 | } 12 | 13 | test-flowing-border { 14 | border: inherit; 15 | border-inline-start: 1px solid black; 16 | border: inherit; 17 | border-inline-end: 2px solid black; 18 | border: inherit; 19 | } 20 | 21 | test-border-color { 22 | border-color: inherit; 23 | border-color: logical red; 24 | border-color: inherit; 25 | border-color: logical orange yellow; 26 | border-color: inherit; 27 | border-color: logical green blue indigo; 28 | border-color: inherit; 29 | border-block-color: violet; 30 | border-color: inherit; 31 | border-block-start-color: white; 32 | border-color: inherit; 33 | border-block-end-color: black; 34 | border-color: inherit; 35 | border-inline-color: grey; 36 | border-color: inherit; 37 | } 38 | 39 | test-flowing-border-width { 40 | border-width: logical 1px 2px 3px 4px; 41 | border-width: inherit; 42 | border-inline-width: 2px 4px; 43 | border-width: inherit; 44 | border-inline-start-width: 2px; 45 | border-width: inherit; 46 | border-inline-end-width: 4px; 47 | border-width: inherit; 48 | border-block-width: 2px 4px; 49 | } 50 | 51 | test-flowing-border-radius { 52 | border-start-start-radius: 1px; 53 | border-start-end-radius: 2px; 54 | border-end-start-radius: 3px; 55 | border-end-end-radius: 4px; 56 | } 57 | -------------------------------------------------------------------------------- /test/border.expect.css: -------------------------------------------------------------------------------- 1 | test-border { 2 | border: inherit; 3 | border-top: 1px solid black; 4 | border-bottom: 1px solid black; 5 | border: inherit; 6 | border-top: 2px solid black; 7 | border: inherit; 8 | border-bottom: 3px solid black; 9 | border: inherit; 10 | border-left: 4px solid black; 11 | border-right: 4px solid black; 12 | border: inherit; 13 | } 14 | 15 | test-flowing-border:dir(ltr) { 16 | border-left: 1px solid black; 17 | } 18 | 19 | test-flowing-border:dir(rtl) { 20 | border-right: 1px solid black; 21 | } 22 | 23 | test-flowing-border:dir(ltr) { 24 | border-right: 2px solid black; 25 | } 26 | 27 | test-flowing-border:dir(rtl) { 28 | border-left: 2px solid black; 29 | } 30 | 31 | test-flowing-border { 32 | border: inherit; 33 | border: inherit; 34 | border: inherit; 35 | } 36 | 37 | test-border-color { 38 | border-color: inherit; 39 | border-color: red; 40 | border-color: inherit; 41 | border-color: orange yellow; 42 | border-color: inherit; 43 | border-color: green blue indigo; 44 | border-color: inherit; 45 | border-top-color: violet; 46 | border-bottom-color: violet; 47 | border-color: inherit; 48 | border-top-color: white; 49 | border-color: inherit; 50 | border-bottom-color: black; 51 | border-color: inherit; 52 | border-left-color: grey; 53 | border-right-color: grey; 54 | border-color: inherit; 55 | } 56 | 57 | test-flowing-border-width:dir(ltr) { 58 | border-width: 1px 4px 3px 2px; 59 | } 60 | 61 | test-flowing-border-width:dir(rtl) { 62 | border-width: 1px 2px 3px 4px; 63 | } 64 | 65 | test-flowing-border-width:dir(ltr) { 66 | border-left-width: 2px; 67 | border-right-width: 4px; 68 | } 69 | 70 | test-flowing-border-width:dir(rtl) { 71 | border-right-width: 2px; 72 | border-left-width: 4px; 73 | } 74 | 75 | test-flowing-border-width:dir(ltr) { 76 | border-left-width: 2px; 77 | } 78 | 79 | test-flowing-border-width:dir(rtl) { 80 | border-right-width: 2px; 81 | } 82 | 83 | test-flowing-border-width:dir(ltr) { 84 | border-right-width: 4px; 85 | } 86 | 87 | test-flowing-border-width:dir(rtl) { 88 | border-left-width: 4px; 89 | } 90 | 91 | test-flowing-border-width { 92 | border-width: inherit; 93 | border-width: inherit; 94 | border-width: inherit; 95 | border-width: inherit; 96 | border-top-width: 2px; 97 | border-bottom-width: 4px; 98 | } 99 | 100 | test-flowing-border-radius:dir(ltr) { 101 | border-top-left-radius: 1px; 102 | } 103 | 104 | test-flowing-border-radius:dir(rtl) { 105 | border-top-right-radius: 1px; 106 | } 107 | 108 | test-flowing-border-radius:dir(ltr) { 109 | border-top-right-radius: 2px; 110 | } 111 | 112 | test-flowing-border-radius:dir(rtl) { 113 | border-top-left-radius: 2px; 114 | } 115 | 116 | test-flowing-border-radius:dir(ltr) { 117 | border-bottom-left-radius: 3px; 118 | } 119 | 120 | test-flowing-border-radius:dir(rtl) { 121 | border-bottom-right-radius: 3px; 122 | } 123 | 124 | test-flowing-border-radius:dir(ltr) { 125 | border-bottom-right-radius: 4px; 126 | } 127 | 128 | test-flowing-border-radius:dir(rtl) { 129 | border-bottom-left-radius: 4px; 130 | } 131 | -------------------------------------------------------------------------------- /test/border.ltr.expect.css: -------------------------------------------------------------------------------- 1 | test-border { 2 | border: inherit; 3 | border-top: 1px solid black; 4 | border-bottom: 1px solid black; 5 | border: inherit; 6 | border-top: 2px solid black; 7 | border: inherit; 8 | border-bottom: 3px solid black; 9 | border: inherit; 10 | border-left: 4px solid black; 11 | border-right: 4px solid black; 12 | border: inherit; 13 | } 14 | 15 | test-flowing-border { 16 | border: inherit; 17 | border-left: 1px solid black; 18 | border: inherit; 19 | border-right: 2px solid black; 20 | border: inherit; 21 | } 22 | 23 | test-border-color { 24 | border-color: inherit; 25 | border-color: red; 26 | border-color: inherit; 27 | border-color: orange yellow; 28 | border-color: inherit; 29 | border-color: green blue indigo; 30 | border-color: inherit; 31 | border-top-color: violet; 32 | border-bottom-color: violet; 33 | border-color: inherit; 34 | border-top-color: white; 35 | border-color: inherit; 36 | border-bottom-color: black; 37 | border-color: inherit; 38 | border-left-color: grey; 39 | border-right-color: grey; 40 | border-color: inherit; 41 | } 42 | 43 | test-flowing-border-width { 44 | border-width: 1px 4px 3px 2px; 45 | border-width: inherit; 46 | border-left-width: 2px; 47 | border-right-width: 4px; 48 | border-width: inherit; 49 | border-left-width: 2px; 50 | border-width: inherit; 51 | border-right-width: 4px; 52 | border-width: inherit; 53 | border-top-width: 2px; 54 | border-bottom-width: 4px; 55 | } 56 | 57 | test-flowing-border-radius { 58 | border-top-left-radius: 1px; 59 | border-top-right-radius: 2px; 60 | border-bottom-left-radius: 3px; 61 | border-bottom-right-radius: 4px; 62 | } 63 | -------------------------------------------------------------------------------- /test/border.preserve.expect.css: -------------------------------------------------------------------------------- 1 | test-border { 2 | border: inherit; 3 | border-top: 1px solid black; 4 | border-bottom: 1px solid black; 5 | border-block: 1px solid black; 6 | border: inherit; 7 | border-top: 2px solid black; 8 | border-block-start: 2px solid black; 9 | border: inherit; 10 | border-bottom: 3px solid black; 11 | border-block-end: 3px solid black; 12 | border: inherit; 13 | border-left: 4px solid black; 14 | border-right: 4px solid black; 15 | border-inline: 4px solid black; 16 | border: inherit; 17 | } 18 | 19 | test-flowing-border:dir(ltr) { 20 | border-left: 1px solid black; 21 | } 22 | 23 | test-flowing-border:dir(rtl) { 24 | border-right: 1px solid black; 25 | } 26 | 27 | test-flowing-border:dir(ltr) { 28 | border-right: 2px solid black; 29 | } 30 | 31 | test-flowing-border:dir(rtl) { 32 | border-left: 2px solid black; 33 | } 34 | 35 | test-flowing-border { 36 | border: inherit; 37 | border-inline-start: 1px solid black; 38 | border: inherit; 39 | border-inline-end: 2px solid black; 40 | border: inherit; 41 | } 42 | 43 | test-border-color { 44 | border-color: inherit; 45 | border-color: red; 46 | border-color: logical red; 47 | border-color: inherit; 48 | border-color: orange yellow; 49 | border-color: logical orange yellow; 50 | border-color: inherit; 51 | border-color: green blue indigo; 52 | border-color: logical green blue indigo; 53 | border-color: inherit; 54 | border-top-color: violet; 55 | border-bottom-color: violet; 56 | border-block-color: violet; 57 | border-color: inherit; 58 | border-top-color: white; 59 | border-block-start-color: white; 60 | border-color: inherit; 61 | border-bottom-color: black; 62 | border-block-end-color: black; 63 | border-color: inherit; 64 | border-left-color: grey; 65 | border-right-color: grey; 66 | border-inline-color: grey; 67 | border-color: inherit; 68 | } 69 | 70 | test-flowing-border-width:dir(ltr) { 71 | border-width: 1px 4px 3px 2px; 72 | } 73 | 74 | test-flowing-border-width:dir(rtl) { 75 | border-width: 1px 2px 3px 4px; 76 | } 77 | 78 | test-flowing-border-width:dir(ltr) { 79 | border-left-width: 2px; 80 | border-right-width: 4px; 81 | } 82 | 83 | test-flowing-border-width:dir(rtl) { 84 | border-right-width: 2px; 85 | border-left-width: 4px; 86 | } 87 | 88 | test-flowing-border-width:dir(ltr) { 89 | border-left-width: 2px; 90 | } 91 | 92 | test-flowing-border-width:dir(rtl) { 93 | border-right-width: 2px; 94 | } 95 | 96 | test-flowing-border-width:dir(ltr) { 97 | border-right-width: 4px; 98 | } 99 | 100 | test-flowing-border-width:dir(rtl) { 101 | border-left-width: 4px; 102 | } 103 | 104 | test-flowing-border-width { 105 | border-width: logical 1px 2px 3px 4px; 106 | border-width: inherit; 107 | border-inline-width: 2px 4px; 108 | border-width: inherit; 109 | border-inline-start-width: 2px; 110 | border-width: inherit; 111 | border-inline-end-width: 4px; 112 | border-width: inherit; 113 | border-top-width: 2px; 114 | border-bottom-width: 4px; 115 | border-block-width: 2px 4px; 116 | } 117 | 118 | test-flowing-border-radius:dir(ltr) { 119 | border-top-left-radius: 1px; 120 | } 121 | 122 | test-flowing-border-radius:dir(rtl) { 123 | border-top-right-radius: 1px; 124 | } 125 | 126 | test-flowing-border-radius:dir(ltr) { 127 | border-top-right-radius: 2px; 128 | } 129 | 130 | test-flowing-border-radius:dir(rtl) { 131 | border-top-left-radius: 2px; 132 | } 133 | 134 | test-flowing-border-radius:dir(ltr) { 135 | border-bottom-left-radius: 3px; 136 | } 137 | 138 | test-flowing-border-radius:dir(rtl) { 139 | border-bottom-right-radius: 3px; 140 | } 141 | 142 | test-flowing-border-radius:dir(ltr) { 143 | border-bottom-right-radius: 4px; 144 | } 145 | 146 | test-flowing-border-radius:dir(rtl) { 147 | border-bottom-left-radius: 4px; 148 | } 149 | 150 | test-flowing-border-radius { 151 | border-start-start-radius: 1px; 152 | border-start-end-radius: 2px; 153 | border-end-start-radius: 3px; 154 | border-end-end-radius: 4px; 155 | } 156 | -------------------------------------------------------------------------------- /test/clear.css: -------------------------------------------------------------------------------- 1 | test-clear { 2 | clear: both; 3 | clear: left; 4 | clear: inline-start; 5 | clear: inline-end; 6 | clear: right; 7 | } 8 | -------------------------------------------------------------------------------- /test/clear.expect.css: -------------------------------------------------------------------------------- 1 | test-clear:dir(ltr) { 2 | clear: left; 3 | } 4 | test-clear:dir(rtl) { 5 | clear: right; 6 | } 7 | test-clear:dir(ltr) { 8 | clear: right; 9 | } 10 | test-clear:dir(rtl) { 11 | clear: left; 12 | } 13 | test-clear { 14 | clear: both; 15 | clear: left; 16 | clear: right; 17 | } 18 | -------------------------------------------------------------------------------- /test/clear.ltr.expect.css: -------------------------------------------------------------------------------- 1 | test-clear { 2 | clear: both; 3 | clear: left; 4 | clear: left; 5 | clear: right; 6 | clear: right; 7 | } 8 | -------------------------------------------------------------------------------- /test/float.css: -------------------------------------------------------------------------------- 1 | test-float { 2 | float: left; 3 | float: inline-start; 4 | float: inline-end; 5 | float: right; 6 | } 7 | -------------------------------------------------------------------------------- /test/float.expect.css: -------------------------------------------------------------------------------- 1 | test-float:dir(ltr) { 2 | float: left; 3 | } 4 | test-float:dir(rtl) { 5 | float: right; 6 | } 7 | test-float:dir(ltr) { 8 | float: right; 9 | } 10 | test-float:dir(rtl) { 11 | float: left; 12 | } 13 | test-float { 14 | float: left; 15 | float: right; 16 | } 17 | -------------------------------------------------------------------------------- /test/float.ltr.expect.css: -------------------------------------------------------------------------------- 1 | test-float { 2 | float: left; 3 | float: left; 4 | float: right; 5 | float: right; 6 | } 7 | -------------------------------------------------------------------------------- /test/inset.css: -------------------------------------------------------------------------------- 1 | test-inset { 2 | inset: 0; 3 | inset: logical 1px; 4 | inset: logical 2px 3px; 5 | inset: logical 4px 5px 6px; 6 | inset: logical 7px 8px 9px 8px; 7 | } 8 | 9 | test-flowing-inset { 10 | inset: auto; 11 | inset: logical 1px 2px 3px 4px; 12 | inset: auto; 13 | inset-inline: 5px 6px; 14 | inset: auto; 15 | inset-inline-start: 7px; 16 | inset: auto; 17 | inset-inline-end: 8px; 18 | inset: auto; 19 | } 20 | -------------------------------------------------------------------------------- /test/inset.expect.css: -------------------------------------------------------------------------------- 1 | test-inset { 2 | top: 0; 3 | right: 0; 4 | bottom: 0; 5 | left: 0; 6 | top: 1px; 7 | left: 1px; 8 | bottom: 1px; 9 | right: 1px; 10 | top: 2px; 11 | left: 3px; 12 | bottom: 2px; 13 | right: 3px; 14 | top: 4px; 15 | left: 5px; 16 | bottom: 6px; 17 | right: 5px; 18 | top: 7px; 19 | left: 8px; 20 | bottom: 9px; 21 | right: 8px; 22 | } 23 | 24 | test-flowing-inset:dir(ltr) { 25 | top: 1px; 26 | left: 2px; 27 | bottom: 3px; 28 | right: 4px; 29 | } 30 | 31 | test-flowing-inset:dir(rtl) { 32 | top: 1px; 33 | right: 2px; 34 | bottom: 3px; 35 | left: 4px; 36 | } 37 | 38 | test-flowing-inset:dir(ltr) { 39 | left: 5px; 40 | right: 6px; 41 | } 42 | 43 | test-flowing-inset:dir(rtl) { 44 | right: 5px; 45 | left: 6px; 46 | } 47 | 48 | test-flowing-inset:dir(ltr) { 49 | left: 7px; 50 | } 51 | 52 | test-flowing-inset:dir(rtl) { 53 | right: 7px; 54 | } 55 | 56 | test-flowing-inset:dir(ltr) { 57 | right: 8px; 58 | } 59 | 60 | test-flowing-inset:dir(rtl) { 61 | left: 8px; 62 | } 63 | 64 | test-flowing-inset { 65 | top: auto; 66 | right: auto; 67 | bottom: auto; 68 | left: auto; 69 | top: auto; 70 | right: auto; 71 | bottom: auto; 72 | left: auto; 73 | top: auto; 74 | right: auto; 75 | bottom: auto; 76 | left: auto; 77 | top: auto; 78 | right: auto; 79 | bottom: auto; 80 | left: auto; 81 | top: auto; 82 | right: auto; 83 | bottom: auto; 84 | left: auto; 85 | } 86 | -------------------------------------------------------------------------------- /test/inset.ltr.expect.css: -------------------------------------------------------------------------------- 1 | test-inset { 2 | top: 0; 3 | right: 0; 4 | bottom: 0; 5 | left: 0; 6 | top: 1px; 7 | left: 1px; 8 | bottom: 1px; 9 | right: 1px; 10 | top: 2px; 11 | left: 3px; 12 | bottom: 2px; 13 | right: 3px; 14 | top: 4px; 15 | left: 5px; 16 | bottom: 6px; 17 | right: 5px; 18 | top: 7px; 19 | left: 8px; 20 | bottom: 9px; 21 | right: 8px; 22 | } 23 | 24 | test-flowing-inset { 25 | top: auto; 26 | right: auto; 27 | bottom: auto; 28 | left: auto; 29 | top: 1px; 30 | left: 2px; 31 | bottom: 3px; 32 | right: 4px; 33 | top: auto; 34 | right: auto; 35 | bottom: auto; 36 | left: auto; 37 | left: 5px; 38 | right: 6px; 39 | top: auto; 40 | right: auto; 41 | bottom: auto; 42 | left: auto; 43 | left: 7px; 44 | top: auto; 45 | right: auto; 46 | bottom: auto; 47 | left: auto; 48 | right: 8px; 49 | top: auto; 50 | right: auto; 51 | bottom: auto; 52 | left: auto; 53 | } 54 | -------------------------------------------------------------------------------- /test/margin.css: -------------------------------------------------------------------------------- 1 | test-margin { 2 | margin: logical 1px; 3 | margin: logical 2px 3px; 4 | margin: logical 4px 5px 6px; 5 | margin: logical 7px 8px 9px 8px; 6 | margin-inline: 10px; 7 | margin-inline: 11px 11px; 8 | margin-block: 12px; 9 | margin-block: 13px 14px; 10 | margin-block-start: 15px; 11 | margin-block-end: 16px; 12 | } 13 | 14 | test-flowing-margin { 15 | margin: auto; 16 | margin: logical 1px 2px 3px 4px; 17 | margin: auto; 18 | margin-inline: 5px 6px; 19 | margin: auto; 20 | margin-inline-start: 7px; 21 | margin: auto; 22 | margin-inline-end: 8px; 23 | margin: auto; 24 | } 25 | -------------------------------------------------------------------------------- /test/margin.expect.css: -------------------------------------------------------------------------------- 1 | test-margin { 2 | margin: 1px; 3 | margin: 2px 3px; 4 | margin: 4px 5px 6px; 5 | margin: 7px 8px 9px; 6 | margin-left: 10px; 7 | margin-right: 10px; 8 | margin-left: 11px; 9 | margin-right: 11px; 10 | margin-top: 12px; 11 | margin-bottom: 12px; 12 | margin-top: 13px; 13 | margin-bottom: 14px; 14 | margin-top: 15px; 15 | margin-bottom: 16px; 16 | } 17 | 18 | test-flowing-margin:dir(ltr) { 19 | margin: 1px 4px 3px 2px; 20 | } 21 | 22 | test-flowing-margin:dir(rtl) { 23 | margin: 1px 2px 3px 4px; 24 | } 25 | 26 | test-flowing-margin:dir(ltr) { 27 | margin-left: 5px; 28 | margin-right: 6px; 29 | } 30 | 31 | test-flowing-margin:dir(rtl) { 32 | margin-right: 5px; 33 | margin-left: 6px; 34 | } 35 | 36 | test-flowing-margin:dir(ltr) { 37 | margin-left: 7px; 38 | } 39 | 40 | test-flowing-margin:dir(rtl) { 41 | margin-right: 7px; 42 | } 43 | 44 | test-flowing-margin:dir(ltr) { 45 | margin-right: 8px; 46 | } 47 | 48 | test-flowing-margin:dir(rtl) { 49 | margin-left: 8px; 50 | } 51 | 52 | test-flowing-margin { 53 | margin: auto; 54 | margin: auto; 55 | margin: auto; 56 | margin: auto; 57 | margin: auto; 58 | } 59 | -------------------------------------------------------------------------------- /test/margin.ltr.expect.css: -------------------------------------------------------------------------------- 1 | test-margin { 2 | margin: 1px; 3 | margin: 2px 3px; 4 | margin: 4px 5px 6px; 5 | margin: 7px 8px 9px; 6 | margin-left: 10px; 7 | margin-right: 10px; 8 | margin-left: 11px; 9 | margin-right: 11px; 10 | margin-top: 12px; 11 | margin-bottom: 12px; 12 | margin-top: 13px; 13 | margin-bottom: 14px; 14 | margin-top: 15px; 15 | margin-bottom: 16px; 16 | } 17 | 18 | test-flowing-margin { 19 | margin: auto; 20 | margin: 1px 4px 3px 2px; 21 | margin: auto; 22 | margin-left: 5px; 23 | margin-right: 6px; 24 | margin: auto; 25 | margin-left: 7px; 26 | margin: auto; 27 | margin-right: 8px; 28 | margin: auto; 29 | } 30 | -------------------------------------------------------------------------------- /test/padding.css: -------------------------------------------------------------------------------- 1 | test-padding { 2 | padding: logical 1px; 3 | padding: logical 2px 3px; 4 | padding: logical 4px 5px 6px; 5 | padding: logical 7px 8px 9px 8px; 6 | padding-inline: 10px; 7 | padding-inline: 11px 11px; 8 | padding-block: 12px; 9 | padding-block: 13px 14px; 10 | padding-block-start: 15px; 11 | padding-block-end: 16px; 12 | } 13 | 14 | test-flowing-padding { 15 | padding: auto; 16 | padding: logical 1px 2px 3px 4px; 17 | padding: auto; 18 | padding-inline: 5px 6px; 19 | padding: auto; 20 | padding-inline-start: 7px; 21 | padding: auto; 22 | padding-inline-end: 8px; 23 | padding: auto; 24 | padding-start: 9px; 25 | padding: auto; 26 | padding-start: 10px 11px; 27 | padding: auto; 28 | padding-end: 12px; 29 | padding: auto; 30 | padding-end: 13px 14px; 31 | padding: auto; 32 | } 33 | -------------------------------------------------------------------------------- /test/padding.expect.css: -------------------------------------------------------------------------------- 1 | test-padding { 2 | padding: 1px; 3 | padding: 2px 3px; 4 | padding: 4px 5px 6px; 5 | padding: 7px 8px 9px; 6 | padding-left: 10px; 7 | padding-right: 10px; 8 | padding-left: 11px; 9 | padding-right: 11px; 10 | padding-top: 12px; 11 | padding-bottom: 12px; 12 | padding-top: 13px; 13 | padding-bottom: 14px; 14 | padding-top: 15px; 15 | padding-bottom: 16px; 16 | } 17 | 18 | test-flowing-padding:dir(ltr) { 19 | padding: 1px 4px 3px 2px; 20 | } 21 | 22 | test-flowing-padding:dir(rtl) { 23 | padding: 1px 2px 3px 4px; 24 | } 25 | 26 | test-flowing-padding:dir(ltr) { 27 | padding-left: 5px; 28 | padding-right: 6px; 29 | } 30 | 31 | test-flowing-padding:dir(rtl) { 32 | padding-right: 5px; 33 | padding-left: 6px; 34 | } 35 | 36 | test-flowing-padding:dir(ltr) { 37 | padding-left: 7px; 38 | } 39 | 40 | test-flowing-padding:dir(rtl) { 41 | padding-right: 7px; 42 | } 43 | 44 | test-flowing-padding:dir(ltr) { 45 | padding-right: 8px; 46 | } 47 | 48 | test-flowing-padding:dir(rtl) { 49 | padding-left: 8px; 50 | } 51 | 52 | test-flowing-padding { 53 | padding: auto; 54 | padding: auto; 55 | padding: auto; 56 | padding: auto; 57 | padding: auto; 58 | padding-start: 9px; 59 | padding: auto; 60 | padding-start: 10px 11px; 61 | padding: auto; 62 | padding-end: 12px; 63 | padding: auto; 64 | padding-end: 13px 14px; 65 | padding: auto; 66 | } 67 | -------------------------------------------------------------------------------- /test/padding.ltr.expect.css: -------------------------------------------------------------------------------- 1 | test-padding { 2 | padding: 1px; 3 | padding: 2px 3px; 4 | padding: 4px 5px 6px; 5 | padding: 7px 8px 9px; 6 | padding-left: 10px; 7 | padding-right: 10px; 8 | padding-left: 11px; 9 | padding-right: 11px; 10 | padding-top: 12px; 11 | padding-bottom: 12px; 12 | padding-top: 13px; 13 | padding-bottom: 14px; 14 | padding-top: 15px; 15 | padding-bottom: 16px; 16 | } 17 | 18 | test-flowing-padding { 19 | padding: auto; 20 | padding: 1px 4px 3px 2px; 21 | padding: auto; 22 | padding-left: 5px; 23 | padding-right: 6px; 24 | padding: auto; 25 | padding-left: 7px; 26 | padding: auto; 27 | padding-right: 8px; 28 | padding: auto; 29 | padding-start: 9px; 30 | padding: auto; 31 | padding-start: 10px 11px; 32 | padding: auto; 33 | padding-end: 12px; 34 | padding: auto; 35 | padding-end: 13px 14px; 36 | padding: auto; 37 | } 38 | -------------------------------------------------------------------------------- /test/resize.css: -------------------------------------------------------------------------------- 1 | test-resize { 2 | resize: block; 3 | resize: inline; 4 | } 5 | -------------------------------------------------------------------------------- /test/resize.expect.css: -------------------------------------------------------------------------------- 1 | test-resize { 2 | resize: vertical; 3 | resize: horizontal; 4 | } 5 | -------------------------------------------------------------------------------- /test/size.css: -------------------------------------------------------------------------------- 1 | test-size { 2 | block-size: 320px; 3 | inline-size: 180px; 4 | } 5 | 6 | text-max-size { 7 | max-block-size: 320px; 8 | max-inline-size: 180px; 9 | } 10 | 11 | text-min-size { 12 | min-block-size: 320px; 13 | min-inline-size: 180px; 14 | } 15 | -------------------------------------------------------------------------------- /test/size.expect.css: -------------------------------------------------------------------------------- 1 | test-size { 2 | height: 320px; 3 | width: 180px; 4 | } 5 | 6 | text-max-size { 7 | max-height: 320px; 8 | max-width: 180px; 9 | } 10 | 11 | text-min-size { 12 | min-height: 320px; 13 | min-width: 180px; 14 | } 15 | -------------------------------------------------------------------------------- /test/size.preserve.expect.css: -------------------------------------------------------------------------------- 1 | test-size { 2 | height: 320px; 3 | block-size: 320px; 4 | width: 180px; 5 | inline-size: 180px; 6 | } 7 | 8 | text-max-size { 9 | max-height: 320px; 10 | max-block-size: 320px; 11 | max-width: 180px; 12 | max-inline-size: 180px; 13 | } 14 | 15 | text-min-size { 16 | min-height: 320px; 17 | min-block-size: 320px; 18 | min-width: 180px; 19 | min-inline-size: 180px; 20 | } 21 | -------------------------------------------------------------------------------- /test/text-align.css: -------------------------------------------------------------------------------- 1 | test-text-align { 2 | text-align: left; 3 | text-align: start; 4 | text-align: end; 5 | text-align: right; 6 | } 7 | -------------------------------------------------------------------------------- /test/text-align.expect.css: -------------------------------------------------------------------------------- 1 | test-text-align:dir(ltr) { 2 | text-align: left; 3 | } 4 | test-text-align:dir(rtl) { 5 | text-align: right; 6 | } 7 | test-text-align:dir(ltr) { 8 | text-align: right; 9 | } 10 | test-text-align:dir(rtl) { 11 | text-align: left; 12 | } 13 | test-text-align { 14 | text-align: left; 15 | text-align: right; 16 | } 17 | -------------------------------------------------------------------------------- /test/text-align.ltr.expect.css: -------------------------------------------------------------------------------- 1 | test-text-align { 2 | text-align: left; 3 | text-align: left; 4 | text-align: right; 5 | text-align: right; 6 | } 7 | -------------------------------------------------------------------------------- /test/transition.css: -------------------------------------------------------------------------------- 1 | .transition { 2 | transition: 1s opacity ease; 3 | } 4 | 5 | .transition-block { 6 | transition: 1s inset-block ease; 7 | transition: 1s margin-block ease; 8 | transition: 1s padding-block ease; 9 | transition-property: border-block, border-block-color, border-block-style, border-block-width; 10 | } 11 | 12 | .transition-inline { 13 | transition: 1s inset-inline ease; 14 | transition: 1s margin-inline ease; 15 | transition: 1s padding-inline ease; 16 | transition-property: border-inline, border-inline-color, border-inline-style, border-inline-width; 17 | } 18 | 19 | .transition-block-start { 20 | transition: 1s inset-block-start ease; 21 | transition: 1s margin-block-start ease; 22 | transition: 1s padding-block-start ease; 23 | } 24 | 25 | .transition-inline-end { 26 | transition: 1s inset-inline-end ease; 27 | transition: 1s margin-inline-end ease; 28 | transition: 1s padding-inline-end ease; 29 | } 30 | 31 | .transition-mixed { 32 | transition: 1s opacity ease, 1s inset-inline-start ease; 33 | } 34 | -------------------------------------------------------------------------------- /test/transition.expect.css: -------------------------------------------------------------------------------- 1 | .transition { 2 | transition: 1s opacity ease; 3 | } 4 | 5 | .transition-block { 6 | transition: 1s inset-block ease; 7 | transition: 1s margin-block ease; 8 | transition: 1s padding-block ease; 9 | transition-property: border-block, border-block-color, border-block-style, border-block-width; 10 | } 11 | 12 | .transition-inline { 13 | transition: 1s inset-inline ease; 14 | transition: 1s margin-inline ease; 15 | transition: 1s padding-inline ease; 16 | transition-property: border-inline, border-inline-color, border-inline-style, border-inline-width; 17 | } 18 | 19 | .transition-block-start { 20 | transition: 1s inset-block-start ease; 21 | transition: 1s margin-block-start ease; 22 | transition: 1s padding-block-start ease; 23 | } 24 | 25 | .transition-inline-end:dir(ltr) { 26 | transition: 1s right ease; 27 | } 28 | 29 | .transition-inline-end:dir(rtl) { 30 | transition: 1s left ease; 31 | } 32 | 33 | .transition-inline-end:dir(ltr) { 34 | transition: 1s margin-right ease; 35 | } 36 | 37 | .transition-inline-end:dir(rtl) { 38 | transition: 1s margin-left ease; 39 | } 40 | 41 | .transition-inline-end:dir(ltr) { 42 | transition: 1s padding-right ease; 43 | } 44 | 45 | .transition-inline-end:dir(rtl) { 46 | transition: 1s padding-left ease; 47 | } 48 | 49 | .transition-mixed:dir(ltr) { 50 | transition: 1s opacity ease, 1s left ease; 51 | } 52 | 53 | .transition-mixed:dir(rtl) { 54 | transition: 1s opacity ease, 1s right ease; 55 | } 56 | -------------------------------------------------------------------------------- /test/transition.ltr.expect.css: -------------------------------------------------------------------------------- 1 | .transition { 2 | transition: 1s opacity ease; 3 | } 4 | 5 | .transition-block { 6 | transition: 1s top ease,1s bottom ease; 7 | transition: 1s margin-top ease,1s margin-bottom ease; 8 | transition: 1s padding-top ease,1s padding-bottom ease; 9 | transition-property: border-top,border-bottom, border-top-color, border-bottom-color, border-top-style, border-bottom-style, border-top-width, border-bottom-width; 10 | } 11 | 12 | .transition-inline { 13 | transition: 1s left ease,1s right ease; 14 | transition: 1s margin-left ease,1s margin-right ease; 15 | transition: 1s padding-left ease,1s padding-right ease; 16 | transition-property: border-left,border-right, border-left-color, border-right-color, border-left-style, border-right-style, border-left-width, border-right-width; 17 | } 18 | 19 | .transition-block-start { 20 | transition: 1s top ease; 21 | transition: 1s margin-top ease; 22 | transition: 1s padding-top ease; 23 | } 24 | 25 | .transition-inline-end { 26 | transition: 1s right ease; 27 | transition: 1s margin-right ease; 28 | transition: 1s padding-right ease; 29 | } 30 | 31 | .transition-mixed { 32 | transition: 1s opacity ease, 1s left ease; 33 | } 34 | -------------------------------------------------------------------------------- /test/transition.preserve.ltr.expect.css: -------------------------------------------------------------------------------- 1 | .transition { 2 | transition: 1s opacity ease; 3 | } 4 | 5 | .transition-block { 6 | transition: 1s inset-block ease; 7 | transition: 1s margin-block ease; 8 | transition: 1s padding-block ease; 9 | transition-property: border-block, border-block-color, border-block-style, border-block-width; 10 | } 11 | 12 | .transition-inline { 13 | transition: 1s inset-inline ease; 14 | transition: 1s margin-inline ease; 15 | transition: 1s padding-inline ease; 16 | transition-property: border-inline, border-inline-color, border-inline-style, border-inline-width; 17 | } 18 | 19 | .transition-block-start { 20 | transition: 1s inset-block-start ease; 21 | transition: 1s margin-block-start ease; 22 | transition: 1s padding-block-start ease; 23 | } 24 | 25 | .transition-inline-end:dir(ltr) { 26 | transition: 1s right ease; 27 | } 28 | 29 | .transition-inline-end:dir(rtl) { 30 | transition: 1s left ease; 31 | } 32 | 33 | .transition-inline-end:dir(ltr) { 34 | transition: 1s margin-right ease; 35 | } 36 | 37 | .transition-inline-end:dir(rtl) { 38 | transition: 1s margin-left ease; 39 | } 40 | 41 | .transition-inline-end:dir(ltr) { 42 | transition: 1s padding-right ease; 43 | } 44 | 45 | .transition-inline-end:dir(rtl) { 46 | transition: 1s padding-left ease; 47 | } 48 | 49 | .transition-inline-end { 50 | transition: 1s inset-inline-end ease; 51 | transition: 1s margin-inline-end ease; 52 | transition: 1s padding-inline-end ease; 53 | } 54 | 55 | .transition-mixed:dir(ltr) { 56 | transition: 1s opacity ease, 1s left ease; 57 | } 58 | 59 | .transition-mixed:dir(rtl) { 60 | transition: 1s opacity ease, 1s right ease; 61 | } 62 | 63 | .transition-mixed { 64 | transition: 1s opacity ease, 1s inset-inline-start ease; 65 | } 66 | --------------------------------------------------------------------------------