You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by ro...@apache.org on 2023/04/05 17:46:01 UTC

[activemq-website] 02/04: update to latest 4.x release 4.6.2

This is an automated email from the ASF dual-hosted git repository.

robbie pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/activemq-website.git

commit 5a5bc9a8efe59715726c0f7a3bd7a7d168e3bb3a
Author: Robbie Gemmell <ro...@apache.org>
AuthorDate: Tue Apr 4 17:06:17 2023 +0100

    update to latest 4.x release 4.6.2
---
 src/css/bootstrap/_alert.scss                     |   3 +-
 src/css/bootstrap/_badge.scss                     |   9 +-
 src/css/bootstrap/_breadcrumb.scss                |  16 +-
 src/css/bootstrap/_button-group.scss              |  37 +-
 src/css/bootstrap/_buttons.scss                   |  41 +-
 src/css/bootstrap/_card.scss                      | 138 +++--
 src/css/bootstrap/_carousel.scss                  | 135 +++--
 src/css/bootstrap/_close.scss                     |  22 +-
 src/css/bootstrap/_code.scss                      |  20 +-
 src/css/bootstrap/_custom-forms.scss              | 319 +++++++++--
 src/css/bootstrap/_dropdown.scss                  |  79 ++-
 src/css/bootstrap/_forms.scss                     |  98 ++--
 src/css/bootstrap/_functions.scss                 | 130 ++++-
 src/css/bootstrap/_grid.scss                      |  43 +-
 src/css/bootstrap/_images.scss                    |   8 +-
 src/css/bootstrap/_input-group.scss               |  84 ++-
 src/css/bootstrap/_jumbotron.scss                 |   3 +-
 src/css/bootstrap/_list-group.scss                |  87 ++-
 src/css/bootstrap/_mixins.scss                    |  13 +-
 src/css/bootstrap/_modal.scss                     | 120 +++-
 src/css/bootstrap/_nav.scss                       |  21 +-
 src/css/bootstrap/_navbar.scss                    | 107 ++--
 src/css/bootstrap/_pagination.scss                |  17 +-
 src/css/bootstrap/_popover.scss                   | 123 ++--
 src/css/bootstrap/_print.scss                     |  40 +-
 src/css/bootstrap/_progress.scss                  |  26 +-
 src/css/bootstrap/_reboot.scss                    | 174 +++---
 src/css/bootstrap/_spinners.scss                  |  65 +++
 src/css/bootstrap/_tables.scss                    |  33 +-
 src/css/bootstrap/_toasts.scss                    |  46 ++
 src/css/bootstrap/_tooltip.scss                   |  10 +-
 src/css/bootstrap/_transitions.scss               |  30 +-
 src/css/bootstrap/_type.scss                      |  38 +-
 src/css/bootstrap/_utilities.scss                 |   4 +
 src/css/bootstrap/_variables.scss                 | 669 +++++++++++++++-------
 src/css/bootstrap/bootstrap-grid.scss             |  14 +-
 src/css/bootstrap/bootstrap-reboot.scss           |   8 +-
 src/css/bootstrap/bootstrap.scss                  |  10 +-
 src/css/bootstrap/mixins/_background-variant.scss |   8 +-
 src/css/bootstrap/mixins/_badge.scss              |  11 +-
 src/css/bootstrap/mixins/_border-radius.scss      |  61 +-
 src/css/bootstrap/mixins/_box-shadow.scss         |  17 +-
 src/css/bootstrap/mixins/_breakpoints.scss        |   6 +-
 src/css/bootstrap/mixins/_buttons.scss            |  39 +-
 src/css/bootstrap/mixins/_caret.scss              |  29 +-
 src/css/bootstrap/mixins/_deprecate.scss          |  10 +
 src/css/bootstrap/mixins/_float.scss              |   9 +-
 src/css/bootstrap/mixins/_forms.scss              | 134 +++--
 src/css/bootstrap/mixins/_gradients.scss          |  14 +-
 src/css/bootstrap/mixins/_grid-framework.scss     |  49 +-
 src/css/bootstrap/mixins/_grid.scss               |  51 +-
 src/css/bootstrap/mixins/_hover.scss              |  16 +-
 src/css/bootstrap/mixins/_image.scss              |   8 +-
 src/css/bootstrap/mixins/_list-group.scss         |   4 +-
 src/css/bootstrap/mixins/_lists.scss              |   2 +-
 src/css/bootstrap/mixins/_nav-divider.scss        |   5 +-
 src/css/bootstrap/mixins/_navbar-align.scss       |  10 -
 src/css/bootstrap/mixins/_pagination.scss         |   2 +-
 src/css/bootstrap/mixins/_reset-text.scss         |   6 +-
 src/css/bootstrap/mixins/_screen-reader.scss      |  11 +-
 src/css/bootstrap/mixins/_size.scss               |   1 +
 src/css/bootstrap/mixins/_table-row.scss          |  13 +-
 src/css/bootstrap/mixins/_text-emphasis.scss      |  11 +-
 src/css/bootstrap/mixins/_text-hide.scss          |   4 +-
 src/css/bootstrap/mixins/_transition.scss         |  23 +-
 src/css/bootstrap/mixins/_visibility.scss         |   1 +
 src/css/bootstrap/utilities/_background.scss      |   4 +-
 src/css/bootstrap/utilities/_borders.scss         |  18 +-
 src/css/bootstrap/utilities/_display.scss         |  24 +-
 src/css/bootstrap/utilities/_embed.scss           |  27 +-
 src/css/bootstrap/utilities/_flex.scss            |   5 +
 src/css/bootstrap/utilities/_float.scss           |   8 +-
 src/css/bootstrap/utilities/_interactions.scss    |   5 +
 src/css/bootstrap/utilities/_overflow.scss        |   5 +
 src/css/bootstrap/utilities/_position.scss        |   4 -
 src/css/bootstrap/utilities/_shadows.scss         |   6 +
 src/css/bootstrap/utilities/_sizing.scss          |   8 +
 src/css/bootstrap/utilities/_spacing.scss         |  24 +-
 src/css/bootstrap/utilities/_stretched-link.scss  |  19 +
 src/css/bootstrap/utilities/_text.scss            |  36 +-
 src/css/bootstrap/utilities/_visibility.scss      |   6 +-
 src/css/bootstrap/vendor/_rfs.scss                | 228 ++++++++
 82 files changed, 2672 insertions(+), 1150 deletions(-)

