Bootstrap v6 — Code Duplication Report

Copy-pasted code blocks that can be consolidated

13 Clones
296 Duplicated lines
1.46% Duplication
9 Files affected

SCSS Mixin Duplication

needs review 4

Duplicated mixin definitions or @include patterns. Consider parameterizing the mixin or extracting shared logic.

scss/mixins/_utilities.scss ↔ scss/mixins/_utilities.scss 17 lines
scss/mixins/_utilities.scss:210–226
@if map.has-key($utility, variables) {
            $variables: map.get($utility, variables);
            @if meta.type-of($variables) == "list" {
              // If variables is a list, each variable gets the utility value
              @each $var-name in $variables {
                --#{$var-name}: #{$value};
              }
            } @else if meta.type-of($variables) == "map" {
              // If variables is a map, use the provided values (for static variables)
              @each $var-key, $var-value in $variables {
                --#{$var-key}: #{$var-value};
              }
            }
          }
          @include generate-properties($utility, $propertyMap, $properties, $value);
        }
      }
scss/mixins/_utilities.scss:188–205
@if map.has-key($utility, variables) {
            $variables: map.get($utility, variables);
            @if meta.type-of($variables) == "list" {
              // If variables is a list, each variable gets the utility value
              @each $var-name in $variables {
                --#{$var-name}: #{$value};
              }
            } @else if meta.type-of($variables) == "map" {
              // If variables is a map, use the provided values (for static variables)
              @each $var-key, $var-value in $variables {
                --#{$var-key}: #{$var-value};
              }
            }
          }
          @include generate-properties($utility, $propertyMap, $properties, $value);
        }
      }
File ALinesFile BLinesDuplicated linesSuggested fix
scss/mixins/_utilities.scss 210–226 scss/mixins/_utilities.scss 188–205 17 Parameterize mixin or extract shared SCSS logic to reduce duplication
scss/forms/_validation.scss ↔ scss/mixins/_forms.scss 24 lines
scss/forms/_validation.scss:16–39
// 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

// scss-docs-start form-validation-mixins
@mixin form-validation-state-selector($state) {
  @if ($state == "valid" or $state == "invalid") {
    .was-validated #{if(sass(&): "&"; else: "")}:#{$state},
    #{if(sass(&): "&"; else: "")}.is-#{$state} {
      @content;
    }
  } @else {
    #{if(sass(&): "&"; else: "")}.is-#{$state} {
      @content;
    }
  }
}

@mixin form-validation-state(
  $state,
  $color,
  $icon,
  $tooltip-color: color-contrast($color),
  $tooltip-bg-color: rgba($color, $form-feedback-tooltip-opacity),
  $focus-box-shadow: null
scss/mixins/_forms.scss:1–24
// 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

// scss-docs-start form-validation-mixins
@mixin form-validation-state-selector($state) {
  @if ($state == "valid" or $state == "invalid") {
    .was-validated #{if(sass(&): "&"; else: "")}:#{$state},
    #{if(sass(&): "&"; else: "")}.is-#{$state} {
      @content;
    }
  } @else {
    #{if(sass(&): "&"; else: "")}.is-#{$state} {
      @content;
    }
  }
}

