diff --git a/README.md b/README.md
index 01694129..89dbe00b 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 ## ACM Teach LA's Editor Frontend
 
-[![Build Status](https://travis-ci.org/uclaacm/TeachLAFrontend.svg?branch=master)](https://travis-ci.org/uclaacm/TeachLAFrontend) 
+[![Build Status](https://travis-ci.org/uclaacm/TeachLAFrontend.svg?branch=master)](https://travis-ci.org/uclaacm/TeachLAFrontend)
 [![Netlify Status](https://api.netlify.com/api/v1/badges/15895bed-2a7e-4a27-aa63-633a0cd645f1/deploy-status)](https://app.netlify.com/sites/sleepy-franklin-7a3e4c/deploys)
 
 This repository holds the frontend code for the ACM Teach LA online editor! Teach LA uses the editor to help teach LA students about Python, Web Development, and expose them to computer science!
@@ -58,7 +58,7 @@ The client should now be automatically opened in your browser; however, you can
 
 ## Notes for Developers:
 
-* every time you pull from master, make sure to run `npm install` - it's likely that some dependency has changed!
-* `lint-staged` and `husky` auto-prettify some JS code on save - don't be spooked!
-* Travis CI auto-builds branches and PRs - make sure that `npm run test` `npm run prod_build` pass, or your changes for-sure won't work!
-* Netlify auto-deploys PRs, branches, and production deploys using the contents of `npm run prod_build`!
+- every time you pull from master, make sure to run `npm install` - it's likely that some dependency has changed!
+- `lint-staged` and `husky` auto-prettify some JS code on save - don't be spooked!
+- Travis CI auto-builds branches and PRs - make sure that `npm run test` `npm run prod_build` pass, or your changes for-sure won't work!
+- Netlify auto-deploys PRs, branches, and production deploys using the contents of `npm run prod_build`!
diff --git a/package-lock.json b/package-lock.json
index 77cb3f91..c47da7d3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1944,6 +1944,11 @@
         "strip-ansi": "^5.0.0"
       },
       "dependencies": {
+        "ansi-escapes": {
+          "version": "3.2.0",
+          "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
+          "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ=="
+        },
         "ansi-regex": {
           "version": "4.1.0",
           "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
@@ -2919,9 +2924,19 @@
       "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA=="
     },
     "ansi-escapes": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
-      "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ=="
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz",
+      "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==",
+      "requires": {
+        "type-fest": "^0.8.1"
+      },
+      "dependencies": {
+        "type-fest": {
+          "version": "0.8.1",
+          "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+          "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="
+        }
+      }
     },
     "ansi-html": {
       "version": "0.0.7",
@@ -2951,16 +2966,6 @@
       "requires": {
         "micromatch": "^3.1.4",
         "normalize-path": "^2.1.1"
-      },
-      "dependencies": {
-        "normalize-path": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
-          "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
-          "requires": {
-            "remove-trailing-separator": "^1.0.1"
-          }
-        }
       }
     },
     "aproba": {
@@ -3754,12 +3759,30 @@
       }
     },
     "braces": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
-      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
-      "dev": true,
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+      "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
       "requires": {
-        "fill-range": "^7.0.1"
+        "arr-flatten": "^1.1.0",
+        "array-unique": "^0.3.2",
+        "extend-shallow": "^2.0.1",
+        "fill-range": "^4.0.0",
+        "isobject": "^3.0.1",
+        "repeat-element": "^1.1.2",
+        "snapdragon": "^0.8.1",
+        "snapdragon-node": "^2.0.1",
+        "split-string": "^3.0.2",
+        "to-regex": "^3.0.1"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        }
       }
     },
     "brorand": {
@@ -3853,9 +3876,9 @@
       }
     },
     "browserslist": {
-      "version": "4.8.1",
-      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.1.tgz",
-      "integrity": "sha512-X/lIDboA5bvFg9SOhHN7OBgHHlaZQWcwTXBLBGgrB8+6Iy1dL0wmUfHRe7OdETRTuD2d6f5JPa7iTEcbVyVf6Q==",
+      "version": "4.8.2",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.2.tgz",
+      "integrity": "sha512-+M4oeaTplPm/f1pXDw84YohEv7B1i/2Aisei8s4s6k3QsoSHa7i5sz8u/cGQkkatCPxMASKxPualR4wwYgVboA==",
       "requires": {
         "caniuse-lite": "^1.0.30001015",
         "electron-to-chromium": "^1.3.322",
@@ -4158,42 +4181,6 @@
         "upath": "^1.1.1"
       },
       "dependencies": {
-        "braces": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
-          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
-          "requires": {
-            "arr-flatten": "^1.1.0",
-            "array-unique": "^0.3.2",
-            "extend-shallow": "^2.0.1",
-            "fill-range": "^4.0.0",
-            "isobject": "^3.0.1",
-            "repeat-element": "^1.1.2",
-            "snapdragon": "^0.8.1",
-            "snapdragon-node": "^2.0.1",
-            "split-string": "^3.0.2",
-            "to-regex": "^3.0.1"
-          }
-        },
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        },
-        "fill-range": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
-          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
-          "requires": {
-            "extend-shallow": "^2.0.1",
-            "is-number": "^3.0.0",
-            "repeat-string": "^1.6.1",
-            "to-regex-range": "^2.1.0"
-          }
-        },
         "fsevents": {
           "version": "1.2.9",
           "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz",
@@ -4694,22 +4681,10 @@
             }
           }
         },
-        "is-number": {
+        "normalize-path": {
           "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
-          "requires": {
-            "kind-of": "^3.0.2"
-          }
-        },
-        "to-regex-range": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
-          "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
-          "requires": {
-            "is-number": "^3.0.0",
-            "repeat-string": "^1.6.1"
-          }
+          "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+          "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
         }
       }
     },
@@ -4787,11 +4762,11 @@
       "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="
     },
     "cli-cursor": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
-      "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+      "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
       "requires": {
-        "restore-cursor": "^2.0.0"
+        "restore-cursor": "^3.1.0"
       }
     },
     "cli-truncate": {
@@ -5390,6 +5365,11 @@
           "version": "5.3.1",
           "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
           "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
+        },
+        "normalize-path": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+          "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
         }
       }
     },
@@ -7225,9 +7205,9 @@
       "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w=="
     },
     "figures": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
-      "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/figures/-/figures-3.1.0.tgz",
+      "integrity": "sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg==",
       "requires": {
         "escape-string-regexp": "^1.0.5"
       }
@@ -7255,12 +7235,24 @@
       "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg=="
     },
     "fill-range": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
-      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
-      "dev": true,
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+      "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
       "requires": {
-        "to-regex-range": "^5.0.1"
+        "extend-shallow": "^2.0.1",
+        "is-number": "^3.0.0",
+        "repeat-string": "^1.6.1",
+        "to-regex-range": "^2.1.0"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        }
       }
     },
     "finalhandler": {
@@ -7600,9 +7592,9 @@
       "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w=="
     },
     "get-own-enumerable-property-symbols": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.0.tgz",
