diff --git a/docs/components/forms.md b/docs/components/forms.md
index a4e0addc40da3c7899206eeaa29545f07750c617..5612afb688e4b531cd673e603a4720132b9be3b7 100644
--- a/docs/components/forms.md
+++ b/docs/components/forms.md
@@ -543,7 +543,7 @@ Block help text—for below inputs or for longer lines of help text—can be eas
 <label for="inputPassword5">Password</label>
 <input type="password" id="inputPassword5" class="form-control" aria-describedby="passwordHelpBlock">
 <p id="passwordHelpBlock" class="text-muted">
-  Your password must be 8-20 characters long, contain letters and numbers, and must not contain spaces, special characters or emoji. 
+  Your password must be 8-20 characters long, contain letters and numbers, and must not contain spaces, special characters or emoji.
 </p>
 {% endexample %}
 
@@ -607,33 +607,33 @@ Each checkbox and radio is wrapped in a `<label>` for three reasons:
 - It provides a helpful and semantic wrapper to help us replace the default `<input>`s.
 - It triggers the state of the `<input>` automatically, meaning no JavaScript is required.
 
-We hide the default `<input>` with `opacity` and use the `.c-indicator` to build a new custom form control. We can't build a custom one from just the `<input>` because CSS's `content` doesn't work on that element.
+We hide the default `<input>` with `opacity` and use the `.custom-control-indicator` to build a new custom form indicator in its place. Unfortunately we can't build a custom one from just the `<input>` because CSS's `content` doesn't work on that element.
 
