├── .editorconfig
├── .gitignore
├── .npmignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── package.json
├── src
├── plugin.spec.ts
└── plugin.ts
├── tsconfig.json
└── tslint.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = tab
5 | end_of_line = lf
6 | trim_trailing_whitespace = true
7 | insert_final_newline = true
8 |
9 | [{package.json,*.yml}]
10 | indent_style = space
11 | indent_size = 2
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | .nyc_output/
3 | .vscode/
4 | coverage/
5 | dist/
6 | node_modules/
7 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .*
2 | *.map
3 | *.spec.*
4 | .nyc_output/
5 | .vscode/
6 | coverage/
7 | src/
8 | tsconfig.json
9 | tslint.json
10 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | node_js:
4 | - "node"
5 | - "6"
6 |
7 | after_success:
8 | - npm install codecov
9 | - npm run codecov
10 |
11 | notifications:
12 | email:
13 | on_success: change
14 | on_failure: always
15 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 1.1.0
2 | - Upgrade to PostCSS 6 (still works with PostCSS 5).
3 |
4 | ## 1.0.0
5 | - Supports PostCSS 5.x.
6 |
7 | ## 0.0.7
8 | - Remove CodeClimate integration.
9 |
10 | ## 0.0.6
11 | - Move scripts to package.json.
12 |
13 | ## 0.0.5
14 | - Don't npm ignore .d.ts.
15 |
16 | ## 0.0.4
17 | - npm ignore scripts.
18 |
19 | ## 0.0.3
20 | - Reorganize files.
21 |
22 | ## 0.0.2
23 | - Fix npm package.
24 |
25 | ## 0.0.1
26 | - Supports PostCSS 4.x.
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Jed Mao
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # postcss-center
2 |
3 |
6 |
7 | [](https://www.npmjs.org/package/postcss-center)
8 | [](https://www.npmjs.org/package/postcss-center)
9 | [](https://travis-ci.org/jedmao/postcss-center)
10 | [](https://codecov.io/gh/jedmao/postcss-center)
11 | [](https://gemnasium.com/github.com/jedmao/postcss-center)
12 |
13 | [](https://nodei.co/npm/postcss-center/)
14 |
15 | [PostCSS](https://github.com/postcss/postcss) plugin to center elements.
16 |
17 | ## Introduction
18 |
19 | Centering elements in CSS [isn't exactly straight-forward](http://www.w3.org/Style/Examples/007/center.en.html), but we can change that!
20 |
21 | ```css
22 | .foo {
23 | top: center;
24 | left: center;
25 | }
26 | ```
27 |
28 | Transpiles into:
29 |
30 | ```css
31 | .foo {
32 | position: absolute;
33 | top: 50%;
34 | left: 50%;
35 | margin-right: -50%;
36 | transform: translate(-50%, -50%)
37 | }
38 | ```
39 |
40 | Of course, you don't have to include both `top` and `left`:
41 |
42 | ```css
43 | .foo {
44 | top: center;
45 | }
46 | ```
47 |
48 | Transpiles into:
49 |
50 | ```css
51 | .foo {
52 | position: absolute;
53 | top: 50%;
54 | transform: translateY(-50%);
55 | }
56 | ```
57 |
58 | Or...
59 |
60 | ```css
61 | .foo {
62 | left: center;
63 | }
64 | ```
65 |
66 | Transpiles into:
67 |
68 | ```css
69 | .foo {
70 | position: absolute;
71 | left: 50%;
72 | margin-right: -50%;
73 | transform: translateX(-50%);
74 | }
75 | ```
76 |
77 | That's about it!
78 |
79 | ### Conditions
80 |
81 | - If the value of `top` or `left` is not `center` it will be preserved. If both are not `center`, this plugin will do nothing!
82 | - If the rule already has a `position` it will only be preserved if its value is `relative` or `fixed`. All other values will be replaced with `absolute`.
83 | - If the rule has a `position` of `relative` or the value of `left` is not `center`, the `margin-right` declaration will not be inserted.
84 |
85 | ## Installation
86 |
87 | ```
88 | $ npm install postcss-center
89 | ```
90 |
91 | ## Usage
92 |
93 | ### JavaScript
94 |
95 | ```js
96 | postcss([ require('postcss-center') ]);
97 | ```
98 |
99 | ### TypeScript
100 |
101 | ```ts
102 | import * as postcssCenter from 'postcss-center';
103 |
104 | postcss([ postcssCenter ]);
105 | ```
106 |
107 | ## Options
108 |
109 | None at this time.
110 |
111 | ## Testing
112 |
113 | Run the following command:
114 |
115 | ```
116 | $ npm test
117 | ```
118 |
119 | This will build scripts, run tests and generate a code coverage report. Anything less than 100% coverage will throw an error.
120 |
121 | ### Watching
122 |
123 | For much faster development cycles, run the following commands in 2 separate processes:
124 |
125 | ```
126 | $ npm run build:watch
127 | ```
128 |
129 | Compiles TypeScript source into the `./dist` folder and watches for changes.
130 |
131 | ```
132 | $ npm run watch
133 | ```
134 |
135 | Runs the tests in the `./dist` folder and watches for changes.
136 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "postcss-center",
3 | "version": "1.1.0",
4 | "description": "PostCSS plugin to center elements.",
5 | "main": "dist/plugin.js",
6 | "types": "dist/plugin.d.ts",
7 | "scripts": {
8 | "clean": "rimraf coverage dist *.log",
9 | "codecov": "codecov -f coverage/lcov.info",
10 | "build": "tsc",
11 | "build:watch": "tsc --watch",
12 | "prepublish": "npm test",
13 | "pretest": "npm run tslint && npm run clean && npm run build",
14 | "test": "nyc ava",
15 | "test:watch": "ava --watch",
16 | "tslint": "tslint --project tsconfig.json",
17 | "watch": "npm run test:watch"
18 | },
19 | "ava": {
20 | "files": [
21 | "dist/**/*.spec.js"
22 | ],
23 | "source": [
24 | "dist/**/*.js"
25 | ]
26 | },
27 | "nyc": {
28 | "lines": 100,
29 | "statements": 100,
30 | "functions": 100,
31 | "branches": 100,
32 | "include": [
33 | "dist/**/*.js"
34 | ],
35 | "exclude": [
36 | "dist/**/*.spec.js"
37 | ],
38 | "reporter": [
39 | "lcov",
40 | "text"
41 | ],
42 | "cache": true,
43 | "all": true,
44 | "check-coverage": true
45 | },
46 | "repository": {
47 | "type": "git",
48 | "url": "git+https://github.com/jedmao/postcss-center.git"
49 | },
50 | "keywords": [
51 | "postcss",
52 | "postcss-plugin",
53 | "center",
54 | "top",
55 | "left"
56 | ],
57 | "author": "Jed Mao ",
58 | "license": "MIT",
59 | "bugs": {
60 | "url": "https://github.com/jedmao/postcss-center/issues"
61 | },
62 | "homepage": "https://github.com/jedmao/postcss-center#readme",
63 | "dependencies": {
64 | "postcss": "^6.0.14"
65 | },
66 | "devDependencies": {
67 | "@types/node": "^8.0.47",
68 | "ava": "^0.23.0",
69 | "nyc": "^11.3.0",
70 | "rimraf": "^2.6.2",
71 | "tslint": "^5.8.0",
72 | "typescript": "^2.6.1"
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/plugin.spec.ts:
--------------------------------------------------------------------------------
1 | import test, { TestContext } from 'ava';
2 | import * as postcss from 'postcss';
3 |
4 | import * as plugin from './plugin';
5 |
6 | test('top: center; transpiles into expected declarations', macro,
7 | `foo {
8 | top: center;
9 | }`,
10 | `foo {
11 | position: absolute;
12 | top: 50%;
13 | transform: translateY(-50%);
14 | }`
15 | );
16 |
17 | ['absolute', 'relative', 'fixed'].forEach(position => {
18 | test(
19 | `top: center; perserves location/value of position at top if ${position}`,
20 | macro,
21 | `foo {
22 | position: ${position};
23 | bar: baz;
24 | top: center;
25 | }`,
26 | `foo {
27 | position: ${position};
28 | bar: baz;
29 | top: 50%;
30 | transform: translateY(-50%);
31 | }`
32 | );
33 | test(
34 | `top: center; perserves location/value of position at bottom if ${position}`,
35 | macro,
36 | `foo {
37 | top: center;
38 | bar: baz;
39 | position: ${position};
40 | }`,
41 | `foo {
42 | top: 50%;
43 | transform: translateY(-50%);
44 | bar: baz;
45 | position: ${position};
46 | }`
47 | );
48 | });
49 |
50 | test('top: center; removes position: static and inserts a new position', macro,
51 | `foo {
52 | top: center;
53 | position: static;
54 | }`,
55 | `foo {
56 | position: absolute;
57 | top: 50%;
58 | transform: translateY(-50%);
59 | }`
60 | );
61 |
62 | test('left: center; transpiles into expected declarations', macro,
63 | `foo {
64 | left: center;
65 | }`,
66 | `foo {
67 | position: absolute;
68 | left: 50%;
69 | margin-right: -50%;
70 | transform: translateX(-50%);
71 | }`
72 | );
73 |
74 | test('left: center; omits margin-right: -50% if position is relative', macro,
75 | `foo {
76 | position: relative;
77 | left: center;
78 | }`,
79 | `foo {
80 | position: relative;
81 | left: 50%;
82 | transform: translateX(-50%);
83 | }`
84 | );
85 |
86 | test('top: center; left: center; transpiles into expected declarations', macro,
87 | `foo {
88 | top: center;
89 | left: center;
90 | }`,
91 | `foo {
92 | position: absolute;
93 | top: 50%;
94 | left: 50%;
95 | margin-right: -50%;
96 | transform: translate(-50%, -50%);
97 | }`
98 | );
99 |
100 | test('top: 10px; left: 20px; passes through w/o modification', macro,
101 | `foo {
102 | top: 10px;
103 | left: 20px;
104 | }`,
105 | `foo {
106 | top: 10px;
107 | left: 20px;
108 | }`
109 | );
110 |
111 | function macro(
112 | t: TestContext,
113 | input: string,
114 | expected?: string|RegExp
115 | ) {
116 | const processor = postcss([ plugin() ]);
117 | if (expected instanceof RegExp) {
118 | t.throws(() => {
119 | return processor.process(stripTabs(input)).css;
120 | }, expected);
121 | return;
122 | }
123 | t.is(
124 | processor.process(stripTabs(input)).css,
125 | stripTabs(expected)
126 | );
127 | function stripTabs(input: string) {
128 | return input.replace(/\t/g, '');
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/plugin.ts:
--------------------------------------------------------------------------------
1 | import * as postcss from 'postcss';
2 |
3 | const PostCssCenter = postcss.plugin('postcss-center', () => {
4 | return root => {
5 | root.walkRules(rule => {
6 | let top: postcss.Declaration;
7 | rule.walkDecls('top', decl => {
8 | if (decl.value !== 'center') {
9 | return;
10 | }
11 | top = decl;
12 | decl.value = '50%';
13 | });
14 |
15 | let left: postcss.Declaration;
16 | rule.walkDecls('left', decl => {
17 | if (decl.value !== 'center') {
18 | return;
19 | }
20 | left = decl;
21 | decl.value = '50%';
22 | });
23 |
24 | if (!top && !left) {
25 | return;
26 | }
27 |
28 | let position: string;
29 | rule.walkDecls('position', decl => {
30 | if (/^(absolute|relative|fixed)$/.test(decl.value)) {
31 | position = decl.value;
32 | return;
33 | }
34 | decl.remove();
35 | });
36 |
37 | if (!position) {
38 | (top || left).cloneBefore({
39 | prop: 'position',
40 | value: 'absolute'
41 | });
42 | }
43 |
44 | let translate: string;
45 | if (top && left) {
46 | translate = '(-50%, -50%)';
47 | } else if (top) {
48 | translate = 'Y(-50%)';
49 | } else {
50 | translate = 'X(-50%)';
51 | }
52 | (left || top).cloneAfter({
53 | prop: 'transform',
54 | value: `translate${translate}`
55 | });
56 |
57 | if (left && position !== 'relative') {
58 | left.cloneAfter({
59 | prop: 'margin-right',
60 | value: '-50%'
61 | });
62 | }
63 | });
64 | };
65 | });
66 |
67 | export = PostCssCenter;
68 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "noImplicitAny": true,
5 | "target": "es6",
6 | "declaration": true,
7 | "newLine": "LF",
8 | "outDir": "dist",
9 | "rootDir": "src",
10 | "sourceMap": true,
11 | "lib": [
12 | "es2015"
13 | ]
14 | },
15 | "include": [
16 | "src/**/*.ts"
17 | ],
18 | "exclude": [
19 | "node_modules"
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "class-name": true,
4 | "curly": true,
5 | "eofline": true,
6 | "forin": true,
7 | "label-position": true,
8 | "max-line-length": [true, 120],
9 | "no-arg": true,
10 | "no-bitwise": true,
11 | "no-console": [true,
12 | "debug",
13 | "info",
14 | "time",
15 | "timeEnd",
16 | "trace"
17 | ],
18 | "no-construct": true,
19 | "no-debugger": true,
20 | "no-duplicate-variable": true,
21 | "no-empty": true,
22 | "no-eval": true,
23 | "no-string-literal": true,
24 | "no-trailing-whitespace": true,
25 | "one-line": [true,
26 | "check-open-brace",
27 | "check-catch",
28 | "check-else",
29 | "check-whitespace"
30 | ],
31 | "quotemark": [false],
32 | "radix": true,
33 | "semicolon": [true,
34 | "always"
35 | ],
36 | "triple-equals": [true, "allow-null-check"],
37 | "variable-name": false,
38 | "whitespace": [true,
39 | "check-branch",
40 | "check-decl",
41 | "check-operator",
42 | "check-type"
43 | ]
44 | }
45 | }
46 |
--------------------------------------------------------------------------------