Migration to v6

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

CoreUI for Vue is the Vue.js version of the CoreUI components library, so before reading this migration guide, please also check the CoreUI 5 to 6 migration guide — it covers the markup, class names, Sass architecture and design tokens this release is built on. This page covers what changes in the Vue API.

Dependencies

  • Breaking Popper is gone; positioning runs on Floating UI. @popperjs/core is replaced by @floating-ui/dom. Update your lockfile — nothing else is required for the default configuration.
  • Breaking popperConfig is now floatingConfig on CDropdown, CPopover and CTooltip, and it takes a Floating UI ComputePositionConfig instead of a Popper one. The popper boolean prop stays and still switches static positioning.
  • Breaking 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.
  • Breaking The exported composables are renamed: usePopper → useFloating, useDropdownWithPopper → useDropdownWithFloating.

New components

  • CCombobox — a .form-control toggle over a popup list box: the options are CListBoxOption children or an items array, the selection is single or multiple, the panel takes an optional search field with searchNormalize, and name submits the value through a hidden select that takes part in form validation. Ships with the useCombobox() composable.

  • 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 event vocabulary of their own: show, close and close-prevented on both, where the legacy pair says hide. CModal and COffcanvas are deprecated and may be removed in v7 — prefer CDialog and CDrawer for new work. Pick one per surface; they are independent components.

  • CListBox — the ARIA listbox pattern as a component: single, multiple or no selection, sections, descriptions, link options, an empty state, typeahead and an activeDescendant mode that keeps the focus in your own input. Ships with CListBoxOption, CListBoxSection, CListBoxEmpty, the header parts (CListBoxHeader, CListBoxSelectAll, CListBoxTitle, CListBoxSubtitle) and the useListBox() composable.

  • 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() composable. See the Menu docs.

  • 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 list boxes and four buttons that move values between them, with a select-all header and counter per list, an optional search, a one-way mode and an announcement of every move through announce(). Ships with the useTransfer() composable.

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.

  • Breaking 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.

  • Breaking 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