@mixin form-validation-state(
  $state,
  $color,
  $icon,
  $tooltip-color: color-contrast($color),
  $tooltip-bg-color: rgba($color, $form-feedback-tooltip-opacity),
  $focus-box-shadow: null
File ALinesFile BLinesDuplicated linesSuggested fix
scss/forms/_validation.scss 16–39 scss/mixins/_forms.scss 1–24 24 Parameterize mixin or extract shared SCSS logic to reduce duplication
scss/forms/_validation.scss ↔ scss/mixins/_forms.scss 68 lines
scss/forms/_validation.scss:40–107
$border-color: $color
) {
  .#{$state}-feedback {
    display: none;
    width: 100%;
    margin-top: $form-feedback-margin-top;
    font-size: $form-feedback-font-size;
    font-style: $form-feedback-font-style;
    color: $color;
  }

  .#{$state}-tooltip {
    position: absolute;
    top: 100%;
    z-index: 5;
    display: none;
    max-width: 100%; // Contain to parent when possible
    padding: $form-feedback-tooltip-padding-y $form-feedback-tooltip-padding-x;
    margin-top: .1rem;
    font-size: $form-feedback-tooltip-font-size;
    line-height: $form-feedback-tooltip-line-height;
    color: $tooltip-color;
    background-color: $tooltip-bg-color;
    @include border-radius($form-feedback-tooltip-border-radius);
  }

  @include form-validation-state-selector($state) {
    ~ .#{$state}-feedback,
    ~ .#{$state}-tooltip {
      display: block;
    }
  }

  .form-control {
    @include form-validation-state-selector($state) {
      border-color: $border-color;

      @if $enable-validation-icons {
        padding-inline-end: $input-height-inner;
        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: $border-color;
        @if $enable-shadows {
          @include box-shadow($input-box-shadow, $focus-box-shadow);
        } @else {
          // Avoid using mixin so we can pass custom focus shadow properly
          box-shadow: $focus-box-shadow;
        }
      }
    }
  }

  // stylelint-disable-next-line selector-no-qualifying-type
  textarea.form-control {
    @include form-validation-state-selector($state) {
      @if $enable-validation-icons {
        padding-inline-end: $input-height-inner;
        background-position: top $input-height-inner-quarter right $input-height-inner-quarter;
      }
    }
  }

  // .form-select {
scss/mixins/_forms.scss:25–92
$border-color: $color
) {
  .#{$state}-feedback {
    display: none;
    width: 100%;
    margin-top: $form-feedback-margin-top;
    font-size: $form-feedback-font-size;
    font-style: $form-feedback-font-style;
    color: $color;
  }

  .#{$state}-tooltip {
    position: absolute;
    top: 100%;
    z-index: 5;
    display: none;
    max-width: 100%; // Contain to parent when possible
    padding: $form-feedback-tooltip-padding-y $form-feedback-tooltip-padding-x;
    margin-top: .1rem;
    font-size: $form-feedback-tooltip-font-size;
    line-height: $form-feedback-tooltip-line-height;
    color: $tooltip-color;
    background-color: $tooltip-bg-color;
    @include border-radius($form-feedback-tooltip-border-radius);
  }

  @include form-validation-state-selector($state) {
    ~ .#{$state}-feedback,
    ~ .#{$state}-tooltip {
      display: block;
    }
  }

  .form-control {
    @include form-validation-state-selector($state) {
      border-color: $border-color;

      @if $enable-validation-icons {
        padding-inline-end: $input-height-inner;
        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: $border-color;
        @if $enable-shadows {
          @include box-shadow($input-box-shadow, $focus-box-shadow);
        } @else {
          // Avoid using mixin so we can pass custom focus shadow properly
          box-shadow: $focus-box-shadow;
        }
      }
    }
  }

  // stylelint-disable-next-line selector-no-qualifying-type
  textarea.form-control {
    @include form-validation-state-selector($state) {
      @if $enable-validation-icons {
        padding-inline-end: $input-height-inner;
        background-position: top $input-height-inner-quarter right $input-height-inner-quarter;
      }
    }
  }

  // .form-select {
