diff --git a/scss/_buttons.scss b/scss/_buttons.scss
index 265f35776dab44b8187b222a37582262c00bfec1..c30ce2185c0d43af3dec2cab31b39c0e73e9c423 100644
--- a/scss/_buttons.scss
+++ b/scss/_buttons.scss
@@ -37,16 +37,15 @@
     outline: 0;
     @include box-shadow(inset 0 3px 5px rgba(0,0,0,.125));
   }
+}
 
-  &.disabled,
-  &:disabled,
-  fieldset[disabled] & {
-    cursor: $cursor-disabled;
-    opacity: .65;
-    @include box-shadow(none);
-  }
+@include form-state-disabled('.btn') {
+  cursor: $cursor-disabled;
+  opacity: .65;
+  @include box-shadow(none);
 }
 
+
 // Future-proof disabling of clicks on `<a>` elements
 a.btn.disabled,
 fieldset[disabled] a.btn {
@@ -58,44 +57,20 @@ fieldset[disabled] a.btn {
 // Alternate buttons
 //
 
-.btn-primary {
-  @include button-variant($btn-primary-color, $btn-primary-bg, $btn-primary-border);
-}
-.btn-secondary {
-  @include button-variant($btn-secondary-color, $btn-secondary-bg, $btn-secondary-border);
-}
-.btn-info {
-  @include button-variant($btn-info-color, $btn-info-bg, $btn-info-border);
-}
-.btn-success {
-  @include button-variant($btn-success-color, $btn-success-bg, $btn-success-border);
-}
-.btn-warning {
-  @include button-variant($btn-warning-color, $btn-warning-bg, $btn-warning-border);
-}
-.btn-danger {
-  @include button-variant($btn-danger-color, $btn-danger-bg, $btn-danger-border);
-}
+@include button-variant('.btn-primary', $btn-primary-color, $btn-primary-bg, $btn-primary-border);
+@include button-variant('.btn-secondary', $btn-secondary-color, $btn-secondary-bg, $btn-secondary-border);
+@include button-variant('.btn-info', $btn-info-color, $btn-info-bg, $btn-info-border);
+@include button-variant('.btn-success', $btn-success-color, $btn-success-bg, $btn-success-border);
+@include button-variant('.btn-warning', $btn-warning-color, $btn-warning-bg, $btn-warning-border);
+@include button-variant('.btn-danger', $btn-danger-color, $btn-danger-bg, $btn-danger-border);
 
 // Remove all backgrounds
-.btn-primary-outline {
-  @include button-outline-variant($btn-primary-bg);
-}
-.btn-secondary-outline {
-  @include button-outline-variant($btn-secondary-border);
-}
-.btn-info-outline {
-  @include button-outline-variant($btn-info-bg);
-}
-.btn-success-outline {
-  @include button-outline-variant($btn-success-bg);
-}
-.btn-warning-outline {
-  @include button-outline-variant($btn-warning-bg);
-}
-.btn-danger-outline {
-  @include button-outline-variant($btn-danger-bg);
-}
+@include button-outline-variant('.btn-primary-outline', $btn-primary-bg);
+@include button-outline-variant('.btn-secondary-outline', $btn-secondary-border);
+@include button-outline-variant('.btn-info-outline', $btn-info-bg);
+@include button-outline-variant('.btn-success-outline', $btn-success-bg);
+@include button-outline-variant('.btn-warning-outline', $btn-warning-bg);
+@include button-outline-variant('.btn-danger-outline', $btn-danger-bg);
 
 
 //
@@ -110,9 +85,7 @@ fieldset[disabled] a.btn {
 
   &,
   &:active,
-  &.active,
-  &:disabled,
-  fieldset[disabled] & {
+  &.active {
     background-color: transparent;
     @include box-shadow(none);
   }
@@ -129,16 +102,14 @@ fieldset[disabled] a.btn {
     text-decoration: $link-hover-decoration;
     background-color: transparent;
   }
-  &:disabled,
-  fieldset[disabled] & {
-    @include hover-focus {
-      color: $btn-link-disabled-color;
-      text-decoration: none;
-    }
+}
+@include form-state-disabled('.btn-link') {
+  @include hover-focus {
+    color: $btn-link-disabled-color;
+    text-decoration: none;
   }
 }
 
-
 //
 // Button Sizes
 //
diff --git a/scss/_forms.scss b/scss/_forms.scss
index 72ec9b03f99800d33ba4469e1fef0c95f289701f..7922bf59097e5513c97a5fbca0bdf2e55be245ae 100644
--- a/scss/_forms.scss
+++ b/scss/_forms.scss
@@ -44,25 +44,22 @@
     opacity: 1;
   }
 
-  // Disabled and read-only inputs
-  //
-  // HTML5 says that controls under a fieldset > legend:first-child won't be
-  // disabled if the fieldset is disabled. Due to implementation difficulty, we
-  // don't honor that edge case; we style them as disabled anyway.
-  &:disabled,
-  &[readonly],
-  fieldset[disabled] & {
-    background-color: $input-bg-disabled;
-    // iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655.
-    opacity: 1;
-  }
+}
 
-  &[disabled],
-  fieldset[disabled] & {
-    cursor: $cursor-disabled;
-  }
+// Disabled and read-only inputs
+//
+// HTML5 says that controls under a fieldset > legend:first-child won't be
+// disabled if the fieldset is disabled. Due to implementation difficulty, we
+// don't honor that edge case; we style them as disabled anyway.
+@include form-state-disabled('.form-control', $modifiers: ':disabled, [readonly]') {
+  background-color: $input-bg-disabled;
+  // iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655.
+  opacity: 1;
 }
 
+@include form-state-disabled('.form-control') {
+  cursor: $cursor-disabled;
+}
 
 // Make file inputs better match text inputs by forcing them to new lines.
 .form-control-file,
@@ -230,30 +227,17 @@
 // Some special care is needed because <label>s don't inherit their parent's `cursor`.
 //
 // Note: Neither radios nor checkboxes can be readonly.
-input[type="radio"],
-input[type="checkbox"] {
-  &:disabled,
-  &.disabled,
-  fieldset[disabled] & {
-    cursor: $cursor-disabled;
-  }
+@include form-state-disabled('input[type="radio"], input[type="checkbox"]') {
+  cursor: $cursor-disabled;
 }
 // These classes are used directly on <label>s
-.radio-inline,
-.checkbox-inline {
-  &.disabled,
-  fieldset[disabled] & {
-    cursor: $cursor-disabled;
-  }
+@include form-state-disabled('.radio-inline, .checkbox-inline') {
+  cursor: $cursor-disabled;
 }
 // These classes are used on elements with <label> descendants
-.radio,
-.checkbox {
-  &.disabled,
-  fieldset[disabled] & {
-    label {
-      cursor: $cursor-disabled;
-    }
+@include form-state-disabled('.radio, .checkbox') {
+  label {
+    cursor: $cursor-disabled;
   }
 }
 
diff --git a/scss/_labels.scss b/scss/_labels.scss
index 403504b864f1e9d6b179ec645b9b7b1e3902bcb9..400b8b2a6c669b1d46b998a2babe192c3a325211 100644
--- a/scss/_labels.scss
+++ b/scss/_labels.scss
@@ -19,12 +19,12 @@
   &:empty {
     display: none;
   }
+}
 
-  // Quick fix for labels in buttons
-  .btn & {
-    position: relative;
-    top: -1px;
-  }
+// Quick fix for labels in buttons
+.btn .label {
+  position: relative;
+  top: -1px;
 }
 
 // Add hover effects, but only for links
diff --git a/scss/mixins/_buttons.scss b/scss/mixins/_buttons.scss
index c64f0e8f787bc9765873aca53a691e18c6ac0510..37bc8c0f7544d9cd520d361564d7088bc78ce320 100644
--- a/scss/mixins/_buttons.scss
+++ b/scss/mixins/_buttons.scss
@@ -3,86 +3,84 @@
 // Easily pump out default styles, as well as :hover, :focus, :active,
 // and disabled options for all buttons
 
-@mixin button-variant($color, $background, $border) {
-  $active-background: darken($background, 10%);
-  $active-border: darken($border, 12%);
+@mixin button-variant($selector, $color, $background, $border) {
+  #{$selector} {
+    $active-background: darken($background, 10%);
+    $active-border: darken($border, 12%);
 
-  color: $color;
-  background-color: $background;
-  border-color: $border;
-  @include box-shadow(inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075));
-
-  @include hover {
-    color: $color;
-    background-color: $active-background;
-        border-color: $active-border;
-  }
-
-  &:focus,
-  &.focus {
     color: $color;
-    background-color: $active-background;
-        border-color: $active-border;
-  }
+    background-color: $background;
+    border-color: $border;
+    @include box-shadow(inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075));
 
-  &:active,
-  &.active,
-  .open > &.dropdown-toggle {
-    color: $color;
-    background-color: $active-background;
-        border-color: $active-border;
-    // Remove the gradient for the pressed/active state
-    background-image: none;
-    @include box-shadow(inset 0 3px 5px rgba(0,0,0,.125));
+    @include hover {
+      color: $color;
+      background-color: $active-background;
+      border-color: $active-border;
+    }
 
-    &:hover,
     &:focus,
     &.focus {
       color: $color;
-      background-color: darken($background, 17%);
-          border-color: darken($border, 25%);
+      background-color: $active-background;
+      border-color: $active-border;
     }
-  }
 
-  &.disabled,
-  &:disabled,
-  fieldset[disabled] & {
+    &:active,
+    &.active,
+    .open > &.dropdown-toggle {
+      color: $color;
+      background-color: $active-background;
+      border-color: $active-border;
+      // Remove the gradient for the pressed/active state
+      background-image: none;
+      @include box-shadow(inset 0 3px 5px rgba(0, 0, 0, .125));
+
+      &:hover,
+      &:focus,
+      &.focus {
+        color: $color;
+        background-color: darken($background, 17%);
+        border-color: darken($border, 25%);
+      }
+    }
+  }
+  @include form-state-disabled($selector) {
     &:focus,
     &.focus {
       background-color: $background;
-          border-color: $border;
+      border-color: $border;
     }
     @include hover {
       background-color: $background;
-          border-color: $border;
+      border-color: $border;
     }
   }
 }
 
-@mixin button-outline-variant($color) {
-  color: $color;
-  background-image: none;
-  background-color: transparent;
-  border-color: $color;
+@mixin button-outline-variant($selector, $color) {
+  #{$selector} {
+    color: $color;
+    background-image: none;
+    background-color: transparent;
+    border-color: $color;
 
-  &:focus,
-  &.focus,
-  &:active,
-  &.active,
-  .open > &.dropdown-toggle {
-    color: #fff;
-    background-color: $color;
-        border-color: $color;
-  }
-  @include hover {
-    color: #fff;
-    background-color: $color;
-        border-color: $color;
+    &:focus,
+    &.focus,
+    &:active,
+    &.active,
+    .open > &.dropdown-toggle {
+      color: #fff;
+      background-color: $color;
+      border-color: $color;
+    }
+    @include hover {
+      color: #fff;
+      background-color: $color;
+      border-color: $color;
+    }
   }
-
-  &.disabled,
-  &:disabled,
-  fieldset[disabled] & {
+  @include form-state-disabled($selector) {
     &:focus,
     &.focus {
       border-color: lighten($color, 20%);
diff --git a/scss/mixins/_forms.scss b/scss/mixins/_forms.scss
index 764c5e08350d19275c46d720b8dd41ad952b0e62..3aace2f352ee0466acd01551da265e22fa5d5378 100644
--- a/scss/mixins/_forms.scss
+++ b/scss/mixins/_forms.scss
@@ -87,3 +87,31 @@
     height: auto;
   }
 }
+
+// Applies the content to the disabled state of the selector.
+//
+// For example:
+//
+//   @include form-state-disabled('.btn') {
+//     color: #111
+//   }
+//
+// Compiles to:
+//
+//   .btn.disabled,
+//   .btn:disabled,
+//   fieldset[disabled] .btn {
+//     color: #111;
+//   }
+//
+// This avoids issues with using the parent selector suffix, as in `fieldset[disabled] &`.
+// See https://github.com/sass/sass/issues/1817.
+@mixin form-state-disabled($selector, $modifiers: ':disabled, .disabled') {
+  #{selector-unify($selector, $modifiers), selector-nest('fieldset[disabled]', $selector)} {
+    @content;
+  }
+}
+
+@include form-state-disabled('button') {
+  color: red;
+}