Add CC dark theme option

This commit adds an option to enable a dark theme in the Community
Console. It can be enabled manually with a switch which is injected in
the CC or automatically by syncing it with the OS dark mode setting.

Change-Id: I7506e80a409c5b0190d942c9de88354b6c0cce10
diff --git a/src/_locales/ca/messages.json b/src/_locales/ca/messages.json
index 7a24358..d76a7cb 100644
--- a/src/_locales/ca/messages.json
+++ b/src/_locales/ca/messages.json
@@ -63,6 +63,18 @@
     "message": "Incrementa el contrast entre els fils llegits i no llegits a la Consola de la Comunitat.",
     "description": "Feature checkbox in the options page"
   },
+  "options_ccdarktheme": {
+    "message": "Activa el tema fosc a la Consola de la Comunitat, controlat per <span id='ccdarktheme_mode--container'></span>.",
+    "description": "Feature checkbox in the options page"
+  },
+  "options_ccdarktheme_mode_switch": {
+    "message": "un botó a la consola",
+    "description": "Select option added in #ccdarktheme_mode--container, in the options_ccdarktheme string"
+  },
+  "options_ccdarktheme_mode_system": {
+    "message": "la configuració del mode fosc del SO",
+    "description": "Select option added in #ccdarktheme_mode--container, in the options_ccdarktheme string"
+  },
   "options_profileindicator_header": {
     "message": "Punt indicador",
     "description": "Heading for the profile indicator feature options"
@@ -126,5 +138,9 @@
   "inject_profileindicatoralt_numposts": {
     "message": "Nombre de preguntes i respostes en els $1 darrers mesos.",
     "description": "Tooltip for the profile indicator dot."
+  },
+  "inject_ccdarkmode_helper": {
+    "message": "Canviar tema",
+    "description": "Tooltip for the dark mode switch."
   }
 }
diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json
index b031666..de6f351 100644
--- a/src/_locales/en/messages.json
+++ b/src/_locales/en/messages.json
@@ -67,6 +67,18 @@
     "message": "Increase contrast between read and unread threads in the Community Console.",
     "description": "Feature checkbox in the options page"
   },
+  "options_ccdarktheme": {
+    "message": "Enable the dark theme in the Community Console, controlled <span id='ccdarktheme_mode--container'></span>.",
+    "description": "Feature checkbox in the options page"
+  },
+  "options_ccdarktheme_mode_switch": {
+    "message": "with a switch in the CC",
+    "description": "Select option added in #ccdarktheme_mode--container, in the options_ccdarktheme string"
+  },
+  "options_ccdarktheme_mode_system": {
+    "message": "by the OS dark mode setting",
+    "description": "Select option added in #ccdarktheme_mode--container, in the options_ccdarktheme string"
+  },
   "options_profileindicator_header": {
     "message": "Indicator dot",
     "description": "Heading for the profile indicator feature options"
@@ -126,5 +138,9 @@
   "inject_profileindicatoralt_numposts": {
     "message": "Number of questions and replies in the last $1 months.",
     "description": "Tooltip for the profile indicator dot."
+  },
+  "inject_ccdarktheme_helper": {
+    "message": "Switch theme",
+    "description": "Tooltip for the dark mode switch."
   }
 }
diff --git a/src/_locales/es/messages.json b/src/_locales/es/messages.json
index d1103c1..bdb20af 100644
--- a/src/_locales/es/messages.json
+++ b/src/_locales/es/messages.json
@@ -63,6 +63,18 @@
     "message": "Incrementa el contraste entre los hilos leídos y no leídos en la Consola de la Comunidad.",
     "description": "Feature checkbox in the options page"
   },