-      "integrity": "sha512-CIJYJC4GGF06TakLg8z4GQKvDsx9EMspVxOYih7LerEL/WosUnFIww45CGfxfeKHqlg3twgUrYRT1O3WQqjGCg=="
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.1.tgz",
+      "integrity": "sha512-09/VS4iek66Dh2bctjRkowueRJbY1JDGR1L/zRxO1Qk8Uxs6PnqaNSqalpizPT+CDjre3hnEsuzvhgomz9qYrA=="
     },
     "get-stdin": {
       "version": "4.0.1",
@@ -7752,19 +7744,23 @@
       "dependencies": {
         "abbrev": {
           "version": "1.1.1",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
         },
         "ansi-regex": {
           "version": "2.1.1",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
         },
         "aproba": {
           "version": "1.2.0",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
         },
         "are-we-there-yet": {
           "version": "1.1.5",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
           "requires": {
             "delegates": "^1.0.0",
             "readable-stream": "^2.0.6"
@@ -7772,11 +7768,13 @@
         },
         "balanced-match": {
           "version": "1.0.0",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
         },
         "brace-expansion": {
           "version": "1.1.11",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
           "requires": {
             "balanced-match": "^1.0.0",
             "concat-map": "0.0.1"
@@ -7784,57 +7782,69 @@
         },
         "chownr": {
           "version": "1.1.3",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw=="
         },
         "code-point-at": {
           "version": "1.1.0",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
         },
         "concat-map": {
           "version": "0.0.1",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
         },
         "console-control-strings": {
           "version": "1.1.0",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
         },
         "core-util-is": {
           "version": "1.0.2",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
         },
         "debug": {
           "version": "3.2.6",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
           "requires": {
             "ms": "^2.1.1"
           }
         },
         "deep-extend": {
           "version": "0.6.0",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
         },
         "delegates": {
           "version": "1.0.0",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
         },
         "detect-libc": {
           "version": "1.0.3",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
         },
         "fs-minipass": {
           "version": "1.2.7",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
           "requires": {
             "minipass": "^2.6.0"
           }
         },
         "fs.realpath": {
           "version": "1.0.0",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
         },
         "gauge": {
           "version": "2.7.4",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
           "requires": {
             "aproba": "^1.0.3",
             "console-control-strings": "^1.0.0",
@@ -7848,7 +7858,8 @@
         },
         "glob": {
           "version": "7.1.4",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
           "requires": {
             "fs.realpath": "^1.0.0",
             "inflight": "^1.0.4",
@@ -7860,25 +7871,29 @@
         },
         "has-unicode": {
           "version": "2.0.1",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
         },
         "iconv-lite": {
           "version": "0.4.24",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
           "requires": {
             "safer-buffer": ">= 2.1.2 < 3"
           }
         },
         "ignore-walk": {
           "version": "3.0.3",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==",
           "requires": {
             "minimatch": "^3.0.4"
           }
         },
         "inflight": {
           "version": "1.0.6",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
           "requires": {
             "once": "^1.3.0",
             "wrappy": "1"
@@ -7886,37 +7901,44 @@
         },
         "inherits": {
           "version": "2.0.4",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
         },
         "ini": {
           "version": "1.3.5",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
         },
         "is-fullwidth-code-point": {
           "version": "1.0.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
           "requires": {
             "number-is-nan": "^1.0.0"
           }
         },
         "isarray": {
           "version": "1.0.0",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
         },
         "minimatch": {
           "version": "3.0.4",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
           "requires": {
             "brace-expansion": "^1.1.7"
           }
         },
         "minimist": {
           "version": "1.2.0",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
         },
         "minipass": {
           "version": "2.9.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
           "requires": {
             "safe-buffer": "^5.1.2",
             "yallist": "^3.0.0"
@@ -7924,31 +7946,36 @@
         },
         "minizlib": {
           "version": "1.3.3",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
           "requires": {
             "minipass": "^2.9.0"
           }
         },
         "mkdirp": {
           "version": "0.5.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
           "requires": {
             "minimist": "0.0.8"
           },
           "dependencies": {
             "minimist": {
               "version": "0.0.8",
-              "bundled": true
+              "resolved": false,
+              "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
             }
           }
         },
         "ms": {
           "version": "2.1.2",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
         },
         "needle": {
           "version": "2.4.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==",
           "requires": {
             "debug": "^3.2.6",
             "iconv-lite": "^0.4.4",
@@ -7957,7 +7984,8 @@
         },
         "node-pre-gyp": {
           "version": "0.14.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA==",
           "requires": {
             "detect-libc": "^1.0.2",
             "mkdirp": "^0.5.1",
@@ -7973,7 +8001,8 @@
         },
         "nopt": {
           "version": "4.0.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
           "requires": {
             "abbrev": "1",
             "osenv": "^0.1.4"
@@ -7981,11 +8010,13 @@
         },
         "npm-bundled": {
           "version": "1.0.6",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g=="
         },
         "npm-packlist": {
           "version": "1.4.6",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-u65uQdb+qwtGvEJh/DgQgW1Xg7sqeNbmxYyrvlNznaVTjV3E5P6F/EFjM+BVHXl7JJlsdG8A64M0XI8FI/IOlg==",
           "requires": {
             "ignore-walk": "^3.0.1",
             "npm-bundled": "^1.0.1"
@@ -7993,7 +8024,8 @@
         },
         "npmlog": {
           "version": "4.1.2",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
           "requires": {
             "are-we-there-yet": "~1.1.2",
             "console-control-strings": "~1.1.0",
@@ -8003,30 +8035,36 @@
         },
         "number-is-nan": {
           "version": "1.0.1",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
         },
         "object-assign": {
           "version": "4.1.1",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
         },
         "once": {
           "version": "1.4.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
           "requires": {
             "wrappy": "1"
           }
         },
         "os-homedir": {
           "version": "1.0.2",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
         },
         "os-tmpdir": {
           "version": "1.0.2",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
         },
         "osenv": {
           "version": "0.1.5",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
           "requires": {
             "os-homedir": "^1.0.0",
             "os-tmpdir": "^1.0.0"
@@ -8034,11 +8072,13 @@
         },
         "path-is-absolute": {
           "version": "1.0.1",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
         },
         "process-nextick-args": {
           "version": "2.0.1",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
         },
         "protobufjs": {
           "version": "5.0.3",
@@ -8053,7 +8093,8 @@
         },
         "rc": {
           "version": "1.2.8",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
           "requires": {
             "deep-extend": "^0.6.0",
             "ini": "~1.3.0",
@@ -8063,7 +8104,8 @@
         },
         "readable-stream": {
           "version": "2.3.6",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
           "requires": {
             "core-util-is": "~1.0.0",
             "inherits": "~2.0.3",
@@ -8076,38 +8118,46 @@
         },
         "rimraf": {
           "version": "2.7.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
           "requires": {
             "glob": "^7.1.3"
           }
         },
         "safe-buffer": {
           "version": "5.1.2",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
         },
         "safer-buffer": {
           "version": "2.1.2",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
         },
         "sax": {
           "version": "1.2.4",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
         },
         "semver": {
           "version": "5.7.1",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
         },
         "set-blocking": {
           "version": "2.0.0",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
         },
         "signal-exit": {
           "version": "3.0.2",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
         },
         "string-width": {
           "version": "1.0.2",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
           "requires": {
             "code-point-at": "^1.0.0",
             "is-fullwidth-code-point": "^1.0.0",
@@ -8116,25 +8166,29 @@
         },
         "string_decoder": {
           "version": "1.1.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
           "requires": {
             "safe-buffer": "~5.1.0"
           }
         },
         "strip-ansi": {
           "version": "3.0.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
           "requires": {
             "ansi-regex": "^2.0.0"
           }
         },
         "strip-json-comments": {
           "version": "2.0.1",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
         },
         "tar": {
           "version": "4.4.13",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==",
           "requires": {
             "chownr": "^1.1.1",
             "fs-minipass": "^1.2.5",
@@ -8147,22 +8201,26 @@
         },
         "util-deprecate": {
           "version": "1.0.2",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
         },
         "wide-align": {
           "version": "1.1.3",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
           "requires": {
             "string-width": "^1.0.2 || 2"
           }
         },
         "wrappy": {
           "version": "1.0.2",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
         },
         "yallist": {
           "version": "3.1.1",
-          "bundled": true
+          "resolved": false,
+          "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
         }
       }
     },
@@ -8279,24 +8337,6 @@
         "kind-of": "^4.0.0"
       },
       "dependencies": {
-        "is-number": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
-          "requires": {
-            "kind-of": "^3.0.2"
-          },
-          "dependencies": {
-            "kind-of": {
-              "version": "3.2.2",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-              "requires": {
-                "is-buffer": "^1.1.5"
-              }
-            }
-          }
-        },
         "kind-of": {
           "version": "4.0.0",
           "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
@@ -8832,14 +8872,6 @@
         "through": "^2.3.6"
       },
       "dependencies": {
-        "ansi-escapes": {
-          "version": "4.3.0",
-          "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz",
-          "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==",
-          "requires": {
-            "type-fest": "^0.8.1"
-          }
-        },
         "ansi-regex": {
           "version": "5.0.0",
           "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
@@ -8863,49 +8895,11 @@
             "supports-color": "^5.3.0"
           }
         },
-        "cli-cursor": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
-          "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
-          "requires": {
-            "restore-cursor": "^3.1.0"
-          }
-        },
-        "figures": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/figures/-/figures-3.1.0.tgz",
-          "integrity": "sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg==",
-          "requires": {
-            "escape-string-regexp": "^1.0.5"
-          }
-        },
         "is-fullwidth-code-point": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
           "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
         },
-        "mimic-fn": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
-          "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
-        },
-        "onetime": {
-          "version": "5.1.0",
-          "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz",
-          "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==",
-          "requires": {
-            "mimic-fn": "^2.1.0"
-          }
-        },
-        "restore-cursor": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
-          "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
-          "requires": {
-            "onetime": "^5.1.0",
-            "signal-exit": "^3.0.2"
-          }
-        },
         "string-width": {
           "version": "4.2.0",
           "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
@@ -8948,11 +8942,6 @@
           "requires": {
             "has-flag": "^3.0.0"
           }
-        },
-        "type-fest": {
-          "version": "0.8.1",
-          "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
-          "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="
         }
       }
     },
@@ -9136,10 +9125,12 @@
       }
     },
     "is-number": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
-      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
-      "dev": true
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+      "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+      "requires": {
+        "kind-of": "^3.0.2"
+      }
     },
     "is-number-object": {
       "version": "1.0.3",
@@ -10924,14 +10915,6 @@
         "strip-ansi": "^5.0.0"
       },
       "dependencies": {
-        "ansi-escapes": {
-          "version": "4.3.0",
-          "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz",
-          "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==",
-          "requires": {
-            "type-fest": "^0.8.1"
-          }
-        },
         "ansi-regex": {
           "version": "4.1.0",
           "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
@@ -10984,11 +10967,6 @@
           "requires": {
             "has-flag": "^3.0.0"
           }
-        },
-        "type-fest": {
-          "version": "0.8.1",
-          "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
-          "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="
         }
       }
     },
@@ -11006,6 +10984,11 @@
         "string-length": "^2.0.0"
       },
       "dependencies": {
+        "ansi-escapes": {
+          "version": "3.2.0",
+          "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
+          "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ=="
+        },
         "ansi-styles": {
           "version": "3.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
@@ -11313,6 +11296,15 @@
           "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
           "dev": true
         },
+        "braces": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+          "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+          "dev": true,
+          "requires": {
+            "fill-range": "^7.0.1"
+          }
+        },
         "chalk": {
           "version": "2.4.2",
           "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -11378,9 +11370,9 @@
           }
         },
         "fast-glob": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.1.0.tgz",
-          "integrity": "sha512-TrUz3THiq2Vy3bjfQUB2wNyPdGBeGmdjbzzBLhfHN4YFurYptCKwGq/TfiRavbGywFRzY6U2CdmQ1zmsY5yYaw==",
+          "version": "3.1.1",
+          "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.1.1.tgz",
+          "integrity": "sha512-nTCREpBY8w8r+boyFYAx21iL6faSsQynliPHM4Uf56SbkyohCNxpVPEH9xrF5TXKy+IsjkPUHDKiUkzBVRXn9g==",
           "dev": true,
           "requires": {
             "@nodelib/fs.stat": "^2.0.2",
@@ -11390,6 +11382,15 @@
             "micromatch": "^4.0.2"
           }
         },
