Copy-pasted code blocks that can be consolidated
Duplicated mixin definitions or @include patterns. Consider parameterizing the mixin or extracting shared logic.
@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);
}
}
@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 A | Lines | File B | Lines | Duplicated lines | Suggested fix |
|---|---|---|---|---|---|
| scss/mixins/_utilities.scss | 210–226 | scss/mixins/_utilities.scss | 188–205 | 17 | Parameterize mixin or extract shared SCSS logic to reduce duplication |
// 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
// 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 A | Lines | File B | Lines | Duplicated lines | Suggested fix |
|---|---|---|---|---|---|
| scss/forms/_validation.scss | 16–39 | scss/mixins/_forms.scss | 1–24 | 24 | Parameterize mixin or extract shared SCSS logic to reduce duplication |
$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 {
$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 A | Lines | File B | Lines | Duplicated lines | Suggested fix |
|---|---|---|---|---|---|
| scss/forms/_validation.scss | 40–107 | scss/mixins/_forms.scss | 25–92 | 68 | Parameterize mixin or extract shared SCSS logic to reduce duplication |
.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
.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 A | Lines | File B | Lines | Duplicated lines | Suggested fix |
|---|---|---|---|---|---|
| scss/forms/_validation.scss | 133–178 | scss/mixins/_forms.scss | 118–163 | 46 | Parameterize mixin or extract shared SCSS logic to reduce duplication |
Duplicated SCSS property blocks, value maps, or rule sets. Consider using Sass variables, maps, or loops to DRY up repeated values.
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"
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 A | Lines | File B | Lines | Duplicated lines | Suggested 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 |
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"
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 A | Lines | File B | Lines | Duplicated lines | Suggested 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 |
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"
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 A | Lines | File B | Lines | Duplicated lines | Suggested 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 |
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
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 A | Lines | File B | Lines | Duplicated lines | Suggested 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 |
Shared code duplicated between different plugin files. These are the highest-value consolidation targets — extract to a shared base class method or utility function.
)
if (this._responsivePlacements) {
this._setupMediaQueryListeners()
}
}
_setupMediaQueryListeners() {
this._disposeMediaQueryListeners()
this._mediaQueryListeners = createBreakpointListeners(() => {
if (this._isShown()) {
this._updateFloatingPosition()
}
})
}
_disposeMediaQueryListeners() {
disposeBreakpointListeners(this._mediaQueryListeners)
this._mediaQueryListeners = []
}
_getOffset
)
if (this._responsivePlacements) {
this._setupMediaQueryListeners()
}
}
_setupMediaQueryListeners() {
this._disposeMediaQueryListeners()
this._mediaQueryListeners = createBreakpointListeners(() => {
if (this._isShown()) {
this._updateFloatingPosition()
}
})
}
_disposeMediaQueryListeners() {
disposeBreakpointListeners(this._mediaQueryListeners)
this._mediaQueryListeners = []
}
_getOffset
| File A | Lines | File B | Lines | Duplicated lines | Suggested 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) |
) {
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
) {
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 A | Lines | File B | Lines | Duplicated lines | Suggested 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) |
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.
&& 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
&& 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 A | Lines | File B | Lines | Duplicated lines | Suggested fix |
|---|---|---|---|---|---|
| js/src/dropdown.js | 804–824 | js/src/dropdown.js | 783–803 | 21 | Extract to shared local function within dropdown |
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
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 A | Lines | File B | Lines | Duplicated lines | Suggested fix |
|---|---|---|---|---|---|
| js/src/datepicker.js | 358–373 | js/src/datepicker.js | 207–222 | 16 | Extract to shared local function within datepicker |
Identical event handler patterns (DATA_API, EventHandler.on/off) duplicated across plugins. Consider a shared lifecycle initialization pattern.
)
})
}
}
/**
* 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
)
})
}
}
/**
* 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 A | Lines | File B | Lines | Duplicated lines | Suggested 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) |