+  "options_ccdarktheme": {
+    "message": "Activa el tema oscuro en la Consola de la Comunidad, controlado por <span id='ccdarktheme_mode--container'></span>.",
+    "description": "Feature checkbox in the options page"
+  },
+  "options_ccdarktheme_mode_switch": {
+    "message": "un botón en la consola",
+    "description": "Select option added in #ccdarktheme_mode--container, in the options_ccdarktheme string"
+  },
+  "options_ccdarktheme_mode_system": {
+    "message": "la configuración del modo oscuro del SO",
+    "description": "Select option added in #ccdarktheme_mode--container, in the options_ccdarktheme string"
+  },
   "options_profileindicator_header": {
     "message": "Punto indicador",
     "description": "Heading for the profile indicator feature options"
@@ -126,5 +138,9 @@
   "inject_profileindicatoralt_numposts": {
     "message": "Número de preguntas y respuestas en los últimos $1 meses.",
     "description": "Tooltip for the profile indicator dot."
+  },
+  "inject_ccdarktheme_helper": {
+    "message": "Cambiar tema",
+    "description": "Tooltip for the dark mode switch."
   }
 }
diff --git a/src/common/common.js b/src/common/common.js
index 8845b81..f1366b1 100644
--- a/src/common/common.js
+++ b/src/common/common.js
@@ -14,10 +14,15 @@
   'profileindicator': false,
   'profileindicatoralt': false,
   'profileindicatoralt_months': 12,
+  'ccdarktheme': false,
+  'ccdarktheme_mode': 'switch',
+  'ccdarktheme_switch_enabled': true,
 };
 
 const specialOptions = [
   'profileindicatoralt_months',
+  'ccdarktheme_mode',
+  'ccdarktheme_switch_enabled',
 ];
 
 const deprecatedOptions = [
diff --git a/src/common/content_scripts.js b/src/common/content_scripts.js
index 7186037..1bc9a7d 100644
--- a/src/common/content_scripts.js
+++ b/src/common/content_scripts.js
@@ -1,7 +1,8 @@
-function injectStylesheet(stylesheetName) {
+function injectStylesheet(stylesheetName, attributes = {}) {
   var link = document.createElement('link');
   link.setAttribute('rel', 'stylesheet');
   link.setAttribute('href', stylesheetName);
+  if ('media' in attributes) link.setAttribute('media', attributes['media']);
   document.head.appendChild(link);
 }
 
diff --git a/src/content_scripts/console_inject.js b/src/content_scripts/console_inject.js
index eb0975b..f25fe4b 100644
--- a/src/content_scripts/console_inject.js
+++ b/src/content_scripts/console_inject.js
@@ -100,15 +100,55 @@
 
   if (options.fixedtoolbar) {
     injectStyles(
-        'ec-bulk-actions{position: sticky; top: 0; background: white; z-index: 96;}');
+        'ec-bulk-actions{position: sticky; top: 0; background: var(--TWPT-primary-background, #fff); z-index: 96;}');
   }
 
   if (options.increasecontrast) {
-    injectStyles('.thread-summary.read{background: #ecedee!important;}');
+    injectStyles(
+        '.thread-summary.read{background: var(--TWPT-thread-read-background, #ecedee)!important;}');
   }
 
   if (options.stickysidebarheaders) {
     injectStyles(
-        'material-drawer .main-header{background: #fff; position: sticky; top: 0; z-index: 1;}');
+        'material-drawer .main-header{background: var(--TWPT-drawer-background, #fff)!important; position: sticky; top: 0; z-index: 1;}');
+  }
+
+  if (options.ccdarktheme && options.ccdarktheme_mode == 'switch') {
+    injectStylesheet(
+        chrome.runtime.getURL('injections/ccdarktheme_switch.css'));
+
+    var darkThemeSwitch = document.createElement('material-button');
+    darkThemeSwitch.classList.add('TWPT-dark-theme');
+    darkThemeSwitch.setAttribute('button', '');
+    darkThemeSwitch.setAttribute(
+        'title', chrome.i18n.getMessage('inject_ccdarktheme_helper'));
+
+    darkThemeSwitch.addEventListener('click', e => {
+      chrome.storage.sync.get(null, currentOptions => {
+        currentOptions.ccdarktheme_switch_status =
+            !options.ccdarktheme_switch_status;
+        chrome.storage.sync.set(currentOptions, _ => {
+          location.reload();
+        });
+      });
+    });
+
+    var switchContent = document.createElement('div');
+    switchContent.classList.add('content');
+
+    var icon = document.createElement('material-icon');
+
+    var i = document.createElement('i');
+    i.classList.add('material-icon-i', 'material-icons-extended');
+    i.textContent = 'brightness_4';
+
+    icon.appendChild(i);
+    switchContent.appendChild(icon);
+    darkThemeSwitch.appendChild(switchContent);
+
+    var rightControl = document.querySelector('header .right-control');
+    rightControl.style.width =
+        (parseInt(window.getComputedStyle(rightControl).width) + 58) + 'px';
+    rightControl.insertAdjacentElement('afterbegin', darkThemeSwitch);
   }
 });