+        "fill-range": {
+          "version": "7.0.1",
+          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+          "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+          "dev": true,
+          "requires": {
+            "to-regex-range": "^5.0.1"
+          }
+        },
         "get-stream": {
           "version": "5.1.0",
           "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz",
@@ -11421,10 +11422,10 @@
           "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==",
           "dev": true
         },
-        "is-path-cwd": {
-          "version": "2.2.0",
-          "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz",
-          "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==",
+        "is-number": {
+          "version": "7.0.0",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+          "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
           "dev": true
         },
         "is-path-inside": {
@@ -11449,10 +11450,10 @@
             "picomatch": "^2.0.5"
           }
         },
-        "mimic-fn": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
-          "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+        "normalize-path": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+          "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
           "dev": true
         },
         "npm-run-path": {
@@ -11464,30 +11465,12 @@
             "path-key": "^3.0.0"
           }
         },
-        "onetime": {
-          "version": "5.1.0",
-          "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz",
-          "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==",
-          "dev": true,
-          "requires": {
-            "mimic-fn": "^2.1.0"
-          }
-        },
         "p-finally": {
           "version": "2.0.1",
           "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz",
           "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==",
           "dev": true
         },
-        "p-map": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz",
-          "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==",
-          "dev": true,
-          "requires": {
-            "aggregate-error": "^3.0.0"
-          }
-        },
         "path-key": {
           "version": "3.1.1",
           "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
@@ -11539,6 +11522,15 @@
             "has-flag": "^3.0.0"
           }
         },
+        "to-regex-range": {
+          "version": "5.0.1",
+          "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+          "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+          "dev": true,
+          "requires": {
+            "is-number": "^7.0.0"
+          }
+        },
         "which": {
           "version": "2.0.2",
           "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -11656,6 +11648,49 @@
             "supports-color": "^5.3.0"
           }
         },
+        "cli-cursor": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
+          "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
+          "dev": true,
+          "requires": {
+            "restore-cursor": "^2.0.0"
+          }
+        },
+        "figures": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
+          "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
+          "dev": true,
+          "requires": {
+            "escape-string-regexp": "^1.0.5"
+          }
+        },
+        "mimic-fn": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
+          "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
+          "dev": true
+        },
+        "onetime": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
+          "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
+          "dev": true,
+          "requires": {
+            "mimic-fn": "^1.0.0"
+          }
+        },
+        "restore-cursor": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
+          "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
+          "dev": true,
+          "requires": {
+            "onetime": "^2.0.0",
+            "signal-exit": "^3.0.2"
+          }
+        },
         "supports-color": {
           "version": "5.5.0",
           "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
@@ -11875,18 +11910,58 @@
         "wrap-ansi": "^3.0.1"
       },
       "dependencies": {
+        "ansi-escapes": {
+          "version": "3.2.0",
+          "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
+          "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
+          "dev": true
+        },
         "ansi-regex": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
           "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
           "dev": true
         },
+        "cli-cursor": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
+          "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
+          "dev": true,
+          "requires": {
+            "restore-cursor": "^2.0.0"
+          }
+        },
         "is-fullwidth-code-point": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
           "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
           "dev": true
         },
+        "mimic-fn": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
+          "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
+          "dev": true
+        },
+        "onetime": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
+          "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
+          "dev": true,
+          "requires": {
+            "mimic-fn": "^1.0.0"
+          }
+        },
+        "restore-cursor": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
+          "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
+          "dev": true,
+          "requires": {
+            "onetime": "^2.0.0",
+            "signal-exit": "^3.0.2"
+          }
+        },
         "string-width": {
           "version": "2.1.1",
           "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
@@ -12042,13 +12117,6 @@
         "map-age-cleaner": "^0.1.1",
         "mimic-fn": "^2.0.0",
         "p-is-promise": "^2.0.0"
-      },
-      "dependencies": {
-        "mimic-fn": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
-          "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
-        }
       }
     },
     "memory-fs": {
@@ -12132,85 +12200,10 @@
         "to-regex": "^3.0.2"
       },
       "dependencies": {
-        "braces": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
-          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
-          "requires": {
-            "arr-flatten": "^1.1.0",
-            "array-unique": "^0.3.2",
-            "extend-shallow": "^2.0.1",
-            "fill-range": "^4.0.0",
-            "isobject": "^3.0.1",
-            "repeat-element": "^1.1.2",
-            "snapdragon": "^0.8.1",
-            "snapdragon-node": "^2.0.1",
-            "split-string": "^3.0.2",
-            "to-regex": "^3.0.1"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
-          }
-        },
-        "fill-range": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
-          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
-          "requires": {
-            "extend-shallow": "^2.0.1",
-            "is-number": "^3.0.0",
-            "repeat-string": "^1.6.1",
-            "to-regex-range": "^2.1.0"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
-          }
-        },
-        "is-number": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
-          "requires": {
-            "kind-of": "^3.0.2"
-          },
-          "dependencies": {
-            "kind-of": {
-              "version": "3.2.2",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-              "requires": {
-                "is-buffer": "^1.1.5"
-              }
-            }
-          }
-        },
         "kind-of": {
           "version": "6.0.2",
           "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
           "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
-        },
-        "to-regex-range": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
-          "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
-          "requires": {
-            "is-number": "^3.0.0",
-            "repeat-string": "^1.6.1"
-          }
         }
       }
     },
@@ -12242,9 +12235,9 @@
       }
     },
     "mimic-fn": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
-      "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
     },
     "mini-create-react-context": {
       "version": "0.3.2",
@@ -12709,9 +12702,12 @@
       }
     },
     "normalize-path": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
-      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+      "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+      "requires": {
+        "remove-trailing-separator": "^1.0.1"
+      }
     },
     "normalize-range": {
       "version": "0.1.2",
@@ -12961,11 +12957,11 @@
       }
     },
     "onetime": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
-      "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz",
+      "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==",
       "requires": {
-        "mimic-fn": "^1.0.0"
+        "mimic-fn": "^2.1.0"
       }
     },
     "open": {
@@ -13300,9 +13296,9 @@
       "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
     },
     "picomatch": {
-      "version": "2.0.7",
-      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.0.7.tgz",
-      "integrity": "sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==",
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.1.1.tgz",
+      "integrity": "sha512-OYMyqkKzK7blWO/+XZYP6w8hH0LDvkBvdvKukti+7kqYFCiEAk+gI3DWnryapc0Dau05ugGTy0foQ6mqn4AHYA==",
       "dev": true
     },
     "pify": {
@@ -14827,6 +14823,11 @@
         "text-table": "0.2.0"
       },
       "dependencies": {
+        "ansi-escapes": {
+          "version": "3.2.0",
+          "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
+          "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ=="
+        },
         "ansi-regex": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
@@ -14860,6 +14861,22 @@
             "supports-color": "^5.3.0"
           }
         },
+        "cli-cursor": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
+          "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
+          "requires": {
+            "restore-cursor": "^2.0.0"
+          }
+        },
+        "figures": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
+          "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
+          "requires": {
+            "escape-string-regexp": "^1.0.5"
+          }
+        },
         "find-up": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
