diff --git a/.gitignore b/.gitignore
index 0691a5444..c12003316 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,4 @@ site
 static/styles/vendor.css
 static/styles/app.css
 static/styles/fonts.css
+static/styles/noscript.css
diff --git a/src/lib.rs b/src/lib.rs
index 6823f1193..38e0cfe05 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -88,6 +88,7 @@ impl<'a> Generator<'a> {
             self.render_blog(blog)?;
         }
         self.compile_sass("app")?;
+        self.compile_sass("noscript")?;
         self.compile_sass("fonts")?;
         self.concat_vendor_css(vec!["skeleton", "tachyons"])?;
         self.copy_static_files()?;
diff --git a/src/styles/app.scss b/src/styles/app.scss
index bd59032be..2d53046af 100644
--- a/src/styles/app.scss
+++ b/src/styles/app.scss
@@ -3,14 +3,66 @@
 $body-font: 'Fira Sans', Helvetica, Arial, sans-serif;
 $header-font: 'Alfa Slab One', serif;
 
-$gray: #2a3439;
-$red: #a72145;
-$green: #0b7261;
-$purple: #2e2459;
-$yellow: #ffc832;
+// Switching theme will only work if JavaScript is enabled as well
+
+// Default light theme
+:root, :root:not([data-theme]) {
+  --gray: #2a3439;
+  --red: #a72145;
+  --yellow: #ffc832;
+  --code-color: black;
+  --code-bg-color: rgba(42, 52, 57, 0.05);
+  --code-border-color: rgba(42, 52, 57, 0.25);
+  --blockquote-color: black;
+  --blockquote-bg-color: rgb(247, 249, 249);
+  --blockquote-left-border-color: rgb(195, 205, 210);
+  --body-background-color: white;
+  --body-foreground-color: white;
+  --body-color: rgb(34,34,34);
+  --div-brand-a-color: black;
+  --white-elem-color: black;
+  --white-a: #2a3439;
+  --nav-links-a: #2a3439;
+  --publish-date-author: #2a3439;
+  --section-header-h2-color: black;
+  --theme-icon: #43484d;
+  --theme-popup-border: #43484d;
+  --theme-popup-bg: white;
+  --theme-hover: #cacaca;
+  --theme-choice-color: black;
+  --rust-logo-filter: initial;
+}
+
+// Dark theme
+:root[data-theme='dark'] {
+  --gray: #2a3439;
+  --red: #a72145;
+  --yellow: #ffc832;
+  --code-color: white;
+  --code-bg-color: rgba(213, 203, 198, 0.05);
+  --code-border-color: rgba(213, 203, 198, 0.25);
+  --blockquote-color: rgb(195, 205, 210);
+  --blockquote-bg-color: rgba(213, 203, 198, 0.05);
+  --blockquote-left-border-color: rgb(195, 205, 210);
+  --body-background-color: #181a1b;
+  --body-foreground-color: #e8e6e3;
+  --body-color: white;
+  --div-brand-a-color: white;
+  --white-elem-color: white;
+  --white-elem-a: #d5cbc6;
+  --nav-links-a: #d5cbc6;
+  --publish-date-author: #d5cbc6;
+  --section-header-h2-color: white;
+  --theme-icon: #43484d;
+  --theme-popup-border: #43484d;
+  --theme-popup-bg: #141617;
+  --theme-hover: #474c51;
+  --theme-choice-color: #d5cbc6;
+  --rust-logo-filter: drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);
+}
 
 html {
-  font-size: 62.5%
+  font-size: 62.5%;
 }
 
 @media screen and (min-width: 30em) {
@@ -21,7 +73,8 @@ html {
 
 body {
   font-family: $body-font;
-  background-color: white;
+  background-color: var(--body-background-color);
+  color: var(--body-color);
 
   /* Ensure the footer is always at the bottom of the screen */
   min-height: 100vh;
@@ -35,8 +88,8 @@ body {
 blockquote {
     font-style: italic;
     position: relative;
-    background-color: lighten($gray, 78%);
-    border-left: 8px solid lighten($gray, 60%);
+    background-color: var(--blockquote-bg-color);
+    border-left: 8px solid var(--blockquote-left-border-color);
     border-radius: 5px;
     margin: 0;
     margin-bottom: 2rem;
@@ -80,6 +133,7 @@ section {
       margin: 0;
       padding: 0;
       letter-spacing: 1px;
+      color: var(--section-header-h2-color);
     }
   }
 }
@@ -110,7 +164,8 @@ ul.nav, ul.nav li {
 }
 
 .nav a {
-  color: $gray;
+  color: var(--gray);
+  color: var(--nav-links-a);
 }
 
 div.brand {
@@ -123,7 +178,7 @@ div.brand {
   margin-top: $v-top;
 
   & a {
-    color: black;
+    color: var(--div-brand-a-color);
     text-decoration: none;
   }
 
@@ -139,30 +194,30 @@ div.brand {
 }
 
 .white {
-   color: black;
+   color: var(--white-elem-color);
   .highlight {
-    background-color: $yellow;
+    background-color: var(--yellow);
   }
   a {
-    color: $gray;
+    color: var(--white-elem-a);
     text-decoration: underline;
   }
   .button.button-secondary {
-    background-color: $yellow;
-    border: 1px solid $yellow;
-    color: $gray;
+    background-color: var(--yellow);
+    color: var(--gray);
+    border: 1px solid var(--yellow);
     text-decoration: none;
     display: block;
     overflow: hidden;
     text-overflow: ellipsis;
     &:hover, &:focus {
-      border-color: $gray;
+      border-color: var(--gray);
     }
   }
   code {
-    color: black;
-    background-color: rgba($gray, 0.05);
-    border: 1px solid rgba($gray, 0.25);
+    color: var(--code-color);
+    background-color: var(--code-bg-color);
+    border: 1px solid var(--code-border-color);
   }
 
   a.anchor::before {
@@ -173,7 +228,7 @@ div.brand {
     margin-left: -1em;
     text-decoration: none;
     opacity: 0.7;
-    color: $gray;
+    color: var(--gray);
     font-weight: normal;
   }
   :hover > a.anchor::before {
@@ -194,18 +249,18 @@ ul {
 }
 
 .posts {
-  background-color: $gray;
-  color: white;
+  background-color: var(--gray);
+  color: var(--body-foreground-color);
   .highlight {
-    background-color: $red;
+    background-color: var(--red);
   }
   a {
-    color: white;
+    color: var(--body-foreground-color);
     text-decoration: underline;
   }
   .button.button-secondary {
-    background-color: $red;
-    border: 1px solid $red;
+    background-color: var(--red);
+    border: 1px solid var(--red);
     color: white;
     text-decoration: none;
     display: block;
@@ -230,7 +285,7 @@ table.post-list a:hover {
 }
 
 .publish-date-author {
-  color: $gray;
+  color: var(--publish-date-author);
   margin: -60px 0 60px 0;
 }
 
@@ -245,11 +300,11 @@ footer {
   }
 
   a {
-    color: $yellow;
+    color: var(--yellow);
     text-decoration: none;
 
     &:hover {
-      color: $yellow;
+      color: var(--yellow);
       text-decoration: underline;
     }
   }
@@ -324,3 +379,48 @@ header h1 {
   }
 }
 
+// Theme switch popup
+// theme selector visible only if JavaScript is available
+
+.theme-icon {
+  display: none;
+  line-height: 2em;
+  border: 2px solid var(--theme-icon);
+  border-radius: 5px;
+  padding: 0px 5px;
+  color: var(--theme-choice-color);
+  &:hover {
+    color: var(--theme-choice-color);
+  }
+}
+
+#theme-choice {
+  display: none;
+  border: 2px solid var(--theme-popup-border);
+  border-radius: 5px;
+  color: var(--theme-choice-color);
+  background: var(--theme-popup-bg);
+  position: absolute;
+  list-style: none;
+  font-weight: 400;
+  padding: 0px;
+  // counterbalance vendored skeleton.css
+  margin: 0.2em -0.5em;
+}
+
+.theme-item {
+  padding: 0px 5px;
+}
+
+#theme-choice.showThemeDropdown {
+  display: block;
+  z-index: 1;
+}
+
+li.theme-item:hover {
+  background-color: var(--theme-hover);
+}
+
+.rust-logo {
+    filter: var(--rust-logo-filter);
+}
diff --git a/src/styles/noscript.scss b/src/styles/noscript.scss
new file mode 100644
index 000000000..94a877e0c
--- /dev/null
+++ b/src/styles/noscript.scss
@@ -0,0 +1,55 @@
+// This stylesheet is used when the user agent has no JavaScript capabilities (or has it disabled)
+// Sets dark theme according to user agent preferences
+
+// Default light theme
+:root, :root:not([data-theme]) {
+  --gray: #2a3439;
+  --red: #a72145;
+  --yellow: #ffc832;
+  --code-color: black;
+  --code-bg-color: rgba(42, 52, 57, 0.05);
+  --code-border-color: rgba(42, 52, 57, 0.25);
+  --blockquote-color: black;
+  --blockquote-bg-color: rgb(247, 249, 249);
+  --blockquote-left-border-color: rgb(195, 205, 210);
+  --body-background-color: white;
+  --body-foreground-color: white;
+  --body-color: rgb(34,34,34);
+  --div-brand-a-color: black;
+  --white-elem-color: black;
+  --white-a: #2a3439;
+  --nav-links-a: #2a3439;
+  --publish-date-author: #2a3439;
+  --section-header-h2-color: black;
+  --rust-logo-filter: initial;
+}
+
+// Dark theme (probed from user prefs)
+@media (prefers-color-scheme: dark) {
+  :root, :root:not([data-theme]) {
+    --gray: #2a3439;
+    --red: #a72145;
+    --yellow: #ffc832;
+    --code-color: white;
+    --code-bg-color: rgba(213, 203, 198, 0.05);
+    --code-border-color: rgba(213, 203, 198, 0.25);
+    --blockquote-color: rgb(195, 205, 210);
+    --blockquote-bg-color: rgba(213, 203, 198, 0.05);
+    --blockquote-left-border-color: rgb(195, 205, 210);
+    --body-background-color: #181a1b;
+    --body-foreground-color: #e8e6e3;
+    --body-color: white;
+    --div-brand-a-color: white;
+    --white-elem-color: white;
+    --white-elem-a: #d5cbc6;
+    --nav-links-a: #d5cbc6;
+    --publish-date-author: #d5cbc6;
+    --section-header-h2-color: white;
+    --rust-logo-filter: drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);
+  }
+}
+
+// Don't show the theme selector button when JavaScript is disabled
+#theme-icon {
+  display: none !important;
+}
diff --git a/static/scripts/theme-switch.js b/static/scripts/theme-switch.js
new file mode 100644
index 000000000..804e72b58
--- /dev/null
+++ b/static/scripts/theme-switch.js
@@ -0,0 +1,63 @@
+"use strict";
+
+function changeThemeTo(val) {
+    document.documentElement.setAttribute("data-theme", val);
+    // save theme prefs in the browser
+    if (storageAvailable("localStorage")) {
+        localStorage.setItem("blog-rust-lang-org-theme", val);
+    }
+    // the theme dropdown will close itself when returning to the dropdown() caller
+}
+
+function dropdown () {
+    document.getElementById("theme-choice").classList.toggle("showThemeDropdown");
+}
+
+// courtesy MDN
+function storageAvailable(type) {
+    let storage;
+    try {
+        storage = window[type];
+        const x = "__storage_test__";
+        storage.setItem(x, x);
+        storage.removeItem(x);
+        return true;
+    } catch (e) {
+        return (
+            e instanceof DOMException &&
+                e.name === "QuotaExceededError" &&
+                // acknowledge QuotaExceededError only if there's something already stored
+            storage &&
+                storage.length !== 0
+        );
+    }
+}
+
+function handleBlur(event) {
+    const parent = document.getElementById("theme-choice");
+    if (!parent.contains(document.activeElement) &&
+        !parent.contains(event.relatedTarget) &&
+        parent.classList.contains("showThemeDropdown")
+       ) {
+        console.debug('Closing the dropdown');
+        parent.classList.remove("showThemeDropdown");
+    }
+}
+
+// close the theme dropdown if clicking somewhere else
+document.querySelector('.theme-icon').onblur = handleBlur;
+
+// Check for saved user preference on load, else check and save user agent prefs
+let savedTheme = null;
+if (storageAvailable("localStorage")) {
+    savedTheme = localStorage.getItem("blog-rust-lang-org-theme");
+}
+if (savedTheme) {
+    document.documentElement.setAttribute("data-theme", savedTheme);
+} else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
+    document.documentElement.setAttribute("data-theme", "dark");
+    localStorage.setItem("blog-rust-lang-org-theme", "dark");
+}
+
+// show the theme selector only if JavaScript is enabled/available
+document.querySelector('.theme-icon').style.display = 'block';
diff --git a/templates/footer.hbs b/templates/footer.hbs
index 30899b465..b089d6b1b 100644
--- a/templates/footer.hbs
+++ b/templates/footer.hbs
@@ -44,3 +44,4 @@
 
 <!-- scripts -->
 <script src="{{root}}scripts/highlight.js"></script>
+<script src="{{root}}scripts/theme-switch.js"></script>
diff --git a/templates/headers.hbs b/templates/headers.hbs
index 3e19aa182..fb4b824ae 100644
--- a/templates/headers.hbs
+++ b/templates/headers.hbs
@@ -19,6 +19,11 @@
 <link rel="stylesheet" href="{{root}}styles/app.css"/>
 <link rel="stylesheet" href="{{root}}styles/highlight.css"/>
 
+<!-- stylesheet for user agents without js -->
+<noscript>
+    <link rel="stylesheet" href="{{root}}styles/noscript.css">
+</noscript>
+
 <!-- favicon -->
 <link rel="apple-touch-icon" sizes="180x180" href="{{root}}images/apple-touch-icon.png">
 <link rel="icon" type="image/png" sizes="16x16" href="{{root}}images/favicon-16x16.png">
diff --git a/templates/nav.hbs b/templates/nav.hbs
index d63976122..36d1da5ed 100644
--- a/templates/nav.hbs
+++ b/templates/nav.hbs
@@ -1,7 +1,7 @@
 <nav class="flex flex-row justify-center justify-end-l items-center flex-wrap ph2 pl3-ns pr4-ns">
   <div class="brand flex-auto w-100 w-auto-l self-start tc tl-l">
     <a href="{{root}}{{blog.prefix}}">
-      <img class="v-mid ml0-l" alt="Rust Logo" src="{{root}}images/rust-logo-blk.svg">
+      <img class="v-mid ml0-l rust-logo" alt="Rust Logo" src="{{root}}images/rust-logo-blk.svg">
       <span class="dib ml1 ml0-l">{{blog.title}}</span>
     </a>
   </div>
@@ -13,5 +13,11 @@
     <li class="tc pv2 ph2 ph4-ns flex-20-s"><a href="https://www.rust-lang.org/tools">Tools</a></li>
     <li class="tc pv2 ph2 ph4-ns flex-20-s"><a href="https://www.rust-lang.org/governance">Governance</a></li>
     <li class="tc pv2 ph2 ph4-ns flex-20-s"><a href="https://www.rust-lang.org/community">Community</a></li>
+    <button class="theme-icon" onclick="dropdown();">🖌
+      <ul id="theme-choice">
+        <li class="theme-item" onclick="changeThemeTo('light');">Light</li>
+        <li class="theme-item" onclick="changeThemeTo('dark');">Dark</li>
+      </ul>
+    </button>
   </ul>
 </nav>