Repository: postcss/autoprefixer (js)

Issue:
[css-grid] Not inheriting gaps from less specific rules
<details>
    <summary>Input CSS</summary>

```css

.grid {
  display: grid;
  grid-gap: 20px; /* <-- Notice the grid-gap value here */
  grid-template-areas:
    "a b"
    "c d";
  grid-template-columns: repeat(2, 1fr);
  grid-template-rows: repeat(2, 1fr);
}

.grid.more-specific {
  /* grid-gap left out but should be inherited from .grid */
  grid-template-areas:
    "e f"
    "g h";
  grid-template-columns: repeat(2, 1fr);
  grid-template-rows: repeat(2, 1fr);
}

@media (max-width: 600px) {
  .grid {
    grid-template-areas:
      "a b"
      "c d";
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: repeat(2, 1fr);
  }
  .grid.more-specific {
    grid-template-areas:
      "e f"
      "g h";
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: repeat(2, 1fr);
  }
}

.a { grid-area: a; }
.b { grid-area: b; }
.c { grid-area: c; }
.d { grid-area: d; }
.e { grid-area: e; }
.f { grid-area: f; }
.g { grid-area: g; }
.h { grid-area: h; }
```

</details>

<details>
    <summary>Current CSS output</summary>

```css
.grid {
  display: -ms-grid;
  display: grid;
  grid-gap: 20px;
      grid-template-areas: "a b"
 "c d";
  -ms-grid-columns: 1fr 20px 1fr;
  grid-template-columns: repeat(2, 1fr);
  -ms-grid-rows: 1fr 20px 1fr;
  grid-template-rows: repeat(2, 1fr);
}

.grid.more-specific {
      grid-template-areas: "e f"
 "g h";
  -ms-grid-columns: (1fr)[2];  /* <-- no grid-gap inheritance */
  grid-template-columns: repeat(2, 1fr);
  -ms-grid-rows: (1fr)[2];  /* <-- no grid-gap inheritance */
  grid-template-rows: repeat(2, 1fr);
}

@media (max-width: 600px) {
  .grid {
        grid-template-areas: "a b"
 "c d";
    -ms-grid-columns: 1fr 20px 1fr;
    grid-template-columns: repeat(2, 1fr);
    -ms-grid-rows: 1fr 20px 1fr;
    grid-template-rows: repeat(2, 1fr);
  }
  .grid.more-specific {
        grid-template-areas: "e f"
 "g h";
    -ms-grid-columns: (1fr)[2];  /* <-- no grid-gap inheritance */
    grid-template-columns: repeat(2, 1fr);
    -ms-grid-rows: (1fr)[2];  /* <-- no grid-gap inheritance */
    grid-template-rows: repeat(2, 1fr);
  }
}

.a {
  -ms-grid-row: 1;
  -ms-grid-column: 1;
  grid-area: a;
}

.b {
  -ms-grid-row: 1;
  -ms-grid-column: 3;
  grid-area: b;
}

.c {
  -ms-grid-row: 3;
  -ms-grid-column: 1;
  grid-area: c;
}

.d {
  -ms-grid-row: 3;
  -ms-grid-column: 3;
  grid-area: d;
}

@media (max-width: 600px) {
  .a {
    -ms-grid-row: 1;
    -ms-grid-column: 1;
  }
  .b {
    -ms-grid-row: 1;
    -ms-grid-column: 2;
  }
  .c {
    -ms-grid-row: 2;
    -ms-grid-column: 1;
  }
  .d {
    -ms-grid-row: 2;
    -ms-grid-column: 2;
  }
}

.e {
  -ms-grid-row: 1;
  -ms-grid-column: 1;
  grid-area: e;
}

.f {
  -ms-grid-row: 1;
  -ms-grid-column: 2;
  grid-area: f;
}

.g {
  -ms-grid-row: 2;
  -ms-grid-column: 1;
  grid-area: g;
}

.h {
  -ms-grid-row: 2;
  -ms-grid-column: 2;
  grid-area: h;
}

@media (max-width: 600px) {
  .e {
    -ms-grid-row: 1;
    -ms-grid-column: 1;
  }
  .f {
    -ms-grid-row: 1;
    -ms-grid-column: 2;
  }
  .g {
    -ms-grid-row: 2;
    -ms-grid-column: 1;
  }
  .h {
    -ms-grid-row: 2;
    -ms-grid-column: 2;
  }
}
```

</details>


<details>
    <summary>Expected CSS output</summary>

