├── .eslintrc.js ├── .gitignore ├── .nvmrc ├── .size-limit.js ├── .size.json ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── __tests__ ├── mapping-test.spec.ts └── stylelintrc.js ├── jest.config.js ├── package.json ├── src ├── groups │ ├── animations.ts │ ├── border.ts │ ├── box.ts │ ├── clip.ts │ ├── element-properties.ts │ ├── index.ts │ ├── misc.ts │ ├── scss.ts │ ├── style.ts │ ├── top-level.ts │ ├── typography.ts │ └── visuals.ts ├── index.ts ├── properties-order.ts ├── rules-order.ts └── utils.ts ├── tsconfig.json └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['plugin:@typescript-eslint/recommended', 'plugin:import/typescript', 'plugin:react-hooks/recommended'], 3 | parser: '@typescript-eslint/parser', 4 | plugins: ['@typescript-eslint', 'prettier', 'import'], 5 | rules: { 6 | '@typescript-eslint/ban-ts-ignore': 0, 7 | '@typescript-eslint/no-var-requires': 0, 8 | '@typescript-eslint/camelcase': 0, 9 | 'import/order': [ 10 | 'error', 11 | { 12 | 'newlines-between': 'always-and-inside-groups', 13 | alphabetize: { 14 | order: 'asc', 15 | }, 16 | groups: ['builtin', 'external', 'internal', ['parent', 'index', 'sibling']], 17 | }, 18 | ], 19 | }, 20 | settings: { 21 | 'import/parsers': { 22 | '@typescript-eslint/parser': ['.ts', '.tsx'], 23 | }, 24 | 'import/resolver': { 25 | typescript: { 26 | alwaysTryTypes: true, 27 | }, 28 | }, 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 10 -------------------------------------------------------------------------------- /.size-limit.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | path: 'dist/es2015/index.js', 4 | limit: '5 KB', 5 | }, 6 | ]; 7 | -------------------------------------------------------------------------------- /.size.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "dist/es2015/index.js", 4 | "passed": true, 5 | "size": 1783 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '10' 4 | cache: yarn 5 | script: 6 | - yarn 7 | - yarn test:ci 8 | - codecov 9 | notifications: 10 | email: true -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # [1.2.0](https://github.com/theKashey/stylelint-semantic-groups/compare/v1.1.4...v1.2.0) (2022-10-25) 2 | 3 | ## [1.1.4](https://github.com/theKashey/stylelint-semantic-groups/compare/v1.1.3...v1.1.4) (2022-07-18) 4 | 5 | ### Bug Fixes 6 | 7 | - update tslib version, fixes [#6](https://github.com/theKashey/stylelint-semantic-groups/issues/6) ([3f9fa99](https://github.com/theKashey/stylelint-semantic-groups/commit/3f9fa99c489e4fa7ee769674021753e5e65a0e64)) 8 | 9 | ## [1.1.3](https://github.com/theKashey/stylelint-semantic-groups/compare/v1.1.2...v1.1.3) (2022-02-12) 10 | 11 | ## [1.1.2](https://github.com/theKashey/stylelint-semantic-groups/compare/v1.1.1...v1.1.2) (2022-01-18) 12 | 13 | ## [1.1.1](https://github.com/theKashey/stylelint-semantic-groups/compare/v1.1.0...v1.1.1) (2021-07-26) 14 | 15 | ### Bug Fixes 16 | 17 | - add composite properties place-content, place-items, place-set and inset ([fb21740](https://github.com/theKashey/stylelint-semantic-groups/commit/fb217405939b0c3273814f94c5e6ca53f1e52d8b)) 18 | 19 | # 1.1.0 (2021-07-03) 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Anton Korzunov 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # stylelint-semantic-groups 2 | 3 | Opinionated group based SCSS property ordering for better `maintainability` and `perception`. 4 | 5 | 📖 Theory behind: [Harry Potter and the Order of CSS](https://dev.to/thekashey/happy-potter-and-the-order-of-css-5ec) 6 | 7 | --- 8 | 9 | # Usage 10 | 11 | - install required packages 12 | 13 | ```bash 14 | yarn add stylelint-order stylelint-semantic-groups 15 | ``` 16 | 17 | - set configuration 18 | 19 | ### Recommended configuration 20 | 21 | ```js 22 | // .stylelintrc.js 23 | const { propertyOrdering, selectorOrdering } = require('stylelint-semantic-groups'); 24 | 25 | module.exports = { 26 | plugins: ['stylelint-order'], 27 | rules: { 28 | 'order/order': selectorOrdering, // to fine-tune configuration use selectorOrderFactory 29 | 'order/properties-order': propertyOrdering, 30 | }, 31 | }; 32 | ``` 33 | 34 | ### Minimal configuration 35 | 36 | ```js 37 | // .stylelintrc.js 38 | const { propertyOrdering } = require('stylelint-semantic-groups'); 39 | 40 | module.exports = { 41 | plugins: ['stylelint-order'], 42 | rules: { 43 | /* optional by recommended */ 44 | 'order/order': [ 45 | 'custom-properties', 46 | 'dollar-variables', 47 | 'declarations', 48 | 'at-rules', // <-- important, `@media` should go before `&:pseudo` 49 | 'rules', 50 | ], 51 | /* the actual usage of this package */ 52 | 'order/properties-order': propertyOrdering, 53 | }, 54 | }; 55 | ``` 56 | 57 | Stylelint config that sorts related property declarations by grouping together following the order: 58 | 59 | - (optional) SCSS/CSS variables, handled by `order/order` 60 | - Layout Positioning - Element bound properties (in terms of BEM) such as `grid-area`. Note: in the current implementation it is mostly about grids and grids only. 61 | - Positioning 62 | - Box Model 63 | - Typography 64 | - Visual 65 | - Misc 66 | - Animation 67 | - (optional) at-rules, handled by `order/order` 68 | - (optional) nested rules, handled by `order/order` 69 | 70 | ```scss 71 | .declaration-order { 72 | --color: #fff; 73 | 74 | /* Positioning */ 75 | position: absolute; 76 | top: 0; 77 | right: 0; 78 | bottom: 0; 79 | left: 0; 80 | z-index: 10; 81 | 82 | /* Box Model */ /* white space between groups is ENFORCED */ 83 | display: block; 84 | float: right; 85 | width: 100px; 86 | height: 100px; 87 | margin: 10px; 88 | padding: 10px; 89 | 90 | /* Typography */ 91 | color: #888; 92 | font: normal 16px Helvetica, sans-serif; 93 | line-height: 1.3; 94 | text-align: center; 95 | 96 | /* Visual */ 97 | background-color: #eee; 98 | border: 1px solid #888; 99 | border-radius: 4px; 100 | opacity: 1; 101 | 102 | /* Animation */ 103 | transition: all 1s; 104 | 105 | /* Misc */ 106 | user-select: none; 107 | } 108 | ``` 109 | 110 | ## See also 111 | 112 | - this plugin is based on [stylelint-config-rational-order](https://github.com/constverum/stylelint-config-rational-order) 113 | - this plugin implements ideas from [idiomatic-css](https://github.com/necolas/idiomatic-css) 114 | 115 | # Licence 116 | 117 | MIT 118 | -------------------------------------------------------------------------------- /__tests__/mapping-test.spec.ts: -------------------------------------------------------------------------------- 1 | import stylelint from 'stylelint'; 2 | 3 | describe('Smoke', () => { 4 | const getExtendedConfig = (code: string) => ({ 5 | code, 6 | config: { 7 | extends: ['./stylelintrc.js'], 8 | }, 9 | configBasedir: __dirname, 10 | }); 11 | 12 | const process = async (code: string): Promise => { 13 | const result = await stylelint.lint({ 14 | ...getExtendedConfig(code), 15 | fix: true, 16 | }); 17 | return result.output; 18 | }; 19 | 20 | it('maps styles', async () => { 21 | expect( 22 | await process(` 23 | declaration-order { 24 | --fill-1: #FFF; 25 | --fill-2: #FFF; 26 | /* Positioning */ 27 | position: absolute; 28 | top: 0; 29 | right: 0; 30 | bottom: 0; 31 | left: 0; 32 | inset-inline-start: 0; 33 | 34 | all: unset; 35 | --color: #FFF; 36 | 37 | grid-row-start:4; 38 | order:1; 39 | z-index:2; 40 | grid-row:3; 41 | grid-gap:4; 42 | 43 | /* Box Model */ 44 | display: block; 45 | float: right; 46 | width: 100px; 47 | height: 100px; 48 | margin: 10px; 49 | padding: 10px; 50 | 51 | /* Typography */ 52 | color: #888; 53 | font: normal 16px Helvetica, sans-serif; 54 | line-height: 1.3; 55 | text-align: center; 56 | 57 | /* Visual */ 58 | background-color: #eee; 59 | border-radius: 4px; 60 | border: 1px solid #888; 61 | opacity: 1; 62 | 63 | @media (min-width: 100px) {} 64 | 65 | --stroke: #FFF; 66 | 67 | @media (min-width: 100px) {} 68 | 69 | /* Animation */ 70 | transition: all 1s; 71 | 72 | /* Misc */ 73 | user-select: none; 74 | }`) 75 | ).toMatchInlineSnapshot(` 76 | " 77 | declaration-order { 78 | --fill-1: #FFF; 79 | --fill-2: #FFF; 80 | --color: #FFF; 81 | 82 | --stroke: #FFF; 83 | 84 | all: unset; 85 | 86 | grid-row:3; 87 | grid-row-start:4; 88 | /* Positioning */ 89 | position: absolute; 90 | inset-inline-start: 0; 91 | top: 0; 92 | left: 0; 93 | bottom: 0; 94 | right: 0; 95 | z-index:2; 96 | 97 | order:1; 98 | 99 | /* Box Model */ 100 | display: block; 101 | grid-gap:4; 102 | float: right; 103 | 104 | width: 100px; 105 | height: 100px; 106 | margin: 10px; 107 | padding: 10px; 108 | 109 | /* Typography */ 110 | color: #888; 111 | font: normal 16px Helvetica, sans-serif; 112 | line-height: 1.3; 113 | text-align: center; 114 | 115 | border: 1px solid #888; 116 | border-radius: 4px; 117 | 118 | /* Visual */ 119 | background-color: #eee; 120 | 121 | opacity: 1; 122 | 123 | /* Misc */ 124 | user-select: none; 125 | 126 | /* Animation */ 127 | transition: all 1s; 128 | 129 | @media (min-width: 100px) {} 130 | 131 | @media (min-width: 100px) {} 132 | }" 133 | `); 134 | }); 135 | }); 136 | -------------------------------------------------------------------------------- /__tests__/stylelintrc.js: -------------------------------------------------------------------------------- 1 | const { propertyOrdering, selectorOrdering } = require('../src'); 2 | 3 | module.exports = { 4 | plugins: ['stylelint-order'], 5 | rules: { 6 | 'order/order': selectorOrdering, 7 | 'order/properties-order': propertyOrdering, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testRegex: '__tests__/.*.spec.ts$', 4 | }; 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stylelint-semantic-groups", 3 | "description": "Semantic CSS groups ordering", 4 | "version": "1.2.1", 5 | "main": "dist/es5/index.js", 6 | "types": "dist/es5/index.d.ts", 7 | "module": "dist/es2015/index.js", 8 | "module:es2019": "dist/es2019/index.js", 9 | "keywords": [ 10 | "stylelint", 11 | "stylelint-config", 12 | "stylelint-order", 13 | "stylelint-plugin", 14 | "properties-order", 15 | "css property order", 16 | "declaration-order", 17 | "css declaration order", 18 | "rational-css", 19 | "order-css", 20 | "order" 21 | ], 22 | "scripts": { 23 | "build": "lib-builder build && yarn size:report", 24 | "prepublish-only": "yarn build && yarn changelog", 25 | "test": "jest", 26 | "test:ci": "jest --runInBand --coverage", 27 | "release": "yarn build && yarn test", 28 | "size": "npx size-limit", 29 | "size:report": "npx size-limit --json > .size.json", 30 | "lint": "lib-builder lint", 31 | "format": "lib-builder format", 32 | "update": "lib-builder update", 33 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s", 34 | "changelog:rewrite": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0" 35 | }, 36 | "devDependencies": { 37 | "@theuiteam/lib-builder": "^0.1.3", 38 | "@size-limit/preset-small-lib": "^2.1.6", 39 | "@types/stylelint": "^13.13.0", 40 | "stylelint": "^13.13.1", 41 | "stylelint-order": "^4.1.0", 42 | "typescript": "^4.3.2" 43 | }, 44 | "engines": { 45 | "node": ">=10" 46 | }, 47 | "dependencies": { 48 | "tslib": "^2.1.0" 49 | }, 50 | "files": [ 51 | "dist" 52 | ], 53 | "repository": "https://github.com/theKashey/stylelint-semantic-groups", 54 | "husky": { 55 | "hooks": { 56 | "pre-commit": "lint-staged" 57 | } 58 | }, 59 | "lint-staged": { 60 | "*.{ts,tsx}": [ 61 | "prettier --write", 62 | "eslint --fix", 63 | "git add" 64 | ], 65 | "*.{js,css,json,md}": [ 66 | "prettier --write", 67 | "git add" 68 | ] 69 | }, 70 | "prettier": { 71 | "printWidth": 120, 72 | "trailingComma": "es5", 73 | "tabWidth": 2, 74 | "semi": true, 75 | "singleQuote": true 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/groups/animations.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | 'transition', 3 | 'transition-property', 4 | 'transition-delay', 5 | 'transition-timing-function', 6 | 'transition-duration', 7 | 'animation', 8 | 'animation-name', 9 | 'animation-duration', 10 | 'animation-play-state', 11 | 'animation-timing-function', 12 | 'animation-delay', 13 | 'animation-iteration-count', 14 | 'animation-direction', 15 | 'animation-fill-mode', 16 | ]; 17 | -------------------------------------------------------------------------------- /src/groups/border.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | 'border', 3 | 'border-color', 4 | 'border-style', 5 | 'border-width', 6 | 'border-top', 7 | 'border-top-color', 8 | 'border-top-width', 9 | 'border-top-style', 10 | 'border-right', 11 | 'border-right-color', 12 | 'border-right-width', 13 | 'border-right-style', 14 | 'border-bottom', 15 | 'border-bottom-color', 16 | 'border-bottom-width', 17 | 'border-bottom-style', 18 | 'border-left', 19 | 'border-left-color', 20 | 'border-left-width', 21 | 'border-left-style', 22 | 'border-radius', 23 | 'border-top-left-radius', 24 | 'border-top-right-radius', 25 | 'border-bottom-right-radius', 26 | 'border-bottom-left-radius', 27 | 'border-image', 28 | 'border-image-source', 29 | 'border-image-slice', 30 | 'border-image-width', 31 | 'border-image-outset', 32 | 'border-image-repeat', 33 | 'border-collapse', 34 | 'border-spacing', 35 | ]; 36 | -------------------------------------------------------------------------------- /src/groups/box.ts: -------------------------------------------------------------------------------- 1 | import { logicalProperty } from '../utils'; 2 | 3 | export default [ 4 | [ 5 | 'display', 6 | 'flex', 7 | 'flex-basis', 8 | 'flex-direction', 9 | 'flex-flow', 10 | 'flex-grow', 11 | 'flex-shrink', 12 | 'flex-wrap', 13 | 'grid', 14 | 'grid-auto-rows', 15 | 'grid-auto-columns', 16 | 'grid-auto-flow', 17 | 'grid-template', 18 | 'grid-template-areas', 19 | 'grid-template-rows', 20 | 'grid-template-columns', 21 | 'grid-gap', 22 | 'grid-row-gap', 23 | 'grid-column-gap', 24 | 'gap', 25 | 'row-gap', 26 | 'column-gap', 27 | 'place-content', 28 | 'place-items', 29 | 'place-self', 30 | 'justify-content', 31 | 'justify-items', 32 | 'justify-self', 33 | 'align-content', 34 | 'align-items', 35 | 'align-self', 36 | 'float', 37 | 'clear', 38 | ], 39 | [ 40 | // Add logical size for width , height 41 | 'inline-size', 42 | 'block-size', 43 | 'min-inline-size', 44 | 'max-inline-size', 45 | 'min-block-size', 46 | 'max-block-size', 47 | 'width', 48 | 'height', 49 | 'min-width', 50 | 'max-width', 51 | 'min-height', 52 | 'max-height', 53 | 54 | ...logicalProperty('margin'), 55 | 'margin-top', 56 | 'margin-right', 57 | 'margin-bottom', 58 | 'margin-left', 59 | 60 | ...logicalProperty('padding'), 61 | 'padding-top', 62 | 'padding-right', 63 | 'padding-bottom', 64 | 'padding-left', 65 | 66 | 'box-sizing', 67 | ], 68 | ]; 69 | -------------------------------------------------------------------------------- /src/groups/clip.ts: -------------------------------------------------------------------------------- 1 | export default ['object-fit', 'object-position', 'overflow', 'overflow-x', 'overflow-y']; 2 | -------------------------------------------------------------------------------- /src/groups/element-properties.ts: -------------------------------------------------------------------------------- 1 | import { logicalProperty } from '../utils'; 2 | 3 | export default [ 4 | ['grid-area', 'grid-row', 'grid-column', 'grid-column-start', 'grid-column-end', 'grid-row-start', 'grid-row-end'], 5 | ['position', ...logicalProperty('inset'), 'top', 'left', 'bottom', 'right', 'content', 'z-index'], 6 | // moved to box 7 | // ['width', 'height', 'margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left'], 8 | 9 | ['order'], 10 | ]; 11 | -------------------------------------------------------------------------------- /src/groups/index.ts: -------------------------------------------------------------------------------- 1 | import animations from './animations'; 2 | import border from './border'; 3 | import box from './box'; 4 | import clip from './clip'; 5 | import elementProperties from './element-properties'; 6 | import misc from './misc'; 7 | import scss from './scss'; 8 | import style from './style'; 9 | import topLevel from './top-level'; 10 | import typography from './typography'; 11 | import visuals from './visuals'; 12 | 13 | export { topLevel, animations, border, box, clip, elementProperties, misc, scss, style, typography, visuals }; 14 | -------------------------------------------------------------------------------- /src/groups/misc.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | 'appearance', 3 | 'clip', 4 | 'clip-path', 5 | 'counter-reset', 6 | 'counter-increment', 7 | 'resize', 8 | 'user-select', 9 | 'nav-index', 10 | 'nav-up', 11 | 'nav-right', 12 | 'nav-down', 13 | 'nav-left', 14 | 'pointer-events', 15 | 'quotes', 16 | 'touch-action', 17 | 'will-change', 18 | 'zoom', 19 | ]; 20 | -------------------------------------------------------------------------------- /src/groups/scss.ts: -------------------------------------------------------------------------------- 1 | export default ['composes', '@import', '@extend', '@mixin', '@at-root']; 2 | -------------------------------------------------------------------------------- /src/groups/style.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | 'fill', 3 | 'fill-rule', 4 | 'clip-rule', 5 | 'stroke', 6 | 'opacity', 7 | 'filter', 8 | 'transform', 9 | 'transform-origin', 10 | 'transform-style', 11 | 'backface-visibility', 12 | 'perspective', 13 | 'perspective-origin', 14 | ]; 15 | -------------------------------------------------------------------------------- /src/groups/top-level.ts: -------------------------------------------------------------------------------- 1 | export default ['all']; 2 | -------------------------------------------------------------------------------- /src/groups/typography.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | 'color', 3 | 'font', 4 | 'font-weight', 5 | 'font-size', 6 | 'font-family', 7 | 'font-style', 8 | 'font-variant', 9 | 'font-size-adjust', 10 | 'font-stretch', 11 | 'font-effect', 12 | 'font-emphasize', 13 | 'font-emphasize-position', 14 | 'font-emphasize-style', 15 | 'font-smooth', 16 | 'line-height', 17 | 'direction', 18 | 'letter-spacing', 19 | 'white-space', 20 | 'text-align', 21 | 'text-align-last', 22 | 'text-transform', 23 | 'text-decoration', 24 | 'text-emphasis', 25 | 'text-emphasis-color', 26 | 'text-emphasis-style', 27 | 'text-emphasis-position', 28 | 'text-indent', 29 | 'text-justify', 30 | 'text-outline', 31 | 'text-wrap', 32 | 'text-overflow', 33 | 'text-overflow-ellipsis', 34 | 'text-overflow-mode', 35 | 'text-orientation', 36 | 'text-shadow', 37 | 'vertical-align', 38 | 'word-wrap', 39 | 'word-break', 40 | 'word-spacing', 41 | 'overflow-wrap', 42 | 'tab-size', 43 | 'hyphens', 44 | 'unicode-bidi', 45 | 'columns', 46 | 'column-count', 47 | 'column-fill', 48 | 'column-gap', 49 | 'column-rule', 50 | 'column-rule-color', 51 | 'column-rule-style', 52 | 'column-rule-width', 53 | 'column-span', 54 | 'column-width', 55 | 'page-break-after', 56 | 'page-break-before', 57 | 'page-break-inside', 58 | 'src', 59 | ]; 60 | -------------------------------------------------------------------------------- /src/groups/visuals.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | [ 3 | 'background', 4 | 'background-color', 5 | 'background-image', 6 | 'background-repeat', 7 | 'background-position', 8 | 'background-position-x', 9 | 'background-position-y', 10 | 'background-size', 11 | 'background-clip', 12 | 'background-origin', 13 | 'background-attachment', 14 | 'background-blend-mode', 15 | ], 16 | ['list-style', 'list-style-position', 'list-style-type', 'list-style-image'], 17 | ['table-layout', 'empty-cells', 'caption-side'], 18 | ['outline', 'outline-width', 'outline-style', 'outline-color', 'outline-offset'], 19 | ['box-shadow', 'box-decoration-break'], 20 | ['visibility', 'cursor', 'isolation', 'backdrop-filter', 'mix-blend-mode'], 21 | ]; 22 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { propertyOrdering } from './properties-order'; 2 | import { selectorOrderFactory } from './rules-order'; 3 | 4 | /** 5 | * @deprecated replaced {@link propertyOrdering} 6 | */ 7 | export const semanticOrdering = propertyOrdering; 8 | 9 | export const selectorOrdering = selectorOrderFactory(); 10 | 11 | export { propertyOrdering, selectorOrderFactory }; 12 | -------------------------------------------------------------------------------- /src/properties-order.ts: -------------------------------------------------------------------------------- 1 | import { 2 | topLevel, 3 | animations, 4 | border, 5 | box, 6 | clip, 7 | elementProperties, 8 | misc, 9 | scss, 10 | style, 11 | typography, 12 | visuals, 13 | } from './groups'; 14 | 15 | type Rule = { 16 | groupName: string; 17 | emptyLineBefore: 'always'; 18 | noEmptyLineBetween: true; 19 | properties: string[]; 20 | }; 21 | 22 | const createGroup = (groupName: string, properties: string[]): Rule => ({ 23 | emptyLineBefore: 'always', 24 | noEmptyLineBetween: true, 25 | groupName, 26 | properties, 27 | }); 28 | 29 | type Rules = string[]; 30 | type MultiRules = Rules[]; 31 | 32 | const ruleIsArray = (rules: Rules | MultiRules): rules is MultiRules => Array.isArray(rules[0]); 33 | 34 | const mergeGroups = (groups: Record) => { 35 | const groupRules: Rule[] = []; 36 | 37 | // eslint-disable-next-line no-restricted-syntax 38 | for (const [groupName, rules] of Object.entries(groups)) { 39 | if (ruleIsArray(rules)) { 40 | groupRules.push(...rules.map((props) => createGroup(groupName, props))); 41 | } else { 42 | groupRules.push(createGroup(groupName, rules)); 43 | } 44 | } 45 | 46 | return groupRules; 47 | }; 48 | 49 | export const propertyOrdering = [ 50 | mergeGroups({ 51 | scss, 52 | topLevel, 53 | elementProperties, 54 | 55 | box, 56 | 57 | typography, 58 | border, 59 | visuals, 60 | style, 61 | 62 | clip, 63 | misc, 64 | 65 | animations, 66 | }), 67 | // {"unspecified": "bottomAlphabetical"} 68 | ]; 69 | -------------------------------------------------------------------------------- /src/rules-order.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * confuigures `order/order` (stylelint order) to sort selectors in more maintainable way 3 | * @param [modifiersSelector] - selector for "modifier" selector, defaults to BEM one ('^&--') 4 | * @see https://github.com/hudochenkov/stylelint-order/issues/114 5 | */ 6 | export const selectorOrderFactory = (modifiersSelector = '^&--') => [ 7 | 'custom-properties', 8 | 'dollar-variables', 9 | 'declarations', 10 | { 11 | // modifiers 12 | type: 'rule', 13 | selector: modifiersSelector, 14 | }, 15 | 'at-rules', 16 | { 17 | type: 'rule', 18 | selector: '^&:(before|after)', 19 | }, 20 | { 21 | type: 'rule', 22 | selector: '^&::(before|after)', 23 | }, 24 | { 25 | type: 'rule', 26 | selector: '^&:(first-child|last-child|nth-child|last-of-type|first-of-type|nth-of-type)', 27 | }, 28 | { 29 | type: 'rule', 30 | selector: '&:hover', 31 | }, 32 | { 33 | type: 'rule', 34 | selector: '&:focus', 35 | }, 36 | { 37 | type: 'rule', 38 | selector: '&:active', 39 | }, 40 | { 41 | type: 'rule', 42 | selector: '&:disabled', 43 | }, 44 | 45 | 'rules', 46 | ]; 47 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | const logicalPostfixes = ['-block', '-block-start', '-block-end', '-inline', '-inline-start', '-inline-end']; 2 | 3 | export const logicalProperty = (name: string): string[] => [name, ...logicalPostfixes.map((postfix) => name + postfix)]; 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | "allowSyntheticDefaultImports": true, 5 | "strict": true, 6 | "strictNullChecks": true, 7 | "strictFunctionTypes": true, 8 | "noImplicitThis": true, 9 | "alwaysStrict": true, 10 | "noUnusedLocals": true, 11 | "noUnusedParameters": true, 12 | "noImplicitReturns": true, 13 | "noFallthroughCasesInSwitch": true, 14 | "noImplicitAny": true, 15 | "importHelpers": true, 16 | "isolatedModules": true, 17 | "target": "es6", 18 | "moduleResolution": "node", 19 | "lib": ["dom", "es5", "scripthost", "es2015.collection", "es2015.symbol", "es2015.iterable", "es2015.promise"], 20 | "types": ["node", "jest"], 21 | "typeRoots": ["./node_modules/@types"], 22 | "jsx": "react" 23 | } 24 | } 25 | --------------------------------------------------------------------------------