diff --git a/src/content_scripts/console_inject_start.js b/src/content_scripts/console_inject_start.js
index 92ad72b..52482c5 100644
--- a/src/content_scripts/console_inject_start.js
+++ b/src/content_scripts/console_inject_start.js
@@ -10,4 +10,19 @@
     document.querySelector('html').setAttribute(
         'data-startup', JSON.stringify(startup));
   }
+
+  if (items.ccdarktheme) {
+    switch (items.ccdarktheme_mode) {
+      case 'switch':
+        if (items.ccdarktheme_switch_status == true)
+          injectStylesheet(chrome.runtime.getURL('injections/ccdarktheme.css'));
+        break;
+
+      case 'system':
+        injectStylesheet(chrome.runtime.getURL('injections/ccdarktheme.css'), {
+          'media': '(prefers-color-scheme: dark)',
+        });
+        break;
+    }
+  }
 });
diff --git a/src/injections/ccdarktheme.css b/src/injections/ccdarktheme.css
new file mode 100644
index 0000000..ddb5ea0
--- /dev/null
+++ b/src/injections/ccdarktheme.css
@@ -0,0 +1,563 @@
+:root {
+  --TWPT-primary-text: #e8eaed;
+  --TWPT-primary-text-alt: var(--TWPT-primary-text);
+  --TWPT-secondary-text: #9aa0a6;
+  --TWPT-primary-background: #202124;
+  --TWPT-secondary-background: #28292c;
+  --TWPT-active-background: #3c4043;
+  --TWPT-card-border: #5f6368;
+  --TWPT-subtle-border: #383735;
+  --TWPT-link: #8ab4f8;
+  --TWPT-thread-read-background: rgba(255, 255, 255, 0.08);
+  --TWPT-drawer-background: #2d2e30;
+}
+
+body {
+  color: var(--TWPT-primary-text);
+  background-color: var(--TWPT-primary-background)!important;
+}
+
+p {
+  color: var(--TWPT-primary-text);
+}
+
+body.ec a {
+  color: var(--TWPT-link);
+}
+
+/* Header */
+.material-content > header {
+  background-color: var(--TWPT-primary-background)!important;
+}
+
+.material-content > header material-button {
+  color: rgba(255, 255, 255, .87)!important;
+}
+
+.search-box {
+  background-color: #313235!important;
+}
+
+.search-box .clear-icon {
+  opacity: 0.8;
+  color: rgba(255, 255, 255, .87)!important;
+}
+
+.material-content > header .bell.mixin {
+  fill: rgba(255, 255, 255, .87)!important;
+}
+
+/* Drawer */
+material-drawer, material-drawer .panel, material-list-item {
+  background-color: var(--TWPT-drawer-background)!important;
+}
+
+material-list-item:hover, material-list-item:focus {
+  background-color: var(--TWPT-active-background)!important;
+}
+
+material-drawer .panel {
+  border-bottom-color: #25231f!important;
+}
+
+material-drawer .drawer-section-title, material-drawer .header > material-icon {
+  color: var(--TWPT-secondary-text)!important;
+}
+
+material-drawer material-list-item, material-drawer material-list-item .title {
+  color: #d2cecb!important;
+}
+
+material-drawer ec-forum-drawer-item material-checkbox material-icon {
+  filter: brightness(1.5);
+}
+
+/* Selector */
+.popup-wrapper, material-list {
+  background-color: var(--TWPT-drawer-background)!important;
+}
+
+material-list [group]:not(.empty) + *:not(script):not(template):not(.empty) {
+  box-shadow: inset 0 8px 0 0 var(--TWPT-drawer-background)!important;
+  border-top-color: #1f1f1f!important;
+}
+
+material-list material-select-item:hover,
+    material-list material-select-item:focus,
+    material-list material-select-dropdown-item:hover,
+    material-list material-select-dropdown-item:focus,
+    material-list material-select-dropdown-item.active,
+    material-list material-select-dropdown-item:not(.multiselect).selected {
+  background-color: rgba(255, 255, 255, .04)!important;
+}
+
+material-list .menu-item-label, material-list .label, material-list .text-segment {
+  color: rgba(255, 255, 255, .87)!important;
+}
+
+material-list [group] > [label] {
+  color: #8a8a8a!important;
+}
+
+/* Main */
+.main {
+  color: var(--TWPT-primary-text)!important;
+}
+
+main .title-bar {
+  border-bottom-color: var(--TWPT-subtle-border)!important;
+}
+
+main .title-bar .title, main .page-header h1, main .header h1 {
+  color: var(--TWPT-primary-text)!important;
+}
+
+.card {
+  background-color: var(--TWPT-secondary-background)!important;
+  color: var(--TWPT-primary-text)!important;
+  border-color: var(--TWPT-card-border)!important;
+}
+
+.card .card-title, .card, .card-section-title {
+  color: var(--TWPT-primary-text)!important;
+}
+
+.card .card-section-hint {
+  color: var(--TWPT-secondary-text)!important;
+}
+
+/* Action bar */
+.material-content .action-bar material-button,
+    ec-bulk-actions material-button,
+    ec-back-button material-button {
+  color: rgba(255, 255, 255, .54)!important;
+}
+
+.material-content .action-bar material-button.starred {
+  color: #fbbc04!important;
+}
+
+/* Thread list */
+ec-thread-list ul.thread-group {
+  background-color: var(--TWPT-primary-background)!important;
+}
+
+ec-thread-list ec-bulk-actions, ec-thread-list ec-thread-summary material-expansionpanel {
+  border-bottom-color: var(--TWPT-subtle-border)!important;
+}
+
+ec-thread-summary material-expansionpanel.read {
+  background-color: var(--TWPT-thread-read-background)!important;
+}
+
+ec-thread-summary material-expansionpanel.checked {
+  background-color: #17191c!important;
+}
+
+ec-thread-summary material-expansionpanel .title {
+  color: var(--TWPT-primary-text)!important;
+}
+
+ec-thread-summary material-expansionpanel ec-second-summary-line,
+    material-expansionpanel .header-content,
+    material-expansionpanel ec-thread-counts > span:not(.recommended-answers) {
+  color: #928e89!important;
+}
+
+ec-thread-summary material-expansionpanel ec-safe-html.body {
+  color: var(--TWPT-primary-text)!important;
+}
+
+/* Thread view */
+ec-question, .heading + .group, ec-message {
+  background-color: var(--TWPT-secondary-background)!important;
+}
+
+ec-question ec-review-bar {
+  background-color: var(--TWPT-active-background)!important;
+}
+
+ec-message-header .header, ec-question .state, ec-question ec-thread-counts > span, ec-message ec-thread-counts > span {
+  color: var(--TWPT-secondary-text)!important;
+}
+
+ec-question .alert {
+  background-color: var(--TWPT-active-background)!important;
+}
+
+ec-question .alert material-icon {
+  color: var(--TWPT-primary-text)!important;
+}
+
+ec-question .alert ec-icon {
+  color: var(--TWPT-primary-text)!important;
+  fill: var(--TWPT-primary-text)!important;
+}
+
+ec-question .title {
+  color: var(--TWPT-primary-text-alt)!important;
+}
+
+ec-message-header ec-avatar svg, ec-message-header .role {
+  filter: brightness(1.5);
+}
+
+ec-question .body, ec-message .body {
+  color: var(--TWPT-primary-text)!important;
+}
+
+ec-question .thread-insert {
+  background: none!important;
+}
+
+ec-question .details-heading {
+  color: var(--TWPT-primary-text-alt) !important;
+}
+
+ec-question .footer {
+  color: var(--TWPT-primary-text)!important;
+  background-color: var(--TWPT-active-background)!important;
+  border-top-color: var(--TWPT-card-border)!important;
+}
+
+.heading {
+  color: var(--TWPT-primary-text)!important;
+}
+
+.heading + .group,
+    .load-more-bar,
+    ec-message:not(:first-child),
+    .load-more-bar .load-more-button,
+    .load-more-bar .load-all-button {
+  border-color: var(--TWPT-card-border)!important;
+}
+
+ec-message .footer ec-relative-time,
+    ec-message .footer ec-safe-html {
+  color: var(--TWPT-secondary-text)!important;
+}
+
+ec-message .helpful-prompt {
+  color: var(--TWPT-primary-text)!important;
+}
+
+ec-question .me-too-button,
+    ec-question .subscribe-button,
+    ec-message .upvote-button,
+    ec-message .downvote-button {
+  color: var(--TWPT-secondary-text)!important;
+  background-color: #3c3e42!important;
+}
+
+ec-question .me-too-button.selected,
+    ec-question .subscribe-button.selected,
+    ec-message .upvote-button.selected,
+    ec-message .downvote-button.selected {
+  color: #4285f4!important;
+}
+
+.load-more-bar .load-more-button, .load-more-bar .load-all-button {
+  background-color: var(--TWPT-secondary-background)!important;
+}
+
+.locked-alert {
+  background-color: var(--TWPT-active-background)!important;
+  border: var(--TWPT-card-border)!important;
+}
+
+.locked-alert material-icon {
+  color: rgba(255, 255, 255, .38)!important;
+}
+
+ec-thread .finished-question {
+  background-color: var(--TWPT-active-background)!important;
+  border: var(--TWPT-card-border)!important;
+}
+
+.material-content .action-bar material-button.has-activity {
+  color: #1a73e8!important;
+}
+
+.material-content .action-bar material-button.showing-sidebar {
+  background-color: var(--TWPT-active-background)!important;
+}
+
+ec-activity-panel .title-bar h3 {
+  color: #e8eaf2!important;
+}
+
+ec-activity-panel ec-activity {
+  color: var(--TWPT-secondary-text)!important;
+  border-color: var(--TWPT-card-border)!important;
+}
+
+ec-activity-panel ec-activity .message {
+  color: var(--TWPT-primary-text-alt)!important;
+}
+
+ec-activity-panel ec-activity .thread-title {
+  color: #c3bfbc!important;
+}
+
+/* Recommended answers - show in green where we've overwritten the colors */
+.recommended-answers {
+  color: #34a853!important;
+}
+
+/* New thread view */
+material-stepper .stepper-step-name,
+    material-stepper .purpose-title {
+  color: var(--TWPT-primary-text)!important;
+}
+
+ec-ask-flow .display-name-label,
+    material-stepper .additional-details-label {
+  color: var(--TWPT-secondary-text)!important;
+}
+
+/* Profile view */
+ec-user .main-card-content {
+  background-color: var(--TWPT-secondary-background)!important;
+  border: 1px solid var(--TWPT-card-border)!important;
+  border-top-right-radius: 8px!important;
+  border-bottom-right-radius: 8px!important;
+}
+
+ec-user ec-display-name-editor {
+  color: var(--TWPT-primary-text)!important;
+}
+
+ec-user bar-chart .axis text {
+  fill: rgba(255, 255, 255, .54)!important;
+}
+
+ec-user bar-chart .axis path,
+    ec-user bar-chart .axis .gridline,
+    ec-user bar-chart .axis line {
+  stroke: rgba(255, 255, 255, .12)!important;
+}
+
+ec-user bar-chart .axis line.axis-zero-tick,
+    ec-user bar-chart .axis.x .tick-mark {
+  stroke: rgba(255, 255, 255, .38)!important;
+}
+
+ec-user bar-chart .aplos-legend-entry {
+  color: var(--TWPT-secondary-text)!important;
+}
+
+.aplos-hovercard {
+  background: var(--TWPT-secondary-background)!important;
+}
+
+.aplos-hovercard .title {
+  color: var(--TWPT-primary-text)!important;
+}
+
+.aplos-hovercard .subtitle,
+    .aplos-donut-center .subtitle,
+    .aplos-hovercard .series,
+    .aplos-donut-center .series,
+    .aplos-hovercard .value,
+    .aplos-donut-center .value {
+  color: var(--TWPT-secondary-text)!important;
+}
+
+/* Duplicate thread feature */
+.search-results ec-thread-option material-expansionpanel .panel {
+  background-color: var(--TWPT-primary-background)!important;
+}
+
+.search-results ec-thread-option material-expansionpanel.selected .panel,
+    .search-results ec-thread-option material-expansionpanel .panel > .main-header > .header.closed:hover,
+    .search-results ec-thread-option material-expansionpanel .panel > .main-header > .header.closed:focus {
+  background-color: #17191c!important;
+}
+
+/* Disabled buttons */
+material-button[disabled] {
+  color: rgba(255, 255, 255, .26)!important;
+}
+
+/* Material icons */
+ec-filter-drawer-item material-icon,
+    ec-filter-drawer-item ec-icon,
+    material-drawer .drawer-section material-icon,
+    material-drawer .drawer-section ec-icon,
+    material-list material-icon,
+    ec-query-builder material-icon,
+    material-radio .icon-container:not(.checked) material-icon,
+    ec-thread-summary material-expansionpanel .title material-icon,
+    .search-results ec-thread-option material-icon,
+    .search-results ec-thread-option ec-icon,
+    ec-rich-text-editor material-icon,
+    ec-editor-command material-icon,
+    ec-canned-responses ec-canned-response-row material-icon {
+  color: rgba(255, 255, 255, .87)!important;
+}
+
+material-drawer ec-icon,
+  .search-results ec-thread-option ec-icon {
+  fill: rgba(255, 255, 255, .87)!important;
+}
+
+/* Dialogs */
+material-dialog, material-dialog .dialog-header {
+  background-color: var(--TWPT-primary-background)!important;
+}
+
+ec-movable-dialog[showminimize] material-dialog .dialog-header {
+  background-color: #d2e3fc!important;
+}
+
+material-dialog .title {
+  color: var(--TWPT-primary-text-alt)!important;
+}
+
+ec-movable-dialog[showminimize] material-dialog .dialog-header .title, ec-movable-dialog[showminimize] material-dialog header material-icon {
+  color: var(--TWPT-primary-background)!important;
+}
+
+ec-movable-dialog[showminimize] material-dialog .header-notice, material-dialog .legal-prompt {
+  background-color: #170f01!important;
+}
+
+material-dialog .section-title,
+    material-dialog .select-label,
+    material-dialog .input-label,
+    material-dialog .btn-no,
+    ec-display-name-editor,
+    .forum-selection-label {
+  color: var(--TWPT-secondary-text)!important;
+}
+
+ec-movable-dialog[showminimize] material-dialog footer > [footer] > .footer > [footer] > simple-html {
+  color: var(--TWPT-secondary-text)!important;
+  background-color: var(--TWPT-active-background)!important;
+  border-top-color: #25231f!important;
+}
+
+/* Keyboard shortcuts dialog */
+material-dialog .main.with-scroll-strokes table td {
+  color: var(--TWPT-primary-text)!important;
+}
+
+/* Rich text editor */
+ec-editor-command material-button,
+    ec-formatting-popup material-button {
+  box-shadow: none!important;
+}
+
+ec-editor-command material-button.is-active {
+  background: var(--TWPT-active-background)!important;
+}
+
+ec-rich-text-editor .placeholder {
+  color: rgba(255, 255, 255, .38)!important;
+}
+
+ec-rich-text-editor .input {
+  color: var(--TWPT-primary-text)!important;
+}
+
+ec-rich-text-editor .hint {
+  color: rgba(255, 255, 255, .54)!important;
+}
+
+material-select-searchbox + material-list material-list-item {
+  color: rgba(255, 255, 255, .87)!important;
+}
+
+ec-attachment .filename {
+  color: var(--TWPT-primary-text)!important;
+}
+
+/* Thread insert */
+ec-thread-insert .title {
+  color: var(--TWPT-primary-text)!important;
+}
+
+ec-thread-insert ec-thread-counts,
+    ec-thread-insert .details,
+    ec-thread-insert ec-relative-time {
+  color: rgba(255, 255, 255, .54)!important;
+}
+
+/* Text input */
+material-input input {
+  color: var(--TWPT-primary-text)!important;
+}
+
+material-input .label-text,
+    material-input .hint-text,
+    material-input .counter {
+  color: rgba(255, 255, 255, .54)!important;
+}
+
+material-input .underline .unfocused-underline {
+  background-color: rgba(255, 255, 255, .12)!important;
+}
+
+label .label {
+  color: var(--TWPT-primary-text)!important;
+}
+
+/* Input underline */
+material-input .underline .unfocused-underline,
+    material-dropdown-select dropdown-button [buttondecorator] {
+  border-bottom-color: rgba(255, 255, 255, .12)!important;
+}
+
+/* Checkbox input */
+material-checkbox, material-checkbox .content {
+  color: var(--TWPT-secondary-text)!important;
+}
+
+/* Material menu button and dropdown select */
+material-menu material-button, material-dropdown-select dropdown-button {
+  color: rgba(255, 255, 255, .54)!important;
+}
+
+material-dropdown-select dropdown-button .is-disabled .button-text {
+  color: rgba(255, 255, 255, .38)!important;
+}
+
+/* Announcements content */
+ec-announcements-content .header .title, ec-announcements-content .announcement-title {
+  color: var(--TWPT-primary-text-alt)!important;
+}
+
+ec-announcements-content .no-announcements-message {
+  color: #c3bfbc!important;
+}
+
+/* Account selector */
+.popup-wrapper .profile .email {
+  color: rgba(255, 255, 255, .54)!important;
+}
+
+material-gaia-picker-footer {
+  color: rgba(255, 255, 255, .54)!important;
+  background-color: var(--TWPT-active-background)!important;
+}
+
+/* Canned responses */
+ec-canned-responses .filter-label,
+    ec-canned-responses ec-canned-response-row .name,
+    ec-canned-responses ec-canned-response-row .snippet {
+  color: var(--TWPT-primary-text)!important;
+}
+
+ec-canned-responses ec-canned-response-row .header.closed:hover,
+    ec-canned-responses ec-canned-response-row .header.closed:focus,
+    ec-canned-responses ec-canned-response-row .header.closed:hover .toolbar,
+    ec-canned-responses ec-canned-response-row .header.closed:focus .toolbar {
+  background-color: var(--TWPT-active-background)!important;
+}
+
+/* Reply button */
+material-fab.reply-button {
+  background-color: var(--TWPT-link)!important;
+  color: var(--TWPT-primary-background)!important;
+}
+
diff --git a/src/injections/ccdarktheme_switch.css b/src/injections/ccdarktheme_switch.css
new file mode 100644
index 0000000..c0d94f5
--- /dev/null
+++ b/src/injections/ccdarktheme_switch.css
@@ -0,0 +1,8 @@
+.TWPT-dark-theme {
+  padding: 4px 8px;
+  cursor: pointer;
+}
+
+.TWPT-dark-theme .content {
+  padding: 8px;
+}
diff --git a/src/options.html b/src/options.html
index 6daa9ca..8bb4fb2 100644
--- a/src/options.html
+++ b/src/options.html
@@ -20,6 +20,7 @@
         <input type="checkbox" id="loaddrafts"> <label for="loaddrafts" data-i18n="loaddrafts"></label> <span class="experimental-label" data-i18n="experimental_label"></span><br>
         <input type="checkbox" id="increasecontrast"> <label for="increasecontrast" data-i18n="increasecontrast"></label><br>
         <input type="checkbox" id="stickysidebarheaders"> <label for="stickysidebarheaders" data-i18n="stickysidebarheaders"></label><br>