```css
.grid {
  display: -ms-grid;
  display: grid;
  grid-gap: 20px;
      grid-template-areas: "a b"
 "c d";
  -ms-grid-columns: 1fr 20px 1fr;
  grid-template-columns: repeat(2, 1fr);
  -ms-grid-rows: 1fr 20px 1fr;
  grid-template-rows: repeat(2, 1fr);
}

.grid.more-specific {
      grid-template-areas: "e f"
 "g h";
  -ms-grid-columns: 1fr 20px 1fr;  /* <-- grid-gap inheritance */
  grid-template-columns: repeat(2, 1fr);
  -ms-grid-rows: 1fr 20px 1fr;  /* <-- grid-gap inheritance */
  grid-template-rows: repeat(2, 1fr);
}

@media (max-width: 600px) {
  .grid {
        grid-template-areas: "a b"
 "c d";
    -ms-grid-columns: 1fr 20px 1fr;
    grid-template-columns: repeat(2, 1fr);
    -ms-grid-rows: 1fr 20px 1fr;
    grid-template-rows: repeat(2, 1fr);
  }
  .grid.more-specific {
        grid-template-areas: "e f"
 "g h";
    -ms-grid-columns: 1fr 20px 1fr;  /* <-- grid-gap inheritance */
    grid-template-columns: repeat(2, 1fr);
    -ms-grid-rows: 1fr 20px 1fr;  /* <-- grid-gap inheritance */
    grid-template-rows: repeat(2, 1fr);
  }
}

.a {
  -ms-grid-row: 1;
  -ms-grid-column: 1;
  grid-area: a;
}

.b {
  -ms-grid-row: 1;
  -ms-grid-column: 3;
  grid-area: b;
}

.c {
  -ms-grid-row: 3;
  -ms-grid-column: 1;
  grid-area: c;
}

.d {
  -ms-grid-row: 3;
  -ms-grid-column: 3;
  grid-area: d;
}

@media (max-width: 600px) {
  .a {
    -ms-grid-row: 1;
    -ms-grid-column: 1;
  }
  .b {
    -ms-grid-row: 1;
    -ms-grid-column: 3;
  }
  .c {
    -ms-grid-row: 3;
    -ms-grid-column: 1;
  }
  .d {
    -ms-grid-row: 3;
    -ms-grid-column: 3;
  }
}

.e {
  -ms-grid-row: 1;
  -ms-grid-column: 1;
  grid-area: e;
}

.f {
  -ms-grid-row: 1;
  -ms-grid-column: 3;
  grid-area: f;
}

.g {
  -ms-grid-row: 3;
  -ms-grid-column: 1;
  grid-area: g;
}

.h {
  -ms-grid-row: 3;
  -ms-grid-column: 3;
  grid-area: h;
}

@media (max-width: 600px) {
  .e {
    -ms-grid-row: 1;
    -ms-grid-column: 1;
  }
  .f {
    -ms-grid-row: 1;
    -ms-grid-column: 3;
  }
  .g {
    -ms-grid-row: 3;
    -ms-grid-column: 1;
  }
  .h {
    -ms-grid-row: 3;
    -ms-grid-column: 3;
  }
}
```

</details>


Pull request description:
[grid-gap] Inherit gaps from less specific rules
Fixes #1117

cc @Dan503 

This should work for simple cases. I added rules in the tests as an example, check it out 😀

Patch:
diff --git a/README.md b/README.md
index 217d971..50276ed 100644
--- a/README.md
+++ b/README.md
@@ -194,10 +194,7 @@ Developers are often surprised by how few prefixes are required today.
 If Autoprefixer doesn’t add prefixes to your CSS, check if they’re still
 required on [Can I Use].
 
-There is a [list with all supported] properties, values, and selectors.
-
-[list with all supported]: https://github.com/postcss/autoprefixer/wiki/support-list
-[Can I Use]:               http://caniuse.com/
+[Can I Use]: http://caniuse.com/
 
 
 #### Why Autoprefixer uses unprefixed properties in `@-webkit-keyframes`?
diff --git a/lib/hacks/grid-utils.js b/lib/hacks/grid-utils.js
index c23b97f..65862d0 100644
--- a/lib/hacks/grid-utils.js
+++ b/lib/hacks/grid-utils.js
@@ -1,6 +1,8 @@
 let parser = require('postcss-value-parser')
 let list = require('postcss').list
 let uniq = require('../utils').uniq