-With the sibling selector (`~`), we use the `:checked` state to trigger a makeshift checked state on the custom control.
+We use the sibling selector (`~`) for all our `<input>` states—like `:checked`—to properly style our custom form indicator. When combined with the `.custom-control-description` class, we can also style the text for each item based on the `<input>`'s state.
 
 In the checked states, we use **base64 embedded SVG icons** from [Open Iconic](https://useiconic.com/open). This provides us the best control for styling and positioning across browsers and devices.
 
 #### Checkboxes
 
 {% example html %}
-<label class="c-input c-checkbox">
-  <input type="checkbox">
-  <span class="c-indicator"></span>
-  Check this custom checkbox
+<label class="custom-control custom-checkbox">
+  <input type="checkbox" class="custom-control-input">
+  <span class="custom-control-indicator"></span>
+  <span class="custom-control-description">Check this custom checkbox</span>
 </label>
 {% endexample %}
 
 Custom checkboxes can also utilize the `:indeterminate` pseudo class when manually set via JavaScript (there is no available HTML attribute for specifying it).
 
 <div class="bd-example bd-example-indeterminate">
-  <label class="c-input c-checkbox">
-    <input type="checkbox">
-    <span class="c-indicator"></span>
-    Check this custom checkbox
+  <label class="custom-control custom-checkbox">
+    <input type="checkbox" class="custom-control-input">
+    <span class="custom-control-indicator"></span>
+    <span class="custom-control-description">Check this custom checkbox</span>
   </label>
 </div>
 
- If you're using jQuery, something like this should suffice:
+If you're using jQuery, something like this should suffice:
 
 {% highlight js %}
 $('.your-checkbox').prop('indeterminate', true)
@@ -642,43 +642,62 @@ $('.your-checkbox').prop('indeterminate', true)
 #### Radios
 
 {% example html %}
-<label class="c-input c-radio">
-  <input id="radio1" name="radio" type="radio">
-  <span class="c-indicator"></span>
-  Toggle this custom radio
+<label class="custom-control custom-radio">
+  <input id="radio1" name="radio" type="radio" class="custom-control-input">
+  <span class="custom-control-indicator"></span>
+  <span class="custom-control-description">Toggle this custom radio</span>
 </label>
-<label class="c-input c-radio">
-  <input id="radio2" name="radio" type="radio">
-  <span class="c-indicator"></span>
-  Or toggle this other custom radio
+<label class="custom-control custom-radio">
+  <input id="radio2" name="radio" type="radio" class="custom-control-input">
+  <span class="custom-control-indicator"></span>
+  <span class="custom-control-description">Or toggle this other custom radio</span>
 </label>
 {% endexample %}
 
+#### Disabled
+
+Custom checkboxes and radios can also be disabled. Add the `disabled` boolean attribute to the `<input>` and the custom indicator and label description will be automatically styled.
+
+{% example html %}
+<label class="custom-control custom-checkbox">
+  <input type="checkbox" class="custom-control-input" disabled>
+  <span class="custom-control-indicator"></span>
+  <span class="custom-control-description">Check this custom checkbox</span>
+</label>
+
+<label class="custom-control custom-radio">
+  <input id="radio3" name="radioDisabled" type="radio" class="custom-control-input" disabled>
+  <span class="custom-control-indicator"></span>
+  <span class="custom-control-description">Toggle this custom radio</span>
+</label>
+{% endexample %}
+
+
 #### Stacked
 
-Custom checkboxes and radios are inline to start. Add a parent with class `.c-inputs-stacked` to ensure each form control is on separate lines.
+Custom checkboxes and radios are inline to start. Add a parent with class `.custom-controls-stacked` to ensure each form control is on separate lines.
 
 {% example html %}
-<div class="c-inputs-stacked">
-  <label class="c-input c-radio">
-    <input id="radioStacked1" name="radio-stacked" type="radio">
-    <span class="c-indicator"></span>
-    Toggle this custom radio
+<div class="custom-controls-stacked">
+  <label class="custom-control custom-radio">
+    <input id="radioStacked1" name="radio-stacked" type="radio" class="custom-control-input">
+    <span class="custom-control-indicator"></span>
+    <span class="custom-control-description">Toggle this custom radio</span>
   </label>
-  <label class="c-input c-radio">
-    <input id="radioStacked2" name="radio-stacked" type="radio">
-    <span class="c-indicator"></span>
-    Or toggle this other custom radio
+  <label class="custom-control custom-radio">
+    <input id="radioStacked2" name="radio-stacked" type="radio" class="custom-control-input">
+    <span class="custom-control-indicator"></span>
+    <span class="custom-control-description">Or toggle this other custom radio</span>
   </label>
 </div>
 {% endexample %}
 
 ### Select menu
 
-Custom `<select>` menus need only a custom class, `.c-select` to trigger the custom styles.
+Custom `<select>` menus need only a custom class, `.custom-select` to trigger the custom styles.
 
 {% example html %}
-<select class="c-select">
+<select class="custom-select">
   <option selected>Open this select menu</option>
   <option value="1">One</option>
   <option value="2">Two</option>
@@ -690,14 +709,16 @@ Custom selects degrade nicely in IE9, receiving only a handful of overrides to r
 
 ### File browser
 
+The file input is the most gnarly of the bunch and require additional JavaScript if you'd like to hook them up with functional *Choose file...* and selected file name text.
+
 {% example html %}
-<label class="file">
-  <input type="file" id="file">
-  <span class="file-custom"></span>
+<label class="custom-file">
+  <input type="file" id="file" class="custom-file-input">
+  <span class="custom-file-control"></span>
 </label>
 {% endexample %}
 
-The file input is the most gnarly of the bunch. Here's how it works:
+Here's how it works:
 
 - We wrap the `<input>` in a `<label>` so the custom control properly triggers the file browser.
 - We hide the default file `<input>` via `opacity`.
@@ -706,5 +727,3 @@ The file input is the most gnarly of the bunch. Here's how it works:
 - We declare a `height` on the `<input>` for proper spacing for surrounding content.
 
 In other words, it's an entirely custom element, all generated via CSS.
-
-**Heads up!** The custom file input is currently unable to update the *Choose file...* text with the filename. Without JavaScript, this might not be possible to change, but I'm open to ideas.
diff --git a/scss/_custom-forms.scss b/scss/_custom-forms.scss
index 4ffe79a6c395d2bb6e3b30101e805ab1e451c62a..974f76408813cba676f01d675fc09745e62213a1 100644
--- a/scss/_custom-forms.scss
+++ b/scss/_custom-forms.scss
@@ -1,3 +1,5 @@
+// scss-lint:disable PropertyCount
+
 // Embedded icons from Open Iconic.
 // Released under MIT and copyright 2014 Waybury.
 // http://useiconic.com/open
@@ -7,38 +9,49 @@
 //
 // Base class takes care of all the key behavioral aspects.
 
-.c-input {
+.custom-control {
   position: relative;
   display: inline;
   padding-left: 1.5rem;
   color: #555;
-  cursor: pointer;
 
-  > input {
-    position: absolute;
-    z-index: -1; // Put the input behind the label so it doesn't overlay text
-    opacity: 0;
+  + .custom-control {
+    margin-left: 1rem;
+  }
+}
 
-    &:checked ~ .c-indicator {
-      color: #fff;
-      background-color: #0074d9;
-      @include box-shadow(none);
-    }
+.custom-control-input {
+  position: absolute;
+  z-index: -1; // Put the input behind the label so it doesn't overlay text
+  opacity: 0;
 
-    &:focus ~ .c-indicator {
-      // the mixin is not used here to make sure there is feedback
-      box-shadow: 0 0 0 .075rem #fff, 0 0 0 .2rem #0074d9;
-    }
+  &:checked ~ .custom-control-indicator {
+    color: #fff;
+    background-color: #0074d9;
+    @include box-shadow(none);
+  }
 
-    &:active ~ .c-indicator {
-      color: #fff;
-      background-color: #84c6ff;
-      @include box-shadow(none);
-    }
+  &:focus ~ .custom-control-indicator {
+    // the mixin is not used here to make sure there is feedback
+    box-shadow: 0 0 0 .075rem #fff, 0 0 0 .2rem #0074d9;
   }
 
-  + .c-input {
-    margin-left: 1rem;
+  &:active ~ .custom-control-indicator {
+    color: #fff;
+    background-color: #84c6ff;
+    @include box-shadow(none);
+  }
+
+  &:disabled {
+    ~ .custom-control-indicator {
+      cursor: not-allowed;
+      background-color: $custom-form-bg-color-disabled;
+    }
+
+    ~ .custom-control-description {
+      color: $custom-form-description-color-disabled;
+      cursor: not-allowed;
+    }
   }
 }
 
@@ -46,9 +59,9 @@
 //
 // Generates a shadow element to create our makeshift checkbox/radio background.
 
-.c-indicator {
+.custom-control-indicator {
   position: absolute;
-  top: 0;
+  top: .0625rem;
   left: 0;
   display: block;
   width: 1rem;
@@ -58,27 +71,27 @@
   color: #eee;
   text-align: center;
   user-select: none;
-  background-color: #eee;
+  background-color: $custom-form-bg-color;
   background-repeat: no-repeat;
   background-position: center center;
   background-size: 50% 50%;
-  @include box-shadow(inset 0 .125rem .125rem rgba(0,0,0,.1));
+  @include box-shadow(inset 0 .25rem .25rem rgba(0,0,0,.1));
 }
 
 // Checkboxes
 //
 // Tweak just a few things for checkboxes.
 
-.c-checkbox {
-  .c-indicator {
+.custom-checkbox {
+  .custom-control-indicator {
     border-radius: .25rem;
   }
 
-  input:checked ~ .c-indicator {
+  .custom-control-input:checked ~ .custom-control-indicator {
     background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNy4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSIwIDAgOCA4IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA4IDgiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPHBhdGggZmlsbD0iI0ZGRkZGRiIgZD0iTTYuNCwxTDUuNywxLjdMMi45LDQuNUwyLjEsMy43TDEuNCwzTDAsNC40bDAuNywwLjdsMS41LDEuNWwwLjcsMC43bDAuNy0wLjdsMy41LTMuNWwwLjctMC43TDYuNCwxTDYuNCwxeiINCgkvPg0KPC9zdmc+DQo=);
   }
 
-  input:indeterminate ~ .c-indicator {
+  .custom-control-input:indeterminate ~ .custom-control-indicator {
     background-color: #0074d9;
     background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNy4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iOHB4IiBoZWlnaHQ9IjhweCIgdmlld0JveD0iMCAwIDggOCIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgOCA4IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIGZpbGw9IiNGRkZGRkYiIGQ9Ik0wLDN2Mmg4VjNIMHoiLz4NCjwvc3ZnPg0K);
     @include box-shadow(none);
@@ -89,12 +102,12 @@
 //
 // Tweak just a few things for radios.
 
-.c-radio {
-  .c-indicator {
+.custom-radio {
+  .custom-control-indicator {
     border-radius: 50%;
   }
 
-  input:checked ~ .c-indicator {
+  .custom-control-input:checked ~ .custom-control-indicator {
     background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNy4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSIwIDAgOCA4IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA4IDgiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPHBhdGggZmlsbD0iI0ZGRkZGRiIgZD0iTTQsMUMyLjMsMSwxLDIuMywxLDRzMS4zLDMsMywzczMtMS4zLDMtM1M1LjcsMSw0LDF6Ii8+DQo8L3N2Zz4NCg==);
   }
 }
@@ -105,8 +118,8 @@
 // By default radios and checkboxes are `inline-block` with no additional spacing
 // set. Use these optional classes to tweak the layout.
 
-.c-inputs-stacked {
-  .c-input {
+.custom-controls-stacked {
+  .custom-control {
     display: inline;
 
     &::after {
@@ -115,7 +128,7 @@
       content: "";
     }
 
-    + .c-input {
+    + .custom-control {
       margin-left: 0;
     }
   }
@@ -129,7 +142,7 @@
 //
 // Includes IE9-specific hacks (noted by ` \9`).
 
-.c-select {
+.custom-select {
   display: inline-block;
   max-width: 100%;
   padding: .375rem 1.75rem .375rem .75rem;
@@ -140,6 +153,7 @@
   background-image: none \9;
   background-size: 8px 10px;
   border: $input-btn-border-width solid $input-border-color;
+  @include border-radius($border-radius);
   // Use vendor prefixes as `appearance` isn't part of the CSS spec.
   -moz-appearance: none;
   -webkit-appearance: none;
@@ -156,7 +170,7 @@
   }
 }
 
-.c-select-sm {
+.custom-select-sm {
   padding-top: 3px;
   padding-bottom: 3px;
   font-size: 12px;
@@ -172,21 +186,27 @@
 //
 // Custom file input.
 
-.file {
+.custom-file {
   position: relative;
   display: inline-block;
   max-width: 100%;
   height: 2.5rem;
   cursor: pointer;
 }
-.file input {
+
+.custom-file-input {
   min-width: 14rem;
   max-width: 100%;
   margin: 0;
   filter: alpha(opacity = 0);
   opacity: 0;
+
+  &:focus ~ .custom-file-control {
+    @include box-shadow(0 0 0 .075rem #fff, 0 0 0 .2rem #0074d9);
+  }
 }
-.file-custom {
+
+.custom-file-control {
   position: absolute;
   top: 0;
   right: 0;
@@ -201,28 +221,25 @@
   border: $input-btn-border-width solid #ddd;
   border-radius: .25rem;
   @include box-shadow(inset 0 .2rem .4rem rgba(0,0,0,.05));
-}
-.file-custom::after {
-  content: "Choose file...";
-}
-.file-custom::before {
-  position: absolute;
-  top: -.075rem;
-  right: -.075rem;
-  bottom: -.075rem;
-  z-index: 6;
-  display: block;
-  height: 2.5rem;
-  padding: .5rem 1rem;
-  line-height: 1.5;
-  color: #555;
-  content: "Browse";
-  background-color: #eee;
-  border: $input-btn-border-width solid #ddd;
-  border-radius: 0 .25rem .25rem 0;
-}
 
-// Focus state
-.file input:focus ~ .file-custom {
-  @include box-shadow(0 0 0 .075rem #fff, 0 0 0 .2rem #0074d9);
+  &::after {
+    content: "Choose file...";
+  }
+
+  &::before {
+    position: absolute;
+    top: -.075rem;
+    right: -.075rem;
+    bottom: -.075rem;
+    z-index: 6;
+    display: block;
+    height: 2.5rem;
+    padding: .5rem 1rem;
+    line-height: 1.5;
+    color: #555;
+    content: "Browse";
+    background-color: #eee;
+    border: $input-btn-border-width solid #ddd;
+    border-radius: 0 .25rem .25rem 0;
+  }
 }
diff --git a/scss/_variables.scss b/scss/_variables.scss
index 41bc7dbb4e00e241b3c08d372a0f8d098a7eff47..ae9955ce6eb3f9c06e194f4e78f95637306dd26c 100644
--- a/scss/_variables.scss
+++ b/scss/_variables.scss
@@ -322,6 +322,11 @@ $input-group-addon-border-color: $input-border-color !default;
 
 $cursor-disabled:                not-allowed !default;
 
+$custom-form-bg-color:                    #ddd !default;
+$custom-form-bg-color-disabled:           #eee !default;
+$custom-form-description-color-disabled:  #767676 !default;
+
+
 // Form validation icons
 $form-icon-success: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MTIgNzkyIj48cGF0aCBmaWxsPSIjNWNiODVjIiBkPSJNMjMzLjggNjEwYy0xMy4zIDAtMjYtNi0zNC0xNi44TDkwLjUgNDQ4LjhDNzYuMyA0MzAgODAgNDAzLjMgOTguOCAzODljMTguOC0xNC4yIDQ1LjUtMTAuNCA1OS44IDguNGw3MiA5NUw0NTEuMyAyNDJjMTIuNS0yMCAzOC44LTI2LjIgNTguOC0xMy43IDIwIDEyLjQgMjYgMzguNyAxMy43IDU4LjhMMjcwIDU5MGMtNy40IDEyLTIwLjIgMTkuNC0zNC4zIDIwaC0yeiIvPjwvc3ZnPg==" !default;
 $form-icon-warning: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MTIgNzkyIj48cGF0aCBmaWxsPSIjZjBhZDRlIiBkPSJNNjAzIDY0MC4ybC0yNzguNS01MDljLTMuOC02LjYtMTAuOC0xMC42LTE4LjUtMTAuNnMtMTQuNyA0LTE4LjUgMTAuNkw5IDY0MC4yYy0zLjcgNi41LTMuNiAxNC40LjIgMjAuOCAzLjggNi41IDEwLjggMTAuNCAxOC4zIDEwLjRoNTU3YzcuNiAwIDE0LjYtNCAxOC40LTEwLjQgMy41LTYuNCAzLjYtMTQuNCAwLTIwLjh6bS0yNjYuNC0zMGgtNjEuMlY1NDloNjEuMnY2MS4yem0wLTEwN2gtNjEuMlYzMDRoNjEuMnYxOTl6Ii8+PC9zdmc+" !default;