File ALinesFile BLinesDuplicated linesSuggested fix
scss/forms/_validation.scss 40–107 scss/mixins/_forms.scss 25–92 68 Parameterize mixin or extract shared SCSS logic to reduce duplication
scss/forms/_validation.scss ↔ scss/mixins/_forms.scss 46 lines
scss/forms/_validation.scss:133–178
.form-control-color {
    @include form-validation-state-selector($state) {
      @if $enable-validation-icons {
        width: add($form-color-width, $input-height-inner);
      }
    }
  }

  .form-check-input {
    @include form-validation-state-selector($state) {
      border-color: $border-color;

      &:checked {
        background-color: $color;
      }

      &:focus {
        box-shadow: $focus-box-shadow;
      }

      ~ .form-check-label {
        color: $color;
      }
    }
  }
  .form-check-inline .form-check-input {
    ~ .#{$state}-feedback {
      margin-inline-start: .5em;
    }
  }

  .input-group {
    > .form-control:not(:focus),
    > .form-select:not(:focus),
    > .form-floating:not(:focus-within) {
      @include form-validation-state-selector($state) {
        @if $state == "valid" {
          z-index: 3;
        } @else if $state == "invalid" {
          z-index: 4;
        }
      }
    }
  }
}
// scss-docs-end form-validation-mixins
scss/mixins/_forms.scss:118–163
.form-control-color {
    @include form-validation-state-selector($state) {
      @if $enable-validation-icons {
        width: add($form-color-width, $input-height-inner);
      }
    }
  }

  .form-check-input {
    @include form-validation-state-selector($state) {
      border-color: $border-color;

      &:checked {
        background-color: $color;
      }

      &:focus {
        box-shadow: $focus-box-shadow;
      }

      ~ .form-check-label {
        color: $color;
      }
    }
  }
  .form-check-inline .form-check-input {
    ~ .#{$state}-feedback {
      margin-inline-start: .5em;
    }
  }

  .input-group {
    > .form-control:not(:focus),
    > .form-select:not(:focus),
    > .form-floating:not(:focus-within) {
      @include form-validation-state-selector($state) {
        @if $state == "valid" {
          z-index: 3;
        } @else if $state == "invalid" {
          z-index: 4;
        }
      }
    }
  }
}
// scss-docs-end form-validation-mixins
File ALinesFile BLinesDuplicated linesSuggested fix
scss/forms/_validation.scss 133–178 scss/mixins/_forms.scss 118–163 46 Parameterize mixin or extract shared SCSS logic to reduce duplication

SCSS Pattern Duplication

needs review 4

Duplicated SCSS property blocks, value maps, or rule sets. Consider using Sass variables, maps, or loops to DRY up repeated values.

scss/_utilities.scss ↔ scss/_utilities.scss 13 lines
scss/_utilities.scss:894–906
values: (
        null: var(--border-radius),
        0: 0,
        1: var(--border-radius-sm),
        2: var(--border-radius),
        3: var(--border-radius-lg),
        4: var(--border-radius-xl),
        5: var(--border-radius-2xl),
        circle: 50%,
        pill: var(--border-radius-pill)
      )
    ),
    "rounded-end"
scss/_utilities.scss:879–891
values: (
        null: var(--border-radius),
        0: 0,
        1: var(--border-radius-sm),
        2: var(--border-radius),
        3: var(--border-radius-lg),
        4: var(--border-radius-xl),
        5: var(--border-radius-2xl),
        circle: 50%,
        pill: var(--border-radius-pill)
      )
    ),
    "rounded-end"
File ALinesFile BLinesDuplicated linesSuggested fix
scss/_utilities.scss 894–906 scss/_utilities.scss 879–891 13 Use a Sass @each loop over a shared map to generate these repeated blocks
scss/_utilities.scss ↔ scss/_utilities.scss 13 lines
scss/_utilities.scss:909–921
values: (
        null: var(--border-radius),
        0: 0,
        1: var(--border-radius-sm),
        2: var(--border-radius),
        3: var(--border-radius-lg),
        4: var(--border-radius-xl),
        5: var(--border-radius-2xl),
        circle: 50%,
        pill: var(--border-radius-pill)
      )
    ),
    "rounded-bottom"
scss/_utilities.scss:879–891
values: (
        null: var(--border-radius),
        0: 0,
        1: var(--border-radius-sm),
        2: var(--border-radius),
        3: var(--border-radius-lg),
        4: var(--border-radius-xl),
        5: var(--border-radius-2xl),
        circle: 50%,
        pill: var(--border-radius-pill)
      )
    ),
    "rounded-bottom"
File ALinesFile BLinesDuplicated linesSuggested fix
scss/_utilities.scss 909–921 scss/_utilities.scss 879–891 13 Use a Sass @each loop over a shared map to generate these repeated blocks
scss/_utilities.scss ↔ scss/_utilities.scss 13 lines
scss/_utilities.scss:924–936
values: (
        null: var(--border-radius),
        0: 0,
        1: var(--border-radius-sm),
        2: var(--border-radius),
        3: var(--border-radius-lg),
        4: var(--border-radius-xl),
        5: var(--border-radius-2xl),
        circle: 50%,
        pill: var(--border-radius-pill)
      )
    ),
    "rounded-start"