Breaking 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 runtime validator lists them, so color="dark" and color="light" warn in the console instead of passing silently.

  • 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.

  • Breaking 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. <CButton color="primary" /> becomes <CButton theme="primary" />, and <CButton color="primary" variant="outline" /> becomes <CButton theme="primary" variant="outline" />. 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.

  • Breaking 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.

  • Breaking The bare theme colour tokens are gone. --cui-primary, --cui-secondary, --cui-success, --cui-info, --cui-warning, --cui-danger and --cui-inverse no longer exist. 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 the -base slot: --cui-primary-base is what --cui-primary was, the 100–900 scale is mixed from it, and -bg (the action background) points at it. Rename reads one to one, in custom CSS and in the inline styles the examples use to retint a component:

    - <CTooltip :style="{ '--cui-tooltip-bg': 'var(--cui-primary)' }" />
    + <CTooltip :style="{ '--cui-tooltip-bg': 'var(--cui-primary-base)' }" />
    
    - :root { --cui-primary: #6f42c1; }
    + :root { --cui-primary-500: #6f42c1; }

    No component prop is affected — theme="primary" is unchanged.

  • Breaking 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:

    - :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)' }" />
  • Breaking 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

Breaking 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 lower than they first did: .bg-body-secondary renders --cui-bg-3 and .bg-body-tertiary --cui-bg-2, the v5 stops one rung apart, while .text-body-secondary stays --cui-fg-2 and .text-body-tertiary --cui-fg-3 — tertiary text was the lighter of the two in v5 and still is.

  • Breaking The ladder is three Sass maps, not a row of scalars. $theme-bgs, $theme-fgs and $theme-borders replace $bg-body, $bg-1 … $bg-4, $fg-body, $fg-1 … $fg-4, $border-subtle, $border-muted and $border-emphasized. Each map lives in scss/_theme.scss in the defaults() shape, :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:

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

Sass options

  • Breaking $enable-important-utilities is gone with the !important suffix. Utilities are emitted in the last cascade layer, so they already beat every component and modifier without it, and no switch brings the suffix back. The one case it covered is your own unlayered CSS, which beats every layer: move that CSS into @layer custom and utilities win again.
  • Breaking $enable-negative-margins is gone with the .m*-n* utilities. Two negative inline steps ship unconditionally — .me--1, .me--2, .ms--1 and .ms--2 — from the $negative-spacers map; add a key there to extend the set. The block margins have no negative form.

Breakpoints

  • Breaking The xxl breakpoint prop is 2xl, and the three largest breakpoints move. lg is 1024px (was 992), xl 1280px (was 1200) and the top tier is 2xl at 1536px (was xxl at 1400); sm and md are unchanged. A prop whose name starts with a digit needs the shorthand or the colon form in a template — <CCol :2xl="6">, <CContainer 2xl> — and a quoted key in an options object.

    v5v6
    <CCol xxl="6"><CCol :2xl="6">
    <CRow :xxl="{ cols: 3 }"><CRow :2xl="{ cols: 3 }">
    <CContainer xxl><CContainer 2xl>
    <CPlaceholder :xxl="4"><CPlaceholder :2xl="4">
    v-c-placeholder="{ xxl: 4 }"v-c-placeholder="{ '2xl': 4 }"
    <CDropdown :alignment="{ xxl: 'end' }"><CDropdown :alignment="{ '2xl': 'end' }">
    <CDialog fullscreen="xxl">, <CModal fullscreen="xxl">fullscreen="2xl"
    <CDrawer responsive="xxl">, <COffcanvas responsive="xxl">responsive="2xl"
    <CHeader container="xxl">, <CNavbar container="xxl">container="2xl"
    <CNavbar expand="xxl">expand="2xl"
    <CListGroup layout="horizontal-xxl">layout="horizontal-2xl"
    <CTable responsive="xxl">responsive="2xl"
  • Responsive placement reads the new scale. placement="top 2xl:bottom" on CMenu, CPopover and CTooltip replaces the xxl: prefix, and lg:/xl: switch at 1024px and 1280px.

  • The exported Breakpoints type ends in '2xl' instead of 'xxl'.

  • Size variants that spell xxl are not breakpoints and keep their name — CIcon’s size="xxl" among them.

Grid and sizing

  • Breaking 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.

  • New: .w-1 … .w-12, one $spacer per step from the $sizes map, alongside the percentage widths — see the width utilities.

  • Breaking $grid-gutter-width is $grid-gutter-x, and $grid-gutter-y joins it. make-row() takes both (make-row($gutter-x, $gutter-y)) and writes them to --cui-gutter-x / --cui-gutter-y; the row gutter defaults to 0 as before, so nothing moves 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. CRow’s xs/sm… gutter values follow the new spacing scale (see A denser spacing scale).

Z-index

  • New keys in $zindex-levels: menu, drawer, dialog, on the same values as dropdown, offcanvas and modal (1000, 1045, 1055). Each emits a --cui-z-* token and a .z-* utility, and CMenu, CDrawer and CDialog read their own key instead of the legacy component’s. The legacy keys stay; both spellings of a pair always resolve to one number.

Typography

  • Breaking $font-sizes sets a line height per stop instead of one value for all headings. xs is 1.25, sm … lg the body leading (--cui-body-line-height, 1.5), 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, and the small stops read at body leading instead of 1.2. The sizes are unchanged. Headings follow their stop through --cui-line-height-*: h5 (lg) and h4 (xl) get taller lines, h1 (4xl) a tighter one. $headings-line-height still feeds --cui-heading-line-height for anything that reads it directly.

Shadows

  • Breaking Every shadow is a stack of two to four layers with a negative spread instead of one wide blur: a tight layer for contact, wider ones for ambient light, each at 4–9 % alpha, so a card reads as lifted rather than smeared. .shadow-xs and --cui-box-shadow-xs are new (one thin layer). The inset shadow reads 8 % in the light scheme and 26 % in the dark one, and --cui-shadow-strength in the dark scheme is 2.4 (was 2.5). The knobs are unchanged: --cui-shadow-color and --cui-shadow-strength on :root, .shadow-{color} and .shadow-opacity-* on the element, $shadows in Sass — a custom entry is now a comma-separated list of layers.

Components

The visible family

  • visible is deprecated in favour of show / v-model:show 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. One prop now covers both needs:

    • show passed one-way seeds the starting state, and the component keeps managing itself from there.
    • v-model:show hands the state to your component. Close and trigger gestures (Escape, a backdrop or outside click, the dismiss button, hover, the autohide timer) then only emit update:show, and nothing moves until you flip the value.
    • update:show — emitted with the requested state either way.

    visible keeps its old behaviour unchanged, is ignored when 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 show / v-model:show in place of its visible, which was a seed the submenu silently overwrote on hover; a submenu can be controlled now.

  • CNavGroup moves to show / v-model:show. With a v-model:show (or a bare @update:show listener) the group renders what the prop says and the toggler only emits the request; without a listener a bare show is the default-open seed and nothing freezes. visible, v-model:visible and update:visible stay as deprecated aliases — update:visible keeps firing alongside update:show — and the togglerContent slot receives { show } (with visible still present).

Controlled value props

Breaking 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 emits, and nothing moves until you feed the change back — a bare value prop without its listener freezes the control. Code that passed one as an initial value migrates to the new default* twin, which seeds the state once, on mount:

componentcontrolled propinitial-value twin
CAutocomplete, CMultiSelectvaluedefaultValue
CButton (toggle mode)activedefaultActive
CChipselecteddefaultSelected

Props that carry a real v-model binding need no twin, and none was added: modelValue on CRangeSlider, CRating, CStepper and COneTimePassword, showPassword on CPasswordInput, openItemKeys on CAccordion, show on the visible family, and date / time / startDate / endDate on the pickers and the date-time inputs all seed the starting state when passed one-way, and hand the state over only once you bind them with v-model.

  • A bare YYYY-MM-DD string is read in the local timezone. Date.parse treats it as UTC midnight, so <CCalendar start-date="2026-07-14" /> landed on the 13th west of Greenwich and at the local offset east of it. Every Vue component that takes a date string is affected — CCalendar, CDatePicker, CDateRangePicker, CDateInput. The vanilla and React editions already read it this way, so this closes a divergence rather than opening one.

  • Breaking The pickers and the date-time inputs drop their default* twins — defaultDate on CDatePicker and CDateInput, defaultTime on CTimePicker and CTimeInput, defaultStartDate / defaultEndDate on CDateRangePicker, and defaultValue on COneTimePassword. Pass the value prop one-way instead: <CDatePicker date="2026-07-14" /> seeds the picker, which keeps managing itself. CDateInput and CTimeInput follow the same rule now — a one-way date / time no longer freezes the field; only a v-model binding hands the state over.

  • 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 the change event.

  • Form controls answer a native form reset the same way, bound ones included. CAutocomplete, CChipInput, CCombobox, CDateInput, CDatePicker, CDateRangeInput, CDateRangePicker, CFormRange, CMultiSelect, COneTimePassword, CRangeSlider, CRating, CTimeInput and CTimePicker with a bound value used to keep it on a reset; they now ask for the value they started with through the events that report their value (change for CAutocomplete and CMultiSelect, update:modelValue for CFormRange, the update and change events for the rest) 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. A reset brings CRating, CRangeSlider and CFormRange back to the value they started with rather than the current prop, COneTimePassword emits change only when the reset changes the code, and CStepper keeps the validation marks of a form reset a listener cancels.

  • CButton: a toggle button’s active is controlled when set — a click only emits click 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

Deprecated One validation-state prop replaces the valid and invalid booleans on every form control, field and picker, and on CFormFeedback. validation-state="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 validation-state is set; CCheckbox, CChipInput, CCombobox, CDateInput, CDateRangeInput, CNumberInput, CRadio, CSwitch and CTimeInput take validation-state only. Four side effects worth knowing:

  • feedback follows the state: it renders only while validation-state is set, styled after it, and describes the control in every state, not only the invalid one.
  • Every control that reads validation-state="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.
  • Breaking validation-state="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 emits update:validationState with undefined when the user changes its value or the form is reset; bind it with v-model:validation-state (with the deprecated invalid, set it to false on that event instead), or a form you gave an invalid state stays blocked until you clear it yourself. See Validation.
  • Breaking 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. 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 a validate listener, 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 feedback-invalid 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, feedback-invalid and feedback-valid get generated ids, and the control lists them in its own aria-describedby after the one you pass: the text always, feedback while validation-state is set, feedback-invalid only while the control is invalid and feedback-valid only while it is valid, whether that state comes from validation-state 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 validation-state 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. CFormControlWrapper and CFormControlValidation replace their undocumented describedby prop with description-id, the prefix the ids are built from (<description-id>-label, -text, -feedback, -feedback-invalid and -feedback-valid).

CAccordion

  • Breaking 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.

  • Breaking 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.

  • Breaking 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.

  • Breaking The open items follow the controlled / uncontrolled split the rest of the library uses. activeItemKey fed the initial state, 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:

    • openItemKeys passed one-way is the initial value, where activeItemKey maps — the accordion manages the open items from there.
    • v-model:open-item-keys hands them to your component, which then has to feed each change back.
    • change — emitted with the new list either way.

    They are named for what an accordion holds. active is the vocabulary of a selection — one item out of a set, which is what tabs have — 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: shown / hidden events 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.

  • Breaking 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 <CCloseButton /> 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.
  • Breaking 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.
  • Breaking 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 to 'solid'; v6 has no other alert variant.
  • Breaking 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.
  • Breaking 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.
  • Breaking --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.
  • Breaking .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.

  • Breaking The options list of both is a <CListBox>, so the panel is built from list-box-* classes: list-box, list-box-header, list-box-options, list-box-option (+ -indicator, -label, -description), list-box-section (+ -label), list-box-empty and list-box-select-all, with the selected, active and indeterminate state classes. The combobox-* option classes and the former autocomplete-dropdown / autocomplete-option(s) and form-multi-select-dropdown* / -options* / -option* / -all* / form-multi-selected classes are gone, along with their Sass variables and CSS custom properties — theme the list through --cui-list-box-* and the panel around it through --cui-combobox-*. This markup is generated by the components, so only custom CSS targeting the menu internals needs updating.

  • Breaking role="listbox" is on .list-box-options, not on the panel, and a group is a .list-box-section rather than 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.

  • Breaking selectAllStyle and optionsGroupsStyle are removed from CMultiSelect. One marker answers for the whole list: indicator reaches the options, the group labels and the select-all button alike.

  • The show and hide events fire once per transition, and neither fires on mount. CMultiSelect used to fire hide every time the menu opened.

  • Breaking 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. @change 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, v-model:input-value controls it, and the new update:inputValue event reports every change, picks included, while @input reports the search the options are filtered by. Enter on text that names no option no longer reports it through @change. From @change 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 allow-only-defined-options is off, keeps it out of the Tab order, and leaves the focus in the field after a click.

  • Breaking CAutocomplete keeps the focus in its input. The arrows move a highlight the input reports through aria-activedescendant instead of moving the focus into the options, so the field keeps every keystroke — including Space, Home and End, which now belong to the caret. Options gain the active class while highlighted.

  • Breaking 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. Tab cycles through the panel and the search box without leaving them, and Escape closes the panel and hands the focus back to the control. Shift + arrows, Shift + Home/End and Ctrl/Cmd + A extend or fill the selection, and the select-all button left the arrow order for the header, where Tab reaches it.

  • Breaking 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 :global-search="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).

  • Breaking CMultiSelect’s search no longer takes an object. It takes true, false or 'external', so :search="{ external: true }" becomes search="external".

  • Breaking 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 :global-search="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, a character typed with AltGr or Option reaches the field like any other, in CMultiSelect too, and typing in the field no longer moves the highlight to the first option that starts with the typed text.

  • CCombobox and a CListBox given a text field as activeDescendant leave typing in the field to the field. The list ran its typeahead on the same keys, so a letter typed in the search field also moved the highlight to the first option starting with it, and Enter picked that option. The highlight now moves only with the arrows and the filter.

  • CAutocomplete and CMultiSelect read the typed text without the spaces around it, in the filter, the highlight, the hint, Enter 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 emits update:validationState with undefined when the user changes the selection or the form is reset, so v-model:validation-state drops the state. A native form reset now restores the selection given by defaultValue or the options’ selected. See Validation.

  • CAutocomplete blocks the submit while validationState (or the deprecated invalid) is 'invalid', and emits update:validationState with undefined when the user changes the text or the form is reset, so v-model:validation-state drops the state. The frame also shows the state a validation reports for the input, where it used to show only the state you gave. An aria-invalid attribute on the component is ignored; set validation-state instead. See Validation.

  • 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.

  • Breaking 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.

  • Breaking An unnamed CMultiSelect stays out of the form. The native select took name: props.name ?? uniqueId, so a component the page had not named still submitted its value under a key nobody chose. The attribute is written only when name is given.

  • Breaking 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.

  • Breaking 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.

  • Deprecated CMultiSelect: selection-type="tags" is "chips", and aria-tag-delete-label is aria-chip-remove-label. The selection renders chips, 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.

  • Breaking 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.

    v5v6
    .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: Backspace 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, Backspace or the cleaner is announced (aria-removed-announcement). A single select and the counter and text selections still remove the last value at once.

CAvatar

  • Breaking text-color 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: class="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.
  • Breaking 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 takes xs on CAvatar and CAvatarStack, alongside sm, md, lg, xl — the five sizes the stylesheet ships.
  • The custom properties 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

  • Breaking text-color 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: class="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.
  • Breaking 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.
  • Breaking color no longer takes a gradient. v6 removes the bg-*-gradient utilities the value mapped to.
  • Breaking 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

  • Breaking 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 class="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.

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. min-date and max-date 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 :min-date="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 first-day-of-week 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 start-date did not select the same row. The number, the picked value, the weeks of start-date, end-date, min-date and max-date, 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 disabled-dates 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 min-date and max-date 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 min-date later than the first day of its row disabled that row, and with Tuesday to Thursday first a max-date of 2026W36 disabled the row of week 36.
  • Breaking start-date-change and end-date-change 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.
  • Breaking selectAdjacementDays and showAdjacementDays are spelled selectAdjacentDays and showAdjacentDays on CCalendar and every picker built on it. The typo is fixed in the vanilla and React libraries in the same release.
  • Breaking date-hover 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.
  • Breaking 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].
  • Breaking navigation and nav-year-first 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 nav-year-first: the year comes first in locales that write it first (for example Japanese, Chinese, Korean, Hungarian or Lithuanian).
  • Breaking The navigation icon slots trade places in a right-to-left layout and render inside .calendar-nav-icon, like the built-in chevrons. In right-to-left #navPrevIcon shows on the next button and #navNextIcon on the previous one, and the double slots likewise, so give the icons for a left-to-right page and drop any mirroring you did for right-to-left. .calendar-nav-icon is a 1rem box in the icon colour; a slot that renders text or a larger icon needs its own size on that class.
  • Breaking 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", and a week row no longer carries data-coreui-date, which stays on its day cells. 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.
  • Breaking Setting calendarDate to null keeps the month on screen. v5 moved the calendar back to today; to show today, pass today’s date.
  • New: view-change fires when the calendar switches between the days, months, quarters and years views, and with the starting view each time the calendar mounts.
  • 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; it now shows as selected with an outline on a subtle background instead of the solid fill. Without calendarDate the view follows 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.
  • 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.
  • Breaking 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 Enter 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.
  • The navigation writes the full year whatever yearFormat is, and yearFormat sets only the year cells of the years view. With '2-digit' the year button above September 2026 now reads 2026 instead of 26.
  • Breaking A cell drawn by the dayCell slot no longer gets an aria-label, so its content is what a screen reader reads. Every cell slot receives the cell’s full name as 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.
  • In day selection the dayCell slot receives isInRange: false for a day among an adjacent month’s days that only fills the grid, which the range is not drawn on, as it already did for isToday. 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 the dayCell slot 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, the #dayCell slot 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.
  • Deprecated 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

  • $enable-deprecated-classes drops the v5 color modifiers from the CSS build. It defaults to true, so nothing changes unless you set it. Set it to false once your markup uses the theme classes and .alert-{color}, .btn-{color}, .btn-outline-{color}, .btn-ghost-{color}, .callout-{color}, .chip-{color} and .list-group-item-{color} leave the stylesheet. The components render theme classes now, so none of them emit those spellings — the flag only affects markup you wrote by hand. They are removed outright in v7.