+let escapeRegexp = require('../utils').escapeRegexp
+let splitSelector = require('../utils').splitSelector
 
 function convert (value) {
   if (value &&
@@ -346,7 +348,9 @@ function parseGridTemplatesData (css) {
   css.walkDecls(/grid-template(-areas)?$/, d => {
     let rule = d.parent
     let media = getParentMedia(rule)
-    let { areas } = parseTemplate({ decl: d, gap: getGridGap(d) })
+    let gap = getGridGap(d)
+    let inheritedGap = inheritGridGap(d, gap)
+    let { areas } = parseTemplate({ decl: d, gap: inheritedGap || gap })
     let areaNames = Object.keys(areas)
 
     // skip node if it doesn't have areas
@@ -771,6 +775,9 @@ function getGridGap (decl) {
  * @return {}
  */
 function parseMediaParams (params) {
+  if (!params) {
+    return false
+  }
   let parsed = parser(params)
   let prop
   let value
@@ -786,6 +793,58 @@ function parseMediaParams (params) {
   return [prop, value]
 }
 
+/**
+ * Compare the selectors and decide if we
+ * need to inherit gap from compared selector or not.
+ * @type {String} selA
+ * @type {String} selB
+ * @return {Boolean}
+ */
+function shouldInheritGap (selA, selB) {
+  let result
+
+  // get arrays of selector split in 3-deep array
+  let splitSelectorArrA = splitSelector(selA)
+  let splitSelectorArrB = splitSelector(selB)
+
+  if (splitSelectorArrA[0].length < splitSelectorArrB[0].length) {
+    // abort if selectorA has lower descendant specificity then selectorB
+    // (e.g '.grid' and '.hello .world .grid')
+    return false
+  } else if (splitSelectorArrA[0].length > splitSelectorArrB[0].length) {
+    // if selectorA has higher descendant specificity then selectorB
+    // (e.g '.foo .bar .grid' and '.grid')
+
+    let idx = splitSelectorArrA[0].reduce((res, [item], index) => {
+      let firstSelectorPart = splitSelectorArrB[0][0][0]
+      if (item === firstSelectorPart) {
+        return index
+      }
+      return false
+    }, false)
+
+    if (idx) {
+      result = splitSelectorArrB[0].every((arr, index) => {
+        return arr.every((part, innerIndex) =>
+          // because selectorA has more space elements, we need to slice
+          // selectorA array by 'idx' number to compare them
+          splitSelectorArrA[0].slice(idx)[index][innerIndex] === part)
+      })
+    }
+  } else {
+    // if selectorA has the same descendant specificity as selectorB
+    // this condition covers cases such as: '.grid.foo.bar' and '.grid'
+    result = splitSelectorArrB.some(byCommaArr => {
+      return byCommaArr.every((bySpaceArr, index) => {
+        return bySpaceArr.every(
+          (part, innerIndex) => splitSelectorArrA[0][index][innerIndex] === part
+        )
+      })
+    })
+  }
+
+  return result
+}
 /**
  * inherit grid gap values from the closest rule above
  * with the same selector
@@ -797,19 +856,32 @@ function inheritGridGap (decl, gap) {
   let rule = decl.parent
   let mediaRule = getParentMedia(rule)
   let root = rule.root()
-  if (Object.keys(gap).length > 0 || !mediaRule) {
+
+  // get an array of selector split in 3-deep array
+  let splitSelectorArr = splitSelector(rule.selector)
+
+  // abort if the rule already has gaps
+  if (Object.keys(gap).length > 0) {
     return false
   }
 
   // e.g ['min-width']
   let [prop] = parseMediaParams(mediaRule.params)
 
+  let lastBySpace = splitSelectorArr[0]
+
+  // get escaped value from the selector
+  // if we have '.grid-2.foo.bar' selector, will be '\.grid\-2'
+  let escaped = escapeRegexp(lastBySpace[lastBySpace.length - 1][0])
+
+  let regexp = new RegExp(`(${ escaped }$)|(${ escaped }[,.])`)
+
   // find the closest rule with the same selector
   let closestRuleGap
-  root.walkRules(rule.selector, r => {
+  root.walkRules(regexp, r => {
     let gridGap
 
-    // abort if checking the same rule
+    // abort if are checking the same rule
     if (rule.toString() === r.toString()) {
       return false
     }
@@ -822,8 +894,12 @@ function inheritGridGap (decl, gap) {
       return true
     }
 
-    let media = getParentMedia(r)
+    // skip rules that should not be inherited from
+    if (!shouldInheritGap(rule.selector, r.selector)) {
+      return true
+    }
 
+    let media = getParentMedia(r)
     if (media) {
       // if we are inside media, we need to check that media props match
       // e.g ('min-width' === 'min-width')
diff --git a/lib/processor.js b/lib/processor.js
index f78835b..c994c69 100644
--- a/lib/processor.js
+++ b/lib/processor.js
@@ -19,8 +19,8 @@ class Processor {
   }
 
   /**
-     * Add necessary prefixes
-     */
+   * Add necessary prefixes
+   */
   add (css, result) {
     // At-rules
     let resolution = this.prefixes.add['@resolution']
@@ -63,6 +63,21 @@ class Processor {
       })
     })
 
+    function insideGrid (decl) {
+      return decl.parent.some(subDecl => {
+        let displayGrid = subDecl.prop === 'display' &&
+          /(inline-)?grid/.test(subDecl.value)
+        let gridTemplate = /^grid-template/.test(subDecl.prop)
+        let gridGap = /^grid-([A-z]+-)?gap/.test(subDecl.prop)
+
+        return displayGrid || gridTemplate || gridGap
+      })
+    }
+
+    let gridPrefixes = this.prefixes.options.grid &&
+                       this.prefixes.add['grid-area'] &&
+                       this.prefixes.add['grid-area'].prefixes
+
     css.walkDecls(decl => {
       if (this.disabledDecl(decl, result)) return undefined
 
@@ -71,43 +86,55 @@ class Processor {
 
       if (prop === 'grid-row-span') {
         result.warn(
-          'grid-row-span is not part of final Grid Layout. ' +
-                    'Use grid-row.',
+          'grid-row-span is not part of final Grid Layout. Use grid-row.',
           { node: decl }
         )
         return undefined
       } else if (prop === 'grid-column-span') {
         result.warn(
-          'grid-column-span is not part of final Grid Layout. ' +
-                    'Use grid-column.',
+          'grid-column-span is not part of final Grid Layout. Use grid-column.',
           { node: decl }
         )
         return undefined
       } else if (prop === 'display' && value === 'box') {
         result.warn(
           'You should write display: flex by final spec ' +
-                    'instead of display: box', { node: decl }
+          'instead of display: box', { node: decl }
         )
         return undefined
       } else if (prop === 'text-emphasis-position') {
         if (value === 'under' || value === 'over') {
           result.warn(
             'You should use 2 values for text-emphasis-position ' +
-                        'For example, `under left` instead of just `under`.',
+            'For example, `under left` instead of just `under`.',
             { node: decl }
           )
         }
       } else {
-        let grid = this.prefixes.add['grid-area']
-        if (this.prefixes.options.grid && grid && grid.prefixes) {
-          if (prop === 'display' && decl.value === 'contents') {
+        if (gridPrefixes) {
+          if (/^(align|justify|place)-items$/.test(prop) && insideGrid(decl)) {
+            let fixed = prop.replace('-items', '-self')
+            result.warn(
+              `IE does not support ${ prop } on grid containers. ` +
+              `Try using ${ fixed } on child elements instead: ` +
+              `${ decl.parent.selector } > * { ${ fixed }: ${ decl.value } }`,
+              { node: decl }
+            )
+          } else if (
+            /^(align|justify|place)-content$/.test(prop) && insideGrid(decl)
+          ) {
+            result.warn(
+              `IE does not support ${ decl.prop } on grid containers`,
+              { node: decl }
+            )
+          } else if (prop === 'display' && decl.value === 'contents') {
             result.warn(
               'Please do not use display: contents; ' +
               'if you have grid setting enabled',
               { node: decl }
             )
-          }
-          if (prop === 'grid-auto-columns') {
+            return undefined
+          } else if (prop === 'grid-auto-columns') {
             result.warn(
               'grid-auto-columns is not supported by IE',
               { node: decl }
@@ -267,8 +294,8 @@ class Processor {
   }
 
   /**
-     * Remove unnecessary pefixes
-     */
+   * Remove unnecessary pefixes
+   */
   remove (css, result) {
     // At-rules
     let resolution = this.prefixes.remove['@resolution']
@@ -351,15 +378,15 @@ class Processor {
   }
 
   /**
-     * Some rare old values, which is not in standard
-     */
+   * Some rare old values, which is not in standard
+   */
   withHackValue (decl) {
     return decl.prop === '-webkit-background-clip' && decl.value === 'text'
   }
 
   /**
-     * Check for grid/flexbox options.
-     */
+   * Check for grid/flexbox options.
+   */
   disabledValue (node, result) {
     if (this.prefixes.options.grid === false && node.type === 'decl') {
       if (node.prop === 'display' && node.value.indexOf('grid') !== -1) {
@@ -376,12 +403,12 @@ class Processor {
   }
 
   /**
-     * Check for grid/flexbox options.
-     */
+   * Check for grid/flexbox options.
+   */
   disabledDecl (node, result) {
     if (this.prefixes.options.grid === false && node.type === 'decl') {
-      if (node.prop.indexOf('grid') !== -1 ||
-                node.prop === 'justify-items'
+      if (
+        node.prop.indexOf('grid') !== -1 || node.prop === 'justify-items'
       ) {
         return true
       }
@@ -393,8 +420,8 @@ class Processor {
         'align-items',
         'align-content'
       ]
-      if (node.prop.indexOf('flex') !== -1 ||
-                other.indexOf(node.prop) !== -1
+      if (
+        node.prop.indexOf('flex') !== -1 || other.indexOf(node.prop) !== -1
       ) {
         return true
       }
@@ -404,8 +431,8 @@ class Processor {
   }
 
   /**
-     * Check for control comment and global options
-     */
+   * Check for control comment and global options
+   */
   disabled (node, result) {
     if (!node) return false
 
@@ -431,8 +458,8 @@ class Processor {
           if (typeof status !== 'undefined') {
             result.warn(
               'Second Autoprefixer control comment ' +
-                            'was ignored. Autoprefixer applies control ' +
-                            'comment to whole block, not to next rules.',
+              'was ignored. Autoprefixer applies control ' +
+              'comment to whole block, not to next rules.',
               { node: i }
             )
           } else {
@@ -462,8 +489,8 @@ class Processor {
   }
 
   /**
-     * Normalize spaces in cascade declaration group
-     */
+   * Normalize spaces in cascade declaration group
+   */
   reduceSpaces (decl) {
     let stop = false
     this.prefixes.group(decl).up(() => {
@@ -494,8 +521,8 @@ class Processor {
   }
 
   /**
-     * Is it flebox or grid rule
-     */
+   * Is it flebox or grid rule
+   */
   displayType (decl) {
     for (let i of decl.parent.nodes) {
       if (i.prop !== 'display') {
diff --git a/lib/utils.js b/lib/utils.js
index dcd8a84..a0dbdc1 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -67,6 +67,24 @@ module.exports = {
     let join = value.match(/,\s*/)
     join = join ? join[0] : ', '
     return changed.join(join)
+  },
+
+  /**
+     * Split the selector into parts.
+     * It returns 3 level deep array because selectors can be comma
+     * separated (1), space separated (2), and combined (3)
+     * @param {String} selector selector string
+     * @return {Array<Array<Array>>} 3 level deep array of split selector
+     * @see utils.test.js for examples
+     */
+  splitSelector (selector) {
+    return list
+      .comma(selector)
+      .map(i => {
+        return list.space(i).map(k => {
+          return k.split(/(?=\.|#)/g)
+        })
+      })
   }
 
 }
diff --git a/package.json b/package.json
index 85e9d6a..645beeb 100644
--- a/package.json
+++ b/package.json
@@ -74,7 +74,7 @@
   "size-limit": [
     {
       "path": "build/lib/autoprefixer.js",
-      "limit": "101 KB"
+      "limit": "102 KB"
     }
   ],
   "eslintConfig": {


Test patch:
diff --git a/test/autoprefixer.test.js b/test/autoprefixer.test.js
index c484159..d9e13bd 100644
--- a/test/autoprefixer.test.js
+++ b/test/autoprefixer.test.js
@@ -526,35 +526,43 @@ describe('hacks', () => {
     expect(result.css).toEqual(output)
     expect(result.warnings().map(i => i.toString())).toEqual([
       'autoprefixer: <css input>:36:3: Can not prefix grid-column-end ' +
-                '(grid-column-start is not found)',
+        '(grid-column-start is not found)',
       'autoprefixer: <css input>:39:3: Can not impliment grid-gap ' +
-                'without grid-tamplate-columns',
+        'without grid-tamplate-columns',
       'autoprefixer: <css input>:39:3: Can not find grid areas: ' +
-                'head, nav, main, foot',
+        'head, nav, main, foot',
       'autoprefixer: <css input>:47:3: Can not impliment grid-gap ' +
-                'without grid-tamplate-columns',
+        'without grid-tamplate-columns',
       'autoprefixer: <css input>:47:3: Can not find grid areas: a',
       'autoprefixer: <css input>:55:3: Can not impliment grid-gap ' +
-                'without grid-tamplate-columns',
+        'without grid-tamplate-columns',
       'autoprefixer: <css input>:55:3: Can not find grid areas: b',
       'autoprefixer: <css input>:63:3: Can not find grid areas: c',
       'autoprefixer: <css input>:71:3: Can not find grid areas: d',
       'autoprefixer: <css input>:99:3: grid-column-span is not part ' +
-                'of final Grid Layout. Use grid-column.',
+        'of final Grid Layout. Use grid-column.',
       'autoprefixer: <css input>:100:3: grid-row-span is not part ' +
-                'of final Grid Layout. Use grid-row.',
+        'of final Grid Layout. Use grid-row.',
       'autoprefixer: <css input>:101:3: grid-auto-columns is not ' +
-                'supported by IE',
+        'supported by IE',
       'autoprefixer: <css input>:102:3: grid-auto-rows is not ' +
-                'supported by IE',
+        'supported by IE',
       'autoprefixer: <css input>:103:3: grid-auto-flow is not ' +
-                'supported by IE',
+        'supported by IE',
       'autoprefixer: <css input>:104:33: auto-fill value is not ' +
-                'supported by IE',
+        'supported by IE',
       'autoprefixer: <css input>:105:30: auto-fit value is not ' +
-                'supported by IE',
+        'supported by IE',
       'autoprefixer: <css input>:121:3: Please do not use ' +
-      'display: contents; if you have grid setting enabled'
+        'display: contents; if you have grid setting enabled',
+      'autoprefixer: <css input>:125:3: IE does not support align-items ' +
+        'on grid containers. Try using align-self on child elements instead: ' +
+        '.warn_ie_align > * { align-self: center }',
+      'autoprefixer: <css input>:130:3: IE does not support justify-items ' +
+        'on grid containers. Try using justify-self on child elements ' +
+        'instead: .warn_ie_justify > * { justify-self: center }',
+      'autoprefixer: <css input>:135:3: IE does not support justify-content ' +
+        'on grid containers'
     ])
   })
 
diff --git a/test/cases/grid-gap.css b/test/cases/grid-gap.css
index bd7df11..d0194f4 100644
--- a/test/cases/grid-gap.css
+++ b/test/cases/grid-gap.css
@@ -67,6 +67,17 @@
       / 1fr 1fr 1fr;
 }
 
+.parent .grid {
+  grid-gap: 30px;
+  display: grid;
+  grid-template:
+    "a   b" 100px
+    "c   d" 100px
+    "e   f" 100px /
+    1fr  1fr;
+}
+
+/* must not inherit gap from .parent .grid */
 .grid {
   display: grid;
   grid-template:
@@ -86,6 +97,66 @@
     1fr  1fr;
 }
 
+.grid-2.modifier-b {
+  grid-gap: 22px;
+  grid-template-areas:
+    "x   x"
+    "y   y"
+    "z   z";
+  grid-template-columns: repeat(2, 1fr);
+  grid-template-rows: repeat(3, 100px);
+}
+
+/* must inherit gap from .grid-2.modifier-b */
+.grid-2.modifier-b.foo {
+  grid-template-areas:
+    "x   x"
+    "y   y"
+    "z   z";
+  grid-template-columns: repeat(2, 1fr);
+  grid-template-rows: repeat(3, 100px);
+}
+
+/* must inherit gap from .grid-2.modifier-b */
+.grid-2.modifier-b.bar {
+  grid-template-areas:
+    "x   x"
+    "y   y"
+    "z   z";
+  grid-template-columns: repeat(2, 1fr);
+  grid-template-rows: repeat(3, 100px);
+}
+
+/* must inherit gap from .grid-2.modifier-b */
+.parent .grid-2.modifier-b.bar {
+  grid-template-areas:
+    "x   x"
+    "y   y"
+    "z   z";
+  grid-template-columns: repeat(2, 1fr);
+  grid-template-rows: repeat(3, 100px);
+}
+
+/* must inherit gap from .grid-2 */
+.grid-2.modifier {
+  grid-template-areas:
+    "x   x"
+    "y   y"
+    "z   z";
+  grid-template-columns: repeat(2, 1fr);
+  grid-template-rows: repeat(3, 100px);
+}
+
+/* must inherit gap from .grid-2 */
+.parent .grid-2 {
+  grid-template-areas:
+    "x   x"
+    "y   y"
+    "z   z";
+  grid-template-columns: repeat(2, 1fr);
+  grid-template-rows: repeat(3, 100px);
+}
+
 @media (min-width: 600px){
   .grid {
     grid-gap: 10px;
@@ -138,6 +209,33 @@
       "x   y   z" 100px
       / 1fr 1fr;
   }
+  /* must inherit gap from .grid-2 */
+  .grid-2.modifier {
+    grid-template-areas:
+      "x   x"
+      "y   y"
+      "z   z";
+    grid-template-columns: repeat(2, 1fr);
+    grid-template-rows: repeat(3, 100px);
+  }
+  /* must inherit gap from .grid-2 */
+  .grid-2.modifier.modifier-c {
+    grid-template-areas:
+      "x   x"
+      "y   y"
+      "z   z";
+    grid-template-columns: repeat(2, 1fr);
+    grid-template-rows: repeat(3, 100px);
+  }
+  /* must inherit gap from .grid-2 */
+  .parent .grid-2 {
+    grid-template-areas:
+      "x   x"
+      "y   y"
+      "z   z";
+    grid-template-columns: repeat(2, 1fr);
+    grid-template-rows: repeat(3, 100px);
+  }
 }
 
 @media (max-width: 1000px){
@@ -149,6 +247,14 @@
     grid-template-columns: repeat(3, 1fr);
     grid-template-rows: repeat(2, 100px);
   }
+  /* must inherit gap from .grid-2 */
+  .grid-2.modifier {
+    grid-template-areas:
+      "hi   hi   hi"
+      "hi   hi   hi";
+    grid-template-columns: repeat(3, 1fr);
+    grid-template-rows: repeat(2, 100px);
+  }
 }
 
 @media (max-width: 700px){
@@ -158,6 +264,15 @@
     grid-template-columns: repeat(6, 1fr);
     grid-template-rows: 100px;
   }
+  /* must NOT inherit gap from .grid-2 */
+  .grid-2.modifier {
+    grid-gap: 70px;
+    grid-template-areas:
+      "x   y   z"
+      "x   y   z";
+    grid-template-columns: repeat(3, 1fr);
+    grid-template-rows: repeat(2, 100px);
+  }
 }
 
 @media (max-width: 400px){
@@ -172,4 +287,12 @@
     grid-template-columns: 1fr;
     grid-template-rows: repeat(6, 100px);
   }
+  /* must inherit gap from .grid-2.modifier */
+  .grid-2.modifier {
+    grid-template-areas:
+      "x   y   z"
+      "x   y   z";
+    grid-template-columns: repeat(3, 1fr);
+    grid-template-rows: repeat(2, 100px);
+  }
 }
diff --git a/test/cases/grid-gap.out.css b/test/cases/grid-gap.out.css
index 1053630..28bf0a6 100644
--- a/test/cases/grid-gap.out.css
+++ b/test/cases/grid-gap.out.css
@@ -147,6 +147,20 @@
       / 1fr 1fr 1fr;
 }
 
+.parent .grid {
+  grid-gap: 30px;
+  display: -ms-grid;
+  display: grid;
+  -ms-grid-rows: 100px 30px 100px 30px 100px;
+  -ms-grid-columns: 1fr 30px 1fr;
+  grid-template:
+    "a   b" 100px
+    "c   d" 100px
+    "e   f" 100px /
+    1fr  1fr;
+}
+
+/* must not inherit gap from .parent .grid */
 .grid {
   display: -ms-grid;
   display: grid;
@@ -172,6 +186,78 @@
     1fr  1fr;
 }
 
+.grid-2.modifier-b {
+  grid-gap: 22px;
+  grid-template-areas:
+    "x   x"
+    "y   y"
+    "z   z";
+  -ms-grid-columns: 1fr 22px 1fr;
+  grid-template-columns: repeat(2, 1fr);
+  -ms-grid-rows: 100px 22px 100px 22px 100px;
+  grid-template-rows: repeat(3, 100px);
+}
+
+/* must inherit gap from .grid-2.modifier-b */
+.grid-2.modifier-b.foo {
+  grid-template-areas:
+    "x   x"
+    "y   y"
+    "z   z";
+  -ms-grid-columns: 1fr 22px 1fr;
+  grid-template-columns: repeat(2, 1fr);
+  -ms-grid-rows: 100px 22px 100px 22px 100px;
+  grid-template-rows: repeat(3, 100px);
+}
+
+/* must inherit gap from .grid-2.modifier-b */
+.grid-2.modifier-b.bar {
+  grid-template-areas:
+    "x   x"
+    "y   y"
+    "z   z";
+  -ms-grid-columns: 1fr 22px 1fr;
+  grid-template-columns: repeat(2, 1fr);
+  -ms-grid-rows: 100px 22px 100px 22px 100px;
+  grid-template-rows: repeat(3, 100px);
+}
+
+/* must inherit gap from .grid-2.modifier-b */
+.parent .grid-2.modifier-b.bar {
+  grid-template-areas:
+    "x   x"
+    "y   y"
+    "z   z";
+  -ms-grid-columns: 1fr 22px 1fr;
+  grid-template-columns: repeat(2, 1fr);
+  -ms-grid-rows: 100px 22px 100px 22px 100px;
+  grid-template-rows: repeat(3, 100px);
+}
+
+/* must inherit gap from .grid-2 */
+.grid-2.modifier {
+  grid-template-areas:
+    "x   x"
+    "y   y"
+    "z   z";
+  -ms-grid-columns: 1fr 50px 1fr;
+  grid-template-columns: repeat(2, 1fr);
+  -ms-grid-rows: 100px 50px 100px 50px 100px;
+  grid-template-rows: repeat(3, 100px);
+}
+
+/* must inherit gap from .grid-2 */
+.parent .grid-2 {
+  grid-template-areas:
+    "x   x"
+    "y   y"
+    "z   z";
+  -ms-grid-columns: 1fr 50px 1fr;
+  grid-template-columns: repeat(2, 1fr);
+  -ms-grid-rows: 100px 50px 100px 50px 100px;
+  grid-template-rows: repeat(3, 100px);
+}
+
 @media (min-width: 600px){
   .grid {
     grid-gap: 10px;
@@ -234,6 +320,39 @@
       "x   y   z" 100px
       / 1fr 1fr;
   }
+  /* must inherit gap from .grid-2 */
+  .grid-2.modifier {
+    grid-template-areas:
+      "x   x"
+      "y   y"
+      "z   z";
+    -ms-grid-columns: 1fr 50px 1fr;
+    grid-template-columns: repeat(2, 1fr);
+    -ms-grid-rows: 100px 50px 100px 50px 100px;
+    grid-template-rows: repeat(3, 100px);
+  }
+  /* must inherit gap from .grid-2 */
+  .grid-2.modifier.modifier-c {
+    grid-template-areas:
+      "x   x"
+      "y   y"
+      "z   z";
+    -ms-grid-columns: 1fr 50px 1fr;
+    grid-template-columns: repeat(2, 1fr);
+    -ms-grid-rows: 100px 50px 100px 50px 100px;
+    grid-template-rows: repeat(3, 100px);
+  }
+  /* must inherit gap from .grid-2 */
+  .parent .grid-2 {
+    grid-template-areas:
+      "x   x"
+      "y   y"
+      "z   z";
+    -ms-grid-columns: 1fr 50px 1fr;
+    grid-template-columns: repeat(2, 1fr);
+    -ms-grid-rows: 100px 50px 100px 50px 100px;
+    grid-template-rows: repeat(3, 100px);
+  }
 }
 
 @media (max-width: 1000px){
@@ -247,6 +366,16 @@
     -ms-grid-rows: 100px 20px 100px;
     grid-template-rows: repeat(2, 100px);
   }
+  /* must inherit gap from .grid-2 */
+  .grid-2.modifier {
+    grid-template-areas:
+      "hi   hi   hi"
+      "hi   hi   hi";
+    -ms-grid-columns: 1fr 20px 1fr 20px 1fr;
+    grid-template-columns: repeat(3, 1fr);
+    -ms-grid-rows: 100px 20px 100px;
+    grid-template-rows: repeat(2, 100px);
+  }
 }
 
 @media (max-width: 700px){
@@ -258,6 +387,17 @@
     -ms-grid-rows: 100px;
     grid-template-rows: 100px;
   }
+  /* must NOT inherit gap from .grid-2 */
+  .grid-2.modifier {
+    grid-gap: 70px;
+    grid-template-areas:
+      "x   y   z"
+      "x   y   z";
+    -ms-grid-columns: 1fr 70px 1fr 70px 1fr;
+    grid-template-columns: repeat(3, 1fr);
+    -ms-grid-rows: 100px 70px 100px;
+    grid-template-rows: repeat(2, 100px);
+  }
 }
 
 @media (max-width: 400px){
@@ -274,4 +414,14 @@
     -ms-grid-rows: 100px 20px 100px 20px 100px 20px 100px 20px 100px 20px 100px;
     grid-template-rows: repeat(6, 100px);
   }
+  /* must inherit gap from .grid-2.modifier */
+  .grid-2.modifier {
+    grid-template-areas:
+      "x   y   z"
+      "x   y   z";
+    -ms-grid-columns: 1fr 70px 1fr 70px 1fr;
+    grid-template-columns: repeat(3, 1fr);
+    -ms-grid-rows: 100px 70px 100px;
+    grid-template-rows: repeat(2, 100px);
+  }
 }
diff --git a/test/cases/grid-template.out.css b/test/cases/grid-template.out.css
index c7e6d31..e7aa00a 100644
--- a/test/cases/grid-template.out.css
+++ b/test/cases/grid-template.out.css
@@ -304,7 +304,7 @@
 
 @media (max-width: 600px) {
   .i {
-    -ms-grid-rows: 100px 100px;
+    -ms-grid-rows: 100px 10px 100px;
     -ms-grid-columns: 1fr;
     grid-template:
       "i" 100px
@@ -312,7 +312,7 @@
       1fr;
   }
   .j {
-    -ms-grid-rows: 100px 100px;
+    -ms-grid-rows: 100px 10px 100px;
     -ms-grid-columns: 1fr;
     grid-template:
       "i" 100px
@@ -472,11 +472,11 @@
     -ms-grid-column: 1;
   }
   .k-4 {
-    -ms-grid-row: 2;
+    -ms-grid-row: 3;
     -ms-grid-column: 1;
   }
   .k-4 {
-    -ms-grid-row: 2;
+    -ms-grid-row: 3;
     -ms-grid-column: 1;
   }
   .k > .k-4 {
diff --git a/test/cases/grid.css b/test/cases/grid.css
index a6e2bdf..efcf6a9 100644
--- a/test/cases/grid.css
+++ b/test/cases/grid.css
@@ -120,3 +120,18 @@
 .warn-display-contents .grid {
   display: contents;
 }
+
+.warn_ie_align {
+  align-items: center;
+  display: grid;
+}
+
+.warn_ie_justify {
+  justify-items: center;
+  display: grid;
+}
+
+.warn_ie_justify_content {
+  justify-content: center;
+  display: grid;
+}
diff --git a/test/cases/grid.disabled.css b/test/cases/grid.disabled.css
index e4ba0c3..7bcf0cc 100644
--- a/test/cases/grid.disabled.css
+++ b/test/cases/grid.disabled.css
@@ -122,3 +122,20 @@
 .warn-display-contents .grid {
   display: contents;
 }
+
+.warn_ie_align {
+  -ms-flex-align: center;
+      align-items: center;
+  display: grid;
+}
+
+.warn_ie_justify {
+  justify-items: center;
+  display: grid;
+}
+
+.warn_ie_justify_content {
+  -ms-flex-pack: center;
+      justify-content: center;
+  display: grid;
+}
diff --git a/test/cases/grid.out.css b/test/cases/grid.out.css
index 517d0ac..8d80e6b 100644
--- a/test/cases/grid.out.css
+++ b/test/cases/grid.out.css
@@ -159,3 +159,25 @@
 .warn-display-contents .grid {
   display: contents;
 }
+
+.warn_ie_align {
+  -webkit-align-items: center;
+  -ms-flex-align: center;
+  align-items: center;
+  display: -ms-grid;
+  display: grid;
+}
+
+.warn_ie_justify {
+  justify-items: center;
+  display: -ms-grid;
+  display: grid;
+}
+
+.warn_ie_justify_content {
+  -webkit-justify-content: center;
+  -ms-flex-pack: center;
+  justify-content: center;
+  display: -ms-grid;
+  display: grid;
+}
diff --git a/test/utils.test.js b/test/utils.test.js
index 79553d2..1449019 100644
--- a/test/utils.test.js
+++ b/test/utils.test.js
@@ -100,3 +100,38 @@ describe('.editList()', () => {
     expect(list).toEqual('1, 2')
   })
 })
+
+describe('.splitSelector()', () => {
+  it('splits simple selectors into an array', () => {
+    let arr1 = utils.splitSelector('#foo.bar')
+    let arr2 = utils.splitSelector('.foo, .bar')
+    expect(arr1).toEqual([
+      [
+        ['#foo', '.bar']
+      ]
+    ])
+    expect(arr2).toEqual([
+      [['.foo']],
+      [['.bar']]
+    ])
+  })
+
+  it('splits complex selectors into an array', () => {
+    let arr = utils.splitSelector(
+      '#foo.bar .child-one.mod .child-two.mod, .baz, .hello'
+    )
+    expect(arr).toEqual([
+      [
+        ['#foo', '.bar'],
+        ['.child-one', '.mod'],
+        ['.child-two', '.mod']
+      ],
+      [
+        ['.baz']
+      ],
+      [
+        ['.hello']
+      ]
+    ])
+  })
+})