+        <input type="checkbox" id="ccdarktheme"> <label for="ccdarktheme" data-i18n="ccdarktheme"></label> <span class="experimental-label" data-i18n="experimental_label"></span><br>
       </p>
       <h4 data-i18n="profileindicator_header"></h4>
       <p>
diff --git a/src/options.js b/src/options.js
index 47cecf6..4d37a19 100644
--- a/src/options.js
+++ b/src/options.js
@@ -24,6 +24,14 @@
           options[opt] = document.getElementById(opt).value || 12;
           break;
 
+        case 'ccdarktheme_mode':
+          options[opt] = document.getElementById(opt).value || 'switch';
+          break;
+
+        // This option is controlled directly in the Community Console.
+        case 'ccdarktheme_switch_enabled':
+          break;
+
         default:
           console.warn('Unrecognized option: ' + opt);
           break;
@@ -79,6 +87,28 @@
                 .appendChild(input);
             break;
 
+          case 'ccdarktheme_mode':
+            var select = document.createElement('select');
+            select.id = 'ccdarktheme_mode';
+
+            const modes = ['switch', 'system'];
+            for (const mode of modes) {
+              var modeOption = document.createElement('option');
+              modeOption.value = mode;
+              modeOption.textContent =
+                  chrome.i18n.getMessage('options_ccdarktheme_mode_' + mode);
+              if (items.ccdarktheme_mode == mode) modeOption.selected = true;
+              select.appendChild(modeOption);
+            }
+
+            document.getElementById('ccdarktheme_mode--container')
+                .appendChild(select);
+            break;
+
+          // This option is controlled directly in the Community Console.
+          case 'ccdarktheme_switch_enabled':
+            break;
+
           default:
             console.warn('Unrecognized option: ' + opt);
             break;
diff --git a/templates/manifest.gjson b/templates/manifest.gjson
index 5fd7fff..5905d74 100644
--- a/templates/manifest.gjson
+++ b/templates/manifest.gjson
@@ -17,7 +17,7 @@
     },
     {
       "matches": ["https://support.google.com/s/community*"],
-      "js": ["content_scripts/console_inject_start.js"],
+      "js": ["common/content_scripts.js", "content_scripts/console_inject_start.js"],
       "run_at": "document_start"
     },
     {
@@ -48,7 +48,9 @@
   ],
   "web_accessible_resources": [
     "injections/profileindicator_inject.js",
-    "injections/profileindicator_inject.css"
+    "injections/profileindicator_inject.css",
+    "injections/ccdarktheme.css",
+    "injections/ccdarktheme_switch.css"
   ],
   "browser_action": {},
 #if defined(CHROMIUM)