CCard

  • Breaking text-color 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: class="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.
  • Breaking 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.
  • Breaking 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 class="hstack gap-4"><CLink …/><CLink …/></div>.
  • Breaking .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.
  • Breaking color no longer takes a gradient. v6 removes the bg-*-gradient utilities the value mapped to. Reach for .bg-gradient alongside the color if you want the sheen.
  • 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.
  • Breaking .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">.
  • Breaking <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 class="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 class="g-0"> + <CCol md="4"> recipe, which needed a wrapper per section and drew no divider between them.

CCarousel

  • Breaking The carousel runs on native CSS scroll snapping. The track is a scroll container, so dragging, swiping, momentum and trackpad scrolling come from the browser; the component layers on the controls, indicators, autoplay and active-slide tracking. There is no .slide class any more, and programmatic navigation animates the scroll position instead of translating slides.
  • Breaking index is renamed to activeIndex, and the carousel now follows post-mount changes — updating the prop slides to the new index.
  • Breaking touch and wrap are gone. Touch gestures are the scroll container’s own, and ends supersedes wrap: 'loop' (default) continues seamlessly where the layout allows, 'wrap' jumps to the other end, 'stop' disables the matching control at each end.
  • Breaking CCarouselItem drops direction — the scroll position, not a per-slide class, decides how a slide enters.
  • New: autoplay (turns off for good once the user interacts), playPause with pauseLabel / playLabel (WCAG 2.2.2), keyboard, center, variableWidth, and the --cui-carousel-items / -gap / -peek custom properties for multi-item layouts.