@@ -14893,11 +14910,33 @@
           "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
           "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
         },
+        "mimic-fn": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
+          "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="
+        },
         "mute-stream": {
           "version": "0.0.7",
           "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
           "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s="
         },
+        "onetime": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
+          "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
+          "requires": {
+            "mimic-fn": "^1.0.0"
+          }
+        },
+        "restore-cursor": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
+          "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
+          "requires": {
+            "onetime": "^2.0.0",
+            "signal-exit": "^3.0.2"
+          }
+        },
         "string-width": {
           "version": "2.1.1",
           "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
@@ -15607,11 +15646,11 @@
       }
     },
     "restore-cursor": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
-      "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+      "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
       "requires": {
-        "onetime": "^2.0.0",
+        "onetime": "^5.1.0",
         "signal-exit": "^3.0.2"
       }
     },
@@ -17151,12 +17190,12 @@
       }
     },
     "to-regex-range": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
-      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
-      "dev": true,
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+      "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
       "requires": {
-        "is-number": "^7.0.0"
+        "is-number": "^3.0.0",
+        "repeat-string": "^1.6.1"
       }
     },
     "toidentifier": {
@@ -17207,9 +17246,9 @@
       "integrity": "sha512-ti7OGMOUOzo66wLF3liskw6YQIaSsBgc4GOAlWRnIEj8htCxJUxskanMUoJOD6MDCRAXo36goXJZch+nOS0VMA=="
     },
     "tslib": {
-      "version": "1.9.3",
-      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
-      "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
+      "version": "1.10.0",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
+      "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
     },
     "tsutils": {
       "version": "3.17.1",
diff --git a/src/actions/uiActions.js b/src/actions/uiActions.js
index 63e857ae..8d841ea3 100644
--- a/src/actions/uiActions.js
+++ b/src/actions/uiActions.js
@@ -12,3 +12,8 @@ export const SET_PANEL = "SET_PANEL";
 export function setPanel(value) {
   return { type: SET_PANEL, value };
 }
+
+export const SET_THEME = "SET_THEME";
+export function setTheme(theme) {
+  return { type: SET_THEME, theme };
+}
diff --git a/src/components/Main.js b/src/components/Main.js
index 1d20a25d..d30cc2c0 100644
--- a/src/components/Main.js
+++ b/src/components/Main.js
@@ -4,6 +4,7 @@ import OutputContainer from "./Output/OutputContainer.js";
 import TextEditorContainer from "./TextEditor/containers/TextEditorContainer.js";
 import DropdownButtonContainer from "./common/containers/DropdownButtonContainer";
 import * as fetch from "../lib/fetch.js";
+import * as cookies from "../lib/cookies.js";
 import SketchesPageContainer from "./Sketches/containers/SketchesContainer";
 import "styles/Main.scss";
 import ProfilePanelContainer from "./common/containers/ProfilePanelContainer";
@@ -15,6 +16,7 @@ import CodeDownloader from "../util/languages/CodeDownloader";
 import { PANEL_SIZE } from "../constants";
 import "codemirror/lib/codemirror.css";
 import "codemirror/theme/material.css";
+import "codemirror/theme/duotone-light.css";
 import "styles/CustomCM.scss";
 import "styles/Resizer.scss";
 import "styles/Editor.scss";
@@ -34,6 +36,9 @@ class Main extends React.Component {
       redirect: this.props.listOfPrograms.length === 0 ? "/sketches" : "",
       pane1Style: { transition: "width .5s ease" },
     };
+
+    // Set theme from cookies (yum)
+    this.props.setTheme(cookies.getThemeFromCookie());
   }
 
   //==============React Lifecycle Functions Start===================//
@@ -47,6 +52,12 @@ class Main extends React.Component {
     }
   }
 
