├── .editorconfig
├── .gitignore
├── .travis.yml
├── compositor.json
├── index.js
├── lib
├── contains-immutable-prefix.js
├── contains-mutation-from-source.js
├── get-css-classes-from-ast.js
├── get-mutations.js
├── get-warning-string.js
└── has-mutation.js
├── license
├── media
└── logo.png
├── package.json
├── readme.md
└── test
├── fixtures
├── app.css
├── basscss-mutations.css
├── basscss.css
├── bootstrap-mutations.css
├── bootstrap.css
└── vendor.css
├── get-css-classes-test.js
├── get-mutations-test.js
└── test.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 'stable'
4 | - '4'
5 | - '0.12'
6 |
--------------------------------------------------------------------------------
/compositor.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "johnotander/immutable-css",
3 | "version": "0.1.3",
4 | "libraries": {
5 | "xv": "^1.1.2"
6 | },
7 | "title": "",
8 | "branch": "",
9 | "style": {
10 | "name": "Default",
11 | "fontFamily": "-apple-system, BlinkMacSystemFont, sans-serif",
12 | "fontWeight": 400,
13 | "bold": 600,
14 | "lineHeight": 1.5,
15 | "typeScale": [
16 | 72,
17 | 48,
18 | 24,
19 | 20,
20 | 16,
21 | 14,
22 | 12
23 | ],
24 | "heading": {
25 | "fontFamily": null,
26 | "fontStyle": null,
27 | "fontWeight": 600,
28 | "lineHeight": 1.25,
29 | "textTransform": null,
30 | "letterSpacing": null,
31 | "h0": {},
32 | "h1": {},
33 | "h2": {},
34 | "h3": {},
35 | "h4": {},
36 | "h5": {},
37 | "h6": {}
38 | },
39 | "alternativeText": {},
40 | "space": [
41 | 0,
42 | 8,
43 | 16,
44 | 32,
45 | 48,
46 | 64,
47 | 96
48 | ],
49 | "layout": {
50 | "maxWidth": 1024,
51 | "centered": false
52 | },
53 | "colors": {
54 | "text": "#111",
55 | "background": "#fff",
56 | "inverted": "#fff",
57 | "primary": "#08e",
58 | "secondary": "#0e8",
59 | "highlight": "#e08",
60 | "border": "#ddd",
61 | "muted": "#eee"
62 | },
63 | "border": {
64 | "width": 1,
65 | "radius": 2
66 | },
67 | "link": {},
68 | "button": {
69 | "hover": {
70 | "boxShadow": "inset 0 0 0 999px rgba(0, 0, 0, .125)"
71 | }
72 | },
73 | "input": {},
74 | "body": {
75 | "margin": 0
76 | }
77 | },
78 | "content": [
79 | {
80 | "component": "nav/AbsoluteNav",
81 | "links": [
82 | {
83 | "href": "http://immutablecss.com",
84 | "text": "Home"
85 | },
86 | {
87 | "href": "https://github.com/johnotander/immutable-css",
88 | "text": "GitHub"
89 | },
90 | {
91 | "href": "https://npmjs.com/package/immutable-css",
92 | "text": "npm"
93 | }
94 | ]
95 | },
96 | {
97 | "component": "header/BannerHeader",
98 | "heading": "immutable-css",
99 | "subhead": "A CSS linter for immutable selectors.",
100 | "links": [
101 | {
102 | "text": "Tweet",
103 | "href": "https://twitter.com/intent/tweet?text=immutable-css%253A%2520A%2520CSS%2520linter%2520for%2520immutable%2520selectors.&url=http%253A%252F%252Fimmutablecss.com"
104 | }
105 | ],
106 | "text": "v1.1.2"
107 | },
108 | {
109 | "component": "article/BasicArticle",
110 | "text": "
<h1 align="center">\n <img width="360" src="https://rawgit.com/johnotander/immutable-css/master/media/logo.png" alt="immutable-css">\n</h1>\n\n
\n

\nBest practices suggest avoiding overriding styles from vendor libraries to prevent unwanted side effects. Base library styles should not be altered – or as Harry Roberts describes, base styles should be treated as Immutable CSS.
\nSee the interactive web app.
\nInstallation
\nnpm install --save immutable-css\n
\nUsage
\nimmutableCss.processFiles(immutableSourceCss, customCss, options)
takes two stylesheet paths, and ensures the custom CSS doesn't override any selectors contained within the immutable source.\nThis is typically best when comparing vendor CSS (Bootstrap, Tachyons, Basscss, etc.) to your app's customizations.
\nvar immutableCss = require('immutable-css')\n\nimmutableCss.processFiles('css/vendor.css', 'css/app.css')\n// => [...]\n
\nimmutableCss.processGlob(cssGlob, options)
takes a glob that matches CSS files and ensures that no stylesheet overrides selectors contained within another.\nThis is useful to ensure that CSS partials aren't mixing concerns by mutating selectors contained within another file.
\nvar immutableCss = require('immutable-css')\n\nimmutableCss.processGlob('src/css/**/*.css', { verbose: true })\n
\n\nImmutable CSS detects mutations among files by leveraging PostCSS sourcemaps. It is also best used as a PostCSS plugin in tandem with postcss-import
and postcss-reporter
.
\nvar fs = require('fs')\nvar postcss = require('postcss')\nvar import = require('postcss-import')\nvar reporter = require('postcss-reporter')\nvar immutableCss = require('immutable-css')\n\nvar css = fs.readFileSync('styles.css', 'utf8')\n\nvar mutations = postcss([import(), immutableCss(), reporter()])\n .process(css, { from: 'styles.css' })\n
\n\nvar gulp = require('gulp')\nvar postcss = require('gulp-postcss')\nvar import = require('postcss-import')\nvar reporter = require('postcss-reporter')\nvar immutableCss = require('immutable-css')\n\ngulp.task('immutable', function () {\n var processors = [\n import,\n immutableCss,\n // If you want Immutable CSS to halt the gulp pipline if there are any warnings\n // then set throwError to true\n reporter({clearMessages: true, throwError: false})\n ]\n\n gulp.src('assets/css/base.css')\n .pipe(postcss(processors))\n .pipe(gulp.dest('dist/css'))\n})\n
\n\n@import 'basscss';\n\n.button {}\n.left {}\n.something-else {}\n
\nOutput
\n⚠ .button was mutated 2 times\n[line 93, col 1]: /css/basscss.css\n[line 3, col 1]: /css/custom.css\n[immutable-css]\n⚠ .left was mutated 2 times\n[line 291, col 1]: /css/basscss.css\n[line 4, col 1]: /css/custom.css\n[immutable-css]\n
\nOptions
\n\nstrict
(Boolean): Whether class mutations are allowed in the same file. Default: false
. \nignoredClasses
(Array): List of classes to ignore for mutation violations. <br>Ex: ['.some-mutable-class']
\nimmutableClasses
(Array): List of classes to check against. <br>Ex: ['.button', '.foobar']
\nimmutablePrefixes
(Array): List of prefix regexes that are immutable. <br>Ex: [/\\.u\\-/, /\\.util\\-/]
\ncallback
(Function): Callback that receives a mutations object. <br>Ex: function (mutations) { console.log(mutations) }
\nverbose
(Boolean): Whether mutations are logged (defaults to true with PostCSS). \n
\nUsing the callback
\nImmutable CSS accepts an optional callback, which returns the mutations hash. The key is the mutated class name, the value is an array of mutating filenames.
\npostcss([\n import(),\n immutableCss({ ignoredClasses: ['.button'] }, function(mutations) {\n console.log(mutations)\n // => { '.foobar': [] }\n })\n]).process(css, { from: cssFile })\n
\n\nnpm i -g immutable-css-cli\n
\nimmutable-css css/main.css\n⚠ .button was mutated 2 times\n[line 93, col 1]: /css/_basscss.css\n[line 11, col 1]: /css/_custom.css\n[immutable-css]\n⚠ .left was mutated 2 times\n[line 291, col 1]: /css/_basscss.css\n[line 15, col 1]: /css/_custom.css\n[immutable-css]\n
\nhttps://github.com/johnotander/immutable-css-cli
\nDependencies
\n\n\n\nLicense
\nMIT
\nContributing
\n\n- Fork it
\n- Create your feature branch (
git checkout -b my-new-feature
) \n- Commit your changes (
git commit -am 'Add some feature'
) \n- Push to the branch (
git push origin my-new-feature
) \n- Create new Pull Request
\n
\nCrafted with <3 by @jxnblk & @4lpine.
\n
\n\nThis package was initially generated with yeoman and the p generator.
\n
\n",
111 | "html": "<h1 align="center">\n <img width="360" src="https://rawgit.com/johnotander/immutable-css/master/media/logo.png" alt="immutable-css">\n</h1>\n\n
\n

\nBest practices suggest avoiding overriding styles from vendor libraries to prevent unwanted side effects. Base library styles should not be altered – or as Harry Roberts describes, base styles should be treated as Immutable CSS.
\nSee the interactive web app.
\nInstallation
\nnpm install --save immutable-css\n
\nUsage
\nimmutableCss.processFiles(immutableSourceCss, customCss, options)
takes two stylesheet paths, and ensures the custom CSS doesn't override any selectors contained within the immutable source.\nThis is typically best when comparing vendor CSS (Bootstrap, Tachyons, Basscss, etc.) to your app's customizations.
\nvar immutableCss = require('immutable-css')\n\nimmutableCss.processFiles('css/vendor.css', 'css/app.css')\n// => [...]\n
\nimmutableCss.processGlob(cssGlob, options)
takes a glob that matches CSS files and ensures that no stylesheet overrides selectors contained within another.\nThis is useful to ensure that CSS partials aren't mixing concerns by mutating selectors contained within another file.
\nvar immutableCss = require('immutable-css')\n\nimmutableCss.processGlob('src/css/**/*.css', { verbose: true })\n
\n\nImmutable CSS detects mutations among files by leveraging PostCSS sourcemaps. It is also best used as a PostCSS plugin in tandem with postcss-import
and postcss-reporter
.
\nvar fs = require('fs')\nvar postcss = require('postcss')\nvar import = require('postcss-import')\nvar reporter = require('postcss-reporter')\nvar immutableCss = require('immutable-css')\n\nvar css = fs.readFileSync('styles.css', 'utf8')\n\nvar mutations = postcss([import(), immutableCss(), reporter()])\n .process(css, { from: 'styles.css' })\n
\n\nvar gulp = require('gulp')\nvar postcss = require('gulp-postcss')\nvar import = require('postcss-import')\nvar reporter = require('postcss-reporter')\nvar immutableCss = require('immutable-css')\n\ngulp.task('immutable', function () {\n var processors = [\n import,\n immutableCss,\n // If you want Immutable CSS to halt the gulp pipline if there are any warnings\n // then set throwError to true\n reporter({clearMessages: true, throwError: false})\n ]\n\n gulp.src('assets/css/base.css')\n .pipe(postcss(processors))\n .pipe(gulp.dest('dist/css'))\n})\n
\n\n@import 'basscss';\n\n.button {}\n.left {}\n.something-else {}\n
\nOutput
\n⚠ .button was mutated 2 times\n[line 93, col 1]: /css/basscss.css\n[line 3, col 1]: /css/custom.css\n[immutable-css]\n⚠ .left was mutated 2 times\n[line 291, col 1]: /css/basscss.css\n[line 4, col 1]: /css/custom.css\n[immutable-css]\n
\nOptions
\n\nstrict
(Boolean): Whether class mutations are allowed in the same file. Default: false
. \nignoredClasses
(Array): List of classes to ignore for mutation violations. <br>Ex: ['.some-mutable-class']
\nimmutableClasses
(Array): List of classes to check against. <br>Ex: ['.button', '.foobar']
\nimmutablePrefixes
(Array): List of prefix regexes that are immutable. <br>Ex: [/\\.u\\-/, /\\.util\\-/]
\ncallback
(Function): Callback that receives a mutations object. <br>Ex: function (mutations) { console.log(mutations) }
\nverbose
(Boolean): Whether mutations are logged (defaults to true with PostCSS). \n
\nUsing the callback
\nImmutable CSS accepts an optional callback, which returns the mutations hash. The key is the mutated class name, the value is an array of mutating filenames.
\npostcss([\n import(),\n immutableCss({ ignoredClasses: ['.button'] }, function(mutations) {\n console.log(mutations)\n // => { '.foobar': [] }\n })\n]).process(css, { from: cssFile })\n
\n\nnpm i -g immutable-css-cli\n
\nimmutable-css css/main.css\n⚠ .button was mutated 2 times\n[line 93, col 1]: /css/_basscss.css\n[line 11, col 1]: /css/_custom.css\n[immutable-css]\n⚠ .left was mutated 2 times\n[line 291, col 1]: /css/_basscss.css\n[line 15, col 1]: /css/_custom.css\n[immutable-css]\n
\nhttps://github.com/johnotander/immutable-css-cli
\nDependencies
\n\n\n\nLicense
\nMIT
\nContributing
\n\n- Fork it
\n- Create your feature branch (
git checkout -b my-new-feature
) \n- Commit your changes (
git commit -am 'Add some feature'
) \n- Push to the branch (
git push origin my-new-feature
) \n- Create new Pull Request
\n
\nCrafted with <3 by @jxnblk & @4lpine.
\n
\n\nThis package was initially generated with yeoman and the p generator.
\n
\n"
112 | },
113 | {
114 | "component": "footer/BasicFooter",
115 | "links": [
116 | {
117 | "href": "https://github.com/johnotander/immutable-css",
118 | "text": "GitHub"
119 | },
120 | {
121 | "href": "https://github.com/johnotander",
122 | "text": "johnotander"
123 | }
124 | ]
125 | }
126 | ]
127 | }
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | var fs = require('fs')
4 | var glob = require('glob')
5 | var isCss = require('is-css')
6 | var postcss = require('postcss')
7 | var fileExists = require('file-exists')
8 | var getCssClasses = require('get-css-classes')
9 | var extendOptions = require('extend-options')
10 |
11 | var hasMutation = require('./lib/has-mutation')
12 | var getWarningString = require('./lib/get-warning-string')
13 | var containsMutationFromSource = require('./lib/contains-mutation-from-source')
14 |
15 | var immutableCss = postcss.plugin('immutable-css', function (opts, cb) {
16 | return function immutableCss (root, result) {
17 | var mutationsMap = {}
18 |
19 | if (typeof opts === 'function') {
20 | cb = opts
21 | opts = {}
22 | }
23 |
24 | cb = cb || function () {}
25 | opts = extendOptions({
26 | immutableClasses: [],
27 | immutablePrefixes: [],
28 | ignoredClasses: [],
29 | strict: false
30 | }, opts || {})
31 |
32 | root.walkRules(function (rule) {
33 | rule.selectors.forEach(function (selector) {
34 | getCssClasses(selector).forEach(function (klass) {
35 | mutationsMap[klass] = mutationsMap[klass] || []
36 |
37 | var klassSource = rule.source.input.from
38 | var klassLine = rule.source.start.line
39 | var klassColumn = rule.source.start.column
40 |
41 | if (containsMutationFromSource(klassSource, mutationsMap[klass]) && !opts.strict) {
42 | return
43 | }
44 |
45 | mutationsMap[klass].push({
46 | selector: klass,
47 | line: klassLine,
48 | column: klassColumn,
49 | rule: rule
50 | })
51 | })
52 | })
53 | })
54 |
55 | Object.keys(mutationsMap).forEach(function (mutationClass) {
56 | if (hasMutation(mutationClass, mutationsMap, opts)) {
57 | result.warn(getWarningString(mutationsMap[mutationClass]))
58 | } else {
59 | delete mutationsMap[mutationClass]
60 | }
61 | })
62 |
63 | cb(mutationsMap)
64 | }
65 | })
66 |
67 | module.exports = immutableCss
68 |
69 | module.exports.processFiles = function processFiles (immutableCssFile, customCssFile, options) {
70 | if (isValidFile(immutableCssFile) && isValidFile(customCssFile)) {
71 | var immutableCssSrc = fs.readFileSync(immutableCssFile, 'utf8')
72 | var customCssSrc = fs.readFileSync(customCssFile, 'utf8')
73 |
74 | // Concatenate the files with source maps.
75 | var immutableRoot = postcss.parse(immutableCssSrc, { from: immutableCssFile })
76 | var customRoot = postcss.parse(customCssSrc, { from: customCssFile })
77 | immutableRoot.append(customRoot)
78 |
79 | return immutableCss.process(immutableRoot, options).messages
80 | } else {
81 | console.error('immutable-css expected two CSS files')
82 | }
83 | }
84 |
85 | module.exports.processGlob = function processGlob (cssGlob, options) {
86 | var files = glob.sync(cssGlob)
87 | var root = null
88 |
89 | files.forEach(function (file) {
90 | if (isValidFile(file)) {
91 | var css = fs.readFileSync(file, 'utf8')
92 | var newRoot = postcss.parse(css, { from: file })
93 |
94 | if (root) {
95 | root.append(newRoot)
96 | } else {
97 | root = newRoot
98 | }
99 | } else {
100 | console.error(file + ' is an invalid file')
101 | }
102 | })
103 |
104 | return immutableCss.process(root, options).messages
105 | }
106 |
107 | function isValidFile (file) {
108 | return isCss(file) && fileExists(file)
109 | }
110 |
--------------------------------------------------------------------------------
/lib/contains-immutable-prefix.js:
--------------------------------------------------------------------------------
1 | module.exports = function containsImmutablePrefix (mutationClass, opts) {
2 | return opts.immutablePrefixes.some(function (prefix) {
3 | return prefix.test(mutationClass)
4 | })
5 | }
6 |
--------------------------------------------------------------------------------
/lib/contains-mutation-from-source.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | module.exports = function containsMutationFromSource (source, mutations) {
4 | return mutations.some(function (mutation) {
5 | return mutation.rule.source.input.from === source
6 | })
7 | }
8 |
--------------------------------------------------------------------------------
/lib/get-css-classes-from-ast.js:
--------------------------------------------------------------------------------
1 | var getCssClasses = require('get-css-classes')
2 |
3 | module.exports = function getCssClassesFromAst (cssObject) {
4 | cssClasses = {}
5 |
6 | cssObject.walkRules(function (rule) {
7 | rule.selectors.forEach(function (selector) {
8 | getCssClasses(selector, { keepPseudos: true }).forEach(function (cssClass) {
9 | cssClasses[cssClass] = true
10 | })
11 | })
12 | })
13 |
14 | return cssClasses
15 | }
16 |
--------------------------------------------------------------------------------
/lib/get-mutations.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | // This module is no longer used for any internal immutable-css functionality
4 | // however, it is currently still being used for the immutablecss.com web app.
5 | // So, we will leave this in for now.
6 |
7 | var postcss = require('postcss')
8 | var getCssClasses = require('get-css-classes')
9 | var getCssClassesFromAst = require('./get-css-classes-from-ast')
10 |
11 | var ignoredSelectors, immutableSelectors, immutableClasses
12 |
13 | module.exports = function getMutations (immutableCss, customCss, options) {
14 | options = options || {}
15 | ignoredSelectors = options.ignoredSelectors || []
16 | immutableSelectors = options.immutableSelectors || []
17 |
18 | var immutableAst = postcss.parse(immutableCss)
19 | var customAst = postcss.parse(customCss)
20 |
21 | immutableClasses = getCssClassesFromAst(immutableAst)
22 |
23 | var immutableErrors = []
24 | customAst.walkRules(function (rule) {
25 | rule.selectors.forEach(function (selector) {
26 | getCssClasses(selector).forEach(function (classSelector) {
27 | if (hasMutation(classSelector)) {
28 | immutableErrors.push({
29 | selector: classSelector,
30 | line: rule.source.start.line,
31 | column: rule.source.start.column,
32 | rule: rule
33 | })
34 | }
35 | })
36 | })
37 | })
38 |
39 | return immutableErrors
40 | }
41 |
42 | function hasMutation (classSelector) {
43 | return (immutableClasses[classSelector] || immutableSelectors.indexOf(classSelector) != -1) &&
44 | ignoredSelectors.indexOf(classSelector) == -1
45 | }
46 |
--------------------------------------------------------------------------------
/lib/get-warning-string.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | module.exports = function getWarningString (mutations) {
4 | var warning = mutations[0].selector + ' was mutated ' + mutations.length + ' times\n'
5 |
6 | mutations.forEach(function (mutation) {
7 | warning += '[line ' + mutation.line + ', col ' + mutation.column + ']: ' + mutation.rule.source.input.from + '\n'
8 | })
9 |
10 | return warning
11 | }
12 |
--------------------------------------------------------------------------------
/lib/has-mutation.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | var containsImmutablePrefix = require('./contains-immutable-prefix')
4 |
5 | module.exports = function hasMutation (mutationClass, mutationsMap, opts) {
6 | return (mutationsMap[mutationClass].length > 1 || // class has been seen
7 | opts.immutableClasses.indexOf(mutationClass) !== -1 || // is contained in the immutable classes array
8 | containsImmutablePrefix(mutationClass, opts)) && // is an immutable prefix
9 | opts.ignoredClasses.indexOf(mutationClass) === -1 // is not in the ignored classes array
10 | }
11 |
--------------------------------------------------------------------------------
/license:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) John Otander (johnotander.com)
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
13 | all 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
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/media/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johno/immutable-css/1179af19d5b2509992c8be5ee86bed3ef9470560/media/logo.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "immutable-css",
3 | "description": "A CSS linter for immutable selectors.",
4 | "author": "John Otander",
5 | "version": "1.1.2",
6 | "main": "index.js",
7 | "directories": {
8 | "test": "test"
9 | },
10 | "scripts": {
11 | "test": "mocha test"
12 | },
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/johnotander/immutable-css.git"
16 | },
17 | "keywords": [
18 | "css",
19 | "immutable",
20 | "mutability",
21 | "immutablecss",
22 | "immutability",
23 | "mutation",
24 | "lint",
25 | "linter"
26 | ],
27 | "license": "MIT",
28 | "bugs": {
29 | "url": "https://github.com/johnotander/immutable-css/issues"
30 | },
31 | "homepage": "https://github.com/johnotander/immutable-css",
32 | "dependencies": {
33 | "extend-options": "0.0.1",
34 | "file-exists": "^0.1.1",
35 | "get-css-classes": "1.1.0",
36 | "glob": "^5.0.14",
37 | "has-class-selector": "1.0.0",
38 | "is-css": "^1.0.0",
39 | "meow": "^3.3.0",
40 | "postcss": "^5.0.10",
41 | "specificity": "^0.1.4"
42 | },
43 | "devDependencies": {
44 | "mocha": "*",
45 | "postcss-import": "^7.1.0",
46 | "postcss-reporter": "^1.3.0"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | [](https://travis-ci.org/johnotander/immutable-css) [](https://github.com/feross/standard)
6 |
7 | Best practices suggest avoiding overriding styles from vendor libraries to prevent unwanted side effects. Base library styles should not be altered – or as Harry Roberts describes, base styles should be treated as [Immutable CSS](http://csswizardry.com/2015/03/immutable-css/).
8 |
9 | See the interactive [web app](http://immutablecss.com).
10 |
11 | ## Installation
12 |
13 | ```bash
14 | npm install --save immutable-css
15 | ```
16 |
17 | ## Usage
18 |
19 | `immutableCss.processFiles(immutableSourceCss, customCss, options)` takes two stylesheet paths, and ensures the custom CSS doesn't override any selectors contained within the immutable source.
20 | This is typically best when comparing vendor CSS ([Bootstrap](http://getbootstrap.com), [Tachyons](http://tachyons.io), [Basscss](http://basscss.com), etc.) to your app's customizations.
21 |
22 | ```js
23 | var immutableCss = require('immutable-css')
24 |
25 | immutableCss.processFiles('css/vendor.css', 'css/app.css')
26 | // => [...]
27 |
28 | ```
29 |
30 | `immutableCss.processGlob(cssGlob, options)` takes a glob that matches CSS files and ensures that no stylesheet overrides selectors contained within another.
31 | This is useful to ensure that CSS partials aren't mixing concerns by mutating selectors contained within another file.
32 |
33 | ```js
34 | var immutableCss = require('immutable-css')
35 |
36 | immutableCss.processGlob('src/css/**/*.css', { verbose: true })
37 | ```
38 |
39 | ### Using with [PostCSS](https://github.com/postcss/postcss)
40 |
41 | Immutable CSS detects mutations among files by leveraging PostCSS sourcemaps. It is also best used as a PostCSS plugin in tandem with `postcss-import` and `postcss-reporter`.
42 |
43 | ```js
44 | var fs = require('fs')
45 | var postcss = require('postcss')
46 | var import = require('postcss-import')
47 | var reporter = require('postcss-reporter')
48 | var immutableCss = require('immutable-css')
49 |
50 | var css = fs.readFileSync('styles.css', 'utf8')
51 |
52 | var mutations = postcss([import(), immutableCss(), reporter()])
53 | .process(css, { from: 'styles.css' })
54 | ```
55 |
56 | ### Using with [Gulp](http://gulpjs.com)
57 |
58 | ```js
59 | var gulp = require('gulp')
60 | var postcss = require('gulp-postcss')
61 | var import = require('postcss-import')
62 | var reporter = require('postcss-reporter')
63 | var immutableCss = require('immutable-css')
64 |
65 | gulp.task('immutable', function () {
66 | var processors = [
67 | import,
68 | immutableCss,
69 | // If you want Immutable CSS to halt the gulp pipline if there are any warnings
70 | // then set throwError to true
71 | reporter({clearMessages: true, throwError: false})
72 | ]
73 |
74 | gulp.src('assets/css/base.css')
75 | .pipe(postcss(processors))
76 | .pipe(gulp.dest('dist/css'))
77 | })
78 | ```
79 |
80 | #### Input
81 |
82 | ```css
83 | @import 'basscss';
84 |
85 | .button {}
86 | .left {}
87 | .something-else {}
88 | ```
89 |
90 | #### Output
91 |
92 | ```sh
93 | ⚠ .button was mutated 2 times
94 | [line 93, col 1]: /css/basscss.css
95 | [line 3, col 1]: /css/custom.css
96 | [immutable-css]
97 | ⚠ .left was mutated 2 times
98 | [line 291, col 1]: /css/basscss.css
99 | [line 4, col 1]: /css/custom.css
100 | [immutable-css]
101 | ```
102 |
103 | ### Options
104 |
105 | * `strict` (Boolean): Whether class mutations are allowed in the same file. Default: `false`.
106 | * `ignoredClasses` (Array): List of classes to ignore for mutation violations.
Ex: `['.some-mutable-class']`
107 | * `immutableClasses` (Array): List of classes to check against.
Ex: `['.button', '.foobar']`
108 | * `immutablePrefixes` (Array): List of prefix regexes that are immutable.
Ex: `[/\.u\-/, /\.util\-/]`
109 | * `callback` (Function): Callback that receives a mutations object.
Ex: `function (mutations) { console.log(mutations) }`
110 | * `verbose` (Boolean): Whether mutations are logged (defaults to true with PostCSS).
111 |
112 | #### Using the callback
113 |
114 | Immutable CSS accepts an optional callback, which returns the mutations hash. The key is the mutated class name, the value is an array of mutating filenames.
115 |
116 | ```js
117 | postcss([
118 | import(),
119 | immutableCss({ ignoredClasses: ['.button'] }, function(mutations) {
120 | console.log(mutations)
121 | // => { '.foobar': [] }
122 | })
123 | ]).process(css, { from: cssFile })
124 | ```
125 |
126 | ### Using the [immutable-css-cli](https://github.com/johnotander/immutable-css-cli)
127 |
128 | ```sh
129 | npm i -g immutable-css-cli
130 | ```
131 |
132 | ```sh
133 | immutable-css css/main.css
134 | ⚠ .button was mutated 2 times
135 | [line 93, col 1]: /css/_basscss.css
136 | [line 11, col 1]: /css/_custom.css
137 | [immutable-css]
138 | ⚠ .left was mutated 2 times
139 | [line 291, col 1]: /css/_basscss.css
140 | [line 15, col 1]: /css/_custom.css
141 | [immutable-css]
142 | ```
143 |
144 |
145 |
146 | ## Dependencies
147 |
148 | *
149 | *
150 | *
151 |
152 | ## Related Reading
153 |
154 | *
155 | *
156 | *
157 | *
158 | *
159 |
160 | ## License
161 |
162 | MIT
163 |
164 | ## Contributing
165 |
166 | 1. Fork it
167 | 2. Create your feature branch (`git checkout -b my-new-feature`)
168 | 3. Commit your changes (`git commit -am 'Add some feature'`)
169 | 4. Push to the branch (`git push origin my-new-feature`)
170 | 5. Create new Pull Request
171 |
172 | Crafted with <3 by [@jxnblk](https://twitter.com/jxnblk) & [@4lpine](https://twitter.com/4lpine).
173 |
174 | ***
175 |
176 | > This package was initially generated with [yeoman](http://yeoman.io) and the [p generator](https://github.com/johnotander/generator-p.git).
177 |
--------------------------------------------------------------------------------
/test/fixtures/app.css:
--------------------------------------------------------------------------------
1 | .bar:before {
2 | content: 'baz';
3 | }
4 |
5 | .awesome:before {
6 | color: green;
7 | }
8 |
9 | :root {
10 | color: purple;
11 | }
12 |
13 | .bar.awesome {
14 | color: hotpink;
15 | }
16 |
17 | .sibling + .foo {
18 | color: yellow;
19 | }
20 |
21 | .foobar > .awesome {
22 | color: tomato;
23 | }
24 |
25 | a.awesome {
26 | color: rebeccapurple;
27 | }
28 |
--------------------------------------------------------------------------------
/test/fixtures/basscss-mutations.css:
--------------------------------------------------------------------------------
1 | @import './test/fixtures/basscss';
2 |
3 | .this-is-not-a-mutation {
4 | color: green;
5 | }
6 |
7 | input {
8 | border: none;
9 | }
10 |
11 | .button {
12 | color: blue;
13 | }
14 |
15 | .left {
16 | float: right /* :) */
17 | }
18 |
--------------------------------------------------------------------------------
/test/fixtures/basscss.css:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Basscss v5.0.1
4 |
5 | Low-level CSS toolkit
6 | http://basscss.com
7 |
8 | */
9 |
10 | body,
11 | button {
12 | margin: 0;
13 | }
14 |
15 | button,
16 | input,
17 | select,
18 | textarea {
19 | font-family: inherit;
20 | font-size: 100%;
21 | }
22 |
23 | img {
24 | max-width: 100%;
25 | }
26 |
27 | svg {
28 | max-height: 100%;
29 | }
30 |
31 | /* Basscss Base Forms */
32 |
33 | input,
34 | select,
35 | textarea,
36 | fieldset {
37 | font-size: 1rem;
38 | margin-top: 0;
39 | margin-bottom: .5rem;
40 | }
41 |
42 | input[type=text],
43 | input[type=datetime],
44 | input[type=datetime-local],
45 | input[type=email],
46 | input[type=month],
47 | input[type=number],
48 | input[type=password],
49 | input[type=search],
50 | input[type=tel],
51 | input[type=time],
52 | input[type=url],
53 | input[type=week] {
54 | box-sizing: border-box;
55 | height: 2.25rem;
56 | padding: .5rem .5rem;
57 | vertical-align: middle;
58 | -webkit-appearance: none;
59 | }
60 |
61 | select {
62 | box-sizing: border-box;
63 | line-height: 1.75;
64 | padding: .5rem .5rem;
65 | }
66 |
67 | select:not([multiple]) {
68 | height: 2.25rem;
69 | vertical-align: middle;
70 | }
71 |
72 | textarea {
73 | box-sizing: border-box;
74 | line-height: 1.75;
75 | padding: .5rem .5rem;
76 | }
77 |
78 | .fieldset-reset {
79 | padding: 0;
80 | margin-left: 0;
81 | margin-right: 0;
82 | border: 0;
83 | }
84 |
85 | .fieldset-reset legend {
86 | padding: 0;
87 | }
88 |
89 |
90 |
91 | /* Basscss Base Buttons */
92 |
93 | button,
94 | .button {
95 | font-size: inherit;
96 | font-weight: bold;
97 | text-decoration: none;
98 | cursor: pointer;
99 | display: inline-block;
100 | box-sizing: border-box;
101 | line-height: 1.125rem;
102 | padding: .5rem 1rem;
103 | margin: 0;
104 | height: auto;
105 | border: 1px solid transparent;
106 | vertical-align: middle;
107 | -webkit-appearance: none;
108 | }
109 |
110 | ::-moz-focus-inner {
111 | border: 0;
112 | padding: 0;
113 | }
114 |
115 | .button:hover {
116 | text-decoration: none;
117 | }
118 |
119 |
120 |
121 | /* Basscss Base Tables */
122 |
123 | table {
124 | border-collapse: separate;
125 | border-spacing: 0;
126 | max-width: 100%;
127 | width: 100%;
128 | }
129 |
130 | th {
131 | text-align: left;
132 | font-weight: bold;
133 | }
134 |
135 | th,
136 | td {
137 | padding: .25rem 1rem;
138 | line-height: inherit;
139 | }
140 |
141 | th {
142 | vertical-align: bottom;
143 | }
144 |
145 | td {
146 | vertical-align: top;
147 | }
148 |
149 |
150 |
151 | /* Basscss Base Typography */
152 |
153 | body {
154 | font-family: 'Helvetica Neue', Helvetica, sans-serif;
155 | line-height: 1.5;
156 | font-size: 100%;
157 | }
158 |
159 | h1,
160 | h2,
161 | h3,
162 | h4,
163 | h5,
164 | h6 {
165 | font-family: 'Helvetica Neue', Helvetica, sans-serif;
166 | font-weight: bold;
167 | line-height: 1.25;
168 | margin-top: 1em;
169 | margin-bottom: .5em;
170 | }
171 |
172 | p,
173 | dl,
174 | ol,
175 | ul {
176 | font-size: 1rem;
177 | margin-top: 0;
178 | margin-bottom: 1rem;
179 | }
180 |
181 | ol,
182 | ul {
183 | padding-left: 2rem;
184 | }
185 |
186 | pre,
187 | code,
188 | samp {
189 | font-family: 'Source Code Pro', Consolas, monospace;
190 | font-size: inherit;
191 | }
192 |
193 | pre {
194 | margin-top: 0;
195 | margin-bottom: 1rem;
196 | overflow-x: scroll;
197 | }
198 |
199 | hr {
200 | margin-top: 2rem;
201 | margin-bottom: 2rem;
202 | }
203 |
204 | blockquote {
205 | margin-top: 2rem;
206 | margin-bottom: 2rem;
207 | margin-left: 0;
208 | padding-left: 1rem;
209 | padding-right: 1rem;
210 | }
211 |
212 | blockquote,
213 | blockquote p {
214 | font-size: 1.25rem;
215 | font-style: italic;
216 | }
217 |
218 | h1,
219 | .h1 {
220 | font-size: 2rem;
221 | }
222 |
223 | h2,
224 | .h2 {
225 | font-size: 1.5rem;
226 | }
227 |
228 | h3,
229 | .h3 {
230 | font-size: 1.25rem;
231 | }
232 |
233 | h4,
234 | .h4 {
235 | font-size: 1rem;
236 | }
237 |
238 | h5,
239 | .h5 {
240 | font-size: .875rem;
241 | }
242 |
243 | h6,
244 | .h6 {
245 | font-size: .75rem;
246 | }
247 |
248 | .list-reset {
249 | list-style: none;
250 | padding-left: 0;
251 | }
252 |
253 |
254 |
255 | /* Basscss Utility Layout */
256 |
257 | .inline {
258 | display: inline;
259 | }
260 |
261 | .block {
262 | display: block;
263 | }
264 |
265 | .inline-block {
266 | display: inline-block;
267 | }
268 |
269 | .overflow-hidden {
270 | overflow: hidden;
271 | }
272 |
273 | .overflow-scroll {
274 | overflow: scroll;
275 | }
276 |
277 | .overflow-auto {
278 | overflow: auto;
279 | }
280 |
281 | .clearfix:before,
282 | .clearfix:after {
283 | content: " ";
284 | display: table;
285 | }
286 |
287 | .clearfix:after {
288 | clear: both;
289 | }
290 |
291 | .left {
292 | float: left;
293 | }
294 |
295 | .right {
296 | float: right;
297 | }
298 |
299 | .fit {
300 | max-width: 100%;
301 | }
302 |
303 | .half-width {
304 | width: 50%;
305 | }
306 |
307 | .full-width {
308 | width: 100%;
309 | }
310 |
311 | /* Basscss Utility Typography */
312 |
313 | .bold {
314 | font-weight: bold;
315 | }
316 |
317 | .regular {
318 | font-weight: normal;
319 | }
320 |
321 | .italic {
322 | font-style: italic;
323 | }
324 |
325 | .caps {
326 | text-transform: uppercase;
327 | letter-spacing: .2em;
328 | }
329 |
330 | .left-align {
331 | text-align: left;
332 | }
333 |
334 | .center {
335 | text-align: center;
336 | }
337 |
338 | .right-align {
339 | text-align: right;
340 | }
341 |
342 | .justify {
343 | text-align: justify;
344 | }
345 |
346 | .nowrap {
347 | white-space: nowrap;
348 | }
349 |
350 |
351 |
352 | /* Basscss Utility White Space */
353 |
354 | .m0 {
355 | margin: 0;
356 | }
357 |
358 | .mt0 {
359 | margin-top: 0;
360 | }
361 |
362 | .mr0 {
363 | margin-right: 0;
364 | }
365 |
366 | .mb0 {
367 | margin-bottom: 0;
368 | }
369 |
370 | .ml0 {
371 | margin-left: 0;
372 | }
373 |
374 | .m1 {
375 | margin: .5rem;
376 | }
377 |
378 | .mt1 {
379 | margin-top: .5rem;
380 | }
381 |
382 | .mr1 {
383 | margin-right: .5rem;
384 | }
385 |
386 | .mb1 {
387 | margin-bottom: .5rem;
388 | }
389 |
390 | .ml1 {
391 | margin-left: .5rem;
392 | }
393 |
394 | .m2 {
395 | margin: 1rem;
396 | }
397 |
398 | .mt2 {
399 | margin-top: 1rem;
400 | }
401 |
402 | .mr2 {
403 | margin-right: 1rem;
404 | }
405 |
406 | .mb2 {
407 | margin-bottom: 1rem;
408 | }
409 |
410 | .ml2 {
411 | margin-left: 1rem;
412 | }
413 |
414 | .m3 {
415 | margin: 2rem;
416 | }
417 |
418 | .mt3 {
419 | margin-top: 2rem;
420 | }
421 |
422 | .mr3 {
423 | margin-right: 2rem;
424 | }
425 |
426 | .mb3 {
427 | margin-bottom: 2rem;
428 | }
429 |
430 | .ml3 {
431 | margin-left: 2rem;
432 | }
433 |
434 | .m4 {
435 | margin: 4rem;
436 | }
437 |
438 | .mt4 {
439 | margin-top: 4rem;
440 | }
441 |
442 | .mr4 {
443 | margin-right: 4rem;
444 | }
445 |
446 | .mb4 {
447 | margin-bottom: 4rem;
448 | }
449 |
450 | .ml4 {
451 | margin-left: 4rem;
452 | }
453 |
454 | .mxn1 {
455 | margin-left: -.5rem;
456 | margin-right: -.5rem;
457 | }
458 |
459 | .mxn2 {
460 | margin-left: -1rem;
461 | margin-right: -1rem;
462 | }
463 |
464 | .mxn3 {
465 | margin-left: -2rem;
466 | margin-right: -2rem;
467 | }
468 |
469 | .mxn4 {
470 | margin-left: -4rem;
471 | margin-right: -4rem;
472 | }
473 |
474 | .mx-auto {
475 | margin-left: auto;
476 | margin-right: auto;
477 | }
478 |
479 | .p1 {
480 | padding: .5rem;
481 | }
482 |
483 | .py1 {
484 | padding-top: .5rem;
485 | padding-bottom: .5rem;
486 | }
487 |
488 | .px1 {
489 | padding-left: .5rem;
490 | padding-right: .5rem;
491 | }
492 |
493 | .p2 {
494 | padding: 1rem;
495 | }
496 |
497 | .py2 {
498 | padding-top: 1rem;
499 | padding-bottom: 1rem;
500 | }
501 |
502 | .px2 {
503 | padding-left: 1rem;
504 | padding-right: 1rem;
505 | }
506 |
507 | .p3 {
508 | padding: 2rem;
509 | }
510 |
511 | .py3 {
512 | padding-top: 2rem;
513 | padding-bottom: 2rem;
514 | }
515 |
516 | .px3 {
517 | padding-left: 2rem;
518 | padding-right: 2rem;
519 | }
520 |
521 | .p4 {
522 | padding: 4rem;
523 | }
524 |
525 | .py4 {
526 | padding-top: 4rem;
527 | padding-bottom: 4rem;
528 | }
529 |
530 | .px4 {
531 | padding-left: 4rem;
532 | padding-right: 4rem;
533 | }
534 |
535 |
536 |
537 | /* Basscss Utility Responsive States */
538 |
539 | .sm-show,
540 | .md-show,
541 | .lg-show {
542 | display: none !important;
543 | }
544 |
545 | @media (min-width: 40em) {
546 | .sm-show {
547 | display: block !important;
548 | }
549 | }
550 |
551 | @media (min-width: 52em) {
552 | .md-show {
553 | display: block !important;
554 | }
555 | }
556 |
557 | @media (min-width: 64em) {
558 | .lg-show {
559 | display: block !important;
560 | }
561 | }
562 |
563 | @media (min-width: 40em) {
564 | .sm-hide {
565 | display: none !important;
566 | }
567 | }
568 |
569 | @media (min-width: 52em) {
570 | .md-hide {
571 | display: none !important;
572 | }
573 | }
574 |
575 | @media (min-width: 64em) {
576 | .lg-hide {
577 | display: none !important;
578 | }
579 | }
580 |
581 | .display-none {
582 | display: none !important;
583 | }
584 |
585 | .hide {
586 | position: absolute !important;
587 | height: 1px;
588 | width: 1px;
589 | overflow: hidden;
590 | clip: rect(1px, 1px, 1px, 1px);
591 | }
592 |
593 | /* Basscss Positions */
594 |
595 | .relative {
596 | position: relative;
597 | }
598 |
599 | .absolute {
600 | position: absolute;
601 | }
602 |
603 | .fixed {
604 | position: fixed;
605 | }
606 |
607 | .top-0 {
608 | top: 0;
609 | }
610 |
611 | .right-0 {
612 | right: 0;
613 | }
614 |
615 | .bottom-0 {
616 | bottom: 0;
617 | }
618 |
619 | .left-0 {
620 | left: 0;
621 | }
622 |
623 | .z1 {
624 | z-index: 1;
625 | }
626 |
627 | .z2 {
628 | z-index: 2;
629 | }
630 |
631 | .z3 {
632 | z-index: 3;
633 | }
634 |
635 | .z4 {
636 | z-index: 4;
637 | }
638 |
639 | .absolute-center {
640 | top: 0;
641 | right: 0;
642 | bottom: 0;
643 | left: 0;
644 | margin: auto;
645 | display: table;
646 | }
647 |
648 | /* Basscss UI Utility Button Sizes */
649 |
650 | .button-small {
651 | padding: .25rem .5rem;
652 | }
653 |
654 | .button-big {
655 | padding: 1rem 1.25rem;
656 | }
657 |
658 | .button-narrow {
659 | padding-left: .5rem;
660 | padding-right: .5rem;
661 | }
662 |
663 |
664 |
665 | /* Basscss Grid */
666 |
667 | .container {
668 | max-width: 64em;
669 | margin-left: auto;
670 | margin-right: auto;
671 | }
672 |
673 | .col {
674 | float: left;
675 | box-sizing: border-box;
676 | }
677 |
678 | .col-right {
679 | float: right;
680 | box-sizing: border-box;
681 | }
682 |
683 | .col-1 {
684 | width: 8.333333333333332%;
685 | }
686 |
687 | .col-2 {
688 | width: 16.666666666666664%;
689 | }
690 |
691 | .col-3 {
692 | width: 25%;
693 | }
694 |
695 | .col-4 {
696 | width: 33.33333333333333%;
697 | }
698 |
699 | .col-5 {
700 | width: 41.66666666666667%;
701 | }
702 |
703 | .col-6 {
704 | width: 50%;
705 | }
706 |
707 | .col-7 {
708 | width: 58.333333333333336%;
709 | }
710 |
711 | .col-8 {
712 | width: 66.66666666666666%;
713 | }
714 |
715 | .col-9 {
716 | width: 75%;
717 | }
718 |
719 | .col-10 {
720 | width: 83.33333333333334%;
721 | }
722 |
723 | .col-11 {
724 | width: 91.66666666666666%;
725 | }
726 |
727 | .col-12 {
728 | width: 100%;
729 | }
730 |
731 | @media (min-width: 40em) {
732 | .sm-col {
733 | float: left;
734 | box-sizing: border-box;
735 | }
736 |
737 | .sm-col-right {
738 | float: right;
739 | box-sizing: border-box;
740 | }
741 |
742 | .sm-col-1 {
743 | width: 8.333333333333332%;
744 | }
745 |
746 | .sm-col-2 {
747 | width: 16.666666666666664%;
748 | }
749 |
750 | .sm-col-3 {
751 | width: 25%;
752 | }
753 |
754 | .sm-col-4 {
755 | width: 33.33333333333333%;
756 | }
757 |
758 | .sm-col-5 {
759 | width: 41.66666666666667%;
760 | }
761 |
762 | .sm-col-6 {
763 | width: 50%;
764 | }
765 |
766 | .sm-col-7 {
767 | width: 58.333333333333336%;
768 | }
769 |
770 | .sm-col-8 {
771 | width: 66.66666666666666%;
772 | }
773 |
774 | .sm-col-9 {
775 | width: 75%;
776 | }
777 |
778 | .sm-col-10 {
779 | width: 83.33333333333334%;
780 | }
781 |
782 | .sm-col-11 {
783 | width: 91.66666666666666%;
784 | }
785 |
786 | .sm-col-12 {
787 | width: 100%;
788 | }
789 | }
790 |
791 | @media (min-width: 52em) {
792 | .md-col {
793 | float: left;
794 | box-sizing: border-box;
795 | }
796 |
797 | .md-col-right {
798 | float: right;
799 | box-sizing: border-box;
800 | }
801 |
802 | .md-col-1 {
803 | width: 8.333333333333332%;
804 | }
805 |
806 | .md-col-2 {
807 | width: 16.666666666666664%;
808 | }
809 |
810 | .md-col-3 {
811 | width: 25%;
812 | }
813 |
814 | .md-col-4 {
815 | width: 33.33333333333333%;
816 | }
817 |
818 | .md-col-5 {
819 | width: 41.66666666666667%;
820 | }
821 |
822 | .md-col-6 {
823 | width: 50%;
824 | }
825 |
826 | .md-col-7 {
827 | width: 58.333333333333336%;
828 | }
829 |
830 | .md-col-8 {
831 | width: 66.66666666666666%;
832 | }
833 |
834 | .md-col-9 {
835 | width: 75%;
836 | }
837 |
838 | .md-col-10 {
839 | width: 83.33333333333334%;
840 | }
841 |
842 | .md-col-11 {
843 | width: 91.66666666666666%;
844 | }
845 |
846 | .md-col-12 {
847 | width: 100%;
848 | }
849 | }
850 |
851 | @media (min-width: 64em) {
852 | .lg-col {
853 | float: left;
854 | box-sizing: border-box;
855 | }
856 |
857 | .lg-col-right {
858 | float: right;
859 | box-sizing: border-box;
860 | }
861 |
862 | .lg-col-1 {
863 | width: 8.333333333333332%;
864 | }
865 |
866 | .lg-col-2 {
867 | width: 16.666666666666664%;
868 | }
869 |
870 | .lg-col-3 {
871 | width: 25%;
872 | }
873 |
874 | .lg-col-4 {
875 | width: 33.33333333333333%;
876 | }
877 |
878 | .lg-col-5 {
879 | width: 41.66666666666667%;
880 | }
881 |
882 | .lg-col-6 {
883 | width: 50%;
884 | }
885 |
886 | .lg-col-7 {
887 | width: 58.333333333333336%;
888 | }
889 |
890 | .lg-col-8 {
891 | width: 66.66666666666666%;
892 | }
893 |
894 | .lg-col-9 {
895 | width: 75%;
896 | }
897 |
898 | .lg-col-10 {
899 | width: 83.33333333333334%;
900 | }
901 |
902 | .lg-col-11 {
903 | width: 91.66666666666666%;
904 | }
905 |
906 | .lg-col-12 {
907 | width: 100%;
908 | }
909 | }
910 |
911 |
912 |
913 | /*
914 | * Basscss Flex Object
915 | */
916 |
917 | .flex {
918 | display: -webkit-box;
919 | display: -webkit-flex;
920 | display: -ms-flexbox;
921 | display: flex;
922 | }
923 |
924 | .flex-column {
925 | -webkit-box-orient: vertical;
926 | -webkit-box-direction: normal;
927 | -webkit-flex-direction: column;
928 | -ms-flex-direction: column;
929 | flex-direction: column;
930 | }
931 |
932 | .flex-wrap {
933 | -webkit-flex-wrap: wrap;
934 | -ms-flex-wrap: wrap;
935 | flex-wrap: wrap;
936 | }
937 |
938 | .flex-center {
939 | -webkit-box-align: center;
940 | -webkit-align-items: center;
941 | -ms-flex-align: center;
942 | align-items: center;
943 | }
944 |
945 | .flex-baseline {
946 | -webkit-box-align: baseline;
947 | -webkit-align-items: baseline;
948 | -ms-flex-align: baseline;
949 | align-items: baseline;
950 | }
951 |
952 | .flex-stretch {
953 | -webkit-box-align: stretch;
954 | -webkit-align-items: stretch;
955 | -ms-flex-align: stretch;
956 | align-items: stretch;
957 | }
958 |
959 | .flex-start {
960 | -webkit-box-align: start;
961 | -webkit-align-items: flex-start;
962 | -ms-flex-align: start;
963 | align-items: flex-start;
964 | }
965 |
966 | .flex-end {
967 | -webkit-box-align: end;
968 | -webkit-align-items: flex-end;
969 | -ms-flex-align: end;
970 | align-items: flex-end;
971 | }
972 |
973 | .flex-first {
974 | -webkit-box-ordinal-group: 0;
975 | -webkit-order: -1;
976 | -ms-flex-order: -1;
977 | order: -1;
978 | }
979 |
980 | .flex-last {
981 | -webkit-box-ordinal-group: 1025;
982 | -webkit-order: 1024;
983 | -ms-flex-order: 1024;
984 | order: 1024;
985 | }
986 |
987 | .flex-auto {
988 | -webkit-box-flex: 1;
989 | -webkit-flex: 1 1 auto;
990 | -ms-flex: 1 1 auto;
991 | flex: 1 1 auto;
992 | }
993 |
994 | .flex-grow {
995 | -webkit-box-flex: 1;
996 | -webkit-flex: 1 0 auto;
997 | -ms-flex: 1 0 auto;
998 | flex: 1 0 auto;
999 | }
1000 |
1001 | .flex-none {
1002 | -webkit-box-flex: 0;
1003 | -webkit-flex: none;
1004 | -ms-flex: none;
1005 | flex: none;
1006 | }
1007 |
1008 | .flex > div {
1009 | box-sizing: border-box;
1010 | }
1011 |
1012 | @media (min-width: 40em) {
1013 | .sm-flex {
1014 | display: -webkit-box;
1015 | display: -webkit-flex;
1016 | display: -ms-flexbox;
1017 | display: flex;
1018 | }
1019 |
1020 | .sm-flex > div {
1021 | box-sizing: border-box;
1022 | }
1023 | }
1024 |
1025 | @media (min-width: 52em) {
1026 | .md-flex {
1027 | display: -webkit-box;
1028 | display: -webkit-flex;
1029 | display: -ms-flexbox;
1030 | display: flex;
1031 | }
1032 |
1033 | .md-flex > div {
1034 | box-sizing: border-box;
1035 | }
1036 | }
1037 |
1038 | @media (min-width: 64em) {
1039 | .lg-flex {
1040 | display: -webkit-box;
1041 | display: -webkit-flex;
1042 | display: -ms-flexbox;
1043 | display: flex;
1044 | }
1045 |
1046 | .lg-flex > div {
1047 | box-sizing: border-box;
1048 | }
1049 | }
1050 |
1051 | /* New */
1052 |
1053 | /* Basscss Color Base */
1054 |
1055 | /*
1056 |
1057 | COLOR VARIABLES
1058 |
1059 | - Cool
1060 | - Warm
1061 | - Gray Scale
1062 |
1063 | */
1064 |
1065 | :root {
1066 | /* Cool */
1067 | /* Warm */
1068 | /* Gray scale */
1069 | }
1070 |
1071 | body {
1072 | color: #222;
1073 | background-color: white;
1074 | }
1075 |
1076 | a {
1077 | color: #0074d9;
1078 | text-decoration: none;
1079 | }
1080 |
1081 | a:hover {
1082 | text-decoration: underline;
1083 | }
1084 |
1085 | pre,
1086 | code {
1087 | background-color: #ddd;
1088 | border-radius: 3px;
1089 | }
1090 |
1091 | hr {
1092 | border: 0;
1093 | border-bottom-style: solid;
1094 | border-bottom-width: 1px;
1095 | border-bottom-color: rgba(0, 0, 0, .125);
1096 | }
1097 |
1098 | .button {
1099 | color: white;
1100 | background-color: #0074d9;
1101 | border-radius: 3px;
1102 | -webkit-transition-duration: .05s;
1103 | transition-duration: .05s;
1104 | -webkit-transition-timing-function: ease-out;
1105 | transition-timing-function: ease-out;
1106 | -webkit-transition-property: box-shadow, background-color;
1107 | transition-property: box-shadow, background-color;
1108 | }
1109 |
1110 | .button:hover {
1111 | box-shadow: inset 0 0 0 20rem rgba(0, 0, 0, .0625);
1112 | }
1113 |
1114 | .button:focus {
1115 | outline: none;
1116 | border-color: rgba(0, 0, 0, .125);
1117 | box-shadow: 0 0 2px 1px rgba(0, 0, 0, .25);
1118 | }
1119 |
1120 | .button:active,
1121 | .button.is-active {
1122 | box-shadow: inset 0 0 0 20rem rgba(0, 0, 0, .125),
1123 | inset 0 3px 4px 0 rgba(0, 0, 0, .25),
1124 | 0 0 1px rgba(0, 0, 0, .125);
1125 | }
1126 |
1127 | .button:disabled,
1128 | .button.is-disabled {
1129 | opacity: .5;
1130 | }
1131 |
1132 |
1133 |
1134 | /* Basscss Color Forms */
1135 |
1136 | .field-light {
1137 | background-color: white;
1138 | -webkit-transition: box-shadow .2s ease;
1139 | transition: box-shadow .2s ease;
1140 | border-style: solid;
1141 | border-width: 1px;
1142 | border-color: rgba(0, 0, 0, .125);
1143 | border-radius: 3px;
1144 | }
1145 |
1146 | .field-light:focus {
1147 | outline: none;
1148 | border-color: #0074d9;
1149 | box-shadow: 0 0 2px rgba(0, 116, 217, .5);
1150 | }
1151 |
1152 | .field-light:disabled {
1153 | color: #aaa;
1154 | background-color: rgba(0, 0, 0, .125);
1155 | }
1156 |
1157 | .field-light:read-only:not(select) {
1158 | background-color: rgba(0, 0, 0, .125);
1159 | }
1160 |
1161 | .field-light:invalid {
1162 | border-color: #ff4136;
1163 | }
1164 |
1165 | .field-light.is-success {
1166 | border-color: #2ecc40;
1167 | }
1168 |
1169 | .field-light.is-warning {
1170 | border-color: #ffdc00;
1171 | }
1172 |
1173 | .field-light.is-error {
1174 | border-color: #ff4136;
1175 | }
1176 |
1177 | .radio-light,
1178 | .checkbox-light {
1179 | -webkit-transition: box-shadow .2s ease;
1180 | transition: box-shadow .2s ease;
1181 | }
1182 |
1183 | .radio-light {
1184 | border-radius: 50%;
1185 | }
1186 |
1187 | .radio-light:focus,
1188 | .checkbox-light:focus {
1189 | outline: none;
1190 | box-shadow: 0 0 2px rgba(0, 116, 217, .5);
1191 | }
1192 |
1193 |
1194 |
1195 | /* Basscss Color Forms Dark */
1196 |
1197 | .field-dark {
1198 | color: white;
1199 | background-color: rgba(0, 0, 0, .25);
1200 | border: 1px solid rgba(0, 0, 0, .0625);
1201 | border-radius: 3px;
1202 | }
1203 |
1204 | .field-dark::-webkit-input-placeholder {
1205 | color: rgba(255, 255, 255, .75);
1206 | }
1207 |
1208 | .field-dark::-moz-placeholder {
1209 | color: rgba(255, 255, 255, .75);
1210 | }
1211 |
1212 | .field-dark:-ms-input-placeholder {
1213 | color: rgba(255, 255, 255, .75);
1214 | }
1215 |
1216 | .field-dark::placeholder {
1217 | color: rgba(255, 255, 255, .75);
1218 | }
1219 |
1220 | .field-dark:focus {
1221 | outline: 0;
1222 | border: 1px solid rgba(255, 255, 255, .5);
1223 | }
1224 |
1225 | .field-dark:read-only:not(select) {
1226 | background-color: rgba(255, 255, 255, .25);
1227 | }
1228 |
1229 | .field-dark:invalid {
1230 | border-color: #ff4136;
1231 | }
1232 |
1233 | .field-dark.is-success {
1234 | border-color: #2ecc40;
1235 | }
1236 |
1237 | .field-dark.is-warning {
1238 | border-color: #ffdc00;
1239 | }
1240 |
1241 | .field-dark.is-error {
1242 | border-color: #ff4136;
1243 | }
1244 |
1245 |
1246 |
1247 | /* Basscss Input Range */
1248 |
1249 | input[type=range] {
1250 | vertical-align: middle;
1251 | background-color: transparent;
1252 | }
1253 |
1254 | .range-light {
1255 | color: inherit;
1256 | -webkit-appearance: none;
1257 | padding-top: .5rem;
1258 | padding-bottom: .5rem;
1259 | }
1260 |
1261 | .range-light::-webkit-slider-thumb {
1262 | -webkit-appearance: none;
1263 | position: relative;
1264 | width: .5rem;
1265 | height: 1.25rem;
1266 | border-radius: 3px;
1267 | background-color: currentcolor;
1268 | cursor: pointer;
1269 | margin-top: -0.5rem;
1270 | }
1271 |
1272 | /* Touch screen friendly pseudo element */
1273 |
1274 | .range-light::-webkit-slider-thumb:before {
1275 | content: '';
1276 | display: block;
1277 | position: absolute;
1278 | top: -0.5rem;
1279 | left: -0.875rem;
1280 | width: 2.25rem;
1281 | height: 2.25rem;
1282 | opacity: 0;
1283 | }
1284 |
1285 | .range-light::-moz-range-thumb {
1286 | width: .5rem;
1287 | height: 1.25rem;
1288 | border-radius: 3px;
1289 | border-color: transparent;
1290 | border-width: 0;
1291 | background-color: currentcolor;
1292 | cursor: pointer;
1293 | }
1294 |
1295 | .range-light::-webkit-slider-runnable-track {
1296 | height: 0.25rem;
1297 | cursor: pointer;
1298 | border-radius: 3px;
1299 | background-color: rgba(0, 0, 0, .25);
1300 | }
1301 |
1302 | .range-light::-moz-range-track {
1303 | height: 0.25rem;
1304 | cursor: pointer;
1305 | border-radius: 3px;
1306 | background-color: rgba(0, 0, 0, .25);
1307 | }
1308 |
1309 | .range-light:focus {
1310 | outline: none;
1311 | }
1312 |
1313 | .range-light:focus::-webkit-slider-thumb {
1314 | outline: none;
1315 | border: 0;
1316 | box-shadow: 0 0 1px 2px currentcolor;
1317 | }
1318 |
1319 | .range-light:focus::-moz-range-thumb {
1320 | outline: none;
1321 | border: 0;
1322 | box-shadow: 0 0 1px 2px currentcolor;
1323 | }
1324 |
1325 |
1326 |
1327 | /* Basscss Color Tables */
1328 |
1329 | .table-light th,
1330 | .table-light td {
1331 | border-bottom-style: solid;
1332 | border-bottom-width: 1px;
1333 | border-bottom-color: rgba(0, 0, 0, .125);
1334 | }
1335 |
1336 | .table-light tr:last-child td {
1337 | border-bottom: 0;
1338 | }
1339 |
1340 |
1341 |
1342 | /* Basscss Button Outline */
1343 |
1344 | .button-outline {
1345 | position: relative;
1346 | z-index: 2;
1347 | color: inherit;
1348 | background-color: transparent;
1349 | border-radius: 3px;
1350 | border: 1px solid currentcolor;
1351 | -webkit-transition-duration: .1s;
1352 | transition-duration: .1s;
1353 | -webkit-transition-timing-function: ease-out;
1354 | transition-timing-function: ease-out;
1355 | -webkit-transition-property: box-shadow, background-color;
1356 | transition-property: box-shadow, background-color;
1357 | }
1358 |
1359 | .button-outline:before {
1360 | content: '';
1361 | width: 100%;
1362 | height: 100%;
1363 | display: block;
1364 | position: absolute;
1365 | z-index: -1;
1366 | top: -1px;
1367 | left: -1px;
1368 | border: 1px solid transparent;
1369 | background-color: currentcolor;
1370 | border-radius: 3px;
1371 | -webkit-transition-duration: .1s;
1372 | transition-duration: .1s;
1373 | -webkit-transition-timing-function: ease-out;
1374 | transition-timing-function: ease-out;
1375 | -webkit-transition-property: opacity;
1376 | transition-property: opacity;
1377 | opacity: 0;
1378 | }
1379 |
1380 | .button-outline:hover {
1381 | box-shadow: none;
1382 | }
1383 |
1384 | .button-outline:hover:before {
1385 | opacity: .125;
1386 | }
1387 |
1388 | .button-outline:focus {
1389 | outline: none;
1390 | border: 1px solid currentcolor;
1391 | box-shadow: 0 0 3px 1px;
1392 | }
1393 |
1394 | .button-outline:active,
1395 | .button-outline.is-active {
1396 | box-shadow: inset 0 1px 5px 0, 0 0 1px;
1397 | }
1398 |
1399 | .button-outline:disabled,
1400 | .button-outline.is-disabled {
1401 | opacity: .5;
1402 | }
1403 |
1404 |
1405 |
1406 | /* New */
1407 |
1408 | /* Basscss Button Transparent */
1409 |
1410 | .button-transparent {
1411 | position: relative;
1412 | z-index: 2;
1413 | color: inherit;
1414 | background-color: transparent;
1415 | border-radius: 0;
1416 | border: 1px solid transparent;
1417 | -webkit-transition-duration: .1s;
1418 | transition-duration: .1s;
1419 | -webkit-transition-timing-function: ease-out;
1420 | transition-timing-function: ease-out;
1421 | -webkit-transition-property: box-shadow;
1422 | transition-property: box-shadow;
1423 | }
1424 |
1425 | .button-transparent:before {
1426 | content: '';
1427 | width: 100%;
1428 | height: 100%;
1429 | display: block;
1430 | position: absolute;
1431 | z-index: -1;
1432 | top: -1px;
1433 | left: -1px;
1434 | border: 1px solid transparent;
1435 | background-color: currentcolor;
1436 | -webkit-transition-duration: .1s;
1437 | transition-duration: .1s;
1438 | -webkit-transition-timing-function: ease-out;
1439 | transition-timing-function: ease-out;
1440 | -webkit-transition-property: opacity;
1441 | transition-property: opacity;
1442 | opacity: 0;
1443 | }
1444 |
1445 | .button-transparent:hover {
1446 | box-shadow: none;
1447 | }
1448 |
1449 | .button-transparent:hover:before {
1450 | opacity: .0625;
1451 | opacity: .09375;
1452 | }
1453 |
1454 | .button-transparent:focus {
1455 | outline: none;
1456 | border-color: transparent;
1457 | box-shadow: 0 0 3px;
1458 | }
1459 |
1460 | .button-transparent:active:before,
1461 | .button-transparent.is-active:before {
1462 | opacity: .0625;
1463 | }
1464 |
1465 | .button-transparent:disabled,
1466 | .button-transparent.is-disabled {
1467 | opacity: .5;
1468 | }
1469 |
1470 |
1471 |
1472 | /* New */
1473 |
1474 | /* Basscss Background Images */
1475 |
1476 | .bg-cover {
1477 | background-size: cover;
1478 | }
1479 |
1480 | .bg-contain {
1481 | background-size: contain;
1482 | }
1483 |
1484 | .bg-center {
1485 | background-position: center;
1486 | }
1487 |
1488 | .bg-top {
1489 | background-position: top;
1490 | }
1491 |
1492 | .bg-right {
1493 | background-position: right;
1494 | }
1495 |
1496 | .bg-bottom {
1497 | background-position: bottom;
1498 | }
1499 |
1500 | .bg-left {
1501 | background-position: left;
1502 | }
1503 |
1504 | /* New */
1505 |
1506 | /* Basscss Color Borders */
1507 |
1508 | .border {
1509 | border-style: solid;
1510 | border-width: 1px;
1511 | border-color: rgba(0, 0, 0, .125);
1512 | }
1513 |
1514 | .border-top {
1515 | border-top-style: solid;
1516 | border-top-width: 1px;
1517 | border-top-color: rgba(0, 0, 0, .125);
1518 | }
1519 |
1520 | .border-right {
1521 | border-right-style: solid;
1522 | border-right-width: 1px;
1523 | border-right-color: rgba(0, 0, 0, .125);
1524 | }
1525 |
1526 | .border-bottom {
1527 | border-bottom-style: solid;
1528 | border-bottom-width: 1px;
1529 | border-bottom-color: rgba(0, 0, 0, .125);
1530 | }
1531 |
1532 | .border-left {
1533 | border-left-style: solid;
1534 | border-left-width: 1px;
1535 | border-left-color: rgba(0, 0, 0, .125);
1536 | }
1537 |
1538 | .rounded {
1539 | border-radius: 3px;
1540 | }
1541 |
1542 | .circle {
1543 | border-radius: 50%;
1544 | }
1545 |
1546 | .rounded-top {
1547 | border-radius: 3px 3px 0 0;
1548 | }
1549 |
1550 | .rounded-right {
1551 | border-radius: 0 3px 3px 0;
1552 | }
1553 |
1554 | .rounded-bottom {
1555 | border-radius: 0 0 3px 3px;
1556 | }
1557 |
1558 | .rounded-left {
1559 | border-radius: 3px 0 0 3px;
1560 | }
1561 |
1562 | .not-rounded {
1563 | border-radius: 0;
1564 | }
1565 |
1566 |
1567 |
1568 | /* Basscss Colors */
1569 |
1570 | /* Color */
1571 |
1572 | .black,
1573 | .dark-gray {
1574 | color: #222;
1575 | }
1576 |
1577 | .gray,
1578 | .mid-gray {
1579 | color: #aaa;
1580 | }
1581 |
1582 | .silver,
1583 | .light-gray {
1584 | color: #ddd;
1585 | }
1586 |
1587 | .white {
1588 | color: #fff;
1589 | }
1590 |
1591 | .aqua {
1592 | color: #7fdbff;
1593 | }
1594 |
1595 | .blue {
1596 | color: #0074d9;
1597 | }
1598 |
1599 | .navy {
1600 | color: #001f3f;
1601 | }
1602 |
1603 | .teal {
1604 | color: #39cccc;
1605 | }
1606 |
1607 | .green {
1608 | color: #2ecc40;
1609 | }
1610 |
1611 | .olive {
1612 | color: #3d9970;
1613 | }
1614 |
1615 | .lime {
1616 | color: #01ff70;
1617 | }
1618 |
1619 | .yellow {
1620 | color: #ffdc00;
1621 | }
1622 |
1623 | .orange {
1624 | color: #ff851b;
1625 | }
1626 |
1627 | .red {
1628 | color: #ff4136;
1629 | }
1630 |
1631 | .fuchsia {
1632 | color: #f012be;
1633 | }
1634 |
1635 | .purple {
1636 | color: #b10dc9;
1637 | }
1638 |
1639 | .maroon {
1640 | color: #85144b;
1641 | }
1642 |
1643 | /* Background Color */
1644 |
1645 | .bg-black,
1646 | .bg-dark-gray {
1647 | background-color: #222;
1648 | }
1649 |
1650 | .bg-gray,
1651 | .bg-mid-gray {
1652 | background-color: #aaa;
1653 | }
1654 |
1655 | .bg-silver,
1656 | .bg-light-gray {
1657 | background-color: #ddd;
1658 | }
1659 |
1660 | .bg-white {
1661 | background-color: #fff;
1662 | }
1663 |
1664 | .bg-aqua {
1665 | background-color: #7fdbff;
1666 | }
1667 |
1668 | .bg-blue {
1669 | background-color: #0074d9;
1670 | }
1671 |
1672 | .bg-navy {
1673 | background-color: #001f3f;
1674 | }
1675 |
1676 | .bg-teal {
1677 | background-color: #39cccc;
1678 | }
1679 |
1680 | .bg-green {
1681 | background-color: #2ecc40;
1682 | }
1683 |
1684 | .bg-olive {
1685 | background-color: #3d9970;
1686 | }
1687 |
1688 | .bg-lime {
1689 | background-color: #01ff70;
1690 | }
1691 |
1692 | .bg-yellow {
1693 | background-color: #ffdc00;
1694 | }
1695 |
1696 | .bg-orange {
1697 | background-color: #ff851b;
1698 | }
1699 |
1700 | .bg-red {
1701 | background-color: #ff4136;
1702 | }
1703 |
1704 | .bg-fuchsia {
1705 | background-color: #f012be;
1706 | }
1707 |
1708 | .bg-purple {
1709 | background-color: #b10dc9;
1710 | }
1711 |
1712 | .bg-maroon {
1713 | background-color: #85144b;
1714 | }
1715 |
1716 | .bg-darken-1 {
1717 | background-color: rgba(0, 0, 0, .0625);
1718 | }
1719 |
1720 | .bg-darken-2 {
1721 | background-color: rgba(0, 0, 0, .125);
1722 | }
1723 |
1724 | .bg-darken-3 {
1725 | background-color: rgba(0, 0, 0, .25);
1726 | }
1727 |
1728 | .bg-darken-4 {
1729 | background-color: rgba(0, 0, 0, .5);
1730 | }
1731 |
1732 | /* Border Color */
1733 |
1734 | .border-aqua {
1735 | border-color: #7fdbff;
1736 | }
1737 |
1738 | .border-blue {
1739 | border-color: #0074d9;
1740 | }
1741 |
1742 | .border-navy {
1743 | border-color: #001f3f;
1744 | }
1745 |
1746 | .border-teal {
1747 | border-color: #39cccc;
1748 | }
1749 |
1750 | .border-green {
1751 | border-color: #2ecc40;
1752 | }
1753 |
1754 | .border-olive {
1755 | border-color: #3d9970;
1756 | }
1757 |
1758 | .border-lime {
1759 | border-color: #01ff70;
1760 | }
1761 |
1762 | .border-yellow {
1763 | border-color: #ffdc00;
1764 | }
1765 |
1766 | .border-orange {
1767 | border-color: #ff851b;
1768 | }
1769 |
1770 | .border-red {
1771 | border-color: #ff4136;
1772 | }
1773 |
1774 | .border-fuchsia {
1775 | border-color: #f012be;
1776 | }
1777 |
1778 | .border-purple {
1779 | border-color: #b10dc9;
1780 | }
1781 |
1782 | .border-maroon {
1783 | border-color: #85144b;
1784 | }
1785 |
1786 | .border-black {
1787 | border-color: #222;
1788 | }
1789 |
1790 | .border-gray {
1791 | border-color: #aaa;
1792 | }
1793 |
1794 | .border-silver {
1795 | border-color: #ddd;
1796 | }
1797 |
1798 | .border-white {
1799 | border-color: #fff;
1800 | }
1801 |
1802 | .border-darken-1 {
1803 | border-color: rgba(0, 0, 0, .0625);
1804 | }
1805 |
1806 | .border-darken-2 {
1807 | border-color: rgba(0, 0, 0, .125);
1808 | }
1809 |
1810 | .border-darken-3 {
1811 | border-color: rgba(0, 0, 0, .25);
1812 | }
1813 |
1814 | .border-darken-4 {
1815 | border-color: rgba(0, 0, 0, .5);
1816 | }
1817 |
1818 | /* Opacity */
1819 |
1820 | .muted {
1821 | opacity: .5;
1822 | }
1823 |
--------------------------------------------------------------------------------
/test/fixtures/bootstrap-mutations.css:
--------------------------------------------------------------------------------
1 | .input-group {
2 | border: none;
3 | }
4 |
--------------------------------------------------------------------------------
/test/fixtures/vendor.css:
--------------------------------------------------------------------------------
1 | .foo {
2 | background-color: tomato;
3 | }
4 |
5 | .foo {
6 | color: purple;
7 | }
8 |
9 | .awesome .opossum {
10 | color: blue;
11 | }
12 |
--------------------------------------------------------------------------------
/test/get-css-classes-test.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs')
2 | var assert = require('assert')
3 | var getCssClassesFromAst = require('../lib/get-css-classes-from-ast')
4 | var postcss = require('postcss')
5 |
6 | function fixture(name) {
7 | return fs.readFileSync('test/fixtures/' + name, 'utf8').trim()
8 | }
9 |
10 | describe('get-css-classes-from-ast', function() {
11 |
12 | it('should return a hash of the selectors', function() {
13 | var css = postcss.parse(fixture('vendor.css'))
14 | assert.deepEqual(getCssClassesFromAst(css), { '.foo': true, '.awesome': true, '.opossum': true })
15 | })
16 | })
17 |
--------------------------------------------------------------------------------
/test/get-mutations-test.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs')
2 | var assert = require('assert')
3 | var getMutations = require('../lib/get-mutations')
4 |
5 | function fixture(name) {
6 | return fs.readFileSync('test/fixtures/' + name, 'utf8').trim()
7 | }
8 |
9 | describe('get-mutations', function() {
10 |
11 | it('should return an array of mutations', function() {
12 | assert.equal(getMutations(fixture('vendor.css'), fixture('app.css')).length, 5)
13 | })
14 |
15 | it('should find basscss mutations when they exist', function() {
16 | assert.equal(getMutations(fixture('basscss.css'), fixture('basscss-mutations.css')).length, 2)
17 | })
18 |
19 | it('should find bootstrap mutations when they exist', function() {
20 | assert.equal(getMutations(fixture('bootstrap.css'), fixture('bootstrap-mutations.css')).length, 1)
21 | })
22 |
23 | it('should include immutable selectors that are passes as options', function() {
24 | assert.equal(
25 | getMutations(
26 | fixture('vendor.css'),
27 | fixture('app.css'),
28 | { immutableSelectors: ['.sibling'] }
29 | ).length, 6)
30 | })
31 |
32 | it('should ignore the specified classes', function() {
33 | assert.equal(
34 | getMutations(
35 | fixture('basscss.css'),
36 | fixture('basscss-mutations.css'),
37 | { ignoredSelectors: ['.button'] }
38 | ).length, 1)
39 | })
40 | })
41 |
--------------------------------------------------------------------------------
/test/test.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs')
2 | var path = require('path')
3 | var assert = require('assert')
4 | var postcss = require('postcss')
5 | var postcssImport = require('postcss-import')
6 | var postcssReporter = require('postcss-reporter')
7 | var immutableCss = require('..')
8 |
9 | function fixture (name) {
10 | return fs.readFileSync('test/fixtures/' + name, 'utf8')
11 | }
12 |
13 | function test (input, mutations, opts, cb) {
14 | var messages = postcss([ postcssImport(), immutableCss(opts, cb), postcssReporter() ])
15 | .process(fixture(input), { from: input })
16 | .messages
17 |
18 | if (mutations) {
19 | assert.deepEqual(messages, mutations)
20 | }
21 | }
22 |
23 | describe('immutable-css', function () {
24 |
25 | it('should report the correct mutations', function () {
26 | test('basscss-mutations.css', mutations)
27 | })
28 |
29 | it('should return the mutations object in the callback', function (done) {
30 | test('basscss-mutations.css', undefined, {}, function (classMap) {
31 | assert.equal(Object.keys(classMap).length, 2)
32 | done()
33 | })
34 | })
35 |
36 | it('should ignore the specified selectors', function (done) {
37 | test('basscss-mutations.css', undefined, { ignoredClasses: ['.button'] }, function (classMap) {
38 | assert.equal(Object.keys(classMap).length, 1)
39 | done()
40 | })
41 | })
42 |
43 | it('should ignore the specified prefixes', function (done) {
44 | test('basscss-mutations.css', undefined, { immutablePrefixes: [/\.right/] }, function (classMap) {
45 | assert.equal(Object.keys(classMap).length, 5)
46 | done()
47 | })
48 | })
49 | })
50 |
51 | describe('immutable-css --strict', function () {
52 | it('does not allow class mutations in the same file', function (done) {
53 | test('vendor.css', undefined, { strict: true }, function (classMap) {
54 | assert.equal(Object.keys(classMap).length, 1)
55 | done()
56 | })
57 | })
58 | })
59 |
60 | describe('immutable-css.processFiles', function () {
61 |
62 | it('reports the mutations', function () {
63 | var foundMutations = immutableCss.processFiles('test/fixtures/basscss.css', 'test/fixtures/basscss-mutations.css')
64 | assert.equal(foundMutations.length, mutations.length)
65 | })
66 | })
67 |
68 | describe('immutable-css.processGlob', function () {
69 |
70 | it('reports the mutations', function () {
71 | var foundMutations = immutableCss.processGlob('test/fixtures/**/*.css')
72 | assert.equal(foundMutations.length, 15) // Magic mutation number in test/fixtures
73 | })
74 | })
75 |
76 | var mutations = [{
77 | plugin: 'immutable-css',
78 | text: '.button was mutated 2 times\n[line 93, col 1]: ' + __dirname + '/fixtures/basscss.css\n[line 11, col 1]: ' + path.resolve(__dirname, '..') + '/basscss-mutations.css\n',
79 | type: 'warning'
80 | }, {
81 | plugin: 'immutable-css',
82 | text: '.left was mutated 2 times\n[line 291, col 1]: ' + __dirname + '/fixtures/basscss.css\n[line 15, col 1]: ' + path.resolve(__dirname, '..') + '/basscss-mutations.css\n',
83 | type: 'warning'
84 | }]
85 |
--------------------------------------------------------------------------------