CChip, CChipSet, CChipInput

  • Breaking 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, validation-state, update:validationState 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. It renders its messages after the frame, so the component has two root nodes; reach the frame through the exposed rootRef. See Validation.

  • Breaking CChip loses the active prop. selected is the single prop that renders the .active class on any chip; the toggle behaviour, ARIA state and the filter check icon apply only with selectable or filter. Replace active with selected on a static chip.

  • Breaking 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.

  • Breaking 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.

  • Breaking 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. aria-remove-label (and aria-chip-remove-label 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 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

  • Breaking 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 the component 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.
  • Breaking .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. The show event fires when that happens — set show 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.

CContextMenu

  • New component. <CContextMenu> is the trigger area: a right-click, or Shift+F10 / the Menu 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 @contextmenu handler runs first — prevent the event there to keep both menus closed.

CDateInput, CDateRangeInput, CTimeInput

  • Breaking 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. 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.
  • Breaking A given state is a verdict on one value. validation-state="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 emit update:validationState with undefined before the change is reported, so bind the state with v-model:validation-state; 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.

  • Breaking 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, v-model:date).

  • Breaking The button-configuration props are gone — todayButton, todayButtonColor, todayButtonSize, todayButtonVariant, cancelButton*, confirmButton* and rangesButtons*. Project the content instead: the footer scoped slot receives 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().

    <CDatePicker>
      <template #footer="{ today, clear, close, isDateSelectable }">
        <CButton :disabled="!isDateSelectable(new Date())" size="sm" @click="isDateSelectable(new Date()) && today()">Today</CButton>
        <CButton size="sm" @click="clear">Clear</CButton>
        <CButton size="sm" @click="close">OK</CButton>
      </template>
    </CDatePicker>
  • Breaking CDateRangePicker: ranges is a projected region too. It is a scoped slot receiving the context, so a preset button calls setRange with its own dates; rangesButtonsColor/-Size/-Variant are gone with it. The range is startDate/endDate (one-way to seed, v-model to bind), 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.

  • Breaking 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 <CDatePicker id="delivery" /> 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.

  • Breaking 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 min-date or max-date that carries a time of day bounds the time on its day: the time selects disable the hours, minutes and seconds before min-date or after max-date 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.

  • Breaking 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.

  • Breaking 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.

  • Breaking 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.

  • Breaking The time selection is one tab stop. Tab reaches the body once, Arrow Left and Arrow Right move between the hour, minute, second and AM/PM parts and stop at the ends, and Arrow Up / Arrow Down change the focused value. Both renderings answer the same keys.

  • Breaking CTimePicker: ampm is gone. The hour cycle follows the field’s mask when it names one (HH vs hh a) and the locale otherwise.

  • Breaking closeOnSelect and previewDateOnHover are gone. A complete selection closes the panel unless a footer projects actions the user still needs.

  • Breaking indicator is gone. The button that opens the panel is always rendered; the pickerIcon slot replaces its artwork. The button that clears the value is the one you can switch off, with cleaner.

  • Breaking 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 the cleanerIcon slot, the pickerIcon slot, disabled, show / v-model:show with the show/hide events, size, and the accessible labels ariaCleanerLabel / ariaPickerLabel.

  • CDatePicker and CDateRangePicker emit view-change from their calendar, with the starting view each time the panel opens.

  • 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 events 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.
  • Breaking popperConfig → floatingConfig, see Dependencies.
  • placement takes what CMenu takes and wins over direction and alignment — logical placements (start, end), physical ones, and breakpoint prefixes such as bottom-start md:top-end. Its default is now unset, which keeps deriving the placement from direction and alignment as in v5.
  • Breaking A responsive alignment object no longer renders .dropdown-menu-{sm|md|lg|xl|2xl}-{start|end} — those classes ship only in the opt-in coreui-legacy.css. The object becomes a responsive placement instead, so the menu keeps dynamic positioning at every breakpoint; a statically positioned menu (:popper="false", or a navbar) carries the resolved placement in data-coreui-placement, which the stylesheet positions from. Custom CSS keyed on the responsive alignment classes must move to that attribute.