+  onThemeChange = () => {
+    let newTheme = this.props.theme === "dark" ? "light" : "dark";
+    cookies.setThemeCookie(newTheme);
+    this.props.setTheme(newTheme);
+  };
+
   resetSaveText = () => {
     this.setState({
       saveText: "Save",
@@ -139,6 +150,7 @@ class Main extends React.Component {
       screenHeight={this.props.screenHeight}
       screenWidth={this.props.screenWidth}
       handleDownload={this.handleDownload}
+      theme={this.props.theme}
     />
   );
 
@@ -178,8 +190,12 @@ class Main extends React.Component {
     };
 
     return (
-      <div className="main">
-        <ProfilePanelContainer contentType={this.props.contentType} />
+      <div className={`main theme-` + this.props.theme}>
+        <ProfilePanelContainer
+          contentType={this.props.contentType}
+          theme={this.props.theme}
+          onThemeChange={this.onThemeChange}
+        />
         <div className="editor" style={codeStyle}>
           {this.renderContent()}
         </div>
diff --git a/src/components/Output/OutputContainer.js b/src/components/Output/OutputContainer.js
index c55ebd89..ed2155ab 100644
--- a/src/components/Output/OutputContainer.js
+++ b/src/components/Output/OutputContainer.js
@@ -18,9 +18,6 @@ const mapDispatchToProps = dispatch => {
   };
 };
 
-const OutputContainer = connect(
-  mapStateToProps,
-  mapDispatchToProps,
-)(Output);
+const OutputContainer = connect(mapStateToProps, mapDispatchToProps)(Output);
 
 export default OutputContainer;
diff --git a/src/components/Output/Python.js b/src/components/Output/Python.js
index 7d0451f7..00c33246 100644
--- a/src/components/Output/Python.js
+++ b/src/components/Output/Python.js
@@ -3,27 +3,25 @@ const getPythonSrcDocHead = () => `
   <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js" type="text/javascript"></script>
   <script src="https://cdn.rawgit.com/skulpt/skulpt-dist/1.1.0/skulpt.min.js" type="text/javascript"></script>
   <script src="https://cdn.rawgit.com/skulpt/skulpt-dist/1.1.0/skulpt-stdlib.js" type="text/javascript"></script>
-  <style> html, body { margin:0; background-color: #585166;}
-          #inner {
-            height:100px;
-            background-color:#222;
-            color: #DDD;
-            font-family: monospace;
-            word-wrap:break-word;
-            overflow:auto;
-            margin: 10px auto;
-            position:relative;
-            padding: 10px 35px 10px 10px;
-            width: 100%;
-            box-sizing: border-box;         /* For IE and modern versions of Chrome */
-            -moz-box-sizing: border-box;    /* For Firefox                          */
-            -webkit-box-sizing: border-box; /* For Safari                           */
-            resize: vertical;
-          }
-          #output { margin: 0px 10px; position: relative;}
-          #mycanvas { margin: 10px; }
-          #closeConsoleButton { position: fixed; top: 20px; right: 30px; color: #ddd;}
-          canvas { border: 1px solid black; }
+  <style> 
+    * { box-sizing: border-box }
+    html, body { margin:0}
+    #inner {
+      height:100px;
+      background-color:#222;
+      color: #DDD;
+      font-family: monospace;
+      word-wrap:break-word;
+      overflow:auto;
+      margin: 10px auto;
+      position:relative;
+      padding: 10px 35px 10px 10px;
+      width: 100%;
+      resize: vertical;
+    }
+    #output { margin: 0px 10px; position: relative;}
+    #mycanvas { margin: 10px; }
+    canvas { border: 1px solid black; }
   </style>
 </head>
 `;
diff --git a/src/components/Sketches/containers/SketchesContainer.js b/src/components/Sketches/containers/SketchesContainer.js
index 71f5c9ba..1e3f7486 100644
--- a/src/components/Sketches/containers/SketchesContainer.js
+++ b/src/components/Sketches/containers/SketchesContainer.js
@@ -33,9 +33,6 @@ const mapDispatchToProps = dispatch => {
   };
 };
 
-const SketchesContainer = connect(
-  mapStateToProps,
-  mapDispatchToProps,
-)(Sketches);
+const SketchesContainer = connect(mapStateToProps, mapDispatchToProps)(Sketches);
 
 export default SketchesContainer;
diff --git a/src/components/TextEditor/components/TextEditor.js b/src/components/TextEditor/components/TextEditor.js
index 55270ab4..c8fce92a 100644
--- a/src/components/TextEditor/components/TextEditor.js
+++ b/src/components/TextEditor/components/TextEditor.js
@@ -96,6 +96,21 @@ class TextEditor extends React.Component {
     this.setState({ currentLine: line });
   };
 
+  /**
+   * returns a theme string for the CodeMirror editor, based off of the app's current theme
+   * @param {string} theme - the app's current theme
+   * @returns {string} the codemirror theme - see https://codemirror.net/demo/theme.html for more info
+   */
+
+  getCMTheme = theme => {
+    switch (theme) {
+      case "light":
+        return "duotone-light";
+      case "dark":
+      default:
+        return "material";
+    }
+  };
   renderDropdown = () => <DropdownButtonContainer />;
 
   renderBanner = () => {
@@ -131,7 +146,7 @@ class TextEditor extends React.Component {
     //json required by CodeMirror
     const options = {
       mode: CODEMIRROR_CONVERSIONS[this.props.language],
-      theme: "material", //requires lots of CSS tuning to get a theme to work, be wary of changing
+      theme: this.getCMTheme(this.props.theme),
       lineNumbers: true, //text editor has line numbers
       lineWrapping: true, //text editor does not overflow in the x direction, uses word wrap (NOTE: it's like MO Word wrapping, so words are not cut in the middle, if a word overlaps, the whole word is brought to the next line)
       indentWithTabs: true,
diff --git a/src/components/TextEditor/containers/TextEditorContainer.js b/src/components/TextEditor/containers/TextEditorContainer.js
index 8e9da71f..024e4b21 100644
--- a/src/components/TextEditor/containers/TextEditorContainer.js
+++ b/src/components/TextEditor/containers/TextEditorContainer.js
@@ -3,7 +3,7 @@ import TextEditor from "../components/TextEditor";
 import { connect } from "react-redux";
 import { setProgramCode, setProgramDirty } from "../../../actions/programsActions.js";
 
-const mapStateToProps = state => {
+const mapStateToProps = (state, ownProps) => {
   const { uid, mostRecentProgram } = state.userData;
 
   //program data should be an object representing the most recent program
@@ -14,6 +14,7 @@ const mapStateToProps = state => {
   return {
     ...programData,
     mostRecentProgram,
+    theme: ownProps.theme,
     uid,
   };
 };
@@ -29,9 +30,6 @@ const mapDispatchToProps = (dispatch, ownProps) => {
   };
 };
 
-const TextEditorContainer = connect(
-  mapStateToProps,
-  mapDispatchToProps,
-)(TextEditor);
+const TextEditorContainer = connect(mapStateToProps, mapDispatchToProps)(TextEditor);
 
 export default TextEditorContainer;
diff --git a/src/components/common/ProfilePanel.js b/src/components/common/ProfilePanel.js
index 843bd93a..a71895e5 100644
--- a/src/components/common/ProfilePanel.js
+++ b/src/components/common/ProfilePanel.js
@@ -15,9 +15,12 @@ import { faBook } from "@fortawesome/free-solid-svg-icons";
 import { faEdit } from "@fortawesome/free-solid-svg-icons";
 import { faPencilAlt } from "@fortawesome/free-solid-svg-icons";
 import { faSignOutAlt } from "@fortawesome/free-solid-svg-icons";
+import { faMoon } from "@fortawesome/free-solid-svg-icons";
+import { faSun } from "@fortawesome/free-solid-svg-icons";
 import { faTimes } from "@fortawesome/free-solid-svg-icons";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import "styles/Panel.scss";
+import Switch from "./Switch";
 import Footer from "./Footer";
 
 /**--------Props--------
@@ -248,7 +251,6 @@ class ProfilePanel extends React.Component {
 
   renderButtons = () => {
     let panelButtons = [];
-
     switch (this.props.contentType) {
       case "sketches":
         panelButtons.push(this.renderEditorButton());
@@ -265,6 +267,21 @@ class ProfilePanel extends React.Component {
     return <div className="panel-buttons">{panelButtons}</div>;
   };
 
+  renderThemeSwitch = () => {
+    let onToggle = on => {
+      this.props.onThemeChange(on ? "light" : "dark");
+    };
+
+    return (
+      <Switch
+        on={this.props.theme === "dark" ? true : false}
+        onToggle={onToggle}
+        onImg={<FontAwesomeIcon icon={faMoon} className="icon-dark" />}
+        offImg={<FontAwesomeIcon icon={faSun} className="icon-light" />}
+      />
+    );
+  };
+
   renderContent = () => (
     <div className="panel-content">
       {this.renderPanelImage()}
@@ -272,6 +289,7 @@ class ProfilePanel extends React.Component {
       {this.renderName()}
       {this.renderErrorMessage(this.state.displayNameMessage)}
       {this.renderButtons()}
+      {this.renderThemeSwitch()}
     </div>
   );
 
diff --git a/src/components/common/Switch.js b/src/components/common/Switch.js
new file mode 100644
index 00000000..b80d0fe2
--- /dev/null
+++ b/src/components/common/Switch.js
@@ -0,0 +1,49 @@
+import React from "react";
+
+import "styles/Switch.scss";
+
+/**
+ * Generic switch component. Props are as follows:
+ * @param {boolean} [on] - whether the switch is set to the "on" position or not; optional prop, defaults to false
+ * @param {function} onToggle - function to be called when switch is toggled. Has a single parameter (bool) that holds whether the switch is in the "on" position (true) or not (false)
+ * @param {JSX} onImg (optional): (JSX) element to be displayed on switch body when set to "on"
+ * @param {JSX} offImg (optional): (JSX) element to be displayed on switch body when set to "off"
+ */
+export default class Switch extends React.Component {
+  constructor(props) {
+    super(props);
+
+    this.state = {
+      on: !this.props.on ? false : this.props.on,
+    };
+  }
+
+  componentDidUpdate(prevProps) {
+    if (this.props.on !== prevProps.on) {
+      this.setState({
+        on: this.props.on,
+      });
+    }
+  }
+
+  onSwitchChange = () => {
+    this.props.onToggle(!this.state.on);
+    this.setState({
+      on: !this.state.on,
+    });
+  };
+
+  render() {
+    let switchedClass = this.state.on ? " switch-on" : "";
+
+    return (
+      <label className="switch">
+        <input className="switch-input" type="checkbox" onChange={this.onSwitchChange} />
+        <span className={"switch-body" + switchedClass}>
+          {this.state.on ? this.props.onImg : this.props.offImg}
+        </span>
+        <span className={"switch-handle" + switchedClass}></span>
+      </label>
+    );
+  }
+}
diff --git a/src/components/common/Switch.test.js b/src/components/common/Switch.test.js
new file mode 100644
index 00000000..4eb7e63a
--- /dev/null
+++ b/src/components/common/Switch.test.js
@@ -0,0 +1,51 @@
+import React from "react";
+import { shallow } from "enzyme";
+import Switch from "components/common/Switch.js";
+
+const switchOnClass = ".switch-body.switch-on"; // there has to be a better way of doing this
+const switchCheckboxClass = ".switch-input";
+
+describe("Switch", () => {
+  it("smoke test", () => {
+    const component = shallow(<Switch />);
+    expect(component.exists()).toBe(true);
+  });
+  it("handles on prop properly", () => {
+    const componentDefault = shallow(<Switch />);
+    expect(componentDefault.find(switchOnClass)).toHaveLength(0);
+
+    const componentFalse = shallow(<Switch on={false} />);
+    expect(componentFalse.find(switchOnClass)).toHaveLength(0);
+
+    const componentTrue = shallow(<Switch on={true} />);
+    expect(componentTrue.find(switchOnClass)).toHaveLength(1);
+  });
+  it("triggers the onToggle function when clicked", () => {
+    const clickFn = jest.fn(val => val);
+    const component = shallow(<Switch on={false} onToggle={clickFn} />);
+    expect(component.find(switchOnClass)).toHaveLength(0);
+
+    component.find(switchCheckboxClass).simulate("change", { target: { checked: true } });
+    expect(clickFn.mock.calls.length).toBe(1);
+    expect(clickFn.mock.calls[0][0]).toBe(true);
+
+    component.find(switchCheckboxClass).simulate("change", { target: { checked: false } });
+    expect(clickFn.mock.calls.length).toBe(2);
+    expect(clickFn.mock.calls[1][0]).toBe(false);
+  });
+  it("handles the onImg and offImg props properly", () => {
+    const clickFn = jest.fn(val => val);
+    const onText = <span id="on-test"></span>;
+    const offText = <span id="off-test"></span>;
+    const component = shallow(
+      <Switch on={false} onToggle={clickFn} onImg={onText} offImg={offText} />,
+    );
+
+    expect(component.find("#off-test")).toHaveLength(1);
+    expect(component.find("#on-test")).toHaveLength(0);
+
+    component.find(switchCheckboxClass).simulate("change", { target: { checked: true } });
+    expect(component.find("#off-test")).toHaveLength(0);
+    expect(component.find("#on-test")).toHaveLength(1);
+  });
+});
diff --git a/src/components/common/containers/DropdownButtonContainer.js b/src/components/common/containers/DropdownButtonContainer.js
index 920091b4..875a0e63 100644
--- a/src/components/common/containers/DropdownButtonContainer.js
+++ b/src/components/common/containers/DropdownButtonContainer.js
@@ -33,9 +33,6 @@ const mapDispatchToProps = dispatch => {
   };
 };
 
-const DropdownButtonContainer = connect(
-  mapStateToProps,
-  mapDispatchToProps,
-)(DropdownButton);
+const DropdownButtonContainer = connect(mapStateToProps, mapDispatchToProps)(DropdownButton);
 
 export default DropdownButtonContainer;
diff --git a/src/components/common/containers/ProfilePanelContainer.js b/src/components/common/containers/ProfilePanelContainer.js
index 962fb2e6..b40a62e5 100644
--- a/src/components/common/containers/ProfilePanelContainer.js
+++ b/src/components/common/containers/ProfilePanelContainer.js
@@ -11,11 +11,13 @@ const mapStateToProps = (state, ownProps) => {
     photoName: state.userData.photoName || DEFAULT_PHOTO_NAME,
     screenHeight: state.ui.screenHeight,
     left: state.ui.panelOpen ? OPEN_PANEL_LEFT : CLOSED_PANEL_LEFT,
+    theme: ownProps.theme,
   };
 };
 
 const mapDispatchToProps = (dispatch, ownProps) => {
   return {
+    onThemeChange: ownProps.onThemeChange,
     collectUserPhoto: () => {},
     setDisplayName: name => dispatch(setDisplayName(name)),
     setPhotoName: name => dispatch(setPhotoName(name)),
@@ -25,9 +27,6 @@ const mapDispatchToProps = (dispatch, ownProps) => {
   };
 };
 
-const ProfilePanelContainer = connect(
-  mapStateToProps,
-  mapDispatchToProps,
-)(ProfilePanel);
+const ProfilePanelContainer = connect(mapStateToProps, mapDispatchToProps)(ProfilePanel);
 
 export default ProfilePanelContainer;
diff --git a/src/components/containers/MainContainer.js b/src/components/containers/MainContainer.js
index c1047f14..1ad79f51 100644
--- a/src/components/containers/MainContainer.js
+++ b/src/components/containers/MainContainer.js
@@ -4,6 +4,7 @@ import { setOutput } from "../../actions/outputActions.js";
 import { setMostRecentProgram } from "../../actions/userDataActions.js";
 import { setProgramDirty } from "../../actions/programsActions.js";
 import { togglePanel } from "../../actions/uiActions.js";
+import { setTheme } from "../../actions/uiActions.js";
 import { CLOSED_PANEL_LEFT, OPEN_PANEL_LEFT, PANEL_SIZE } from "../../constants";
 
 const mapStateToProps = state => {
@@ -32,6 +33,7 @@ const mapStateToProps = state => {
     left: (state.ui.panelOpen ? OPEN_PANEL_LEFT : CLOSED_PANEL_LEFT) + PANEL_SIZE,
     name,
     language,
+    theme: state.ui.theme,
   };
 };
 
@@ -41,6 +43,7 @@ const mapDispatchToProps = dispatch => {
     runCode: (code, language) => dispatch(setOutput(code, language)),
     cleanCode: program => dispatch(setProgramDirty(program, false)),
     togglePanel: () => dispatch(togglePanel()),
+    setTheme: theme => dispatch(setTheme(theme)),
   };
 };
 
diff --git a/src/lib/cookies.js b/src/lib/cookies.js
new file mode 100644
index 00000000..a43cc03b
--- /dev/null
+++ b/src/lib/cookies.js
@@ -0,0 +1,77 @@
+/**
+ * an internal helper function to get a cookie from the document.
+ * almost word-for-word copied from https://www.w3schools.com/js/js_cookies.asp
+ * @param {string} cname - the key value for the requested cookie
+ * @return {string} the value of the requested key, "" for none set
+ */
+
+function getCookie(cname) {
+  let name = cname + "=";
+  let decodedCookie = decodeURIComponent(document.cookie);
+  let ca = decodedCookie.split(";");
+  for (let i = 0; i < ca.length; i++) {
+    let c = ca[i];
+    while (c.charAt(0) === " ") {
+      c = c.substring(1);
+    }
+    if (c.indexOf(name) === 0) {
+      return c.substring(name.length, c.length);
+    }
+  }
+  return "";
+}
+
+/**
+ * an internal helpder function to set a cookie in the document
+ * almost word-for-word copied from https://www.w3schools.com/js/js_cookies.asp
+ * @param {string} cname - the key value for the requested cookie
+ * @param {string} cvalue - the value to set the cookie to
+ * @param {number} exdays - the number of days until the cookie expires
+ */
+
+function setCookie(cname, cvalue, exdays) {
+  let d = new Date();
+  d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1000);
+  let expires = "expires=" + d.toUTCString();
+  document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
+}
+
+/**
+ * exported function that gets the current app theme from the theme cookie;
+ * if none is set, returns dark and sets the cookie to dark
+ * @returns {string} the current theme, or dark if the cookie doesn't exist
+ */
+
+export const getThemeFromCookie = () => {
+  let theme = getCookie("theme");
+  if (theme !== "") {
+    return theme;
+  } else {
+    setCookie("theme", "dark", 365);
+    return "dark";
+  }
+};
+
+/**
+ * exported function that sets the theme cookie to the input, with a one-year expiry date
+ * @param {string} theme theme string to store in the cookie
+ */
+
+export const setThemeCookie = theme => {
+  setCookie("theme", theme, 365);
+};
+
+/**
+ * exported function that toggles the theme cookie to flip-flop its value from light to dark
+ * if none is set, assumes that the user is currently in dark and switches to light
+ * @returns {string} returns the current value of the theme cookie
+ */
+
+export const toggleCookie = () => {
+  if (getThemeFromCookie() === "light") {
+    setCookie("theme", "dark", 365);
+    return "dark";
+  }
+  setCookie("theme", "light", 365);
+  return "light";
+};
diff --git a/src/lib/index.js b/src/lib/index.js
index 5e8c7be8..1b84bca2 100644
--- a/src/lib/index.js
+++ b/src/lib/index.js
@@ -1,7 +1,9 @@
-import * as fetch from './fetch.js'
-import * as helpers from './fetch.js'
+import * as cookies from "./cookies.js";
+import * as fetch from "./fetch.js";
+import * as helpers from "./fetch.js";
 
 export default {
+  ...cookies,
   ...fetch,
   ...helpers,
-}
\ No newline at end of file
+};
diff --git a/src/reducers/uiReducer.js b/src/reducers/uiReducer.js
index d139cf5b..848f06c7 100644
--- a/src/reducers/uiReducer.js
+++ b/src/reducers/uiReducer.js
@@ -1,9 +1,10 @@
-import { SCREEN_RESIZE, TOGGLE_PANEL, SET_PANEL } from "../actions/uiActions";
+import { SCREEN_RESIZE, TOGGLE_PANEL, SET_PANEL, SET_THEME } from "../actions/uiActions";
 
 const initialState = {
   screenWidth: typeof window === "object" ? window.innerWidth : null,
   screenHeight: typeof window === "object" ? window.innerHeight : null,
   panelOpen: false,
+  theme: "dark",
 };
 
 function uiReducer(state = initialState, action) {
@@ -14,6 +15,8 @@ function uiReducer(state = initialState, action) {
       return Object.assign({}, state, { panelOpen: !state.panelOpen });
     case SET_PANEL:
       return Object.assign({}, state, { panelOpen: action.value });
+    case SET_THEME:
+      return Object.assign({}, state, { theme: action.theme });
     default:
       return state;
   }
diff --git a/src/styles/CustomCM.scss b/src/styles/CustomCM.scss
index c3a0383a..63b05b5b 100644
--- a/src/styles/CustomCM.scss
+++ b/src/styles/CustomCM.scss
@@ -1,86 +1,113 @@
-@import 'styles/variables.scss';
+@import "styles/variables.scss";
 
 .CodeMirror-lines {
   cursor: default;
 }
 
-.cm-s-material.CodeMirror {
-  box-sizing: border-box;
-  max-width: inherit;
-  max-height: inherit;
-  min-height: inherit;
-  background-color: rgba(38, 50, 56, 0);
-  font-family: "Ubuntu Mono", monospace;
-  color: white;
-  font-size: 18px;
-  height: 100%;
-}
-
-.CodeMirror pre {
-  padding-left: 5px;
-  padding-right: 4px;
-}
-
-.cm-s-material .CodeMirror-gutters {
-  background-color: $theme-dark;
-}
-.cm-s-material .CodeMirror-guttermarker,
-.cm-s-material .CodeMirror-guttermarker-subtle,
-.cm-s-material .CodeMirror-linenumber {
-  color: white;
-}
-
-.cm-s-material .CodeMirror-guttermarker,
-.cm-s-material .CodeMirror-guttermarker-subtle,
-.cm-s-material .CodeMirror-linenumber {
-  text-align: left;
-  padding-left: 15px;
-}
-
 /* comments, e.g. // # /* */
-.cm-s-material .cm-comment {
-  color: white;
+.cm-comment {
+  @include themify($themes) {
+    color: themed("comment");
+  }
 }
 
 /* import for while in etc */
-.cm-s-material span.cm-keyword {
-  color: #deb0ff;
+span.cm-keyword {
+  @include themify($themes) {
+    color: themed("keyword");
+  }
 }
 
 /*e.g. var *blah*, blah will be colored */
-.cm-s-material span.cm-def {
-  color: #47e2d5;
+span.cm-def {
+  @include themify($themes) {
+    color: themed("def");
+  }
 }
 
 /*e.g. var *blah*, blah will be colored */
-.cm-s-material span.cm-variable {
-  color: #f5e83c;
+span.cm-variable {
+  @include themify($themes) {
+    color: themed("variable");
+  }
 }
 
 /*for python: print, range, all built-in functions */
-.cm-s-material .cm-builtin {
-  color: #ffb36f;
+.cm-builtin {
+  @include themify($themes) {
+    color: themed("builtin");
+  }
 }
 
 /* 1 2 -11 etc */
-.cm-s-material .cm-number {
-  color: #f69797;
+.cm-number {
+  @include themify($themes) {
+    color: themed("number");
+  }
 }
 
 /* + - > < etc */
-.cm-s-material .cm-operator {
-  color: rgba(233, 237, 237, 1);
+.cm-operator {
+  @include themify($themes) {
+    color: themed("operator");
+  }
+}
+.cm-property {
+  @include themify($themes) {
+    color: themed("property");
+  }
+}
+.CodeMirror {
+  box-sizing: border-box;
+  max-width: inherit;
+  max-height: inherit;
+  min-height: inherit;
+  font-family: "Ubuntu Mono", monospace;
+  font-size: 18px;
+  height: 100%;
+  @include themify($themes) {
+    background-color: themed("backgroundColor");
+    color: themed("color");
+  }
+}
+.CodeMirror .CodeMirror pre {
+  padding-left: 5px;
+  padding-right: 4px;
+}
+
+.CodeMirror-gutter {
+  @include themify($themes) {
+    background-color: themed("backgroundColor");
+    color: themed("color");
+  }
+  white-space: normal;
+  height: 100%;
+  display: inline-block;
+  vertical-align: top;
+  margin-bottom: -30px;
+  width: 41px;
 }
 
-/* e.g. turtle.*Blah*(), Blah will be colored */
-.cm-s-material .cm-property {
-  color: #80cbae;
+.CodeMirror-linenumber {
+  width: 23px;
+  color: #fff;
+  text-align: left;
+  padding-left: 15px;
+  @include themify($themes) {
+    color: themed("color");
+  }
 }
-.cm-s-material .CodeMirror-cursor {
-  border-left: 1px solid #f4ee94;
+
+// a strange CSS way to create our floating cursor. not great, not awful.
+
+.CodeMirror-cursor {
+  border-top: none !important;
+  border-bottom: none !important;
+  border-left: 1px solid #A38B41 !important;
+  border-right: 0.5px solid #A38B41 !important;
 }
 
-.cm-s-material .CodeMirror-cursor::after {
+.CodeMirror-cursor::after {
   position: absolute;
   margin-top: 26px;
   left: -10px;
@@ -96,11 +123,10 @@
 }
 
 /* required for safari because it places the ::after element differently than chrome/fiefox; taken from https://stackoverflow.com/questions/16348489/is-there-a-css-hack-for-safari-only-not-chrome */
-@media not all and (min-resolution:.001dpcm) { 
-  .cm-s-material .CodeMirror-cursor::after {
+@media not all and (min-resolution: 0.001dpcm) {
+  .CodeMirror-cursor::after {
     margin-top: 2px;
   }
-
 }
 .CodeMirror-scroll {
   overflow: hidden !important;
diff --git a/src/styles/Editor.scss b/src/styles/Editor.scss
index a1cbe6cb..3ffd875e 100644
--- a/src/styles/Editor.scss
+++ b/src/styles/Editor.scss
@@ -1,10 +1,11 @@
 @import "styles/variables.scss";
-
+.root {
+  transition: all 0.5s ease;
+}
 .editor {
   display: flex;
   flex-direction: row;
   align-items: flex-start;
-  background-color: $theme-dark;
   justify-content: flex-start;
   position: absolute;
   transition: all 0.5s ease;
@@ -18,7 +19,9 @@
   align-items: flex-start;
   max-height: 100vh;
   overflow-y: auto;
-  background-color: $theme-dark;
+  @include themify($themes) {
+    background-color: themed("backgroundColor");
+  }
 }
 
 .code-section-banner {
@@ -30,23 +33,21 @@
   justify-content: flex-start;
   width: 100%;
   height: 67px;
-  border-bottom: 1px white solid;
+  @include themify($themes) {
+    border-bottom: 1px solid themed("color");
+  }
 }
 
 .editor-expand-panel-arrow {
-  color: #dddcdf;
+  @include themify($themes) {
+    color: themed("color");
+  }
   padding: 1rem;
   cursor: pointer;
   flex: 0 1 auto;
   width: 46px;
 }
 
-.editor-expand-panel-arrow-hidden {
-  // padding: 1rem;
-  // flex: 0 1 auto;
-  // width: 46px;
-}
-
 .editor-language-dropdown {
   flex: 0 1 auto;
 }
@@ -60,7 +61,9 @@
 }
 
 .editor-output {
-  background-color: $theme-tertiary;
+  @include themify($themes) {
+    background-color: themed("outputBackground");
+  }
   height: 100%;
 }
 
@@ -72,37 +75,53 @@
   align-items: center;
   justify-content: flex-start;
   width: 100%;
-  border-bottom: 1px white solid;
+  @include themify($themes) {
+    border-bottom: 1px solid themed("color");
+  }
 }
 
 .editor-output-iframe {
   width: 100%;
   height: 100%;
-  background-color: white;
+  @include themify($themes) {
+    background-color: themed("outputBackground");
+  }
+  border: none;
   display: flex;
 }
 
 .text-editor-container {
   width: 100%;
-  margin-top: 10px;
+  padding-top: 10px;
   top: 61px;
   overflow: auto;
+  @include themify($themes) {
+    background-color: themed("backgroundColor");
+  }
 }
 
 .btn-language-dropdown {
+  transition: none;
   display: flex;
   align-items: center;
   font-size: 24px;
-  color: white;
+  @include themify($themes) {
+    color: themed("color");
+    background-color: themed("backgroundColor");
+  }
   border: 0px;
 }
 
 .btn-language-dropdown:hover {
-  color: white;
+  @include themify($themes) {
+    color: themed("color");
+  }
 }
 
 .selected-line {
-  background-color: rgba(255, 255, 255, 0.1);
+  @include themify($themes) {
+    background-color: themed("selectedLine");
+  }
 }
 
 .text-editor-container::-webkit-scrollbar {
@@ -122,4 +141,4 @@
 /* Handle on hover */
 .text-editor-container::-webkit-scrollbar-thumb:hover {
   background: #2f2d33;
-}
\ No newline at end of file
+}
diff --git a/src/styles/Footer.scss b/src/styles/Footer.scss
index 282e84bf..3593f613 100644
--- a/src/styles/Footer.scss
+++ b/src/styles/Footer.scss
@@ -6,9 +6,11 @@
     width: 100%;
     bottom: 0px;
     height: 8vh; /*calc used to do arithmetic*/
-    background-color: $theme-light;
     text-align: center;
     align-items: center;
+    @include themify($themes) {
+        background-color: themed("accentBackground");
+    }
 }
 
 .footer-image {
diff --git a/src/styles/Panel.scss b/src/styles/Panel.scss
index 766cf5f5..ff4c62e9 100644
--- a/src/styles/Panel.scss
+++ b/src/styles/Panel.scss
@@ -1,9 +1,8 @@
-@import 'styles/variables.scss';
+@import "styles/variables.scss";
 
 .panel {
   display: flex;
   position: absolute;
-  background-color: $theme-light;
   flex-direction: column;
   align-items: center;
   justify-content: flex-start;
@@ -11,10 +10,16 @@
   margin: 0;
   padding: 0;
   transition: left 0.5s ease;
+  @include themify($themes) {
+    color: themed("accentColor");
+    background-color: themed("accentBackground");
+  }
 }
 
 .panel-collapse-button {
-  color: $off-white;
+  @include themify($themes) {
+    color: themed("accentColor");
+  }
   padding-right: 15px;
   flex: 0 0 auto;
   font-size: 1.25rem;
@@ -74,10 +79,12 @@
   font-size: 1.25rem;
   border: none;
   background-color: transparent;
-  color: $off-white;
   position: absolute;
   right: -15px;
   bottom: -15px;
+  @include themify($themes) {
+    color: themed("accentColor");
+  }
 }
 
 .gallery {
@@ -124,7 +131,9 @@
 
 .edit-icon-image {
   font-size: 1.25rem;
-  color: $off-white;
+  @include themify($themes) {
+    color: themed("accentColor");
+  }
   position: absolute;
   border: none;
   right: -30px;
@@ -146,12 +155,14 @@
   padding: 20px 0 5px;
   text-align: center;
   font-size: 2rem;
-  font-weight: bold;
-  color: $off-white;
   word-wrap: break-word;
-  background-color: $theme-dark;
+  background: none;
   align-items: baseline;
   border: none;
+  @include themify($themes) {
+    color: themed("accentColor");
+    border-bottom: 1px solid themed("accentColor");
+  }
 }
 
 .profile-input-error {
@@ -169,7 +180,9 @@
   padding: 20px 0 20px;
   font-size: 2rem;
   word-wrap: break-word;
-  color: white;
+  @include themify($themes) {
+    color: themed("accentColor");
+  }
   position: relative;
   font-family: $font-family-heading;
 }
@@ -184,8 +197,6 @@
 }
 
 .panel-button {
-  background-color: $theme-dark;
-  border-color: $theme-dark;
   text-align: left;
   font-size: 2em !important; /* reactstrap also has a !important somewhere, so we need to do this */
   /* this next block of CSS properly aligns button content */
@@ -193,12 +204,20 @@
   flex-direction: row;
   justify-content: flex-start;
   align-items: center;
+  @include themify($themes) {
+    color: themed("color");
+    border-color: themed("backgroundColor");
+    background-color: themed("backgroundColor");
+  }
 }
 
-.panel-button:hover, .panel-button:active {
-  color: $theme-dark !important;
-  border-color: $off-white !important;
-  background-color: $off-white !important;
+.panel-button:hover,
+.panel-button:active {
+  @include themify($themes) {
+    color: themed("backgroundColor");
+    border-color: themed("color");
+    background-color: themed("color");
+  }
 }
 
 .panel-button-text {
@@ -225,3 +244,12 @@
 .panel-content::-webkit-scrollbar-thumb:hover {
   background: #fff;
 }
+
+// Theme Switch Images
+.icon-dark {
+  color: #ffffff;
+}
+
+.icon-light {
+  color: $theme-dark;
+}
\ No newline at end of file
diff --git a/src/styles/SketchBox.scss b/src/styles/SketchBox.scss
index 63862662..8fccc194 100644
--- a/src/styles/SketchBox.scss
+++ b/src/styles/SketchBox.scss
@@ -15,12 +15,16 @@
     justify-content: flex-start;
     align-items: center;
     width: 200px;
-    background-color: $off-white;
     border-radius: 5px;
     color: $theme-dark;
     margin: 0px 10px;
     cursor: pointer;
     flex: 0 0 auto;
+    @include themify($themes) {
+        color: themed("cardColor");
+        background-color: themed("cardBackground");
+        border: themed("cardBorder")
+    }
 }
 
 .sketch-box-body {
@@ -36,15 +40,19 @@
 }
 
 .sketch-divider {
-    border-top: 1px solid $theme-dark;
     width: 100%;
     margin: 0;
     padding: 0;
+    @include themify($themes){
+        border-top: 1px solid themed("cardDivider");
+    }
 }
 
 .sketch-button-divider {
     width: 1px;
-    border: 0.5px solid $theme-dark;
+    @include themify($themes){
+        border: 0.5px solid themed("cardDivider");
+    }
 }
 
 .sketch-name {
diff --git a/src/styles/Sketches.scss b/src/styles/Sketches.scss
index 408bf806..9551176a 100644
--- a/src/styles/Sketches.scss
+++ b/src/styles/Sketches.scss
@@ -3,7 +3,10 @@
 .sketches-container {
   flex-direction: column;
   align-items: flex-start;
-  background-color: $theme-dark;
+  @include themify($themes) {
+    background-color: themed("backgroundColor");
+    color: themed("color");
+  }
   justify-content: flex-start;
   position: absolute;
   transition: all 0.5s ease;
@@ -11,7 +14,6 @@
 }
 
 .sketches-header-text {
-  color: $off-white;
   font-weight: bold;
   padding: 1rem;
   cursor: pointer;
@@ -26,7 +28,9 @@
   flex-direction: row;
   justify-content: flex-start;
   align-items: center;
-  border-bottom: 2px solid $off-white;
+  @include themify($themes) {
+    border-bottom: 1px solid themed("color");
+  }
 }
 
 .sketches-grid {
@@ -51,8 +55,10 @@
 .no-sketches-container {
   margin-top: 3em;
   text-align: center;
-  color: $off-white;
   width: 100%;
   height: 100%;
   padding: 1em;
+  @include themify($themes) {
+    color: themed("color");
+  }
 }
\ No newline at end of file
diff --git a/src/styles/Switch.scss b/src/styles/Switch.scss
new file mode 100644
index 00000000..9372b2cb
--- /dev/null
+++ b/src/styles/Switch.scss
@@ -0,0 +1,57 @@
+@import "styles/variables.scss";
+
+$switch-transition-time: 0.3s;
+
+.switch {
+  padding: 0.5rem 1rem;
+  position: relative;
+  display: block;
+  width: 100px;
+  height: 30px;
+  border-radius: 6px;
+  cursor: pointer;
+  box-sizing: content-box;
+  font-family: inherit;
+}
+
+.switch-input {
+  padding: 0px;
+  position: absolute;
+  top: 0;
+  left: 0;
+  opacity: 0;
+  box-sizing: content-box;
+}
+
+.switch-body {
+  position: relative;
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+  padding: 10px;
+  height: inherit;
+  font-size: 1rem;
+  background: $white;
+  border-radius: inherit;
+  transition: all $switch-transition-time;
+  &.switch-on {
+    justify-content: flex-start !important;
+    background: $theme-dark;
+  }
+}
+
+.switch-handle {
+  position: absolute;
+  top: 8px;
+
+  width: 30px;
+  height: 30px;
+  border-radius: 6px;
+  box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.2);
+  background: $theme-dark;
+  transition: all $switch-transition-time;
+  &.switch-on {
+    margin-left: 70px;
+    background: white;
+  }
+}
diff --git a/src/styles/variables.scss b/src/styles/variables.scss
index fbeb1244..ec46d988 100644
--- a/src/styles/variables.scss
+++ b/src/styles/variables.scss
@@ -11,11 +11,94 @@ $gray-purple: #585166;
 $off-white: #dddcdf;
 $select-cyan: #83dadd;
 $error-red: #f34d4d;
+$teachla-green: #aed643;
+$light-lavender: #8485cc;
+$white: #fcfcfc;
 
 // "Theme" Colours - specifically designed to allow for hot-swapping later
-
 $theme-light: $light-purple;
 $theme-dark: $dark-purple;
 $theme-tertiary: $gray-purple;
 $theme-highlight: $select-cyan;
-$theme-error: $error-red;
\ No newline at end of file
+$theme-error: $error-red;
+
+//Theme variables
+/* To change colors according to theme:
+ex)
+    .class{ 
+        @include themify($themes) {
+            background-color: themed("backgroundColor");
+        }
+    }
+*/
+
+$themes: (
+  light: (
+    color: $theme-dark !important,
+    backgroundColor: $white !important,
+    outputBackground: #e6e6fa,
+    accentColor: $white,
+    accentBackground: $light-lavender,
+    /* Card variables */ cardColor: $theme-dark,
+    cardBackground: $white,
+    cardBorder: 1px solid $light-lavender,
+    cardDivider: $light-lavender,
+    /* these next colors are all for the editor - check out CustomCM.scss for more info! */ comment:
+      $theme-dark,
+    keyword: #9715f3,
+    def: #0052ff,
+    variable: #f07178,
+    builtin: #ff7800,
+    number: #fd1f44,
+    operator: $theme-dark,
+    property: #056d45,
+    cursor: #f4ee94,
+    selectedLine: $off-white,
+  ),
+  dark: (
+    color: white !important,
+    backgroundColor: $theme-dark !important,
+    outputBackground: $theme-tertiary,
+    accentColor: white,
+    accentBackground: $theme-light,
+    /* Card variables */ cardColor: $theme-dark,
+    cardBackground: $off-white,
+    cardBorder: none,
+    cardDivider: $theme-dark,
+    /* these next colors are all for the editor - check out CustomCM.scss for more info! */ comment:
+      white,
+    keyword: #deb0ff,
+    def: #47e2d5,
+    variable: #f5e83c,
+    builtin: #ffb36f,
+    number: #f69797,
+    operator: rgba(233, 237, 237, 1),
+    property: #80cbae,
+    cursor: #f4ee94,
+    selectedLine: rgba(255, 255, 255, 0.1),
+  ),
+);
+
+@mixin themify($themes: $themes) {
+  @each $theme, $map in $themes {
+    .theme-#{$theme} & {
+      $theme-map: () !global;
+      @each $key, $submap in $map {
+        $value: map-get(map-get($themes, $theme), "#{$key}");
+        $theme-map: map-merge(
+          $theme-map,
+          (
+            $key: $value,
+          )
+        ) !global;
+      }
+
+      @content;
+      $theme-map: null !global;
+    }
+  }
+}
+
+@function themed($key) {
+  @return map-get($theme-map, $key);
+}