scss/_utilities.scss:879–891
values: (
        null: var(--border-radius),
        0: 0,
        1: var(--border-radius-sm),
        2: var(--border-radius),
        3: var(--border-radius-lg),
        4: var(--border-radius-xl),
        5: var(--border-radius-2xl),
        circle: 50%,
        pill: var(--border-radius-pill)
      )
    ),
    "rounded-start"
File ALinesFile BLinesDuplicated linesSuggested fix
scss/_utilities.scss 924–936 scss/_utilities.scss 879–891 13 Use a Sass @each loop over a shared map to generate these repeated blocks
scss/_utilities.scss ↔ scss/_utilities.scss 13 lines
scss/_utilities.scss:939–951
values: (
        null: var(--border-radius),
        0: 0,
        1: var(--border-radius-sm),
        2: var(--border-radius),
        3: var(--border-radius-lg),
        4: var(--border-radius-xl),
        5: var(--border-radius-2xl),
        circle: 50%,
        pill: var(--border-radius-pill)
      )
    ),
    // scss-docs-end utils-border-radius
scss/_utilities.scss:879–891
values: (
        null: var(--border-radius),
        0: 0,
        1: var(--border-radius-sm),
        2: var(--border-radius),
        3: var(--border-radius-lg),
        4: var(--border-radius-xl),
        5: var(--border-radius-2xl),
        circle: 50%,
        pill: var(--border-radius-pill)
      )
    ),
    // scss-docs-end utils-border-radius
File ALinesFile BLinesDuplicated linesSuggested fix
scss/_utilities.scss 939–951 scss/_utilities.scss 879–891 13 Use a Sass @each loop over a shared map to generate these repeated blocks

Cross-Plugin Logic

needs review 2

Shared code duplicated between different plugin files. These are the highest-value consolidation targets — extract to a shared base class method or utility function.

js/src/dropdown.js ↔ js/src/tooltip.js 22 lines
js/src/dropdown.js:348–369
)

    if (this._responsivePlacements) {
      this._setupMediaQueryListeners()
    }
  }

  _setupMediaQueryListeners() {
    this._disposeMediaQueryListeners()
    this._mediaQueryListeners = createBreakpointListeners(() => {
      if (this._isShown()) {
        this._updateFloatingPosition()
      }
    })
  }

  _disposeMediaQueryListeners() {
    disposeBreakpointListeners(this._mediaQueryListeners)
    this._mediaQueryListeners = []
  }

  _getOffset
js/src/tooltip.js:403–424
)

    if (this._responsivePlacements) {
      this._setupMediaQueryListeners()
    }
  }

  _setupMediaQueryListeners() {
    this._disposeMediaQueryListeners()
    this._mediaQueryListeners = createBreakpointListeners(() => {
      if (this._isShown()) {
        this._updateFloatingPosition()
      }
    })
  }

  _disposeMediaQueryListeners() {
    disposeBreakpointListeners(this._mediaQueryListeners)
    this._mediaQueryListeners = []
  }

  _getOffset