CFocusTrap

  • additionalContainer accepts an element, not only a ref. A ref is read when the trap activates, which is too early for a panel that is built on open.

CFooter

  • Breaking CFooter is removed. It rendered a .footer class whose nine declarations utilities already cover, so a footer is plain markup now: <footer class="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.
  • Breaking 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 (submit is emitted 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 @submit that only called checkValidity() and preventDefault() can go. The new validate event is emitted first; call preventDefault() on its argument to handle the result yourself.

CFormCheck

  • Breaking hit-area 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"><CCheckbox class="me-1" /> Text </CListGroupItem> — which is what the stylesheet is written for.

CFormFloating

  • Breaking 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.

CFormLabel

  • Breaking customClassName is removed. It was the one prop in the library that replaced the class list 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 class 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.
  • Breaking <CFormRange> renders a wrapper. The input sits inside <div class="form-range"> as .form-range-input; class, style, theme, dir and hidden land on the wrapper, which carries the tokens, while every other attribute still reaches the input. 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; :track="false" leaves it unfilled. update:modelValue fires on input (it fired on change), so a bound value follows the thumb while it moves; change still fires on release. Without v-model the range seeds from value (the native midpoint without it), and a form reset brings it back there.
  • tooltips, tooltips-format and tooltip-class-name 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>. Several handles, distance, clickable ticks and a vertical orientation are what <CRangeSlider> adds.

CHeader

  • Breaking 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 class instead.

CInputGroup

  • Breaking 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, feedback-valid or feedback-invalid props, which render it next to the control.

    <CFormField>
      <CInputGroup>
        <CInputGroupText>@</CInputGroupText>
        <CFormInput validation-state="invalid" required />
      </CInputGroup>
      <CFormFeedback validation-state="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. The click event 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.
  • Breaking 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), and the component’s own props (loading, disabledOnLoading, timeout) no longer reach the button as attributes.

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

  • Breaking 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 teleport of its own.
  • Breaking The wrapper layers are gone. .modal > .modal-dialog > .modal-content flattens to a single <dialog class="modal"> holding the header, body and footer directly. CModal rendered the wrappers for you, so markup written against the components is unchanged; custom CSS targeting those two classes is not, and the contentClassName prop goes with them.
  • Breaking teleport 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.
  • Breaking focus is gone — a modal dialog always traps focus, and the option could only have turned off something the browser does.
  • Breaking 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.
  • Breaking .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-*.
  • Breaking Animation opt-out is a class now. :transition="false" on CModal and :animation="false" on COffcanvas render .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

  • Breaking The group animation no longer writes display and height inline. The items get .collapsing for the length of the run and the stylesheet times it; where the browser supports interpolate-size, nothing is written to the element at all. Custom CSS or tests keyed on the inline style the group used to carry need updating.

