diff --git a/framework/core/js/src/admin/components/EditGroupModal.js b/framework/core/js/src/admin/components/EditGroupModal.js
index 70d3c65fd..129af73a9 100644
--- a/framework/core/js/src/admin/components/EditGroupModal.js
+++ b/framework/core/js/src/admin/components/EditGroupModal.js
@@ -32,7 +32,7 @@ export default class EditGroupModal extends Modal {
       this.color() || this.icon()
         ? Badge.component({
             icon: this.icon(),
-            style: { backgroundColor: this.color() },
+            color: this.color(),
           })
         : '',
       ' ',
diff --git a/framework/core/js/src/common/components/Badge.js b/framework/core/js/src/common/components/Badge.js
index 885f69d7f..39f63e16b 100644
--- a/framework/core/js/src/common/components/Badge.js
+++ b/framework/core/js/src/common/components/Badge.js
@@ -18,15 +18,18 @@ import classList from '../utils/classList';
  */
 export default class Badge extends Component {
   view() {
-    const { type, icon: iconName, label, ...attrs } = this.attrs;
+    const { type, icon: iconName, label, color, style = {}, ...attrs } = this.attrs;
 
     const className = classList('Badge', [type && `Badge--${type}`], attrs.className);
 
     const iconChild = iconName ? icon(iconName, { className: 'Badge-icon' }) : m.trust(' ');
 
+    const newStyle = { ...style, '--badge-bg': color };
+
     const badgeAttrs = {
       ...attrs,
       className,
+      style: newStyle,
     };
 
     const badgeNode = <div {...badgeAttrs}>{iconChild}</div>;
diff --git a/framework/core/js/src/common/components/GroupBadge.js b/framework/core/js/src/common/components/GroupBadge.js
index 07af8e223..5479f7db0 100644
--- a/framework/core/js/src/common/components/GroupBadge.js
+++ b/framework/core/js/src/common/components/GroupBadge.js
@@ -6,7 +6,7 @@ export default class GroupBadge extends Badge {
 
     if (attrs.group) {
       attrs.icon = attrs.group.icon();
-      attrs.style = { backgroundColor: attrs.group.color() };
+      attrs.color = attrs.group.color();
       attrs.label = typeof attrs.label === 'undefined' ? attrs.group.nameSingular() : attrs.label;
       attrs.type = 'group--' + attrs.group.id();
 
diff --git a/framework/core/js/src/common/helpers/avatar.tsx b/framework/core/js/src/common/helpers/avatar.tsx
index a60872b43..4262abc75 100644
--- a/framework/core/js/src/common/helpers/avatar.tsx
+++ b/framework/core/js/src/common/helpers/avatar.tsx
@@ -31,7 +31,7 @@ export default function avatar(user: User, attrs: Object = {}): Mithril.Vnode {
     }
 
     content = username.charAt(0).toUpperCase();
-    attrs.style = { background: user.color() };
+    attrs.style = { '--avatar-bg': user.color() };
   }
 
   return <span {...attrs}>{content}</span>;
diff --git a/framework/core/js/src/forum/components/UserCard.js b/framework/core/js/src/forum/components/UserCard.js
index 4b1003a60..e1c71de78 100644
--- a/framework/core/js/src/forum/components/UserCard.js
+++ b/framework/core/js/src/forum/components/UserCard.js
@@ -30,7 +30,7 @@ export default class UserCard extends Component {
     const badges = user.badges().toArray();
 
     return (
-      <div className={'UserCard ' + (this.attrs.className || '')} style={color ? { backgroundColor: color } : ''}>
+      <div className={'UserCard ' + (this.attrs.className || '')} style={color && { '--usercard-bg': color }}>
         <div className="darkenBackground">
           <div className="container">
             {controls.length
diff --git a/framework/core/less/admin/DashboardPage.less b/framework/core/less/admin/DashboardPage.less
index f8ca72e0a..5e7c771e7 100644
--- a/framework/core/less/admin/DashboardPage.less
+++ b/framework/core/less/admin/DashboardPage.less
@@ -13,7 +13,7 @@
   margin-bottom: 20px;
 
   .Button {
-    .Button--color(@control-color, @body-bg)
+    .Button--color(@control-color, @body-bg, 'button-alternate')
   }
 }
 
diff --git a/framework/core/less/common/Avatar.less b/framework/core/less/common/Avatar.less
index 5fe5054c8..5df0db6bf 100644
--- a/framework/core/less/common/Avatar.less
+++ b/framework/core/less/common/Avatar.less
@@ -4,7 +4,7 @@
   color: #fff;
   text-align: center;
   vertical-align: top;
-  background-color: @control-bg;
+  background-color: var(--avatar-bg);
   font-weight: normal;
   .Avatar--size(48px);
 
diff --git a/framework/core/less/common/Badge.less b/framework/core/less/common/Badge.less
index eb886a630..a43c97baa 100644
--- a/framework/core/less/common/Badge.less
+++ b/framework/core/less/common/Badge.less
@@ -1,7 +1,7 @@
 .Badge {
   .Badge--size(22px);
-  background: @muted-color;
-  color: #fff;
+  background: var(--badge-bg);
+  color: var(--badge-color);
   display: inline-block;
   vertical-align: middle;
   text-align: center;
diff --git a/framework/core/less/common/Button.less b/framework/core/less/common/Button.less
index 852d18ff8..2b6890e06 100644
--- a/framework/core/less/common/Button.less
+++ b/framework/core/less/common/Button.less
@@ -53,7 +53,7 @@
   padding: 8px 13px;
   border-radius: @border-radius;
   .user-select(none);
-  .Button--color(@control-color, @control-bg);
+  .Button--color(@control-color, @control-bg, 'button');
   border: 0;
 
   &:hover {
@@ -98,26 +98,28 @@
   }
 }
 
-.Button--color(@color; @background) {
-  color: @color;
-  background: @background;
+.Button--color(@color; @background; @name: 'button') {
+  color: var(~"--@{name}-color", ~"@{color}");
+  background: var(~"--@{name}-bg", ~"@{background}");
+  @bg-hover: darken(fadein(@background, 5%), 5%);
+  @bg-active: darken(fadein(@background, 10%), 10%);
 
   &:hover,
   &:focus,
   &.focus {
-    background-color: darken(fadein(@background, 5%), 5%);
+    background-color: var(~"--@{name}-bg-hover", ~"@{bg-hover}");
   }
 
   &:active,
   &.active,
   .open > .Dropdown-toggle& {
-    background-color: darken(fadein(@background, 10%), 10%);
+    background-color: var(~"--@{name}-bg-active", ~"@{bg-active}");
   }
 
   &.disabled,
   &[disabled],
   fieldset[disabled] & {
-    background: @background;
+    background: var(~"--@{name}-bg-disabled", ~"@{background}");
   }
 }
 
@@ -166,7 +168,7 @@
   }
 }
 .Button--primary {
-  .Button--color(@body-bg, @primary-color);
+  .Button--color(@body-bg, @primary-color, 'button-primary');
   font-weight: bold;
   padding-left: 20px;
   padding-right: 20px;
@@ -176,7 +178,7 @@
   }
 }
 .Button--danger {
-  .Button--color(@control-danger-color, @control-danger-bg);
+  .Button--color(@control-danger-color, @control-danger-bg, 'control-danger');
 }
 .Button--more {
   padding: 2px 4px;
diff --git a/framework/core/less/common/common.less b/framework/core/less/common/common.less
index 6b530e0c0..39b6bbb79 100644
--- a/framework/core/less/common/common.less
+++ b/framework/core/less/common/common.less
@@ -6,6 +6,7 @@
 
 @import "normalize";
 @import "print";
+@import "root";
 @import "scaffolding";
 @import "sideNav";
 
diff --git a/framework/core/less/common/root.less b/framework/core/less/common/root.less
new file mode 100644
index 000000000..3df86817d
--- /dev/null
+++ b/framework/core/less/common/root.less
@@ -0,0 +1,18 @@
+:root {
+  // Component colors
+  --avatar-bg: @control-bg;
+  --badge-bg: @muted-color;
+  --badge-color: #fff;
+  --usercard-bg: @control-bg;
+  --hero-bg: @hero-bg;
+  --hero-color: @hero-color;
+
+  // Store the current responsive screen mode in a CSS variable, to make it
+  // available to the JS code.
+  --flarum-screen: none;
+
+  @media @phone { --flarum-screen: phone; }
+  @media @tablet { --flarum-screen: tablet; }
+  @media @desktop { --flarum-screen: desktop; }
+  @media @desktop-hd { --flarum-screen: desktop-hd; }
+}
diff --git a/framework/core/less/common/scaffolding.less b/framework/core/less/common/scaffolding.less
index facfa6f43..87ae50792 100644
--- a/framework/core/less/common/scaffolding.less
+++ b/framework/core/less/common/scaffolding.less
@@ -1,14 +1,3 @@
-// Store the current responsive screen mode in a CSS variable, to make it
-// available to the JS code.
-:root {
-  --flarum-screen: none;
-
-  @media @phone { --flarum-screen: phone; }
-  @media @tablet { --flarum-screen: tablet; }
-  @media @desktop { --flarum-screen: desktop; }
-  @media @desktop-hd { --flarum-screen: desktop-hd; }
-}
-
 * {
   &,
   &:before,
diff --git a/framework/core/less/common/sideNav.less b/framework/core/less/common/sideNav.less
index dc33857e2..c37e92e0a 100644
--- a/framework/core/less/common/sideNav.less
+++ b/framework/core/less/common/sideNav.less
@@ -19,11 +19,11 @@
       & .Dropdown-menu {
         & > li > a {
           padding: 8px 0 8px 30px;
-          color: @muted-color;
+          color: var(--sidenav-color, @muted-color);
 
           &:hover {
             background: none;
-            color: @link-color;
+            color: var(--sidenav-color-hover, @link-color);
             text-decoration: none;
           }
 
@@ -35,7 +35,7 @@
         }
         & > li.active > a {
           background: none;
-          color: @primary-color;
+          color: var(--sidenav-color-active, @primary-color);
           font-weight: bold;
         }
         & > .Dropdown-separator {
diff --git a/framework/core/less/forum/Hero.less b/framework/core/less/forum/Hero.less
index 5ffaa4397..9f39aa1a0 100644
--- a/framework/core/less/forum/Hero.less
+++ b/framework/core/less/forum/Hero.less
@@ -1,8 +1,8 @@
 .Hero {
   margin-top: -1px;
-  background: @hero-bg;
+  background: var(--hero-bg);
   text-align: center;
-  color: @hero-color;
+  color: var(--hero-color);
 
   h2 {
     margin: 0;
diff --git a/framework/core/less/forum/Post.less b/framework/core/less/forum/Post.less
index 93bba10d8..f063cad04 100644
--- a/framework/core/less/forum/Post.less
+++ b/framework/core/less/forum/Post.less
@@ -201,7 +201,7 @@
     opacity: 0.5;
   }
   .Post-header .Button--more {
-    .Button--color(@muted-more-color, fade(@muted-more-color, 30%));
+    .Button--color(@muted-more-color, fade(@muted-more-color, 30%), 'muted-more');
   }
 }
 .Post--loading {
@@ -287,7 +287,7 @@
     font-size: 14px;
     margin-right: 5px;
   }
-  
+
   &:empty {
     display: none;
   }
diff --git a/framework/core/less/forum/UserCard.less b/framework/core/less/forum/UserCard.less
index c1e0d8c1b..29b4d4f0f 100644
--- a/framework/core/less/forum/UserCard.less
+++ b/framework/core/less/forum/UserCard.less
@@ -1,5 +1,5 @@
 .UserCard {
-  background: @control-bg;
+  background: var(--usercard-bg);
   .light-contents();
   background-size: 100% 100%;
 }