├── .babelrc ├── .eslintignore ├── .eslintrc ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .npmrc ├── .prettierrc ├── CHANGELOG.md ├── README.md ├── UNLICENSE ├── codecov.yml ├── develop └── index.js ├── jest.config.js ├── package.json ├── rollup.config.js ├── src └── index.js ├── test ├── .eslintrc ├── __snapshots__ │ ├── color.spec.js.snap │ ├── control.spec.js.snap │ ├── options.spec.js.snap │ └── width.spec.js.snap ├── color.spec.js ├── control.spec.js ├── options.spec.js └── width.spec.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "targets": { 7 | "node": 12 8 | } 9 | } 10 | ] 11 | ], 12 | "plugins": ["@babel/plugin-proposal-do-expressions"] 13 | } 14 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | --- 2 | root: true 3 | parser: "@babel/eslint-parser" 4 | extends: 5 | - "airbnb-base" 6 | - "plugin:jest/recommended" 7 | - "prettier" 8 | 9 | plugins: 10 | - "jest" 11 | 12 | env: 13 | jest: true 14 | 15 | rules: 16 | prefer-const: off 17 | one-var: off 18 | no-use-before-define: 19 | - error 20 | - functions: false 21 | comma-dangle: 22 | - error 23 | - arrays: "always-multiline" 24 | objects: "always-multiline" 25 | imports: "always-multiline" 26 | exports: "always-multiline" 27 | functions: ignore 28 | 29 | no-unused-expressions: off 30 | 31 | import/no-extraneous-dependencies: 32 | - error 33 | - devDependencies: true 34 | optionalDependencies: false 35 | peerDependencies: false 36 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | concurrency: 10 | group: ${{ github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | test-and-validate: 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [18.x, lts/*] 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v3 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | cache: 'yarn' 29 | 30 | - name: Install 31 | run: yarn install --frozen-lockfile --ignore-scripts 32 | 33 | - name: Test 34 | run: yarn run test:ci 35 | 36 | - name: Upload coverage 37 | uses: codecov/codecov-action@v3 38 | 39 | - name: Lint 40 | run: yarn run lint 41 | 42 | - name: Format 43 | run: yarn run format:check 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | .jest-cache 5 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org 2 | package-lock=false 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "arrowParens": "avoid" 4 | } 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # postcss-scrollbar change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | ## [Unreleased] 9 | 10 | ## [0.5.1] - 2022-06-21 11 | ### Fixed 12 | * Functional colors being ignored in `scrollbar-color` (#13). 13 | 14 | ## [0.5.0] - 2022-03-01 15 | ### Changed 16 | **Breaking** 17 | * PostCSS 8 migration. 18 | Now requires `postcss >= 8` as peerDependency. 19 | 20 | ## [0.4.0] - 2022-02-25 21 | ### Changed 22 | * Change default width value for the `thin` keyword from `rem` to `px`. 23 | 24 | ### Added 25 | * A new `width` option. 26 | Allows for setting the webkit fallbacks `width` and `height`. 27 | 28 | ## [0.3.0] - 2019-01-27 29 | ### Added 30 | * Added webkit scrollbar corner background color (#2). 31 | * Added webkit horizontal scrollbar height to match vertical scrollbar width (#3). 32 | 33 | ## [0.2.1] - 2018-01-12 34 | ### Fixed 35 | * Fixed Node commonjs support. 36 | 37 | ## [0.2.0] - 2018-12-27 38 | ## [0.1.0] - 2018-12-20 39 | - Initial release. 40 | 41 | [unreleased]: https://github.com/pascalduez/postcss-scrollbar/compare/0.5.1...HEAD 42 | [0.5.1]: https://github.com/pascalduez/postcss-scrollbar/releases/tag/0.5.1 43 | [0.5.0]: https://github.com/pascalduez/postcss-scrollbar/releases/tag/0.5.0 44 | [0.4.0]: https://github.com/pascalduez/postcss-scrollbar/releases/tag/0.4.0 45 | [0.3.0]: https://github.com/pascalduez/postcss-scrollbar/releases/tag/0.3.0 46 | [0.2.1]: https://github.com/pascalduez/postcss-scrollbar/releases/tag/0.2.1 47 | [0.2.0]: https://github.com/pascalduez/postcss-scrollbar/releases/tag/0.2.0 48 | [0.1.0]: https://github.com/pascalduez/postcss-scrollbar/releases/tag/0.1.0 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # postcss-scrollbar 2 | 3 | [![npm version][npm-image]][npm-url] 4 | [![CI Status][ci-image]][ci-url] 5 | [![Coverage Status][codecov-image]][codecov-url] 6 | 7 | > [PostCSS] plugin enabling custom scrollbars 8 | 9 | Spec : https://drafts.csswg.org/css-scrollbars-1 10 | Browser support: https://caniuse.com/#feat=css-scrollbar 11 | Docs: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Scrollbars 12 | 13 | ## Installation 14 | 15 | ``` 16 | npm install postcss postcss-scrollbar --save-dev 17 | ``` 18 | 19 | or 20 | 21 | ``` 22 | yarn add postcss postcss-scrollbar --save-dev 23 | ``` 24 | 25 | ## Usage 26 | 27 | ```js 28 | const fs = require('node:fs'); 29 | const postcss = require('postcss'); 30 | const scrollbar = require('postcss-scrollbar'); 31 | 32 | let input = fs.readFileSync('input.css', 'utf8'); 33 | 34 | postcss() 35 | .use(scrollbar) 36 | .process(input) 37 | .then(result => { 38 | fs.writeFileSync('output.css', result.css); 39 | }); 40 | ``` 41 | 42 | ## Examples 43 | 44 | ```css 45 | /* input */ 46 | .scrollable { 47 | scrollbar-color: rebeccapurple green; 48 | scrollbar-width: thin; 49 | } 50 | ``` 51 | 52 | ```css 53 | /* output */ 54 | .scrollable::-webkit-scrollbar-thumb { 55 | background-color: rebeccapurple; 56 | } 57 | .scrollable::-webkit-scrollbar-track { 58 | background-color: green; 59 | } 60 | .scrollable::-webkit-scrollbar-corner { 61 | background-color: green; 62 | } 63 | .scrollable::-webkit-scrollbar { 64 | width: 8px; 65 | height: 8px; 66 | } 67 | .scrollable { 68 | -ms-overflow-style: auto; 69 | scrollbar-color: rebeccapurple green; 70 | scrollbar-width: thin; 71 | } 72 | ``` 73 | 74 | ```css 75 | /* input */ 76 | .scrollable { 77 | scrollbar-width: none; 78 | } 79 | ``` 80 | 81 | ```css 82 | /* output */ 83 | .scrollable::-webkit-scrollbar { 84 | width: 0; 85 | height: 0; 86 | } 87 | .scrollable { 88 | -ms-overflow-style: none; 89 | scrollbar-width: none; 90 | } 91 | ``` 92 | 93 | ## options 94 | 95 | ### `width` 96 | 97 | type: `String` 98 | default: `8px` 99 | Allows for setting the webkit fallbacks `width` and `height`. 100 | 101 | ### `edgeAutohide` 102 | 103 | type: `Boolean` 104 | default: `false` 105 | Allows for setting the scrollbar behaviour for the Edge Browser. 106 | `-ms-overflow-style: -ms-autohiding-scrollbar;` 107 | Edge doesn't support scrollbar styling. 108 | See https://developer.mozilla.org/fr/docs/Web/CSS/-ms-overflow-style 109 | 110 | ## Credits 111 | 112 | - [Pascal Duez](https://github.com/pascalduez) 113 | 114 | ## Licence 115 | 116 | postcss-scrollbar is [unlicensed](http://unlicense.org/). 117 | 118 | [postcss]: https://github.com/postcss/postcss 119 | [npm-url]: https://www.npmjs.org/package/postcss-scrollbar 120 | [npm-image]: http://img.shields.io/npm/v/postcss-scrollbar.svg?style=flat-square 121 | [ci-url]: https://github.com/pascalduez/postcss-scrollbar/actions/workflows/ci.yml 122 | [ci-image]: https://img.shields.io/github/actions/workflow/status/pascalduez/postcss-scrollbar/ci.yml?branch=main&style=flat-square 123 | [codecov-url]: https://codecov.io/gh/pascalduez/postcss-scrollbar 124 | [codecov-image]: https://img.shields.io/codecov/c/github/pascalduez/postcss-scrollbar.svg?style=flat-square 125 | [license-image]: http://img.shields.io/npm/l/postcss-scrollbar.svg?style=flat-square 126 | [license-url]: UNLICENSE 127 | 128 | 129 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | parsers: 2 | javascript: 3 | enable_partials: yes 4 | -------------------------------------------------------------------------------- /develop/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console, one-var */ 2 | 3 | import postcss from 'postcss'; 4 | import reporter from 'postcss-reporter'; 5 | import { stripIndent } from 'common-tags'; 6 | import plugin from '../src'; 7 | 8 | let from, to; 9 | let input = stripIndent` 10 | .test { 11 | scrollbar-width: thin; 12 | } 13 | `; 14 | 15 | postcss() 16 | .use(plugin) 17 | .use(reporter) 18 | .process(input, { from, to }) 19 | .then(result => { 20 | console.log(result.css); 21 | }) 22 | .catch(console.error); 23 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | cacheDirectory: '.jest-cache', 4 | collectCoverageFrom: ['src/*.js'], 5 | }; 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postcss-scrollbar", 3 | "version": "0.5.1", 4 | "description": "PostCSS plugin enabling custom scrollbars", 5 | "keywords": [ 6 | "css", 7 | "scrollbar", 8 | "postcss", 9 | "postcss-plugin" 10 | ], 11 | "author": { 12 | "name": "Pascal Duez", 13 | "url": "https://github.com/pascalduez" 14 | }, 15 | "homepage": "https://github.com/pascalduez/postcss-scrollbar", 16 | "bugs": "https://github.com/pascalduez/postcss-scrollbar/issues", 17 | "repository": { 18 | "type": "git", 19 | "url": "git://github.com/pascalduez/postcss-scrollbar.git" 20 | }, 21 | "license": "Unlicense", 22 | "files": [ 23 | "dist", 24 | "CHANGELOG.md", 25 | "README.md", 26 | "UNLICENSE" 27 | ], 28 | "main": "dist/index.js", 29 | "module": "dist/index.mjs", 30 | "scripts": { 31 | "lint:js": "eslint src/ test/", 32 | "lint": "run-s lint:*", 33 | "format:check": "prettier -l '{src,test}/**/*.{js,css}'", 34 | "format:write": "prettier --write '{src,test}/**/*.{js,css}'", 35 | "validate": "run-s lint format:check", 36 | "test": "jest", 37 | "test:ci": "run-s 'test --coverage'", 38 | "develop": "babel-node develop/", 39 | "prebuild": "rm -rf dist/", 40 | "build": "rollup -c", 41 | "prepare": "run-s build", 42 | "prepublishOnly": "run-s validate test" 43 | }, 44 | "dependencies": { 45 | "postcss-selector-parser": "^6.0.10", 46 | "postcss-value-parser": "^4.2.0" 47 | }, 48 | "devDependencies": { 49 | "@babel/cli": "^7.17.10", 50 | "@babel/core": "^7.18.5", 51 | "@babel/eslint-parser": "^7.18.2", 52 | "@babel/node": "^7.18.5", 53 | "@babel/plugin-proposal-do-expressions": "^7.16.7", 54 | "@babel/preset-env": "^7.18.2", 55 | "babel-eslint": "^10.1.0", 56 | "common-tags": "^1.8.2", 57 | "eslint": "^8.18.0", 58 | "eslint-config-airbnb-base": "^15.0.0", 59 | "eslint-config-prettier": "^8.5.0", 60 | "eslint-plugin-import": "^2.26.0", 61 | "eslint-plugin-jest": "^26.5.3", 62 | "jest": "^28.1.1", 63 | "npm-run-all": "^4.0.2", 64 | "postcss": "^8.4.14", 65 | "postcss-reporter": "^6.0.1", 66 | "prettier": "^2.7.1", 67 | "rollup": "^2.75.7", 68 | "rollup-plugin-babel": "^4.4.0", 69 | "rollup-plugin-json": "^4.0.0" 70 | }, 71 | "peerDependencies": { 72 | "postcss": "^8.0.0" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel'; 2 | import json from 'rollup-plugin-json'; 3 | import pkg from './package.json'; 4 | 5 | export default { 6 | plugins: [json(), babel()], 7 | external: ['postcss', 'postcss-selector-parser', 'postcss-value-parser'], 8 | input: 'src/index.js', 9 | output: [ 10 | { 11 | file: pkg.main, 12 | format: 'cjs', 13 | }, 14 | { 15 | file: pkg.module, 16 | format: 'esm', 17 | }, 18 | ], 19 | }; 20 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable consistent-return */ 2 | 3 | import selectorParser from 'postcss-selector-parser'; 4 | import valueParser from 'postcss-value-parser'; 5 | import { name } from '../package.json'; 6 | 7 | function postcssScrollbar({ width = '8px', edgeAutohide = false } = {}) { 8 | const widthMap = { 9 | none: '0', 10 | auto: 'initial', 11 | thin: width, 12 | }; 13 | 14 | const colorMap = { 15 | auto: 'initial', 16 | dark: 'initial', 17 | light: 'initial', 18 | }; 19 | 20 | function processWidth(decl, { result, Rule, Declaration }) { 21 | let { parent, value: keyword } = decl; 22 | let root = parent.parent; 23 | 24 | if (!isValidWidth(keyword)) { 25 | return decl.warn( 26 | result, 27 | 'Invalid value for property `scrollbar-width`. ' + 28 | 'Must be one of `auto | thin | none`.', 29 | { word: keyword } 30 | ); 31 | } 32 | 33 | let processor = selectorParser(selectors => { 34 | selectors.each(selector => { 35 | selector.append( 36 | selectorParser.pseudo({ 37 | value: '::-webkit-scrollbar', 38 | }) 39 | ); 40 | }); 41 | }); 42 | 43 | let newRule = new Rule({ 44 | selector: processor.processSync(parent.selector), 45 | }); 46 | 47 | newRule.append( 48 | new Declaration({ 49 | prop: 'width', 50 | value: widthMap[keyword], 51 | }), 52 | new Declaration({ 53 | prop: 'height', 54 | value: widthMap[keyword], 55 | }) 56 | ); 57 | 58 | root.insertBefore(parent, newRule); 59 | 60 | let value = do { 61 | if (edgeAutohide) { 62 | ('-ms-autohiding-scrollbar'); 63 | } else if (keyword === 'none') { 64 | ('none'); 65 | } else { 66 | ('auto'); 67 | } 68 | }; 69 | 70 | parent.insertBefore(decl, { 71 | prop: '-ms-overflow-style', 72 | value, 73 | }); 74 | } 75 | 76 | function processColor(decl, { result, Rule, Declaration }) { 77 | let { nodes } = valueParser(decl.value); 78 | 79 | if (isInvalidColor(nodes)) { 80 | return decl.warn( 81 | result, 82 | 'Invalid value for property `scrollbar-color`. ' + 83 | 'Must be one of `auto | dark | light | `.', 84 | { word: nodes[0].value } 85 | ); 86 | } 87 | 88 | function getColorValue(node) { 89 | if (colorMap[node.value]) { 90 | return colorMap[node.value]; 91 | } 92 | 93 | if (node.type === 'word') { 94 | return node.value; 95 | } 96 | 97 | if (node.type === 'function') { 98 | return decl.value.slice(node.sourceIndex, node.sourceEndIndex); 99 | } 100 | } 101 | 102 | let values = nodes 103 | .filter(value => value.type === 'word' || value.type === 'function') 104 | .reduce((acc, curr, idx) => { 105 | if (idx >= 1) { 106 | return { 107 | ...acc, 108 | track: getColorValue(curr), 109 | corner: getColorValue(curr), 110 | }; 111 | } 112 | 113 | return { 114 | thumb: getColorValue(curr), 115 | track: getColorValue(curr), 116 | corner: getColorValue(curr), 117 | }; 118 | }, {}); 119 | 120 | let { parent } = decl; 121 | let root = parent.parent; 122 | 123 | Object.keys(values).forEach(pseudo => { 124 | let processor = selectorParser(selectors => { 125 | selectors.each(selector => { 126 | selector.append( 127 | selectorParser.pseudo({ 128 | value: `::-webkit-scrollbar-${pseudo}`, 129 | }) 130 | ); 131 | }); 132 | }); 133 | 134 | let newRule = new Rule({ 135 | selector: processor.processSync(parent.selector), 136 | }).append( 137 | new Declaration({ 138 | prop: 'background-color', 139 | value: values[pseudo], 140 | }) 141 | ); 142 | 143 | root.insertBefore(parent, newRule); 144 | }); 145 | } 146 | 147 | return { 148 | postcssPlugin: name, 149 | Declaration: { 150 | 'scrollbar-width': processWidth, 151 | 'scrollbar-color': processColor, 152 | }, 153 | }; 154 | } 155 | 156 | function isValidWidth(keyword) { 157 | return /auto|thin|none/.test(keyword); 158 | } 159 | 160 | function isInvalidColor(nodes) { 161 | return ( 162 | Array.isArray(nodes) && 163 | nodes.length === 1 && 164 | !/auto|dark|light/.test(nodes[0].value) 165 | ); 166 | } 167 | 168 | postcssScrollbar.postcss = true; 169 | 170 | export default postcssScrollbar; 171 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | --- 2 | rules: 3 | import/no-extraneous-dependencies: off 4 | -------------------------------------------------------------------------------- /test/__snapshots__/color.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`color: 1`] = ` 4 | ".test::-webkit-scrollbar-thumb { 5 | background-color: rebeccapurple; 6 | } 7 | .test::-webkit-scrollbar-track { 8 | background-color: green; 9 | } 10 | .test::-webkit-scrollbar-corner { 11 | background-color: green; 12 | } 13 | .test { 14 | scrollbar-color: rebeccapurple green; 15 | }" 16 | `; 17 | 18 | exports[`color: erroneous keyword 1`] = ` 19 | ".test { 20 | scrollbar-color: blue; 21 | }" 22 | `; 23 | 24 | exports[`color: functional color 1`] = ` 25 | ".test::-webkit-scrollbar-thumb { 26 | background-color: rgb(255, 0, 0); 27 | } 28 | .test::-webkit-scrollbar-track { 29 | background-color: transparent; 30 | } 31 | .test::-webkit-scrollbar-corner { 32 | background-color: transparent; 33 | } 34 | .test { 35 | scrollbar-color: rgb(255, 0, 0) transparent; 36 | }" 37 | `; 38 | 39 | exports[`color: keyword 1`] = ` 40 | ".test::-webkit-scrollbar-thumb { 41 | background-color: initial; 42 | } 43 | .test::-webkit-scrollbar-track { 44 | background-color: initial; 45 | } 46 | .test::-webkit-scrollbar-corner { 47 | background-color: initial; 48 | } 49 | .test { 50 | scrollbar-color: dark; 51 | }" 52 | `; 53 | -------------------------------------------------------------------------------- /test/__snapshots__/control.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`control: PostCSS API 1`] = ` 4 | ".test::-webkit-scrollbar { 5 | width: 8px; 6 | height: 8px; 7 | } 8 | .test { 9 | -ms-overflow-style: auto; 10 | scrollbar-width: thin; 11 | }" 12 | `; 13 | 14 | exports[`control: no options 1`] = ` 15 | ".test::-webkit-scrollbar { 16 | width: 8px; 17 | height: 8px; 18 | } 19 | .test { 20 | -ms-overflow-style: auto; 21 | scrollbar-width: thin; 22 | }" 23 | `; 24 | 25 | exports[`control: with options 1`] = ` 26 | ".test::-webkit-scrollbar { 27 | width: 8px; 28 | height: 8px; 29 | } 30 | .test { 31 | -ms-overflow-style: auto; 32 | scrollbar-width: thin; 33 | }" 34 | `; 35 | -------------------------------------------------------------------------------- /test/__snapshots__/options.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`options: default options 1`] = ` 4 | ".test::-webkit-scrollbar { 5 | width: initial; 6 | height: initial; 7 | } 8 | .test { 9 | -ms-overflow-style: auto; 10 | scrollbar-width: auto; 11 | }" 12 | `; 13 | 14 | exports[`options: the \`edgeAutohide\` options 1`] = ` 15 | ".test::-webkit-scrollbar { 16 | width: 0; 17 | height: 0; 18 | } 19 | .test { 20 | -ms-overflow-style: -ms-autohiding-scrollbar; 21 | scrollbar-width: none; 22 | }" 23 | `; 24 | 25 | exports[`options: the \`width\` options 1`] = ` 26 | ".test::-webkit-scrollbar { 27 | width: 11px; 28 | height: 11px; 29 | } 30 | .test { 31 | -ms-overflow-style: auto; 32 | scrollbar-width: thin; 33 | }" 34 | `; 35 | -------------------------------------------------------------------------------- /test/__snapshots__/width.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`width: auto keyword 1`] = ` 4 | ".test::-webkit-scrollbar { 5 | width: initial; 6 | height: initial; 7 | } 8 | .test { 9 | -ms-overflow-style: auto; 10 | scrollbar-width: auto; 11 | }" 12 | `; 13 | 14 | exports[`width: erroneous keyword 1`] = ` 15 | ".test { 16 | scrollbar-width: 1rem; 17 | }" 18 | `; 19 | 20 | exports[`width: none keyword 1`] = ` 21 | ".test::-webkit-scrollbar { 22 | width: 0; 23 | height: 0; 24 | } 25 | .test { 26 | -ms-overflow-style: none; 27 | scrollbar-width: none; 28 | }" 29 | `; 30 | 31 | exports[`width: thin keyword 1`] = ` 32 | ".test::-webkit-scrollbar { 33 | width: 8px; 34 | height: 8px; 35 | } 36 | .test { 37 | -ms-overflow-style: auto; 38 | scrollbar-width: thin; 39 | }" 40 | `; 41 | -------------------------------------------------------------------------------- /test/color.spec.js: -------------------------------------------------------------------------------- 1 | import postcss from 'postcss'; 2 | import { stripIndent } from 'common-tags'; 3 | import plugin from '../src'; 4 | 5 | let from, to; 6 | let run = input => postcss().use(plugin).process(input, { from, to }); 7 | 8 | describe('color:', () => { 9 | test(' ', async () => { 10 | let input = stripIndent` 11 | .test { 12 | scrollbar-color: rebeccapurple green; 13 | } 14 | `; 15 | 16 | let result = await run(input); 17 | expect(result.css).toMatchSnapshot(); 18 | }); 19 | 20 | test('keyword', async () => { 21 | let input = stripIndent` 22 | .test { 23 | scrollbar-color: dark; 24 | } 25 | `; 26 | 27 | let result = await run(input); 28 | expect(result.css).toMatchSnapshot(); 29 | }); 30 | 31 | test('functional color', async () => { 32 | let input = stripIndent` 33 | .test { 34 | scrollbar-color: rgb(255, 0, 0) transparent; 35 | } 36 | `; 37 | 38 | let result = await run(input); 39 | expect(result.css).toMatchSnapshot(); 40 | }); 41 | 42 | test('erroneous keyword', async () => { 43 | let input = stripIndent` 44 | .test { 45 | scrollbar-color: blue; 46 | } 47 | `; 48 | 49 | let result = await run(input); 50 | 51 | expect(result.css).toMatchSnapshot(); 52 | expect(result.messages.length).toBeGreaterThan(0); 53 | expect(result.messages[0].type).toBe('warning'); 54 | expect(result.messages[0].text).toMatch( 55 | /Invalid value for property `scrollbar-color`/ 56 | ); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /test/control.spec.js: -------------------------------------------------------------------------------- 1 | import postcss from 'postcss'; 2 | import { stripIndent } from 'common-tags'; 3 | import { name } from '../package.json'; 4 | import plugin from '../src'; 5 | 6 | let from, to; 7 | let input = stripIndent` 8 | .test { 9 | scrollbar-width: thin; 10 | } 11 | `; 12 | 13 | describe('control:', () => { 14 | test('no options', () => 15 | postcss() 16 | .use(plugin) 17 | .process(input, { from, to }) 18 | .then(result => { 19 | expect(result.css).toMatchSnapshot(); 20 | })); 21 | 22 | test('with options', () => 23 | postcss() 24 | .use(plugin({})) 25 | .process(input, { from, to }) 26 | .then(result => { 27 | expect(result.css).toMatchSnapshot(); 28 | })); 29 | 30 | test('PostCSS API', async () => { 31 | let processor = postcss(); 32 | let result = await processor.use(plugin).process(input, { from, to }); 33 | 34 | expect(result.css).toMatchSnapshot(); 35 | expect(processor.plugins[0].postcssPlugin).toBe(name); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /test/options.spec.js: -------------------------------------------------------------------------------- 1 | import postcss from 'postcss'; 2 | import { stripIndent } from 'common-tags'; 3 | import plugin from '../src'; 4 | 5 | let from, to; 6 | 7 | describe('options:', () => { 8 | test('default options', () => { 9 | let input = stripIndent` 10 | .test { 11 | scrollbar-width: auto; 12 | } 13 | `; 14 | 15 | return postcss() 16 | .use(plugin) 17 | .process(input, { from, to }) 18 | .then(result => { 19 | expect(result.css).toMatchSnapshot(); 20 | }); 21 | }); 22 | 23 | test('the `width` options', () => { 24 | let input = stripIndent` 25 | .test { 26 | scrollbar-width: thin; 27 | } 28 | `; 29 | 30 | return postcss() 31 | .use(plugin({ width: '11px' })) 32 | .process(input, { from, to }) 33 | .then(result => { 34 | expect(result.css).toMatchSnapshot(); 35 | }); 36 | }); 37 | 38 | test('the `edgeAutohide` options', () => { 39 | let input = stripIndent` 40 | .test { 41 | scrollbar-width: none; 42 | } 43 | `; 44 | 45 | return postcss() 46 | .use(plugin({ edgeAutohide: true })) 47 | .process(input, { from, to }) 48 | .then(result => { 49 | expect(result.css).toMatchSnapshot(); 50 | }); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /test/width.spec.js: -------------------------------------------------------------------------------- 1 | import postcss from 'postcss'; 2 | import { stripIndent } from 'common-tags'; 3 | import plugin from '../src'; 4 | 5 | let from, to; 6 | 7 | describe('width:', () => { 8 | test('auto keyword', () => { 9 | let input = stripIndent` 10 | .test { 11 | scrollbar-width: auto; 12 | } 13 | `; 14 | 15 | return postcss([plugin()]) 16 | .process(input, { from, to }) 17 | .then(result => { 18 | expect(result.css).toMatchSnapshot(); 19 | }); 20 | }); 21 | 22 | test('thin keyword', () => { 23 | let input = stripIndent` 24 | .test { 25 | scrollbar-width: thin; 26 | } 27 | `; 28 | 29 | return postcss([plugin()]) 30 | .process(input, { from, to }) 31 | .then(result => { 32 | expect(result.css).toMatchSnapshot(); 33 | }); 34 | }); 35 | 36 | test('none keyword', () => { 37 | let input = stripIndent` 38 | .test { 39 | scrollbar-width: none; 40 | } 41 | `; 42 | 43 | return postcss([plugin()]) 44 | .process(input, { from, to }) 45 | .then(result => { 46 | expect(result.css).toMatchSnapshot(); 47 | }); 48 | }); 49 | 50 | test('erroneous keyword', async () => { 51 | let input = stripIndent` 52 | .test { 53 | scrollbar-width: 1rem; 54 | } 55 | `; 56 | 57 | let result = await postcss().use(plugin).process(input, { from, to }); 58 | 59 | expect(result.css).toMatchSnapshot(); 60 | expect(result.messages.length).toBeGreaterThan(0); 61 | expect(result.messages[0].type).toBe('warning'); 62 | expect(result.messages[0].text).toMatch( 63 | /Invalid value for property `scrollbar-width`/ 64 | ); 65 | }); 66 | }); 67 | --------------------------------------------------------------------------------