COneTimePassword

  • Breaking 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.
  • Breaking 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 validation-state="invalid", and the stylesheet shows the message after the group, which it never did for a slot marked invalid. A native form reset returns a code without v-model to the value it was created with. 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.
  • Breaking COneTimePassword blocks the submit while validation-state (or the deprecated invalid) is 'invalid', and emits update:validationState with undefined when the user changes the code or the form is reset, so v-model:validation-state drops the state. 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 on the component or on a slot is no longer passed on, the hidden input sends input and change whenever the code changes, also through v-model, and a class on COneTimePassword joins the classes of the group instead of replacing them. See Validation.
  • Breaking 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

  • Breaking Renamed ariaLabelToggler to ariaToggleLabel.
  • Breaking The toggle icon is rendered by the component instead of being drawn by CSS, and is replaceable through the new showIcon / hideIcon slots. .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

  • New: shown / hidden events, emitted once the entry or exit transition has finished. show and hide keep firing when the tip starts showing or hiding, after delay.

CProgress, CProgressBar

  • Breaking thin is removed from CProgress. It rendered .progress-thin, which v6 does not ship; set height for a slimmer bar.
  • Breaking color no longer takes a gradient. v6 removes the bg-*-gradient utilities the value mapped to. Reach for .bg-gradient alongside the color if you want the sheen.

