--- name: bootstrap-v5-v6-migration description: Migrate projects from Bootstrap 5 to Bootstrap 6. Use when upgrading Bootstrap, migrating v5 to v6, or updating Bootstrap class names, components, Sass, or JavaScript to the latest version. --- # Bootstrap v5 to v6 Migration ## Workflow Work through each phase in order. After each phase, search the codebase for remaining v5 patterns before moving on. - [ ] Phase 1: Update dependencies and build setup - [ ] Phase 2: Rename CSS classes and data attributes - [ ] Phase 3: Restructure component HTML - [ ] Phase 4: Update JavaScript - [ ] Phase 5: Update Sass - [ ] Phase 6: Verify --- ## Phase 1: Dependencies & Build 1. Update `package.json`: `"bootstrap": "^6.0.0"` 2. Replace `@popperjs/core` with `@floating-ui/dom` 3. If using Datepicker, add peer dep `vanilla-calendar-pro` 4. Sass: replace all `@import` with `@use` (Node Sass is no longer supported) ```scss // v5 @import "bootstrap/scss/bootstrap"; // v6 @use "bootstrap/scss/bootstrap"; // v6 with overrides @use "bootstrap/scss/bootstrap" with ( $spacer: 1rem ); ``` --- ## Phase 2: CSS Class & Attribute Renames ### Responsive & state prefix syntax v6 moves breakpoints and pseudo-states from infix/suffix to prefix with colon. Also renames `xxl` to `2xl`. **Pattern:** `.{class}-{bp}-{value}` becomes `.{bp}:{class}-{value}` and `.{class}-{bp}` becomes `.{bp}:{class}` | v5 | v6 | |---|---| | `.d-md-none`, `.p-lg-3` | `.md:d-none`, `.lg:p-3` | | `.col-md-6` | `.md:col-6` | | `.row-cols-md-3` | `.md:row-cols-3` | | `.offset-md-2` | `.md:offset-2` | | `.g-md-3`, `.gx-md-3` | `.md:g-3`, `.md:gx-3` | | `.g-col-md-4` | `.md:g-col-4` | | `.container-sm` | `.sm:container` | | `.navbar-expand-md` | `.md:navbar-expand` | | `.offcanvas-md` | `.md:drawer` | | `.table-responsive-md` | `.md:table-responsive` | | `.list-group-horizontal-md` | `.md:list-group-horizontal` | | `.sticky-md-top` | `.md:sticky-top` | | `.vstack-md` | `.md:vstack` | | `.dialog-fullscreen-sm-down` | `.sm-down:dialog-fullscreen` | | `.d-print-none` | `.print:d-none` | | `.opacity-50-hover` | `.hover:opacity-50` | ### Component renames Three components have been fully renamed. Find-and-replace these prefixes across classes, data attributes, events, JS imports, and CSS variables. **Modal -> Dialog** | Scope | v5 | v6 | |---|---|---| | Classes | `.modal`, `.modal-header/body/footer/title` | `.dialog`, `.dialog-header/body/footer/title` | | Sizes | `.modal-sm/lg/xl/fullscreen` | `.dialog-sm/lg/xl/fullscreen` | | Data attrs | `data-bs-toggle="modal"`, `data-bs-dismiss="modal"` | `data-bs-toggle="dialog"`, `data-bs-dismiss="dialog"` | | JS export | `Modal` | `Dialog` | | Events | `*.bs.modal` | `*.bs.dialog` | | CSS vars | `--modal-*` | `--dialog-*` | | Body class | `.modal-open` on `` | `.dialog-open` on `` | Remove `.modal-dialog` and `.modal-content` wrappers entirely — see Phase 3. **Offcanvas -> Drawer** | Scope | v5 | v6 | |---|---|---| | Classes | `.offcanvas`, `.offcanvas-start/end/top/bottom/header/body/title` | `.drawer`, `.drawer-start/end/top/bottom/header/body/title` | | Data attrs | `data-bs-toggle="offcanvas"`, `data-bs-dismiss="offcanvas"` | `data-bs-toggle="drawer"`, `data-bs-dismiss="drawer"` | | JS export | `Offcanvas` | `Drawer` | | Events | `*.bs.offcanvas` | `*.bs.drawer` | | CSS vars | `--offcanvas-*` | `--drawer-*` | | Sass | `$zindex-offcanvas` | `$zindex-drawer` | **Dropdown -> Menu** | Scope | v5 | v6 | |---|---|---| | Classes | `.dropdown-menu`, `.dropdown-item`, `.dropdown-divider`, `.dropdown-header` | `.menu`, `.menu-item`, `.menu-divider`, `.menu-header` | | Data attrs | `data-bs-toggle="dropdown"` | `data-bs-toggle="menu"` | | JS export | `Dropdown` | `Menu` | | Events | `*.bs.dropdown` | `*.bs.menu` | | Sass | `$zindex-dropdown` | `$zindex-menu` | Also remove: `.dropdown-toggle` (no longer needed), `.dropdown` wrapper, `.dropdown-toggle-split`. See Phase 3 for new markup. ### Button & badge variants -> theme tokens Per-color classes are replaced by variant + `.theme-*` composition. Apply to all colors (`primary`, `secondary`, `success`, `danger`, `warning`, `info`, `light`, `dark`). | v5 | v6 | |---|---| | `.btn-primary` | `.btn-solid .theme-primary` | | `.btn-outline-primary` | `.btn-outline .theme-primary` | | `.alert-primary` | `.alert .theme-primary` | | `.badge.bg-primary` | `.badge-subtle .theme-primary` | New button variants: `.btn-solid`, `.btn-outline`, `.btn-subtle`, `.btn-text`, `.btn-styled`, `.btn-link`. ### Utility class renames | v5 | v6 | |---|---| | `.text-primary`, `.text-danger`, etc. | `.fg-primary`, `.fg-danger`, etc. | | `.text-muted` | `.fg-secondary` | | `.mh-*` | `.max-h-*` | | `.mw-*` | `.max-w-*` | | `.form-select` | `.form-control` (on `
``` Radio and switch equivalents: ```html
``` ### Toggle buttons Input is now nested inside the label. `.btn-check` goes on the label. No `id`/`for` needed. ```html ``` ### Breadcrumbs Add `.breadcrumb-link` on `` elements. Add `.breadcrumb-divider` separator elements between items (replaces `::before` pseudo-elements). ```html ``` --- ## Phase 4: JavaScript ### ESM-only All dist files are now ES modules. No more UMD bundles or `window.bootstrap` global. ```html ``` Replace global namespace access with imports: ```js // v5 const tooltip = bootstrap.Tooltip.getOrCreateInstance(el) // v6 import { Tooltip } from './bootstrap.bundle.min.js' const tooltip = Tooltip.getOrCreateInstance(el) ``` Data attribute APIs (`data-bs-toggle`, etc.) are unchanged — just add `type="module"` to the script tag. Bundler imports (`import { X } from 'bootstrap'`) work as before. ### Renamed JS exports | v5 | v6 | |---|---| | `Modal` | `Dialog` | | `Offcanvas` | `Drawer` | | `Dropdown` | `Menu` | ### Popper -> Floating UI Replace `@popperjs/core` with `@floating-ui/dom`. Rename the `popperConfig` option to `floatingConfig` on Tooltip, Popover, and Menu. ### Removed - jQuery support - `bootstrap.esm.js` / `bootstrap.esm.min.js` — use `bootstrap.js` - `js/index.umd.js` ### Validation JS ```js // v5 document.querySelectorAll('.needs-validation') form.classList.add('was-validated') // v6 document.querySelectorAll('form[data-bs-validate]') // Remove the was-validated line entirely ``` --- ## Phase 5: Sass ### Renamed files | v5 | v6 | |---|---| | `_variables.scss` | `_config.scss` | | `_variables-dark.scss` | Removed (merged into `_theme.scss`) | | `_maps.scss` | Removed | | `_placeholders.scss` | `_placeholder.scss` | | `_spinners.scss` | `_spinner.scss` | | `_form-check.scss` | `_checkbox.scss`, `_radio.scss`, `_switch.scss` | | `mixins/_forms.scss` | `mixins/_form-validation.scss` | | `forms/_form-variables.scss` | Removed | | `vendor/_rfs.scss` | Removed | ### Renamed variables and functions | v5 | v6 | |---|---| | `$grid-breakpoints` | `$breakpoints` | | `$border-radius-xxl` | `$border-radius-2xl` | | `$text-muted` | Use secondary color | | `$hr-bg-color` | `$hr-border-color` | | `$hr-height` | `$hr-border-width` | | `$zindex-dropdown` | `$zindex-menu` | | `$zindex-offcanvas` | `$zindex-drawer` | | `$form-validation-states` | `$validation-states` | | `$btn-close-white-filter` | `$btn-close-filter-dark` | | `add()` / `subtract()` | `calc()` | | `breakpoint-infix()` | `breakpoint-prefix()` (returns `"md\:"` not `"-md"`) | | `$infix` (in loop mixins) | `$prefix` | | `$prefix` (CSS var prefix) | Removed — use PostCSS instead | ### Removed (no replacement) - `$nested-kbd-font-weight` - `$enable-validation-icons` - `$accordion-button-focus-border-color`, `$tooltip-arrow-color` - `$popover-arrow-color`, `$popover-arrow-outer-color` - `$alert-bg-scale`, `$alert-border-scale`, `$alert-color-scale` - `$list-group-item-bg-scale`, `$list-group-item-color-scale` - `$carousel-dark-indicator-active-bg`, `$carousel-dark-caption-color`, `$carousel-dark-control-icon-filter` - `$dropdown-header-padding` - All `*-focus-box-shadow` variables — use `focus-ring()` mixin with `--focus-ring-*` CSS custom properties - RFS mixins — use `clamp()` for responsive sizing - `create-css-vars()` mixin - `muted`, `black-50`, `white-50` from text color utilities map ### Utility API Removed `css-var`, `css-variable-name`, and `local-vars` options. Use `property` map and `variables` instead. --- ## Phase 6: Verify 1. Build the project and fix any compilation errors. 2. Search for remaining v5 patterns: - `modal` classes/attributes (should be `dialog`) - `offcanvas` (should be `drawer`) - `dropdown` (should be `menu`) - `d-md-`, `d-lg-`, `col-md-`, etc. (should use `md:` prefix syntax) - `btn-primary`, `btn-outline-` (should use `.btn-solid .theme-*`) - `.text-primary`, `.text-danger` (should be `.fg-*`) - `@import` in Sass (should be `@use`) - `form-select` (should be `form-control`) - `was-validated`, `needs-validation` (should be `data-bs-validate`) - `popperConfig` (should be `floatingConfig`) - `form-check` (should be `check`, `radio`, or `switch`) 3. Test in browser — v6 requires support for `oklch()` and `color-mix()`.