diff --git a/src/CanvasRenderer.js b/src/CanvasRenderer.js
index b77f8cf..3f61318 100644
--- a/src/CanvasRenderer.js
+++ b/src/CanvasRenderer.js
@@ -48,8 +48,10 @@ export default class CanvasRenderer {
     }
 
     renderNode(container: NodeContainer) {
-        this.renderNodeBackgroundAndBorders(container);
-        this.renderNodeContent(container);
+        if (container.isVisible()) {
+            this.renderNodeBackgroundAndBorders(container);
+            this.renderNodeContent(container);
+        }
     }
 
     renderNodeContent(container: NodeContainer) {
@@ -242,69 +244,71 @@ export default class CanvasRenderer {
     }
 
     renderStack(stack: StackingContext) {
-        this.ctx.globalAlpha = stack.getOpacity();
-        const transform = stack.container.style.transform;
-        if (transform !== null) {
-            this.ctx.save();
-            this.ctx.translate(
-                stack.container.bounds.left + transform.transformOrigin[0].value,
-                stack.container.bounds.top + transform.transformOrigin[1].value
-            );
-            this.ctx.transform(
-                transform.transform[0],
-                transform.transform[1],
-                transform.transform[2],
-                transform.transform[3],
-                transform.transform[4],
-                transform.transform[5]
-            );
-            this.ctx.translate(
-                -(stack.container.bounds.left + transform.transformOrigin[0].value),
-                -(stack.container.bounds.top + transform.transformOrigin[1].value)
-            );
-        }
-        const [
-            negativeZIndex,
-            zeroOrAutoZIndexOrTransformedOrOpacity,
-            positiveZIndex,
-            nonPositionedFloats,
-            nonPositionedInlineLevel
-        ] = splitStackingContexts(stack);
-        const [inlineLevel, nonInlineLevel] = splitDescendants(stack);
+        if (stack.container.isVisible()) {
+            this.ctx.globalAlpha = stack.getOpacity();
+            const transform = stack.container.style.transform;
+            if (transform !== null) {
+                this.ctx.save();
+                this.ctx.translate(
+                    stack.container.bounds.left + transform.transformOrigin[0].value,
+                    stack.container.bounds.top + transform.transformOrigin[1].value
+                );
+                this.ctx.transform(
+                    transform.transform[0],
+                    transform.transform[1],
+                    transform.transform[2],
+                    transform.transform[3],
+                    transform.transform[4],
+                    transform.transform[5]
+                );
+                this.ctx.translate(
+                    -(stack.container.bounds.left + transform.transformOrigin[0].value),
+                    -(stack.container.bounds.top + transform.transformOrigin[1].value)
+                );
+            }
+            const [
+                negativeZIndex,
+                zeroOrAutoZIndexOrTransformedOrOpacity,
+                positiveZIndex,
+                nonPositionedFloats,
+                nonPositionedInlineLevel
+            ] = splitStackingContexts(stack);
+            const [inlineLevel, nonInlineLevel] = splitDescendants(stack);
 
-        // https://www.w3.org/TR/css-position-3/#painting-order
-        // 1. the background and borders of the element forming the stacking context.
-        this.renderNodeBackgroundAndBorders(stack.container);
-        // 2. the child stacking contexts with negative stack levels (most negative first).
-        negativeZIndex.sort(sortByZIndex).forEach(this.renderStack, this);
-        // 3. For all its in-flow, non-positioned, block-level descendants in tree order:
-        this.renderNodeContent(stack.container);
-        nonInlineLevel.forEach(this.renderNode, this);
-        // 4. All non-positioned floating descendants, in tree order. For each one of these,
-        // treat the element as if it created a new stacking context, but any positioned descendants and descendants
-        // which actually create a new stacking context should be considered part of the parent stacking context,
-        // not this new one.
-        nonPositionedFloats.forEach(this.renderStack, this);
-        // 5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
-        nonPositionedInlineLevel.forEach(this.renderStack, this);
-        inlineLevel.forEach(this.renderNode, this);
-        // 6. All positioned, opacity or transform descendants, in tree order that fall into the following categories:
-        //  All positioned descendants with 'z-index: auto' or 'z-index: 0', in tree order.
-        //  For those with 'z-index: auto', treat the element as if it created a new stacking context,
-        //  but any positioned descendants and descendants which actually create a new stacking context should be
-        //  considered part of the parent stacking context, not this new one. For those with 'z-index: 0',
-        //  treat the stacking context generated atomically.
-        //
-        //  All opacity descendants with opacity less than 1
-        //
-        //  All transform descendants with transform other than none
-        zeroOrAutoZIndexOrTransformedOrOpacity.forEach(this.renderStack, this);
-        // 7. Stacking contexts formed by positioned descendants with z-indices greater than or equal to 1 in z-index
-        // order (smallest first) then tree order.
-        positiveZIndex.sort(sortByZIndex).forEach(this.renderStack, this);
+            // https://www.w3.org/TR/css-position-3/#painting-order
+            // 1. the background and borders of the element forming the stacking context.
+            this.renderNodeBackgroundAndBorders(stack.container);
+            // 2. the child stacking contexts with negative stack levels (most negative first).
+            negativeZIndex.sort(sortByZIndex).forEach(this.renderStack, this);
+            // 3. For all its in-flow, non-positioned, block-level descendants in tree order:
+            this.renderNodeContent(stack.container);
+            nonInlineLevel.forEach(this.renderNode, this);
+            // 4. All non-positioned floating descendants, in tree order. For each one of these,
+            // treat the element as if it created a new stacking context, but any positioned descendants and descendants
+            // which actually create a new stacking context should be considered part of the parent stacking context,
+            // not this new one.
+            nonPositionedFloats.forEach(this.renderStack, this);
+            // 5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
+            nonPositionedInlineLevel.forEach(this.renderStack, this);
+            inlineLevel.forEach(this.renderNode, this);
+            // 6. All positioned, opacity or transform descendants, in tree order that fall into the following categories:
+            //  All positioned descendants with 'z-index: auto' or 'z-index: 0', in tree order.
+            //  For those with 'z-index: auto', treat the element as if it created a new stacking context,
+            //  but any positioned descendants and descendants which actually create a new stacking context should be
+            //  considered part of the parent stacking context, not this new one. For those with 'z-index: 0',
+            //  treat the stacking context generated atomically.
+            //
+            //  All opacity descendants with opacity less than 1
+            //
+            //  All transform descendants with transform other than none
+            zeroOrAutoZIndexOrTransformedOrOpacity.forEach(this.renderStack, this);
+            // 7. Stacking contexts formed by positioned descendants with z-indices greater than or equal to 1 in z-index
+            // order (smallest first) then tree order.
+            positiveZIndex.sort(sortByZIndex).forEach(this.renderStack, this);
 
-        if (transform !== null) {
-            this.ctx.restore();
+            if (transform !== null) {
+                this.ctx.restore();
+            }
         }
     }
 
diff --git a/src/NodeContainer.js b/src/NodeContainer.js
index d0ea94b..62984d6 100644
--- a/src/NodeContainer.js
+++ b/src/NodeContainer.js
@@ -13,6 +13,7 @@ import type {Position} from './parsing/position';
 import type {TextTransform} from './parsing/textTransform';
 import type {TextDecoration} from './parsing/textDecoration';
 import type {Transform} from './parsing/transform';
+import type {Visibility} from './parsing/visibility';
 import type {zIndex} from './parsing/zIndex';
 
 import type {Bounds, BoundCurves, Path} from './Bounds';
@@ -36,6 +37,7 @@ import {parsePosition, POSITION} from './parsing/position';
 import {parseTextDecoration} from './parsing/textDecoration';
 import {parseTextTransform} from './parsing/textTransform';
 import {parseTransform} from './parsing/transform';
+import {parseVisibility, VISIBILITY} from './parsing/visibility';
 import {parseZIndex} from './parsing/zIndex';
 
 import {parseBounds, parseBoundCurves, calculatePaddingBoxPath} from './Bounds';
@@ -56,6 +58,7 @@ type StyleDeclaration = {
     textDecoration: TextDecoration,
     textTransform: TextTransform,
     transform: Transform,
+    visibility: Visibility,
     zIndex: zIndex
 };
 
@@ -90,6 +93,7 @@ export default class NodeContainer {
             textDecoration: parseTextDecoration(style),
             textTransform: parseTextTransform(style.textTransform),
             transform: parseTransform(style),
+            visibility: parseVisibility(style.visibility),
             zIndex: parseZIndex(style.zIndex)
         };
 
@@ -127,7 +131,11 @@ export default class NodeContainer {
         return this.isRootElement() && !this.isFloating() && !this.isAbsolutelyPositioned();
     }
     isVisible(): boolean {
-        return !contains(this.style.display, DISPLAY.NONE) && this.style.opacity > 0;
+        return (
+            !contains(this.style.display, DISPLAY.NONE) &&
+            this.style.opacity > 0 &&
+            this.style.visibility === VISIBILITY.VISIBLE
+        );
     }
     isAbsolutelyPositioned(): boolean {
         return this.style.position !== POSITION.STATIC && this.style.position !== POSITION.RELATIVE;
diff --git a/src/parsing/visibility.js b/src/parsing/visibility.js
new file mode 100644
index 0000000..2362d98
--- /dev/null
+++ b/src/parsing/visibility.js
@@ -0,0 +1,22 @@
+/* @flow */
+'use strict';
+
+export const VISIBILITY = {
+    VISIBLE: 0,
+    HIDDEN: 1,
+    COLLAPSE: 2
+};
+
+export type Visibility = $Values<typeof VISIBILITY>;
+
+export const parseVisibility = (visibility: string): Visibility => {
+    switch (visibility) {
+        case 'hidden':
+            return VISIBILITY.HIDDEN;
+        case 'collapse':
+            return VISIBILITY.COLLAPSE;
+        case 'visible':
+        default:
+            return VISIBILITY.VISIBLE;
+    }
+};
diff --git a/tests/cases/visibility.html b/tests/cases/visibility.html
index 9c09a63..77dcdb9 100644
--- a/tests/cases/visibility.html
+++ b/tests/cases/visibility.html
@@ -1,24 +1,30 @@
 <!DOCTYPE html>
 <html>
-    <head>
-        <title>Visible elements tests</title>
-        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-        <script type="text/javascript" src="../test.js"></script>
-        <style>
-            div{
-                border:2px solid black;
-            }
-        </style>
+<head>
+    <title>Visible elements tests</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <script type="text/javascript" src="../test.js"></script>
+    <style>
+        div{
+            border:2px solid black;
+        }
+        .none {
+            display:none
+        }
+    </style>
 
-    </head>
-    <body>
-        <h1>Display:none and visible:hidden tests</h1>
-        <div>This should be visible </div>
-        <div style="display:none">display:none, This should be <b>hidden</b></div>
-        <div style="visibility:hidden">visibility:hidden, This should be <b>hidden</b></div>
-        <hr />
-        <div style="display:none">display:none, This should be <b>hidden</b></div>
-        <div style="visibility:hidden">visibility:hidden, This should be <b>hidden</b></div>
+</head>
+<body>
+<div>
+    <h1>Display:none and visible:hidden tests</h1>
+    <div>This should be visible </div>
+    <div class="none">display:none, This should be <b>hidden</b></div>
+    <div style="visibility:hidden">visibility:hidden, This should be <b>hidden</b></div>
+    <hr />
+    <div class="none">display:none, This should be <b>hidden</b></div>
+    <div style="visibility:hidden">visibility:hidden, This should be <b>hidden</b></div>
+</div>
 
-    </body>
+
+</body>
 </html>