CRangeSlider

  • Breaking The slider renders the markup of the range. It is a <div class="form-range"> with one native .form-range-input per handle; class, style, theme, dir, hidden, role, a string aria-label and aria-labelledby 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.
  • Breaking 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.
  • Breaking aria-labels is aria-label, an array with one name per handle or a function (index, total) => string; a string is the attribute.
  • The slider is a group named after the field. The .form-range gets role="group" with the aria-label string 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. validation-state 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 emits update:validationState with undefined then. See Validation.
  • Breaking 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 VNode is no longer an entry, pass it as { label: h('strong', 'High') }.
  • 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 value. A slider bound with v-model whose parent ignores the update 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. validation-state colours the focus ring, and 'invalid' sets aria-invalid and blocks the submit until you drop the state; the rating asks for that with update:validationState and undefined when the user picks or clears a star or the form is reset. required makes every radio required. See 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

  • visible-change is deprecated in favour of in-viewport-change. The event 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 listened to.
  • Theme variants. A .theme-{color} class on the sidebar paints the surface, text, icons and the hover and active links from that colour’s slots.
  • color-scheme="light" joins color-scheme="dark" as the scheme switch, so a light navigation column can sit on a dark page.
  • Hover and active links read the foreground ladder instead of the pure black/white emphasis color and a Sass-computed translucent background — slightly stronger on a light sidebar, slightly weaker on a dark one. Set --cui-sidebar-nav-link-hover-bg / -active-bg on the nav for a different tint.