diff --git a/src/css/bootstrap/_alert.scss b/src/css/bootstrap/_alert.scss
index dd43e2376..4aa1fc203 100644
--- a/src/css/bootstrap/_alert.scss
+++ b/src/css/bootstrap/_alert.scss
@@ -27,13 +27,14 @@
 // Expand the right padding and account for the close button's positioning.
 
 .alert-dismissible {
-  padding-right: ($close-font-size + $alert-padding-x * 2);
+  padding-right: $close-font-size + $alert-padding-x * 2;
 
   // Adjust close link position
   .close {
     position: absolute;
     top: 0;
     right: 0;
+    z-index: 2;
     padding: $alert-padding-y $alert-padding-x;
     color: inherit;
   }
diff --git a/src/css/bootstrap/_badge.scss b/src/css/bootstrap/_badge.scss
index b87a1b004..42c5d08d7 100644
--- a/src/css/bootstrap/_badge.scss
+++ b/src/css/bootstrap/_badge.scss
@@ -6,13 +6,20 @@
 .badge {
   display: inline-block;
   padding: $badge-padding-y $badge-padding-x;
-  font-size: $badge-font-size;
+  @include font-size($badge-font-size);
   font-weight: $badge-font-weight;
   line-height: 1;
   text-align: center;
   white-space: nowrap;
   vertical-align: baseline;
   @include border-radius($badge-border-radius);
+  @include transition($badge-transition);
+
+  @at-root a#{&} {
+    @include hover-focus() {
+      text-decoration: none;
+    }
+  }
 
   // Empty badges collapse automatically
   &:empty {
diff --git a/src/css/bootstrap/_breadcrumb.scss b/src/css/bootstrap/_breadcrumb.scss
index 25b9d85a1..9c204c7d5 100644
--- a/src/css/bootstrap/_breadcrumb.scss
+++ b/src/css/bootstrap/_breadcrumb.scss
@@ -3,19 +3,23 @@
   flex-wrap: wrap;
   padding: $breadcrumb-padding-y $breadcrumb-padding-x;
   margin-bottom: $breadcrumb-margin-bottom;
+  @include font-size($breadcrumb-font-size);
   list-style: none;
   background-color: $breadcrumb-bg;
-  @include border-radius($border-radius);
+  @include border-radius($breadcrumb-border-radius);
 }
 
 .breadcrumb-item {
   // The separator between breadcrumbs (by default, a forward-slash: "/")
-  + .breadcrumb-item::before {
-    display: inline-block; // Suppress underlining of the separator in modern browsers
-    padding-right: $breadcrumb-item-padding;
+  + .breadcrumb-item {
     padding-left: $breadcrumb-item-padding;
-    color: $breadcrumb-divider-color;
-    content: "#{$breadcrumb-divider}";
+
+    &::before {
+      float: left; // Suppress inline spacings and underlining of the separator
+      padding-right: $breadcrumb-item-padding;
+      color: $breadcrumb-divider-color;
+      content: escape-svg($breadcrumb-divider);
+    }
   }
 
   // IE9-11 hack to properly handle hyperlink underlines for breadcrumbs built
diff --git a/src/css/bootstrap/_button-group.scss b/src/css/bootstrap/_button-group.scss
index c1b826536..da02d7931 100644
--- a/src/css/bootstrap/_button-group.scss
+++ b/src/css/bootstrap/_button-group.scss
@@ -9,11 +9,11 @@
 
   > .btn {
     position: relative;
-    flex: 0 1 auto;
+    flex: 1 1 auto;
 
     // Bring the hover, focused, and "active" buttons to the front to overlay
     // the borders properly
-    @include hover {
+    @include hover() {
       z-index: 1;
     }
     &:focus,
@@ -22,14 +22,6 @@
       z-index: 1;
     }
   }
-
-  // Prevent double borders when buttons are next to each other
-  .btn + .btn,
-  .btn + .btn-group,
-  .btn-group + .btn,
-  .btn-group + .btn-group {
-    margin-left: -$btn-border-width;
-  }
 }
 
 // Optional: Group multiple button groups together for a toolbar
@@ -44,8 +36,10 @@
 }
 
 .btn-group {
-  > .btn:first-child {
-    margin-left: 0;
+  // Prevent double borders when buttons are next to each other
+  > .btn:not(:first-child),
+  > .btn-group:not(:first-child) {
+    margin-left: -$btn-border-width;
   }
 
   // Reset rounded corners
@@ -76,9 +70,15 @@
   padding-right: $btn-padding-x * .75;
   padding-left: $btn-padding-x * .75;
 
-  &::after {
+  &::after,
+  .dropup &::after,
+  .dropright &::after {
     margin-left: 0;
   }
+
+  .dropleft &::before {
+    margin-right: 0;
+  }
 }
 
 .btn-sm + .dropdown-toggle-split {
@@ -113,17 +113,14 @@
   align-items: flex-start;
   justify-content: center;
 
-  .btn,
-  .btn-group {
+  > .btn,
+  > .btn-group {
     width: 100%;
   }
 
-  > .btn + .btn,
-  > .btn + .btn-group,
-  > .btn-group + .btn,
-  > .btn-group + .btn-group {
+  > .btn:not(:first-child),
+  > .btn-group:not(:first-child) {
     margin-top: -$btn-border-width;
-    margin-left: 0;
   }
 
   // Reset rounded corners
diff --git a/src/css/bootstrap/_buttons.scss b/src/css/bootstrap/_buttons.scss
index da6d87916..6ee24ba1f 100644
--- a/src/css/bootstrap/_buttons.scss
+++ b/src/css/bootstrap/_buttons.scss
@@ -6,17 +6,21 @@
 
 .btn {
   display: inline-block;
+  font-family: $btn-font-family;
   font-weight: $btn-font-weight;
+  color: $body-color;
   text-align: center;
-  white-space: nowrap;
+  text-decoration: if($link-decoration == none, null, none);
+  white-space: $btn-white-space;
   vertical-align: middle;
   user-select: none;
+  background-color: transparent;
   border: $btn-border-width solid transparent;
-  @include button-size($btn-padding-y, $btn-padding-x, $font-size-base, $btn-line-height, $btn-border-radius);
+  @include button-size($btn-padding-y, $btn-padding-x, $btn-font-size, $btn-line-height, $btn-border-radius);
   @include transition($btn-transition);
 
-  // Share hover and focus styles
-  @include hover-focus {
+  @include hover() {
+    color: $body-color;
     text-decoration: none;
   }
 
@@ -33,18 +37,16 @@
     @include box-shadow(none);
   }
 
-  // Opinionated: add "hand" cursor to non-disabled .btn elements
   &:not(:disabled):not(.disabled) {
-    cursor: pointer;
-  }
+    cursor: if($enable-pointer-cursor-for-buttons, pointer, null);
 
-  &:not(:disabled):not(.disabled):active,
-  &:not(:disabled):not(.disabled).active {
-    background-image: none;
-    @include box-shadow($btn-active-box-shadow);
+    &:active,
+    &.active {
+      @include box-shadow($btn-active-box-shadow);
 
-    &:focus {
-      @include box-shadow($btn-focus-box-shadow, $btn-active-box-shadow);
+      &:focus {
+        @include box-shadow($btn-focus-box-shadow, $btn-active-box-shadow);
+      }
     }
   }
 }
@@ -81,25 +83,22 @@ fieldset:disabled a.btn {
 .btn-link {
   font-weight: $font-weight-normal;
   color: $link-color;
-  background-color: transparent;
+  text-decoration: $link-decoration;
 
-  @include hover {
+  @include hover() {
     color: $link-hover-color;
     text-decoration: $link-hover-decoration;
-    background-color: transparent;
-    border-color: transparent;
   }
 
   &:focus,
   &.focus {
     text-decoration: $link-hover-decoration;
-    border-color: transparent;
-    box-shadow: none;
   }
 
   &:disabled,
   &.disabled {
     color: $btn-link-disabled-color;
+    pointer-events: none;
   }
 
   // No need for an active state here
@@ -111,11 +110,11 @@ fieldset:disabled a.btn {
 //
 
 .btn-lg {
-  @include button-size($btn-padding-y-lg, $btn-padding-x-lg, $font-size-lg, $btn-line-height-lg, $btn-border-radius-lg);
+  @include button-size($btn-padding-y-lg, $btn-padding-x-lg, $btn-font-size-lg, $btn-line-height-lg, $btn-border-radius-lg);
 }
 
 .btn-sm {
-  @include button-size($btn-padding-y-sm, $btn-padding-x-sm, $font-size-sm, $btn-line-height-sm, $btn-border-radius-sm);
+  @include button-size($btn-padding-y-sm, $btn-padding-x-sm, $btn-font-size-sm, $btn-line-height-sm, $btn-border-radius-sm);
 }
 
 
diff --git a/src/css/bootstrap/_card.scss b/src/css/bootstrap/_card.scss
index 4c4845c0b..a974163f8 100644
--- a/src/css/bootstrap/_card.scss
+++ b/src/css/bootstrap/_card.scss
@@ -6,7 +6,8 @@
   position: relative;
   display: flex;
   flex-direction: column;
-  min-width: 0;
+  min-width: 0; // See https://github.com/twbs/bootstrap/pull/22740#issuecomment-305868106
+  height: $card-height;
   word-wrap: break-word;
   background-color: $card-bg;
   background-clip: border-box;
@@ -18,24 +19,38 @@
     margin-left: 0;
   }
 
-  > .list-group:first-child {
-    .list-group-item:first-child {
-      @include border-top-radius($card-border-radius);
+  > .list-group {
+    border-top: inherit;
+    border-bottom: inherit;
+
+    &:first-child {
+      border-top-width: 0;
+      @include border-top-radius($card-inner-border-radius);
     }
-  }
 
-  > .list-group:last-child {
-    .list-group-item:last-child {
-      @include border-bottom-radius($card-border-radius);
+    &:last-child  {
+      border-bottom-width: 0;
+      @include border-bottom-radius($card-inner-border-radius);
     }
   }
+
+  // Due to specificity of the above selector (`.card > .list-group`), we must
+  // use a child selector here to prevent double borders.
+  > .card-header + .list-group,
+  > .list-group + .card-footer {
+    border-top: 0;
+  }
 }
 
 .card-body {
   // Enable `flex-grow: 1` for decks and groups so that card blocks take up
   // as much space as possible, ensuring footers are aligned to the bottom.
   flex: 1 1 auto;
+  // Workaround for the image size bug in IE
+  // See: https://github.com/twbs/bootstrap/pull/28855
+  min-height: 1px;
   padding: $card-spacer-x;
+  color: $card-color;
 }
 
 .card-title {
@@ -43,7 +58,7 @@
 }
 
 .card-subtitle {
-  margin-top: -($card-spacer-y / 2);
+  margin-top: -$card-spacer-y * .5;
   margin-bottom: 0;
 }
 
@@ -52,7 +67,7 @@
 }
 
 .card-link {
-  @include hover {
+  @include hover() {
     text-decoration: none;
   }
 
@@ -68,22 +83,18 @@
 .card-header {
   padding: $card-spacer-y $card-spacer-x;
   margin-bottom: 0; // Removes the default margin-bottom of <hN>
+  color: $card-cap-color;
   background-color: $card-cap-bg;
   border-bottom: $card-border-width solid $card-border-color;
 
   &:first-child {
     @include border-radius($card-inner-border-radius $card-inner-border-radius 0 0);
   }
-
-  + .list-group {
-    .list-group-item:first-child {
-      border-top: 0;
-    }
-  }
 }
 
 .card-footer {
   padding: $card-spacer-y $card-spacer-x;
+  color: $card-cap-color;
   background-color: $card-cap-bg;
   border-top: $card-border-width solid $card-border-color;
 
@@ -98,15 +109,15 @@
 //
 
 .card-header-tabs {
-  margin-right: -($card-spacer-x / 2);
+  margin-right: -$card-spacer-x * .5;
   margin-bottom: -$card-spacer-y;
-  margin-left: -($card-spacer-x / 2);
+  margin-left: -$card-spacer-x * .5;
   border-bottom: 0;
 }
 
 .card-header-pills {
-  margin-right: -($card-spacer-x / 2);
-  margin-left: -($card-spacer-x / 2);
+  margin-right: -$card-spacer-x * .5;
+  margin-left: -$card-spacer-x * .5;
 }
 
 // Card image
@@ -117,21 +128,23 @@
   bottom: 0;
   left: 0;
   padding: $card-img-overlay-padding;
+  @include border-radius($card-inner-border-radius);
 }
 
-.card-img {
+.card-img,
+.card-img-top,
+.card-img-bottom {
+  flex-shrink: 0; // For IE: https://github.com/twbs/bootstrap/issues/29396
   width: 100%; // Required because we use flexbox and this inherently applies align-self: stretch
-  @include border-radius($card-inner-border-radius);
 }
 
-// Card image caps
+.card-img,
 .card-img-top {
-  width: 100%; // Required because we use flexbox and this inherently applies align-self: stretch
   @include border-top-radius($card-inner-border-radius);
 }
 
+.card-img,
 .card-img-bottom {
-  width: 100%; // Required because we use flexbox and this inherently applies align-self: stretch
   @include border-bottom-radius($card-inner-border-radius);
 }
 
@@ -139,23 +152,19 @@
 // Card deck
 
 .card-deck {
-  display: flex;
-  flex-direction: column;
-
   .card {
     margin-bottom: $card-deck-margin;
   }
 
   @include media-breakpoint-up(sm) {
+    display: flex;
     flex-flow: row wrap;
     margin-right: -$card-deck-margin;
     margin-left: -$card-deck-margin;
 
     .card {
-      display: flex;
-      // Flexbugs #4: https://github.com/philipwalton/flexbugs#4-flex-shorthand-declarations-with-unitless-flex-basis-values-are-ignored
+      // Flexbugs #4: https://github.com/philipwalton/flexbugs#flexbug-4
       flex: 1 0 0%;
-      flex-direction: column;
       margin-right: $card-deck-margin;
       margin-bottom: 0; // Override the default
       margin-left: $card-deck-margin;
@@ -169,9 +178,6 @@
 //
 
 .card-group {
-  display: flex;
-  flex-direction: column;
-
   // The child selector allows nested `.card` within `.card-group`
   // to display properly.
   > .card {
@@ -179,11 +185,12 @@
   }
 
   @include media-breakpoint-up(sm) {
+    display: flex;
     flex-flow: row wrap;
     // The child selector allows nested `.card` within `.card-group`
     // to display properly.
     > .card {
-      // Flexbugs #4: https://github.com/philipwalton/flexbugs#4-flex-shorthand-declarations-with-unitless-flex-basis-values-are-ignored
+      // Flexbugs #4: https://github.com/philipwalton/flexbugs#flexbug-4
       flex: 1 0 0%;
       margin-bottom: 0;
 
@@ -194,55 +201,35 @@
 
       // Handle rounded corners
       @if $enable-rounded {
-        &:first-child {
+        &:not(:last-child) {
           @include border-right-radius(0);
 
           .card-img-top,
           .card-header {
+            // stylelint-disable-next-line property-disallowed-list
             border-top-right-radius: 0;
           }
           .card-img-bottom,
           .card-footer {
+            // stylelint-disable-next-line property-disallowed-list
             border-bottom-right-radius: 0;
           }
         }
 
-        &:last-child {
+        &:not(:first-child) {
           @include border-left-radius(0);
 
           .card-img-top,
           .card-header {
+            // stylelint-disable-next-line property-disallowed-list
             border-top-left-radius: 0;
           }
           .card-img-bottom,
           .card-footer {
+            // stylelint-disable-next-line property-disallowed-list
             border-bottom-left-radius: 0;
           }
         }
-
-        &:only-child {
-          @include border-radius($card-border-radius);
-
-          .card-img-top,
-          .card-header {
-            @include border-top-radius($card-border-radius);
-          }
-          .card-img-bottom,
-          .card-footer {
-            @include border-bottom-radius($card-border-radius);
-          }
-        }
-
-        &:not(:first-child):not(:last-child):not(:only-child) {
-          @include border-radius(0);
-
-          .card-img-top,
-          .card-img-bottom,
-          .card-header,
-          .card-footer {
-            @include border-radius(0);
-          }
-        }
       }
     }
   }
@@ -261,6 +248,8 @@
   @include media-breakpoint-up(sm) {
     column-count: $card-columns-count;
     column-gap: $card-columns-gap;
+    orphans: 1;
+    widows: 1;
 
     .card {
       display: inline-block; // Don't let them vertically span multiple columns
@@ -268,3 +257,30 @@
     }
   }
 }
+
+
+//
+// Accordion
+//
+
+.accordion {
+  overflow-anchor: none;
+
+  > .card {
+    overflow: hidden;
+
+    &:not(:last-of-type) {
+      border-bottom: 0;
+      @include border-bottom-radius(0);
+    }
+
+    &:not(:first-of-type) {
+      @include border-top-radius(0);
+    }
+
+    > .card-header {
+      @include border-radius(0);
+      margin-bottom: -$card-border-width;
+    }
+  }
+}
diff --git a/src/css/bootstrap/_carousel.scss b/src/css/bootstrap/_carousel.scss
index 72a503445..25aaf5873 100644
--- a/src/css/bootstrap/_carousel.scss
+++ b/src/css/bootstrap/_carousel.scss
@@ -1,22 +1,39 @@
-// Wrapper for the slide container and indicators
+// Notes on the classes:
+//
+// 1. .carousel.pointer-event should ideally be pan-y (to allow for users to scroll vertically)
+//    even when their scroll action started on a carousel, but for compatibility (with Firefox)
+//    we're preventing all actions instead
+// 2. The .carousel-item-left and .carousel-item-right is used to indicate where
+//    the active slide is heading.
+// 3. .active.carousel-item is the current slide.
+// 4. .active.carousel-item-left and .active.carousel-item-right is the current
+//    slide in its in-transition state. Only one of these occurs at a time.
+// 5. .carousel-item-next.carousel-item-left and .carousel-item-prev.carousel-item-right
+//    is the upcoming slide in transition.
+
 .carousel {
   position: relative;
 }
 
+.carousel.pointer-event {
+  touch-action: pan-y;
+}
+
 .carousel-inner {
   position: relative;
   width: 100%;
   overflow: hidden;
+  @include clearfix();
 }
 
 .carousel-item {
   position: relative;
   display: none;
-  align-items: center;
+  float: left;
   width: 100%;
-  @include transition($carousel-transition);
+  margin-right: -100%;
   backface-visibility: hidden;
-  perspective: 1000px;
+  @include transition($carousel-transition);
 }
 
 .carousel-item.active,
@@ -25,37 +42,40 @@
   display: block;
 }
 
-.carousel-item-next,
-.carousel-item-prev {
-  position: absolute;
-  top: 0;
+.carousel-item-next:not(.carousel-item-left),
+.active.carousel-item-right {
+  transform: translateX(100%);
 }
 
-// CSS3 transforms when supported by the browser
-.carousel-item-next.carousel-item-left,
-.carousel-item-prev.carousel-item-right {
-  transform: translateX(0);
-
-  @supports (transform-style: preserve-3d) {
-    transform: translate3d(0, 0, 0);
-  }
+.carousel-item-prev:not(.carousel-item-right),
+.active.carousel-item-left {
+  transform: translateX(-100%);
 }
 
-.carousel-item-next,
-.active.carousel-item-right {
-  transform: translateX(100%);
 
-  @supports (transform-style: preserve-3d) {
-    transform: translate3d(100%, 0, 0);
+//
+// Alternate transitions
+//
+
+.carousel-fade {
+  .carousel-item {
+    opacity: 0;
+    transition-property: opacity;
+    transform: none;
   }
-}
 
-.carousel-item-prev,
-.active.carousel-item-left {
-  transform: translateX(-100%);
+  .carousel-item.active,
+  .carousel-item-next.carousel-item-left,
+  .carousel-item-prev.carousel-item-right {
+    z-index: 1;
+    opacity: 1;
+  }
 
-  @supports (transform-style: preserve-3d) {
-    transform: translate3d(-100%, 0, 0);
+  .active.carousel-item-left,
+  .active.carousel-item-right {
+    z-index: 0;
+    opacity: 0;
+    @include transition(opacity 0s $carousel-transition-duration);
   }
 }
 
@@ -69,35 +89,38 @@
   position: absolute;
   top: 0;
   bottom: 0;
+  z-index: 1;
   // Use flex for alignment (1-3)
   display: flex; // 1. allow flex styles
   align-items: center; // 2. vertically center contents
   justify-content: center; // 3. horizontally center contents
   width: $carousel-control-width;
+  padding: 0;
   color: $carousel-control-color;
   text-align: center;
+  background: none;
+  border: 0;
   opacity: $carousel-control-opacity;
-  // We can't have a transition here because WebKit cancels the carousel
-  // animation if you trip this while in the middle of another animation.
+  @include transition($carousel-control-transition);
 
   // Hover/focus state
-  @include hover-focus {
+  @include hover-focus() {
     color: $carousel-control-color;
     text-decoration: none;
     outline: 0;
-    opacity: .9;
+    opacity: $carousel-control-hover-opacity;
   }
 }
 .carousel-control-prev {
   left: 0;
   @if $enable-gradients {
-    background: linear-gradient(90deg, rgba(0, 0, 0, .25), rgba(0, 0, 0, .001));
+    background-image: linear-gradient(90deg, rgba($black, .25), rgba($black, .001));
   }
 }
 .carousel-control-next {
   right: 0;
   @if $enable-gradients {
-    background: linear-gradient(270deg, rgba(0, 0, 0, .25), rgba(0, 0, 0, .001));
+    background-image: linear-gradient(270deg, rgba($black, .25), rgba($black, .001));
   }
 }
 
@@ -107,14 +130,13 @@
   display: inline-block;
   width: $carousel-control-icon-width;
   height: $carousel-control-icon-width;
-  background: transparent no-repeat center center;
-  background-size: 100% 100%;
+  background: 50% / 100% 100% no-repeat;
 }
 .carousel-control-prev-icon {
-  background-image: $carousel-control-prev-icon-bg;
+  background-image: escape-svg($carousel-control-prev-icon-bg);
 }
 .carousel-control-next-icon {
-  background-image: $carousel-control-next-icon-bg;
+  background-image: escape-svg($carousel-control-next-icon-bg);
 }
 
 
@@ -126,7 +148,7 @@
 .carousel-indicators {
   position: absolute;
   right: 0;
-  bottom: 10px;
+  bottom: 0;
   left: 0;
   z-index: 15;
   display: flex;
@@ -138,38 +160,25 @@
   list-style: none;
 
   li {
-    position: relative;
+    box-sizing: content-box;
     flex: 0 1 auto;
     width: $carousel-indicator-width;
     height: $carousel-indicator-height;
     margin-right: $carousel-indicator-spacer;
     margin-left: $carousel-indicator-spacer;
     text-indent: -999px;
-    background-color: rgba($carousel-indicator-active-bg, .5);
-
-    // Use pseudo classes to increase the hit area by 10px on top and bottom.
-    &::before {
-      position: absolute;
-      top: -10px;
-      left: 0;
-      display: inline-block;
-      width: 100%;
-      height: 10px;
-      content: "";
-    }
-    &::after {
-      position: absolute;
-      bottom: -10px;
-      left: 0;
-      display: inline-block;
-      width: 100%;
-      height: 10px;
-      content: "";
-    }
+    cursor: pointer;
+    background-color: $carousel-indicator-active-bg;
+    background-clip: padding-box;
+    // Use transparent borders to increase the hit area by 10px on top and bottom.
+    border-top: $carousel-indicator-hit-area-height solid transparent;
+    border-bottom: $carousel-indicator-hit-area-height solid transparent;
+    opacity: .5;
+    @include transition($carousel-indicator-transition);
   }
 
   .active {
-    background-color: $carousel-indicator-active-bg;
+    opacity: 1;
   }
 }
 
@@ -180,9 +189,9 @@
 
 .carousel-caption {
   position: absolute;
-  right: ((100% - $carousel-caption-width) / 2);
+  right: (100% - $carousel-caption-width) * .5;
   bottom: 20px;
-  left: ((100% - $carousel-caption-width) / 2);
+  left: (100% - $carousel-caption-width) * .5;
   z-index: 10;
   padding-top: 20px;
   padding-bottom: 20px;
diff --git a/src/css/bootstrap/_close.scss b/src/css/bootstrap/_close.scss
index 871cb08fd..82e9593ef 100644
--- a/src/css/bootstrap/_close.scss
+++ b/src/css/bootstrap/_close.scss
@@ -1,21 +1,22 @@
 .close {
   float: right;
-  font-size: $close-font-size;
+  @include font-size($close-font-size);
   font-weight: $close-font-weight;
   line-height: 1;
   color: $close-color;
   text-shadow: $close-text-shadow;
   opacity: .5;
 
-  @include hover-focus {
+  // Override <a>'s hover style
+  @include hover() {
     color: $close-color;
     text-decoration: none;
-    opacity: .75;
   }
 
-  // Opinionated: add "hand" cursor to non-disabled .close elements
   &:not(:disabled):not(.disabled) {
-    cursor: pointer;
+    @include hover-focus() {
+      opacity: .75;
+    }
   }
 }
 
@@ -24,11 +25,16 @@
 // If you want the anchor version, it requires `href="#"`.
 // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile
 
-// stylelint-disable property-no-vendor-prefix, selector-no-qualifying-type
+// stylelint-disable-next-line selector-no-qualifying-type
 button.close {
   padding: 0;
   background-color: transparent;
   border: 0;
-  -webkit-appearance: none;
 }
-// stylelint-enable
+
+// Future-proof disabling of clicks on `<a>` elements
+
+// stylelint-disable-next-line selector-no-qualifying-type
+a.close.disabled {
+  pointer-events: none;
+}
diff --git a/src/css/bootstrap/_code.scss b/src/css/bootstrap/_code.scss
index 9de20fa0d..4bc117a96 100644
--- a/src/css/bootstrap/_code.scss
+++ b/src/css/bootstrap/_code.scss
@@ -1,16 +1,8 @@
-// Inline and block code styles
-code,
-kbd,
-pre,
-samp {
-  font-family: $font-family-monospace;
-}
-
 // Inline code
 code {
-  font-size: $code-font-size;
+  @include font-size($code-font-size);
   color: $code-color;
-  word-break: break-word;
+  word-wrap: break-word;
 
   // Streamline the style when inside anchors to avoid broken underline and more
   a > & {
@@ -21,7 +13,7 @@ code {
 // User input typically entered via keyboard
 kbd {
   padding: $kbd-padding-y $kbd-padding-x;
-  font-size: $kbd-font-size;
+  @include font-size($kbd-font-size);
   color: $kbd-color;
   background-color: $kbd-bg;
   @include border-radius($border-radius-sm);
@@ -29,7 +21,7 @@ kbd {
 
   kbd {
     padding: 0;
-    font-size: 100%;
+    @include font-size(100%);
     font-weight: $nested-kbd-font-weight;
     @include box-shadow(none);
   }
@@ -38,12 +30,12 @@ kbd {
 // Blocks of code
 pre {
   display: block;
-  font-size: $code-font-size;
+  @include font-size($code-font-size);
   color: $pre-color;
 
   // Account for some code outputs that place code tags in pre tags
   code {
-    font-size: inherit;
+    @include font-size(inherit);
     color: inherit;
     word-break: normal;
   }
diff --git a/src/css/bootstrap/_custom-forms.scss b/src/css/bootstrap/_custom-forms.scss
index 2d83f7ea6..750ea9cfb 100644
--- a/src/css/bootstrap/_custom-forms.scss
+++ b/src/css/bootstrap/_custom-forms.scss
@@ -9,9 +9,11 @@
 
 .custom-control {
   position: relative;
+  z-index: 1;
   display: block;
-  min-height: (1rem * $line-height-base);
-  padding-left: $custom-control-gutter;
+  min-height: $font-size-base * $line-height-base;
+  padding-left: $custom-control-gutter + $custom-control-indicator-size;
+  print-color-adjust: exact; // Keep themed appearance for print
 }
 
 .custom-control-inline {
@@ -21,26 +23,41 @@
 
 .custom-control-input {
   position: absolute;
+  left: 0;
   z-index: -1; // Put the input behind the label so it doesn't overlay text
+  width: $custom-control-indicator-size;
+  height: ($font-size-base * $line-height-base + $custom-control-indicator-size) * .5;
   opacity: 0;
 
   &:checked ~ .custom-control-label::before {
     color: $custom-control-indicator-checked-color;
+    border-color: $custom-control-indicator-checked-border-color;
     @include gradient-bg($custom-control-indicator-checked-bg);
     @include box-shadow($custom-control-indicator-checked-box-shadow);
   }
 
   &:focus ~ .custom-control-label::before {
     // the mixin is not used here to make sure there is feedback
-    box-shadow: $custom-control-indicator-focus-box-shadow;
+    @if $enable-shadows {
+      box-shadow: $input-box-shadow, $custom-control-indicator-focus-box-shadow;
+    } @else {
+      box-shadow: $custom-control-indicator-focus-box-shadow;
+    }
   }
 
-  &:active ~ .custom-control-label::before {
+  &:focus:not(:checked) ~ .custom-control-label::before {
+    border-color: $custom-control-indicator-focus-border-color;
+  }
+
+  &:not(:disabled):active ~ .custom-control-label::before {
     color: $custom-control-indicator-active-color;
     background-color: $custom-control-indicator-active-bg;
+    border-color: $custom-control-indicator-active-border-color;
     @include box-shadow($custom-control-indicator-active-box-shadow);
   }
 
+  // Use [disabled] and :disabled to work around https://github.com/twbs/bootstrap/issues/28247
+  &[disabled],
   &:disabled {
     ~ .custom-control-label {
       color: $custom-control-label-disabled-color;
@@ -54,38 +71,40 @@
 
 // Custom control indicators
 //
-// Build the custom controls out of psuedo-elements.
+// Build the custom controls out of pseudo-elements.
 
 .custom-control-label {
+  position: relative;
   margin-bottom: 0;
+  color: $custom-control-label-color;
+  vertical-align: top;
+  cursor: $custom-control-cursor;
 
   // Background-color and (when enabled) gradient
   &::before {
     position: absolute;
-    top: (($line-height-base - $custom-control-indicator-size) / 2);
-    left: 0;
+    top: ($font-size-base * $line-height-base - $custom-control-indicator-size) * .5;
+    left: -($custom-control-gutter + $custom-control-indicator-size);
     display: block;
     width: $custom-control-indicator-size;
     height: $custom-control-indicator-size;
     pointer-events: none;
     content: "";
-    user-select: none;
     background-color: $custom-control-indicator-bg;
+    border: $custom-control-indicator-border-width solid $custom-control-indicator-border-color;
     @include box-shadow($custom-control-indicator-box-shadow);
   }
 
   // Foreground (icon)
   &::after {
     position: absolute;
-    top: (($line-height-base - $custom-control-indicator-size) / 2);
-    left: 0;
+    top: ($font-size-base * $line-height-base - $custom-control-indicator-size) * .5;
+    left: -($custom-control-gutter + $custom-control-indicator-size);
     display: block;
     width: $custom-control-indicator-size;
     height: $custom-control-indicator-size;
     content: "";
-    background-repeat: no-repeat;
-    background-position: center center;
-    background-size: $custom-control-indicator-bg-size;
+    background: 50% / #{$custom-control-indicator-bg-size} no-repeat;
   }
 }
 
@@ -100,30 +119,28 @@
   }
 
   .custom-control-input:checked ~ .custom-control-label {
-    &::before {
-      @include gradient-bg($custom-control-indicator-checked-bg);
-    }
     &::after {
-      background-image: $custom-checkbox-indicator-icon-checked;
+      background-image: escape-svg($custom-checkbox-indicator-icon-checked);
     }
   }
 
   .custom-control-input:indeterminate ~ .custom-control-label {
     &::before {
+      border-color: $custom-checkbox-indicator-indeterminate-border-color;
       @include gradient-bg($custom-checkbox-indicator-indeterminate-bg);
       @include box-shadow($custom-checkbox-indicator-indeterminate-box-shadow);
     }
     &::after {
-      background-image: $custom-checkbox-indicator-icon-indeterminate;
+      background-image: escape-svg($custom-checkbox-indicator-icon-indeterminate);
     }
   }
 
   .custom-control-input:disabled {
     &:checked ~ .custom-control-label::before {
-      background-color: $custom-control-indicator-checked-disabled-bg;
+      @include gradient-bg($custom-control-indicator-checked-disabled-bg);
     }
     &:indeterminate ~ .custom-control-label::before {
-      background-color: $custom-control-indicator-checked-disabled-bg;
+      @include gradient-bg($custom-control-indicator-checked-disabled-bg);
     }
   }
 }
@@ -134,21 +151,62 @@
 
 .custom-radio {
   .custom-control-label::before {
+    // stylelint-disable-next-line property-disallowed-list
     border-radius: $custom-radio-indicator-border-radius;
   }
 
   .custom-control-input:checked ~ .custom-control-label {
+    &::after {
+      background-image: escape-svg($custom-radio-indicator-icon-checked);
+    }
+  }
+
+  .custom-control-input:disabled {
+    &:checked ~ .custom-control-label::before {
+      @include gradient-bg($custom-control-indicator-checked-disabled-bg);
+    }
+  }
+}
+
+
+// switches
+//
+// Tweak a few things for switches
+
+.custom-switch {
+  padding-left: $custom-switch-width + $custom-control-gutter;
+
+  .custom-control-label {
     &::before {
-      @include gradient-bg($custom-control-indicator-checked-bg);
+      left: -($custom-switch-width + $custom-control-gutter);
+      width: $custom-switch-width;
+      pointer-events: all;
+      // stylelint-disable-next-line property-disallowed-list
+      border-radius: $custom-switch-indicator-border-radius;
     }
+
     &::after {
-      background-image: $custom-radio-indicator-icon-checked;
+      top: add(($font-size-base * $line-height-base - $custom-control-indicator-size) * .5, $custom-control-indicator-border-width * 2);
+      left: add(-($custom-switch-width + $custom-control-gutter), $custom-control-indicator-border-width * 2);
+      width: $custom-switch-indicator-size;
+      height: $custom-switch-indicator-size;
+      background-color: $custom-control-indicator-border-color;
+      // stylelint-disable-next-line property-disallowed-list
+      border-radius: $custom-switch-indicator-border-radius;
+      @include transition(transform .15s ease-in-out, $custom-forms-transition);
+    }
+  }
+
+  .custom-control-input:checked ~ .custom-control-label {
+    &::after {
+      background-color: $custom-control-indicator-bg;
+      transform: translateX($custom-switch-width - $custom-control-indicator-size);
     }
   }
 
   .custom-control-input:disabled {
     &:checked ~ .custom-control-label::before {
-      background-color: $custom-control-indicator-checked-disabled-bg;
+      @include gradient-bg($custom-control-indicator-checked-disabled-bg);
     }
   }
 }
@@ -157,7 +215,7 @@
 // Select
 //
 // Replaces the browser default select with a custom one, mostly pulled from
-// http://primercss.io.
+// https://primer.github.io/.
 //
 
 .custom-select {
@@ -165,23 +223,27 @@
   width: 100%;
   height: $custom-select-height;
   padding: $custom-select-padding-y ($custom-select-padding-x + $custom-select-indicator-padding) $custom-select-padding-y $custom-select-padding-x;
+  font-family: $custom-select-font-family;
+  @include font-size($custom-select-font-size);
+  font-weight: $custom-select-font-weight;
   line-height: $custom-select-line-height;
   color: $custom-select-color;
   vertical-align: middle;
-  background: $custom-select-bg $custom-select-indicator no-repeat right $custom-select-padding-x center;
-  background-size: $custom-select-bg-size;
+  background: $custom-select-bg $custom-select-background;
   border: $custom-select-border-width solid $custom-select-border-color;
-  @if $enable-rounded {
-    border-radius: $custom-select-border-radius;
-  } @else {
-    border-radius: 0;
-  }
+  @include border-radius($custom-select-border-radius, 0);
+  @include box-shadow($custom-select-box-shadow);
   appearance: none;
 
   &:focus {
     border-color: $custom-select-focus-border-color;
     outline: 0;
-    box-shadow: $custom-select-focus-box-shadow;
+    @if $enable-shadows {
+      @include box-shadow($custom-select-box-shadow, $custom-select-focus-box-shadow);
+    } @else {
+      // Avoid using mixin so we can pass custom focus shadow properly
+      box-shadow: $custom-select-focus-box-shadow;
+    }
 
     &::-ms-value {
       // For visual consistency with other platforms/browsers,
@@ -208,22 +270,30 @@
 
   // Hides the default caret in IE11
   &::-ms-expand {
-    opacity: 0;
+    display: none;
+  }
+
+  // Remove outline from select box in FF
+  &:-moz-focusring {
+    color: transparent;
+    text-shadow: 0 0 0 $custom-select-color;
   }
 }
 
 .custom-select-sm {
   height: $custom-select-height-sm;
-  padding-top: $custom-select-padding-y;
-  padding-bottom: $custom-select-padding-y;
-  font-size: $custom-select-font-size-sm;
+  padding-top: $custom-select-padding-y-sm;
+  padding-bottom: $custom-select-padding-y-sm;
+  padding-left: $custom-select-padding-x-sm;
+  @include font-size($custom-select-font-size-sm);
 }
 
 .custom-select-lg {
   height: $custom-select-height-lg;
-  padding-top: $custom-select-padding-y;
-  padding-bottom: $custom-select-padding-y;
-  font-size: $custom-select-font-size-lg;
+  padding-top: $custom-select-padding-y-lg;
+  padding-bottom: $custom-select-padding-y-lg;
+  padding-left: $custom-select-padding-x-lg;
+  @include font-size($custom-select-font-size-lg);
 }
 
 
@@ -245,15 +315,18 @@
   width: 100%;
   height: $custom-file-height;
   margin: 0;
+  overflow: hidden;
   opacity: 0;
 
-  &:focus ~ .custom-file-control {
+  &:focus ~ .custom-file-label {
     border-color: $custom-file-focus-border-color;
     box-shadow: $custom-file-focus-box-shadow;
+  }
 
-    &::before {
-      border-color: $custom-file-focus-border-color;
-    }
+  // Use [disabled] and :disabled to work around https://github.com/twbs/bootstrap/issues/28247
+  &[disabled] ~ .custom-file-label,
+  &:disabled ~ .custom-file-label {
+    background-color: $custom-file-disabled-bg;
   }
 
   @each $lang, $value in $custom-file-text {
@@ -261,6 +334,10 @@
       content: $value;
     }
   }
+
+  ~ .custom-file-label[data-browse]::after {
+    content: attr(data-browse);
+  }
 }
 
 .custom-file-label {
@@ -271,6 +348,9 @@
   z-index: 1;
   height: $custom-file-height;
   padding: $custom-file-padding-y $custom-file-padding-x;
+  overflow: hidden;
+  font-family: $custom-file-font-family;
+  font-weight: $custom-file-font-weight;
   line-height: $custom-file-line-height;
   color: $custom-file-color;
   background-color: $custom-file-bg;
@@ -285,13 +365,162 @@
     bottom: 0;
     z-index: 3;
     display: block;
-    height: calc(#{$custom-file-height} - #{$custom-file-border-width} * 2);
+    height: $custom-file-height-inner;
     padding: $custom-file-padding-y $custom-file-padding-x;
     line-height: $custom-file-line-height;
     color: $custom-file-button-color;
     content: "Browse";
     @include gradient-bg($custom-file-button-bg);
-    border-left: $custom-file-border-width solid $custom-file-border-color;
+    border-left: inherit;
     @include border-radius(0 $custom-file-border-radius $custom-file-border-radius 0);
   }
 }
+
+// Range
+//
+// Style range inputs the same across browsers. Vendor-specific rules for pseudo
+// elements cannot be mixed. As such, there are no shared styles for focus or
+// active states on prefixed selectors.
+
+.custom-range {
+  width: 100%;
+  height: add($custom-range-thumb-height, $custom-range-thumb-focus-box-shadow-width * 2);
+  padding: 0; // Need to reset padding
+  background-color: transparent;
+  appearance: none;
+
+  &:focus {
+    outline: 0;
+
+    // Pseudo-elements must be split across multiple rulesets to have an effect.
+    // No box-shadow() mixin for focus accessibility.
+    &::-webkit-slider-thumb { box-shadow: $custom-range-thumb-focus-box-shadow; }
+    &::-moz-range-thumb     { box-shadow: $custom-range-thumb-focus-box-shadow; }
+    &::-ms-thumb            { box-shadow: $custom-range-thumb-focus-box-shadow; }
+  }
+
+  &::-moz-focus-outer {
+    border: 0;
+  }
+
+  &::-webkit-slider-thumb {
+    width: $custom-range-thumb-width;
+    height: $custom-range-thumb-height;
+    margin-top: ($custom-range-track-height - $custom-range-thumb-height) * .5; // Webkit specific
+    @include gradient-bg($custom-range-thumb-bg);
+    border: $custom-range-thumb-border;
+    @include border-radius($custom-range-thumb-border-radius);
+    @include box-shadow($custom-range-thumb-box-shadow);
+    @include transition($custom-forms-transition);
+    appearance: none;
+
+    &:active {
+      @include gradient-bg($custom-range-thumb-active-bg);
+    }
+  }
+
+  &::-webkit-slider-runnable-track {
+    width: $custom-range-track-width;
+    height: $custom-range-track-height;
+    color: transparent; // Why?
+    cursor: $custom-range-track-cursor;
+    background-color: $custom-range-track-bg;
+    border-color: transparent;
+    @include border-radius($custom-range-track-border-radius);
+    @include box-shadow($custom-range-track-box-shadow);
+  }
+
+  &::-moz-range-thumb {
+    width: $custom-range-thumb-width;
+    height: $custom-range-thumb-height;
+    @include gradient-bg($custom-range-thumb-bg);
+    border: $custom-range-thumb-border;
+    @include border-radius($custom-range-thumb-border-radius);
+    @include box-shadow($custom-range-thumb-box-shadow);
+    @include transition($custom-forms-transition);
+    appearance: none;
+
+    &:active {
+      @include gradient-bg($custom-range-thumb-active-bg);
+    }
+  }
+
+  &::-moz-range-track {
+    width: $custom-range-track-width;
+    height: $custom-range-track-height;
+    color: transparent;
+    cursor: $custom-range-track-cursor;
+    background-color: $custom-range-track-bg;
+    border-color: transparent; // Firefox specific?
+    @include border-radius($custom-range-track-border-radius);
+    @include box-shadow($custom-range-track-box-shadow);
+  }
+
+  &::-ms-thumb {
+    width: $custom-range-thumb-width;
+    height: $custom-range-thumb-height;
+    margin-top: 0; // Edge specific
+    margin-right: $custom-range-thumb-focus-box-shadow-width; // Workaround that overflowed box-shadow is hidden.
+    margin-left: $custom-range-thumb-focus-box-shadow-width;  // Workaround that overflowed box-shadow is hidden.
+    @include gradient-bg($custom-range-thumb-bg);
+    border: $custom-range-thumb-border;
+    @include border-radius($custom-range-thumb-border-radius);
+    @include box-shadow($custom-range-thumb-box-shadow);
+    @include transition($custom-forms-transition);
+    appearance: none;
+
+    &:active {
+      @include gradient-bg($custom-range-thumb-active-bg);
+    }
+  }
+
+  &::-ms-track {
+    width: $custom-range-track-width;
+    height: $custom-range-track-height;
+    color: transparent;
+    cursor: $custom-range-track-cursor;
+    background-color: transparent;
+    border-color: transparent;
+    border-width: $custom-range-thumb-height * .5;
+    @include box-shadow($custom-range-track-box-shadow);
+  }
+
+  &::-ms-fill-lower {
+    background-color: $custom-range-track-bg;
+    @include border-radius($custom-range-track-border-radius);
+  }
+
+  &::-ms-fill-upper {
+    margin-right: 15px; // arbitrary?
+    background-color: $custom-range-track-bg;
+    @include border-radius($custom-range-track-border-radius);
+  }
+
+  &:disabled {
+    &::-webkit-slider-thumb {
+      background-color: $custom-range-thumb-disabled-bg;
+    }
+
+    &::-webkit-slider-runnable-track {
+      cursor: default;
+    }
+
+    &::-moz-range-thumb {
+      background-color: $custom-range-thumb-disabled-bg;
+    }
+
+    &::-moz-range-track {
+      cursor: default;
+    }
+
+    &::-ms-thumb {
+      background-color: $custom-range-thumb-disabled-bg;
+    }
+  }
+}
+
+.custom-control-label::before,
+.custom-file-label,
+.custom-select {
+  @include transition($custom-forms-transition);
+}
diff --git a/src/css/bootstrap/_dropdown.scss b/src/css/bootstrap/_dropdown.scss
index a9d4cfed2..f39de1185 100644
--- a/src/css/bootstrap/_dropdown.scss
+++ b/src/css/bootstrap/_dropdown.scss
@@ -1,12 +1,16 @@
 // The dropdown wrapper (`<div>`)
 .dropup,
-.dropdown {
+.dropright,
+.dropdown,
+.dropleft {
   position: relative;
 }
 
 .dropdown-toggle {
+  white-space: nowrap;
+
   // Generate the caret automatically
-  @include caret;
+  @include caret();
 }
 
 // The dropdown menu
@@ -18,10 +22,10 @@
   display: none; // none by default, but block on "open" of the menu
   float: left;
   min-width: $dropdown-min-width;
-  padding: $dropdown-padding-y 0;
+  padding: $dropdown-padding-y $dropdown-padding-x;
   margin: $dropdown-spacer 0 0; // override default ul
-  font-size: $font-size-base; // Redeclare because nesting can cause inheritance issues
-  color: $body-color;
+  @include font-size($dropdown-font-size);
+  color: $dropdown-color;
   text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer)
   list-style: none;
   background-color: $dropdown-bg;
@@ -31,10 +35,28 @@
   @include box-shadow($dropdown-box-shadow);
 }
 
+@each $breakpoint in map-keys($grid-breakpoints) {
+  @include media-breakpoint-up($breakpoint) {
+    $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+
+    .dropdown-menu#{$infix}-left {
+      right: auto;
+      left: 0;
+    }
+
+    .dropdown-menu#{$infix}-right {
+      right: 0;
+      left: auto;
+    }
+  }
+}
+
 // Allow for dropdowns to go bottom up (aka, dropup-menu)
 // Just add .dropup after the standard .dropdown class and you're set.
 .dropup {
   .dropdown-menu {
+    top: auto;
+    bottom: 100%;
     margin-top: 0;
     margin-bottom: $dropdown-spacer;
   }
@@ -46,6 +68,9 @@
 
 .dropright {
   .dropdown-menu {
+    top: 0;
+    right: auto;
+    left: 100%;
     margin-top: 0;
     margin-left: $dropdown-spacer;
   }
@@ -60,6 +85,9 @@
 
 .dropleft {
   .dropdown-menu {
+    top: 0;
+    right: 100%;
+    left: auto;
     margin-top: 0;
     margin-right: $dropdown-spacer;
   }
@@ -72,9 +100,21 @@
   }
 }
 
+// When Popper is enabled, reset the basic dropdown position
+// stylelint-disable-next-line no-duplicate-selectors
+.dropdown-menu {
+  &[x-placement^="top"],
+  &[x-placement^="right"],
+  &[x-placement^="bottom"],
+  &[x-placement^="left"] {
+    right: auto;
+    bottom: auto;
+  }
+}
+
 // Dividers (basically an `<hr>`) within the dropdown
 .dropdown-divider {
-  @include nav-divider($dropdown-divider-bg);
+  @include nav-divider($dropdown-divider-bg, $dropdown-divider-margin-y, true);
 }
 
 // Links, buttons, and more within the dropdown menu
@@ -88,11 +128,24 @@
   font-weight: $font-weight-normal;
   color: $dropdown-link-color;
   text-align: inherit; // For `<button>`s
+  text-decoration: if($link-decoration == none, null, none);
   white-space: nowrap; // prevent links from randomly breaking onto new lines
   background-color: transparent; // For `<button>`s
   border: 0; // For `<button>`s
 
-  @include hover-focus {
+  // Prevent dropdown overflow if there's no padding
+  // See https://github.com/twbs/bootstrap/pull/27703
+  @if $dropdown-padding-y == 0 {
+    &:first-child {
+      @include border-top-radius($dropdown-inner-border-radius);
+    }
+
+    &:last-child {
+      @include border-bottom-radius($dropdown-inner-border-radius);
+    }
+  }
+
+  @include hover-focus() {
     color: $dropdown-link-hover-color;
     text-decoration: none;
     @include gradient-bg($dropdown-link-hover-bg);
@@ -108,6 +161,7 @@
   &.disabled,
   &:disabled {
     color: $dropdown-link-disabled-color;
+    pointer-events: none;
     background-color: transparent;
     // Remove CSS gradients if they're enabled
     @if $enable-gradients {
@@ -123,9 +177,16 @@
 // Dropdown section headers
 .dropdown-header {
   display: block;
-  padding: $dropdown-padding-y $dropdown-item-padding-x;
+  padding: $dropdown-header-padding;
   margin-bottom: 0; // for use with heading elements
-  font-size: $font-size-sm;
+  @include font-size($font-size-sm);
   color: $dropdown-header-color;
   white-space: nowrap; // as with > li > a
 }
+
+// Dropdown text
+.dropdown-item-text {
+  display: block;
+  padding: $dropdown-item-padding-y $dropdown-item-padding-x;
+  color: $dropdown-link-color;
+}
diff --git a/src/css/bootstrap/_forms.scss b/src/css/bootstrap/_forms.scss
index b0954f8c2..cde466327 100644
--- a/src/css/bootstrap/_forms.scss
+++ b/src/css/bootstrap/_forms.scss
@@ -7,8 +7,11 @@
 .form-control {
   display: block;
   width: 100%;
+  height: $input-height;
   padding: $input-padding-y $input-padding-x;
-  font-size: $font-size-base;
+  font-family: $input-font-family;
+  @include font-size($input-font-size);
+  font-weight: $input-font-weight;
   line-height: $input-line-height;
   color: $input-color;
   background-color: $input-bg;
@@ -16,13 +19,7 @@
   border: $input-border-width solid $input-border-color;
 
   // Note: This has no effect on <select>s in some browsers, due to the limited stylability of `<select>`s in CSS.
-  @if $enable-rounded {
-    // Manually use the if/else instead of the mixin to account for iOS override
-    border-radius: $input-border-radius;
-  } @else {
-    // Otherwise undo the iOS default
-    border-radius: 0;
-  }
+  @include border-radius($input-border-radius, 0);
 
   @include box-shadow($input-box-shadow);
   @include transition($input-transition);
@@ -34,7 +31,7 @@
   }
 
   // Customize the `:focus` state to imitate native WebKit styles.
-  @include form-control-focus();
+  @include form-control-focus($ignore-warning: true);
 
   // Placeholder
   &::placeholder {
@@ -56,9 +53,20 @@
   }
 }
 
+input[type="date"],
+input[type="time"],
+input[type="datetime-local"],
+input[type="month"] {
+  &.form-control {
+    appearance: none; // Fix appearance for date inputs in Safari
+  }
+}
+
 select.form-control {
-  &:not([size]):not([multiple]) {
-    height: $input-height;
+  // Remove select outline from select box in FF
+  &:-moz-focusring {
+    color: transparent;
+    text-shadow: 0 0 0 $input-color;
   }
 
   &:focus::-ms-value {
@@ -87,24 +95,24 @@ select.form-control {
 // For use with horizontal and inline forms, when you need the label (or legend)
 // text to align with the form controls.
 .col-form-label {
-  padding-top: calc(#{$input-padding-y} + #{$input-border-width});
-  padding-bottom: calc(#{$input-padding-y} + #{$input-border-width});
+  padding-top: add($input-padding-y, $input-border-width);
+  padding-bottom: add($input-padding-y, $input-border-width);
   margin-bottom: 0; // Override the `<label>/<legend>` default
-  font-size: inherit; // Override the `<legend>` default
+  @include font-size(inherit); // Override the `<legend>` default
   line-height: $input-line-height;
 }
 
 .col-form-label-lg {
-  padding-top: calc(#{$input-padding-y-lg} + #{$input-border-width});
-  padding-bottom: calc(#{$input-padding-y-lg} + #{$input-border-width});
-  font-size: $font-size-lg;
+  padding-top: add($input-padding-y-lg, $input-border-width);
+  padding-bottom: add($input-padding-y-lg, $input-border-width);
+  @include font-size($input-font-size-lg);
   line-height: $input-line-height-lg;
 }
 
 .col-form-label-sm {
-  padding-top: calc(#{$input-padding-y-sm} + #{$input-border-width});
-  padding-bottom: calc(#{$input-padding-y-sm} + #{$input-border-width});
-  font-size: $font-size-sm;
+  padding-top: add($input-padding-y-sm, $input-border-width);
+  padding-bottom: add($input-padding-y-sm, $input-border-width);
+  @include font-size($input-font-size-sm);
   line-height: $input-line-height-sm;
 }
 
@@ -117,10 +125,11 @@ select.form-control {
 .form-control-plaintext {
   display: block;
   width: 100%;
-  padding-top: $input-padding-y;
-  padding-bottom: $input-padding-y;
+  padding: $input-padding-y 0;
   margin-bottom: 0; // match inputs if this class comes on inputs with default margins
+  @include font-size($input-font-size);
   line-height: $input-line-height;
+  color: $input-plaintext-color;
   background-color: transparent;
   border: solid transparent;
   border-width: $input-border-width 0;
@@ -138,35 +147,35 @@ select.form-control {
 // Build on `.form-control` with modifier classes to decrease or increase the
 // height and font-size of form controls.
 //
-// The `.form-group-* form-control` variations are sadly duplicated to avoid the
-// issue documented in https://github.com/twbs/bootstrap/issues/15074.
+// Repeated in `_input_group.scss` to avoid Sass extend issues.
 
 .form-control-sm {
+  height: $input-height-sm;
   padding: $input-padding-y-sm $input-padding-x-sm;
-  font-size: $font-size-sm;
+  @include font-size($input-font-size-sm);
   line-height: $input-line-height-sm;
   @include border-radius($input-border-radius-sm);
 }
 
-select.form-control-sm {
-  &:not([size]):not([multiple]) {
-    height: $input-height-sm;
-  }
-}
-
 .form-control-lg {
+  height: $input-height-lg;
   padding: $input-padding-y-lg $input-padding-x-lg;
-  font-size: $font-size-lg;
+  @include font-size($input-font-size-lg);
   line-height: $input-line-height-lg;
   @include border-radius($input-border-radius-lg);
 }
 
-select.form-control-lg {
-  &:not([size]):not([multiple]) {
-    height: $input-height-lg;
+// stylelint-disable-next-line no-duplicate-selectors
+select.form-control {
+  &[size],
+  &[multiple] {
+    height: auto;
   }
 }
 
+textarea.form-control {
+  height: auto;
+}
 
 // Form groups
 //
@@ -190,13 +199,13 @@ select.form-control-lg {
 .form-row {
   display: flex;
   flex-wrap: wrap;
-  margin-right: -5px;
-  margin-left: -5px;
+  margin-right: -$form-grid-gutter-width * .5;
+  margin-left: -$form-grid-gutter-width * .5;
 
   > .col,
   > [class*="col-"] {
-    padding-right: 5px;
-    padding-left: 5px;
+    padding-right: $form-grid-gutter-width * .5;
+    padding-left: $form-grid-gutter-width * .5;
   }
 }
 
@@ -216,6 +225,8 @@ select.form-control-lg {
   margin-top: $form-check-input-margin-y;
   margin-left: -$form-check-input-gutter;
 
+  // Use [disabled] and :disabled for workaround https://github.com/twbs/bootstrap/issues/28247
+  &[disabled] ~ .form-check-label,
   &:disabled ~ .form-check-label {
     color: $text-muted;
   }
@@ -248,8 +259,9 @@ select.form-control-lg {
 // pseudo-classes but also includes `.is-invalid` and `.is-valid` classes for
 // server side validation.
 
-@include form-validation-state("valid", $form-feedback-valid-color);
-@include form-validation-state("invalid", $form-feedback-invalid-color);
+@each $state, $data in $form-validation-states {
+  @include form-validation-state($state, map-get($data, color), map-get($data, icon));
+}
 
 // Inline forms
 //
@@ -302,7 +314,8 @@ select.form-control-lg {
       display: inline-block;
     }
 
-    .input-group {
+    .input-group,
+    .custom-select {
       width: auto;
     }
 
@@ -317,6 +330,7 @@ select.form-control-lg {
     }
     .form-check-input {
       position: relative;
+      flex-shrink: 0;
       margin-top: 0;
       margin-right: $form-check-input-margin-x;
       margin-left: 0;
diff --git a/src/css/bootstrap/_functions.scss b/src/css/bootstrap/_functions.scss
index 1266d34bd..13d9de0c1 100644
--- a/src/css/bootstrap/_functions.scss
+++ b/src/css/bootstrap/_functions.scss
@@ -1,6 +1,6 @@
 // Bootstrap functions
 //
-// Utility mixins and functions for evalutating source code across our variables, maps, and mixins.
+// Utility mixins and functions for evaluating source code across our variables, maps, and mixins.
 
 // Ascending
 // Used to evaluate Sass maps like our grid breakpoints.
@@ -8,7 +8,7 @@
   $prev-key: null;
   $prev-num: null;
   @each $key, $num in $map {
-    @if $prev-num == null {
+    @if $prev-num == null or unit($num) == "%" or unit($prev-num) == "%" {
       // Do nothing
     } @else if not comparable($prev-num, $num) {
       @warn "Potentially invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} whose unit makes it incomparable to #{$prev-num}, the value of the previous key '#{$prev-key}' !";
@@ -21,12 +21,14 @@
 }
 
 // Starts at zero
-// Another grid mixin that ensures the min-width of the lowest breakpoint starts at 0.
-@mixin _assert-starts-at-zero($map) {
-  $values: map-values($map);
-  $first-value: nth($values, 1);
-  @if $first-value != 0 {
-    @warn "First breakpoint in `$grid-breakpoints` must start at 0, but starts at #{$first-value}.";
+// Used to ensure the min-width of the lowest breakpoint starts at 0.
+@mixin _assert-starts-at-zero($map, $map-name: "$grid-breakpoints") {
+  @if length($map) > 0 {
+    $values: map-values($map);
+    $first-value: nth($values, 1);
+    @if $first-value != 0 {
+      @warn "First breakpoint in #{$map-name} must start at 0, but starts at #{$first-value}.";
+    }
   }
 }
 
@@ -48,18 +50,37 @@
   @return $string;
 }
 
+// See https://codepen.io/kevinweber/pen/dXWoRw
+//
+// Requires the use of quotes around data URIs.
+
+@function escape-svg($string) {
+  @if str-index($string, "data:image/svg+xml") {
+    @each $char, $encoded in $escaped-characters {
+      // Do not escape the url brackets
+      @if str-index($string, "url(") == 1 {
+        $string: url("#{str-replace(str-slice($string, 6, -3), $char, $encoded)}");
+      } @else {
+        $string: str-replace($string, $char, $encoded);
+      }
+    }
+  }
+
+  @return $string;
+}
+
 // Color contrast
-@function color-yiq($color) {
+@function color-yiq($color, $dark: $yiq-text-dark, $light: $yiq-text-light) {
   $r: red($color);
   $g: green($color);
   $b: blue($color);
 
-  $yiq: (($r * 299) + ($g * 587) + ($b * 114)) / 1000;
+  $yiq: (($r * 299) + ($g * 587) + ($b * 114)) * .001;
 
   @if ($yiq >= $yiq-contrasted-threshold) {
-    @return $yiq-text-dark;
+    @return $dark;
   } @else {
-    @return $yiq-text-light;
+    @return $light;
   }
 }
 
@@ -79,8 +100,91 @@
 // Request a theme color level
 @function theme-color-level($color-name: "primary", $level: 0) {
   $color: theme-color($color-name);
-  $color-base: if($level > 0, #000, #fff);
+  $color-base: if($level > 0, $black, $white);
   $level: abs($level);
 
   @return mix($color-base, $color, $level * $theme-color-interval);
 }
+
+// Return valid calc
+@function add($value1, $value2, $return-calc: true) {
+  @if $value1 == null {
+    @return $value2;
+  }
+
+  @if $value2 == null {
+    @return $value1;
+  }
+
+  @if type-of($value1) == number and type-of($value2) == number and comparable($value1, $value2) {
+    @return $value1 + $value2;
+  }
+
+  @return if($return-calc == true, calc(#{$value1} + #{$value2}), $value1 + unquote(" + ") + $value2);
+}
+
+@function subtract($value1, $value2, $return-calc: true) {
+  @if $value1 == null and $value2 == null {
+    @return null;
+  }
+
+  @if $value1 == null {
+    @return -$value2;
+  }
+
+  @if $value2 == null {
+    @return $value1;
+  }
+
+  @if type-of($value1) == number and type-of($value2) == number and comparable($value1, $value2) {
+    @return $value1 - $value2;
+  }
+
+  @if type-of($value2) != number {
+    $value2: unquote("(") + $value2 + unquote(")");
+  }
+
+  @return if($return-calc == true, calc(#{$value1} - #{$value2}), $value1 + unquote(" - ") + $value2);
+}
+
+@function divide($dividend, $divisor, $precision: 10) {
+  $sign: if($dividend > 0 and $divisor > 0 or $dividend < 0 and $divisor < 0, 1, -1);
+  $dividend: abs($dividend);
+  $divisor: abs($divisor);
+  @if $dividend == 0 {
+    @return 0;
+  }
+  @if $divisor == 0 {
+    @error "Cannot divide by 0";
+  }
+  $remainder: $dividend;
+  $result: 0;
+  $factor: 10;
+  @while ($remainder > 0 and $precision >= 0) {
+    $quotient: 0;
+    @while ($remainder >= $divisor) {
+      $remainder: $remainder - $divisor;
+      $quotient: $quotient + 1;
+    }
+    $result: $result * 10 + $quotient;
+    $factor: $factor * .1;
+    $remainder: $remainder * 10;
+    $precision: $precision - 1;
+    @if ($precision < 0 and $remainder >= $divisor * 5) {
+      $result: $result + 1;
+    }
+  }
+  $result: $result * $factor * $sign;
+  $dividend-unit: unit($dividend);
+  $divisor-unit: unit($divisor);
+  $unit-map: (
+    "px": 1px,
+    "rem": 1rem,
+    "em": 1em,
+    "%": 1%
+  );
+  @if ($dividend-unit != $divisor-unit and map-has-key($unit-map, $dividend-unit)) {
+    $result: $result * map-get($unit-map, $dividend-unit);
+  }
+  @return $result;
+}
diff --git a/src/css/bootstrap/_grid.scss b/src/css/bootstrap/_grid.scss
index a22751537..0bfe5303b 100644
--- a/src/css/bootstrap/_grid.scss
+++ b/src/css/bootstrap/_grid.scss
@@ -3,26 +3,47 @@
 // Set the container width, and override it for fixed navbars in media queries.
 
 @if $enable-grid-classes {
-  .container {
+  // Single container class with breakpoint max-widths
+  .container,
+  // 100% wide container at all breakpoints
+  .container-fluid {
     @include make-container();
-    @include make-container-max-widths();
   }
-}
 
-// Fluid container
-//
-// Utilizes the mixin meant for fixed width containers, but with 100% width for
-// fluid, full width layouts.
+  // Responsive containers that are 100% wide until a breakpoint
+  @each $breakpoint, $container-max-width in $container-max-widths {
+    .container-#{$breakpoint} {
+      @extend .container-fluid;
+    }
 
-@if $enable-grid-classes {
-  .container-fluid {
-    @include make-container();
+    @include media-breakpoint-up($breakpoint, $grid-breakpoints) {
+      %responsive-container-#{$breakpoint} {
+        max-width: $container-max-width;
+      }
+
+      // Extend each breakpoint which is smaller or equal to the current breakpoint
+      $extend-breakpoint: true;
+
+      @each $name, $width in $grid-breakpoints {
+        @if ($extend-breakpoint) {
+          .container#{breakpoint-infix($name, $grid-breakpoints)} {
+            @extend %responsive-container-#{$breakpoint};
+          }
+
+          // Once the current breakpoint is reached, stop extending
+          @if ($breakpoint == $name) {
+            $extend-breakpoint: false;
+          }
+        }
+      }
+    }
   }
 }
 
+
 // Row
 //
-// Rows contain and clear the floats of your columns.
+// Rows contain your columns.
 
 @if $enable-grid-classes {
   .row {
diff --git a/src/css/bootstrap/_images.scss b/src/css/bootstrap/_images.scss
index 2bce02f63..3d6a1014c 100644
--- a/src/css/bootstrap/_images.scss
+++ b/src/css/bootstrap/_images.scss
@@ -6,7 +6,7 @@
 // which weren't expecting the images within themselves to be involuntarily resized.
 // See also https://github.com/twbs/bootstrap/issues/18178
 .img-fluid {
-  @include img-fluid;
+  @include img-fluid();
 }
 
 
@@ -19,7 +19,7 @@
   @include box-shadow($thumbnail-box-shadow);
 
   // Keep them at most 100% wide
-  @include img-fluid;
+  @include img-fluid();
 }
 
 //
@@ -32,11 +32,11 @@
 }
 
 .figure-img {
-  margin-bottom: ($spacer / 2);
+  margin-bottom: $spacer * .5;
   line-height: 1;
 }
 
 .figure-caption {
-  font-size: $figure-caption-font-size;
+  @include font-size($figure-caption-font-size);
   color: $figure-caption-color;
 }
diff --git a/src/css/bootstrap/_input-group.scss b/src/css/bootstrap/_input-group.scss
index 2f7b05d5d..5acedd696 100644
--- a/src/css/bootstrap/_input-group.scss
+++ b/src/css/bootstrap/_input-group.scss
@@ -12,20 +12,15 @@
   width: 100%;
 
   > .form-control,
+  > .form-control-plaintext,
   > .custom-select,
   > .custom-file {
     position: relative; // For focus state's z-index
     flex: 1 1 auto;
-    // Add width 1% and flex-basis auto to ensure that button will not wrap out
-    // the column. Applies to IE Edge+ and Firefox. Chrome does not require this.
     width: 1%;
+    min-width: 0; // https://stackoverflow.com/questions/36247140/why-dont-flex-items-shrink-past-content-size
     margin-bottom: 0;
 
-    // Bring the "active" form control to the top of surrounding elements
-    &:focus {
-      z-index: 3;
-    }
-
     + .form-control,
     + .custom-select,
     + .custom-file {
@@ -33,9 +28,20 @@
     }
   }
 
+  // Bring the "active" form control to the top of surrounding elements
+  > .form-control:focus,
+  > .custom-select:focus,
+  > .custom-file .custom-file-input:focus ~ .custom-file-label {
+    z-index: 3;
+  }
+
+  // Bring the custom file input above the label
+  > .custom-file .custom-file-input:focus {
+    z-index: 4;
+  }
+
   > .form-control,
   > .custom-select {
-    &:not(:last-child) { @include border-right-radius(0); }
     &:not(:first-child) { @include border-left-radius(0); }
   }
 
@@ -46,9 +52,26 @@
     align-items: center;
 
     &:not(:last-child) .custom-file-label,
-    &:not(:last-child) .custom-file-label::before { @include border-right-radius(0); }
-    &:not(:first-child) .custom-file-label,
-    &:not(:first-child) .custom-file-label::before { @include border-left-radius(0); }
+    &:not(:last-child) .custom-file-label::after { @include border-right-radius(0); }
+    &:not(:first-child) .custom-file-label { @include border-left-radius(0); }
+  }
+
+  &:not(.has-validation) {
+    > .form-control:not(:last-child),
+    > .custom-select:not(:last-child),
+    > .custom-file:not(:last-child) .custom-file-label,
+    > .custom-file:not(:last-child) .custom-file-label::after {
+      @include border-right-radius(0);
+    }
+  }
+
+  &.has-validation {
+    > .form-control:nth-last-child(n + 3),
+    > .custom-select:nth-last-child(n + 3),
+    > .custom-file:nth-last-child(n + 3) .custom-file-label,
+    > .custom-file:nth-last-child(n + 3) .custom-file-label::after {
+      @include border-right-radius(0);
+    }
   }
 }
 
@@ -69,6 +92,10 @@
   .btn {
     position: relative;
     z-index: 2;
+
+    &:focus {
+      z-index: 3;
+    }
   }
 
   .btn + .btn,
@@ -93,7 +120,7 @@
   align-items: center;
   padding: $input-padding-y $input-padding-x;
   margin-bottom: 0; // Allow use of <label> elements by overriding our default margin-bottom
-  font-size: $font-size-base; // Match inputs
+  @include font-size($input-font-size); // Match inputs
   font-weight: $font-weight-normal;
   line-height: $input-line-height;
   color: $input-group-addon-color;
@@ -116,20 +143,43 @@
 // Remix the default form control sizing classes into new ones for easier
 // manipulation.
 
+.input-group-lg > .form-control:not(textarea),
+.input-group-lg > .custom-select {
+  height: $input-height-lg;
+}
+
 .input-group-lg > .form-control,
+.input-group-lg > .custom-select,
 .input-group-lg > .input-group-prepend > .input-group-text,
 .input-group-lg > .input-group-append > .input-group-text,
 .input-group-lg > .input-group-prepend > .btn,
 .input-group-lg > .input-group-append > .btn {
-  @extend .form-control-lg;
+  padding: $input-padding-y-lg $input-padding-x-lg;
+  @include font-size($input-font-size-lg);
+  line-height: $input-line-height-lg;
+  @include border-radius($input-border-radius-lg);
+}
+
+.input-group-sm > .form-control:not(textarea),
+.input-group-sm > .custom-select {
+  height: $input-height-sm;
 }
 
 .input-group-sm > .form-control,
+.input-group-sm > .custom-select,
 .input-group-sm > .input-group-prepend > .input-group-text,
 .input-group-sm > .input-group-append > .input-group-text,
 .input-group-sm > .input-group-prepend > .btn,
 .input-group-sm > .input-group-append > .btn {
-  @extend .form-control-sm;
+  padding: $input-padding-y-sm $input-padding-x-sm;
+  @include font-size($input-font-size-sm);
+  line-height: $input-line-height-sm;
+  @include border-radius($input-border-radius-sm);
+}
+
+.input-group-lg > .custom-select,
+.input-group-sm > .custom-select {
+  padding-right: $custom-select-padding-x + $custom-select-indicator-padding;
 }
 
 
@@ -142,8 +192,10 @@
 
 .input-group > .input-group-prepend > .btn,
 .input-group > .input-group-prepend > .input-group-text,
-.input-group > .input-group-append:not(:last-child) > .btn,
-.input-group > .input-group-append:not(:last-child) > .input-group-text,
+.input-group:not(.has-validation) > .input-group-append:not(:last-child) > .btn,
+.input-group:not(.has-validation) > .input-group-append:not(:last-child) > .input-group-text,
+.input-group.has-validation > .input-group-append:nth-last-child(n + 3) > .btn,
+.input-group.has-validation > .input-group-append:nth-last-child(n + 3) > .input-group-text,
 .input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle),
 .input-group > .input-group-append:last-child > .input-group-text:not(:last-child) {
   @include border-right-radius(0);
diff --git a/src/css/bootstrap/_jumbotron.scss b/src/css/bootstrap/_jumbotron.scss
index 7966bba1e..2c617edb5 100644
--- a/src/css/bootstrap/_jumbotron.scss
+++ b/src/css/bootstrap/_jumbotron.scss
@@ -1,6 +1,7 @@
 .jumbotron {
-  padding: $jumbotron-padding ($jumbotron-padding / 2);
+  padding: $jumbotron-padding ($jumbotron-padding * .5);
   margin-bottom: $jumbotron-padding;
+  color: $jumbotron-color;
   background-color: $jumbotron-bg;
   @include border-radius($border-radius-lg);
 
diff --git a/src/css/bootstrap/_list-group.scss b/src/css/bootstrap/_list-group.scss
index 9f145c1d3..ec25ba8a1 100644
--- a/src/css/bootstrap/_list-group.scss
+++ b/src/css/bootstrap/_list-group.scss
@@ -9,6 +9,7 @@
   // No need to set list-style: none; since .list-group-item is block level
   padding-left: 0; // reset padding because ul and ol
   margin-bottom: 0;
+  @include border-radius($list-group-border-radius);
 }
 
 
@@ -23,7 +24,8 @@
   text-align: inherit; // For `<button>`s (anchors inherit)
 
   // Hover state
-  @include hover-focus {
+  @include hover-focus() {
+    z-index: 1; // Place hover/focus items above their siblings for proper border styling
     color: $list-group-action-hover-color;
     text-decoration: none;
     background-color: $list-group-hover-bg;
@@ -44,28 +46,23 @@
   position: relative;
   display: block;
   padding: $list-group-item-padding-y $list-group-item-padding-x;
-  // Place the border on the list items and negative margin up for better styling
-  margin-bottom: -$list-group-border-width;
+  color: $list-group-color;
+  text-decoration: if($link-decoration == none, null, none);
   background-color: $list-group-bg;
   border: $list-group-border-width solid $list-group-border-color;
 
   &:first-child {
-    @include border-top-radius($list-group-border-radius);
+    @include border-top-radius(inherit);
   }
 
   &:last-child {
-    margin-bottom: 0;
-    @include border-bottom-radius($list-group-border-radius);
-  }
-
-  @include hover-focus {
-    z-index: 1; // Place hover/active items above their siblings for proper border styling
-    text-decoration: none;
+    @include border-bottom-radius(inherit);
   }
 
   &.disabled,
   &:disabled {
     color: $list-group-disabled-color;
+    pointer-events: none;
     background-color: $list-group-disabled-bg;
   }
 
@@ -76,6 +73,56 @@
     background-color: $list-group-active-bg;
     border-color: $list-group-active-border-color;
   }
+
+  & + & {
+    border-top-width: 0;
+
+    &.active {
+      margin-top: -$list-group-border-width;
+      border-top-width: $list-group-border-width;
+    }
+  }
+}
+
+
+// Horizontal
+//
+// Change the layout of list group items from vertical (default) to horizontal.
+
+@each $breakpoint in map-keys($grid-breakpoints) {
+  @include media-breakpoint-up($breakpoint) {
+    $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+
+    .list-group-horizontal#{$infix} {
+      flex-direction: row;
+
+      > .list-group-item {
+        &:first-child {
+          @include border-bottom-left-radius($list-group-border-radius);
+          @include border-top-right-radius(0);
+        }
+
+        &:last-child {
+          @include border-top-right-radius($list-group-border-radius);
+          @include border-bottom-left-radius(0);
+        }
+
+        &.active {
+          margin-top: 0;
+        }
+
+        + .list-group-item {
+          border-top-width: $list-group-border-width;
+          border-left-width: 0;
+
+          &.active {
+            margin-left: -$list-group-border-width;
+            border-left-width: $list-group-border-width;
+          }
+        }
+      }
+    }
+  }
 }
 
 
@@ -85,21 +132,13 @@
 // useful within other components (e.g., cards).
 
 .list-group-flush {
-  .list-group-item {
-    border-right: 0;
-    border-left: 0;
-    @include border-radius(0);
-  }
+  @include border-radius(0);
 
-  &:first-child {
-    .list-group-item:first-child {
-      border-top: 0;
-    }
-  }
+  > .list-group-item {
+    border-width: 0 0 $list-group-border-width;
 
-  &:last-child {
-    .list-group-item:last-child {
-      border-bottom: 0;
+    &:last-child {
+      border-bottom-width: 0;
     }
   }
 }
diff --git a/src/css/bootstrap/_mixins.scss b/src/css/bootstrap/_mixins.scss
index d9a1774b7..7e7a23d25 100644
--- a/src/css/bootstrap/_mixins.scss
+++ b/src/css/bootstrap/_mixins.scss
@@ -2,6 +2,12 @@
 //
 // Used in conjunction with global variables to enable certain theme features.
 
+// Vendor
+@import "vendor/rfs";
+
+// Deprecate
+@import "mixins/deprecate";
+
 // Utilities
 @import "mixins/breakpoints";
 @import "mixins/hover";
@@ -16,7 +22,7 @@
 @import "mixins/text-truncate";
 @import "mixins/visibility";
 
-// // Components
+// Components
 @import "mixins/alert";
 @import "mixins/buttons";
 @import "mixins/caret";
@@ -27,16 +33,15 @@
 @import "mixins/forms";
 @import "mixins/table-row";
 
-// // Skins
+// Skins
 @import "mixins/background-variant";
 @import "mixins/border-radius";
 @import "mixins/box-shadow";
 @import "mixins/gradients";
 @import "mixins/transition";
 
-// // Layout
+// Layout
 @import "mixins/clearfix";
-// @import "mixins/navbar-align";
 @import "mixins/grid-framework";
 @import "mixins/grid";
 @import "mixins/float";
diff --git a/src/css/bootstrap/_modal.scss b/src/css/bootstrap/_modal.scss
index edda83663..4d01788d7 100644
--- a/src/css/bootstrap/_modal.scss
+++ b/src/css/bootstrap/_modal.scss
@@ -4,20 +4,25 @@
 // .modal-content   - actual modal w/ bg and corners and stuff
 
 
-// Kill the scroll on the body
 .modal-open {
+  // Kill the scroll on the body
   overflow: hidden;
+
+  .modal {
+    overflow-x: hidden;
+    overflow-y: auto;
+  }
 }
 
 // Container that the modal scrolls within
 .modal {
   position: fixed;
   top: 0;
-  right: 0;
-  bottom: 0;
   left: 0;
   z-index: $zindex-modal;
   display: none;
+  width: 100%;
+  height: 100%;
   overflow: hidden;
   // Prevent Chrome on Windows from adding a focus outline. For details, see
   // https://github.com/twbs/bootstrap/pull/10951.
@@ -25,11 +30,6 @@
   // We deliberately don't use `-webkit-overflow-scrolling: touch;` due to a
   // gnarly iOS Safari bug: https://bugs.webkit.org/show_bug.cgi?id=158342
   // See also https://github.com/twbs/bootstrap/issues/17695
-
-  .modal-open & {
-    overflow-x: hidden;
-    overflow-y: auto;
-  }
 }
 
 // Shell div to position the modal with bottom padding
@@ -43,17 +43,64 @@
   // When fading in the modal, animate it to slide down
   .modal.fade & {
     @include transition($modal-transition);
-    transform: translate(0, -25%);
+    transform: $modal-fade-transform;
   }
   .modal.show & {
-    transform: translate(0, 0);
+    transform: $modal-show-transform;
+  }
+
+  // When trying to close, animate focus to scale
+  .modal.modal-static & {
+    transform: $modal-scale-transform;
+  }
+}
+
+.modal-dialog-scrollable {
+  display: flex; // IE10/11
+  max-height: subtract(100%, $modal-dialog-margin * 2);
+
+  .modal-content {
+    max-height: subtract(100vh, $modal-dialog-margin * 2); // IE10/11
+    overflow: hidden;
+  }
+
+  .modal-header,
+  .modal-footer {
+    flex-shrink: 0;
+  }
+
+  .modal-body {
+    overflow-y: auto;
   }
 }
 
 .modal-dialog-centered {
   display: flex;
   align-items: center;
-  min-height: calc(100% - (#{$modal-dialog-margin} * 2));
+  min-height: subtract(100%, $modal-dialog-margin * 2);
+
+  // Ensure `modal-dialog-centered` extends the full height of the view (IE10/11)
+  &::before {
+    display: block; // IE10
+    height: subtract(100vh, $modal-dialog-margin * 2);
+    height: min-content; // Reset height to 0 except on IE
+    content: "";
+  }
+
+  // Ensure `.modal-body` shows scrollbar (IE10/11)
+  &.modal-dialog-scrollable {
+    flex-direction: column;
+    justify-content: center;
+    height: 100%;
+
+    .modal-content {
+      max-height: none;
+    }
+
+    &::before {
+      content: none;
+    }
+  }
 }
 
 // Actual modal
@@ -63,11 +110,12 @@
   flex-direction: column;
   width: 100%; // Ensure `.modal-content` extends the full width of the parent `.modal-dialog`
   // counteract the pointer-events: none; in the .modal-dialog
+  color: $modal-content-color;
   pointer-events: auto;
   background-color: $modal-content-bg;
   background-clip: padding-box;
   border: $modal-content-border-width solid $modal-content-border-color;
-  @include border-radius($border-radius-lg);
+  @include border-radius($modal-content-border-radius);
   @include box-shadow($modal-content-box-shadow-xs);
   // Remove focus outline from opened modal
   outline: 0;
@@ -77,10 +125,10 @@
 .modal-backdrop {
   position: fixed;
   top: 0;
-  right: 0;
-  bottom: 0;
   left: 0;
   z-index: $zindex-modal-backdrop;
+  width: 100vw;
+  height: 100vh;
   background-color: $modal-backdrop-bg;
 
   // Fade for backdrop
@@ -96,12 +144,12 @@
   justify-content: space-between; // Put modal header elements (title and dismiss) on opposite ends
   padding: $modal-header-padding;
   border-bottom: $modal-header-border-width solid $modal-header-border-color;
-  @include border-top-radius($border-radius-lg);
+  @include border-top-radius($modal-content-inner-border-radius);
 
   .close {
     padding: $modal-header-padding;
     // auto on the left force icon to the right even when there is no .modal-title
-    margin: (-$modal-header-padding) (-$modal-header-padding) (-$modal-header-padding) auto;
+    margin: (-$modal-header-padding-y) (-$modal-header-padding-x) (-$modal-header-padding-y) auto;
   }
 }
 
@@ -116,7 +164,7 @@
 .modal-body {
   position: relative;
   // Enable `flex-grow: 1` so that the body take up as much space as possible
-  // when should there be a fixed height on `.modal-dialog`.
+  // when there should be a fixed height on `.modal-dialog`.
   flex: 1 1 auto;
   padding: $modal-inner-padding;
 }
@@ -124,14 +172,19 @@
 // Footer (for actions)
 .modal-footer {
   display: flex;
+  flex-wrap: wrap;
   align-items: center; // vertically center
   justify-content: flex-end; // Right align buttons with flex property because text-align doesn't work on flex items
-  padding: $modal-inner-padding;
+  padding: $modal-inner-padding - $modal-footer-margin-between * .5;
   border-top: $modal-footer-border-width solid $modal-footer-border-color;
+  @include border-bottom-radius($modal-content-inner-border-radius);
 
-  // Easily place margin between footer elements
-  > :not(:first-child) { margin-left: .25rem; }
-  > :not(:last-child) { margin-right: .25rem; }
+  // Place margin between footer elements
+  // This solution is far from ideal because of the universal selector usage,
+  // but is needed to fix https://github.com/twbs/bootstrap/issues/24800
+  > * {
+    margin: $modal-footer-margin-between * .5;
+  }
 }
 
 // Measure scrollbar width for padding body during modal show/hide
@@ -151,8 +204,21 @@
     margin: $modal-dialog-margin-y-sm-up auto;
   }
 
+  .modal-dialog-scrollable {
+    max-height: subtract(100%, $modal-dialog-margin-y-sm-up * 2);
+
+    .modal-content {
+      max-height: subtract(100vh, $modal-dialog-margin-y-sm-up * 2);
+    }
+  }
+
   .modal-dialog-centered {
-    min-height: calc(100% - (#{$modal-dialog-margin-y-sm-up} * 2));
+    min-height: subtract(100%, $modal-dialog-margin-y-sm-up * 2);
+
+    &::before {
+      height: subtract(100vh, $modal-dialog-margin-y-sm-up * 2);
+      height: min-content;
+    }
   }
 
   .modal-content {
@@ -160,9 +226,15 @@
   }
 
   .modal-sm { max-width: $modal-sm; }
-
 }
 
 @include media-breakpoint-up(lg) {
-  .modal-lg { max-width: $modal-lg; }
+  .modal-lg,
+  .modal-xl {
+    max-width: $modal-lg;
+  }
+}
+
+@include media-breakpoint-up(xl) {
+  .modal-xl { max-width: $modal-xl; }
 }
diff --git a/src/css/bootstrap/_nav.scss b/src/css/bootstrap/_nav.scss
index fc82161e3..7626f9306 100644
--- a/src/css/bootstrap/_nav.scss
+++ b/src/css/bootstrap/_nav.scss
@@ -1,7 +1,7 @@
 // Base class
 //
 // Kickstart any navigation component with a set of style resets. Works with
-// `<nav>`s or `<ul>`s.
+// `<nav>`s, `<ul>`s or `<ol>`s.
 
 .nav {
   display: flex;
@@ -14,14 +14,17 @@
 .nav-link {
   display: block;
   padding: $nav-link-padding-y $nav-link-padding-x;
+  text-decoration: if($link-decoration == none, null, none);
 
-  @include hover-focus {
+  @include hover-focus() {
     text-decoration: none;
   }
 
   // Disabled state lightens text
   &.disabled {
     color: $nav-link-disabled-color;
+    pointer-events: none;
+    cursor: default;
   }
 }
 
@@ -32,15 +35,15 @@
 .nav-tabs {
   border-bottom: $nav-tabs-border-width solid $nav-tabs-border-color;
 
-  .nav-item {
-    margin-bottom: -$nav-tabs-border-width;
-  }
-
   .nav-link {
+    margin-bottom: -$nav-tabs-border-width;
+    background-color: transparent;
     border: $nav-tabs-border-width solid transparent;
     @include border-top-radius($nav-tabs-border-radius);
 
-    @include hover-focus {
+    @include hover-focus() {
+      // Prevents active .nav-link tab overlapping focus outline of previous/next .nav-link
+      isolation: isolate;
       border-color: $nav-tabs-link-hover-border-color;
     }
 
@@ -73,6 +76,8 @@
 
 .nav-pills {
   .nav-link {
+    background: none;
+    border: 0;
     @include border-radius($nav-pills-border-radius);
   }
 
@@ -89,6 +94,7 @@
 //
 
 .nav-fill {
+  > .nav-link,
   .nav-item {
     flex: 1 1 auto;
     text-align: center;
@@ -96,6 +102,7 @@
 }
 
 .nav-justified {
+  > .nav-link,
   .nav-item {
     flex-basis: 0;
     flex-grow: 1;
diff --git a/src/css/bootstrap/_navbar.scss b/src/css/bootstrap/_navbar.scss
index 8d68c6c8c..cf5b66790 100644
--- a/src/css/bootstrap/_navbar.scss
+++ b/src/css/bootstrap/_navbar.scss
@@ -24,14 +24,24 @@
   padding: $navbar-padding-y $navbar-padding-x;
 
   // Because flex properties aren't inherited, we need to redeclare these first
-  // few properities so that content nested within behave properly.
-  > .container,
-  > .container-fluid {
+  // few properties so that content nested within behave properly.
+  %container-flex-properties {
     display: flex;
     flex-wrap: wrap;
     align-items: center;
     justify-content: space-between;
   }
+
+  .container,
+  .container-fluid {
+    @extend %container-flex-properties;
+  }
+
+  @each $breakpoint, $container-max-width in $container-max-widths {
+    > .container#{breakpoint-infix($breakpoint, $container-max-widths)} {
+      @extend %container-flex-properties;
+    }
+  }
 }
 
 
@@ -44,11 +54,11 @@
   padding-top: $navbar-brand-padding-y;
   padding-bottom: $navbar-brand-padding-y;
   margin-right: $navbar-padding-x;
-  font-size: $navbar-brand-font-size;
+  @include font-size($navbar-brand-font-size);
   line-height: inherit;
   white-space: nowrap;
 
-  @include hover-focus {
+  @include hover-focus() {
     text-decoration: none;
   }
 }
@@ -94,7 +104,7 @@
 // Powered by the collapse Bootstrap JavaScript plugin.
 
 // When collapsed, prevent the toggleable navbar contents from appearing in
-// the default flexbox row orienation. Requires the use of `flex-wrap: wrap`
+// the default flexbox row orientation. Requires the use of `flex-wrap: wrap`
 // on the `.navbar` parent.
 .navbar-collapse {
   flex-basis: 100%;
@@ -107,20 +117,15 @@
 // Button for toggling the navbar when in its collapsed state
 .navbar-toggler {
   padding: $navbar-toggler-padding-y $navbar-toggler-padding-x;
-  font-size: $navbar-toggler-font-size;
+  @include font-size($navbar-toggler-font-size);
   line-height: 1;
   background-color: transparent; // remove default button style
   border: $border-width solid transparent; // remove default button style
   @include border-radius($navbar-toggler-border-radius);
 
-  @include hover-focus {
+  @include hover-focus() {
     text-decoration: none;
   }
-
-  // Opinionated: add "hand" cursor to non-disabled .navbar-toggler elements
-  &:not(:disabled):not(.disabled) {
-    cursor: pointer;
-  }
 }
 
 // Keep as a separate element so folks can easily override it with another icon
@@ -131,8 +136,12 @@
   height: 1.5em;
   vertical-align: middle;
   content: "";
-  background: no-repeat center center;
-  background-size: 100% 100%;
+  background: 50% / 100% 100% no-repeat;
+}
+
+.navbar-nav-scroll {
+  max-height: $navbar-nav-scroll-max-height;
+  overflow-y: auto;
 }
 
 // Generate series of `.navbar-expand-*` responsive classes for configuring
@@ -144,11 +153,21 @@
 
     &#{$infix} {
       @include media-breakpoint-down($breakpoint) {
-        > .container,
-        > .container-fluid {
+        %container-navbar-expand-#{$breakpoint} {
           padding-right: 0;
           padding-left: 0;
         }
+
+        > .container,
+        > .container-fluid {
+          @extend %container-navbar-expand-#{$breakpoint};
+        }
+
+        @each $size, $container-max-width in $container-max-widths {
+          > .container#{breakpoint-infix($size, $container-max-widths)} {
+            @extend %container-navbar-expand-#{$breakpoint};
+          }
+        }
       }
 
       @include media-breakpoint-up($next) {
@@ -162,11 +181,6 @@
             position: absolute;
           }
 
-          .dropdown-menu-right {
-            right: 0;
-            left: auto; // Reset the default from `.dropdown-menu`
-          }
-
           .nav-link {
             padding-right: $navbar-nav-link-padding-x;
             padding-left: $navbar-nav-link-padding-x;
@@ -174,13 +188,27 @@
         }
 
         // For nesting containers, have to redeclare for alignment purposes
+        %container-nesting-#{$breakpoint} {
+          flex-wrap: nowrap;
+        }
+
         > .container,
         > .container-fluid {
-          flex-wrap: nowrap;
+          @extend %container-nesting-#{$breakpoint};
+        }
+
+        @each $size, $container-max-width in $container-max-widths {
+          > .container#{breakpoint-infix($size, $container-max-widths)} {
+            @extend %container-nesting-#{$breakpoint};
+          }
+        }
+
+        .navbar-nav-scroll {
+          overflow: visible;
         }
 
         .navbar-collapse {
-          display: flex !important;  // stylelint-disable-line declaration-no-important
+          display: flex !important; // stylelint-disable-line declaration-no-important
 
           // Changes flex-bases to auto because of an IE10 bug
           flex-basis: auto;
@@ -189,13 +217,6 @@
         .navbar-toggler {
           display: none;
         }
-
-        .dropup {
-          .dropdown-menu {
-            top: auto;
-            bottom: 100%;
-          }
-        }
       }
     }
   }
@@ -209,10 +230,10 @@
 // Dark links against a light background
 .navbar-light {
   .navbar-brand {
-    color: $navbar-light-active-color;
+    color: $navbar-light-brand-color;
 
-    @include hover-focus {
-      color: $navbar-light-active-color;
+    @include hover-focus() {
+      color: $navbar-light-brand-hover-color;
     }
   }
 
@@ -220,7 +241,7 @@
     .nav-link {
       color: $navbar-light-color;
 
-      @include hover-focus {
+      @include hover-focus() {
         color: $navbar-light-hover-color;
       }
 
@@ -243,7 +264,7 @@
   }
 
   .navbar-toggler-icon {
-    background-image: $navbar-light-toggler-icon-bg;
+    background-image: escape-svg($navbar-light-toggler-icon-bg);
   }
 
   .navbar-text {
@@ -251,7 +272,7 @@
     a {
       color: $navbar-light-active-color;
 
-      @include hover-focus {
+      @include hover-focus() {
         color: $navbar-light-active-color;
       }
     }
@@ -261,10 +282,10 @@
 // White links against a dark background
 .navbar-dark {
   .navbar-brand {
-    color: $navbar-dark-active-color;
+    color: $navbar-dark-brand-color;
 
-    @include hover-focus {
-      color: $navbar-dark-active-color;
+    @include hover-focus() {
+      color: $navbar-dark-brand-hover-color;
     }
   }
 
@@ -272,7 +293,7 @@
     .nav-link {
       color: $navbar-dark-color;
 
-      @include hover-focus {
+      @include hover-focus() {
         color: $navbar-dark-hover-color;
       }
 
@@ -295,7 +316,7 @@
   }
 
   .navbar-toggler-icon {
-    background-image: $navbar-dark-toggler-icon-bg;
+    background-image: escape-svg($navbar-dark-toggler-icon-bg);
   }
 
   .navbar-text {
@@ -303,7 +324,7 @@
     a {
       color: $navbar-dark-active-color;
 
-      @include hover-focus {
+      @include hover-focus() {
         color: $navbar-dark-active-color;
       }
     }
diff --git a/src/css/bootstrap/_pagination.scss b/src/css/bootstrap/_pagination.scss
index 959b2eb4b..9313cc547 100644
--- a/src/css/bootstrap/_pagination.scss
+++ b/src/css/bootstrap/_pagination.scss
@@ -11,10 +11,12 @@
   margin-left: -$pagination-border-width;
   line-height: $pagination-line-height;
   color: $pagination-color;
+  text-decoration: if($link-decoration == none, null, none);
   background-color: $pagination-bg;
   border: $pagination-border-width solid $pagination-border-color;
 
   &:hover {
+    z-index: 2;
     color: $pagination-hover-color;
     text-decoration: none;
     background-color: $pagination-hover-bg;
@@ -22,15 +24,10 @@
   }
 
   &:focus {
-    z-index: 2;
-    outline: 0;
+    z-index: 3;
+    outline: $pagination-focus-outline;
     box-shadow: $pagination-focus-box-shadow;
   }
-
-  // Opinionated: add "hand" cursor to non-disabled .page-link elements
-  &:not(:disabled):not(.disabled) {
-    cursor: pointer;
-  }
 }
 
 .page-item {
@@ -47,7 +44,7 @@
   }
 
   &.active .page-link {
-    z-index: 1;
+    z-index: 3;
     color: $pagination-active-color;
     background-color: $pagination-active-bg;
     border-color: $pagination-active-border-color;
@@ -69,9 +66,9 @@
 //
 
 .pagination-lg {
-  @include pagination-size($pagination-padding-y-lg, $pagination-padding-x-lg, $font-size-lg, $line-height-lg, $border-radius-lg);
+  @include pagination-size($pagination-padding-y-lg, $pagination-padding-x-lg, $font-size-lg, $line-height-lg, $pagination-border-radius-lg);
 }
 
 .pagination-sm {
-  @include pagination-size($pagination-padding-y-sm, $pagination-padding-x-sm, $font-size-sm, $line-height-sm, $border-radius-sm);
+  @include pagination-size($pagination-padding-y-sm, $pagination-padding-x-sm, $font-size-sm, $line-height-sm, $pagination-border-radius-sm);
 }
diff --git a/src/css/bootstrap/_popover.scss b/src/css/bootstrap/_popover.scss
index 3ef5f628b..c7106a809 100644
--- a/src/css/bootstrap/_popover.scss
+++ b/src/css/bootstrap/_popover.scss
@@ -8,7 +8,7 @@
   // Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element.
   // So reset our font and text properties to avoid inheriting weird values.
   @include reset-text();
-  font-size: $popover-font-size;
+  @include font-size($popover-font-size);
   // Allow breaking very long words so they don't overflow the popover's bounds
   word-wrap: break-word;
   background-color: $popover-bg;
@@ -22,7 +22,7 @@
     display: block;
     width: $popover-arrow-width;
     height: $popover-arrow-height;
-    margin: 0 $border-radius-lg;
+    margin: 0 $popover-border-radius;
 
     &::before,
     &::after {
@@ -38,72 +38,63 @@
 .bs-popover-top {
   margin-bottom: $popover-arrow-height;
 
-  .arrow {
-    bottom: calc((#{$popover-arrow-height} + #{$popover-border-width}) * -1);
-  }
+  > .arrow {
+    bottom: subtract(-$popover-arrow-height, $popover-border-width);
 
-  .arrow::before,
-  .arrow::after {
-    border-width: $popover-arrow-height ($popover-arrow-width / 2) 0;
-  }
-
-  .arrow::before {
-    bottom: 0;
-    border-top-color: $popover-arrow-outer-color;
-  }
+    &::before {
+      bottom: 0;
+      border-width: $popover-arrow-height ($popover-arrow-width * .5) 0;
+      border-top-color: $popover-arrow-outer-color;
+    }
 
-  .arrow::after {
-    bottom: $popover-border-width;
-    border-top-color: $popover-arrow-color;
+    &::after {
+      bottom: $popover-border-width;
+      border-width: $popover-arrow-height ($popover-arrow-width * .5) 0;
+      border-top-color: $popover-arrow-color;
+    }
   }
 }
 
 .bs-popover-right {
   margin-left: $popover-arrow-height;
 
-  .arrow {
-    left: calc((#{$popover-arrow-height} + #{$popover-border-width}) * -1);
+  > .arrow {
+    left: subtract(-$popover-arrow-height, $popover-border-width);
     width: $popover-arrow-height;
     height: $popover-arrow-width;
-    margin: $border-radius-lg 0; // make sure the arrow does not touch the popover's rounded corners
-  }
-
-  .arrow::before,
-  .arrow::after {
-    border-width: ($popover-arrow-width / 2) $popover-arrow-height ($popover-arrow-width / 2) 0;
-  }
+    margin: $popover-border-radius 0; // make sure the arrow does not touch the popover's rounded corners
 
-  .arrow::before {
-    left: 0;
-    border-right-color: $popover-arrow-outer-color;
-  }
+    &::before {
+      left: 0;
+      border-width: ($popover-arrow-width * .5) $popover-arrow-height ($popover-arrow-width * .5) 0;
+      border-right-color: $popover-arrow-outer-color;
+    }
 
-  .arrow::after {
-    left: $popover-border-width;
-    border-right-color: $popover-arrow-color;
+    &::after {
+      left: $popover-border-width;
+      border-width: ($popover-arrow-width * .5) $popover-arrow-height ($popover-arrow-width * .5) 0;
+      border-right-color: $popover-arrow-color;
+    }
   }
 }
 
 .bs-popover-bottom {
   margin-top: $popover-arrow-height;
 
-  .arrow {
-    top: calc((#{$popover-arrow-height} + #{$popover-border-width}) * -1);
-  }
-
-  .arrow::before,
-  .arrow::after {
-    border-width: 0 ($popover-arrow-width / 2) $popover-arrow-height ($popover-arrow-width / 2);
-  }
+  > .arrow {
+    top: subtract(-$popover-arrow-height, $popover-border-width);
 
-  .arrow::before {
-    top: 0;
-    border-bottom-color: $popover-arrow-outer-color;
-  }
+    &::before {
+      top: 0;
+      border-width: 0 ($popover-arrow-width * .5) $popover-arrow-height ($popover-arrow-width * .5);
+      border-bottom-color: $popover-arrow-outer-color;
+    }
 
-  .arrow::after {
-    top: $popover-border-width;
-    border-bottom-color: $popover-arrow-color;
+    &::after {
+      top: $popover-border-width;
+      border-width: 0 ($popover-arrow-width * .5) $popover-arrow-height ($popover-arrow-width * .5);
+      border-bottom-color: $popover-arrow-color;
+    }
   }
 
   // This will remove the popover-header's border just below the arrow
@@ -113,7 +104,7 @@
     left: 50%;
     display: block;
     width: $popover-arrow-width;
-    margin-left: ($popover-arrow-width / -2);
+    margin-left: -$popover-arrow-width * .5;
     content: "";
     border-bottom: $popover-border-width solid $popover-header-bg;
   }
@@ -122,26 +113,23 @@
 .bs-popover-left {
   margin-right: $popover-arrow-height;
 
-  .arrow {
-    right: calc((#{$popover-arrow-height} + #{$popover-border-width}) * -1);
+  > .arrow {
+    right: subtract(-$popover-arrow-height, $popover-border-width);
     width: $popover-arrow-height;
     height: $popover-arrow-width;
-    margin: $border-radius-lg 0; // make sure the arrow does not touch the popover's rounded corners
-  }
-
-  .arrow::before,
-  .arrow::after {
-    border-width: ($popover-arrow-width / 2) 0 ($popover-arrow-width / 2) $popover-arrow-height;
-  }
+    margin: $popover-border-radius 0; // make sure the arrow does not touch the popover's rounded corners
 
-  .arrow::before {
-    right: 0;
-    border-left-color: $popover-arrow-outer-color;
-  }
+    &::before {
+      right: 0;
+      border-width: ($popover-arrow-width * .5) 0 ($popover-arrow-width * .5) $popover-arrow-height;
+      border-left-color: $popover-arrow-outer-color;
+    }
 
-  .arrow::after {
-    right: $popover-border-width;
-    border-left-color: $popover-arrow-color;
+    &::after {
+      right: $popover-border-width;
+      border-width: ($popover-arrow-width * .5) 0 ($popover-arrow-width * .5) $popover-arrow-height;
+      border-left-color: $popover-arrow-color;
+    }
   }
 }
 
@@ -165,12 +153,11 @@
 .popover-header {
   padding: $popover-header-padding-y $popover-header-padding-x;
   margin-bottom: 0; // Reset the default from Reboot
-  font-size: $font-size-base;
+  @include font-size($font-size-base);
   color: $popover-header-color;
   background-color: $popover-header-bg;
   border-bottom: $popover-border-width solid darken($popover-header-bg, 5%);
-  $offset-border-width: calc(#{$border-radius-lg} - #{$popover-border-width});
-  @include border-top-radius($offset-border-width);
+  @include border-top-radius($popover-inner-border-radius);
 
   &:empty {
     display: none;
diff --git a/src/css/bootstrap/_print.scss b/src/css/bootstrap/_print.scss
index 5e2ce3a8a..915298aaa 100644
--- a/src/css/bootstrap/_print.scss
+++ b/src/css/bootstrap/_print.scss
@@ -1,11 +1,11 @@
 // stylelint-disable declaration-no-important, selector-no-qualifying-type
 
-// Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css
+// Source: https://github.com/h5bp/main.css/blob/master/src/_print.css
 
 // ==========================================================================
 // Print styles.
 // Inlined to avoid the additional HTTP request:
-// http://www.phpied.com/delay-loading-your-print-css/
+// https://www.phpied.com/delay-loading-your-print-css/
 // ==========================================================================
 
 @if $enable-print-styles {
@@ -14,7 +14,7 @@
     *::before,
     *::after {
       // Bootstrap specific; comment out `color` and `background`
-      //color: #000 !important; // Black prints faster: http://www.sanbeiji.com/archives/953
+      //color: $black !important; // Black prints faster
       text-shadow: none !important;
       //background: transparent !important;
       box-shadow: none !important;
@@ -51,19 +51,10 @@
     }
     pre,
     blockquote {
-      border: $border-width solid #999;   // Bootstrap custom code; using `$border-width` instead of 1px
+      border: $border-width solid $gray-500; // Bootstrap custom code; using `$border-width` instead of 1px
       page-break-inside: avoid;
     }
 
-    //
-    // Printing Tables:
-    // http://css-discuss.incutio.com/wiki/Printing_Tables
-    //
-
-    thead {
-      display: table-header-group;
-    }
-
     tr,
     img {
       page-break-inside: avoid;
@@ -101,7 +92,7 @@
       display: none;
     }
     .badge {
-      border: $border-width solid #000;
+      border: $border-width solid $black;
     }
 
     .table {
@@ -109,16 +100,33 @@
 
       td,
       th {
-        background-color: #fff !important;
+        background-color: $white !important;
       }
     }
+
     .table-bordered {
       th,
       td {
-        border: 1px solid #ddd !important;
+        border: 1px solid $gray-300 !important;
+      }
+    }
+
+    .table-dark {
+      color: inherit;
+
+      th,
+      td,
+      thead th,
+      tbody + tbody {
+        border-color: $table-border-color;
       }
     }
 
+    .table .thead-dark th {
+      color: inherit;
+      border-color: $table-border-color;
+    }
+
     // Bootstrap specific changes end
   }
 }
diff --git a/src/css/bootstrap/_progress.scss b/src/css/bootstrap/_progress.scss
index a58111689..e206474a8 100644
--- a/src/css/bootstrap/_progress.scss
+++ b/src/css/bootstrap/_progress.scss
@@ -1,13 +1,17 @@
-@keyframes progress-bar-stripes {
-  from { background-position: $progress-height 0; }
-  to { background-position: 0 0; }
+// Disable animation if transitions are disabled
+@if $enable-transitions {
+  @keyframes progress-bar-stripes {
+    from { background-position: $progress-height 0; }
+    to { background-position: 0 0; }
+  }
 }
 
 .progress {
   display: flex;
   height: $progress-height;
   overflow: hidden; // force rounded corners by cropping it
-  font-size: $progress-font-size;
+  line-height: 0;
+  @include font-size($progress-font-size);
   background-color: $progress-bg;
   @include border-radius($progress-border-radius);
   @include box-shadow($progress-box-shadow);
@@ -17,8 +21,10 @@
   display: flex;
   flex-direction: column;
   justify-content: center;
+  overflow: hidden;
   color: $progress-bar-color;
   text-align: center;
+  white-space: nowrap;
   background-color: $progress-bar-bg;
   @include transition($progress-bar-transition);
 }
@@ -28,6 +34,14 @@
   background-size: $progress-height $progress-height;
 }
 
-.progress-bar-animated {
-  animation: progress-bar-stripes $progress-bar-animation-timing;
+@if $enable-transitions {
+  .progress-bar-animated {
+    animation: $progress-bar-animation-timing progress-bar-stripes;
+
+    @if $enable-prefers-reduced-motion-media-query {
+      @media (prefers-reduced-motion: reduce) {
+        animation: none;
+      }
+    }
+  }
 }
diff --git a/src/css/bootstrap/_reboot.scss b/src/css/bootstrap/_reboot.scss
index c79fa4740..cd93bfe24 100644
--- a/src/css/bootstrap/_reboot.scss
+++ b/src/css/bootstrap/_reboot.scss
@@ -1,4 +1,4 @@
-// stylelint-disable at-rule-no-vendor-prefix, declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix
+// stylelint-disable declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix
 
 // Reboot
 //
@@ -14,9 +14,7 @@
 // 2. Change the default font family in all browsers.
 // 3. Correct the line height in all browsers.
 // 4. Prevent adjustments of font size after orientation changes in IE on Windows Phone and in iOS.
-// 5. Setting @viewport causes scrollbars to overlap content in IE11 and Edge, so
-//    we force a non-overlapping, non-auto-hiding scrollbar to counteract.
-// 6. Change the default tap highlight to be completely transparent in iOS.
+// 5. Change the default tap highlight to be completely transparent in iOS.
 
 *,
 *::before,
@@ -28,36 +26,27 @@ html {
   font-family: sans-serif; // 2
   line-height: 1.15; // 3
   -webkit-text-size-adjust: 100%; // 4
-  -ms-text-size-adjust: 100%; // 4
-  -ms-overflow-style: scrollbar; // 5
-  -webkit-tap-highlight-color: rgba(0, 0, 0, 0); // 6
+  -webkit-tap-highlight-color: rgba($black, 0); // 5
 }
 
-// IE10+ doesn't honor `<meta name="viewport">` in some cases.
-@at-root {
-  @-ms-viewport {
-    width: device-width;
-  }
-}
-
-// stylelint-disable selector-list-comma-newline-after
 // Shim for "new" HTML5 structural elements to display correctly (IE10, older browsers)
-article, aside, dialog, figcaption, figure, footer, header, hgroup, main, nav, section {
+// TODO: remove in v5
+// stylelint-disable-next-line selector-list-comma-newline-after
+article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
   display: block;
 }
-// stylelint-enable selector-list-comma-newline-after
 
 // Body
 //
 // 1. Remove the margin in all browsers.
 // 2. As a best practice, apply a default `background-color`.
-// 3. Set an explicit initial text-align value so that we can later use the
+// 3. Set an explicit initial text-align value so that we can later use
 //    the `inherit` value on things like `<th>` elements.
 
 body {
   margin: 0; // 1
   font-family: $font-family-base;
-  font-size: $font-size-base;
+  @include font-size($font-size-base);
   font-weight: $font-weight-base;
   line-height: $line-height-base;
   color: $body-color;
@@ -65,12 +54,16 @@ body {
   background-color: $body-bg; // 2
 }
 
-// Suppress the focus outline on elements that cannot be accessed via keyboard.
-// This prevents an unwanted focus outline from appearing around elements that
-// might still respond to pointer events.
+// Future-proof rule: in browsers that support :focus-visible, suppress the focus outline
+// on elements that programmatically receive focus but wouldn't normally show a visible
+// focus outline. In general, this would mean that the outline is only applied if the
+// interaction that led to the element receiving programmatic focus was a keyboard interaction,
+// or the browser has somehow determined that the user is primarily a keyboard user and/or
+// wants focus outlines to always be presented.
 //
-// Credit: https://github.com/suitcss/base
-[tabindex="-1"]:focus {
+// See https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible
+// and https://developer.paciellogroup.com/blog/2018/03/focus-visible-and-backwards-compatibility/
+[tabindex="-1"]:focus:not(:focus-visible) {
   outline: 0 !important;
 }
 
@@ -95,12 +88,11 @@ hr {
 //
 // By default, `<h1>`-`<h6>` all receive top and bottom margins. We nuke the top
 // margin for easier control within type scales as it avoids margin collapsing.
-// stylelint-disable selector-list-comma-newline-after
+// stylelint-disable-next-line selector-list-comma-newline-after
 h1, h2, h3, h4, h5, h6 {
   margin-top: 0;
   margin-bottom: $headings-margin-bottom;
 }
-// stylelint-enable selector-list-comma-newline-after
 
 // Reset margins on paragraphs
 //
@@ -113,17 +105,19 @@ p {
 
 // Abbreviations
 //
-// 1. Remove the bottom border in Firefox 39-.
+// 1. Duplicate behavior to the data-* attribute for our tooltip plugin
 // 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
 // 3. Add explicit cursor to indicate changed behavior.
-// 4. Duplicate behavior to the data-* attribute for our tooltip plugin
+// 4. Remove the bottom border in Firefox 39-.
+// 5. Prevent the text-decoration to be skipped.
 
 abbr[title],
-abbr[data-original-title] { // 4
+abbr[data-original-title] { // 1
   text-decoration: underline; // 2
   text-decoration: underline dotted; // 2
   cursor: help; // 3
-  border-bottom: 0; // 1
+  border-bottom: 0; // 4
+  text-decoration-skip-ink: none; // 5
 }
 
 address {
@@ -159,19 +153,13 @@ blockquote {
   margin: 0 0 1rem;
 }
 
-dfn {
-  font-style: italic; // Add the correct font style in Android 4.3-
-}
-
-// stylelint-disable font-weight-notation
 b,
 strong {
-  font-weight: bolder; // Add the correct font weight in Chrome, Edge, and Safari
+  font-weight: $font-weight-bolder; // Add the correct font weight in Chrome, Edge, and Safari
 }
-// stylelint-enable font-weight-notation
 
 small {
-  font-size: 80%; // Add the correct font size in all browsers
+  @include font-size(80%); // Add the correct font size in all browsers
 }
 
 //
@@ -182,7 +170,7 @@ small {
 sub,
 sup {
   position: relative;
-  font-size: 75%;
+  @include font-size(75%);
   line-height: 0;
   vertical-align: baseline;
 }
@@ -199,32 +187,26 @@ a {
   color: $link-color;
   text-decoration: $link-decoration;
   background-color: transparent; // Remove the gray background on active links in IE 10.
-  -webkit-text-decoration-skip: objects; // Remove gaps in links underline in iOS 8+ and Safari 8+.
 
-  @include hover {
+  @include hover() {
     color: $link-hover-color;
     text-decoration: $link-hover-decoration;
   }
 }
 
-// And undo these styles for placeholder links/named anchors (without href)
-// which have not been made explicitly keyboard-focusable (without tabindex).
+// And undo these styles for placeholder links/named anchors (without href).
 // It would be more straightforward to just use a[href] in previous block, but that
 // causes specificity issues in many other styles that are too complex to fix.
 // See https://github.com/twbs/bootstrap/issues/19402
 
-a:not([href]):not([tabindex]) {
+a:not([href]):not([class]) {
   color: inherit;
   text-decoration: none;
 
-  @include hover-focus {
+  @include hover() {
     color: inherit;
     text-decoration: none;
   }
-
-  &:focus {
-    outline: 0;
-  }
 }
 
 
@@ -232,15 +214,13 @@ a:not([href]):not([tabindex]) {
 // Code
 //
 
-// stylelint-disable font-family-no-duplicate-names
 pre,
 code,
 kbd,
 samp {
-  font-family: monospace, monospace; // Correct the inheritance and scaling of font size in all browsers.
-  font-size: 1em; // Correct the odd `em` font sizing in all browsers.
+  font-family: $font-family-monospace;
+  @include font-size(1em); // Correct the odd `em` font sizing in all browsers.
 }
-// stylelint-enable font-family-no-duplicate-names
 
 pre {
   // Remove browser default top margin
@@ -249,8 +229,8 @@ pre {
   margin-bottom: 1rem;
   // Don't allow content to break outside
   overflow: auto;
-  // We have @viewport set which causes scrollbars to overlap content in IE11 and Edge, so
-  // we force a non-overlapping, non-auto-hiding scrollbar to counteract.
+  // Disable auto-hiding scrollbar in IE & legacy Edge to avoid overlap,
+  // making it impossible to interact with the content
   -ms-overflow-style: scrollbar;
 }
 
@@ -274,8 +254,11 @@ img {
   border-style: none; // Remove the border on images inside links in IE 10-.
 }
 
-svg:not(:root) {
-  overflow: hidden; // Hide the overflow in IE
+svg {
+  // Workaround for the SVG overflow bug in IE10/11 is still required.
+  // See https://github.com/twbs/bootstrap/issues/26878
+  overflow: hidden;
+  vertical-align: middle;
 }
 
 
@@ -290,15 +273,19 @@ table {
 caption {
   padding-top: $table-cell-padding;
   padding-bottom: $table-cell-padding;
-  color: $text-muted;
+  color: $table-caption-color;
   text-align: left;
   caption-side: bottom;
 }
 
+// 1. Removes font-weight bold by inheriting
+// 2. Matches default `<td>` alignment by inheriting `text-align`.
+// 3. Fix alignment for Safari
+
 th {
-  // Matches default `<td>` alignment by inheriting from the `<body>`, or the
-  // closest parent with a set `text-align`.
-  text-align: inherit;
+  font-weight: $table-th-font-weight; // 1
+  text-align: inherit; // 2
+  text-align: -webkit-match-parent; // 3
 }
 
 
@@ -309,23 +296,24 @@ th {
 label {
   // Allow labels to use `margin` for spacing.
   display: inline-block;
-  margin-bottom: .5rem;
+  margin-bottom: $label-margin-bottom;
 }
 
 // Remove the default `border-radius` that macOS Chrome adds.
 //
 // Details at https://github.com/twbs/bootstrap/issues/24093
 button {
+  // stylelint-disable-next-line property-disallowed-list
   border-radius: 0;
 }
 
-// Work around a Firefox/IE bug where the transparent `button` background
-// results in a loss of the default `button` focus styles.
-//
-// Credit: https://github.com/suitcss/base/
-button:focus {
-  outline: 1px dotted;
-  outline: 5px auto -webkit-focus-ring-color;
+// Explicitly remove focus outline in Chromium when it shouldn't be
+// visible (e.g. as result of mouse click or touch tap). It already
+// should be doing this automatically, but seems to currently be
+// confused and applies its very visible two-tone outline anyway.
+
+button:focus:not(:focus-visible) {
+  outline: 0;
 }
 
 input,
@@ -335,7 +323,7 @@ optgroup,
 textarea {
   margin: 0; // Remove the margin in Firefox and Safari
   font-family: inherit;
-  font-size: inherit;
+  @include font-size(inherit);
   line-height: inherit;
 }
 
@@ -349,16 +337,43 @@ select {
   text-transform: none; // Remove the inheritance of text transform in Firefox
 }
 
+// Set the cursor for non-`<button>` buttons
+//
+// Details at https://github.com/twbs/bootstrap/pull/30562
+[role="button"] {
+  cursor: pointer;
+}
+
+// Remove the inheritance of word-wrap in Safari.
+//
+// Details at https://github.com/twbs/bootstrap/issues/24990
+select {
+  word-wrap: normal;
+}
+
+
 // 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
 //    controls in Android 4.
 // 2. Correct the inability to style clickable types in iOS and Safari.
 button,
-html [type="button"], // 1
+[type="button"], // 1
 [type="reset"],
 [type="submit"] {
   -webkit-appearance: button; // 2
 }
 
+// Opinionated: add "hand" cursor to non-disabled button elements.
+@if $enable-pointer-cursor-for-buttons {
+  button,
+  [type="button"],
+  [type="reset"],
+  [type="submit"] {
+    &:not(:disabled) {
+      cursor: pointer;
+    }
+  }
+}
+
 // Remove inner border and padding from Firefox, but don't restore the outline like Normalize.
 button::-moz-focus-inner,
 [type="button"]::-moz-focus-inner,
@@ -375,18 +390,6 @@ input[type="checkbox"] {
 }
 
 
-input[type="date"],
-input[type="time"],
-input[type="datetime-local"],
-input[type="month"] {
-  // Remove the default appearance of temporal inputs to avoid a Mobile Safari
-  // bug where setting a custom line-height prevents text from being vertically
-  // centered within the input.
-  // See https://bugs.webkit.org/show_bug.cgi?id=139848
-  // and https://github.com/twbs/bootstrap/issues/11266
-  -webkit-appearance: listbox;
-}
-
 textarea {
   overflow: auto; // Remove the default vertical scrollbar in IE.
   // Textareas should really only resize vertically so they don't break their (horizontal) containers.
@@ -414,7 +417,7 @@ legend {
   max-width: 100%; // 1
   padding: 0;
   margin-bottom: .5rem;
-  font-size: 1.5rem;
+  @include font-size(1.5rem);
   line-height: inherit;
   color: inherit; // 2
   white-space: normal; // 1
@@ -440,10 +443,9 @@ progress {
 }
 
 //
-// Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
+// Remove the inner padding in Chrome and Safari on macOS.
 //
 
-[type="search"]::-webkit-search-cancel-button,
 [type="search"]::-webkit-search-decoration {
   -webkit-appearance: none;
 }
diff --git a/src/css/bootstrap/_spinners.scss b/src/css/bootstrap/_spinners.scss
new file mode 100644
index 000000000..61f3e0a35
--- /dev/null
+++ b/src/css/bootstrap/_spinners.scss
@@ -0,0 +1,65 @@
+//
+// Rotating border
+//
+
+@keyframes spinner-border {
+  to { transform: rotate(360deg); }
+}
+
+.spinner-border {
+  display: inline-block;
+  width: $spinner-width;
+  height: $spinner-height;
+  vertical-align: $spinner-vertical-align;
+  border: $spinner-border-width solid currentcolor;
+  border-right-color: transparent;
+  // stylelint-disable-next-line property-disallowed-list
+  border-radius: 50%;
+  animation: .75s linear infinite spinner-border;
+}
+
+.spinner-border-sm {
+  width: $spinner-width-sm;
+  height: $spinner-height-sm;
+  border-width: $spinner-border-width-sm;
+}
+
+//
+// Growing circle
+//
+
+@keyframes spinner-grow {
+  0% {
+    transform: scale(0);
+  }
+  50% {
+    opacity: 1;
+    transform: none;
+  }
+}
+
+.spinner-grow {
+  display: inline-block;
+  width: $spinner-width;
+  height: $spinner-height;
+  vertical-align: $spinner-vertical-align;
+  background-color: currentcolor;
+  // stylelint-disable-next-line property-disallowed-list
+  border-radius: 50%;
+  opacity: 0;
+  animation: .75s linear infinite spinner-grow;
+}
+
+.spinner-grow-sm {
+  width: $spinner-width-sm;
+  height: $spinner-height-sm;
+}
+
+@if $enable-prefers-reduced-motion-media-query {
+  @media (prefers-reduced-motion: reduce) {
+    .spinner-border,
+    .spinner-grow {
+      animation-duration: 1.5s;
+    }
+  }
+}
diff --git a/src/css/bootstrap/_tables.scss b/src/css/bootstrap/_tables.scss
index 0e3b1198e..5fd77a4eb 100644
--- a/src/css/bootstrap/_tables.scss
+++ b/src/css/bootstrap/_tables.scss
@@ -4,8 +4,8 @@
 
 .table {
   width: 100%;
-  max-width: 100%;
   margin-bottom: $spacer;
+  color: $table-color;
   background-color: $table-bg; // Reset for nesting within parents with `background-color`.
 
   th,
@@ -23,10 +23,6 @@
   tbody + tbody {
     border-top: (2 * $table-border-width) solid $table-border-color;
   }
-
-  .table {
-    background-color: $body-bg;
-  }
 }
 
 
@@ -42,9 +38,9 @@
 }
 
 
-// Bordered version
+// Border versions
 //
-// Add borders all around the table and between all the columns.
+// Add or remove borders all around the table and between all the columns.
 
 .table-bordered {
   border: $table-border-width solid $table-border-color;
@@ -57,18 +53,26 @@
   thead {
     th,
     td {
-      border-bottom-width: (2 * $table-border-width);
+      border-bottom-width: 2 * $table-border-width;
     }
   }
 }
 
+.table-borderless {
+  th,
+  td,
+  thead th,
+  tbody + tbody {
+    border: 0;
+  }
+}
 
 // Zebra-striping
 //
 // Default zebra-stripe styles (alternating gray and transparent backgrounds)
 
 .table-striped {
-  tbody tr:nth-of-type(odd) {
+  tbody tr:nth-of-type(#{$table-striped-order}) {
     background-color: $table-accent-bg;
   }
 }
@@ -80,7 +84,8 @@
 
 .table-hover {
   tbody tr {
-    @include hover {
+    @include hover() {
+      color: $table-hover-color;
       background-color: $table-hover-bg;
     }
   }
@@ -93,7 +98,7 @@
 // inheritance to nested tables.
 
 @each $color, $value in $theme-colors {
-  @include table-row-variant($color, theme-color-level($color, -9));
+  @include table-row-variant($color, theme-color-level($color, $table-bg-level), theme-color-level($color, $table-border-level));
 }
 
 @include table-row-variant(active, $table-active-bg);
@@ -137,14 +142,15 @@
   }
 
   &.table-striped {
-    tbody tr:nth-of-type(odd) {
+    tbody tr:nth-of-type(#{$table-striped-order}) {
       background-color: $table-dark-accent-bg;
     }
   }
 
   &.table-hover {
     tbody tr {
-      @include hover {
+      @include hover() {
+        color: $table-dark-hover-color;
         background-color: $table-dark-hover-bg;
       }
     }
@@ -168,7 +174,6 @@
         width: 100%;
         overflow-x: auto;
         -webkit-overflow-scrolling: touch;
-        -ms-overflow-style: -ms-autohiding-scrollbar; // See https://github.com/twbs/bootstrap/pull/10057
 
         // Prevent double border on horizontal scroll due to use of `display: block;`
         > .table-bordered {
diff --git a/src/css/bootstrap/_toasts.scss b/src/css/bootstrap/_toasts.scss
new file mode 100644
index 000000000..f9ca8d91c
--- /dev/null
+++ b/src/css/bootstrap/_toasts.scss
@@ -0,0 +1,46 @@
+.toast {
+  // Prevents from shrinking in IE11, when in a flex container
+  // See https://github.com/twbs/bootstrap/issues/28341
+  flex-basis: $toast-max-width;
+  max-width: $toast-max-width;
+  @include font-size($toast-font-size);
+  color: $toast-color;
+  background-color: $toast-background-color;
+  background-clip: padding-box;
+  border: $toast-border-width solid $toast-border-color;
+  box-shadow: $toast-box-shadow;
+  opacity: 0;
+  @include border-radius($toast-border-radius);
+
+  &:not(:last-child) {
+    margin-bottom: $toast-padding-x;
+  }
+
+  &.showing {
+    opacity: 1;
+  }
+
+  &.show {
+    display: block;
+    opacity: 1;
+  }
+
+  &.hide {
+    display: none;
+  }
+}
+
+.toast-header {
+  display: flex;
+  align-items: center;
+  padding: $toast-padding-y $toast-padding-x;
+  color: $toast-header-color;
+  background-color: $toast-header-background-color;
+  background-clip: padding-box;
+  border-bottom: $toast-border-width solid $toast-header-border-color;
+  @include border-top-radius(subtract($toast-border-radius, $toast-border-width));
+}
+
+.toast-body {
+  padding: $toast-padding-x; // apply to both vertical and horizontal
+}
diff --git a/src/css/bootstrap/_tooltip.scss b/src/css/bootstrap/_tooltip.scss
index 1286ebfcf..6771b4eb4 100644
--- a/src/css/bootstrap/_tooltip.scss
+++ b/src/css/bootstrap/_tooltip.scss
@@ -7,7 +7,7 @@
   // Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element.
   // So reset our font and text properties to avoid inheriting weird values.
   @include reset-text();
-  font-size: $tooltip-font-size;
+  @include font-size($tooltip-font-size);
   // Allow breaking very long words so they don't overflow the tooltip's bounds
   word-wrap: break-word;
   opacity: 0;
@@ -37,7 +37,7 @@
 
     &::before {
       top: 0;
-      border-width: $tooltip-arrow-height ($tooltip-arrow-width / 2) 0;
+      border-width: $tooltip-arrow-height ($tooltip-arrow-width * .5) 0;
       border-top-color: $tooltip-arrow-color;
     }
   }
@@ -53,7 +53,7 @@
 
     &::before {
       right: 0;
-      border-width: ($tooltip-arrow-width / 2) $tooltip-arrow-height ($tooltip-arrow-width / 2) 0;
+      border-width: ($tooltip-arrow-width * .5) $tooltip-arrow-height ($tooltip-arrow-width * .5) 0;
       border-right-color: $tooltip-arrow-color;
     }
   }
@@ -67,7 +67,7 @@
 
     &::before {
       bottom: 0;
-      border-width: 0 ($tooltip-arrow-width / 2) $tooltip-arrow-height;
+      border-width: 0 ($tooltip-arrow-width * .5) $tooltip-arrow-height;
       border-bottom-color: $tooltip-arrow-color;
     }
   }
@@ -83,7 +83,7 @@
 
     &::before {
       left: 0;
-      border-width: ($tooltip-arrow-width / 2) 0 ($tooltip-arrow-width / 2) $tooltip-arrow-height;
+      border-width: ($tooltip-arrow-width * .5) 0 ($tooltip-arrow-width * .5) $tooltip-arrow-height;
       border-left-color: $tooltip-arrow-color;
     }
   }
diff --git a/src/css/bootstrap/_transitions.scss b/src/css/bootstrap/_transitions.scss
index df5744b25..a261f2ddc 100644
--- a/src/css/bootstrap/_transitions.scss
+++ b/src/css/bootstrap/_transitions.scss
@@ -1,30 +1,14 @@
-// stylelint-disable selector-no-qualifying-type
-
 .fade {
-  opacity: 0;
   @include transition($transition-fade);
 
-  &.show {
-    opacity: 1;
+  &:not(.show) {
+    opacity: 0;
   }
 }
 
 .collapse {
-  display: none;
-  &.show {
-    display: block;
-  }
-}
-
-tr {
-  &.collapse.show {
-    display: table-row;
-  }
-}
-
-tbody {
-  &.collapse.show {
-    display: table-row-group;
+  &:not(.show) {
+    display: none;
   }
 }
 
@@ -33,4 +17,10 @@ tbody {
   height: 0;
   overflow: hidden;
   @include transition($transition-collapse);
+
+  &.width {
+    width: 0;
+    height: auto;
+    @include transition($transition-collapse-width);
+  }
 }
diff --git a/src/css/bootstrap/_type.scss b/src/css/bootstrap/_type.scss
index 57d610f0c..3112a734b 100644
--- a/src/css/bootstrap/_type.scss
+++ b/src/css/bootstrap/_type.scss
@@ -1,4 +1,4 @@
-// stylelint-disable declaration-no-important, selector-list-comma-newline-after
+// stylelint-disable selector-list-comma-newline-after
 
 //
 // Headings
@@ -13,36 +13,36 @@ h1, h2, h3, h4, h5, h6,
   color: $headings-color;
 }
 
-h1, .h1 { font-size: $h1-font-size; }
-h2, .h2 { font-size: $h2-font-size; }
-h3, .h3 { font-size: $h3-font-size; }
-h4, .h4 { font-size: $h4-font-size; }
-h5, .h5 { font-size: $h5-font-size; }
-h6, .h6 { font-size: $h6-font-size; }
+h1, .h1 { @include font-size($h1-font-size); }
+h2, .h2 { @include font-size($h2-font-size); }
+h3, .h3 { @include font-size($h3-font-size); }
+h4, .h4 { @include font-size($h4-font-size); }
+h5, .h5 { @include font-size($h5-font-size); }
+h6, .h6 { @include font-size($h6-font-size); }
 
 .lead {
-  font-size: $lead-font-size;
+  @include font-size($lead-font-size);
   font-weight: $lead-font-weight;
 }
 
 // Type display classes
 .display-1 {
-  font-size: $display1-size;
+  @include font-size($display1-size);
   font-weight: $display1-weight;
   line-height: $display-line-height;
 }
 .display-2 {
-  font-size: $display2-size;
+  @include font-size($display2-size);
   font-weight: $display2-weight;
   line-height: $display-line-height;
 }
 .display-3 {
-  font-size: $display3-size;
+  @include font-size($display3-size);
   font-weight: $display3-weight;
   line-height: $display-line-height;
 }
 .display-4 {
-  font-size: $display4-size;
+  @include font-size($display4-size);
   font-weight: $display4-weight;
   line-height: $display-line-height;
 }
@@ -66,7 +66,7 @@ hr {
 
 small,
 .small {
-  font-size: $small-font-size;
+  @include font-size($small-font-size);
   font-weight: $font-weight-normal;
 }
 
@@ -82,12 +82,12 @@ mark,
 //
 
 .list-unstyled {
-  @include list-unstyled;
+  @include list-unstyled();
 }
 
 // Inline turns list items into inline-block
 .list-inline {
-  @include list-unstyled;
+  @include list-unstyled();
 }
 .list-inline-item {
   display: inline-block;
@@ -104,22 +104,22 @@ mark,
 
 // Builds on `abbr`
 .initialism {
-  font-size: 90%;
+  @include font-size(90%);
   text-transform: uppercase;
 }
 
 // Blockquotes
 .blockquote {
   margin-bottom: $spacer;
-  font-size: $blockquote-font-size;
+  @include font-size($blockquote-font-size);
 }
 
 .blockquote-footer {
   display: block;
-  font-size: 80%; // back to default font-size
+  @include font-size($blockquote-small-font-size);
   color: $blockquote-small-color;
 
   &::before {
-    content: "\2014 \00A0"; // em dash, nbsp
+    content: "\2014\00A0"; // em dash, nbsp
   }
 }
diff --git a/src/css/bootstrap/_utilities.scss b/src/css/bootstrap/_utilities.scss
index 7b2a1ebe9..10e31dd7e 100644
--- a/src/css/bootstrap/_utilities.scss
+++ b/src/css/bootstrap/_utilities.scss
@@ -6,9 +6,13 @@
 @import "utilities/embed";
 @import "utilities/flex";
 @import "utilities/float";
+@import "utilities/interactions";
+@import "utilities/overflow";
 @import "utilities/position";
 @import "utilities/screenreaders";
+@import "utilities/shadows";
 @import "utilities/sizing";
 @import "utilities/spacing";
+@import "utilities/stretched-link";
 @import "utilities/text";
 @import "utilities/visibility";
diff --git a/src/css/bootstrap/_variables.scss b/src/css/bootstrap/_variables.scss
index be580deb3..293d238b9 100644
--- a/src/css/bootstrap/_variables.scss
+++ b/src/css/bootstrap/_variables.scss
@@ -3,12 +3,8 @@
 // Variables should follow the `$component-state-property-size` formula for
 // consistent naming. Ex: $nav-link-disabled-color and $modal-content-box-shadow-xs.
 
-
-//
 // Color system
-//
 
-// stylelint-disable
 $white:    #fff !default;
 $gray-100: #f8f9fa !default;
 $gray-200: #e9ecef !default;
@@ -22,17 +18,20 @@ $gray-900: #212529 !default;
 $black:    #000 !default;
 
 $grays: () !default;
-$grays: map-merge((
-  "100": $gray-100,
-  "200": $gray-200,
-  "300": $gray-300,
-  "400": $gray-400,
-  "500": $gray-500,
-  "600": $gray-600,
-  "700": $gray-700,
-  "800": $gray-800,
-  "900": $gray-900
-), $grays);
+$grays: map-merge(
+  (
+    "100": $gray-100,
+    "200": $gray-200,
+    "300": $gray-300,
+    "400": $gray-400,
+    "500": $gray-500,
+    "600": $gray-600,
+    "700": $gray-700,
+    "800": $gray-800,
+    "900": $gray-900
+  ),
+  $grays
+);
 
 $blue:    #007bff !default;
 $indigo:  #6610f2 !default;
@@ -46,21 +45,24 @@ $teal:    #20c997 !default;
 $cyan:    #17a2b8 !default;
 
 $colors: () !default;
-$colors: map-merge((
-  "blue":       $blue,
-  "indigo":     $indigo,
-  "purple":     $purple,
-  "pink":       $pink,
-  "red":        $red,
-  "orange":     $orange,
-  "yellow":     $yellow,
-  "green":      $green,
-  "teal":       $teal,
-  "cyan":       $cyan,
-  "white":      $white,
-  "gray":       $gray-600,
-  "gray-dark":  $gray-800
-), $colors);
+$colors: map-merge(
+  (
+    "blue":       $blue,
+    "indigo":     $indigo,
+    "purple":     $purple,
+    "pink":       $pink,
+    "red":        $red,
+    "orange":     $orange,
+    "yellow":     $yellow,
+    "green":      $green,
+    "teal":       $teal,
+    "cyan":       $cyan,
+    "white":      $white,
+    "gray":       $gray-600,
+    "gray-dark":  $gray-800
+  ),
+  $colors
+);
 
 $primary:       $blue !default;
 $secondary:     $gray-600 !default;
@@ -72,40 +74,57 @@ $light:         $gray-100 !default;
 $dark:          $gray-800 !default;
 
 $theme-colors: () !default;
-$theme-colors: map-merge((
-  "primary":    $primary,
-  "secondary":  $secondary,
-  "success":    $success,
-  "info":       $info,
-  "warning":    $warning,
-  "danger":     $danger,
-  "light":      $light,
-  "dark":       $dark
-), $theme-colors);
-// stylelint-enable
+$theme-colors: map-merge(
+  (
+    "primary":    $primary,
+    "secondary":  $secondary,
+    "success":    $success,
+    "info":       $info,
+    "warning":    $warning,
+    "danger":     $danger,
+    "light":      $light,
+    "dark":       $dark
+  ),
+  $theme-colors
+);
 
 // Set a specific jump point for requesting color jumps
 $theme-color-interval:      8% !default;
 
 // The yiq lightness value that determines when the lightness of color changes from "dark" to "light". Acceptable values are between 0 and 255.
-$yiq-contrasted-threshold: 150 !default;
+$yiq-contrasted-threshold:  150 !default;
 
 // Customize the light and dark text colors for use in our YIQ color contrast function.
-$yiq-text-dark: $gray-900 !default;
-$yiq-text-light: $white !default;
+$yiq-text-dark:             $gray-900 !default;
+$yiq-text-light:            $white !default;
+
+// Characters which are escaped by the escape-svg function
+$escaped-characters: (
+  ("<", "%3c"),
+  (">", "%3e"),
+  ("#", "%23"),
+  ("(", "%28"),
+  (")", "%29"),
+) !default;
+
 
 // Options
 //
 // Quickly modify global styling by enabling or disabling optional features.
 
-$enable-caret:              true !default;
-$enable-rounded:            true !default;
-$enable-shadows:            false !default;
-$enable-gradients:          false !default;
-$enable-transitions:        true !default;
-$enable-hover-media-query:  false !default; // Deprecated, no longer affects any compiled CSS
-$enable-grid-classes:       true !default;
-$enable-print-styles:       true !default;
+$enable-caret:                                true !default;
+$enable-rounded:                              true !default;
+$enable-shadows:                              false !default;
+$enable-gradients:                            false !default;
+$enable-transitions:                          true !default;
+$enable-prefers-reduced-motion-media-query:   true !default;
+$enable-hover-media-query:                    false !default; // Deprecated, no longer affects any compiled CSS
+$enable-grid-classes:                         true !default;
+$enable-pointer-cursor-for-buttons:           true !default;
+$enable-print-styles:                         true !default;
+$enable-responsive-font-sizes:                false !default;
+$enable-validation-icons:                     true !default;
+$enable-deprecation-messages:                 true !default;
 
 
 // Spacing
@@ -114,27 +133,33 @@ $enable-print-styles:       true !default;
 // variables. Mostly focused on spacing.
 // You can add more entries to the $spacers map, should you need more variation.
 
-// stylelint-disable
 $spacer: 1rem !default;
 $spacers: () !default;
-$spacers: map-merge((
-  0: 0,
-  1: ($spacer * .25),
-  2: ($spacer * .5),
-  3: $spacer,
-  4: ($spacer * 1.5),
-  5: ($spacer * 3)
-), $spacers);
+$spacers: map-merge(
+  (
+    0: 0,
+    1: ($spacer * .25),
+    2: ($spacer * .5),
+    3: $spacer,
+    4: ($spacer * 1.5),
+    5: ($spacer * 3)
+  ),
+  $spacers
+);
 
 // This variable affects the `.h-*` and `.w-*` classes.
 $sizes: () !default;
-$sizes: map-merge((
-  25: 25%,
-  50: 50%,
-  75: 75%,
-  100: 100%
-), $sizes);
-// stylelint-enable
+$sizes: map-merge(
+  (
+    25: 25%,
+    50: 50%,
+    75: 75%,
+    100: 100%,
+    auto: auto
+  ),
+  $sizes
+);
+
 
 // Body
 //
@@ -143,14 +168,17 @@ $sizes: map-merge((
 $body-bg:                   $white !default;
 $body-color:                $gray-900 !default;
 
+
 // Links
 //
 // Style anchor elements.
 
-$link-color:                theme-color("primary") !default;
-$link-decoration:           none !default;
-$link-hover-color:          darken($link-color, 15%) !default;
-$link-hover-decoration:     underline !default;
+$link-color:                              theme-color("primary") !default;
+$link-decoration:                         none !default;
+$link-hover-color:                        darken($link-color, 15%) !default;
+$link-hover-decoration:                   underline !default;
+// Darken percentage for links with `.text-*` class (e.g. `.text-success`)
+$emphasized-link-hover-darken-percentage: 15% !default;
 
 // Paragraphs
 //
@@ -173,7 +201,7 @@ $grid-breakpoints: (
 ) !default;
 
 @include _assert-ascending($grid-breakpoints, "$grid-breakpoints");
-@include _assert-starts-at-zero($grid-breakpoints);
+@include _assert-starts-at-zero($grid-breakpoints, "$grid-breakpoints");
 
 
 // Grid containers
@@ -196,6 +224,8 @@ $container-max-widths: (
 
 $grid-columns:                12 !default;
 $grid-gutter-width:           30px !default;
+$grid-row-columns:            6 !default;
+
 
 // Components
 //
@@ -211,33 +241,54 @@ $border-radius:               .25rem !default;
 $border-radius-lg:            .3rem !default;
 $border-radius-sm:            .2rem !default;
 
+$rounded-pill:                50rem !default;
+
+$box-shadow-sm:               0 .125rem .25rem rgba($black, .075) !default;
+$box-shadow:                  0 .5rem 1rem rgba($black, .15) !default;
+$box-shadow-lg:               0 1rem 3rem rgba($black, .175) !default;
+
 $component-active-color:      $white !default;
 $component-active-bg:         theme-color("primary") !default;
 
 $caret-width:                 .3em !default;
+$caret-vertical-align:        $caret-width * .85 !default;
+$caret-spacing:               $caret-width * .85 !default;
 
 $transition-base:             all .2s ease-in-out !default;
 $transition-fade:             opacity .15s linear !default;
 $transition-collapse:         height .35s ease !default;
-
-
-// Fonts
+$transition-collapse-width:   width .35s ease !default;
+
+$embed-responsive-aspect-ratios: () !default;
+$embed-responsive-aspect-ratios: join(
+  (
+    (21 9),
+    (16 9),
+    (4 3),
+    (1 1),
+  ),
+  $embed-responsive-aspect-ratios
+);
+
+// Typography
 //
 // Font, line-height, and color for body text, headings, and more.
 
 // stylelint-disable value-keyword-case
-$font-family-sans-serif:      -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !default;
+$font-family-sans-serif:      -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji" !default;
 $font-family-monospace:       SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !default;
 $font-family-base:            $font-family-sans-serif !default;
 // stylelint-enable value-keyword-case
 
 $font-size-base:              1rem !default; // Assumes the browser default, typically `16px`
-$font-size-lg:                ($font-size-base * 1.25) !default;
-$font-size-sm:                ($font-size-base * .875) !default;
+$font-size-lg:                $font-size-base * 1.25 !default;
+$font-size-sm:                $font-size-base * .875 !default;
 
+$font-weight-lighter:         lighter !default;
 $font-weight-light:           300 !default;
 $font-weight-normal:          400 !default;
 $font-weight-bold:            700 !default;
+$font-weight-bolder:          bolder !default;
 
 $font-weight-base:            $font-weight-normal !default;
 $line-height-base:            1.5 !default;
@@ -249,11 +300,11 @@ $h4-font-size:                $font-size-base * 1.5 !default;
 $h5-font-size:                $font-size-base * 1.25 !default;
 $h6-font-size:                $font-size-base !default;
 
-$headings-margin-bottom:      ($spacer / 2) !default;
-$headings-font-family:        inherit !default;
+$headings-margin-bottom:      $spacer * .5 !default;
+$headings-font-family:        null !default;
 $headings-font-weight:        500 !default;
 $headings-line-height:        1.2 !default;
-$headings-color:              inherit !default;
+$headings-color:              null !default;
 
 $display1-size:               6rem !default;
 $display2-size:               5.5rem !default;
@@ -266,15 +317,16 @@ $display3-weight:             300 !default;
 $display4-weight:             300 !default;
 $display-line-height:         $headings-line-height !default;
 
-$lead-font-size:              ($font-size-base * 1.25) !default;
+$lead-font-size:              $font-size-base * 1.25 !default;
 $lead-font-weight:            300 !default;
 
-$small-font-size:             80% !default;
+$small-font-size:             .875em !default;
 
 $text-muted:                  $gray-600 !default;
 
 $blockquote-small-color:      $gray-600 !default;
-$blockquote-font-size:        ($font-size-base * 1.25) !default;
+$blockquote-small-font-size:  $small-font-size !default;
+$blockquote-font-size:        $font-size-base * 1.25 !default;
 
 $hr-border-color:             rgba($black, .1) !default;
 $hr-border-width:             $border-width !default;
@@ -300,22 +352,33 @@ $hr-margin-y:                 $spacer !default;
 $table-cell-padding:          .75rem !default;
 $table-cell-padding-sm:       .3rem !default;
 
-$table-bg:                    transparent !default;
+$table-color:                 $body-color !default;
+$table-bg:                    null !default;
 $table-accent-bg:             rgba($black, .05) !default;
+$table-hover-color:           $table-color !default;
 $table-hover-bg:              rgba($black, .075) !default;
 $table-active-bg:             $table-hover-bg !default;
 
 $table-border-width:          $border-width !default;
-$table-border-color:          $gray-300 !default;
+$table-border-color:          $border-color !default;
 
 $table-head-bg:               $gray-200 !default;
 $table-head-color:            $gray-700 !default;
+$table-th-font-weight:        null !default;
 
-$table-dark-bg:               $gray-900 !default;
+$table-dark-color:            $white !default;
+$table-dark-bg:               $gray-800 !default;
 $table-dark-accent-bg:        rgba($white, .05) !default;
+$table-dark-hover-color:      $table-dark-color !default;
 $table-dark-hover-bg:         rgba($white, .075) !default;
-$table-dark-border-color:     lighten($gray-900, 7.5%) !default;
-$table-dark-color:            $body-bg !default;
+$table-dark-border-color:     lighten($table-dark-bg, 7.5%) !default;
+
+$table-striped-order:         odd !default;
+
+$table-caption-color:         $text-muted !default;
+
+$table-bg-level:              -9 !default;
+$table-border-level:          -6 !default;
 
 
 // Buttons + Forms
@@ -324,6 +387,8 @@ $table-dark-color:            $body-bg !default;
 
 $input-btn-padding-y:         .375rem !default;
 $input-btn-padding-x:         .75rem !default;
+$input-btn-font-family:       null !default;
+$input-btn-font-size:         $font-size-base !default;
 $input-btn-line-height:       $line-height-base !default;
 
 $input-btn-focus-width:       .2rem !default;
@@ -332,10 +397,12 @@ $input-btn-focus-box-shadow:  0 0 0 $input-btn-focus-width $input-btn-focus-colo
 
 $input-btn-padding-y-sm:      .25rem !default;
 $input-btn-padding-x-sm:      .5rem !default;
+$input-btn-font-size-sm:      $font-size-sm !default;
 $input-btn-line-height-sm:    $line-height-sm !default;
 
 $input-btn-padding-y-lg:      .5rem !default;
 $input-btn-padding-x-lg:      1rem !default;
+$input-btn-font-size-lg:      $font-size-lg !default;
 $input-btn-line-height-lg:    $line-height-lg !default;
 
 $input-btn-border-width:      $border-width !default;
@@ -347,14 +414,19 @@ $input-btn-border-width:      $border-width !default;
 
 $btn-padding-y:               $input-btn-padding-y !default;
 $btn-padding-x:               $input-btn-padding-x !default;
+$btn-font-family:             $input-btn-font-family !default;
+$btn-font-size:               $input-btn-font-size !default;
 $btn-line-height:             $input-btn-line-height !default;
+$btn-white-space:             null !default; // Set to `nowrap` to prevent text wrapping
 
 $btn-padding-y-sm:            $input-btn-padding-y-sm !default;
 $btn-padding-x-sm:            $input-btn-padding-x-sm !default;
+$btn-font-size-sm:            $input-btn-font-size-sm !default;
 $btn-line-height-sm:          $input-btn-line-height-sm !default;
 
 $btn-padding-y-lg:            $input-btn-padding-y-lg !default;
 $btn-padding-x-lg:            $input-btn-padding-x-lg !default;
+$btn-font-size-lg:            $input-btn-font-size-lg !default;
 $btn-line-height-lg:          $input-btn-line-height-lg !default;
 
 $btn-border-width:            $input-btn-border-width !default;
@@ -380,16 +452,23 @@ $btn-transition:              color .15s ease-in-out, background-color .15s ease
 
 // Forms
 
+$label-margin-bottom:                   .5rem !default;
+
 $input-padding-y:                       $input-btn-padding-y !default;
 $input-padding-x:                       $input-btn-padding-x !default;
+$input-font-family:                     $input-btn-font-family !default;
+$input-font-size:                       $input-btn-font-size !default;
+$input-font-weight:                     $font-weight-base !default;
 $input-line-height:                     $input-btn-line-height !default;
 
 $input-padding-y-sm:                    $input-btn-padding-y-sm !default;
 $input-padding-x-sm:                    $input-btn-padding-x-sm !default;
+$input-font-size-sm:                    $input-btn-font-size-sm !default;
 $input-line-height-sm:                  $input-btn-line-height-sm !default;
 
 $input-padding-y-lg:                    $input-btn-padding-y-lg !default;
 $input-padding-x-lg:                    $input-btn-padding-x-lg !default;
+$input-font-size-lg:                    $input-btn-font-size-lg !default;
 $input-line-height-lg:                  $input-btn-line-height-lg !default;
 
 $input-bg:                              $white !default;
@@ -411,17 +490,17 @@ $input-focus-width:                     $input-btn-focus-width !default;
 $input-focus-box-shadow:                $input-btn-focus-box-shadow !default;
 
 $input-placeholder-color:               $gray-600 !default;
+$input-plaintext-color:                 $body-color !default;
 
 $input-height-border:                   $input-border-width * 2 !default;
 
-$input-height-inner:                    ($font-size-base * $input-btn-line-height) + ($input-btn-padding-y * 2) !default;
-$input-height:                          calc(#{$input-height-inner} + #{$input-height-border}) !default;
+$input-height-inner:                    add($input-line-height * 1em, $input-padding-y * 2) !default;
+$input-height-inner-half:               add($input-line-height * .5em, $input-padding-y) !default;
+$input-height-inner-quarter:            add($input-line-height * .25em, $input-padding-y * .5) !default;
 
-$input-height-inner-sm:                 ($font-size-sm * $input-btn-line-height-sm) + ($input-btn-padding-y-sm * 2) !default;
-$input-height-sm:                       calc(#{$input-height-inner-sm} + #{$input-height-border}) !default;
-
-$input-height-inner-lg:                 ($font-size-lg * $input-btn-line-height-lg) + ($input-btn-padding-y-lg * 2) !default;
-$input-height-lg:                       calc(#{$input-height-inner-lg} + #{$input-height-border}) !default;
+$input-height:                          add($input-line-height * 1em, add($input-padding-y * 2, $input-height-border, false)) !default;
+$input-height-sm:                       add($input-line-height-sm * 1em, add($input-padding-y-sm * 2, $input-height-border, false)) !default;
+$input-height-lg:                       add($input-line-height-lg * 1em, add($input-padding-y-lg * 2, $input-height-border, false)) !default;
 
 $input-transition:                      border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;
 
@@ -434,80 +513,134 @@ $form-check-input-margin-x:             .25rem !default;
 $form-check-inline-margin-x:            .75rem !default;
 $form-check-inline-input-margin-x:      .3125rem !default;
 
+$form-grid-gutter-width:                10px !default;
 $form-group-margin-bottom:              1rem !default;
 
 $input-group-addon-color:               $input-color !default;
 $input-group-addon-bg:                  $gray-200 !default;
 $input-group-addon-border-color:        $input-border-color !default;
 
-$custom-control-gutter:                 1.5rem !default;
+$custom-forms-transition:               background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;
+
+$custom-control-gutter:                 .5rem !default;
 $custom-control-spacer-x:               1rem !default;
+$custom-control-cursor:                 null !default;
 
 $custom-control-indicator-size:         1rem !default;
-$custom-control-indicator-bg:           $gray-300 !default;
+$custom-control-indicator-bg:           $input-bg !default;
+
 $custom-control-indicator-bg-size:      50% 50% !default;
-$custom-control-indicator-box-shadow:   inset 0 .25rem .25rem rgba($black, .1) !default;
+$custom-control-indicator-box-shadow:   $input-box-shadow !default;
+$custom-control-indicator-border-color: $gray-500 !default;
+$custom-control-indicator-border-width: $input-border-width !default;
+
+$custom-control-label-color:            null !default;
 
-$custom-control-indicator-disabled-bg:          $gray-200 !default;
-$custom-control-label-disabled-color:     $gray-600 !default;
+$custom-control-indicator-disabled-bg:          $input-disabled-bg !default;
+$custom-control-label-disabled-color:           $gray-600 !default;
 
 $custom-control-indicator-checked-color:        $component-active-color !default;
 $custom-control-indicator-checked-bg:           $component-active-bg !default;
 $custom-control-indicator-checked-disabled-bg:  rgba(theme-color("primary"), .5) !default;
-$custom-control-indicator-checked-box-shadow:   none !default;
+$custom-control-indicator-checked-box-shadow:   null !default;
+$custom-control-indicator-checked-border-color: $custom-control-indicator-checked-bg !default;
 
-$custom-control-indicator-focus-box-shadow:     0 0 0 1px $body-bg, $input-btn-focus-box-shadow !default;
+$custom-control-indicator-focus-box-shadow:     $input-focus-box-shadow !default;
+$custom-control-indicator-focus-border-color:   $input-focus-border-color !default;
 
 $custom-control-indicator-active-color:         $component-active-color !default;
 $custom-control-indicator-active-bg:            lighten($component-active-bg, 35%) !default;
-$custom-control-indicator-active-box-shadow:    none !default;
+$custom-control-indicator-active-box-shadow:    null !default;
+$custom-control-indicator-active-border-color:  $custom-control-indicator-active-bg !default;
 
 $custom-checkbox-indicator-border-radius:       $border-radius !default;
-$custom-checkbox-indicator-icon-checked:        str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='#{$custom-control-indicator-checked-color}' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E"), "#", "%23") !default;
+$custom-checkbox-indicator-icon-checked:        url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'><path fill='#{$custom-control-indicator-checked-color}' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/></svg>") !default;
 
-$custom-checkbox-indicator-indeterminate-bg:    $component-active-bg !default;
-$custom-checkbox-indicator-indeterminate-color: $custom-control-indicator-checked-color !default;
-$custom-checkbox-indicator-icon-indeterminate:  str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='#{$custom-checkbox-indicator-indeterminate-color}' d='M0 2h4'/%3E%3C/svg%3E"), "#", "%23") !default;
-$custom-checkbox-indicator-indeterminate-box-shadow: none !default;
+$custom-checkbox-indicator-indeterminate-bg:           $component-active-bg !default;
+$custom-checkbox-indicator-indeterminate-color:        $custom-control-indicator-checked-color !default;
+$custom-checkbox-indicator-icon-indeterminate:         url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'><path stroke='#{$custom-checkbox-indicator-indeterminate-color}' d='M0 2h4'/></svg>") !default;
+$custom-checkbox-indicator-indeterminate-box-shadow:   null !default;
+$custom-checkbox-indicator-indeterminate-border-color: $custom-checkbox-indicator-indeterminate-bg !default;
 
 $custom-radio-indicator-border-radius:          50% !default;
-$custom-radio-indicator-icon-checked:           str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='#{$custom-control-indicator-checked-color}'/%3E%3C/svg%3E"), "#", "%23") !default;
+$custom-radio-indicator-icon-checked:           url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'><circle r='3' fill='#{$custom-control-indicator-checked-color}'/></svg>") !default;
+
+$custom-switch-width:                           $custom-control-indicator-size * 1.75 !default;
+$custom-switch-indicator-border-radius:         $custom-control-indicator-size * .5 !default;
+$custom-switch-indicator-size:                  subtract($custom-control-indicator-size, $custom-control-indicator-border-width * 4) !default;
 
-$custom-select-padding-y:           .375rem !default;
-$custom-select-padding-x:          .75rem !default;
+$custom-select-padding-y:           $input-padding-y !default;
+$custom-select-padding-x:           $input-padding-x !default;
+$custom-select-font-family:         $input-font-family !default;
+$custom-select-font-size:           $input-font-size !default;
 $custom-select-height:              $input-height !default;
 $custom-select-indicator-padding:   1rem !default; // Extra padding to account for the presence of the background-image based indicator
-$custom-select-line-height:         $input-btn-line-height !default;
+$custom-select-font-weight:         $input-font-weight !default;
+$custom-select-line-height:         $input-line-height !default;
 $custom-select-color:               $input-color !default;
 $custom-select-disabled-color:      $gray-600 !default;
-$custom-select-bg:                  $white !default;
+$custom-select-bg:                  $input-bg !default;
 $custom-select-disabled-bg:         $gray-200 !default;
 $custom-select-bg-size:             8px 10px !default; // In pixels because image dimensions
 $custom-select-indicator-color:     $gray-800 !default;
-$custom-select-indicator:           str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='#{$custom-select-indicator-color}' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E"), "#", "%23") !default;
-$custom-select-border-width:        $input-btn-border-width !default;
+$custom-select-indicator:           url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'><path fill='#{$custom-select-indicator-color}' d='M2 0L0 2h4zm0 5L0 3h4z'/></svg>") !default;
+$custom-select-background:          escape-svg($custom-select-indicator) right $custom-select-padding-x center / $custom-select-bg-size no-repeat !default; // Used so we can have multiple background elements (e.g., arrow and feedback icon)
+
+$custom-select-feedback-icon-padding-right: add(1em * .75, (2 * $custom-select-padding-y * .75) + $custom-select-padding-x + $custom-select-indicator-padding) !default;
+$custom-select-feedback-icon-position:      center right ($custom-select-padding-x + $custom-select-indicator-padding) !default;
+$custom-select-feedback-icon-size:          $input-height-inner-half $input-height-inner-half !default;
+
+$custom-select-border-width:        $input-border-width !default;
 $custom-select-border-color:        $input-border-color !default;
 $custom-select-border-radius:       $border-radius !default;
+$custom-select-box-shadow:          inset 0 1px 2px rgba($black, .075) !default;
 
 $custom-select-focus-border-color:  $input-focus-border-color !default;
-$custom-select-focus-box-shadow:    inset 0 1px 2px rgba($black, .075), 0 0 5px rgba($custom-select-focus-border-color, .5) !default;
+$custom-select-focus-width:         $input-focus-width !default;
+$custom-select-focus-box-shadow:    0 0 0 $custom-select-focus-width $input-btn-focus-color !default;
 
-$custom-select-font-size-sm:        75% !default;
+$custom-select-padding-y-sm:        $input-padding-y-sm !default;
+$custom-select-padding-x-sm:        $input-padding-x-sm !default;
+$custom-select-font-size-sm:        $input-font-size-sm !default;
 $custom-select-height-sm:           $input-height-sm !default;
 
-$custom-select-font-size-lg:        125% !default;
+$custom-select-padding-y-lg:        $input-padding-y-lg !default;
+$custom-select-padding-x-lg:        $input-padding-x-lg !default;
+$custom-select-font-size-lg:        $input-font-size-lg !default;
 $custom-select-height-lg:           $input-height-lg !default;
 
+$custom-range-track-width:          100% !default;
+$custom-range-track-height:         .5rem !default;
+$custom-range-track-cursor:         pointer !default;
+$custom-range-track-bg:             $gray-300 !default;
+$custom-range-track-border-radius:  1rem !default;
+$custom-range-track-box-shadow:     inset 0 .25rem .25rem rgba($black, .1) !default;
+
+$custom-range-thumb-width:                   1rem !default;
+$custom-range-thumb-height:                  $custom-range-thumb-width !default;
+$custom-range-thumb-bg:                      $component-active-bg !default;
+$custom-range-thumb-border:                  0 !default;
+$custom-range-thumb-border-radius:           1rem !default;
+$custom-range-thumb-box-shadow:              0 .1rem .25rem rgba($black, .1) !default;
+$custom-range-thumb-focus-box-shadow:        0 0 0 1px $body-bg, $input-focus-box-shadow !default;
+$custom-range-thumb-focus-box-shadow-width:  $input-focus-width !default; // For focus box shadow issue in IE/Edge
+$custom-range-thumb-active-bg:               lighten($component-active-bg, 35%) !default;
+$custom-range-thumb-disabled-bg:             $gray-500 !default;
+
 $custom-file-height:                $input-height !default;
+$custom-file-height-inner:          $input-height-inner !default;
 $custom-file-focus-border-color:    $input-focus-border-color !default;
-$custom-file-focus-box-shadow:      $input-btn-focus-box-shadow !default;
-
-$custom-file-padding-y:             $input-btn-padding-y !default;
-$custom-file-padding-x:             $input-btn-padding-x !default;
-$custom-file-line-height:           $input-btn-line-height !default;
+$custom-file-focus-box-shadow:      $input-focus-box-shadow !default;
+$custom-file-disabled-bg:           $input-disabled-bg !default;
+
+$custom-file-padding-y:             $input-padding-y !default;
+$custom-file-padding-x:             $input-padding-x !default;
+$custom-file-line-height:           $input-line-height !default;
+$custom-file-font-family:           $input-font-family !default;
+$custom-file-font-weight:           $input-font-weight !default;
 $custom-file-color:                 $input-color !default;
 $custom-file-bg:                    $input-bg !default;
-$custom-file-border-width:          $input-btn-border-width !default;
+$custom-file-border-width:          $input-border-width !default;
 $custom-file-border-color:          $input-border-color !default;
 $custom-file-border-radius:         $input-border-radius !default;
 $custom-file-box-shadow:            $input-box-shadow !default;
@@ -519,40 +652,31 @@ $custom-file-text: (
 
 
 // Form validation
+
 $form-feedback-margin-top:          $form-text-margin-top !default;
 $form-feedback-font-size:           $small-font-size !default;
 $form-feedback-valid-color:         theme-color("success") !default;
 $form-feedback-invalid-color:       theme-color("danger") !default;
 
-
-// Dropdowns
-//
-// Dropdown menu container and contents.
-
-$dropdown-min-width:                10rem !default;
-$dropdown-padding-y:                .5rem !default;
-$dropdown-spacer:                   .125rem !default;
-$dropdown-bg:                       $white !default;
-$dropdown-border-color:             rgba($black, .15) !default;
-$dropdown-border-radius:            $border-radius !default;
-$dropdown-border-width:             $border-width !default;
-$dropdown-divider-bg:               $gray-200 !default;
-$dropdown-box-shadow:               0 .5rem 1rem rgba($black, .175) !default;
-
-$dropdown-link-color:               $gray-900 !default;
-$dropdown-link-hover-color:         darken($gray-900, 5%) !default;
-$dropdown-link-hover-bg:            $gray-100 !default;
-
-$dropdown-link-active-color:        $component-active-color !default;
-$dropdown-link-active-bg:           $component-active-bg !default;
-
-$dropdown-link-disabled-color:      $gray-600 !default;
-
-$dropdown-item-padding-y:           .25rem !default;
-$dropdown-item-padding-x:           1.5rem !default;
-
-$dropdown-header-color:             $gray-600 !default;
-
+$form-feedback-icon-valid-color:    $form-feedback-valid-color !default;
+$form-feedback-icon-valid:          url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'><path fill='#{$form-feedback-icon-valid-color}' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/></svg>") !default;
+$form-feedback-icon-invalid-color:  $form-feedback-invalid-color !default;
+$form-feedback-icon-invalid:        url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='#{$form-feedback-icon-invalid-color}' viewBox='0 0 12 12'><circle cx='6' cy='6' r='4.5'/><path stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/><circle cx='6' cy='8.2' r='.6' fill='#{$form-feedback-icon-invalid-color}' stroke='none'/></svg>") !default;
+
+$form-validation-states: () !default;
+$form-validation-states: map-merge(
+  (
+    "valid": (
+      "color": $form-feedback-valid-color,
+      "icon": $form-feedback-icon-valid
+    ),
+    "invalid": (
+      "color": $form-feedback-invalid-color,
+      "icon": $form-feedback-icon-invalid
+    ),
+  ),
+  $form-validation-states
+);
 
 // Z-index master list
 //
@@ -567,6 +691,7 @@ $zindex-modal:                      1050 !default;
 $zindex-popover:                    1060 !default;
 $zindex-tooltip:                    1070 !default;
 
+
 // Navs
 
 $nav-link-padding-y:                .5rem !default;
@@ -585,38 +710,85 @@ $nav-pills-border-radius:           $border-radius !default;
 $nav-pills-link-active-color:       $component-active-color !default;
 $nav-pills-link-active-bg:          $component-active-bg !default;
 
+$nav-divider-color:                 $gray-200 !default;
+$nav-divider-margin-y:              $spacer * .5 !default;
+
+
 // Navbar
 
-$navbar-padding-y:                  ($spacer / 2) !default;
+$navbar-padding-y:                  $spacer * .5 !default;
 $navbar-padding-x:                  $spacer !default;
 
 $navbar-nav-link-padding-x:         .5rem !default;
 
 $navbar-brand-font-size:            $font-size-lg !default;
 // Compute the navbar-brand padding-y so the navbar-brand will have the same height as navbar-text and nav-link
-$nav-link-height:                   ($font-size-base * $line-height-base + $nav-link-padding-y * 2) !default;
+$nav-link-height:                   $font-size-base * $line-height-base + $nav-link-padding-y * 2 !default;
 $navbar-brand-height:               $navbar-brand-font-size * $line-height-base !default;
-$navbar-brand-padding-y:            ($nav-link-height - $navbar-brand-height) / 2 !default;
+$navbar-brand-padding-y:            ($nav-link-height - $navbar-brand-height) * .5 !default;
 
 $navbar-toggler-padding-y:          .25rem !default;
 $navbar-toggler-padding-x:          .75rem !default;
 $navbar-toggler-font-size:          $font-size-lg !default;
 $navbar-toggler-border-radius:      $btn-border-radius !default;
 
+$navbar-nav-scroll-max-height:      75vh !default;
+
 $navbar-dark-color:                 rgba($white, .5) !default;
 $navbar-dark-hover-color:           rgba($white, .75) !default;
 $navbar-dark-active-color:          $white !default;
 $navbar-dark-disabled-color:        rgba($white, .25) !default;
-$navbar-dark-toggler-icon-bg:       str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{$navbar-dark-color}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"), "#", "%23") !default;
+$navbar-dark-toggler-icon-bg:       url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'><path stroke='#{$navbar-dark-color}' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/></svg>") !default;
 $navbar-dark-toggler-border-color:  rgba($white, .1) !default;
 
 $navbar-light-color:                rgba($black, .5) !default;
 $navbar-light-hover-color:          rgba($black, .7) !default;
 $navbar-light-active-color:         rgba($black, .9) !default;
 $navbar-light-disabled-color:       rgba($black, .3) !default;
-$navbar-light-toggler-icon-bg:      str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{$navbar-light-color}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"), "#", "%23") !default;
+$navbar-light-toggler-icon-bg:      url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'><path stroke='#{$navbar-light-color}' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/></svg>") !default;
 $navbar-light-toggler-border-color: rgba($black, .1) !default;
 
+$navbar-light-brand-color:                $navbar-light-active-color !default;
+$navbar-light-brand-hover-color:          $navbar-light-active-color !default;
+$navbar-dark-brand-color:                 $navbar-dark-active-color !default;
+$navbar-dark-brand-hover-color:           $navbar-dark-active-color !default;
+
+
+// Dropdowns
+//
+// Dropdown menu container and contents.
+
+$dropdown-min-width:                10rem !default;
+$dropdown-padding-x:                0 !default;
+$dropdown-padding-y:                .5rem !default;
+$dropdown-spacer:                   .125rem !default;
+$dropdown-font-size:                $font-size-base !default;
+$dropdown-color:                    $body-color !default;
+$dropdown-bg:                       $white !default;
+$dropdown-border-color:             rgba($black, .15) !default;
+$dropdown-border-radius:            $border-radius !default;
+$dropdown-border-width:             $border-width !default;
+$dropdown-inner-border-radius:      subtract($dropdown-border-radius, $dropdown-border-width) !default;
+$dropdown-divider-bg:               $gray-200 !default;
+$dropdown-divider-margin-y:         $nav-divider-margin-y !default;
+$dropdown-box-shadow:               0 .5rem 1rem rgba($black, .175) !default;
+
+$dropdown-link-color:               $gray-900 !default;
+$dropdown-link-hover-color:         darken($gray-900, 5%) !default;
+$dropdown-link-hover-bg:            $gray-200 !default;
+
+$dropdown-link-active-color:        $component-active-color !default;
+$dropdown-link-active-bg:           $component-active-bg !default;
+
+$dropdown-link-disabled-color:      $gray-500 !default;
+
+$dropdown-item-padding-y:           .25rem !default;
+$dropdown-item-padding-x:           1.5rem !default;
+
+$dropdown-header-color:             $gray-600 !default;
+$dropdown-header-padding:           $dropdown-padding-y $dropdown-item-padding-x !default;
+
+
 // Pagination
 
 $pagination-padding-y:              .5rem !default;
@@ -633,6 +805,7 @@ $pagination-border-width:           $border-width !default;
 $pagination-border-color:           $gray-300 !default;
 
 $pagination-focus-box-shadow:       $input-btn-focus-box-shadow !default;
+$pagination-focus-outline:          0 !default;
 
 $pagination-hover-color:            $link-hover-color !default;
 $pagination-hover-bg:               $gray-200 !default;
@@ -646,10 +819,14 @@ $pagination-disabled-color:         $gray-600 !default;
 $pagination-disabled-bg:            $white !default;
 $pagination-disabled-border-color:  $gray-300 !default;
 
+$pagination-border-radius-sm:       $border-radius-sm !default;
+$pagination-border-radius-lg:       $border-radius-lg !default;
+
 
 // Jumbotron
 
 $jumbotron-padding:                 2rem !default;
+$jumbotron-color:                   null !default;
 $jumbotron-bg:                      $gray-200 !default;
 
 
@@ -660,13 +837,16 @@ $card-spacer-x:                     1.25rem !default;
 $card-border-width:                 $border-width !default;
 $card-border-radius:                $border-radius !default;
 $card-border-color:                 rgba($black, .125) !default;
-$card-inner-border-radius:          calc(#{$card-border-radius} - #{$card-border-width}) !default;
+$card-inner-border-radius:          subtract($card-border-radius, $card-border-width) !default;
 $card-cap-bg:                       rgba($black, .03) !default;
+$card-cap-color:                    null !default;
+$card-height:                       null !default;
+$card-color:                        null !default;
 $card-bg:                           $white !default;
 
 $card-img-overlay-padding:          1.25rem !default;
 
-$card-group-margin:                 ($grid-gutter-width / 2) !default;
+$card-group-margin:                 $grid-gutter-width * .5 !default;
 $card-deck-margin:                  $card-group-margin !default;
 
 $card-columns-count:                3 !default;
@@ -676,19 +856,27 @@ $card-columns-margin:               $card-spacer-y !default;
 
 // Tooltips
 
-$tooltip-font-size:           $font-size-sm !default;
-$tooltip-max-width:           200px !default;
-$tooltip-color:               $white !default;
-$tooltip-bg:                  $black !default;
-$tooltip-border-radius:        $border-radius !default;
-$tooltip-opacity:             .9 !default;
-$tooltip-padding-y:           .25rem !default;
-$tooltip-padding-x:           .5rem !default;
-$tooltip-margin:              0 !default;
-
-$tooltip-arrow-width:         .8rem !default;
-$tooltip-arrow-height:        .4rem !default;
-$tooltip-arrow-color:         $tooltip-bg !default;
+$tooltip-font-size:                 $font-size-sm !default;
+$tooltip-max-width:                 200px !default;
+$tooltip-color:                     $white !default;
+$tooltip-bg:                        $black !default;
+$tooltip-border-radius:             $border-radius !default;
+$tooltip-opacity:                   .9 !default;
+$tooltip-padding-y:                 .25rem !default;
+$tooltip-padding-x:                 .5rem !default;
+$tooltip-margin:                    0 !default;
+
+$tooltip-arrow-width:               .8rem !default;
+$tooltip-arrow-height:              .4rem !default;
+$tooltip-arrow-color:               $tooltip-bg !default;
+
+// Form tooltips must come after regular tooltips
+$form-feedback-tooltip-padding-y:     $tooltip-padding-y !default;
+$form-feedback-tooltip-padding-x:     $tooltip-padding-x !default;
+$form-feedback-tooltip-font-size:     $tooltip-font-size !default;
+$form-feedback-tooltip-line-height:   $line-height-base !default;
+$form-feedback-tooltip-opacity:       $tooltip-opacity !default;
+$form-feedback-tooltip-border-radius: $tooltip-border-radius !default;
 
 
 // Popovers
@@ -699,6 +887,7 @@ $popover-max-width:                 276px !default;
 $popover-border-width:              $border-width !default;
 $popover-border-color:              rgba($black, .2) !default;
 $popover-border-radius:             $border-radius-lg !default;
+$popover-inner-border-radius:       subtract($popover-border-radius, $popover-border-width) !default;
 $popover-box-shadow:                0 .25rem .5rem rgba($black, .2) !default;
 
 $popover-header-bg:                 darken($popover-bg, 3%) !default;
@@ -717,6 +906,24 @@ $popover-arrow-color:               $popover-bg !default;
 $popover-arrow-outer-color:         fade-in($popover-border-color, .05) !default;
 
 
+// Toasts
+
+$toast-max-width:                   350px !default;
+$toast-padding-x:                   .75rem !default;
+$toast-padding-y:                   .25rem !default;
+$toast-font-size:                   .875rem !default;
+$toast-color:                       null !default;
+$toast-background-color:            rgba($white, .85) !default;
+$toast-border-width:                1px !default;
+$toast-border-color:                rgba(0, 0, 0, .1) !default;
+$toast-border-radius:               .25rem !default;
+$toast-box-shadow:                  0 .25rem .75rem rgba($black, .1) !default;
+
+$toast-header-color:                $gray-600 !default;
+$toast-header-background-color:     rgba($white, .85) !default;
+$toast-header-border-color:         rgba(0, 0, 0, .05) !default;
+
+
 // Badges
 
 $badge-font-size:                   75% !default;
@@ -725,6 +932,9 @@ $badge-padding-y:                   .25em !default;
 $badge-padding-x:                   .4em !default;
 $badge-border-radius:               $border-radius !default;
 
+$badge-transition:                  $btn-transition !default;
+$badge-focus-width:                 $input-btn-focus-width !default;
+
 $badge-pill-padding-x:              .6em !default;
 // Use a higher than normal value to ensure completely rounded edges when
 // customizing padding or font-size on labels.
@@ -734,32 +944,44 @@ $badge-pill-border-radius:          10rem !default;
 // Modals
 
 // Padding applied to the modal body
-$modal-inner-padding:         1rem !default;
-
-$modal-dialog-margin:         .5rem !default;
-$modal-dialog-margin-y-sm-up: 1.75rem !default;
+$modal-inner-padding:               1rem !default;
 
-$modal-title-line-height:           $line-height-base !default;
+// Margin between elements in footer, must be lower than or equal to 2 * $modal-inner-padding
+$modal-footer-margin-between:       .5rem !default;
 
-$modal-content-bg:               $white !default;
-$modal-content-border-color:     rgba($black, .2) !default;
-$modal-content-border-width:     $border-width !default;
-$modal-content-box-shadow-xs:    0 .25rem .5rem rgba($black, .5) !default;
-$modal-content-box-shadow-sm-up: 0 .5rem 1rem rgba($black, .5) !default;
+$modal-dialog-margin:               .5rem !default;
+$modal-dialog-margin-y-sm-up:       1.75rem !default;
 
-$modal-backdrop-bg:           $black !default;
-$modal-backdrop-opacity:      .5 !default;
-$modal-header-border-color:   $gray-200 !default;
-$modal-footer-border-color:   $modal-header-border-color !default;
-$modal-header-border-width:   $modal-content-border-width !default;
-$modal-footer-border-width:   $modal-header-border-width !default;
-$modal-header-padding:        1rem !default;
+$modal-title-line-height:           $line-height-base !default;
 
+$modal-content-color:               null !default;
+$modal-content-bg:                  $white !default;
+$modal-content-border-color:        rgba($black, .2) !default;
+$modal-content-border-width:        $border-width !default;
+$modal-content-border-radius:       $border-radius-lg !default;
+$modal-content-inner-border-radius: subtract($modal-content-border-radius, $modal-content-border-width) !default;
+$modal-content-box-shadow-xs:       0 .25rem .5rem rgba($black, .5) !default;
+$modal-content-box-shadow-sm-up:    0 .5rem 1rem rgba($black, .5) !default;
+
+$modal-backdrop-bg:                 $black !default;
+$modal-backdrop-opacity:            .5 !default;
+$modal-header-border-color:         $border-color !default;
+$modal-footer-border-color:         $modal-header-border-color !default;
+$modal-header-border-width:         $modal-content-border-width !default;
+$modal-footer-border-width:         $modal-header-border-width !default;
+$modal-header-padding-y:            1rem !default;
+$modal-header-padding-x:            1rem !default;
+$modal-header-padding:              $modal-header-padding-y $modal-header-padding-x !default; // Keep this for backwards compatibility
+
+$modal-xl:                          1140px !default;
 $modal-lg:                          800px !default;
 $modal-md:                          500px !default;
 $modal-sm:                          300px !default;
 
+$modal-fade-transform:              translate(0, -50px) !default;
+$modal-show-transform:              none !default;
 $modal-transition:                  transform .3s ease-out !default;
+$modal-scale-transform:             scale(1.02) !default;
 
 
 // Alerts
@@ -781,7 +1003,7 @@ $alert-color-level:                 6 !default;
 // Progress bars
 
 $progress-height:                   1rem !default;
-$progress-font-size:                ($font-size-base * .75) !default;
+$progress-font-size:                $font-size-base * .75 !default;
 $progress-bg:                       $gray-200 !default;
 $progress-border-radius:            $border-radius !default;
 $progress-box-shadow:               inset 0 .1rem .1rem rgba($black, .1) !default;
@@ -790,8 +1012,10 @@ $progress-bar-bg:                   theme-color("primary") !default;
 $progress-bar-animation-timing:     1s linear infinite !default;
 $progress-bar-transition:           width .6s ease !default;
 
+
 // List group
 
+$list-group-color:                  null !default;
 $list-group-bg:                     $white !default;
 $list-group-border-color:           rgba($black, .125) !default;
 $list-group-border-width:           $border-width !default;
@@ -833,6 +1057,8 @@ $figure-caption-color:              $gray-600 !default;
 
 // Breadcrumbs
 
+$breadcrumb-font-size:              null !default;
+
 $breadcrumb-padding-y:              .75rem !default;
 $breadcrumb-padding-x:              1rem !default;
 $breadcrumb-item-padding:           .5rem !default;
@@ -842,29 +1068,48 @@ $breadcrumb-margin-bottom:          1rem !default;
 $breadcrumb-bg:                     $gray-200 !default;
 $breadcrumb-divider-color:          $gray-600 !default;
 $breadcrumb-active-color:           $gray-600 !default;
-$breadcrumb-divider:                "/" !default;
+$breadcrumb-divider:                quote("/") !default;
+
+$breadcrumb-border-radius:          $border-radius !default;
 
 
 // Carousel
 
-$carousel-control-color:            $white !default;
-$carousel-control-width:            15% !default;
-$carousel-control-opacity:          .5 !default;
+$carousel-control-color:             $white !default;
+$carousel-control-width:             15% !default;
+$carousel-control-opacity:           .5 !default;
+$carousel-control-hover-opacity:     .9 !default;
+$carousel-control-transition:        opacity .15s ease !default;
+
+$carousel-indicator-width:           30px !default;
+$carousel-indicator-height:          3px !default;
+$carousel-indicator-hit-area-height: 10px !default;
+$carousel-indicator-spacer:          3px !default;
+$carousel-indicator-active-bg:       $white !default;
+$carousel-indicator-transition:      opacity .6s ease !default;
+
+$carousel-caption-width:             70% !default;
+$carousel-caption-color:             $white !default;
+
+$carousel-control-icon-width:        20px !default;
 
-$carousel-indicator-width:          30px !default;
-$carousel-indicator-height:         3px !default;
-$carousel-indicator-spacer:         3px !default;
-$carousel-indicator-active-bg:      $white !default;
+$carousel-control-prev-icon-bg:      url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' fill='#{$carousel-control-color}' width='8' height='8' viewBox='0 0 8 8'><path d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/></svg>") !default;
+$carousel-control-next-icon-bg:      url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' fill='#{$carousel-control-color}' width='8' height='8' viewBox='0 0 8 8'><path d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/></svg>") !default;
 
-$carousel-caption-width:            70% !default;
-$carousel-caption-color:            $white !default;
+$carousel-transition-duration:       .6s !default;
+$carousel-transition:                transform $carousel-transition-duration ease-in-out !default; // Define transform transition first if using multiple transitions (e.g., `transform 2s ease, opacity .5s ease-out`)
 
-$carousel-control-icon-width:       20px !default;
 
-$carousel-control-prev-icon-bg:     str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='#{$carousel-control-color}' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E"), "#", "%23") !default;
-$carousel-control-next-icon-bg:     str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='#{$carousel-control-color}' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E"), "#", "%23") !default;
+// Spinners
 
-$carousel-transition:               transform .6s ease !default;
+$spinner-width:           2rem !default;
+$spinner-height:          $spinner-width !default;
+$spinner-vertical-align:  -.125em !default;
+$spinner-border-width:    .25em !default;
+
+$spinner-width-sm:        1rem !default;
+$spinner-height-sm:       $spinner-width-sm !default;
+$spinner-border-width-sm: .2em !default;
 
 
 // Close
@@ -874,6 +1119,7 @@ $close-font-weight:                 $font-weight-bold !default;
 $close-color:                       $black !default;
 $close-text-shadow:                 0 1px 0 $white !default;
 
+
 // Code
 
 $code-font-size:                    87.5% !default;
@@ -889,6 +1135,15 @@ $pre-color:                         $gray-900 !default;
 $pre-scrollable-max-height:         340px !default;
 
 
+// Utilities
+
+$displays: none, inline, inline-block, block, table, table-row, table-cell, flex, inline-flex !default;
+$overflows: auto, hidden !default;
+$positions: static, relative, absolute, fixed, sticky !default;
+$user-selects: all, auto, none !default;
+
+
 // Printing
+
 $print-page-size:                   a3 !default;
 $print-body-min-width:              map-get($grid-breakpoints, "lg") !default;
diff --git a/src/css/bootstrap/bootstrap-grid.scss b/src/css/bootstrap/bootstrap-grid.scss
index 26c0dc89c..1d529a661 100644
--- a/src/css/bootstrap/bootstrap-grid.scss
+++ b/src/css/bootstrap/bootstrap-grid.scss
@@ -1,14 +1,10 @@
 /*!
- * Bootstrap Grid v4.0.0 (https://getbootstrap.com)
- * Copyright 2011-2018 The Bootstrap Authors
- * Copyright 2011-2018 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * Bootstrap Grid v4.6.2 (https://getbootstrap.com/)
+ * Copyright 2011-2022 The Bootstrap Authors
+ * Copyright 2011-2022 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  */
 
-@at-root {
-  @-ms-viewport { width: device-width; } // stylelint-disable-line at-rule-no-vendor-prefix
-}
-
 html {
   box-sizing: border-box;
   -ms-overflow-style: scrollbar;
@@ -23,6 +19,7 @@ html {
 @import "functions";
 @import "variables";
 
+@import "mixins/deprecate";
 @import "mixins/breakpoints";
 @import "mixins/grid-framework";
 @import "mixins/grid";
@@ -30,3 +27,4 @@ html {
 @import "grid";
 @import "utilities/display";
 @import "utilities/flex";
+@import "utilities/spacing";
diff --git a/src/css/bootstrap/bootstrap-reboot.scss b/src/css/bootstrap/bootstrap-reboot.scss
index 46c4610d8..8a5fcf003 100644
--- a/src/css/bootstrap/bootstrap-reboot.scss
+++ b/src/css/bootstrap/bootstrap-reboot.scss
@@ -1,8 +1,8 @@
 /*!
- * Bootstrap Reboot v4.0.0 (https://getbootstrap.com)
- * Copyright 2011-2018 The Bootstrap Authors
- * Copyright 2011-2018 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * Bootstrap Reboot v4.6.2 (https://getbootstrap.com/)
+ * Copyright 2011-2022 The Bootstrap Authors
+ * Copyright 2011-2022 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
  */
 
diff --git a/src/css/bootstrap/bootstrap.scss b/src/css/bootstrap/bootstrap.scss
index bb5fac868..ff38ed033 100644
--- a/src/css/bootstrap/bootstrap.scss
+++ b/src/css/bootstrap/bootstrap.scss
@@ -1,8 +1,8 @@
 /*!
- * Bootstrap v4.0.0 (https://getbootstrap.com)
- * Copyright 2011-2018 The Bootstrap Authors
- * Copyright 2011-2018 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * Bootstrap v4.6.2 (https://getbootstrap.com/)
+ * Copyright 2011-2022 The Bootstrap Authors
+ * Copyright 2011-2022 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  */
 
 @import "functions";
@@ -34,9 +34,11 @@
 @import "media";
 @import "list-group";
 @import "close";
+@import "toasts";
 @import "modal";
 @import "tooltip";
 @import "popover";
 @import "carousel";
+@import "spinners";
 @import "utilities";
 @import "print";
diff --git a/src/css/bootstrap/mixins/_background-variant.scss b/src/css/bootstrap/mixins/_background-variant.scss
index 494439d2b..80580189a 100644
--- a/src/css/bootstrap/mixins/_background-variant.scss
+++ b/src/css/bootstrap/mixins/_background-variant.scss
@@ -2,20 +2,22 @@
 
 // Contextual backgrounds
 
-@mixin bg-variant($parent, $color) {
+@mixin bg-variant($parent, $color, $ignore-warning: false) {
   #{$parent} {
     background-color: $color !important;
   }
   a#{$parent},
   button#{$parent} {
-    @include hover-focus {
+    @include hover-focus() {
       background-color: darken($color, 10%) !important;
     }
   }
+  @include deprecate("The `bg-variant` mixin", "v4.4.0", "v5", $ignore-warning);
 }
 
-@mixin bg-gradient-variant($parent, $color) {
+@mixin bg-gradient-variant($parent, $color, $ignore-warning: false) {
   #{$parent} {
     background: $color linear-gradient(180deg, mix($body-bg, $color, 15%), $color) repeat-x !important;
   }
+  @include deprecate("The `bg-gradient-variant` mixin", "v4.5.0", "v5", $ignore-warning);
 }
diff --git a/src/css/bootstrap/mixins/_badge.scss b/src/css/bootstrap/mixins/_badge.scss
index eeca0b40d..f1c499141 100644
--- a/src/css/bootstrap/mixins/_badge.scss
+++ b/src/css/bootstrap/mixins/_badge.scss
@@ -2,11 +2,16 @@
   color: color-yiq($bg);
   background-color: $bg;
 
-  &[href] {
-    @include hover-focus {
+  @at-root a#{&} {
+    @include hover-focus() {
       color: color-yiq($bg);
-      text-decoration: none;
       background-color: darken($bg, 10%);
     }
+
+    &:focus,
+    &.focus {
+      outline: 0;
+      box-shadow: 0 0 0 $badge-focus-width rgba($bg, .5);
+    }
   }
 }
diff --git a/src/css/bootstrap/mixins/_border-radius.scss b/src/css/bootstrap/mixins/_border-radius.scss
index 2024febcf..4fad91d67 100644
--- a/src/css/bootstrap/mixins/_border-radius.scss
+++ b/src/css/bootstrap/mixins/_border-radius.scss
@@ -1,35 +1,76 @@
+// stylelint-disable property-disallowed-list
 // Single side border-radius
 
-@mixin border-radius($radius: $border-radius) {
+// Helper function to replace negative values with 0
+@function valid-radius($radius) {
+  $return: ();
+  @each $value in $radius {
+    @if type-of($value) == number {
+      $return: append($return, max($value, 0));
+    } @else {
+      $return: append($return, $value);
+    }
+  }
+  @return $return;
+}
+
+@mixin border-radius($radius: $border-radius, $fallback-border-radius: false) {
   @if $enable-rounded {
-    border-radius: $radius;
+    border-radius: valid-radius($radius);
+  }
+  @else if $fallback-border-radius != false {
+    border-radius: $fallback-border-radius;
   }
 }
 
 @mixin border-top-radius($radius) {
   @if $enable-rounded {
-    border-top-left-radius: $radius;
-    border-top-right-radius: $radius;
+    border-top-left-radius: valid-radius($radius);
+    border-top-right-radius: valid-radius($radius);
   }
 }
 
 @mixin border-right-radius($radius) {
   @if $enable-rounded {
-    border-top-right-radius: $radius;
-    border-bottom-right-radius: $radius;
+    border-top-right-radius: valid-radius($radius);
+    border-bottom-right-radius: valid-radius($radius);
   }
 }
 
 @mixin border-bottom-radius($radius) {
   @if $enable-rounded {
-    border-bottom-right-radius: $radius;
-    border-bottom-left-radius: $radius;
+    border-bottom-right-radius: valid-radius($radius);
+    border-bottom-left-radius: valid-radius($radius);
   }
 }
 
 @mixin border-left-radius($radius) {
   @if $enable-rounded {
-    border-top-left-radius: $radius;
-    border-bottom-left-radius: $radius;
+    border-top-left-radius: valid-radius($radius);
+    border-bottom-left-radius: valid-radius($radius);
+  }
+}
+
+@mixin border-top-left-radius($radius) {
+  @if $enable-rounded {
+    border-top-left-radius: valid-radius($radius);
+  }
+}
+
+@mixin border-top-right-radius($radius) {
+  @if $enable-rounded {
+    border-top-right-radius: valid-radius($radius);
+  }
+}
+
+@mixin border-bottom-right-radius($radius) {
+  @if $enable-rounded {
+    border-bottom-right-radius: valid-radius($radius);
+  }
+}
+
+@mixin border-bottom-left-radius($radius) {
+  @if $enable-rounded {
+    border-bottom-left-radius: valid-radius($radius);
   }
 }
diff --git a/src/css/bootstrap/mixins/_box-shadow.scss b/src/css/bootstrap/mixins/_box-shadow.scss
index b2410e53a..0726d4359 100644
--- a/src/css/bootstrap/mixins/_box-shadow.scss
+++ b/src/css/bootstrap/mixins/_box-shadow.scss
@@ -1,5 +1,20 @@
 @mixin box-shadow($shadow...) {
   @if $enable-shadows {
-    box-shadow: $shadow;
+    $result: ();
+
+    @if (length($shadow) == 1) {
+      // We can pass `@include box-shadow(none);`
+      $result: $shadow;
+    } @else {
+      // Filter to avoid invalid properties for example `box-shadow: none, 1px 1px black;`
+      @for $i from 1 through length($shadow) {
+        @if nth($shadow, $i) != "none" {
+          $result: append($result, nth($shadow, $i), "comma");
+        }
+      }
+    }
+    @if (length($result) > 0) {
+      box-shadow: $result;
+    }
   }
 }
diff --git a/src/css/bootstrap/mixins/_breakpoints.scss b/src/css/bootstrap/mixins/_breakpoints.scss
index d1ad684cc..23a5de96b 100644
--- a/src/css/bootstrap/mixins/_breakpoints.scss
+++ b/src/css/bootstrap/mixins/_breakpoints.scss
@@ -16,7 +16,7 @@
 //    md
 @function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {
   $n: index($breakpoint-names, $name);
-  @return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);
+  @return if($n != null and $n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);
 }
 
 // Minimum breakpoint width. Null for the smallest (first) breakpoint.
@@ -39,10 +39,10 @@
 //    767.98px
 @function breakpoint-max($name, $breakpoints: $grid-breakpoints) {
   $next: breakpoint-next($name, $breakpoints);
-  @return if($next, breakpoint-min($next, $breakpoints) - .02px, null);
+  @return if($next, breakpoint-min($next, $breakpoints) - .02, null);
 }
 
-// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash infront.
+// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash in front.
 // Useful for making responsive utilities.
 //
 //    >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
diff --git a/src/css/bootstrap/mixins/_buttons.scss b/src/css/bootstrap/mixins/_buttons.scss
index 06ad6772f..d6235aa27 100644
--- a/src/css/bootstrap/mixins/_buttons.scss
+++ b/src/css/bootstrap/mixins/_buttons.scss
@@ -9,7 +9,7 @@
   border-color: $border;
   @include box-shadow($btn-box-shadow);
 
-  @include hover {
+  @include hover() {
     color: color-yiq($hover-background);
     @include gradient-bg($hover-background);
     border-color: $hover-border;
@@ -17,11 +17,14 @@
 
   &:focus,
   &.focus {
-    // Avoid using mixin so we can pass custom focus shadow properly
+    color: color-yiq($hover-background);
+    @include gradient-bg($hover-background);
+    border-color: $hover-border;
     @if $enable-shadows {
-      box-shadow: $btn-box-shadow, 0 0 0 $btn-focus-width rgba($border, .5);
+      @include box-shadow($btn-box-shadow, 0 0 0 $btn-focus-width rgba(mix(color-yiq($background), $border, 15%), .5));
     } @else {
-      box-shadow: 0 0 0 $btn-focus-width rgba($border, .5);
+      // Avoid using mixin so we can pass custom focus shadow properly
+      box-shadow: 0 0 0 $btn-focus-width rgba(mix(color-yiq($background), $border, 15%), .5);
     }
   }
 
@@ -31,6 +34,10 @@
     color: color-yiq($background);
     background-color: $background;
     border-color: $border;
+    // Remove CSS gradients if they're enabled
+    @if $enable-gradients {
+      background-image: none;
+    }
   }
 
   &:not(:disabled):not(.disabled):active,
@@ -44,11 +51,11 @@
     border-color: $active-border;
 
     &:focus {
-      // Avoid using mixin so we can pass custom focus shadow properly
-      @if $enable-shadows {
-        box-shadow: $btn-active-box-shadow, 0 0 0 $btn-focus-width rgba($border, .5);
+      @if $enable-shadows and $btn-active-box-shadow != none {
+        @include box-shadow($btn-active-box-shadow, 0 0 0 $btn-focus-width rgba(mix(color-yiq($background), $border, 15%), .5));
       } @else {
-        box-shadow: 0 0 0 $btn-focus-width rgba($border, .5);
+        // Avoid using mixin so we can pass custom focus shadow properly
+        box-shadow: 0 0 0 $btn-focus-width rgba(mix(color-yiq($background), $border, 15%), .5);
       }
     }
   }
@@ -56,11 +63,9 @@
 
 @mixin button-outline-variant($color, $color-hover: color-yiq($color), $active-background: $color, $active-border: $color) {
   color: $color;
-  background-color: transparent;
-  background-image: none;
   border-color: $color;
 
-  &:hover {
+  @include hover() {
     color: $color-hover;
     background-color: $active-background;
     border-color: $active-border;
@@ -85,10 +90,10 @@
     border-color: $active-border;
 
     &:focus {
-      // Avoid using mixin so we can pass custom focus shadow properly
       @if $enable-shadows and $btn-active-box-shadow != none {
-        box-shadow: $btn-active-box-shadow, 0 0 0 $btn-focus-width rgba($color, .5);
+        @include box-shadow($btn-active-box-shadow, 0 0 0 $btn-focus-width rgba($color, .5));
       } @else {
+        // Avoid using mixin so we can pass custom focus shadow properly
         box-shadow: 0 0 0 $btn-focus-width rgba($color, .5);
       }
     }
@@ -98,12 +103,8 @@
 // Button sizes
 @mixin button-size($padding-y, $padding-x, $font-size, $line-height, $border-radius) {
   padding: $padding-y $padding-x;
-  font-size: $font-size;
+  @include font-size($font-size);
   line-height: $line-height;
   // Manually declare to provide an override to the browser default
-  @if $enable-rounded {
-    border-radius: $border-radius;
-  } @else {
-    border-radius: 0;
-  }
+  @include border-radius($border-radius, 0);
 }
diff --git a/src/css/bootstrap/mixins/_caret.scss b/src/css/bootstrap/mixins/_caret.scss
index 40478e492..27466495b 100644
--- a/src/css/bootstrap/mixins/_caret.scss
+++ b/src/css/bootstrap/mixins/_caret.scss
@@ -1,24 +1,25 @@
-@mixin caret-down {
+@mixin caret-down() {
   border-top: $caret-width solid;
   border-right: $caret-width solid transparent;
   border-bottom: 0;
   border-left: $caret-width solid transparent;
 }
 
-@mixin caret-up {
+@mixin caret-up() {
   border-top: 0;
   border-right: $caret-width solid transparent;
   border-bottom: $caret-width solid;
   border-left: $caret-width solid transparent;
 }
 
-@mixin caret-right {
+@mixin caret-right() {
   border-top: $caret-width solid transparent;
+  border-right: 0;
   border-bottom: $caret-width solid transparent;
   border-left: $caret-width solid;
 }
 
-@mixin caret-left {
+@mixin caret-left() {
   border-top: $caret-width solid transparent;
   border-right: $caret-width solid;
   border-bottom: $caret-width solid transparent;
@@ -28,17 +29,15 @@
   @if $enable-caret {
     &::after {
       display: inline-block;
-      width: 0;
-      height: 0;
-      margin-left: $caret-width * .85;
-      vertical-align: $caret-width * .85;
+      margin-left: $caret-spacing;
+      vertical-align: $caret-vertical-align;
       content: "";
       @if $direction == down {
-        @include caret-down;
+        @include caret-down();
       } @else if $direction == up {
-        @include caret-up;
+        @include caret-up();
       } @else if $direction == right {
-        @include caret-right;
+        @include caret-right();
       }
     }
 
@@ -49,12 +48,10 @@
 
       &::before {
         display: inline-block;
-        width: 0;
-        height: 0;
-        margin-right: $caret-width * .85;
-        vertical-align: $caret-width * .85;
+        margin-right: $caret-spacing;
+        vertical-align: $caret-vertical-align;
         content: "";
-        @include caret-left;
+        @include caret-left();
       }
     }
 
diff --git a/src/css/bootstrap/mixins/_deprecate.scss b/src/css/bootstrap/mixins/_deprecate.scss
new file mode 100644
index 000000000..df070bc59
--- /dev/null
+++ b/src/css/bootstrap/mixins/_deprecate.scss
@@ -0,0 +1,10 @@
+// Deprecate mixin
+//
+// This mixin can be used to deprecate mixins or functions.
+// `$enable-deprecation-messages` is a global variable, `$ignore-warning` is a variable that can be passed to
+// some deprecated mixins to suppress the warning (for example if the mixin is still be used in the current version of Bootstrap)
+@mixin deprecate($name, $deprecate-version, $remove-version, $ignore-warning: false) {
+  @if ($enable-deprecation-messages != false and $ignore-warning != true) {
+    @warn "#{$name} has been deprecated as of #{$deprecate-version}. It will be removed entirely in #{$remove-version}.";
+  }
+}
diff --git a/src/css/bootstrap/mixins/_float.scss b/src/css/bootstrap/mixins/_float.scss
index 48fa8b6d5..6b376a258 100644
--- a/src/css/bootstrap/mixins/_float.scss
+++ b/src/css/bootstrap/mixins/_float.scss
@@ -1,11 +1,14 @@
 // stylelint-disable declaration-no-important
 
-@mixin float-left {
+@mixin float-left() {
   float: left !important;
+  @include deprecate("The `float-left` mixin", "v4.3.0", "v5");
 }
-@mixin float-right {
+@mixin float-right() {
   float: right !important;
+  @include deprecate("The `float-right` mixin", "v4.3.0", "v5");
 }
-@mixin float-none {
+@mixin float-none() {
   float: none !important;
+  @include deprecate("The `float-none` mixin", "v4.3.0", "v5");
 }
diff --git a/src/css/bootstrap/mixins/_forms.scss b/src/css/bootstrap/mixins/_forms.scss
index 229068242..b8847cc80 100644
--- a/src/css/bootstrap/mixins/_forms.scss
+++ b/src/css/bootstrap/mixins/_forms.scss
@@ -10,67 +10,133 @@
 //
 // Example usage: change the default blue border and shadow to white for better
 // contrast against a dark gray background.
-@mixin form-control-focus() {
+@mixin form-control-focus($ignore-warning: false) {
   &:focus {
     color: $input-focus-color;
     background-color: $input-focus-bg;
     border-color: $input-focus-border-color;
     outline: 0;
-    // Avoid using mixin so we can pass custom focus shadow properly
     @if $enable-shadows {
-      box-shadow: $input-box-shadow, $input-focus-box-shadow;
+      @include box-shadow($input-box-shadow, $input-focus-box-shadow);
     } @else {
+      // Avoid using mixin so we can pass custom focus shadow properly
       box-shadow: $input-focus-box-shadow;
     }
   }
+  @include deprecate("The `form-control-focus()` mixin", "v4.4.0", "v5", $ignore-warning);
 }
 
+// This mixin uses an `if()` technique to be compatible with Dart Sass
+// See https://github.com/sass/sass/issues/1873#issuecomment-152293725 for more details
+@mixin form-validation-state-selector($state) {
+  @if ($state == "valid" or $state == "invalid") {
+    .was-validated #{if(&, "&", "")}:#{$state},
+    #{if(&, "&", "")}.is-#{$state} {
+      @content;
+    }
+  } @else {
+    #{if(&, "&", "")}.is-#{$state} {
+      @content;
+    }
+  }
+}
 
-@mixin form-validation-state($state, $color) {
+@mixin form-validation-state($state, $color, $icon) {
   .#{$state}-feedback {
     display: none;
     width: 100%;
     margin-top: $form-feedback-margin-top;
-    font-size: $form-feedback-font-size;
+    @include font-size($form-feedback-font-size);
     color: $color;
   }
 
   .#{$state}-tooltip {
     position: absolute;
     top: 100%;
+    left: 0;
     z-index: 5;
     display: none;
     max-width: 100%; // Contain to parent when possible
-    padding: .5rem;
+    padding: $form-feedback-tooltip-padding-y $form-feedback-tooltip-padding-x;
     margin-top: .1rem;
-    font-size: .875rem;
-    line-height: 1;
-    color: #fff;
-    background-color: rgba($color, .8);
-    border-radius: .2rem;
+    @include font-size($form-feedback-tooltip-font-size);
+    line-height: $form-feedback-tooltip-line-height;
+    color: color-yiq($color);
+    background-color: rgba($color, $form-feedback-tooltip-opacity);
+    @include border-radius($form-feedback-tooltip-border-radius);
+
+    // See https://github.com/twbs/bootstrap/pull/31557
+    // Align tooltip to form elements
+    .form-row > .col > &,
+    .form-row > [class*="col-"] > & {
+      left: $form-grid-gutter-width * .5;
+    }
   }
 
-  .form-control,
-  .custom-select {
-    .was-validated &:#{$state},
-    &.is-#{$state} {
+  @include form-validation-state-selector($state) {
+    ~ .#{$state}-feedback,
+    ~ .#{$state}-tooltip {
+      display: block;
+    }
+  }
+
+  .form-control {
+    @include form-validation-state-selector($state) {
       border-color: $color;
 
+      @if $enable-validation-icons {
+        padding-right: $input-height-inner !important; // stylelint-disable-line declaration-no-important
+        background-image: escape-svg($icon);
+        background-repeat: no-repeat;
+        background-position: right $input-height-inner-quarter center;
+        background-size: $input-height-inner-half $input-height-inner-half;
+      }
+
       &:focus {
         border-color: $color;
         box-shadow: 0 0 0 $input-focus-width rgba($color, .25);
       }
+    }
+  }
 
-      ~ .#{$state}-feedback,
-      ~ .#{$state}-tooltip {
-        display: block;
+  // stylelint-disable-next-line selector-no-qualifying-type
+  select.form-control {
+    @include form-validation-state-selector($state) {
+      @if $enable-validation-icons {
+        padding-right: $input-padding-x * 4 !important; // stylelint-disable-line declaration-no-important
+        background-position: right $input-padding-x * 2 center;
+      }
+    }
+  }
+
+  // stylelint-disable-next-line selector-no-qualifying-type
+  textarea.form-control {
+    @include form-validation-state-selector($state) {
+      @if $enable-validation-icons {
+        padding-right: $input-height-inner;
+        background-position: top $input-height-inner-quarter right $input-height-inner-quarter;
+      }
+    }
+  }
+
+  .custom-select {
+    @include form-validation-state-selector($state) {
+      border-color: $color;
+
+      @if $enable-validation-icons {
+        padding-right: $custom-select-feedback-icon-padding-right !important; // stylelint-disable-line declaration-no-important
+        background: $custom-select-background, $custom-select-bg escape-svg($icon) $custom-select-feedback-icon-position / $custom-select-feedback-icon-size no-repeat;
+      }
+
+      &:focus {
+        border-color: $color;
+        box-shadow: 0 0 0 $input-focus-width rgba($color, .25);
       }
     }
   }
 
   .form-check-input {
-    .was-validated &:#{$state},
-    &.is-#{$state} {
+    @include form-validation-state-selector($state) {
       ~ .form-check-label {
         color: $color;
       }
@@ -83,30 +149,29 @@
   }
 
   .custom-control-input {
-    .was-validated &:#{$state},
-    &.is-#{$state} {
+    @include form-validation-state-selector($state) {
       ~ .custom-control-label {
         color: $color;
 
         &::before {
-          background-color: lighten($color, 25%);
+          border-color: $color;
         }
       }
 
-      ~ .#{$state}-feedback,
-      ~ .#{$state}-tooltip {
-        display: block;
-      }
-
       &:checked {
         ~ .custom-control-label::before {
+          border-color: lighten($color, 10%);
           @include gradient-bg(lighten($color, 10%));
         }
       }
 
       &:focus {
         ~ .custom-control-label::before {
-          box-shadow: 0 0 0 1px $body-bg, 0 0 0 $input-focus-width rgba($color, .25);
+          box-shadow: 0 0 0 $input-focus-width rgba($color, .25);
+        }
+
+        &:not(:checked) ~ .custom-control-label::before {
+          border-color: $color;
         }
       }
     }
@@ -114,21 +179,14 @@
 
   // custom file
   .custom-file-input {
-    .was-validated &:#{$state},
-    &.is-#{$state} {
+    @include form-validation-state-selector($state) {
       ~ .custom-file-label {
         border-color: $color;
-
-        &::before { border-color: inherit; }
-      }
-
-      ~ .#{$state}-feedback,
-      ~ .#{$state}-tooltip {
-        display: block;
       }
 
       &:focus {
         ~ .custom-file-label {
+          border-color: $color;
           box-shadow: 0 0 0 $input-focus-width rgba($color, .25);
         }
       }
diff --git a/src/css/bootstrap/mixins/_gradients.scss b/src/css/bootstrap/mixins/_gradients.scss
index ecd01f729..88c4d64b7 100644
--- a/src/css/bootstrap/mixins/_gradients.scss
+++ b/src/css/bootstrap/mixins/_gradients.scss
@@ -11,7 +11,7 @@
 // Horizontal gradient, from left to right
 //
 // Creates two color stops, start and end, by specifying a color and position for each color stop.
-@mixin gradient-x($start-color: #555, $end-color: #333, $start-percent: 0%, $end-percent: 100%) {
+@mixin gradient-x($start-color: $gray-700, $end-color: $gray-800, $start-percent: 0%, $end-percent: 100%) {
   background-image: linear-gradient(to right, $start-color $start-percent, $end-color $end-percent);
   background-repeat: repeat-x;
 }
@@ -19,27 +19,27 @@
 // Vertical gradient, from top to bottom
 //
 // Creates two color stops, start and end, by specifying a color and position for each color stop.
-@mixin gradient-y($start-color: #555, $end-color: #333, $start-percent: 0%, $end-percent: 100%) {
+@mixin gradient-y($start-color: $gray-700, $end-color: $gray-800, $start-percent: 0%, $end-percent: 100%) {
   background-image: linear-gradient(to bottom, $start-color $start-percent, $end-color $end-percent);
   background-repeat: repeat-x;
 }
 
-@mixin gradient-directional($start-color: #555, $end-color: #333, $deg: 45deg) {
+@mixin gradient-directional($start-color: $gray-700, $end-color: $gray-800, $deg: 45deg) {
   background-image: linear-gradient($deg, $start-color, $end-color);
   background-repeat: repeat-x;
 }
-@mixin gradient-x-three-colors($start-color: #00b3ee, $mid-color: #7a43b6, $color-stop: 50%, $end-color: #c3325f) {
+@mixin gradient-x-three-colors($start-color: $blue, $mid-color: $purple, $color-stop: 50%, $end-color: $red) {
   background-image: linear-gradient(to right, $start-color, $mid-color $color-stop, $end-color);
   background-repeat: no-repeat;
 }
-@mixin gradient-y-three-colors($start-color: #00b3ee, $mid-color: #7a43b6, $color-stop: 50%, $end-color: #c3325f) {
+@mixin gradient-y-three-colors($start-color: $blue, $mid-color: $purple, $color-stop: 50%, $end-color: $red) {
   background-image: linear-gradient($start-color, $mid-color $color-stop, $end-color);
   background-repeat: no-repeat;
 }
-@mixin gradient-radial($inner-color: #555, $outer-color: #333) {
+@mixin gradient-radial($inner-color: $gray-700, $outer-color: $gray-800) {
   background-image: radial-gradient(circle, $inner-color, $outer-color);
   background-repeat: no-repeat;
 }
-@mixin gradient-striped($color: rgba(255,255,255,.15), $angle: 45deg) {
+@mixin gradient-striped($color: rgba($white, .15), $angle: 45deg) {
   background-image: linear-gradient($angle, $color 25%, transparent 25%, transparent 50%, $color 50%, $color 75%, transparent 75%, transparent);
 }
diff --git a/src/css/bootstrap/mixins/_grid-framework.scss b/src/css/bootstrap/mixins/_grid-framework.scss
index 7b37f868f..ef3291753 100644
--- a/src/css/bootstrap/mixins/_grid-framework.scss
+++ b/src/css/bootstrap/mixins/_grid-framework.scss
@@ -8,20 +8,22 @@
   %grid-column {
     position: relative;
     width: 100%;
-    min-height: 1px; // Prevent columns from collapsing when empty
-    padding-right: ($gutter / 2);
-    padding-left: ($gutter / 2);
+    padding-right: $gutter * .5;
+    padding-left: $gutter * .5;
   }
 
   @each $breakpoint in map-keys($breakpoints) {
     $infix: breakpoint-infix($breakpoint, $breakpoints);
 
-    // Allow columns to stretch full width below their breakpoints
-    @for $i from 1 through $columns {
-      .col#{$infix}-#{$i} {
-        @extend %grid-column;
+    @if $columns > 0 {
+      // Allow columns to stretch full width below their breakpoints
+      @for $i from 1 through $columns {
+        .col#{$infix}-#{$i} {
+          @extend %grid-column;
+        }
       }
     }
+
     .col#{$infix},
     .col#{$infix}-auto {
       @extend %grid-column;
@@ -34,15 +36,24 @@
         flex-grow: 1;
         max-width: 100%;
       }
+
+      @if $grid-row-columns > 0 {
+        @for $i from 1 through $grid-row-columns {
+          .row-cols#{$infix}-#{$i} {
+            @include row-cols($i);
+          }
+        }
+      }
+
       .col#{$infix}-auto {
-        flex: 0 0 auto;
-        width: auto;
-        max-width: none; // Reset earlier grid tiers
+        @include make-col-auto();
       }
 
-      @for $i from 1 through $columns {
-        .col#{$infix}-#{$i} {
-          @include make-col($i, $columns);
+      @if $columns > 0 {
+        @for $i from 1 through $columns {
+          .col#{$infix}-#{$i} {
+            @include make-col($i, $columns);
+          }
         }
       }
 
@@ -54,11 +65,13 @@
         .order#{$infix}-#{$i} { order: $i; }
       }
 
-      // `$columns - 1` because offsetting by the width of an entire row isn't possible
-      @for $i from 0 through ($columns - 1) {
-        @if not ($infix == "" and $i == 0) { // Avoid emitting useless .offset-0
-          .offset#{$infix}-#{$i} {
-            @include make-col-offset($i, $columns);
+      @if $columns > 0 {
+        // `$columns - 1` because offsetting by the width of an entire row isn't possible
+        @for $i from 0 through ($columns - 1) {
+          @if not ($infix == "" and $i == 0) { // Avoid emitting useless .offset-0
+            .offset#{$infix}-#{$i} {
+              @include make-col-offset($i, $columns);
+            }
           }
         }
       }
diff --git a/src/css/bootstrap/mixins/_grid.scss b/src/css/bootstrap/mixins/_grid.scss
index b75ebcbca..0eb09910d 100644
--- a/src/css/bootstrap/mixins/_grid.scss
+++ b/src/css/bootstrap/mixins/_grid.scss
@@ -2,14 +2,20 @@
 //
 // Generate semantic grid columns with these mixins.
 
-@mixin make-container() {
+@mixin make-container($gutter: $grid-gutter-width) {
   width: 100%;
-  padding-right: ($grid-gutter-width / 2);
-  padding-left: ($grid-gutter-width / 2);
+  padding-right: $gutter * .5;
+  padding-left: $gutter * .5;
   margin-right: auto;
   margin-left: auto;
 }
 
+@mixin make-row($gutter: $grid-gutter-width) {
+  display: flex;
+  flex-wrap: wrap;
+  margin-right: -$gutter * .5;
+  margin-left: -$gutter * .5;
+}
 
 // For each breakpoint, define the maximum width of the container in a media query
 @mixin make-container-max-widths($max-widths: $container-max-widths, $breakpoints: $grid-breakpoints) {
@@ -18,35 +24,46 @@
       max-width: $container-max-width;
     }
   }
+  @include deprecate("The `make-container-max-widths` mixin", "v4.5.2", "v5");
 }
 
-@mixin make-row() {
-  display: flex;
-  flex-wrap: wrap;
-  margin-right: ($grid-gutter-width / -2);
-  margin-left: ($grid-gutter-width / -2);
-}
-
-@mixin make-col-ready() {
+@mixin make-col-ready($gutter: $grid-gutter-width) {
   position: relative;
   // Prevent columns from becoming too narrow when at smaller grid tiers by
   // always setting `width: 100%;`. This works because we use `flex` values
   // later on to override this initial width.
   width: 100%;
-  min-height: 1px; // Prevent collapsing
-  padding-right: ($grid-gutter-width / 2);
-  padding-left: ($grid-gutter-width / 2);
+  padding-right: $gutter * .5;
+  padding-left: $gutter * .5;
 }
 
 @mixin make-col($size, $columns: $grid-columns) {
-  flex: 0 0 percentage($size / $columns);
+  flex: 0 0 percentage(divide($size, $columns));
   // Add a `max-width` to ensure content within each column does not blow out
   // the width of the column. Applies to IE10+ and Firefox. Chrome and Safari
   // do not appear to require this.
-  max-width: percentage($size / $columns);
+  max-width: percentage(divide($size, $columns));
+}
+
+@mixin make-col-auto() {
+  flex: 0 0 auto;
+  width: auto;
+  max-width: 100%; // Reset earlier grid tiers
 }
 
 @mixin make-col-offset($size, $columns: $grid-columns) {
-  $num: $size / $columns;
+  $num: divide($size, $columns);
   margin-left: if($num == 0, 0, percentage($num));
 }
+
+// Row columns
+//
+// Specify on a parent element(e.g., .row) to force immediate children into NN
+// numberof columns. Supports wrapping to new lines, but does not do a Masonry
+// style grid.
+@mixin row-cols($count) {
+  > * {
+    flex: 0 0 divide(100%, $count);
+    max-width: divide(100%, $count);
+  }
+}
diff --git a/src/css/bootstrap/mixins/_hover.scss b/src/css/bootstrap/mixins/_hover.scss
index ec2e328a4..409f8244e 100644
--- a/src/css/bootstrap/mixins/_hover.scss
+++ b/src/css/bootstrap/mixins/_hover.scss
@@ -1,28 +1,26 @@
-// stylelint-disable indentation
-
 // Hover mixin and `$enable-hover-media-query` are deprecated.
 //
-// Origally added during our alphas and maintained during betas, this mixin was
-// designed to prevent `:hover` stickiness on iOS—an issue where hover styles
+// Originally added during our alphas and maintained during betas, this mixin was
+// designed to prevent `:hover` stickiness on iOS-an issue where hover styles
 // would persist after initial touch.
 //
 // For backward compatibility, we've kept these mixins and updated them to
-// always return their regular psuedo-classes instead of a shimmed media query.
+// always return their regular pseudo-classes instead of a shimmed media query.
 //
 // Issue: https://github.com/twbs/bootstrap/issues/25195
 
-@mixin hover {
+@mixin hover() {
   &:hover { @content; }
 }
 
-@mixin hover-focus {
+@mixin hover-focus() {
   &:hover,
   &:focus {
     @content;
   }
 }
 
-@mixin plain-hover-focus {
+@mixin plain-hover-focus() {
   &,
   &:hover,
   &:focus {
@@ -30,7 +28,7 @@
   }
 }
 
-@mixin hover-focus-active {
+@mixin hover-focus-active() {
   &:hover,
   &:focus,
   &:active {
diff --git a/src/css/bootstrap/mixins/_image.scss b/src/css/bootstrap/mixins/_image.scss
index 0544f0d2a..3aaa0d704 100644
--- a/src/css/bootstrap/mixins/_image.scss
+++ b/src/css/bootstrap/mixins/_image.scss
@@ -7,7 +7,7 @@
 //
 // Keep images from scaling beyond the width of their parents.
 
-@mixin img-fluid {
+@mixin img-fluid() {
   // Part 1: Set a maximum relative to the parent
   max-width: 100%;
   // Part 2: Override the height to auto, otherwise images will be stretched
@@ -20,17 +20,17 @@
 //
 // Short retina mixin for setting background-image and -size.
 
-// stylelint-disable indentation, media-query-list-comma-newline-after
 @mixin img-retina($file-1x, $file-2x, $width-1x, $height-1x) {
   background-image: url($file-1x);
 
   // Autoprefixer takes care of adding -webkit-min-device-pixel-ratio and -o-min-device-pixel-ratio,
   // but doesn't convert dppx=>dpi.
   // There's no such thing as unprefixed min-device-pixel-ratio since it's nonstandard.
-  // Compatibility info: https://caniuse.com/#feat=css-media-resolution
+  // Compatibility info: https://caniuse.com/css-media-resolution
   @media only screen and (min-resolution: 192dpi), // IE9-11 don't support dppx
-  only screen and (min-resolution: 2dppx) { // Standardized
+    only screen and (min-resolution: 2dppx) { // Standardized
     background-image: url($file-2x);
     background-size: $width-1x $height-1x;
   }
+  @include deprecate("`img-retina()`", "v4.3.0", "v5");
 }
diff --git a/src/css/bootstrap/mixins/_list-group.scss b/src/css/bootstrap/mixins/_list-group.scss
index 607ffcb86..0da353156 100644
--- a/src/css/bootstrap/mixins/_list-group.scss
+++ b/src/css/bootstrap/mixins/_list-group.scss
@@ -6,13 +6,13 @@
     background-color: $background;
 
     &.list-group-item-action {
-      @include hover-focus {
+      @include hover-focus() {
         color: $color;
         background-color: darken($background, 5%);
       }
 
       &.active {
-        color: #fff;
+        color: $white;
         background-color: $color;
         border-color: $color;
       }
diff --git a/src/css/bootstrap/mixins/_lists.scss b/src/css/bootstrap/mixins/_lists.scss
index 251856266..251cb0733 100644
--- a/src/css/bootstrap/mixins/_lists.scss
+++ b/src/css/bootstrap/mixins/_lists.scss
@@ -1,7 +1,7 @@
 // Lists
 
 // Unstyled keeps list items block level, just removes default browser padding and list-style
-@mixin list-unstyled {
+@mixin list-unstyled() {
   padding-left: 0;
   list-style: none;
 }
diff --git a/src/css/bootstrap/mixins/_nav-divider.scss b/src/css/bootstrap/mixins/_nav-divider.scss
index 493de03a3..3e0cceafe 100644
--- a/src/css/bootstrap/mixins/_nav-divider.scss
+++ b/src/css/bootstrap/mixins/_nav-divider.scss
@@ -2,9 +2,10 @@
 //
 // Dividers (basically an hr) within dropdowns and nav lists
 
-@mixin nav-divider($color: #e5e5e5) {
+@mixin nav-divider($color: $nav-divider-color, $margin-y: $nav-divider-margin-y, $ignore-warning: false) {
   height: 0;
-  margin: ($spacer / 2) 0;
+  margin: $margin-y 0;
   overflow: hidden;
   border-top: 1px solid $color;
+  @include deprecate("The `nav-divider()` mixin", "v4.4.0", "v5", $ignore-warning);
 }
diff --git a/src/css/bootstrap/mixins/_navbar-align.scss b/src/css/bootstrap/mixins/_navbar-align.scss
deleted file mode 100644
index b35166033..000000000
--- a/src/css/bootstrap/mixins/_navbar-align.scss
+++ /dev/null
@@ -1,10 +0,0 @@
-// Navbar vertical align
-//
-// Vertically center elements in the navbar.
-// Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);`
-// to calculate the appropriate top margin.
-
-// @mixin navbar-vertical-align($element-height) {
-//   margin-top: (($navbar-height - $element-height) / 2);
-//   margin-bottom: (($navbar-height - $element-height) / 2);
-// }
diff --git a/src/css/bootstrap/mixins/_pagination.scss b/src/css/bootstrap/mixins/_pagination.scss
index ff36eb6b4..af8e16d6a 100644
--- a/src/css/bootstrap/mixins/_pagination.scss
+++ b/src/css/bootstrap/mixins/_pagination.scss
@@ -3,7 +3,7 @@
 @mixin pagination-size($padding-y, $padding-x, $font-size, $line-height, $border-radius) {
   .page-link {
     padding: $padding-y $padding-x;
-    font-size: $font-size;
+    @include font-size($font-size);
     line-height: $line-height;
   }
 
diff --git a/src/css/bootstrap/mixins/_reset-text.scss b/src/css/bootstrap/mixins/_reset-text.scss
index 71edb0061..868253307 100644
--- a/src/css/bootstrap/mixins/_reset-text.scss
+++ b/src/css/bootstrap/mixins/_reset-text.scss
@@ -1,17 +1,17 @@
-@mixin reset-text {
+@mixin reset-text() {
   font-family: $font-family-base;
   // We deliberately do NOT reset font-size or word-wrap.
   font-style: normal;
   font-weight: $font-weight-normal;
   line-height: $line-height-base;
   text-align: left; // Fallback for where `start` is not supported
-  text-align: start; // stylelint-disable-line declaration-block-no-duplicate-properties
+  text-align: start;
   text-decoration: none;
   text-shadow: none;
   text-transform: none;
   letter-spacing: normal;
   word-break: normal;
-  word-spacing: normal;
   white-space: normal;
+  word-spacing: normal;
   line-break: auto;
 }
diff --git a/src/css/bootstrap/mixins/_screen-reader.scss b/src/css/bootstrap/mixins/_screen-reader.scss
index 8f3eb1b19..70b677e3f 100644
--- a/src/css/bootstrap/mixins/_screen-reader.scss
+++ b/src/css/bootstrap/mixins/_screen-reader.scss
@@ -1,17 +1,17 @@
 // Only display content to screen readers
 //
-// See: http://a11yproject.com/posts/how-to-hide-content/
-// See: https://hugogiraudel.com/2016/10/13/css-hide-and-seek/
+// See: https://www.a11yproject.com/posts/2013-01-11-how-to-hide-content/
+// See: https://kittygiraudel.com/2016/10/13/css-hide-and-seek/
 
-@mixin sr-only {
+@mixin sr-only() {
   position: absolute;
   width: 1px;
   height: 1px;
   padding: 0;
+  margin: -1px; // Fix for https://github.com/twbs/bootstrap/issues/25686
   overflow: hidden;
   clip: rect(0, 0, 0, 0);
   white-space: nowrap;
-  clip-path: inset(50%);
   border: 0;
 }
 
@@ -21,7 +21,7 @@
 //
 // Credit: HTML5 Boilerplate
 
-@mixin sr-only-focusable {
+@mixin sr-only-focusable() {
   &:active,
   &:focus {
     position: static;
@@ -30,6 +30,5 @@
     overflow: visible;
     clip: auto;
     white-space: normal;
-    clip-path: none;
   }
 }
diff --git a/src/css/bootstrap/mixins/_size.scss b/src/css/bootstrap/mixins/_size.scss
index b9dd48e8d..69e056d2c 100644
--- a/src/css/bootstrap/mixins/_size.scss
+++ b/src/css/bootstrap/mixins/_size.scss
@@ -3,4 +3,5 @@
 @mixin size($width, $height: $width) {
   width: $width;
   height: $height;
+  @include deprecate("`size()`", "v4.3.0", "v5");
 }
diff --git a/src/css/bootstrap/mixins/_table-row.scss b/src/css/bootstrap/mixins/_table-row.scss
index 84f1d305a..1ccde6b6c 100644
--- a/src/css/bootstrap/mixins/_table-row.scss
+++ b/src/css/bootstrap/mixins/_table-row.scss
@@ -1,6 +1,6 @@
 // Tables
 
-@mixin table-row-variant($state, $background) {
+@mixin table-row-variant($state, $background, $border: null) {
   // Exact selectors below required to override `.table-striped` and prevent
   // inheritance to nested tables.
   .table-#{$state} {
@@ -9,6 +9,15 @@
     > td {
       background-color: $background;
     }
+
+    @if $border != null {
+      th,
+      td,
+      thead th,
+      tbody + tbody {
+        border-color: $border;
+      }
+    }
   }
 
   // Hover states for `.table-hover`
@@ -17,7 +26,7 @@
     $hover-background: darken($background, 5%);
 
     .table-#{$state} {
-      @include hover {
+      @include hover() {
         background-color: $hover-background;
 
         > td,
diff --git a/src/css/bootstrap/mixins/_text-emphasis.scss b/src/css/bootstrap/mixins/_text-emphasis.scss
index 58db3e0fc..5eb8a5515 100644
--- a/src/css/bootstrap/mixins/_text-emphasis.scss
+++ b/src/css/bootstrap/mixins/_text-emphasis.scss
@@ -2,13 +2,16 @@
 
 // Typography
 
-@mixin text-emphasis-variant($parent, $color) {
+@mixin text-emphasis-variant($parent, $color, $ignore-warning: false) {
   #{$parent} {
     color: $color !important;
   }
-  a#{$parent} {
-    @include hover-focus {
-      color: darken($color, 10%) !important;
+  @if $emphasized-link-hover-darken-percentage != 0 {
+    a#{$parent} {
+      @include hover-focus() {
+        color: darken($color, $emphasized-link-hover-darken-percentage) !important;
+      }
     }
   }
+  @include deprecate("`text-emphasis-variant()`", "v4.4.0", "v5", $ignore-warning);
 }
diff --git a/src/css/bootstrap/mixins/_text-hide.scss b/src/css/bootstrap/mixins/_text-hide.scss
index aa551fde5..3a923011e 100644
--- a/src/css/bootstrap/mixins/_text-hide.scss
+++ b/src/css/bootstrap/mixins/_text-hide.scss
@@ -1,9 +1,11 @@
 // CSS image replacement
-@mixin text-hide() {
+@mixin text-hide($ignore-warning: false) {
   // stylelint-disable-next-line font-family-no-missing-generic-family-keyword
   font: 0/0 a;
   color: transparent;
   text-shadow: none;
   background-color: transparent;
   border: 0;
+
+  @include deprecate("`text-hide()`", "v4.1.0", "v5", $ignore-warning);
 }
diff --git a/src/css/bootstrap/mixins/_transition.scss b/src/css/bootstrap/mixins/_transition.scss
index 7e33dee31..54870bf6a 100644
--- a/src/css/bootstrap/mixins/_transition.scss
+++ b/src/css/bootstrap/mixins/_transition.scss
@@ -1,9 +1,26 @@
+// stylelint-disable property-disallowed-list
 @mixin transition($transition...) {
+  @if length($transition) == 0 {
+    $transition: $transition-base;
+  }
+
+  @if length($transition) > 1 {
+    @each $value in $transition {
+      @if $value == null or $value == none {
+        @warn "The keyword 'none' or 'null' must be used as a single argument.";
+      }
+    }
+  }
+
   @if $enable-transitions {
-    @if length($transition) == 0 {
-      transition: $transition-base;
-    } @else {
+    @if nth($transition, 1) != null {
       transition: $transition;
     }
+
+    @if $enable-prefers-reduced-motion-media-query and nth($transition, 1) != null and nth($transition, 1) != none {
+      @media (prefers-reduced-motion: reduce) {
+        transition: none;
+      }
+    }
   }
 }
diff --git a/src/css/bootstrap/mixins/_visibility.scss b/src/css/bootstrap/mixins/_visibility.scss
index fe523d0ee..f17467311 100644
--- a/src/css/bootstrap/mixins/_visibility.scss
+++ b/src/css/bootstrap/mixins/_visibility.scss
@@ -4,4 +4,5 @@
 
 @mixin invisible($visibility) {
   visibility: $visibility !important;
+  @include deprecate("`invisible()`", "v4.3.0", "v5");
 }
diff --git a/src/css/bootstrap/utilities/_background.scss b/src/css/bootstrap/utilities/_background.scss
index 1f18b2f3f..3accbc4fd 100644
--- a/src/css/bootstrap/utilities/_background.scss
+++ b/src/css/bootstrap/utilities/_background.scss
@@ -1,12 +1,12 @@
 // stylelint-disable declaration-no-important
 
 @each $color, $value in $theme-colors {
-  @include bg-variant(".bg-#{$color}", $value);
+  @include bg-variant(".bg-#{$color}", $value, true);
 }
 
 @if $enable-gradients {
   @each $color, $value in $theme-colors {
-    @include bg-gradient-variant(".bg-gradient-#{$color}", $value);
+    @include bg-gradient-variant(".bg-gradient-#{$color}", $value, true);
   }
 }
 
diff --git a/src/css/bootstrap/utilities/_borders.scss b/src/css/bootstrap/utilities/_borders.scss
index b8832ef72..205f3fc24 100644
--- a/src/css/bootstrap/utilities/_borders.scss
+++ b/src/css/bootstrap/utilities/_borders.scss
@@ -1,4 +1,4 @@
-// stylelint-disable declaration-no-important
+// stylelint-disable property-disallowed-list, declaration-no-important
 
 //
 // Border
@@ -30,30 +30,46 @@
 // Border-radius
 //
 
+.rounded-sm {
+  border-radius: $border-radius-sm !important;
+}
+
 .rounded {
   border-radius: $border-radius !important;
 }
+
 .rounded-top {
   border-top-left-radius: $border-radius !important;
   border-top-right-radius: $border-radius !important;
 }
+
 .rounded-right {
   border-top-right-radius: $border-radius !important;
   border-bottom-right-radius: $border-radius !important;
 }
+
 .rounded-bottom {
   border-bottom-right-radius: $border-radius !important;
   border-bottom-left-radius: $border-radius !important;
 }
+
 .rounded-left {
   border-top-left-radius: $border-radius !important;
   border-bottom-left-radius: $border-radius !important;
 }
 
+.rounded-lg {
+  border-radius: $border-radius-lg !important;
+}
+
 .rounded-circle {
   border-radius: 50% !important;
 }
 
+.rounded-pill {
+  border-radius: $rounded-pill !important;
+}
+
 .rounded-0 {
   border-radius: 0 !important;
 }
diff --git a/src/css/bootstrap/utilities/_display.scss b/src/css/bootstrap/utilities/_display.scss
index 20aeeb5f3..130367998 100644
--- a/src/css/bootstrap/utilities/_display.scss
+++ b/src/css/bootstrap/utilities/_display.scss
@@ -8,15 +8,9 @@
   @include media-breakpoint-up($breakpoint) {
     $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
 
-    .d#{$infix}-none         { display: none !important; }
-    .d#{$infix}-inline       { display: inline !important; }
-    .d#{$infix}-inline-block { display: inline-block !important; }
-    .d#{$infix}-block        { display: block !important; }
-    .d#{$infix}-table        { display: table !important; }
-    .d#{$infix}-table-row    { display: table-row !important; }
-    .d#{$infix}-table-cell   { display: table-cell !important; }
-    .d#{$infix}-flex         { display: flex !important; }
-    .d#{$infix}-inline-flex  { display: inline-flex !important; }
+    @each $value in $displays {
+      .d#{$infix}-#{$value} { display: $value !important; }
+    }
   }
 }
 
@@ -26,13 +20,7 @@
 //
 
 @media print {
-  .d-print-none         { display: none !important; }
-  .d-print-inline       { display: inline !important; }
-  .d-print-inline-block { display: inline-block !important; }
-  .d-print-block        { display: block !important; }
-  .d-print-table        { display: table !important; }
-  .d-print-table-row    { display: table-row !important; }
-  .d-print-table-cell   { display: table-cell !important; }
-  .d-print-flex         { display: flex !important; }
-  .d-print-inline-flex  { display: inline-flex !important; }
+  @each $value in $displays {
+    .d-print-#{$value} { display: $value !important; }
+  }
 }
diff --git a/src/css/bootstrap/utilities/_embed.scss b/src/css/bootstrap/utilities/_embed.scss
index d3362b6fd..e37465e40 100644
--- a/src/css/bootstrap/utilities/_embed.scss
+++ b/src/css/bootstrap/utilities/_embed.scss
@@ -27,26 +27,13 @@
   }
 }
 
-.embed-responsive-21by9 {
-  &::before {
-    padding-top: percentage(9 / 21);
-  }
-}
-
-.embed-responsive-16by9 {
-  &::before {
-    padding-top: percentage(9 / 16);
-  }
-}
-
-.embed-responsive-4by3 {
-  &::before {
-    padding-top: percentage(3 / 4);
-  }
-}
+@each $embed-responsive-aspect-ratio in $embed-responsive-aspect-ratios {
+  $embed-responsive-aspect-ratio-x: nth($embed-responsive-aspect-ratio, 1);
+  $embed-responsive-aspect-ratio-y: nth($embed-responsive-aspect-ratio, 2);
 
-.embed-responsive-1by1 {
-  &::before {
-    padding-top: percentage(1 / 1);
+  .embed-responsive-#{$embed-responsive-aspect-ratio-x}by#{$embed-responsive-aspect-ratio-y} {
+    &::before {
+      padding-top: percentage(divide($embed-responsive-aspect-ratio-y, $embed-responsive-aspect-ratio-x));
+    }
   }
 }
diff --git a/src/css/bootstrap/utilities/_flex.scss b/src/css/bootstrap/utilities/_flex.scss
index 8e4703843..3d4266e0d 100644
--- a/src/css/bootstrap/utilities/_flex.scss
+++ b/src/css/bootstrap/utilities/_flex.scss
@@ -16,6 +16,11 @@
     .flex#{$infix}-wrap         { flex-wrap: wrap !important; }
     .flex#{$infix}-nowrap       { flex-wrap: nowrap !important; }
     .flex#{$infix}-wrap-reverse { flex-wrap: wrap-reverse !important; }
+    .flex#{$infix}-fill         { flex: 1 1 auto !important; }
+    .flex#{$infix}-grow-0       { flex-grow: 0 !important; }
+    .flex#{$infix}-grow-1       { flex-grow: 1 !important; }
+    .flex#{$infix}-shrink-0     { flex-shrink: 0 !important; }
+    .flex#{$infix}-shrink-1     { flex-shrink: 1 !important; }
 
     .justify-content#{$infix}-start   { justify-content: flex-start !important; }
     .justify-content#{$infix}-end     { justify-content: flex-end !important; }
diff --git a/src/css/bootstrap/utilities/_float.scss b/src/css/bootstrap/utilities/_float.scss
index 01655e9a5..54250844f 100644
--- a/src/css/bootstrap/utilities/_float.scss
+++ b/src/css/bootstrap/utilities/_float.scss
@@ -1,9 +1,11 @@
+// stylelint-disable declaration-no-important
+
 @each $breakpoint in map-keys($grid-breakpoints) {
   @include media-breakpoint-up($breakpoint) {
     $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
 
-    .float#{$infix}-left  { @include float-left; }
-    .float#{$infix}-right { @include float-right; }
-    .float#{$infix}-none  { @include float-none; }
+    .float#{$infix}-left  { float: left !important; }
+    .float#{$infix}-right { float: right !important; }
+    .float#{$infix}-none  { float: none !important; }
   }
 }
diff --git a/src/css/bootstrap/utilities/_interactions.scss b/src/css/bootstrap/utilities/_interactions.scss
new file mode 100644
index 000000000..cc75fc21b
--- /dev/null
+++ b/src/css/bootstrap/utilities/_interactions.scss
@@ -0,0 +1,5 @@
+// stylelint-disable declaration-no-important
+
+@each $value in $user-selects {
+  .user-select-#{$value} { user-select: $value !important; }
+}
diff --git a/src/css/bootstrap/utilities/_overflow.scss b/src/css/bootstrap/utilities/_overflow.scss
new file mode 100644
index 000000000..8326c3064
--- /dev/null
+++ b/src/css/bootstrap/utilities/_overflow.scss
@@ -0,0 +1,5 @@
+// stylelint-disable declaration-no-important
+
+@each $value in $overflows {
+  .overflow-#{$value} { overflow: $value !important; }
+}
diff --git a/src/css/bootstrap/utilities/_position.scss b/src/css/bootstrap/utilities/_position.scss
index ef962eddb..cdf6c115f 100644
--- a/src/css/bootstrap/utilities/_position.scss
+++ b/src/css/bootstrap/utilities/_position.scss
@@ -1,10 +1,6 @@
 // stylelint-disable declaration-no-important
 
 // Common values
-
-// Sass list not in variables since it's not intended for customization.
-$positions: static, relative, absolute, fixed, sticky;
-
 @each $position in $positions {
   .position-#{$position} { position: $position !important; }
 }
diff --git a/src/css/bootstrap/utilities/_shadows.scss b/src/css/bootstrap/utilities/_shadows.scss
new file mode 100644
index 000000000..f5d03fcd5
--- /dev/null
+++ b/src/css/bootstrap/utilities/_shadows.scss
@@ -0,0 +1,6 @@
+// stylelint-disable declaration-no-important
+
+.shadow-sm { box-shadow: $box-shadow-sm !important; }
+.shadow { box-shadow: $box-shadow !important; }
+.shadow-lg { box-shadow: $box-shadow-lg !important; }
+.shadow-none { box-shadow: none !important; }
diff --git a/src/css/bootstrap/utilities/_sizing.scss b/src/css/bootstrap/utilities/_sizing.scss
index e95a4db36..f37648802 100644
--- a/src/css/bootstrap/utilities/_sizing.scss
+++ b/src/css/bootstrap/utilities/_sizing.scss
@@ -10,3 +10,11 @@
 
 .mw-100 { max-width: 100% !important; }
 .mh-100 { max-height: 100% !important; }
+
+// Viewport additional helpers
+
+.min-vw-100 { min-width: 100vw !important; }
+.min-vh-100 { min-height: 100vh !important; }
+
+.vw-100 { width: 100vw !important; }
+.vh-100 { height: 100vh !important; }
diff --git a/src/css/bootstrap/utilities/_spacing.scss b/src/css/bootstrap/utilities/_spacing.scss
index b2e2354b1..3e98581a9 100644
--- a/src/css/bootstrap/utilities/_spacing.scss
+++ b/src/css/bootstrap/utilities/_spacing.scss
@@ -8,7 +8,6 @@
 
     @each $prop, $abbrev in (margin: m, padding: p) {
       @each $size, $length in $spacers {
-
         .#{$abbrev}#{$infix}-#{$size} { #{$prop}: $length !important; }
         .#{$abbrev}t#{$infix}-#{$size},
         .#{$abbrev}y#{$infix}-#{$size} {
@@ -29,6 +28,29 @@
       }
     }
 
+    // Negative margins (e.g., where `.mb-n1` is negative version of `.mb-1`)
+    @each $size, $length in $spacers {
+      @if "#{$size}" != "0" {
+        .m#{$infix}-n#{$size} { margin: -$length !important; }
+        .mt#{$infix}-n#{$size},
+        .my#{$infix}-n#{$size} {
+          margin-top: -$length !important;
+        }
+        .mr#{$infix}-n#{$size},
+        .mx#{$infix}-n#{$size} {
+          margin-right: -$length !important;
+        }
+        .mb#{$infix}-n#{$size},
+        .my#{$infix}-n#{$size} {
+          margin-bottom: -$length !important;
+        }
+        .ml#{$infix}-n#{$size},
+        .mx#{$infix}-n#{$size} {
+          margin-left: -$length !important;
+        }
+      }
+    }
+
     // Some special margin utils
     .m#{$infix}-auto { margin: auto !important; }
     .mt#{$infix}-auto,
diff --git a/src/css/bootstrap/utilities/_stretched-link.scss b/src/css/bootstrap/utilities/_stretched-link.scss
new file mode 100644
index 000000000..fb5066bf5
--- /dev/null
+++ b/src/css/bootstrap/utilities/_stretched-link.scss
@@ -0,0 +1,19 @@
+//
+// Stretched link
+//
+
+.stretched-link {
+  &::after {
+    position: absolute;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    z-index: 1;
+    // Just in case `pointer-events: none` is set on a parent
+    pointer-events: auto;
+    content: "";
+    // IE10 bugfix, see https://stackoverflow.com/questions/16947967/ie10-hover-pseudo-class-doesnt-work-without-background-color
+    background-color: rgba(0, 0, 0, 0);
+  }
+}
diff --git a/src/css/bootstrap/utilities/_text.scss b/src/css/bootstrap/utilities/_text.scss
index f4b6e65e5..3a9f83edf 100644
--- a/src/css/bootstrap/utilities/_text.scss
+++ b/src/css/bootstrap/utilities/_text.scss
@@ -4,11 +4,14 @@
 // Text
 //
 
+.text-monospace { font-family: $font-family-monospace !important; }
+
 // Alignment
 
 .text-justify  { text-align: justify !important; }
+.text-wrap     { white-space: normal !important; }
 .text-nowrap   { white-space: nowrap !important; }
-.text-truncate { @include text-truncate; }
+.text-truncate { @include text-truncate(); }
 
 // Responsive alignment
 
@@ -30,23 +33,40 @@
 
 // Weight and italics
 
-.font-weight-light  { font-weight: $font-weight-light !important; }
-.font-weight-normal { font-weight: $font-weight-normal !important; }
-.font-weight-bold   { font-weight: $font-weight-bold !important; }
-.font-italic        { font-style: italic !important; }
+.font-weight-light   { font-weight: $font-weight-light !important; }
+.font-weight-lighter { font-weight: $font-weight-lighter !important; }
+.font-weight-normal  { font-weight: $font-weight-normal !important; }
+.font-weight-bold    { font-weight: $font-weight-bold !important; }
+.font-weight-bolder  { font-weight: $font-weight-bolder !important; }
+.font-italic         { font-style: italic !important; }
 
 // Contextual colors
 
-.text-white { color: #fff !important; }
+.text-white { color: $white !important; }
 
 @each $color, $value in $theme-colors {
-  @include text-emphasis-variant(".text-#{$color}", $value);
+  @include text-emphasis-variant(".text-#{$color}", $value, true);
 }
 
+.text-body { color: $body-color !important; }
 .text-muted { color: $text-muted !important; }
 
+.text-black-50 { color: rgba($black, .5) !important; }
+.text-white-50 { color: rgba($white, .5) !important; }
+
 // Misc
 
 .text-hide {
-  @include text-hide();
+  @include text-hide($ignore-warning: true);
 }
+
+.text-decoration-none { text-decoration: none !important; }
+
+.text-break {
+  word-break: break-word !important; // Deprecated, but avoids issues with flex containers
+  word-wrap: break-word !important; // Used instead of `overflow-wrap` for IE & Edge Legacy
+}
+
+// Reset
+
+.text-reset { color: inherit !important; }
diff --git a/src/css/bootstrap/utilities/_visibility.scss b/src/css/bootstrap/utilities/_visibility.scss
index 823406dc3..7756c3bfa 100644
--- a/src/css/bootstrap/utilities/_visibility.scss
+++ b/src/css/bootstrap/utilities/_visibility.scss
@@ -1,11 +1,13 @@
+// stylelint-disable declaration-no-important
+
 //
 // Visibility utilities
 //
 
 .visible {
-  @include invisible(visible);
+  visibility: visible !important;
 }
 
 .invisible {
-  @include invisible(hidden);
+  visibility: hidden !important;
 }
diff --git a/src/css/bootstrap/vendor/_rfs.scss b/src/css/bootstrap/vendor/_rfs.scss
new file mode 100644
index 000000000..e453f441e
--- /dev/null
+++ b/src/css/bootstrap/vendor/_rfs.scss
@@ -0,0 +1,228 @@
+// stylelint-disable property-blacklist, scss/dollar-variable-default
+
+// SCSS RFS mixin
+//
+// Automated responsive font sizes
+//
+// Licensed under MIT (https://github.com/twbs/rfs/blob/v8.x/LICENSE)
+
+// Configuration
+
+// Base font size
+$rfs-base-font-size: 1.25rem !default;
+$rfs-font-size-unit: rem !default;
+
+@if $rfs-font-size-unit != rem and $rfs-font-size-unit != px {
+  @error "`#{$rfs-font-size-unit}` is not a valid unit for $rfs-font-size-unit. Use `px` or `rem`.";
+}
+
+// Breakpoint at where font-size starts decreasing if screen width is smaller
+$rfs-breakpoint: 1200px !default;
+$rfs-breakpoint-unit: px !default;
+
+@if $rfs-breakpoint-unit != px and $rfs-breakpoint-unit != em and $rfs-breakpoint-unit != rem {
+  @error "`#{$rfs-breakpoint-unit}` is not a valid unit for $rfs-breakpoint-unit. Use `px`, `em` or `rem`.";
+}
+
+// Resize font size based on screen height and width
+$rfs-two-dimensional: false !default;
+
+// Factor of decrease
+$rfs-factor: 10 !default;
+
+@if type-of($rfs-factor) != "number" or $rfs-factor <= 1 {
+  @error "`#{$rfs-factor}` is not a valid  $rfs-factor, it must be greater than 1.";
+}
+
+// Generate enable or disable classes. Possibilities: false, "enable" or "disable"
+$rfs-class: false !default;
+
+// 1 rem = $rfs-rem-value px
+$rfs-rem-value: 16 !default;
+
+// Safari iframe resize bug: https://github.com/twbs/rfs/issues/14
+$rfs-safari-iframe-resize-bug-fix: false !default;
+
+// Disable RFS by setting $enable-responsive-font-sizes to false
+$enable-responsive-font-sizes: true !default;
+
+// Cache $rfs-base-font-size unit
+$rfs-base-font-size-unit: unit($rfs-base-font-size);
+
+@function divide($dividend, $divisor, $precision: 10) {
+  $sign: if($dividend > 0 and $divisor > 0 or $dividend < 0 and $divisor < 0, 1, -1);
+  $dividend: abs($dividend);
+  $divisor: abs($divisor);
+  @if $dividend == 0 {
+    @return 0;
+  }
+  @if $divisor == 0 {
+    @error "Cannot divide by 0";
+  }
+  $remainder: $dividend;
+  $result: 0;
+  $factor: 10;
+  @while ($remainder > 0 and $precision >= 0) {
+    $quotient: 0;
+    @while ($remainder >= $divisor) {
+      $remainder: $remainder - $divisor;
+      $quotient: $quotient + 1;
+    }
+    $result: $result * 10 + $quotient;
+    $factor: $factor * .1;
+    $remainder: $remainder * 10;
+    $precision: $precision - 1;
+    @if ($precision < 0 and $remainder >= $divisor * 5) {
+      $result: $result + 1;
+    }
+  }
+  $result: $result * $factor * $sign;
+  $dividend-unit: unit($dividend);
+  $divisor-unit: unit($divisor);
+  $unit-map: (
+    "px": 1px,
+    "rem": 1rem,
+    "em": 1em,
+    "%": 1%
+  );
+  @if ($dividend-unit != $divisor-unit and map-has-key($unit-map, $dividend-unit)) {
+    $result: $result * map-get($unit-map, $dividend-unit);
+  }
+  @return $result;
+}
+
+// Remove px-unit from $rfs-base-font-size for calculations
+@if $rfs-base-font-size-unit == "px" {
+  $rfs-base-font-size: divide($rfs-base-font-size, $rfs-base-font-size * 0 + 1);
+}
+@else if $rfs-base-font-size-unit == "rem" {
+  $rfs-base-font-size: divide($rfs-base-font-size, divide($rfs-base-font-size * 0 + 1, $rfs-rem-value));
+}
+
+// Cache $rfs-breakpoint unit to prevent multiple calls
+$rfs-breakpoint-unit-cache: unit($rfs-breakpoint);
+
+// Remove unit from $rfs-breakpoint for calculations
+@if $rfs-breakpoint-unit-cache == "px" {
+  $rfs-breakpoint: divide($rfs-breakpoint, $rfs-breakpoint * 0 + 1);
+}
+@else if $rfs-breakpoint-unit-cache == "rem" or $rfs-breakpoint-unit-cache == "em" {
+  $rfs-breakpoint: divide($rfs-breakpoint, divide($rfs-breakpoint * 0 + 1, $rfs-rem-value));
+}
+
+// Internal mixin that adds disable classes to the selector if needed.
+@mixin _rfs-disable-class {
+  @if $rfs-class == "disable" {
+    // Adding an extra class increases specificity, which prevents the media query to override the font size
+    &,
+    .disable-responsive-font-size &,
+    &.disable-responsive-font-size {
+      @content;
+    }
+  }
+  @else {
+    @content;
+  }
+}
+
+// Internal mixin that adds enable classes to the selector if needed.
+@mixin _rfs-enable-class {
+  @if $rfs-class == "enable" {
+    .enable-responsive-font-size &,
+    &.enable-responsive-font-size {
+      @content;
+    }
+  }
+  @else {
+    @content;
+  }
+}
+
+// Internal mixin used to determine which media query needs to be used
+@mixin _rfs-media-query($mq-value) {
+  @if $rfs-two-dimensional {
+    @media (max-width: #{$mq-value}), (max-height: #{$mq-value}) {
+      @content;
+    }
+  }
+  @else {
+    @media (max-width: #{$mq-value}) {
+      @content;
+    }
+  }
+}
+
+// Responsive font size mixin
+@mixin rfs($fs, $important: false) {
+  // Cache $fs unit
+  $fs-unit: if(type-of($fs) == "number", unit($fs), false);
+
+  // Add !important suffix if needed
+  $rfs-suffix: if($important, " !important", "");
+
+  // If $fs isn't a number (like inherit) or $fs has a unit (not px or rem, like 1.5em) or $ is 0, just print the value
+  @if not $fs-unit or $fs-unit != "" and $fs-unit != "px" and $fs-unit != "rem" or $fs == 0 {
+    font-size: #{$fs}#{$rfs-suffix};
+  }
+  @else {
+    // Remove unit from $fs for calculations
+    @if $fs-unit == "px" {
+      $fs: divide($fs, $fs * 0 + 1);
+    }
+    @else if $fs-unit == "rem" {
+      $fs: divide($fs, divide($fs * 0 + 1, $rfs-rem-value));
+    }
+
+    // Set default font size
+    $rfs-static: if($rfs-font-size-unit == rem, #{divide($fs, $rfs-rem-value)}rem, #{$fs}px);
+
+    // Only add the media query if the font size is bigger than the minimum font size
+    @if $fs <= $rfs-base-font-size or not $enable-responsive-font-sizes {
+      font-size: #{$rfs-static}#{$rfs-suffix};
+    }
+    @else {
+      // Calculate the minimum font size for $fs
+      $fs-min: $rfs-base-font-size + divide($fs - $rfs-base-font-size, $rfs-factor);
+
+      // Calculate difference between $fs and the minimum font size
+      $fs-diff: $fs - $fs-min;
+
+      // Base font-size formatting
+      $min-width: if($rfs-font-size-unit == rem, #{divide($fs-min, $rfs-rem-value)}rem, #{$fs-min}px);
+
+      // Use `vmin` if two-dimensional is enabled
+      $variable-unit: if($rfs-two-dimensional, vmin, vw);
+
+      // Calculate the variable width between 0 and $rfs-breakpoint
+      $variable-width: #{divide($fs-diff * 100, $rfs-breakpoint)}#{$variable-unit};
+
+      // Set the calculated font-size
+      $rfs-fluid: calc(#{$min-width} + #{$variable-width}) #{$rfs-suffix};
+
+      // Breakpoint formatting
+      $mq-value: if($rfs-breakpoint-unit == px, #{$rfs-breakpoint}px, #{divide($rfs-breakpoint, $rfs-rem-value)}#{$rfs-breakpoint-unit});
+
+      @include _rfs-disable-class {
+        font-size: #{$rfs-static}#{$rfs-suffix};
+      }
+
+      @include _rfs-media-query($mq-value) {
+        @include _rfs-enable-class {
+          font-size: $rfs-fluid;
+        }
+
+        // Include safari iframe resize fix if needed
+        min-width: if($rfs-safari-iframe-resize-bug-fix, (0 * 1vw), null);
+      }
+    }
+  }
+}
+
+// The font-size & responsive-font-size mixins use RFS to rescale the font size
+@mixin font-size($fs, $important: false) {
+  @include rfs($fs, $important);
+}
+
+@mixin responsive-font-size($fs, $important: false) {
+  @include rfs($fs, $important);
+}