File ALinesFile BLinesDuplicated linesSuggested fix
js/src/dropdown.js 348–369 js/src/tooltip.js 403–424 22 Extract responsive placement logic to shared util (used by dropdown and tooltip)
js/src/dropdown.js ↔ js/src/tooltip.js 13 lines
js/src/dropdown.js:387–399
) {
    const offsetValue = this._getOffset()

    const middleware = [
      // Offset middleware - handles distance from reference
      offset(
        typeof offsetValue === 'function' ?
          offsetValue :
          { mainAxis: offsetValue[1] || 0, crossAxis: offsetValue[0] || 0 }
      ),
      // Flip middleware - handles fallback placements
      flip({
        fallbackPlacements: this._getFallbackPlacements
js/src/tooltip.js:516–528
) {
    const offsetValue = this._getOffset()

    const middleware = [
      // Offset middleware - handles distance from reference
      offset(
        typeof offsetValue === 'function' ?
          offsetValue :
          { mainAxis: offsetValue[1] || 0, crossAxis: offsetValue[0] || 0 }
      ),
      // Flip middleware - handles fallback placements
      flip({
        fallbackPlacements: this._getFallbackPlacements
File ALinesFile BLinesDuplicated linesSuggested fix
js/src/dropdown.js 387–399 js/src/tooltip.js 516–528 13 Extract responsive placement logic to shared util (used by dropdown and tooltip)

Same-File Duplication

needs review 2

Code blocks duplicated within the same JS file. These indicate copy-paste within a single component that can be refactored into a shared local function.

js/src/dropdown.js ↔ js/src/dropdown.js 21 lines
js/src/dropdown.js:804–824
&& isSubmenuTrigger) {
      event.preventDefault()
      event.stopPropagation()

      const submenu = SelectorEngine.findOne(SELECTOR_MENU, submenuWrapper)
      if (submenu) {
        this._closeSiblingSubmenus(submenuWrapper)
        this._openSubmenu(target, submenu, submenuWrapper)
        // Focus first item in submenu
        requestAnimationFrame(() => {
          const firstItem = SelectorEngine.findOne(SELECTOR_VISIBLE_ITEMS, submenu)
          if (firstItem) {
            firstItem.focus()
          }
        })
      }

      return true
    }

    // Handle Left arrow (or Right in RTL) - exit submenu
js/src/dropdown.js:783–803
&& isSubmenuTrigger) {
      event.preventDefault()
      event.stopPropagation()

      const submenu = SelectorEngine.findOne(SELECTOR_MENU, submenuWrapper)
      if (submenu) {
        this._closeSiblingSubmenus(submenuWrapper)
        this._openSubmenu(target, submenu, submenuWrapper)
        // Focus first item in submenu
        requestAnimationFrame(() => {
          const firstItem = SelectorEngine.findOne(SELECTOR_VISIBLE_ITEMS, submenu)
          if (firstItem) {
            firstItem.focus()
          }
        })
      }

      return true
    }

    // Handle Left arrow (or Right in RTL) - exit submenu
File ALinesFile BLinesDuplicated linesSuggested fix
js/src/dropdown.js 804–824 js/src/dropdown.js 783–803 21 Extract to shared local function within dropdown
js/src/datepicker.js ↔ js/src/datepicker.js 16 lines
js/src/datepicker.js:358–373
const formattedDate = this._formatDateForInput(selectedDates)

      if (this._isInput) {
        this._element.value = formattedDate
      }

      if (this._boundInput) {
        this._boundInput.value = selectedDates.join(',')
      }

      if (this._displayElement) {
        this._displayElement.textContent = formattedDate
      }
    }

    EventHandler
js/src/datepicker.js:207–222
const formattedDate = this._formatDateForInput(selectedDates)

      if (this._isInput) {
        this._element.value = formattedDate
      }

      if (this._boundInput) {
        this._boundInput.value = selectedDates.join(',')
      }

      if (this._displayElement) {
        this._displayElement.textContent = formattedDate
      }
    }

    EventHandler
File ALinesFile BLinesDuplicated linesSuggested fix
js/src/datepicker.js 358–373 js/src/datepicker.js 207–222 16 Extract to shared local function within datepicker

Event Lifecycle

needs review 1

Identical event handler patterns (DATA_API, EventHandler.on/off) duplicated across plugins. Consider a shared lifecycle initialization pattern.

js/src/dialog.js ↔ js/src/offcanvas.js 17 lines
js/src/dialog.js:217–233
)
    })
  }
}

/**
 * Data API implementation
 */

EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
  const target = SelectorEngine.getElementFromSelector(this)

  if (['A', 'AREA'].includes(this.tagName)) {
    event.preventDefault()
  }

  EventHandler
js/src/offcanvas.js:205–221
)
    })
  }
}

/**
 * Data API implementation
 */

EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
  const target = SelectorEngine.getElementFromSelector(this)

  if (['A', 'AREA'].includes(this.tagName)) {
    event.preventDefault()
  }

  EventHandler
File ALinesFile BLinesDuplicated linesSuggested fix
js/src/dialog.js 217–233 js/src/offcanvas.js 205–221 17 Extract DATA_API event registration to shared pattern in BaseComponent (dialog ↔ offcanvas)