CSmartTable

  • Breaking 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:

    v5v6
    itemsNumbertotalItems
    tableFilter, tableFilterValueglobalFilter, globalFilterValue
    tableFilterLabel, tableFilterPlaceholderglobalFilterLabel, globalFilterPlaceholder
    :columnSorter="{ resetable: true }":columnSorter="{ resettable: true }"
    Column._style, Column._propsColumn.headerStyle, Column.headerProps
    Column._colStyle, Column._colPropsColumn.cellStyle, Column.cellProps
    Column.sorter: falseColumn.sortable: false (sorter is the comparator only)
    Column.filter: falseColumn.filterable: false (filter is the custom filter only)
    FooterItem._propsFooterItem.props
    elementCover slotloadingContent prop and slot
    sortingIcon, sortingIconAscending, sortingIconDescending slotssortNeutralIcon, sortAscendingIcon, sortDescendingIcon props and slots
    @table-filter-change@global-filter-change
    @selected-items-change@selected-change
    @select-all— (@selected-change carries the same information)
  • Breaking 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: [...] }.

  • Breaking 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.

  • Breaking 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 sorter-change payload are SorterValue[] whether or not :columnSorter="{ multiple: true }" is set — no more Array.isArray at the call site.

  • Breaking 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.

  • Breaking activePage, itemsPerPage, selected, sorterValue, columnFilterValue and globalFilterValue are controlled props. They no longer seed the state and then let the table take over: what you pass is what renders, and an interaction only emits the matching event. Use defaultActivePage, defaultItemsPerPage, defaultSelected, defaultSorterValue, defaultColumnFilterValue and defaultGlobalFilterValue to seed a table that manages itself.

  • The selection checkboxes and the cleaner have accessible names. ariaSelectAllLabel ('Select all rows'), ariaSelectRowLabel ('Select row') and ariaCleanerLabel ('Clear filters and sorting') name controls that a screen reader announced as unlabelled before.

  • itemKey identifies a row. With it set, the selection and the row keys compare item[itemKey] instead of the whole object, so a selected row survives a re-fetch of the data. The internal _id the component used to inject into every item — and leak back through row-checked and the selection payload — is gone.

  • The layout is composable. <CSmartTable> provides its state to <CSmartTableFilter>, <CSmartTableCleaner>, <CSmartTableContent>, <CSmartTablePagination> and <CSmartTableItemsPerPage>, and renders the familiar toolbar, table and pagination row only while its default slot is empty. Fill the slot and you place those parts yourself; useSmartTableContext() reads and writes the same state from a component of your own. See the smart table.

  • The state runs in a composable of its own. useSmartTable(options) takes the state props of the table — refs, getters or plain values — and returns the filtered, sorted and paginated items together with the selection and the writers, without rendering anything. Use it when the data needs a layout a table cannot give it, a list of cards or a chart. See headless.

  • The selection checkboxes render .check instead of .form-check-input. The row cells render <CCheckbox>, the select-all header cell the same bare <input class="check"> — custom CSS keyed on .form-check-input inside the table needs updating.

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 class on any element to spin your own artwork without inheriting the bordered circle.

CStepper

  • Breaking --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.

  • 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 feedback-invalid, 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().

  • stepValidationComplete is emitted before the stepper decides. A validity its handler sets with setCustomValidity() blocks the step, and isValid tells whether every control passed before the handler 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.

  • next() on the last step finishes the stepper, as finish() does: it validates the step and emits finish when it passes. It used to do nothing there.

  • 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

  • Breaking 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 caption-top>…</CTable>, and <CTable captionTop="List of users" /> becomes <CTable caption="List of users" caption-top />.
  • 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.

CToast and CToaster

  • Breaking CToaster renders a store. createToaster() creates a store of toasts outside the component tree, and <CToaster :toaster="toaster" /> renders a <CToast> per entry: add() returns the id and upserts when the id exists (a closing toast is replaced), update() rewrites in place, close() hides one or all, promise() follows an operation through loading, success and error; limit keeps the newest toasts visible and parks the rest with inert, priority: 'high' picks the assertive live region. useToaster() returns the store inside a <CToaster>. Without the toaster property the component stays the slot container it was.
  • Breaking CToast.index is removed and the close and show events no longer carry the index argument. Key your toasts by id and read it from your own state; the toaster does that for you.
  • Breaking A toast rendered by a store carries no role or aria-live. Every added or updated toast passes its title and description to announce(), which reads them as a message at the priority priority selects, so an update announces without the toast element changing role. A standalone <CToast> keeps its role="alert" and aria-live="assertive" defaults.
  • New toaster options. ariaLabel names the region landmark, enter and leave pick the edges a toast slides in from and out to, pauseOnHover stops every timer while the pointer or focus is inside the container, and stack lays the toasts out as a deck that spreads on hover. See the toaster.
  • Breaking CToaster places itself with the toast-container-{placement} classes (nine values, top|middle|bottom × start|center|end) instead of the position-fixed top-0 end-0 utility stack. Custom CSS targeting those utilities on the container needs updating.
  • 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

  • Breaking 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:
<!-- Before -->
<CWidgetStatsF color="primary" title="Users" value="87.500">
  <template #icon>
    <CIcon :icon="cilSettings" size="xl" />
  </template>
</CWidgetStatsF>

<!-- After -->
<CCard>
  <CCardBody class="d-flex flex-row align-items-center">
    <div class="me-4 fg-white bg-primary p-4">
      <CIcon :icon="cilSettings" size="xl" />
    </div>
    <div>
      <div class="fs-6 fw-semibold fg-primary">87.500</div>
      <div class="fg-2 text-uppercase fw-semibold small">Users</div>
    </div>
  </CCardBody>
</CCard>
  • The six shapes live on as blocks. 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.