diff --git a/app/assets/javascripts/discourse-common/lib/icon-library.js.es6 b/app/assets/javascripts/discourse-common/lib/icon-library.js.es6
index 15001e4ca59..84242d03f65 100644
--- a/app/assets/javascripts/discourse-common/lib/icon-library.js.es6
+++ b/app/assets/javascripts/discourse-common/lib/icon-library.js.es6
@@ -70,12 +70,7 @@ export function registerIconRenderer(renderer) {
 
 // Support for font awesome icons
 function faClasses(icon, params) {
-  let classNames;
-  if (typeof icon.replacementId !== "undefined") {
-    classNames = `fa fa-${icon.replacementId} d-icon ${icon.id}`;
-  } else {
-    classNames = `fa fa-${icon.id} d-icon d-${icon.id}`;
-  }
+  let classNames = `fa fa-${icon.replacementId || icon.id} d-icon d-icon-${icon.id}`;
 
   if (params) {
     if (params.modifier) { classNames += " fa-" + params.modifier; }
diff --git a/app/assets/javascripts/discourse/templates/preferences/categories.hbs b/app/assets/javascripts/discourse/templates/preferences/categories.hbs
index 27a07fef118..d4f4bf67a7b 100644
--- a/app/assets/javascripts/discourse/templates/preferences/categories.hbs
+++ b/app/assets/javascripts/discourse/templates/preferences/categories.hbs
@@ -3,7 +3,7 @@
   <label class="control-label">{{i18n 'user.categories_settings'}}</label>
 
   <div class="controls category-controls">
-    <label>{{d-icon "d-watching" class="icon watching"}} {{i18n 'user.watched_categories'}}</label>
+    <label>{{d-icon "d-watching"}} {{i18n 'user.watched_categories'}}</label>
     {{category-selector categories=model.watchedCategories blacklist=selectedCategories}}
   </div>
   <div class="instructions">{{i18n 'user.watched_categories_instructions'}}</div>
@@ -12,7 +12,7 @@
   </div>
 
   <div class="controls category-controls">
-    <label>{{d-icon "d-tracking" class="icon tracking"}} {{i18n 'user.tracked_categories'}}</label>
+    <label>{{d-icon "d-tracking"}} {{i18n 'user.tracked_categories'}}</label>
     {{category-selector categories=model.trackedCategories blacklist=selectedCategories}}
   </div>
   <div class="instructions">{{i18n 'user.tracked_categories_instructions'}}</div>
@@ -21,13 +21,13 @@
   </div>
 
   <div class="controls category-controls">
-    <label>{{d-icon "d-watching-first" class="icon watching-first-post"}} {{i18n 'user.watched_first_post_categories'}}</label>
+    <label>{{d-icon "d-watching-first"}} {{i18n 'user.watched_first_post_categories'}}</label>
     {{category-selector categories=model.watchedFirstPostCategories}}
   </div>
   <div class="instructions">{{i18n 'user.watched_first_post_categories_instructions'}}</div>
 
   <div class="controls category-controls">
-    <label>{{d-icon "d-muted" class="icon muted"}} {{i18n 'user.muted_categories'}}</label>
+    <label>{{d-icon "d-muted"}} {{i18n 'user.muted_categories'}}</label>
     {{category-selector categories=model.mutedCategories blacklist=selectedCategories}}
   </div>
   <div class="instructions">{{i18n 'user.muted_categories_instructions'}}</div>
diff --git a/app/assets/javascripts/select-kit/components/notifications-button.js.es6 b/app/assets/javascripts/select-kit/components/notifications-button.js.es6
index 344ca204cee..eaa9c6780f3 100644
--- a/app/assets/javascripts/select-kit/components/notifications-button.js.es6
+++ b/app/assets/javascripts/select-kit/components/notifications-button.js.es6
@@ -20,8 +20,7 @@ export default DropdownSelectBoxComponent.extend({
   @computed("iconForSelectedDetails")
   headerIcon(iconForSelectedDetails) { return iconForSelectedDetails; },
 
-  @computed("selectedDetails.icon")
-  iconForSelectedDetails(icon) { return icon; },
+  iconForSelectedDetails: Ember.computed.alias("selectedDetails.icon"),
 
   computeHeaderContent() {
     let content = this.baseHeaderComputedContent();
diff --git a/app/assets/stylesheets/common/base/d-icon.scss b/app/assets/stylesheets/common/base/d-icon.scss
new file mode 100644
index 00000000000..bc7c9526260
--- /dev/null
+++ b/app/assets/stylesheets/common/base/d-icon.scss
@@ -0,0 +1,11 @@
+.d-icon.d-icon-d-regular,
+.d-icon.d-icon-d-muted,
+.d-icon.d-icon-d-watching-first,
+.d-icon.d-icon-d-watching-first-post {
+  color: dark-light-choose($primary-medium, $secondary-medium);
+}
+
+.d-icon.d-icon-d-tracking,
+.d-icon.d-icon-d-watching {
+  color: $tertiary;
+}
diff --git a/app/assets/stylesheets/common/select-kit/dropdown-select-box.scss b/app/assets/stylesheets/common/select-kit/dropdown-select-box.scss
index 97c0aae8c18..a79b5909c0e 100644
--- a/app/assets/stylesheets/common/select-kit/dropdown-select-box.scss
+++ b/app/assets/stylesheets/common/select-kit/dropdown-select-box.scss
@@ -6,20 +6,6 @@
     min-width: auto;
     border: none;
 
-
-    .d-icon {
-      color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%));
-    }
-
-    .d-regular, .d-muted, .d-watching-first-post {
-      color: dark-light-choose($primary-medium, $secondary-medium);
-    }
-
-    .d-tracking, .d-watching {
-      color: $tertiary;
-      font-weight: normal;
-    }
-
     &.is-expanded {
       .select-box-kit-collection,
       .select-box-kit-body,
diff --git a/plugins/discourse-details/test/javascripts/acceptance/details-button-test.js.es6 b/plugins/discourse-details/test/javascripts/acceptance/details-button-test.js.es6
index f4639ca26c8..b0f25283ec5 100644
--- a/plugins/discourse-details/test/javascripts/acceptance/details-button-test.js.es6
+++ b/plugins/discourse-details/test/javascripts/acceptance/details-button-test.js.es6
@@ -11,7 +11,7 @@ test('details button', (assert) => {
 
   click('#create-topic');
   click('button.options');
-  click('.popup-menu .d-caret-right');
+  click('.popup-menu .d-icon-caret-right');
 
   andThen(() => {
     assert.equal(
@@ -30,7 +30,7 @@ test('details button', (assert) => {
   });
 
   click('button.options');
-  click('.popup-menu .d-caret-right');
+  click('.popup-menu .d-icon-caret-right');
 
   andThen(() => {
     assert.equal(
@@ -53,7 +53,7 @@ test('details button', (assert) => {
   });
 
   click('button.options');
-  click('.popup-menu .d-caret-right');
+  click('.popup-menu .d-icon-caret-right');
 
   andThen(() => {
     assert.equal(
@@ -76,7 +76,7 @@ test('details button', (assert) => {
   });
 
   click('button.options');
-  click('.popup-menu .d-caret-right');
+  click('.popup-menu .d-icon-caret-right');
 
   andThen(() => {
     assert.equal(
@@ -105,7 +105,7 @@ test('details button surrounds all selected text in a single details block', (as
   });
 
   click('button.options');
-  click('.popup-menu .d-caret-right');
+  click('.popup-menu .d-icon-caret-right');
 
   andThen(() => {
     assert.equal(
diff --git a/plugins/poll/test/javascripts/widgets/discourse-poll-option-test.js.es6 b/plugins/poll/test/javascripts/widgets/discourse-poll-option-test.js.es6
index 32ab64e6a30..75035b6651c 100644
--- a/plugins/poll/test/javascripts/widgets/discourse-poll-option-test.js.es6
+++ b/plugins/poll/test/javascripts/widgets/discourse-poll-option-test.js.es6
@@ -14,7 +14,7 @@ widgetTest('single, not selected', {
   },
 
   test(assert) {
-    assert.ok(find('li .d-circle-o:eq(0)').length === 1);
+    assert.ok(find('li .d-icon-circle-o:eq(0)').length === 1);
   }
 });
 
@@ -27,7 +27,7 @@ widgetTest('single, selected', {
   },
 
   test(assert) {
-    assert.ok(find('li .d-dot-circle-o:eq(0)').length === 1);
+    assert.ok(find('li .d-icon-dot-circle-o:eq(0)').length === 1);
   }
 });
 
@@ -43,7 +43,7 @@ widgetTest('multi, not selected', {
   },
 
   test(assert) {
-    assert.ok(find('li .d-square-o:eq(0)').length === 1);
+    assert.ok(find('li .d-icon-square-o:eq(0)').length === 1);
   }
 });
 
@@ -59,6 +59,6 @@ widgetTest('multi, selected', {
   },
 
   test(assert) {
-    assert.ok(find('li .d-check-square-o:eq(0)').length === 1);
+    assert.ok(find('li .d-icon-check-square-o:eq(0)').length === 1);
   }
 });
diff --git a/test/javascripts/acceptance/composer-test.js.es6 b/test/javascripts/acceptance/composer-test.js.es6
index 97df4f971ce..48afc43867d 100644
--- a/test/javascripts/acceptance/composer-test.js.es6
+++ b/test/javascripts/acceptance/composer-test.js.es6
@@ -264,7 +264,7 @@ QUnit.test("Composer can toggle between reply and createTopic", assert => {
 
   click('.topic-post:eq(0) button.reply');
   click('button.options');
-  click('.popup-menu .d-eye-slash');
+  click('.popup-menu .d-icon-eye-slash');
   andThen(() => {
     assert.ok(
       find('.composer-fields .whisper').text().indexOf(I18n.t("composer.whisper")) > 0,
@@ -286,7 +286,7 @@ QUnit.test("Composer can toggle between reply and createTopic", assert => {
   });
 
   click('button.options');
-  click('.popup-menu .d-eye-slash');
+  click('.popup-menu .d-icon-eye-slash');
   andThen(() => {
     assert.ok(
       find('.composer-fields .whisper').text().indexOf(I18n.t("composer.unlist")) > 0,
diff --git a/test/javascripts/acceptance/topic-test.js.es6 b/test/javascripts/acceptance/topic-test.js.es6
index 6286bc55f83..499fe32022b 100644
--- a/test/javascripts/acceptance/topic-test.js.es6
+++ b/test/javascripts/acceptance/topic-test.js.es6
@@ -33,7 +33,7 @@ QUnit.test("Share Popup", assert => {
 QUnit.test("Showing and hiding the edit controls", assert => {
   visit("/t/internationalization-localization/280");
 
-  click('#topic-title .d-pencil');
+  click('#topic-title .d-icon-pencil');
 
   andThen(() => {
     assert.ok(exists('#edit-title'), 'it shows the editing controls');
@@ -50,7 +50,7 @@ QUnit.test("Showing and hiding the edit controls", assert => {
 QUnit.test("Updating the topic title and category", assert => {
   visit("/t/internationalization-localization/280");
 
-  click('#topic-title .d-pencil');
+  click('#topic-title .d-icon-pencil');
 
   fillIn('#edit-title', 'this is the new title');
 
@@ -165,7 +165,7 @@ QUnit.test("Visit topic routes", assert => {
 
 QUnit.test("Updating the topic title with emojis", assert => {
   visit("/t/internationalization-localization/280");
-  click('#topic-title .d-pencil');
+  click('#topic-title .d-icon-pencil');
 
   fillIn('#edit-title', 'emojis title :bike: :blonde_woman:t6:');
 
diff --git a/test/javascripts/components/categories-admin-dropdown-test.js.es6 b/test/javascripts/components/categories-admin-dropdown-test.js.es6
index 3c999f03e9d..d6c63197b68 100644
--- a/test/javascripts/components/categories-admin-dropdown-test.js.es6
+++ b/test/javascripts/components/categories-admin-dropdown-test.js.es6
@@ -7,8 +7,8 @@ componentTest('default', {
   test(assert) {
     const $selectKit = selectKit('.categories-admin-dropdown');
 
-    assert.equal($selectKit.el.find(".d-bars").length, 1);
-    assert.equal($selectKit.el.find(".d-caret-down").length, 1);
+    assert.equal($selectKit.el.find(".d-icon-bars").length, 1);
+    assert.equal($selectKit.el.find(".d-icon-caret-down").length, 1);
 
     expandSelectKit();
 
diff --git a/test/javascripts/components/d-button-test.js.es6 b/test/javascripts/components/d-button-test.js.es6
index e40d47fe851..d283da4f21c 100644
--- a/test/javascripts/components/d-button-test.js.es6
+++ b/test/javascripts/components/d-button-test.js.es6
@@ -6,7 +6,7 @@ componentTest('icon only button', {
 
   test(assert) {
     assert.ok(this.$('button.btn.btn-icon.no-text').length, 'it has all the classes');
-    assert.ok(this.$('button .d-icon.d-plus').length, 'it has the icon');
+    assert.ok(this.$('button .d-icon.d-icon-plus').length, 'it has the icon');
     assert.equal(this.$('button').attr('tabindex'), "3", 'it has the tabindex');
   }
 });
@@ -16,7 +16,7 @@ componentTest('icon and text button', {
 
   test(assert) {
     assert.ok(this.$('button.btn.btn-icon-text').length, 'it has all the classes');
-    assert.ok(this.$('button .d-icon.d-plus').length, 'it has the icon');
+    assert.ok(this.$('button .d-icon.d-icon-plus').length, 'it has the icon');
     assert.ok(this.$('button span.d-button-label').length, 'it has the label');
   }
 });
diff --git a/test/javascripts/components/d-icon-test.js.es6 b/test/javascripts/components/d-icon-test.js.es6
new file mode 100644
index 00000000000..d4f3d489aa5
--- /dev/null
+++ b/test/javascripts/components/d-icon-test.js.es6
@@ -0,0 +1,21 @@
+import componentTest from 'helpers/component-test';
+
+moduleForComponent('d-icon', {integration: true});
+
+componentTest('default', {
+  template: '{{d-icon "bars"}}',
+
+  test(assert) {
+    const html = this.$().html().trim();
+    assert.equal(html, '<i class="fa fa-bars d-icon d-icon-bars"></i>');
+  }
+});
+
+componentTest('with replacement', {
+  template: '{{d-icon "d-watching"}}',
+
+  test(assert) {
+    const html = this.$().html().trim();
+    assert.equal(html, '<i class="fa fa-exclamation-circle d-icon d-icon-d-watching"></i>');
+  }
+});
diff --git a/test/javascripts/widgets/actions-summary-test.js.es6 b/test/javascripts/widgets/actions-summary-test.js.es6
index 2115d79cf78..f4eb8b78821 100644
--- a/test/javascripts/widgets/actions-summary-test.js.es6
+++ b/test/javascripts/widgets/actions-summary-test.js.es6
@@ -74,7 +74,7 @@ widgetTest('post deleted', {
     });
   },
   test(assert) {
-    assert.ok(this.$('.post-action .d-trash-o').length === 1, 'it has the deleted icon');
+    assert.ok(this.$('.post-action .d-icon-trash-o').length === 1, 'it has the deleted icon');
     assert.ok(this.$('.avatar[title=eviltrout]').length === 1, 'it has the deleted by avatar');
   }
 });
diff --git a/test/javascripts/widgets/button-test.js.es6 b/test/javascripts/widgets/button-test.js.es6
index afe75224448..96bd4b245c7 100644
--- a/test/javascripts/widgets/button-test.js.es6
+++ b/test/javascripts/widgets/button-test.js.es6
@@ -11,7 +11,7 @@ widgetTest('icon only button', {
 
   test(assert) {
     assert.ok(this.$('button.btn.btn-icon.no-text').length, 'it has all the classes');
-    assert.ok(this.$('button .d-icon.d-smile-o').length, 'it has the icon');
+    assert.ok(this.$('button .d-icon.d-icon-smile-o').length, 'it has the icon');
   }
 });
 
@@ -24,7 +24,7 @@ widgetTest('icon and text button', {
 
   test(assert) {
     assert.ok(this.$('button.btn.btn-icon-text').length, 'it has all the classes');
-    assert.ok(this.$('button .d-icon.d-plus').length, 'it has the icon');
+    assert.ok(this.$('button .d-icon.d-icon-plus').length, 'it has the icon');
     assert.ok(this.$('button span.d-button-label').length, 'it has the label');
   }
 });
diff --git a/test/javascripts/widgets/home-logo-test.js.es6 b/test/javascripts/widgets/home-logo-test.js.es6
index 80bec5f4bef..96bd889f50f 100644
--- a/test/javascripts/widgets/home-logo-test.js.es6
+++ b/test/javascripts/widgets/home-logo-test.js.es6
@@ -66,7 +66,7 @@ widgetTest('no logo - minimized', {
   },
 
   test(assert) {
-    assert.ok(this.$('.d-home').length === 1);
+    assert.ok(this.$('.d-icon-home').length === 1);
   }
 });
 
diff --git a/test/javascripts/widgets/post-test.js.es6 b/test/javascripts/widgets/post-test.js.es6
index 89d72fdbcd8..0296c9dcf3d 100644
--- a/test/javascripts/widgets/post-test.js.es6
+++ b/test/javascripts/widgets/post-test.js.es6
@@ -436,7 +436,7 @@ widgetTest("reply directly above", {
     click('a.reply-to-tab');
     andThen(() => {
       assert.equal(this.$('section.embedded-posts.top .cooked').length, 1);
-      assert.equal(this.$('section.embedded-posts .d-arrow-up').length, 1);
+      assert.equal(this.$('section.embedded-posts .d-icon-arrow-up').length, 1);
     });
   }
 });
@@ -673,7 +673,7 @@ widgetTest("replies - one below, not suppressed", {
     click('button.show-replies');
     andThen(() => {
       assert.equal(this.$('section.embedded-posts.bottom .cooked').length, 1);
-      assert.equal(this.$('section.embedded-posts .d-arrow-down').length, 1);
+      assert.equal(this.$('section.embedded-posts .d-icon-arrow-down').length, 1);
     });
   }
 });
@@ -760,7 +760,7 @@ widgetTest("topic map - links", {
     click('nav.buttons button');
     andThen(() => {
       assert.equal(this.$('.map.map-collapsed').length, 0);
-      assert.equal(this.$('.topic-map .d-chevron-up').length, 1);
+      assert.equal(this.$('.topic-map .d-icon-chevron-up').length, 1);
       assert.equal(this.$('.topic-map-expanded').length, 1);
       assert.equal(this.$('.topic-map-expanded .topic-link').length, 5, 'it limits the links displayed');
     });
diff --git a/test/javascripts/widgets/poster-name-test.js.es6 b/test/javascripts/widgets/poster-name-test.js.es6
index f324e043254..c8ab8391d7a 100644
--- a/test/javascripts/widgets/poster-name-test.js.es6
+++ b/test/javascripts/widgets/poster-name-test.js.es6
@@ -38,7 +38,7 @@ widgetTest('extra classes and glyphs', {
     assert.ok(this.$('span.staff').length);
     assert.ok(this.$('span.admin').length);
     assert.ok(this.$('span.moderator').length);
-    assert.ok(this.$('.d-shield').length);
+    assert.ok(this.$('.d-icon-shield').length);
     assert.ok(this.$('span.new-user').length);
     assert.ok(this.$('span.fish').length);
   }