diff --git a/.cspell.json b/.cspell.json index 4a0473ae3573..8bc4bb1fb765 100644 --- a/.cspell.json +++ b/.cspell.json @@ -33,7 +33,9 @@ "cssgrid", "csstricks", "Csvg", + "currentcolor", "Datalists", + "Datepicker", "Deque", "discoverability", "docsref", @@ -46,6 +48,7 @@ "favicons", "fieldsets", "flexbox", + "focustrap", "frontmatter", "fullscreen", "getbootstrap", @@ -76,6 +79,8 @@ "Neue", "noindex", "nonmodal", + "oklab", + "oklch", "Noto", "drawer", "offcanvas", diff --git a/site/src/content/docs/guides/migration.mdx b/site/src/content/docs/guides/migration.mdx index 48e9b7019315..a8db1cece0d0 100644 --- a/site/src/content/docs/guides/migration.mdx +++ b/site/src/content/docs/guides/migration.mdx @@ -108,7 +108,7 @@ Bootstrap 6 is a major release with many breaking changes to modernize our codeb - Removed `muted`, `black-50`, and `white-50` from text colors utilities map - Removed the carousel dark Sass variables (`$carousel-dark-indicator-active-bg`, `$carousel-dark-control-icon-filter`) and the `.carousel-dark` class—they’re not reassigned. Dark carousels now use `data-bs-theme="dark"` (see the carousel changes below). Carousel captions were removed entirely too. - Removed `$btn-close-white-filter`. The close button no longer uses a filter—its icon is a CSS mask painted with `currentcolor`, so it adapts to dark backgrounds automatically. - - Removed `$border-radius-xxl`, use `$border-radius-2xl`. + - Removed all `$border-radius-*` variables (`-xs`, `-sm`, default, `-lg`, `-xl`, `-xxl`, `-pill`) in favor of the numeric `$radii` map and `--radius-*` tokens—see [Border radius tokens](#utilities) under Utilities. - Removed `$text-muted` for secondary color. - Removed `$hr-bg-color` for `$hr-border-color` and `$hr-height` for `$hr-border-width`. - Renamed `$zindex-dropdown` to `$zindex-menu`. @@ -148,6 +148,7 @@ Bootstrap 6 is a major release with many breaking changes to modernize our codeb - Removed the separate `bootstrap.esm.js` and `bootstrap.esm.min.js` files — `bootstrap.js` is now the ESM entry point. - Removed `js/index.umd.js` entry point. - Removed jQuery support and the `js-test-jquery` test target. +- **Removed the internal `util/backdrop`, `util/focustrap`, and `util/scrollbar` helpers.** They’re obsolete now that Dialog and Drawer build on the native `` element—the browser provides the backdrop (`::backdrop`), focus trap, and an inert top layer, and the body scroll-lock is handled in CSS (`:root.dialog-open` with `scrollbar-gutter: stable`). If you imported any of these modules directly from `bootstrap/js/src/util/`, they’re gone. - **Replaced the Dropdown component with Menu.** All `.dropdown-*` classes are now `.menu-*` classes, and `data-bs-toggle="dropdown"` is now `data-bs-toggle="menu"`. See the [Menu docs]([[docsref:/components/menus]]) for full details. - Renamed CSS classes: `.dropdown-menu` to `.menu`, `.dropdown-item` to `.menu-item`, `.dropdown-divider` to `.menu-divider`, `.dropdown-header` to `.menu-header`, `.dropdown-submenu` to `.submenu`. - Deprecated directional wrappers: `.dropstart`, `.dropend`, and `.dropup`. Use `data-bs-placement` to control direction instead (for example, `.dropup` becomes `data-bs-placement="top"`). diff --git a/skills/bootstrap-v5-v6-migration/SKILL.md b/skills/bootstrap-v5-v6-migration/SKILL.md new file mode 100644 index 000000000000..2993b542313b --- /dev/null +++ b/skills/bootstrap-v5-v6-migration/SKILL.md @@ -0,0 +1,544 @@ +--- +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 — global namespace +const tooltip = bootstrap.Tooltip.getOrCreateInstance(el) +``` + +```js +// v6 — explicit import +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`, `$border-radius-sm/lg/xl/xxl`, `$border-radius-pill` | Removed — see the radius scale below | +| `$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 | + +### Border radius scale + +The `$border-radius-*` variables are gone. v6 uses a single base `$radius: .5rem` and a `$radii` map (keys `0`–`9`, e.g. `5: $radius`, `9: $radius * 3`), exposed as `--radius-0`–`--radius-9` tokens (plus `--radius-pill`). The `.rounded-*` utilities now span `0`–`9` and map to different values than v5, so shift class numbers up to keep the same roundness (e.g. `.rounded-1` → `.rounded-3`, `.rounded-3` → `.rounded-5`). Override the base or the map entries rather than the old per-size variables: + +```scss +@use "bootstrap/scss/bootstrap" with ( + $radius: .375rem // scales the whole map +); +``` + +### 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. + +--- + +## Rebuilt behavior & new components + +Beyond the renames above, several things changed how a v5 project behaves or what's available. Check the docs for full markup/options. + +### Carousel — rebuilt on CSS scroll-snap + +The markup (`.carousel` → `.carousel-inner` → `.carousel-item`) and the JS API (`next`/`prev`/`to`/`cycle`/`pause`, `slide`/`slid` events) are preserved, but several things changed: + +- **`ride` → `autoplay` (boolean, opt-in).** `data-bs-ride="carousel"` becomes `data-bs-autoplay="true"`; `{ ride: 'carousel' }` becomes `{ autoplay: true }`. Default is `autoplay: false`, so a v5 carousel that auto-advanced now sits still until you opt in. Interacting with an autoplaying carousel now **permanently stops** it (WCAG 2.2.2). +- **`wrap` → `ends`.** `wrap: true` → `ends: "wrap"` (or the new default `"loop"`); `wrap: false` → `ends: "stop"`. Set via `data-bs-ends`. The `touch` option is removed (native scroll handles it). +- **Removed classes:** `.carousel-control-prev/next` (compose a `.btn-icon` + `data-bs-slide` + `.carousel-icon-prev/next`), `.carousel-caption` (use your own markup), `.carousel-dark` (use `data-bs-theme="dark"`), `.carousel-stacked` (now the default), and the transitional `.carousel-item-start/end/next/prev`. Overlaid controls now require `.carousel-overlay`. Control-icon classes renamed `.carousel-control-prev-icon` → `.carousel-icon-prev` (and `-next`). + +### New components (didn't exist in v5) + +| Component | Trigger / hook | Purpose | +|---|---|---| +| Combobox | `data-bs-toggle="combobox"` | Filterable/autocomplete select built on Menu | +| Chip / Chip input | `.chip`, `.chip-input` (`data-bs-chips`) | Tags / tokens + interactive entry | +| Datepicker | `data-bs-toggle="datepicker"` | Date picker (peer dep `vanilla-calendar-pro`) | +| Range | `.form-range` (+ `data-bs-bubble`, ticks) | Enhanced range slider with a value bubble | +| Strength | `data-bs-strength` | Password-strength meter | +| OTP input | `data-bs-otp` | One-time-code input | +| Nav overflow | `.nav-overflow` | Collapses overflowing nav items into a menu | +| Toggler | `data-bs-toggle="toggler"` | Generic class/attribute toggler | +| Submenu | `.submenu` (within Menu) | Nested menus (`submenuTrigger`, `submenuDelay`) | +| Stepper | `.stepper` | Multi-step workflow (CSS-only) | +| Avatar | `.avatar` | Avatars with sizes, status, `.avatar-stack` (CSS-only) | +| Form adorn | `.form-adorn` | Icon/text decoration on inputs (CSS-only) | +| Prose | `.prose` / `.not-prose` | Rich-typography scoping (CSS-only) | + +### Removed / changed internals + +- **`util/backdrop.js`, `util/focustrap.js`, and `util/scrollbar.js` removed.** Dialog and Drawer use the native `` element, which provides the backdrop (`::backdrop`), the focus trap, and an inert top layer; the body scroll-lock is now CSS (`:root.dialog-open`). If you imported `bootstrap/js/src/util/backdrop`, `.../focustrap`, or `.../scrollbar` directly, they're gone. +- **CSS `@layer`.** Component styles are wrapped in cascade layers (`colors, theme, config, root, reboot, layout, content, forms, components, custom, helpers, utilities`). Author CSS outside any layer now wins over Bootstrap regardless of source order — if your v5 overrides relied on specificity or load order, re-check them. +- **`--bs-*-rgb` variables removed.** The `$*-rgb` Sass vars and `--bs-*-rgb` custom properties are gone. Replace `rgba(var(--bs-primary-rgb), .5)` with `color-mix(in oklab, var(--bs-primary), transparent 50%)` (or use the color directly). + +--- + +## 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`) + - `var(--bs-*-rgb)` / `rgba(var(--bs-…-rgb)` (removed — use `color-mix()`) + - `$border-radius` Sass vars (removed — use `$radius` / `$radii` / `--radius-*`) + - child `` inside `.btn-close` (should be empty — icon is a CSS mask) + - `.btn-close-white` (removed — set text `color` instead) + - `data-bs-ride` (renamed to `data-bs-autoplay`); `wrap:` carousel option (now `ends:`) + - `.carousel-control-prev/next`, `.carousel-caption`, `.carousel-dark`, `.carousel-stacked` (all removed) + - `.fs-1`–`.fs-6` (should be `.fs-4xl` … `.fs-md`) + - `.link-offset-*` / `.link-underline-*` (now `.underline-offset-*` / `.underline-*`) +3. Test in browser — v6 requires support for `oklch()` and `color-mix()`.