# Migration to v6

> Track and review changes to the CoreUI for React.js components to help you migrate from v5 to v6.

CoreUI for React is the React.js version of the CoreUI components library, so before reading this
migration guide, please also check the
[CoreUI 5 to 6 migration guide](https://coreui.io/bootstrap/docs/migration/v6/) — it covers the
markup, class names, Sass architecture and design tokens this release is built on. This page covers
what changes in the React API.

## Dependencies

- <span className="badge bg-danger">Breaking</span> **`propTypes` are gone from every component**,
  and `prop-types` is no longer a dependency. Prop validation is the TypeScript types, which every
  component has carried for years — the runtime copy only duplicated them, and React 19 ignores it
  anyway. If you were reading `SomeComponent.propTypes` to introspect a component, read the
  exported `SomeComponentProps` type instead.
- <span className="badge bg-danger">Breaking</span> **React 18 is the minimum.**
  `peerDependencies` moved from `>=17` to `>=18`. The library already used `useId`, which React 17
  does not have, so the old range promised support it could not deliver. React 19 is supported.
- <span className="badge bg-danger">Breaking</span> **Popper is gone; positioning runs on
  [Floating UI](https://floating-ui.com/).** `@popperjs/core` is replaced by `@floating-ui/dom`.
  Update your lockfile — nothing else is required for the default configuration.
- <span className="badge bg-danger">Breaking</span> **`popperConfig` is now `floatingConfig`** on
  `CDropdown`, `CPopover` and `CTooltip`, and it takes a Floating UI
  [`ComputePositionConfig`](https://floating-ui.com/docs/computePosition#options) instead of a
  Popper one. The `popper` boolean prop stays and still switches static positioning.
- <span className="badge bg-danger">Breaking</span> **Positioned elements are stamped with
  `data-coreui-placement`**, not `data-popper-placement`. Custom CSS, tests or scripts keyed on the
  old attribute must be updated. The v6 stylesheet draws tooltip, popover and menu arrows from this
  attribute, so it is not optional.
- <span className="badge bg-danger">Breaking</span> **The exported hooks are renamed**: `usePopper`
  → `useFloating`, `useDropdownWithPopper` → `useDropdownWithFloating`.

## New components

- **`CCombobox`** — a select-like control: a `.form-control` toggle next to a popup
  [List box](https://coreui.io/react/docs/components/list-box/), with single and multiple selection, a search field with
  optional accent-insensitive matching, sections, a checkbox indicator and a hidden select that
  posts the value and takes part in form validation. Ships with the `useCombobox()` hook. See the
  [Combobox docs](https://coreui.io/react/docs/forms/combobox/).
- **`CDateInput`, `CTimeInput`** — section fields: a masked, keyboard-first
  input where every part of the value (day, month, year, hour…) is its own editable section.
  `<CDateInput type="datetime">` carries the time too. The pickers are built on them, and they
  work standalone.
- **`CDialog`** and **`CDrawer`** — the native-`<dialog>` popup and side panel with the Bootstrap 6
  look and the `dialog-*` / `drawer-*` classes. `CDialog` is a centered floating panel over a
  blurred backdrop with optional slide-in variants; `CDrawer` is a floating, inset, rounded panel
  with sheet, translucent, fit-content and fullscreen variants plus responsive breakpoints.
  `CDialog`'s `fullscreen` takes a breakpoint as well as `true`, like `CModal`'s.

  They share their behavior with `CModal` and `COffcanvas`, which keep the classic look and the v5
  vocabulary — and one callback vocabulary of their own: `onShow`, `onClose` and
  `onClosePrevented` on both, where the legacy pair says `onHide`. **`CModal` and `COffcanvas` are deprecated and removed in v7** — prefer
  `CDialog` and `CDrawer` for new work. Pick one per surface; they are independent components.
- **`CListBox`** — a keyboard-driven list of values on the ARIA listbox pattern, with
  `none`/`single`/`multiple` selection, sections, descriptions, links, an empty state and an
  `activeDescendant` mode that leaves the focus in your own input. Ships with `CListBoxOption`,
  `CListBoxSection`, `CListBoxHeader`, `CListBoxSelectAll`, `CListBoxTitle`, `CListBoxSubtitle`,
  `CListBoxEmpty` and the `useListBox()` hook. See the
  [List Box docs](https://coreui.io/react/docs/components/list-box/).
- **`CMenu`** — contextual overlays with submenus, container teleport, responsive placement and
  logical (`start`/`end`) placements. Ships with `CMenuToggle`, `CMenuContent`, `CMenuItem`,
  `CMenuHeader`, `CMenuDivider`, `CMenuText`, `CSubmenu` and the `useMenu()` hook. See the
  [Menu docs](https://coreui.io/react/docs/components/menu/).
- **`CNumberInput`** — a number field with a stepper you can hit, in place of the browser's own
  spinner (tiny in some engines, absent on iOS). `min`, `max` and `step` stay native.
- **`CTransfer`** — two lists and four buttons that move values between them, with a select-all
  header and counter, an optional per-list search, a one-way mode and an announcement of every
  move through `announce()`. Composes two `CListBox`es and ships with the `useTransfer()` hook. See the
  [Transfer docs](https://coreui.io/react/docs/components/transfer/).

## Field components

`CAutocomplete`, `CMultiSelect`, `CDatePicker`, `CDateRangePicker` and `CTimePicker` are built on
one frame and one panel. These two changes apply to all five; the per-component sections below
cover what is specific to each.

- <span className="badge bg-danger">Breaking</span> **A field component *is* the frame.** It carries
  `.form-control-group` on its own root instead of nesting one, so a field is a direct child of an
  `.input-group` like any other control and there is no wrapper level to reach through. Custom CSS
  written as `.date-picker .form-control-group` no longer matches anything. On the pickers, the
  label and the validation feedback also move out of the frame.

  The frame replaces the per-component shells: `.autocomplete-input-group`,
  `.form-multi-select-input-group` and their `-sm`/`-lg` variants are gone, the adornments are
  `.form-control-cleaner` / `.form-control-action` (replacing `.autocomplete-cleaner`,
  `.form-multi-select-cleaner`, `.autocomplete-indicator`, `.form-multi-select-indicator`), and the
  `size` prop puts `.form-control-sm` / `.form-control-lg` on the frame itself.

- <span className="badge bg-danger">Breaking</span> **The options/picker panel exists only while it
  is open.** It is built when the field opens and removed once the exit transition has played, so a
  closed field carries no panel markup at all — code that queried `.combobox-popup` or
  `.date-picker-popup` up front, or held a ref to it across a close, needs to look it up per open.

## Theme colors

<span className="badge bg-danger">Breaking</span> **`light` and `dark` are replaced by `inverse`.**
They were theme colors named after a color scheme and neither followed one: `<CButton color="dark">`
stayed near-black on a near-black page. There are seven colors now — `primary`, `secondary`,
`success`, `info`, `warning`, `danger`, `inverse` — and the `Colors` type lists them, so
`color="dark"` and `color="light"` no longer type-check against the union.

- **`color="dark"` becomes `theme="inverse"`.** `inverse` resolves to gray-900 in light mode and
  gray-100 in dark, so it stands against the page in both.
- **`color="light"` becomes `theme="secondary"`.** `secondary` is the light neutral now (gray-100 /
  gray-600) rather than the mid gray-blue `#6b7785`, so components already using it change
  appearance.
- **Where the class was painting a plain panel** rather than naming a color, reach for
  `bg-4` or `bg-3`; where it was a fixed dark backdrop, set
  `data-coreui-theme="dark"` on the container.

- <span className="badge bg-danger">Breaking</span> **`color` on buttons is deprecated in favour of
  `theme`.** A v6 button is a shape plus a colour: `variant` picks the shape (`solid`, `outline`,
  `subtle`, `ghost`) and `theme` picks the colour. `color` renders the v5 spelling `btn-{color}`,
  which fuses the two and cannot inherit a colour from an ancestor; it still works and is removed in
  v7. `` becomes ``, and
  `` becomes
  ``. `color="link"` becomes the
  `link` property — a link button is a shape, not a colour, and it takes its colour from the link
  colour rather than from a theme. `color="transparent"` is gone with the
  `.btn-transparent` class it rendered — use `variant="ghost"`, which shows nothing but the
  button's content until hover and carries the base styling on its own.

- <span className="badge bg-danger">Breaking</span> **The theme colour tokens finish their rename.**
  `--cui-{color}-text-emphasis` is `--cui-{color}-fg-emphasis` and
  `--cui-{color}-border-subtle` is `--cui-{color}-border`, matching the slot names the
  components already read. Custom CSS naming the old tokens has to follow; the class names
  (`.text-primary-emphasis`, `.border-primary-subtle`) are unchanged.
- <span className="badge bg-danger">Breaking</span> **Theme colours have no bare token.**
  `--cui-primary`, `--cui-secondary`, `--cui-success`, `--cui-info`, `--cui-warning`,
  `--cui-danger` and `--cui-inverse` are gone. A theme colour is its set of slots — `-base`,
  `-bg`, `-fg`, `-fg-emphasis`, `-bg-subtle`, `-bg-muted`, `-border`, `-focus-ring`,
  `-contrast` — and the colour itself is `-base`: `--cui-primary-base` is what `--cui-primary`
  was, and `-bg` points at the same stop. A style prop or stylesheet of yours renames one to
  one, and setting the palette stop retints the colour:

  ```diff
  - <CBadge style={{ '--cui-badge-bg': 'var(--cui-primary)' }}>
  + <CBadge style={{ '--cui-badge-bg': 'var(--cui-primary-base)' }}>

  - :root { --cui-primary: #6f42c1; }
  + :root { --cui-primary-500: #6f42c1; }
  ```
- <span className="badge bg-danger">Breaking</span> **The palette is the theme colours.**
  `$colors` holds `primary`, `success`, `info`, `warning`, `danger` and `gray`; the ten
  hues (`$blue` … `$cyan`) and every `--cui-{hue}` and `--cui-{hue}-{stop}` token are
  gone, as are `$secondary` and `$inverse`. Each `$theme-colors` entry points `base` and
  `bg` at its palette's `500` stop and the other slots at that scale; `secondary` reads
  `--cui-gray-200` and `inverse` `--cui-gray-900`. No palette token is bare: the literal
  sits in `-500`, so `--cui-gray-500` is what `--cui-gray` was, and `--cui-primary-500` is
  the runtime knob for the whole family:

  ```diff
  - :root { --cui-primary-base: #6f42c1; }
  + :root { --cui-primary-500: #6f42c1; }

  - <CBadge style={{ '--cui-badge-bg': 'var(--cui-teal-600)' }}>
  + <CBadge style={{ '--cui-badge-bg': 'var(--cui-success-600)' }}>
  ```

- <span className="badge bg-danger">Breaking</span> **Under a theme the theme wins over a component color token.** A component
  reads the `--cui-theme-*` slot on the property and its own token only as the fallback. In v5 the
  color came from the component's tokens, so a token you set on a colored alert applied; in v6 a
  token set on a themed component, or for the whole page in your CSS or a `$…-tokens` map, changes
  only the components without a theme. To recolor one themed component, set the slot instead:
  `<CAlert theme="danger" style={{ '--cui-theme-bg-subtle': '#fde2e4' }}>`.
- **Variants keep the component token as the override point.** Button variants, `variant="solid"`
  on an alert, `subtle` and `outline` on a badge, `subtle` on an avatar and `outline` on a chip map
  the component tokens to their own slots and paint from the tokens, so there a component token you
  set wins over the theme. Validation states win over a theme too.

## Surface ladder

<span className="badge bg-danger">Breaking</span> **The neutral surfaces are a numbered ladder.**
`.bg-body-secondary`, `.bg-body-tertiary`, `.text-body-secondary` and `.text-body-tertiary` are
deprecated in favor of ordinal names: `.bg-1` … `.bg-4` for surfaces and `.fg-body`, `.fg-1` …
`.fg-4` for the text hierarchy, with two new rungs (`.bg-1`, `.bg-2`) between the body and the old
surfaces. The deprecated names render identically and are removed in v7. The tokens moved with
them — `--cui-bg-body`, `--cui-bg-1` … and `--cui-fg-body`, `--cui-fg-1` … are canonical, and
`--cui-secondary-bg`, `--cui-tertiary-bg`, `--cui-secondary-color`, `--cui-tertiary-color`,
`--cui-body-bg` and `--cui-body-color` remain as aliases until v7. No component prop is affected.

The deprecated surface names sit two rungs down with the rest of the library:
`.bg-body-secondary` renders `--cui-bg-3` and `.bg-body-tertiary` `--cui-bg-2`, while
`.text-body-secondary` stays `--cui-fg-2` and `.text-body-tertiary` `--cui-fg-3`.

## Sass

- <span className="badge bg-danger">Breaking</span> **A denser spacing scale.** `$spacers` has thirteen steps, `0`–`12`: `.25 / .375 / .5 / .75 / 1 / 1.25 / 1.5 / 1.75 / 2 / 2.25 / 2.5 / 3 rem` from `1` (v5: `.25 / .5 / 1 / 1.5 / 3`). Every spacing, gap, space and gutter utility changes value under the same number, and so do the numeric `gutter`, `gutterX` and `gutterY` values of `CRow`; the v5 steps map to v6 as `1`→`1`, `2`→`3`, `3`→`5`, `4`→`7`, `5`→`12`. `p-3` becomes `p-5`, `mb-4` becomes `mb-7`, `<CRow xs={{ gutter: 3 }}>` becomes a gutter of `5`. Components keep their size, cards aside (see `CCard`): their tokens read the stops that carry the v5 values.

<span className="badge bg-danger">Breaking</span> **The neutral ladder is three maps.**
`$theme-bgs`, `$theme-fgs` and `$theme-borders` in `scss/_theme.scss` replace the `$fg-*`,
`$bg-*`, `$border-subtle`, `$border-muted` and `$border-emphasized` scalars. `:root` emits every
entry as `--cui-bg-{key}`, `--cui-fg-{key}` or `--cui-border-{key}`, and the `.bg-*`, `.fg-*` and
`.border-*` utilities are generated from the same maps. `$border-color` stays a scalar — it is the
default every component reads, not a rung. Override an entry instead of a variable:

```diff
- $bg-body: #000;
- $fg-body: #111;
+ $theme-bgs: ("body": #000);
+ $theme-fgs: ("body": #111);
```

<span className="badge bg-danger">Breaking</span> **`$enable-important-utilities` and
`$enable-negative-margins` are gone.** Utilities carry no `!important` and there is no switch to
bring it back — they are emitted in the last cascade layer, so they already beat every component;
if your own CSS sits outside a layer and has to lose to utilities, move it into `@layer custom`.
Negative margins are no longer a switch over the whole scale: the `$negative-spacers` map ships two
inline steps, so `.me--1`, `.me--2`, `.ms--1` and `.ms--2` are always available.

<span className="badge bg-danger">Breaking</span> **`$grid-gutter-width` is `$grid-gutter-x`, and
`$grid-gutter-y` joins it.** The row gutter still defaults to `0`, so `<CRow>` and `<CCol>` render
as before unless you set it; `$grid-gutter-width` stays as a deprecated alias of `$grid-gutter-x`
until v7 — set the new name, the alias only reads.

<span className="badge bg-danger">Breaking</span> **`$font-sizes` sets a line height per stop
instead of one value for all headings.** `xs` is 1.25, `sm` … `lg` the body leading, `xl` 2.5⁄1.75,
`2xl` 3⁄2.25, `3xl` 1.2, `4xl` and `5xl` 1.1, `6xl` 1 — the larger the type, the tighter the
leading. The sizes are unchanged, so headings keep their size and change their leading: `<CCardTitle>`
and every `h1`–`h6` follow their stop through `--cui-line-height-*`.

<span className="badge bg-danger">Breaking</span> **Every shadow is a stack of two to four layers
with a negative spread** instead of one wide blur, so a raised surface reads as lifted rather than
smeared. Components that draw a shadow — `<CDropdown>`, `<CModal>`, `<CPopover>`, `<CToast>` — pick
this up with no change of yours. `.shadow-xs` and `--cui-box-shadow-xs` are new, and a custom
`$shadows` entry is now a comma-separated list of layers.

**New: `.w-1` … `.w-12`**, one `$spacer` per step from the `$sizes` map, next to the percentage
width utilities.

**New keys in `$zindex-levels`: `menu`, `drawer`, `dialog`**, on the same values as `dropdown`,
`offcanvas` and `modal`, so `<CMenu>`, `<CDrawer>` and `<CDialog>` read their own `--cui-z-*` token
and `.z-menu`, `.z-drawer` and `.z-dialog` join the utilities. The legacy keys stay; both spellings
of a pair resolve to one number.

## Breakpoints

<span className="badge bg-danger">Breaking</span> **The three largest breakpoints move and the
largest is renamed.** `lg` is `1024px` (was 992), `xl` `1280px` (was 1200) and the top tier is
`2xl` at `1536px` (was `xxl` at 1400); `xs`, `sm` and `md` are unchanged. `<CContainer>` grows
with them: `1200px` at `xl` (was 1140) and `1440px` at `2xl` (was 1320).

Every prop and prop value that named the top tier is renamed. `2xl` is not a valid JSX attribute
name, so the four grid-shaped props are passed with a spread:

```diff
- <CCol xxl={6} />
+ <CCol {...{ '2xl': 6 }} />

- <CRow xxl={{ cols: 3 }} />
+ <CRow {...{ '2xl': { cols: 3 } }} />

- <CContainer xxl />
+ <CContainer {...{ '2xl': true }} />

- <CPlaceholder xxl={4} />
+ <CPlaceholder {...{ '2xl': 4 }} />
```

The props that take a breakpoint as a *value* change without ceremony:

| Component | Prop | Before | After |
| --- | --- | --- | --- |
| `CCol`, `CRow`, `CContainer`, `CPlaceholder` | the tier itself | `xxl` | `'2xl'` |
| `CDialog`, `CModal` | `fullscreen` | `"xxl"` | `"2xl"` |
| `CDrawer`, `COffcanvas` | `responsive` | `"xxl"` | `"2xl"` |
| `CDropdown` | `alignment` | `{ xxl: 'end' }` | `{ '2xl': 'end' }` |
| `CHeader`, `CNavbar` | `container` | `"xxl"` | `"2xl"` |
| `CNavbar` | `expand` | `"xxl"` | `"2xl"` |
| `CListGroup` | `layout` | `"horizontal-xxl"` | `"horizontal-2xl"` |
| `CTable` | `responsive` | `"xxl"` | `"2xl"` |
| `CMenu`, `CPopover`, `CTooltip` | `placement` | `"xxl:top"` | `"2xl:top"` |

`CIcon`'s `size` is not a breakpoint and keeps every value it had — `size="xxl"` is unchanged.

## Components

### The `visible` family

- **`visible` is deprecated in favour of `defaultShow` / `show` / `onShowChange`** on `CAlert`,
  `CDialog`, `CDrawer`, `CDropdown`, `CMenu`, `CModal`, `COffcanvas`, `CPopover`, `CSidebar`,
  `CToast` and `CTooltip`. The old prop was semi-controlled: it fed the internal state, but the
  component still closed itself, so the prop and reality desynchronized on the first Escape or
  backdrop click — a confirm-before-close dialog or a pinned tooltip could not be built on it. The
  new props say which mode is which:
  - `defaultShow` — the uncontrolled starting point, read once on mount.
  - `show` — controlled. The component renders exactly what you pass; close and trigger gestures
    (Escape, a backdrop or outside click, the dismiss button, hover, the autohide timer) only call
    `onShowChange`, so it has to be fed back.
  - `onShowChange` — fired with the requested state in both modes.

  `visible` keeps its old behaviour unchanged, is ignored when `defaultShow` or `show` is set, and
  is removed in v7.
- **`show` replaces `visible` on the purely controlled components too** — `CCollapse`, `CTabPane`,
  `CTabPanel` and `CBackdrop` take `show`, and `visible` stays as a deprecated alias that loses to
  it. These never owned their state, so nothing changes in behaviour; the library speaks one word
  for the same thing.
- **`CSubmenu` takes `defaultShow` / `show` / `onShowChange`** in place of its `visible`, which was
  a seed the submenu silently overwrote on hover; a submenu can be controlled now.
- <span className="badge bg-danger">Breaking</span> **`CNavGroup` moves to `defaultShow` /
  `show` / `onShowChange`, and `show` is purely controlled** — a group given `show` renders what
  the prop says and the toggler only calls `onShowChange`, so a bare `show` without the callback
  freezes the group. An expand-by-default `visible` maps to `defaultShow`. `visible`,
  `defaultVisible` and `onVisibleChange` stay as deprecated aliases; the function form of
  `toggler` receives `{ show }` (with `visible` still present).
- **`CToaster` renders the toasts of a store as controlled** — each one takes `show` from its
  `open` flag, and the close button and the autohide timer go through `toaster.close(id)`.

### Controlled value props

<span className="badge bg-danger">Breaking</span> **The value and state props are fully controlled
when set** — the same contract the `visible` family moved to, applied to the props that hold a
component's value. They used to be semi-controlled: the prop fed the internal state, but the
component still wrote to it, so the prop and reality desynchronized on the first user interaction.
Now the component renders exactly what you pass, an interaction only calls the callback, and
nothing moves until you feed the change back — a bare value prop without its callback freezes the
control. Code that passed one as an initial value migrates to the new `default*` twin, which seeds
the uncontrolled state once, on mount:

| component | controlled prop | uncontrolled seed |
| --- | --- | --- |
| `CAutocomplete`, `CMultiSelect` | `value` | `defaultValue` |
| `CButton` (toggle mode) | `active` | `defaultActive` |
| `CChip` | `selected` | `defaultSelected` |
| `CDatePicker` | `date` | `defaultDate` |
| `CDateRangePicker` | `startDate`, `endDate` | `defaultStartDate`, `defaultEndDate` |
| `CPasswordInput` | `showPassword` | `defaultShowPassword` (with the new `onShowPasswordChange`) |
| `CRangeSlider` | `value` | `defaultValue` |
| `CSmartTable` | `sorterValue`, `columnFilterValue`, `globalFilterValue`, `activePage`, `itemsPerPage`, `selected` | `defaultSorterValue`, `defaultColumnFilterValue`, `defaultGlobalFilterValue`, `defaultActivePage`, `defaultItemsPerPage`, `defaultSelected` |
| `CTimePicker` | `time` | `defaultTime` |

- **`CAutocomplete`, `CMultiSelect`: the per-option `selected` flags are a seed**, ignored entirely
  when `value` or `defaultValue` is set, and they no longer re-apply on later renders of the same
  option. In `CAutocomplete` a flag applies once, when its option first appears. In `CMultiSelect`
  it applies only to the options the component starts with: options that arrive later never change
  the selection, and `resetSelectionOnOptionsChange` empties it and takes the marked options when
  the values change. With `value` set the reset only requests the empty selection through
  `onChange`. A native reset of the form with `value` set requests the first `value` through
  `onChange`; it used to leave the controlled selection to you.
- **Form controls answer a native form reset the same way, controlled ones included.**
  `CAutocomplete`, `CChipInput`, `CCombobox`, `CDateInput`, `CDatePicker`, `CDateRangeInput`,
  `CDateRangePicker`, `CFormRange`, `COneTimePassword`, `CRangeSlider`, `CRating`, `CTimeInput`
  and `CTimePicker` with a controlled value used to keep it on a reset; they now ask for the value
  they started with through their change callback and show your value until you take it, so a
  page that resets its own state from the form's `reset` gets that request after its handler.
  `CRangeSlider` and `CFormRange` now call `onChange` when an uncontrolled reset moves the value,
  and `CStepper` keeps the validation marks of a form reset a listener cancels.
- **`CSmartTable`: a controlled prop change no longer echoes its own callback.** Passing a new
  `sorterValue` used to fire `onSorterChange` back at you (and likewise for the other five); the
  callbacks now fire only when the user acts. The page clamp is a request in controlled mode — when
  the items shrink below the current page, the table calls `onActivePageChange(1)` instead of
  moving itself.
- **`CLoadingButton`: `loading` is the controlled mode and wins over `timeout`.** While `loading`
  is set the button never stops the spinner itself and `timeout` is ignored; `timeout` without
  `loading` is the uncontrolled auto-stop it always was.
- **`CButton`: a toggle button's `active` is controlled when set** — a click only fires `onClick`
  and the pressed state follows your prop; seed an uncontrolled toggle with `defaultActive`.
  Without either prop nothing changes.
- **`CCalendar`: equal re-renders no longer reset the view.** A parent re-rendering with an equal
  `calendarDate`, `startDate` or `endDate` (a fresh `Date` object holding the same value) used to
  yank back the navigated month and cancel an in-progress range selection; only a real value
  change syncs now. Month names also follow `locale` and `monthFormat` changes at runtime.

### Validation state

<span className="badge bg-warning">Deprecated</span> **One `validationState` prop replaces the
`valid` and `invalid` booleans** on every form control, field and picker, and on `CFormFeedback`.
`validationState="invalid"` renders `is-invalid` and sets `aria-invalid` on the control,
`"valid"` renders `is-valid`, and any custom state from the `$form-validation-states` Sass map
renders `is-{state}` — two independent flags could neither express a third state nor refuse
`valid` and `invalid` at once. On the components that had them in v5 the booleans keep working as
deprecated aliases (with both set, `invalid` wins, where they used to stack), and are ignored
whenever `validationState` is set; `CCheckbox`, `CChipInput`, `CCombobox`, `CDateInput`,
`CDateRangeInput`, `CNumberInput`, `CRadio`, `CSwitch` and `CTimeInput` take only
`validationState`. Four side effects worth knowing:

- `feedback` follows the state: it renders only while `validationState` is set, styled after it,
  and describes the control in every state, not only the invalid one.
- Every control that reads `validationState="invalid"` now sets `aria-invalid="true"`, except
  `CCombobox`, whose toggle is a button and takes only the message in `aria-describedby`; a control
  without a validation state still sets none.
- <span className="badge bg-danger">Breaking</span> `validationState="invalid"` (or the deprecated
  `invalid`) blocks the submit on `CFormInput`, `CFormSelect`, `CFormTextarea`, `CNumberInput`,
  `CPasswordInput`, `CCheckbox`, `CRadio`, `CSwitch`, `CFormCheck` and `CFormSwitch`, with the text
  of the invalid feedback as the browser's message, the way it already did on the fields that
  build their own markup. Each control calls `onValidationStateChange(undefined)` when the user
  changes its value or the form is reset; pass your state setter there (with the deprecated
  `invalid`, set it to `false` there instead), or a form you gave an invalid state stays blocked
  until you clear it yourself. See
  [Validation](https://coreui.io/react/docs/forms/validation/#custom-validation).
- <span className="badge bg-danger">Breaking</span> `CMultiSelect`, `CChipInput`, `CCombobox`,
  `COneTimePassword`, the date and time fields, the pickers and `CAutocomplete` take and show their
  validation state the same way, described under
  [Form components](https://coreui.io/react/docs/forms/validation/#form-components). A field a validation reports shows the
  state on the component's frame, and `<CForm>` and `CStepper` leave the field that carries the
  value to the component. After taking the result over with `preventDefault()` in `onValidate`,
  keep `event.isValid` rather than calling `checkValidity()`: the call reports every invalid field
  again, and the components then show their error.

### Help text and feedback ids

**`text` and the feedback get ids of their own.** A form control used to hand its
`aria-describedby` to the `text` and to the feedback as their id, so the help text and the error
shared one id: the control read the help text and never the error, a valid control with
`feedbackInvalid` was described by the hidden error, and an `aria-describedby` that pointed at an
element of the page gave that id to a second element. Now `text`, `feedback`, `feedbackInvalid` and
`feedbackValid` get generated ids, and the control lists them in its own `aria-describedby` after
the one you pass: the text always, `feedback` while `validationState` is set, `feedbackInvalid`
only while the control is invalid and `feedbackValid` only while it is valid, whether that state
comes from `validationState` or, in a `data-coreui-validate` form, from the browser's `:user-invalid`
and `:user-valid` (the date and time fields and the pickers use the field's own verdict instead). A
`validationState` you set takes precedence. Pass `aria-describedby` only
to point at elements of your own; a value you passed so that the `text` would carry it is no longer
needed, and CSS or scripts that found the text by that id need another hook. The pickers drop the
undocumented `describedby` prop for the same reason, and `CFormControlWrapper` and
`CFormControlValidation` replace it with `descriptionId`, the prefix the ids are built from
(`<descriptionId>-label`, `-text`, `-feedback`, `-feedback-invalid` and `-feedback-valid`).

### CAccordion

- <span className="badge bg-danger">Breaking</span> **The accordion is built on the native
  `<details>` and `<summary>` elements.** `CAccordionItem` renders a `<details class="accordion-item">`
  and `CAccordionHeader` its `<summary>`, so the browser owns opening, closing, keyboard
  interaction and the disclosure semantics — none of it depends on the component being mounted.
- <span className="badge bg-danger">Breaking</span> **`CAccordionButton` is removed**, together
  with `.accordion-button`, `.accordion-collapse` and `.collapsed`. The trigger is the `<summary>`
  itself and the panel is the element's own content, revealed through `::details-content`.
  `CAccordionHeader` already rendered the button for you, so markup written against the components
  needs no change; custom CSS keyed on those classes does. Style the open state with
  `.accordion-item[open] > .accordion-header`.
- <span className="badge bg-danger">Breaking</span> **`CAccordionHeader` renders a `<summary>`,
  not an `<h2>`.** A `<summary>` is not a heading — where the accordion divides a page into
  sections, wrap the label in an `<h2>`–`<h6>` inside the header. The heading inherits the
  summary's typography, so nothing looks different.
- **Grouping is the `name` attribute.** Without `alwaysOpen` the items of one accordion share a
  generated `name`, and the browser keeps a single one open.
- <span className="badge bg-danger">Breaking</span> **The open items follow the controlled /
  uncontrolled split the rest of the library uses.** `activeItemKey` fed a `useState` initial
  value, so it read like a controlled prop and behaved like a default: changing it after mount did
  nothing, and nothing reported back when the user opened an item. It is deprecated in favour of
  three props that say which is which:
  - `defaultOpenItemKeys` — the uncontrolled initial value, where `activeItemKey` maps.
  - `openItemKeys` — controlled. The accordion renders exactly what you pass, so it has to be fed
    back from `onChange`.
  - `onChange` — fired with the new list in both modes.

  They are named for what an accordion holds. `active` is the vocabulary of a selection — one
  item out of a set, which is what `CTabs` has — while a `<details>` element is open or closed,
  and that is what `CAccordionItem` calls its own prop.

  All three take a single key as well as an array; an array only opens more than one item under
  `alwaysOpen`, since the HTML Standard forbids leaving more than one open item in a `name` group.
- **New: `onShown` / `onHidden` on `CAccordionItem`**, fired from the element's own `toggle` event
  — so they report the state whichever path opened the item, including the browser closing a
  sibling of the same `name`.
- **The panel animation is CSS-driven** through `interpolate-size: allow-keywords`, which only
  Chromium implements today. Elsewhere the component measures the two sizes and the stylesheet
  times the move, exactly as `CCollapse` does; that part goes away, without a breaking change, once
  Firefox and Safari ship `calc-size()`.
- **The indicator can be an element.** Put a node carrying `accordion-icon` in the header and it
  replaces the chevron the header draws for itself; a second one marked `accordion-icon-active` is
  swapped in while the item is open.
- **New: `theme` on `CAccordion`** tints the open item. Closed items are untouched, so it marks
  what is expanded rather than recoloring the control.
- <span className="badge bg-danger">Breaking</span> **`CAccordionItem` takes `open`, and `visible`
  is deprecated.** The item renders a `<details>` element and that is what the attribute is called
  there, so the prop no longer carries a second word for the same state. It is also the controlled
  form for a single item, for the cases `openItemKeys` does not cover; `visible` keeps working.
- **A collapsed item stays findable.** `<details>` keeps its content searchable and the browser
  expands the item on a find-in-page match, so the accordion needs no counterpart to `CCollapse`'s
  `hiddenUntilFound`.

### CAlert

- **`dismissible` is no longer required to position a close button.** An alert makes room for a
  `` rendered as its direct child and positions it on its own. The prop keeps
  working and stays the way to get the built-in button. The button has to be a *direct* child to be
  pushed to the trailing edge — one nested in a wrapper is laid out by that wrapper.
- <span className="badge bg-danger">Breaking</span> **`color` applies `.theme-{color}`, not
  `.alert-{color}`.** The two emit a byte-identical block of `--cui-theme-*` declarations — the
  `.alert-*` names survive in v6 only as back-compat aliases, and every other themeable component
  reads the same class. Markup that still carries `.alert-success` renders exactly as it did; the
  component just stops generating the older spelling.
- <span className="badge bg-danger">Breaking</span> **`variant="solid"` renders `.alert-solid`
  alongside the theme class, not `bg-{color} text-white`.** The old pair hard-coded white text,
  which fails WCAG AA on five of the eight theme colors — 1.10 on `light`, where white on
  near-white is invisible. `.alert-solid` reads the theme's own contrast token, so every color
  lands above the threshold, including one you define yourself. The value also narrows from
  `'solid' | string` to `'solid'`; v6 has no other alert variant.
- <span className="badge bg-danger">Breaking</span> **The alert lays its children out in a row.**
  It is `display: flex` with `--cui-alert-gap` between children, so an icon beside a label needs no
  `d-flex` and no spacing utility. Text mixed with inline elements is unaffected, but an alert
  built from **several block elements** — a heading, paragraphs, an `<hr>` — now sits side by side.
  Wrap that content in a `.vstack`.
- **An alert rendered with `transition` fades in when it is added to the page**, not only out when
  it is dismissed. Nothing to change; `@starting-style` supplies the state the animation was
  missing in that direction.
- **An `<hr>` inside an alert takes the alert's border color** rather than the page's neutral
  grey: the theme's border color, or `--cui-alert-border-color` without a theme, so a separator
  follows a border you recolored. With `variant="solid"` the border has the surface color, so the
  separator takes the theme's border color instead.
- <span className="badge bg-danger">Breaking</span> **The alert is rounded with `--cui-radius-7` (`.75rem`)**, like the dropdown,
  popover, toast, dialog, drawer and accordion; v5 used `.375rem`. Set
  `--cui-alert-border-radius: .375rem` to keep the v5 corners.
- <span className="badge bg-danger">Breaking</span> **`--cui-alert-border` is gone.** The border is written in the rule from
  `--cui-alert-border-width` and the border color, so that a theme can color it. Set those two
  instead, and `border-style` on the alert for a dashed or dotted line.
- <span className="badge bg-danger">Breaking</span> **`.alert-dismissible` is no longer rendered.**
  The class carries no styling in v6 — the close button is a flex item pushed to the end with
  `margin-inline-start: auto`, so the alert reserves no extra padding and needs no marker. The
  `dismissible` prop is unchanged and still renders the button and the self-dismiss behaviour;
  only the dead class is gone. CSS of your own that targeted it should target `.alert` or the
  button instead.

### CAutocomplete, CMultiSelect

- **`loading` shows a row below the options instead of covering them.** The white cover with a
  spinner that sat over the options while `loading` was set is gone: the list is marked
  `aria-busy="true"`, a row with a spinner and the new `loadingLabel` text (`'Loading…'`) shows
  below the options and is announced to screen readers when the shown list starts loading or the list shows up loading, and the options it
  shows can still be picked with the pointer and the keyboard.
- <span className="badge bg-danger">Breaking</span> **The list inside the panel is a
  [`CListBox`](https://coreui.io/react/docs/components/list-box/)**, so it carries the `list-box-*` classes: `list-box`,
  `list-box-options`, `list-box-option` (+`-label`, `-description`, `-indicator`),
  `list-box-section` (+`-label`), `list-box-empty`, `list-box-header`, `list-box-select-all`, with
  the `active`, `selected` and `indeterminate` state classes. The panel itself keeps
  `combobox-popup`. The `combobox-option*`, `combobox-optgroup*`, `combobox-options*`,
  `combobox-header` and `combobox-all` classes are gone, and so are their
  `--cui-combobox-option-*`, `-optgroup-*` and `-all-*` properties — theme the list through
  `--cui-list-box-*`. The former `autocomplete-dropdown` / `autocomplete-option(s)` and
  `form-multi-select-dropdown*` / `-options*` / `-option*` / `-all*` / `form-multi-selected`
  classes are gone as well. This markup is generated by the components, so only custom CSS
  targeting the panel internals needs updating.
- <span className="badge bg-danger">Breaking</span> **`selectAllStyle` and `optionsGroupsStyle` are
  removed from `CMultiSelect`.** One marker serves the whole list: `indicator` decides what
  the options, the group labels and the select-all button all draw.
- <span className="badge bg-danger">Breaking</span> **`role="listbox"` is on `.list-box-options`,
  not on the panel**, and a group is wrapped in `.list-box-section` rather than being a label
  followed by loose siblings. In `CMultiSelect` the panel also holds the search field and the select
  all button, which the old placement claimed as options.
- **`onShow` and `onHide` fire once per transition**, and neither fires on mount. `CMultiSelect`
  used to fire `onHide` every time the menu opened.
- <span className="badge bg-danger">Breaking</span> **`CAutocomplete` keeps the selection and the
  typed text apart.** `value` and `defaultValue` take an option value, compared as text, so `1` and
  `'1'` select the same option; a string used to be matched against the labels. `value={null}`
  selects nothing. `onChange` reports an option or `null`, never typed text: text the user types
  without picking an option stays in the field and is posted under `name`. The text is a state of
  its own: `defaultInputValue` starts it, `inputValue` controls it, and the new `onInputChange`
  reports every change, picks included, while `onInput` reports the search the options are filtered
  by. <kbd>Enter</kbd> on text that names no option no longer reports it through `onChange`. From
  `onChange` store `option.value`, `option.label` for an option without a value, or the string of a
  plain string option.
- **`CAutocomplete` shows the cleaner for typed text** when `allowOnlyDefinedOptions` is off, keeps
  it out of the Tab order, and leaves the focus in the field after a click.
- <span className="badge bg-danger">Breaking</span> **`CAutocomplete` keeps the focus in its
  input.** The arrows no longer walk the focus into the panel: the input keeps it and reports the
  highlighted option through `aria-activedescendant`, which the option marks with the `active`
  class. The input also keeps the keys that belong to its text — <kbd>Home</kbd>, <kbd>End</kbd>
  and <kbd>Space</kbd> move the caret rather than the highlight.
- <span className="badge bg-danger">Breaking</span> **`CMultiSelect` opens a dialog instead.** The
  panel is `role="dialog"` named by the control, and the focus moves into it: onto the search box
  when `search` is on, otherwise onto the option the list points at. Options take a roving
  `tabindex` and the arrows move the focus itself, so there is no `aria-activedescendant` to read
  any more. <kbd>Tab</kbd> cycles through the panel and the search box without leaving them, and
  <kbd>Escape</kbd> closes the panel and hands the focus back to the control. The select-all button
  is a control over the list rather than a value in it, so it is reached with <kbd>Tab</kbd>;
  selectable group labels are in the arrow order in its place.
- <span className="badge bg-danger">Breaking</span> **`CMultiSelect`'s `search` no longer takes
  `'global'`.** Typing reaches the search box from anywhere in the component by default, which is
  what that value used to turn on, so `search="global"` becomes `search` and
  `search={{ external: true, global: true }}` becomes `search="external"`. Set
  `globalSearch={false}` to keep typing local: the search box then filters only while it has the
  focus, and a keystroke over the options list jumps to the first option starting with it (with
  `search="external"` it does nothing, since the list holds only the last results).
- <span className="badge bg-danger">Breaking</span> **`CMultiSelect`'s `search` no longer takes an
  object.** It takes `true`, `false` or `'external'`, so `search={{ external: true }}` becomes
  `search="external"`.
- <span className="badge bg-danger">Breaking</span> **`CAutocomplete`'s `search` takes only
  `'external'`, and global typing is `globalSearch`, on by default.** `search="global"` goes away:
  typing on the picker button or in the options panel reaches the field without it. The object form
  goes too, so `search={{ external: true, global: true }}` becomes `search="external"`. Set
  `globalSearch={false}` to keep a keystroke where the focus is. The space bar and shortcuts with
  Ctrl or Cmd stay with the button that has the focus, and a character typed with AltGr or Option
  reaches the field like any other, in `CMultiSelect` too.
- **`CAutocomplete` and `CMultiSelect` read the typed text without the spaces around it**, in the
  filter, the highlight, the hint, <kbd>Enter</kbd> and leaving the field, and `CMultiSelect` creates
  an option from the trimmed text. A query of spaces lists every option. In `CMultiSelect` the search
  matches option labels only: a group whose own label matches no longer keeps all of its options,
  and a group without options is not listed.
- **`CMultiSelect` blocks the submit while `validationState` is `'invalid'`**, and asks to drop the
  state with `onValidationStateChange(undefined)` when the user changes the selection or the form
  is reset. A native form reset now restores the selection given by `defaultValue` or the options'
  `selected`. See [Validation](https://coreui.io/react/docs/forms/multi-select/#validation).
- **`CAutocomplete` blocks the submit while `validationState` (or the deprecated `invalid`) is
  `'invalid'`**, and asks to drop the state with `onValidationStateChange(undefined)` when the user
  changes the text or the form is reset. The frame also shows the state a validation reports for
  the input, where it used to show only the state you gave. See
  [Validation](https://coreui.io/react/docs/forms/autocomplete/#validation).
- **Keyboard, `CMultiSelect`:** arrow keys skip disabled options.
- **Keyboard, `CAutocomplete`:** `ArrowDown` out of the input highlights the first *enabled*
  option, and `Escape` hands the focus back to the input. A closed field renders
  `aria-expanded="false"` instead of omitting the attribute.
- <span className="badge bg-danger">Breaking</span> **`CMultiSelect`'s search field no longer
  reaches the server.** The input that filters the options carried a `name` built from the field's
  own, and a plain text input is serialized, so every form holding a `CMultiSelect` submitted
  whatever the user had typed to narrow the list. It carries no `name` now and the `search…` key is
  gone from the payload.
- <span className="badge bg-danger">Breaking</span> **An unnamed `CMultiSelect` stays out of the
  form.** The native select took `name={name ?? uniqueId}`, so a component the page had not named
  still submitted its value under React's generated id, a key like `:r1:` that nobody chose. The
  attribute is written only when `name` is given. `CMultiSelectNativeSelect` carried a second
  fallback of `${id}-multi-select` for the same purpose; it was unreachable through `CMultiSelect`
  and is gone too.
- <span className="badge bg-danger">Breaking</span> **An unnamed `CAutocomplete` stays out of the
  form too.** Its input took a generated `name` when the prop was not set, so a form carried a key
  nobody chose. It does not now. The generated `id` is unaffected.
- <span className="badge bg-danger">Breaking</span> **`CAutocomplete`'s typeahead hint no longer
  takes the field's `id`.** With `showHints`, the decorative hint input rendered the same `id` as
  the real one, so `document.getElementById`, a `label[for]` and the floating label association all
  resolved to the hint. It carries no `id` now.

- <span className="badge bg-warning">Deprecated</span> **CMultiSelect: `selectionType="tags"` is
  `"chips"`, and `ariaTagDeleteLabel` is `ariaChipRemoveLabel`.** The selection renders
  [chips](https://coreui.io/react/docs/components/chip/), so the mode carries their name and `"chips"` is the default. `"tags"`
  renders the same until v7. The old label prop still names the remove button and takes precedence
  while it is set.
- <span className="badge bg-danger">Breaking</span> **CMultiSelect's classes drop the `form-`
  prefix**, so they carry the component's name like `.autocomplete`, `.chip-input` and
  `.date-picker`. There is no alias; update CSS that targets them.

  | v5 | v6 |
  | --- | --- |
  | `.form-multi-select` | `.multi-select` |
  | `.form-multi-select-selection`, `.form-multi-select-selection-tags` | `.multi-select-selection`, `.multi-select-selection-chips` |
  | `.form-multi-select-search`, `.form-multi-select-placeholder` | `.multi-select-search`, `.multi-select-placeholder` |
  | `--cui-form-multi-select-selection-tags-gap`, `-padding-y` | `--cui-multi-select-selection-chips-gap`, `-padding-y` |

  The filled state is `.multi-select-filled`, and the component stylesheet is
  `@coreui/coreui-pro/css/components/forms/multi-select.css`.
- **CMultiSelect: <kbd>Backspace</kbd> in the empty search box focuses the last chip.** It removed
  the last value at once; the first press now moves the focus to the last chip and the second removes
  it, as in `CChipInput`. The arrow keys move between the search box and the chips, the selection is
  one tab stop, removing a focused chip moves the focus to its neighbour, and a value removed through
  a chip, <kbd>Backspace</kbd> or the cleaner is announced (`ariaRemovedAnnouncement`). A single
  select and the `counter` and `text` selections still remove the last value at once.

### CAvatar

- <span className="badge bg-danger">Breaking</span> **`textColor` is removed.** It wrote a single
  `fg-{value}` class, and the values it listed from v5 (`body-secondary`, `body-tertiary`,
  `*-emphasis`, `black-50`, `white-50`) had no rule in v6, so the text kept its inherited color. Put
  the color utility on the component instead: `className="fg-2"` for `body-secondary`, `fg-3` for
  `body-tertiary`, `fg-emphasis-primary` for `primary-emphasis`, `text-body-emphasis` for
  `body-emphasis`, `fg-white fg-50` and `fg-black fg-50` for `white-50` and `black-50`, and
  `fg-{color}` for a theme color.
- <span className="badge bg-danger">Breaking</span> **`color` applies a theme class, not a
  background utility.** It rendered `bg-{color}`, which left the letters on the inherited color and
  is why the docs paired it with a hand-picked `textColor` — a recipe that failed WCAG AA on five
  of the six examples they shipped. `.theme-*` supplies the background *and* the color that reads
  on it, so drop the `textColor` that went with it. A bare `<CAvatar>` is a neutral circle instead
  of a transparent one.
- **New: `variant="subtle"`** — a tinted avatar that keeps the theme color in the letters rather
  than behind them.
- **New: `ariaStatusLabel`** names the status indicator, rendering `role="img"` with the label. The
  indicator is color and nothing else, so a state that carries meaning needs a name; `aria-label`
  on a bare `<span>` has no role to attach to and may be ignored. Without the property the
  indicator stays decorative, as before.
- **`size` is typed** on `CAvatar` and `CAvatarStack`: `xs`, `sm`, `md`, `lg`, `xl` — the five
  sizes the stylesheet ships — instead of any string.
- The Sass variables behind the size moved: `--cui-avatar-width` / `-height` become
  `--cui-avatar-size`, and `--cui-avatar-status-width` / `-height` become
  `--cui-avatar-status-size`. Only inline overrides of those names need updating.

### CBadge

- <span className="badge bg-danger">Breaking</span> **`textColor` is removed.** It wrote a single
  `fg-{value}` class, and the values it listed from v5 (`body-secondary`, `body-tertiary`,
  `*-emphasis`, `black-50`, `white-50`) had no rule in v6, so the text kept its inherited color. Put
  the color utility on the component instead: `className="fg-2"` for `body-secondary`, `fg-3` for
  `body-tertiary`, `fg-emphasis-primary` for `primary-emphasis`, `text-body-emphasis` for
  `body-emphasis`, `fg-white fg-50` and `fg-black fg-50` for `white-50` and `black-50`, and
  `fg-{color}` for a theme color.
- <span className="badge bg-danger">Breaking</span> **`color` applies a theme class, not a
  background utility.** It rendered `bg-{color}`, which in v6 leaves the badge on the inherited
  text color instead of white — the `.badge-*` namespace carries the variants now, and the color
  comes from `.theme-*`. The prop is unchanged at the call site; only the class it emits moved.
  A bare `<CBadge>` is neutral rather than invisible, so a badge that relied on a utility for its
  background can drop it.
- <span className="badge bg-danger">Breaking</span> **`color` no longer takes a gradient.** v6
  removes the `bg-*-gradient` utilities the value mapped to.
- <span className="badge bg-danger">Breaking</span> **`textBgColor` is gone.** It emitted
  `.text-bg-{color}`, which resolves to the same two values `color` now supplies through the theme
  class — `--cui-{color}-contrast` on the text and the colour itself behind it — so the two props
  had become one. Keeping it was also destructive: `.text-bg-*` ships in the `helpers` layer and
  writes `color` and `background-color` directly, over the tokens `.badge` reads, so pairing it
  with `variant="outline"` filled in the background the variant had cleared. Use `color`.
- **New: `variant`** — `"subtle"` or `"outline"`, alongside the default solid.
- **`size` also takes `"lg"`.**

### CBreadcrumb

- <span className="badge bg-danger">Breaking</span> **A step is a padded, hoverable target.**
  `CBreadcrumbItem` renders `.breadcrumb-link` on the link its `href` produces, and wraps an active
  step in a `<span className="breadcrumb-link">` so every step keeps the same height. To keep the
  plain text link of v5, render your own link as a child instead of passing `href`.
- **New: `CBreadcrumbDivider`** — a separator you put in the markup, carrying whatever you give it:
  an inline SVG, an icon, a character. An empty one renders the default divider. The automatic
  `::before` divider steps aside wherever an element is placed, so the two never double up, and the
  component marks itself `aria-hidden`.
- **A step can open a menu.** Wrap the step's content in a `CMenu` whose `CMenuToggle` takes the
  new `custom` prop, and the toggle behaviour is wired onto your own `.breadcrumb-link` button
  instead of a `CButton`. See Collapsed items.

### CCalendar

- **Changed.** **The range band sits under the day cells and follows the theme.**
  `--cui-calendar-cell-range-bg` is `--cui-primary-bg-subtle` (the theme's `bg-subtle` under a
  `.theme-*` class) instead of a 12.5% tint laid over the cells, so the selected start and end keep
  their solid fill and the day numbers are no longer tinted. A hovered day in the range takes
  `--cui-primary-bg-muted` (the theme's `bg-muted`), one step stronger than the band.
- **Changed.** **`minDate` and `maxDate` are compared by the day.** v5 compared a day cell at
  midnight with the instant, so a limit that carried a time of day, such as `minDate={new Date()}`,
  disabled its own day. Now that day can be picked; the date picker's time selects and the field
  bound the time on it.
- **Changed.** **A week row stands for the ISO week that holds most of its days.** With
  `firstDayOfWeek` other than `1` a row spans two ISO weeks, and v5 numbered it by its last day but
  picked the week of its first day: a Sunday-first row from August 30 to September 5, 2026 showed
  36 and picked `2026W35`, and that value given back as `startDate` did not select the same row.
  The number, the picked value, the weeks of `startDate`, `endDate`, `minDate` and `maxDate`, and
  the range and its preview now all follow the row's majority week, so that row is `2026W36` and
  the picked date is its Monday, August 31, the date `2026W36` stands for. With Tuesday to
  Thursday first, the numbers move instead, so the row from September 1 to 7 shows 36 where it
  showed 37.
- **Changed.** **In week selection `disabledDates` disables a week only when it disables every day of
  it**, as it does for a month, quarter or year. Before, a week was disabled when its row's first
  day was: with Monday first a single disabled Monday disabled its week, and with Sunday first a
  function that disables weekends disabled every row. A range of weeks across a week that holds a
  disabled day is kept, as a range of months is.
- **Changed.** **In week selection `minDate` and `maxDate` count as whole weeks**, so their weeks can
  be picked on any first day of the week, as the month, quarter or year of a limit can in those
  selections, and the months view opens a month only when its page holds a week inside them.
  Before, a `minDate` later than the first day of its row disabled that row, and with Tuesday to
  Thursday first a `maxDate` of `2026W36` disabled the row of week 36.
- <span className="badge bg-danger">Breaking</span> **`onStartDateChange` and `onEndDateChange`
  receive the `Date` first**, the same shape the pickers use, and the value formatted per
  `selectionType` second — a `Date` for day selection but a string for week (`'2026W27'`), month
  (`'2026-01'`), quarter (`'2026Q1'`) and year (`'2026'`). v5 passed them the other way round.
- <span className="badge bg-danger">Breaking</span> **`CCalendar` no longer reports a date that
  stays empty.** Picking a day before the start of a range, or an end across a disabled day, used to
  call `onEndDateChange(null)` although no end had been picked, and clearing a range across a
  disabled day before a start was picked called `onStartDateChange(null)`. A day picked again is
  still reported, as in the Vue library.
- <span className="badge bg-danger">Breaking</span> **`selectAdjacementDays` and
  `showAdjacementDays` are spelled `selectAdjacentDays` and `showAdjacentDays`** on `CCalendar`
  and every picker built on it. The typo is fixed in the vanilla and Vue libraries in the same
  release.
- <span className="badge bg-danger">Breaking</span> **`onDateHover` is gone** from `CCalendar`,
  `CDatePicker` and `CDateRangePicker`, as `cellHover` is from the vanilla calendar. It served the
  hover preview in the field, which went with `previewDateOnHover`. To follow the pointer or the
  focus over a `CCalendar`, listen for `mouseover` or `focusin` on an element that wraps it: each
  day, month, quarter and year cell the calendar shows carries its date in `data-coreui-date`, in
  the `toDateString()` form, and a week row carries it on the day cells it shows. Unlike the event,
  the listeners also see cells that cannot be picked, and only `mouseout` or `focusout` tells that
  the pointer or the focus left. The pickers have no replacement for it.
- <span className="badge bg-danger">Breaking</span> **Each grid is one tab stop, and `tabindex` no
  longer marks a selectable cell.** The arrows, `Home`, `End` and the page keys move within a
  calendar and Tab leaves it, which is what `role="grid"` promises. Every cell inside `minDate` /
  `maxDate` can take the focus, also one that cannot be picked: the keys stop on a date in
  `disabledDates` instead of skipping it, a screen reader announces it as unavailable, and Enter
  and Space do nothing there, so a month where nothing can be picked is explored like any other.
  A selectable day, month, quarter, year or week row carries `data-coreui-selectable`, and
  `tabindex="0"` sits on one cell per grid, so a two-month range picker keeps a stop in each
  month that reaches into the limits. Custom CSS or scripts that reached for `.calendar-cell[tabindex="0"]` need
  `.calendar-cell[data-coreui-selectable]`.
- <span className="badge bg-danger">Breaking</span> **`navigation` and `navYearFirst` are gone**
  from `CCalendar`, `CDatePicker` and `CDateRangePicker`. With `navigation={false}` the
  month and year buttons stayed on screen but did nothing; the arrows are always there now.
  To hide them, hide `.calendar-nav-prev` and `.calendar-nav-next` in your CSS. The month and
  year buttons still open the months and years views; to keep the calendar on the month you set,
  hide `.calendar-nav` and give the month a heading of your own.
  The order of the year and the month follows the locale instead of `navYearFirst`: the year
  comes first in locales that write it first (for example Japanese, Chinese, Korean, Hungarian
  or Lithuanian).
- <span className="badge bg-danger">Breaking</span> **The calendar's classes and data attributes are the
  vanilla calendar's.** A panel is `.calendar` without the view class (`days`, `months`, `quarters`,
  `years`) and carries its position in `data-coreui-calendar-index`; the view is named on the cell
  content instead (`.calendar-cell-inner.day`, `.month`, `.quarter`, `.year`), and quarter and year
  cells no longer carry `quarters` / `year`. The navigation buttons carry `btn-double-prev`,
  `btn-prev`, `btn-month`, `btn-year`, `btn-next` and `btn-double-next`, and
  `data-coreui-selectable` is empty instead of `"true"`. A rule that styled a
  panel by its view needs `.calendar:has(.calendar-cell-inner.month)` instead of
  `.calendar.months`, and one that styled `.calendar-cell.quarters` or `.calendar-cell.year`
  needs `.calendar-cell:has(> .calendar-cell-inner.quarter)` or `.year`, since the cell states
  (`selected`, `range`, `disabled`) stay on the cell.
- <span className="badge bg-danger">Breaking</span> **Setting `calendarDate` to `null` keeps the
  month on screen.** v5 moved the calendar back to today; to show today, pass today's date. An empty
  `calendarDate` (`null` or `''`) at mount no longer opens the calendar on today: it opens on
  `startDate` or `endDate`, as when `calendarDate` is left out; v5 opened on today for `null`.
- **`onViewChanged` is deprecated in favour of `onViewChange`**, the tense every other callback
  uses; both fire while the old name is set.
- **The grid answers the full APG keyboard model.** On top of the arrows: `Home` / `End` move
  within the focused week, `Page Down` / `Page Up` move a month at a time (a year in the months and
  quarters views, the twelve years of a page in years), and `Shift` with either moves a year in the
  days view. A jump never lands outside `minDate` / `maxDate`: focus stops on the limit, also when
  that date cannot be picked. Every cell carries its own `data-coreui-date`.
- **`selectionType="week"` is reachable from the keyboard.** The day cells take no focus in that
  mode because the row is what gets picked, and the row itself was not a tab stop — so the grid had
  none at all and week selection was mouse-only. The rows are tab stops now, and `Home` / `End`
  move to the first and last week of the month.
- **Picking a date keeps the months on screen.** A picked day of an adjacent month
  (`selectAdjacentDays`) used to turn the calendar to that month, and so did any pick the parent
  passed back through `startDate`: a start date picked in the second month of `CDateRangePicker`
  shifted both months by one. A picked day of an adjacent month now shows as selected with an
  outline on a subtle background instead of the solid fill. The view still follows `calendarDate`,
  and without it a start date the parent changes to a new instant, the day already selected
  included (a Today button after browsing); it stays for the calendar's own picks passed back
  before the view moves and for the same instant passed again. An end date moves it while no start
  date is set, unless the calendar already holds that end.
- A selected day or week keeps its colour under the pointer, a shade darker
  (`--cui-calendar-cell-selected-hover-bg`, `--cui-calendar-cell-selected-hover-color`); it used to
  take the hover colour until the pointer left.
- The range preview no longer stays on the date just picked, and in week selection it no longer
  drops while the pointer crosses a day of the adjacent month inside the row it previews.
- In the years view the navigation names the page it shows, `2020 – 2031`, instead of the one year the
  page is built around, so turning a page is announced as the new range. The grid label and the
  navigation write the range in the locale's own digits.
- In the years view the double arrows and `Page Down` / `Page Up` turn the calendar by the twelve
  years a page shows instead of ten, so a turn no longer repeats two years of the previous page.
- In the years view the year button goes back to the view that picks the date instead of doing
  nothing, and it is disabled in year selection, where the years view is the one that picks.
- Without `weekNumbersLabel` the week-number column is named for screen readers with the locale's
  word for week, in a visually hidden span; before, its header was empty. Each week number is named
  the same way with its number (`Week 36`), and the new `ariaWeekNumberLabel` prop takes a function
  of the week number to name it yourself.
- <span className="badge bg-danger">Breaking</span> **The years view names its double arrows with new
  `ariaNavNextYearsLabel` / `ariaNavPrevYearsLabel` props** (`'Next 12 years'` / `'Previous 12 years'`)
  instead of the year labels, which stay on the days, months and quarters views. A page that
  translates `ariaNavNextYearLabel` / `ariaNavPrevYearLabel` needs to translate these two as well.
- **A grid in which nothing can be picked is described by the new `ariaNothingToPickLabel` prop**
  (`'Nothing on this page can be picked'`), also announced when the calendar turns to such a page,
  so a page that translates the other calendar labels translates this one too.
- **A pick is announced through the new `ariaSelectedDateAnnouncement` and
  `ariaSelectedRangeAnnouncement` props** (`'Selected date: {date}'`, `'Selected range: {start} to
  {end}'`); before, picking a date with <kbd>Enter</kbd> left a screen reader silent. A page that
  translates the other calendar labels translates these two as well.
- The toggle of `CDatePicker` and of `CDateRangePicker` is named with the value the picker holds
  (`Toggle calendar, Friday, August 14, 2026`), so a screen reader reads the picked date when the
  popup closes and focus goes back to it.
- The month and year between the navigation arrows are no longer a live region. A page turn is
  announced through `announce()`, inside the panel of a date picker when the calendar sits in one,
  and a key that leaves a day or a week row names no page, since the date that takes focus names
  it: a key that moved from August 31 to September 1 used to be read twice, as the date and then
  as `September 2026`.
- A locale whose own calendar has different months (Persian by default in `fa-IR`, or an Islamic,
  Hebrew or Chinese calendar asked for with `-u-ca-`) now shows the calendar in the Gregorian
  calendar, in its own language and digits. The grid was always Gregorian, so `fa-IR` used to show
  September 1, 2026 as the 10th of Shahrivar 1405 and name the January cell Dey. Calendars that
  share the Gregorian months, such as Thai Buddhist or Japanese, keep their year numbering in the
  calendar. The date field lays out and reads Gregorian numbers for every locale, without the
  locale's era.
- <span className="badge bg-danger">Breaking</span> **A cell drawn by `renderDayCell` no longer gets
  an `aria-label`**, so its content is what a screen reader reads. Every render prop receives the
  cell's full name as `meta.label` (`Wednesday, August 12, 2026`, `August 2026`, `Q3 2026`, `2026`);
  put it in the cell, for example in a `visually-hidden` span next to a short visible label marked
  `aria-hidden="true"`. Day cells the calendar draws itself announce the full date with the weekday
  instead of the short numeric date (`8/12/2026`), which also leaves their `title` tooltip, and in
  week selection the row that takes focus is named by the days it spans (`August 10 – 16, 2026`),
  after its week number when week numbers are shown (`Week 33, August 10 – 16, 2026`), instead of
  reading out all seven.
- `renderDayCell` receives `isToday: false` for the copy of today among an adjacent month's days,
  which takes neither the `today` class nor `aria-current="date"` either, and in day selection
  `isInRange: false` for a day among an adjacent month's days that only fills the grid, which the
  range is not drawn on. With `selectAdjacentDays` the range and its hover preview are now drawn on
  the adjacent month's days as well, and `isInRange` stays `true` for them.
- A cell that cannot be picked carries `aria-disabled="true"`: a day outside `minDate` / `maxDate`
  or in `disabledDates`, a week row, month, quarter or year outside the range, and in day selection a
  day among an adjacent month's days unless `selectAdjacentDays` is on. Such an adjacent day is no
  longer `aria-selected` either, and `renderDayCell` gets `isDisabled: true` and `isSelected: false`
  for it, so the copy of a picked date that fills the next panel reads as unavailable filler.
- With `range`, the grid of the view that picks the range is `aria-multiselectable`, and every day,
  week row, month, quarter or year of the selected range that can be picked carries
  `aria-selected="true"`, not only its two ends, so a screen reader announces the ones in between as
  selected. The months and years views of a day range only navigate and keep it on the ends, and
  `isSelected` still marks only the ends.
- In week selection, `renderDayCell` gets the `isDisabled`, `isInRange` and `isSelected` of the
  day's week row, the way the calendar draws it: every day of a selected week is `isSelected`, every
  day of a week in the range is `isInRange`, and a day is `isDisabled` only when its row cannot be
  picked. Before, each field described the day alone, so a selected week reported only the day
  holding its start or end date as selected.
- A year below 100 stays as written: a calendar on December of year 99 draws the days of year 99,
  where it drew 1999, and its next panel shows January of year 100, where it showed January 2000.
  The month, quarter and year views, the arrow keys and paging keep the year the same way. Years
  before 1 cannot be picked, as dates before `minDate` cannot: their cells are disabled, and the
  arrow keys and paging stop at 1 January of year 1.
- A year string written in four digits keeps its year: `0026-05`, `0026W05`, `0026Q2`, `0026` and
  `12/31/0099` given to the calendar or a picker stand for the years 26 and 99, where they became
  2026 and 1999. Two digits still expand to the century within 50 years of today. The value reported
  for week, month, quarter and year selection writes the year in at least four digits (`0026-05`,
  where it was `26-05`), so it reads back as the same year.

### CCallout

- **New: `theme`.** It sets the color context and renders a `.theme-*` class, so the callout also
  picks up a theme class placed on any element above it. The class colors the leading edge and tints
  the border.
- <span className="badge bg-danger">Deprecated</span> **`color` still works and is removed in v7.**
  It renders the same `.theme-{color}` class as `theme` and loses to `theme` when both are set, so
  the modifier classes the component used to emit (`.callout-{color}`) are gone from its output.
- **A callout without a color no longer draws its leading edge in `currentColor`.** The class read a
  custom property that only the color modifiers declared, so on a bare `<CCallout>` the declaration
  was invalid and the property fell back to its initial value. It now defaults to the callout's own
  border color.
- **The 1px border of a colored callout is tinted** instead of staying neutral. The leading edge is
  unchanged.

### Deprecated color classes

- **`.alert-{color}`, `.btn-{color}`, `.btn-outline-{color}`, `.btn-subtle-{color}`,
  `.btn-ghost-{color}`, `.callout-{color}`, `.chip-{color}` and `.list-group-item-{color}` ship in
  [`coreui-legacy.css`](https://coreui.io/react/docs/customize/optimize/#v5-compatibility-css), not in the component
  stylesheets.** The components render theme classes now, so none of them emit those spellings —
  the sheet only serves markup you wrote by hand. Import it while you still need it, and drop it
  once your markup uses the theme classes. They are removed outright in v7.
- **The `color` property renders the theme classes.** `` renders
  `btn btn-solid theme-primary` rather than `btn btn-primary`, so an app that has not moved off
  `color` keeps its styling without loading the legacy sheet.
- **`CFormCheck` and `CFormSwitch` import `coreui-legacy.css` themselves.** They render the v5
  `.form-check` markup by design; the sheet comes with them, so nothing changes for you.

### CCard

- <span className="badge bg-danger">Breaking</span> **`textColor` is removed.** It wrote a single
  `fg-{value}` class, and the values it listed from v5 (`body-secondary`, `body-tertiary`,
  `*-emphasis`, `black-50`, `white-50`) had no rule in v6, so the text kept its inherited color. Put
  the color utility on the component instead: `className="fg-2"` for `body-secondary`, `fg-3` for
  `body-tertiary`, `fg-emphasis-primary` for `primary-emphasis`, `text-body-emphasis` for
  `body-emphasis`, `fg-white fg-50` and `fg-black fg-50` for `white-50` and `black-50`, and
  `fg-{color}` for a theme color.
- <span className="badge bg-danger">Breaking</span> **A card is roomier, rounder and framed in the solid
  border colour.** `--cui-card-spacer-y` / `-x` go from `1rem` to `--cui-spacer-6` (`1.25rem`) and the
  image-overlay padding follows them. `CCardHeader`, `CCardFooter` and a `CListGroup` placed directly in
  the card take `--cui-card-spacer-x` as their inline padding, so their text lines up with the body; the
  header and footer keep `.5rem` of block padding. The border reads `--cui-border-color` instead of
  `--cui-border-color-translucent`, except on a `translucent` card, and the radius is `--cui-radius-7`
  (`.75rem`; v5 used `.375rem`). A card filled with `textBgColor` or `color` keeps that neutral frame;
  add the matching `border-<color>` class to frame it in the fill colour. To keep the v5 look, set
  `--cui-card-spacer-y` and `-x` to `var(--cui-spacer-5)` and `--cui-card-border-radius` to `.375rem`
  on the card.
- <span className="badge bg-danger">Breaking</span> **`CCardLink` is removed.** `.card-link` is
  gone from v6 — it existed to space sibling links with an inline margin, which only worked while
  they were inline, and `.card-body` is a flex column with `--cui-card-body-gap` now. Wrap the
  links yourself: `<div className="hstack gap-4"></div>`.
- <span className="badge bg-danger">Breaking</span> **`.card-body` is a flex column.** It spaces
  its children with `--cui-card-body-gap` and zeroes their block margins, so nothing in a card
  needs `.mb-*` any more. Two consequences for existing markup: a direct child takes its natural
  width rather than filling the card — add `.w-100` where a block child has to stretch — and
  `text-align` no longer moves a button or a link, because they are flex items. Use `.mx-auto` or
  `.ms-auto` instead.
- **Stacked sections are divided by a single rule.** A `<CCardBody>`, a list group or a bottom image
  that follows a top image, a body or a list group draws a divider above itself in the card's own
  border, so two bodies in a row, or an image above a body, no longer sit with nothing between
  them, and a border utility or a theme on the card carries over to every divider. A section
  hidden with `hidden` or `.d-none` does not count. `<CCardHeader>` and `<CCardFooter>` draw their
  own divider, as before.
- **`<CCardGroup>` squares the corners at each seam along the writing direction.** The rules named
  the physical corners, so a right-to-left group squared its outer corners and rounded the ones
  that meet. A list group at the edge of a grouped card is squared like the other sections.
- <span className="badge bg-danger">Breaking</span> **`color` no longer takes a gradient.** v6
  removes the `bg-*-gradient` utilities the value mapped to, so the `Gradients` union is gone from
  the prop's type. Reach for `.bg-gradient` alongside the color if you want the sheen.
- **`style` on `CProgress` lands on the track.** It used to travel to the bar with the other
  attributes; now it stays on the `.progress`, as it does in the Vue edition, so the segment and
  gradient variables set there reach the mask and the bar alike. The other HTML attributes still
  go to the bar.
- **Gradient bars.** `--cui-progress-bar-bg-image` paints the bar with an image over its colour, so
  a gradient is one variable in `style`; stripes layer over it and have their own
  `--cui-progress-bar-stripes` and `--cui-progress-bar-stripe-color`.
- **`segmented` and `segments` on `CProgress`.** The track becomes a row of rounded segments —
  `--cui-progress-segment-min-width` wide and `--cui-progress-segment-gap` apart, or at most
  `segments` of them — and the bar fills whole segments only, recounted as the track resizes.
  Corners come from `--cui-progress-segment-border-radius`.
- <span className="badge bg-danger">Breaking</span> **`.card-header-pills` is gone.** It pulled
  the pill row out to the header's padding edge, which is wrong for pills — they carry their own
  background, so flush against the inner edge they touch the border. Drop the class from
  `<CNav variant="pills">`.
- <span className="badge bg-danger">Breaking</span> **`<CCardGroup>` attaches from a container
  query, not a media query.** The `sm` breakpoint is the width of the nearest query container, not
  the viewport. Mark an ancestor with `.contains-inline`; without a container the query has nothing
  to read and the cards stay stacked at every width. The same applies to `<CListGroup>` with
  `layout="horizontal-*"`.
- **New: `theme`** paints the card's frame and caps with a theme color, with contrast text in the
  header and footer; the body keeps the card background. It is a different axis from `color`, which
  still applies the `bg-{color}` utility across the whole surface, and from `textBgColor`. Put the
  class on a single section — `<CCardFooter className="theme-danger">` — and only that section
  follows it.
- **New: `variant="subtle"`** swaps the solid fill for the theme's subtle background and border,
  and **new: `translucent`** blurs whatever is behind the card and thins the caps to match. They
  are independent — `.card-subtle` and `.card-translucent` combine — which is why `translucent` is
  a boolean of its own and not a second `variant` value.
- **New: `layout="row"`** lays a card out along the inline axis, with
  `<CCardImage orientation="start" | "end">` for an image cap on either side. It replaces the
  `<CRow className="g-0">` + `<CCol md={4}>` recipe, which needed a wrapper per section and drew no
  divider between them.

### CChip, CChipSet, CChipInput

- <span className="badge bg-danger">Breaking</span> **`CChipInput` carries its chips in a hidden
  `<textarea>`, submitted only once you name it.** It took a generated `name` when the prop was not
  set, so a form carried a key nobody chose; the field is now named only by the prop. It is a
  `<textarea>` instead of an `<input type="hidden">`, because the browser leaves a hidden input out
  of validation, and the submitted value is the same, the chips separated by commas. With it
  `CChipInput` takes `required`, `validationState`, `onValidationStateChange` and the feedback
  props, blocks the submit while it is empty and required, and a native form reset puts back the
  chips it started with. A disabled `CChipInput` is no longer submitted, like any disabled field.
  See [Validation](https://coreui.io/react/docs/forms/chip-input/#validation).

- <span className="badge bg-danger">Breaking</span> **`CChip` drops `active`; `selected` is the
  one prop that renders `.active`.** On a `selectable` or `filter` chip `selected` keeps its
  controlled toggle semantics (with `aria-pressed`/`aria-selected` and the filter check icon);
  on any other chip `selected={true}` only renders the `.active` class — no toggle, no ARIA
  state — which is what `active` did. Replace `active` with `selected`.
- <span className="badge bg-danger">Breaking</span> **The chips expose real roles.** A selectable
  `CChipSet` is a horizontal `role="listbox"` (with `aria-multiselectable` outside single mode)
  whose chips are `role="option"` carrying `aria-selected`; a non-selectable set is a
  `role="group"`. A **standalone** selectable `CChip` is a toggle button (`role="button"`) and
  reflects its state through **`aria-pressed` instead of `aria-selected`**.
- <span className="badge bg-danger">Breaking</span> **`aria-disabled` is stamped only on a chip
  that carries a role.** On a role-less chip the attribute was invalid; the `disabled` class and
  the removed interactivity carry the state.
- <span className="badge bg-danger">Breaking</span> **`CChipInput` no longer stamps
  `aria-disabled`/`aria-readonly` on its container.** The container is a generic element, so those
  attributes were invalid on it; the native `disabled`/`readonly` states of the inner input carry
  the semantics.
- **Adding and removing chips is announced.** `CChipSet` and `CChipInput` announce each change
  through `announce()`; the wording is configurable through `ariaAddedAnnouncement` and
  `ariaRemovedAnnouncement`.
- **Every remove button names its chip.** `CChip` and `CChipSet` named each remove button
  `Remove`, so a set read the same name for every chip; the button is `Remove <text>` now, as
  `CChipInput` and `CMultiSelect` already did. `ariaRemoveLabel` (and `ariaChipRemoveLabel` on
  `CMultiSelect`) takes a string, where `{label}` stands for the chip's text and is appended when
  missing, or a function of the text for a different word order. A label that already spelled the
  text out, such as `` ariaRemoveLabel={`Remove ${chip}`} ``, now repeats it; drop it or use
  `{label}`.
- **A selectable chip hides its remove affordance.** A chip with a role — a selectable chip, which
  is a toggle button, or an option in a selectable set — gets a `<span aria-hidden="true">` instead
  of a `<button>` inside it, so its name is its text and a control is no longer nested in a button.
  Backspace and Delete remove it from the keyboard, the affordance from the pointer.
- **A chip set is one tab stop.** `CChipSet` and `CChipInput` gave every chip `tabIndex={0}`, so
  Tab walked through each chip. The chip that last had focus is the tab stop now, the first chip
  until then, and the arrow keys move between the rest, chips without a `value` included. `CChip`
  computes its `tabIndex` from `selectable`, `removable` and `disabled`; a `tabIndex` passed to it is
  ignored.
- **`CChipInput` keeps focus among the chips after a removal.** Removing the focused chip sent
  focus to the text input; it now moves to the next chip, else the previous one, and reaches the
  text input once no chip is left, the rule `CChipSet` follows.

### CCloseButton

- <span className="badge bg-danger">Breaking</span> **The `white` and `dark` props are gone.** The
  glyph is drawn with a CSS mask and takes the button's inherited `color`, so on any surface that
  paints its own text — a `.text-bg-inverse` toast, a dark navbar's offcanvas — it is already the
  right color, and neither prop has anything left to do.

  `white` rendered `.btn-close-white`, which v6 removed. `dark` stamped
  `data-coreui-theme="dark"` on the button, which used to match a rule that inverted the glyph
  through a `filter`; that rule went with the mask, and what the attribute still sets — the shadow
  strength and `color-scheme` — reaches nothing the close button draws. To flip a region, put the
  attribute on the section and give it a surface (`.bg-body.text-body`); the button follows the
  text it inherits.

### CCollapse

- **The stylesheet owns the animation where the browser can interpolate a size to `auto`.**
  `CCollapse` no longer writes an inline `height`/`width` there — driving the size from React as
  well would run the same expansion from both ends. Browsers without `interpolate-size` keep the
  measured path, and both read the duration and easing from
  `--cui-transition-collapse-duration` / `-timing` instead of a hardcoded 350 ms.
- <span className="badge bg-danger">Breaking</span> **`.collapse` stays on the element for the
  whole run.** It used to be dropped while `.collapsing` was on, which in v6 means the element
  misses `interpolate-size` and the transition the stylesheet puts on `.collapse` — so the CSS
  path would not animate at all. `.show` also lands at the *start* of the opening run rather than
  at its end, because the stylesheet reads it as the open state and animates towards it. Custom
  CSS that keyed off the old combinations needs updating.
- **New: `hiddenUntilFound`** hides the collapsed area with `hidden="until-found"` rather than
  taking it out of the layout, so find-in-page searches it and the browser expands it on a match.
  `onShow` fires when that happens — set `visible` from it, or the stylesheet hides the content the
  reader was just sent to. Off by default; the attribute is left off entirely below Chrome 102 /
  Firefox 148 / Safari 26.2.

### CConditionalPortal

- **`CConditionalPortal` resolves its target while rendering.** It used to render its children in
  place for one commit and then teleport them, which remounted the subtree and dropped any focus
  those children had taken.

### CContextMenu

- **New component.** `<CContextMenu>` is the trigger area: a right-click, or <kbd>Shift</kbd>+<kbd>F10</kbd> /
  the <kbd>Menu</kbd> key while it has focus, opens the `<CMenuContent>` inside it at the pointer
  and suppresses the browser's own menu. It is a `CMenu` positioned against the pointer with
  `position: fixed`, so items, submenus, keyboard navigation and `autoClose` are the Menu's. Focus
  moves into the menu on open and returns to the area on close; the page does not scroll while
  the menu is open. A row's own `onContextMenu` handler runs first — call `preventDefault()` there
  to keep both menus closed.

### CDateInput, CDateRangeInput, CTimeInput

- <span className="badge bg-danger">Breaking</span> **A required field blocks the form.** The value
  moved from an `<input type="hidden">`, which the browser leaves out of validation, to a `<textarea>`
  laid invisibly over the field, with `tabIndex={-1}` and `aria-hidden="true"`; a textarea, unlike a
  text input, leaves Enter submitting a form that has no submit button. A `required` field that is
  empty, incomplete or holds a rejected date now stops the submit, and the browser puts focus on its
  first empty section with its own message over the field. The field marks itself invalid whenever a
  validation reports it, also outside a submit, and its value field fires native `input` and
  `change` events when the date changes, which `onInput` on a form sees and React's `onChange` does
  not. This applies to the pickers and to both ends of a date range as well. Code that
  selects `input[type="hidden"]` inside the field needs `.form-date-time > textarea`.
- <span className="badge bg-danger">Breaking</span> **A given state is a verdict on one value.**
  `validationState="invalid"` now blocks the submit, with the text of the message after the field,
  until it is cleared. The user's next change of the value and a native form reset call
  `onValidationStateChange(undefined)` before the change is reported, so pass your state setter
  there; a value your code sets keeps the state. Without a given state the field shows what a
  validation reports and, in a form with `validate`, the `:user-invalid` and `:user-valid` states of
  its value field, as a native field does. `aria-invalid` sits on the sections, not on the group,
  and an `aria-invalid` you pass is not read as a state. This applies to the pickers and to both
  ends of a date range as well.

### CDatePicker, CDateRangePicker, CTimePicker

The pickers are no longer monoliths. Each is a composition of a **section field**, a **calendar or
time selection**, and an **anchored panel** — which is what removes most of the props below: the
field and the panel are now the same primitives you can use directly.

- <span className="badge bg-danger">Breaking</span> **`CDatePicker` is its own component.** In v5
  it was a thin wrapper over `CDateRangePicker` and inherited its props; it now has its own,
  single-date API (`date`, `defaultDate`, `onDateChange`).
- <span className="badge bg-danger">Breaking</span> **The button-configuration props are gone** —
  `todayButton`, `todayButtonColor`, `todayButtonSize`, `todayButtonVariant`, `cancelButton*`,
  `confirmButton*` and `rangesButtons*`. Project the content instead: the `footer` prop takes a
  node, or a function receiving the picker's context, so a button you render yourself can act on
  the picker and read its state. The same context is available to any descendant through
  `useDatePicker()`, `useDateRangePicker()` and `useTimePicker()`.

  ```jsx
  <CDatePicker
    footer={({ today, clear, close, isDateSelectable }) => (
      <>
        <CButton disabled={!isDateSelectable(new Date())} onClick={() => isDateSelectable(new Date()) && today()} size="sm">Today</CButton>
        <CButton onClick={clear} size="sm">Clear</CButton>
        <CButton onClick={close} size="sm">OK</CButton>
      </>
    )}
  />
  ```

- <span className="badge bg-danger">Breaking</span> **`CDateRangePicker`: `ranges` is a projected
  region too.** It takes a node or a function receiving the context, so a preset button calls
  `setRange` with its own dates; `rangesButtonsColor`/`-Size`/`-Variant` are gone with it. The
  controlled range is `startDate`/`endDate` (with `defaultStartDate`/`defaultEndDate` for the
  uncontrolled mode), and the two fields take `startName`/`endName`.
- **A picker action shows its date in the calendar.** `setRange`, `reset`, `setDate` and `today`
  from the picker's context turn the calendar to the date they set (a range's start, else its end)
  in the view it is in, also when the value does not change and over a `calendarDate` you pass, so a
  preset clicked again after browsing brings its range back. `clear` and an invalid date leave the
  calendar where it is; a typed date, a picked time and the dates you pass move it as before.
- <span className="badge bg-danger">Breaking</span> **A date field is submitted only once you name
  it.** `CDatePicker` and `CDateRangePicker` built the name of their inputs from `id` when `name`
  was absent, so `` posted `delivery-date` and the range posted
  `delivery-start-date` / `delivery-end-date`. That fallback was deprecated in 5.3.0 and is gone:
  an `id` names the element for a label, it does not put the value in the payload. Pass `name`, or
  `startName`/`endName`, for the field to reach the server.
- <span className="badge bg-danger">Breaking</span> **The field props are gone**:
  `inputDateFormat`, `inputOnChangeDelay`, `inputReadOnly`, `placeholder`, `separator`. The value
  is a mask now — pass `format` (or a section layout) and, for the placeholder text, one prop
  per section — `dayPlaceholder`, `monthPlaceholder`, `yearPlaceholder` and the rest. The
  range placeholder also named the two fields; `CDateRangePicker` names them with
  `ariaStartLabel` and `ariaEndLabel` now ("Start date" and "End date" by default), or with
  `floatingLabels` when set. Use `readOnly` for a read-only control and `separatorIcon` for the
  artwork between the two range fields.
- **Kept.** `timepicker` still adds the time half to `CDatePicker`, now as a row of time selects
  under the calendar rather than a second panel beside it. Because the two halves own different
  parts of one value, a picker with time does not auto-close: picking a day keeps the time and
  picking a time keeps the day. `hours`, `minutes` and `seconds` narrow what the time half offers.
- **Changed.** With `timepicker`, a `minDate` or `maxDate` that carries a time of day bounds the
  time on its day: the time selects disable the hours, minutes and seconds before `minDate` or
  after `maxDate` there, a time the panel sets beyond the limit (the current time when that day is
  picked) moves to the limit, and a typed time beyond it marks the field invalid. A limit without a
  time of day covers its whole day.
- <span className="badge bg-danger">Breaking</span> **`hours` is 0 to 23 in every locale.** v5 read
  the list and the filter in the column's cycle, so on a locale whose v5 columns were 12-hour (the
  ones that write AM and PM in Latin letters, such as `en-US`) `hours={[1, 3, 5, 7]}` meant 1, 3, 5
  and 7 in the morning and in the afternoon, `12` meant noon and midnight, and a filter received 1
  to 12. The 12-hour column still lists 1 to 12 next to AM/PM, but each entry now stands for the
  24-hour hours the list allows: picking an hour that only one period allows sets that period, and
  the period the picked hour does not allow is disabled. To keep such a list, name both halves:
  `hours={[1, 3, 5, 7, 13, 15, 17, 19]}`, with `0` and `12` for `12`. More locales get 12-hour
  columns than in v5, because the columns follow the field's clock, Korean, Traditional Chinese,
  Arabic and Mexican Spanish among them; their v5 columns were 24-hour, so a list written for them
  keeps its meaning. The day period column also speaks the locale, 오전 and 오후 in Korean, where it
  read "AM" and "PM". A longer name widens its column, so `--cui-time-picker-cell-width` is now the
  narrowest a cell gets, and `--cui-time-picker-cell-padding-x` keeps the name off the cell's edges.
- <span className="badge bg-danger">Breaking</span> **The time picker's roll classes lose the `roll`
  infix, and the date picker's time controls carry its own name.** `.time-picker-roll-col` and
  `.time-picker-roll-cell` are `.time-picker-col` and `.time-picker-cell`, with
  `--cui-time-picker-col-*` and `--cui-time-picker-cell-*` in place of their `-roll-` spellings;
  `.time-picker-roll` was always present on `.time-picker-body`, so it is gone rather than renamed,
  and `--cui-time-picker-body-padding` went with it. `.time-picker-inline-icon` and
  `.time-picker-inline-select` are `.date-picker-time-icon` and `.date-picker-time-select`, with
  `--cui-date-picker-time-*` tokens, because they belong to the date picker's panel. Custom CSS
  written against any of these names needs the new one.
- <span className="badge bg-danger">Breaking</span> **`CTimePicker`: `variant` is gone.** Each
  picker has one time rendering — the time picker shows the roll, and the date picker's time half
  shows the selects, which is what v5 rendered inside a date picker with time. The selects ship in
  the date picker stylesheet, so a page that only uses the time picker no longer downloads them.
- <span className="badge bg-danger">Breaking</span> **The time selection is one tab stop.**
  <kbd>Tab</kbd> reaches the body once, <kbd>Arrow Left</kbd> and <kbd>Arrow Right</kbd> move
  between the hour, minute, second and AM/PM parts and stop at the ends, and <kbd>Arrow Up</kbd> /
  <kbd>Arrow Down</kbd> change the focused value. Both renderings answer the same keys.
- <span className="badge bg-danger">Breaking</span> **`CTimePicker`: `ampm` is gone.** The hour
  cycle follows the field's mask when it names one (`HH` vs `hh a`) and the locale otherwise.
- <span className="badge bg-danger">Breaking</span> **`closeOnSelect` and `previewDateOnHover` are
  gone.** A complete selection closes the panel unless a `footer` projects actions the user still
  needs.
- <span className="badge bg-danger">Breaking</span> **`indicator` is gone.** The button that opens
  the panel is always rendered; `pickerIcon` replaces its artwork. The button that clears the
  value is the one you can switch off, with `cleaner`.
- <span className="badge bg-danger">Breaking</span> **The v1 shell class names are gone**:
  `date-picker-input`, `date-picker-input-wrapper`, `date-picker-input-preview`,
  `date-picker-cleaner`, `time-picker-input`, `time-picker-cleaner`, and the `-dropdown` panels are
  now `.date-picker-popup` / `.time-picker-popup`.
- **The panel is rendered through a portal by default** (`portal`), into `container` when given.
  It is built on the first open, not on mount.
- **New props across the family**: `cleaner` and `cleanerIcon`, `pickerIcon`, `disabled`,
  `visible`, `onShow`/`onHide`, `size`, and the accessible labels `ariaCleanerLabel` /
  `ariaPickerLabel`.
- **Pickers open on the platform's dropdown keys.** `Alt`+`ArrowDown` and `F4` open the panel and
  move focus into it — onto the selected date, else today or the `minDate` / `maxDate` today falls
  outside of, also when that date cannot be picked. The
  field's own arrows keep editing the value, as `<input type="date">` does. Opening with the mouse
  moves focus in too.

### CDropdown

- **Placements are mirrored once now, by Floating UI.** The `-start`/`-end` half of a placement is
  logical — Floating UI resolves it from the writing direction of the floating element — and the
  library was mirroring it a second time, which cancelled it. In a right-to-left context that moves
  every placement whose suffix you set: `alignment="end"` now hangs from the inline end, and an
  explicit `placement="bottom-start"` opens from the right edge instead of the left. The sides are
  the other way round: Floating UI never mirrors `left`/`right`, so a logical `start`/`end` side
  still maps to the physical one the direction asks for and `dropend`/`dropstart` still swap. Also
  in this change: a `dropup` aligned to the end keeps its side (it resolved to `bottom-end`
  before), `direction="center"` resolves to `bottom` rather than `bottom-start`, a submenu in a
  right-to-left context anchors to the top of its trigger rather than the bottom, and
  `useDropdownWithFloating` asks Floating UI for a plain `bottom-start`. `CMenu`, `CSubmenu`,
  `CContextMenu` and the picker panels all resolve a placement the same way. `CTooltip` and
  `CPopover` keep mirroring a physical `left`/`right` as a v5 compatibility fallback, which is
  removed in v7.
- **A panel sent through a portal carries the direction of its trigger.** Out in `document.body` it
  no longer inherits the direction that resolves the suffix, so it would have been laid out as
  left-to-right while its trigger sat in a `dir="rtl"` subtree. A `dir` you set yourself on the
  panel is left alone.
- **`CDropdown` is now a compatibility layer over `CMenu`.** The v5 markup, class names, props and
  callbacks keep working — the two share one engine underneath, so the dropdown also gained
  `Escape` closing regardless of `autoClose`. **Deprecation notice: `CDropdown` may be removed in
  v7** — prefer `CMenu` for new work.
- <span className="badge bg-danger">Breaking</span> `popperConfig` → `floatingConfig`, see
  [Dependencies](#dependencies).

### CFocusTrap

- **`CFocusTrap`'s `additionalContainer` accepts an element**, not only a ref. A ref is read once,
  when the trap activates, which is too early for a panel that is built on open.

### CFooter

- <span className="badge bg-danger">Breaking</span> **`CFooter` is removed.** It rendered a `.footer`
  class whose nine declarations utilities already cover, so a footer is plain markup now:
  `<footer className="d-flex flex-wrap align-items-center justify-content-between bg-3 border-top px-5 py-3">`.
  The `min-height: 3rem` floor has no utility — set it yourself where it matters, or pad with `py-4`.
- <span className="badge bg-danger">Breaking</span> **The `position` prop goes with it.** Use
  `.sticky-bottom` in place of `position="sticky"` (it pins the same way, on `--cui-z-sticky` rather
  than `--cui-z-fixed`) and `.fixed-bottom` in place of `position="fixed"`, which emitted a
  `.footer-fixed` class the PRO stylesheet has never defined.

### CForm

- **`validate` with `noValidate` validates the form on submit.** `<CForm>` stops an invalid submit
  (`onSubmit` runs only for a submit that goes through), marks the invalid controls with
  `.is-invalid` and `aria-invalid`, links the message of each to it with `aria-describedby` while it
  stays invalid, and moves focus to the first. A hand-written `onSubmit` that only called
  `checkValidity()` and `preventDefault()` can go. The new `onValidate` callback runs first; call
  `preventDefault()` on its argument to handle the result yourself.

### CFormCheck

- <span className="badge bg-danger">Breaking</span> **`hitArea` is removed.** It rendered the label
  as a `stretched-link` so a whole list group item would toggle the box; the list group item can
  be the label itself now — `<CListGroupItem as="label"> Text
  </CListGroupItem>` — which is what the stylesheet is written for.

### CFormFloating

- <span className="badge bg-danger">Breaking</span> **The placeholder shows once the field has
  focus.** It used to stay transparent in every state; it now appears under the floated label while
  the field, or the control inside a field component, has focus, and stays hidden while the label
  rests in the field. A `plainText` control keeps it hidden.
- **New: `always`** on `<CFormFloating>` and **`floatingLabelAlways`** on every form control that
  renders a floating label keep the label floated and the placeholder showing while the field is
  empty. See [Always floating](https://coreui.io/react/docs/forms/floating-labels/#always-floating).

### CFormLabel

- <span className="badge bg-danger">Breaking</span> **`customClassName` is removed.** It was the one
  prop in the library that *replaced* `className` instead of adding to it, and only `CFormCheck` and
  `CFormSwitch` used it internally to render a label without `.form-label`; they render a plain
  `<label>` now. Pass `className` for extra classes; a label without the base class is a plain
  `<label>` element.

### CFormRange

- **Changed.** **A pressed thumb takes `--cui-primary-bg-muted`**, the theme's `bg-muted` under a
  `.theme-*` class, instead of a 30% tint of the primary color.
- <span className="badge bg-danger">Breaking</span> **`<CFormRange>` renders a wrapper.** The input
  sits inside `<div class="form-range">` as `.form-range-input`; `className`, `style`, `theme`,
  `dir` and `hidden` land on the wrapper, which carries the tokens, while every other prop still
  reaches the input and `ref` still points at it. A `label` without an `id` gets a generated one, so
  the label names the input. The component writes `--cui-range-fill` (0–1) from the value, so the
  track fills up to the thumb, a shade lighter than it, in every browser. An uncontrolled range
  seeds from `defaultValue` (the native midpoint without it).
- **`tooltips` and `tooltipsFormat`** show the value above the thumb, on hover, drag and focus or
  `"always"`; **`ticks`** renders tick marks from an array of values, labels or `{ value, label,
  className, style }` objects, labels beneath. Both follow the vanilla `Range` plugin, which also
  reads a `<datalist>`, and share names with `<CRangeSlider>`. `onChange` gets the value as a
  number in its second argument, `onChange(event, value)`. Several handles, `distance`, clickable
  ticks and a vertical orientation are what `<CRangeSlider>` adds.

### CHeader

- <span className="badge bg-danger">Breaking</span> **`position` renders position utilities.**
  `position="sticky"` emits `.sticky-top` in place of `.header-sticky`, which the stylesheet no
  longer defines — the two rules were identical, so the header pins exactly as before.
  `position="fixed"` emits `.fixed-top`; the `.header-fixed` class it used to render was never
  defined anywhere, so that value had no effect at all. For the bottom variants, or a responsive
  one like `.sticky-md-top`, pass the utility through `className` instead.

### CInputGroup

- <span className="badge bg-danger">Breaking</span> **Validation feedback moves out of the group.**
  The `.has-validation` class is gone, and a group keeps its corner logic by having the control as
  its last child — a `CFormFeedback` left inside squares off the end of the control it belongs to.
  Wrap the group in a `CFormField` and put the feedback after it, so the message renders under the
  whole frame. Inside a group that means a standalone `CFormFeedback` rather than the `feedback`,
  `feedbackValid` or `feedbackInvalid` props, which render it next to the control.

  ```jsx
  <CFormField>
    <CInputGroup>
      <CInputGroupText>@</CInputGroupText>
      
    </CInputGroup>
    <CFormFeedback validationState="invalid">Please choose a username.</CFormFeedback>
  </CFormField>
  ```

### CLoadingButton

- **`spinnerType` is deprecated in favour of `spinnerVariant`** — the same word `CSpinner` uses for
  `border` / `grow`; the old name loses to the new one. `onClick` receives the click event, as on
  `CButton`, instead of nothing.
- **The spinner takes the button's font size.** It no longer asks `CSpinner` for `size="sm"` — a
  spinner is `1em` square in v6, so inside a button it already lands where the small size used to
  put it. Custom CSS targeting `.btn-loading-spinner.spinner-border-sm` or `.spinner-grow-sm` has
  nothing to match; the sizing classes are gone from that element.
- <span className="badge bg-danger">Breaking</span> **`disabledOnLoading` no longer renders the
  `disabled` attribute.** A disabled button drops focus, so a keyboard or screen reader user who
  pressed it landed on the page body. While loading, the button now gets `aria-disabled="true"` and
  `.disabled`, keeps focus, and ignores clicks and form submission until it stops. CSS or tests that
  read `:disabled` on a loading button should read `[aria-disabled="true"]` instead.
- **The start and the end of loading are announced** when the button has focus: its name followed
  by the new `ariaLoadingLabel` prop, then its name again. The spinner no longer renders
  `role="status"` or the "Loading..." text that Safari read on an idle button; while loading it is
  a `role="img"` named by `ariaLoadingLabel`.

### CMenu

- **New: `custom` on `CMenuToggle`.** With it the single child element receives the toggle
  behaviour — `aria-expanded`, `aria-haspopup`, the click handler and the ref — and keeps its own
  markup and classes, instead of the toggle rendering a `CButton`. `CDropdownToggle` has carried
  the same prop since v4; this closes the gap in the component that replaces it.

### CModal, COffcanvas

- <span className="badge bg-danger">Breaking</span> **Both render a native `<dialog>`.** The
  browser provides the top layer, the `::backdrop`, focus trapping, background inertness and the
  dialog ARIA semantics, so the markup drops `role`, `aria-modal`, `aria-hidden` and
  `tabIndex={-1}`, and the component no longer renders a backdrop, a focus trap or a portal of its
  own.
- <span className="badge bg-danger">Breaking</span> **`CModalDialog` and `CModalContent` are
  removed.** `.modal > .modal-dialog > .modal-content` flattens to a single
  `<dialog className="modal">` holding the header, body and footer directly. `CModal` rendered
  both for you, so markup written against the components is unchanged; custom CSS targeting those
  two classes is not.
- <span className="badge bg-danger">Breaking</span> **`portal` and `container` are gone.** They
  existed to lift the modal out of a stacking context. A dialog opened with `showModal()` renders
  in the top layer, above every stacking context on the page, so there is nothing left to escape.
- <span className="badge bg-danger">Breaking</span> **`focus` is gone** — a modal dialog always
  traps focus, and the option could only have turned off something the browser does.
- <span className="badge bg-danger">Breaking</span> **`alignment` is gone** — the browser centers
  a modal dialog. `alignment="top"` has no replacement; a dialog that must sit at the top is a
  positioning override in your own CSS.
- <span className="badge bg-danger">Breaking</span> **`.modal-dialog-scrollable` is
  `.modal-scrollable`**, and `.modal-backdrop` / `.offcanvas-backdrop` are gone with the elements
  they styled — the backdrop is the `::backdrop` pseudo-element, themed through
  `--cui-modal-backdrop-*` and `--cui-offcanvas-backdrop-*`.
- <span className="badge bg-danger">Breaking</span> **Animation opt-out is a class now.**
  `transition={false}` renders `.modal-instant` / `.offcanvas-instant` instead of dropping
  `.fade`; the offcanvas `.showing` state class is replaced by the native `open` attribute, and
  `.hiding` carries the exit.
- **New: `modal={false}` on `CModal`** opens the dialog non-modally, leaving the rest of the page
  interactive. **New: `animation` on `COffcanvas`.**
- **Scroll locking moved to the root element** — `dialog-open` with `scrollbar-gutter: stable`,
  instead of inline `overflow` and `padding-right` on the body.

### CNavGroup

- **The stylesheet owns the group animation where the browser can interpolate a size to `auto`.**
  `CNavGroup` no longer writes an inline `height`/`display` on `.nav-group-items`; the run is
  marked with `.collapsing` and the open state with `.show` on the group, and the duration and
  easing come from `--cui-sidebar-nav-group-items-transition-*`. Browsers without
  `interpolate-size` keep the measured path. Custom CSS keyed on the inline height needs updating.
- **A group that renders open no longer plays the opening animation on mount** — `defaultShow`,
  a controlled `show` and an active nav link all reveal the branch straight away.

### COneTimePassword

- <span className="badge bg-danger">Breaking</span> **Only `COneTimePassword`'s hidden input
  carries the field's `name`.** Each `COneTimePasswordInput` was given `<name>-<index>` on top of
  it, so a six-slot field submitted seven keys where the prop is documented as the name of the
  hidden input. The slots are unnamed now, and a `name` you pass to a slot is left alone instead of
  being overwritten.
- <span className="badge bg-danger">Breaking</span> **The code is validated as one value.** It
  moved from an `<input type="hidden">`, which the browser leaves out of validation, to a text
  input laid invisibly over the group, with `tabIndex={-1}`, `aria-hidden="true"`, a `pattern` for
  the full length of the code and the `required` that the slots used to carry. A `required` code
  that is empty or incomplete stops the submit, so does an incomplete code that is not required,
  and the browser puts focus on the first empty slot. Once the browser reports the code, every slot
  carries `aria-invalid="true"` and the error message while the code stays invalid, as it already
  did for `validationState="invalid"`, and the stylesheet shows the message after the group, which
  it never did for a slot marked invalid. Code that selects `input[type="hidden"]` inside the field
  needs `.form-otp > input:not(.form-otp-control)`, and a `data-coreui-invalid-feedback` written on
  the slots goes on `COneTimePassword`.
- <span className="badge bg-danger">Breaking</span> **`COneTimePassword` blocks the submit while
  `validationState` (or the deprecated `invalid`) is `'invalid'`**, and asks to drop the state with
  `onValidationStateChange(undefined)` when the user changes the code or the form is reset. The
  state class moved from each `COneTimePasswordInput` to the `.form-otp` group, which the stylesheet
  colours as a whole, so CSS or tests keyed on `.form-otp-control.is-invalid` need
  `.form-otp.is-invalid`. An `aria-invalid` passed to the component or to a slot is no longer put on
  the group or the slot, the hidden input sends `input` and `change` whenever the code changes, also
  through `value`, and a native form reset puts back the code given by `defaultValue`. See
  [Validation](https://coreui.io/react/docs/forms/one-time-password-input/#validation).
- <span className="badge bg-danger">Breaking</span> **The `readOnly` prop is removed, and every slot is
  editable.** A code the user cannot type has no use in a form, so a `readOnly` on
  `COneTimePassword` or on a `COneTimePasswordInput` no longer makes a slot read-only; use
  `disabled` to show a code the user cannot change.

### CPasswordInput

- <span className="badge bg-danger">Breaking</span> **Renamed `ariaLabelToggler` to
  `ariaToggleLabel`.**
- <span className="badge bg-danger">Breaking</span> **The toggle icon is rendered by the component
  instead of being drawn by CSS**, and is replaceable through the new `showIcon` / `hideIcon`
  props. `.form-password-action-icon` is gone, the frame is `.form-control-group` and the button is
  `.form-control-action`.

### CPlaceholder

- **`animation="wave"` is a sweeping highlight and the recommended loading animation.** A narrow
  bright band crosses the placeholders every 1.6s instead of a wide gradient every 2s, and it also
  works on text. The opacity pulse is `animation="pulse"` now; `animation="glow"` stays as its alias.
  The prop still has no default. The keyframes are renamed to `placeholder-pulse` with it.
  With reduced motion the wave stops and drops its mask.
- **Placeholders are paler, taller and rounded.** The resting opacity is `.18` (was `.5`) and the
  pulse dips to `.1` (was `.2`); the default height is `1.25em` (was `1em`), capped a quarter em
  below the line height so a placeholder never makes a heading taller, and `.75em`, `1em` and
  `1.75em` for the `xs`, `sm` and `lg` sizes (were `.6em`, `.8em`, `1.2em`). Each placeholder has a
  `--cui-radius-3` corner; a `CButton`, `CAvatar`, `CBadge` or `CChip` with the `placeholder` class
  keeps its own height and corners. The wave reads its own `--cui-placeholder-wave-opacity-max` and
  `-min`.

### CPopover, CTooltip

- **`onShow` and `onHide` fire when the tip starts showing or hiding**, after `delay` and only when
  the visibility actually changes. `onShow` used to fire on the trigger gesture, before
  `delay.show`, and again for a second trigger on a visible tip, while `onHide` also fired after a
  hover that ended before the tip appeared. `onShowChange` reports the requests; a deprecated
  `visible` kept in step through `onShow` and `onHide` belongs on `show` and `onShowChange`.
- **New: `onShown` / `onHidden`**, fired once the entry or exit transition has finished.

### CProgress, CProgressBar

- <span className="badge bg-danger">Breaking</span> **`thin` is removed from `CProgress`.** It
  rendered `.progress-thin`, which v6 does not ship; set `height` for a slimmer bar.
- <span className="badge bg-danger">Breaking</span> **`color` no longer takes a gradient.** v6
  removes the `bg-*-gradient` utilities the value mapped to, so the `Gradients` union is gone from
  the prop's type. Reach for `.bg-gradient` alongside the color if you want the sheen.

### CRangeSlider

- <span className="badge bg-danger">Breaking</span> **The slider renders the markup of the
  range.** It is a `<div class="form-range">` with one native `.form-range-input` per handle;
  `className`, `style`, `theme`, `dir`, `hidden`, `role`, `aria-label`, `aria-labelledby` and the `ref` land on that `div`, and other input attributes, such
  as `id` (on the first handle) and `aria-describedby`, reach the handles. The classes follow:
  `.range-slider` → `.form-range`, `.range-slider-input` → `.form-range-input`,
  `.range-slider-vertical` → `.form-range-vertical`, `.range-slider-tooltip` →
  `.form-range-tooltip` (now a `<div>`), `.range-slider-ticks`/`.range-slider-tick` →
  `.form-range-ticks`/`.form-range-tick` with the text in `.form-range-tick-label`, and the
  per-tick `.clickable` → `.form-range-ticks-clickable` on the container;
  `.range-slider-inputs-container`, `.range-slider-track` and the `.disabled` class are gone. The
  handles no longer carry `role="slider"`, `aria-valuemin`, `aria-valuemax` or `aria-valuenow`: the
  native inputs expose them.
- <span className="badge bg-danger">Breaking</span> **The handles and the track read the range's
  tokens.** `--cui-range-slider-*` is gone: set `--cui-range-thumb-*`, `--cui-range-track-*`,
  `--cui-range-track-fill-bg` (was `-track-in-range-bg`), `--cui-range-track-disabled-bg` (was
  `-disabled-track-in-range-bg`) and `--cui-range-tick-label-color` and `-font-size` (were
  `--cui-range-slider-label-*`) on the slider. The label padding has no counterpart, the tooltips
  sit like the range's without `--cui-range-slider-tooltip-margin-*`, and a vertical slider's
  height is `--cui-range-vertical-height` and its track width `--cui-range-track-height`.
- <span className="badge bg-danger">Breaking</span> **`ariaLabels` is `ariaLabel`**, an array with
  one name per handle or a function `(index, total) => string`.
- **The slider is a group named after the field.** The `.form-range` gets `role="group"` with the
  `aria-label` or `aria-labelledby` you pass, so a screen reader announces the field when the focus
  enters it. With several handles, each handle carries only its own name ("Minimum value"); a
  single handle takes the name of the group. `aria-describedby` describes every handle, and a
  `role` you pass replaces `group` on the slider.
- **The slider takes a validation state.**
  `validationState` shows on the slider and on the handles, and `'invalid'` blocks the submit
  through the first handle until the user moves a handle or the form is reset; the slider calls
  `onValidationStateChange(undefined)` then. See [Validation](https://coreui.io/react/docs/forms/range-slider/#validation).
- <span className="badge bg-danger">Breaking</span> **`labels` is `ticks`, `clickableLabels` is
  `clickableTicks`.** An entry is a number (a tick without text), a string (a
  label placed by its position), or `{ value, label, className, style }`; a bare element is no
  longer an entry, pass it as `{ label: <strong>High</strong> }`.
- **Interaction follows the native control.** Pressing a handle no longer moves it; a press on the
  track or a clickable tick moves the nearest handle and it follows the pointer (mouse, touch or
  pen); a form reset restores `defaultValue`. A controlled slider whose parent ignores `onChange`
  keeps the handle on its value instead of letting it follow the pointer until the drag ends.
- **`tooltips` takes `"always"`.** `true` shows a handle's tooltip on hover, drag and focus;
  `"always"` keeps them on.

### CRating

- **A read-only rating submits its value under the `name` you give it.** `readOnly` used to
  disable the radios, so the form left the value out; now they stay enabled, the mouse and the
  keyboard cannot change them, only the checked star takes focus (the first star while no star is checked), and the group carries
  `aria-readonly="true"`. A read-only rating without `name` still submits nothing.
- **A star with keyboard focus shows the focus ring.**
- **A read-only rating shows no tooltips.**
- **The rating takes a validation state and can be required.** `validationState` colours the
  focus ring, and `'invalid'` sets `aria-invalid` and blocks the submit until
  you drop the state; the rating asks for that with `onValidationStateChange(undefined)` when the
  user picks or clears a star or the form is reset. `required` makes every radio required. See
  [Validation](https://coreui.io/react/docs/forms/rating/#validation).
- **Empty stars are `--cui-fg-4`**: gray-500 in the light scheme and gray-600 in the dark one, the
  lightest grays that keep 3:1 against the page background. The empty stars of v5 were 38% of the
  text colour, below 3:1 against the page in the light scheme.

### CSidebar

- **`onVisibleChange` is deprecated in favour of `onInViewportChange`.** The callback reports
  whether the sidebar is on screen — it is off-canvas on narrow viewports — not the `show` state,
  and the old name read like the latter's twin. Both fire while the old name is set.
- **`colorScheme="light"` joins `colorScheme="dark"`.** Either value pins the sidebar to one
  scheme whatever the page uses, so a dark navigation column can sit on a light page.
- **Theme variants.** `className="theme-{color}"` paints the surface, text, icons and the
  hover/active links from that colour's slots.
- **Hover and active links read the foreground ladder.** Their text was the pure black/white
  `--cui-emphasis-color` and their background the Sass-computed `--cui-tertiary-bg-translucent`;
  they are `--cui-fg-body` and `color-mix(in srgb, var(--cui-fg-body) 10%, transparent)` now.
  Slightly stronger than before on a light sidebar, slightly weaker on a dark one.

### CSmartTable

- <span className="badge bg-danger">Breaking</span> **The props are renamed.** Every filter prop
  says which filter it means, the column configuration drops the v3 underscores and the sorter
  icons take the names the other data components use:

  | v5 | v6 |
  | --- | --- |
  | `elementCover` | `loadingContent` |
  | `itemsNumber` | `totalItems` |
  | `onSelectedItemsChange` | `onSelectedChange` |
  | `sortingIcon`, `sortingIconAscending`, `sortingIconDescending` | `sortNeutralIcon`, `sortAscendingIcon`, `sortDescendingIcon` |
  | `tableFilter`, `tableFilterValue`, `defaultTableFilterValue`, `onTableFilterChange` | `globalFilter`, `globalFilterValue`, `defaultGlobalFilterValue`, `onGlobalFilterChange` |
  | `tableFilterLabel`, `tableFilterPlaceholder` | `globalFilterLabel`, `globalFilterPlaceholder` |
  | `columnSorter={{ resetable: true }}` | `columnSorter={{ resettable: true }}` |
  | `Column._style`, `Column._props` | `Column.headerStyle`, `Column.headerProps` |
  | `Column._colStyle`, `Column._colProps` | `Column.cellStyle`, `Column.cellProps` |
  | `Column.sorter: false` | `Column.sortable: false` (`sorter` is the comparator only) |
  | `Column.filter: false` | `Column.filterable: false` (`filter` is the custom filter only) |
  | `FooterItem._props` | `FooterItem.props` |
  | `onSelectAll` | — (`onSelectedChange` carries the same information) |

- <span className="badge bg-danger">Breaking</span> **A column group is `{ label, children }`.**
  The group label moves from `group` to `label` and a group no longer needs a `key`:
  `{ group: 'Address', key: 'address', children: [...] }` becomes
  `{ label: 'Address', children: [...] }`.

- <span className="badge bg-danger">Breaking</span> **One `external` prop replaces the six
  `external` options.** `columnFilter`, `tableFilter`, `columnSorter`, `pagination`,
  `itemsPerPageSelect` and `selectAll` each carried an `external` flag; `external` on the table
  says the same thing once — the items arrive already filtered, sorted and sliced, so the table
  renders them as they are and only reports the requested state. Pair it with `totalItems` so it
  can compute the number of pages. Where the option only meant "let the parent own this piece of
  state", pass the controlled prop instead.

- <span className="badge bg-danger">Breaking</span> **`SorterValue` is `{ column, direction }` and
  the sorter value is always an array.** `state: 'asc' | 'desc' | 0` becomes
  `direction: 'asc' | 'desc'`, with an absent entry standing for "not sorted", and
  `sorterValue`, `defaultSorterValue` and the `onSorterChange` payload are `SorterValue[]` whether
  or not `columnSorter={{ multiple: true }}` is set — no more `Array.isArray` at the call site.

- <span className="badge bg-danger">Breaking</span> **`Item._selected` is removed.** Seed the
  selection with `defaultSelected`, or own it with `selected` — the flag in the data was a second
  source of truth and the two drifted apart.

- **The selection checkboxes and the cleaner button have accessible names.**
  `ariaSelectAllLabel` (`'Select all rows'`), `ariaSelectRowLabel` (`'Select row'`) and
  `ariaCleanerLabel` (`'Clear filters and sorting'`) are read by screen readers; before, none of
  the three controls had a name at all.

- **`itemKey` identifies a row.** With it set, selection and React keys compare `item[itemKey]`
  instead of the whole object, so a selected row survives a re-fetch of the data.

- **`cleanerIcon` replaces the cleaner button's icon**, the way the sorter icons already could.

- **The selection checkboxes render `CCheckbox`.** The select-all control in the head and the
  per-row control in the body are `<input class="check">` now, not `.form-check-input`, so custom
  CSS keyed on the old class needs updating.

- **The chrome is composable.** `CSmartTable` renders the layout it always did as long as it has
  no children; pass children and it renders them instead, so `CSmartTableFilter`,
  `CSmartTableCleaner`, `CSmartTableContent`, `CSmartTablePagination` and
  `CSmartTableItemsPerPage` go wherever you put them, next to your own markup.
  `useSmartTableContext()` reads the same state — the items, the page, the filters, the sorter and
  the selection — so a part of your own is a component that calls it.

- **`useSmartTable(options)` runs the table without any of its markup.** The whole state — the
  filters, the sorter, the pages, the selection with `isSelected`, `toggle`, `toggleAll` and
  `selectedAll` — is a hook you can call with the state props of `CSmartTable` and render however
  you like, tables included or not. `CSmartTable` itself is that hook plus the layout.

### CSpinner

- **`visuallyHiddenLabel` is deprecated in favour of `ariaLoadingLabel`**, which works as
  `ariaLoadingLabel` does on `CLoadingButton`: the text defaults to `'Loading'` instead of
  `'Loading...'`, and an empty string hides the spinner from screen readers. `visuallyHiddenLabel`
  still sets the text while `ariaLoadingLabel` is unset.

- **Spinners are sized in `em`, not `rem`.** A spinner is `1em` square now, so it takes the font
  size of whatever it sits in instead of being a fixed 32 px block. Inside a button or a heading it
  lands at the size you previously had to ask for with `size="sm"`; standalone it is half of what it
  was. Set `--cui-spinner-width` / `--cui-spinner-height` to go back to a fixed size.

- **`size="sm"` renders `.spinner-sm`, not `.spinner-border-sm` / `.spinner-grow-sm`.** The prop is
  unchanged; only custom CSS targeting the old per-type classes needs updating.

- **`.spinner-grow` eases with `ease-in-out`** instead of pulsing linearly. Override
  `--cui-spinner-animation-timing-function` to change either spinner's easing.

- **`.spinner-rotate` is new.** Pass it through `className` on any element to spin your own artwork
  without inheriting the bordered circle.

### CStepper

- <span className="badge bg-danger">Breaking</span> **`--cui-stepper-step-indicator-active-bg` is removed.** The active step's
  indicator background is a 5% tint of the stepper's theme color, or of
  `--cui-stepper-step-indicator-active-color` without one. To give it another background, set
  `--cui-stepper-step-indicator-bg` on `.stepper-step-button.active` in your stylesheet.

- <span className="badge bg-danger">Breaking</span> **`activeStepIndex` and
  `defaultActiveStepIndex` are removed.** Deprecated since 5.16.0 for their 0-based indexing; use
  `activeStepNumber` / `defaultActiveStepNumber`, which count from 1.
- **A failed step moves focus to the first invalid control, and the panes are no longer live
  regions.** With a `noValidate` form the invalid controls also get `aria-invalid`, and the message
  is read with the field: the stepper adds the `.invalid-feedback` that follows the control, which
  covers `feedbackInvalid`, or the elements named in `data-coreui-invalid-feedback`, to its
  `aria-describedby`. The panes used to read out every message that appeared in them; a message that
  changes while the user types is now silent, so pass the ones that have to be heard to
  [`announce()`](https://coreui.io/react/docs/accessibility/screen-readers/#announcing-messages).
- **`onStepValidationComplete` runs before the stepper decides.** A validity it sets with
  `setCustomValidity()` blocks the step, and `isValid` tells whether every control passed before
  it ran. A `noValidate` step form is marked each time the step is validated, also when it passes,
  so `data-coreui-validate="valid"` now marks the valid controls of a passing step with `.is-valid`.
- **A non-linear stepper validates the step being left.** With `linear={false}`, clicking a later
  step used to skip validation; it now runs it like `next()` does and stays put while the step is
  invalid. Going back is never blocked. Add `validation={false}` for the old behaviour.

### CTable

- <span className="badge bg-danger">Breaking</span> **`caption` is the caption and `captionTop` is
  a boolean.** `caption="top"` no longer carries a magic value and `captionTop` no longer carries
  text: `<CTable caption="top"><CTableCaption>…</CTableCaption></CTable>` becomes
  `<CTable captionTop>…</CTable>`, and `` becomes
  ``.
- **New: `card`** wraps the table in a card's border and corner radius, clips its cells and drops
  its bottom margin and the borders that would stack on the frame. With `responsive` the class lands on the same
  wrapper. See [Card-like tables](https://coreui.io/react/docs/components/table/#card-like-tables).

### CToast, CToaster

- <span className="badge bg-danger">Breaking</span> **`CToaster.push` is gone; toasts come from a
  store.** `createToaster({ placement, limit, timeout })` builds the store outside the React tree
  and `` renders it: `add(options)` returns the id (an existing id
  updates in place and bumps `updateKey`, a closing one is replaced), `update(id, options | fn(prev))` rewrites a
  toast, `close(id?)` hides one or all, `promise(p, { loading, success, error })` follows an
  asynchronous operation. A toast is options — `title`, `description`, `theme`, `timeout`
  (`0` = sticky), `priority` (`'normal'` or `'high'`, the polite or the assertive live region), `dismissible`, `action`,
  `render` — instead of a `<CToast>` element. `limit` keeps the newest toasts visible and leaves
  the oldest in the DOM as `inert` and hidden. `useToaster()` returns the store inside the
  container. `<CToaster>` without `toaster` stays a container for `<CToast>` children.

  ```jsx
  // v5
  const [toast, addToast] = useState()
  <CButton onClick={() => addToast(<CToast><CToastBody>Saved</CToastBody></CToast>)} />
  

  // v6
  const toaster = createToaster({ placement: 'top-end' })
  <CButton onClick={() => toaster.add({ description: 'Saved' })} />
  
  ```
- <span className="badge bg-danger">Breaking</span> **`CToast` drops `index` and `innerKey`, and
  `onClose` / `onShow` fire without an argument** — they were the plumbing of `push`, and a toast
  rendered by the store is identified by its `key` and `data-coreui-toast-id`.
- **Placement is one class.** `<CToaster placement>` renders `.toast-container-{placement}` in
  place of the `position-fixed top-0 end-0` utility stack; the edge inset comes from
  `--cui-toast-container-inset`. The container is a `region` landmark named `Notifications`.
- **The store carries the rest of the behavior.** `ariaLabel` names the landmark, `enter` and
  `leave` pick the edges a toast slides in from and out to, `pauseOnHover` (on by default) stops
  every timer while the pointer or focus is inside the container, and `stack: true` piles the
  toasts up newest-in-front and spreads them out on hover. `<CToaster portal={false}>` keeps the
  container where it is written instead of portalling it onto `document.body`.
- **Announcements moved off the toast.** A toast rendered by the toaster carries no `role`,
  `aria-live` or `aria-atomic`; every `add()` and `update()` passes the string parts of `title`
  and `description` to `announce()`, which reads them at the priority `priority` selects. A standalone `<CToast>` keeps its own
  `role="alert"` defaults.
- **New: `translucent`** renders `.toast-translucent` — the background shows through the toast,
  blurred, and the header thins out with it.
- **The fade moved to CSS.** The toast no longer renders the `fade` and `showing` helper classes
  and no longer drives its own opacity — `show` alone drives the transition, with
  `@starting-style` supplying the state to animate from and a discrete `display` transition
  keeping the toast laid out until the fade-out finishes. `animation={false}` now renders
  `.toast-instant` instead of dropping `fade`, and finishes both directions immediately.
  The prop itself is unchanged; only custom CSS targeting `.toast.fade` or `.toast.showing`
  needs updating. Tune the motion with `--cui-toast-transition-duration` and
  `--cui-toast-transition-timing`.

### CWidgetStatsA – CWidgetStatsF

- <span className="badge bg-danger">Breaking</span> **The six widget components are removed.** They
  were the one family in the library with no stylesheet of their own: each rendered a `CCard` and a
  fixed stack of utility classes, so they carried a layout decision rather than styling you cannot
  write in place. Compose the same markup directly, which also lets a widget diverge from the six
  shapes we happened to ship:

```jsx
// Before
<CWidgetStatsF color="primary" icon={<CIcon icon={cilSettings} height={24} />} title="Users" value="87.500" />

// After
<CCard>
  <CCardBody className="d-flex flex-row align-items-center">
    <div className="me-4 fg-white bg-primary p-4">
      <CIcon icon={cilSettings} height={24} />
    </div>
    <div>
      <div className="fs-6 fw-semibold fg-primary">87.500</div>
      <div className="fg-2 text-uppercase fw-semibold small">Users</div>
    </div>
  </CCardBody>
</CCard>
```

- **The six shapes live on as [blocks](https://coreui.io/react/docs/blocks/widgets/).** Every one of them is on that page as
  markup to copy, with the names they go by now: `CWidgetStatsA` is the stat with a chart,
  `CWidgetStatsB` the stat with a progress bar, `CWidgetStatsC` the one with an icon and a progress
  bar, `CWidgetStatsD` the stat group, `CWidgetStatsE` the compact stat and `CWidgetStatsF` the stat
  with an icon.
- **Three of them did not survive the v6 stylesheet unchanged, which is the other half of the
  reason they are going.** `CCardBody` became a flex column in v6, so `CWidgetStatsA` stacked its
  menu under the figure instead of putting it in the corner, `CWidgetStatsF` stacked its icon above
  the text, and `CWidgetStatsD` stacked its values instead of laying them in a row. The blocks carry
  the `flex-row` and `w-100` those layouts now need.
