CoreUI v6 Alpha vs Bootstrap 6 Alpha: What We Took, What We Didn't, and Why
Bootstrap published its first v6 alpha on October 8. CoreUI v6 followed with its first alpha two weeks later. CoreUI has been built on Bootstrap from the start, so the obvious question is how close the two v6 lines are.
The short answer: as close as we could make them, with a list of deliberate exceptions. For every file we share with Bootstrap we follow one rule. Class names, CSS custom properties, options, events and public methods stay the same as in Bootstrap wherever we can manage it, so moving from one to the other does not mean rewriting markup. The code behind those names is a separate question. Where we can do better than Bootstrap without changing a name, we do. Where following Bootstrap would cost our users a migration step that buys them nothing, we don’t.
This post goes through all three cases: what we took, what we left out, and where we kept the name but changed what is behind it.
Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.
One difference before we start: the prefix
Bootstrap 6 writes its custom properties without a prefix in the Sass source
and adds bs- to them at build time, so its tokens come out as --bs-*. Its
data attributes (data-bs-*) and events (*.bs.*) are written into the source
as they are. CoreUI keeps the custom property prefix as a Sass variable,
$prefix, with cui- as the default, because our React, Vue and Angular
editions and the admin templates all build on it. Our tokens are --cui-*,
our data attributes data-coreui-* and our events *.coreui.*.
So “the same name” in this post means the same name after the prefix:
Bootstrap’s --bs-alert-bg is our --cui-alert-bg.
What we took
The Sass and CSS architecture
This is the biggest piece, and we took it whole:
-
Sass modules only. Customisation goes through
@useand@forward.@importis no longer supported in either library. -
The same cascade layers in the same order:
@layer colors, config, root, reboot, layout, content, forms, components, custom, helpers, utilities;Utilities sit on top and no longer need
!important, and your unlayered CSS no longer has to out-specify the library’s selectors. -
A token map per component, merged with
defaults(), so overriding one token keeps the rest. Root tokens are emitted on:root, :host, so components inside a shadow root see them too. -
The theme model.
.theme-*classes set a family of nine slots (base,fg,fg-emphasis,bg,bg-subtle,bg-muted,border,focus-ring,contrast) with the same names as in Bootstrap, and.theme-resetclears them for one part of a component. CoreUI adds two more slots,hoverandactive, for the interactive states. -
The colour model. The palette is defined in oklch, tints and shades are
color-mix()in the browser, light and dark modes resolve throughlight-dark(), and the*-rgbvariables are gone. -
The scales. The
lg,xland2xlbreakpoints move to 1024, 1280 and 1536 px (xxlis now2xl), spacing has 13 steps, radius is a numeric scale, shadows are layered, and the focus ring is drawn withoutline.
If you know how to customise Bootstrap 6, you know how to customise CoreUI v6. Component token and theme slot overrides carry over with a different prefix. The palette is the one exception, covered further down.
Native HTML
Bootstrap 6 replaced a lot of JavaScript with platform features, and we followed:
- One base on the native
<dialog>element, withshowModal(), for Dialog and Drawer. - Accordion on
<details>and<summary>. - Carousel on CSS scroll snap.
- Validation on
:user-invalid, scoped by adata-*-validateattribute on the form, in place of.was-validated. - Enter and exit transitions with
@starting-style.
Forms
The checkbox and radio classes move onto the <input> itself (.check,
.radio), and .form-select folds into .form-control. The switch is one
place where the markup differs: Bootstrap wraps the input in
<div class="switch">, CoreUI puts .switch on the input like the other two.
Range becomes a JavaScript component in both, with different option names for
its value tooltip.
JavaScript
Both libraries are written in TypeScript and ship their own type declarations.
Both replaced Popper with Floating UI, and the option has the same name,
floatingConfig. In both, show() and hide() return a promise on the
components that animate in and out, and neither ships a separate right-to-left
stylesheet any more.
What we did not take, and why
Responsive prefixes
Bootstrap 6 moves responsive and state variants from a suffix to a prefix:
.d-md-none becomes .md:d-none, .col-lg-6 becomes .lg:col-6, and
.underline-offset-2-hover becomes .hover:underline-offset-2. Its
announcement calls this a direct copy of how Tailwind does responsive.
CoreUI v6 keeps the suffixes. Responsive classes appear on almost every page of
a real application, so renaming them is the most expensive migration step there
is, and for the utilities and components CoreUI ships, the prefix and the
suffix say exactly the same thing. A colon also has to be escaped everywhere a
class name is used as a selector, in your CSS and in querySelector() alike
(.md\:d-none).
We do ask you to rename some classes in v6. Colour modifiers like
.btn-primary give way to theme classes, and for those we ship
coreui-legacy.css so you can move one page at a time. Responsive classes are
not worth that cost.
Modal, Offcanvas and Dropdown keep their names
Bootstrap 6 renamed three components and their classes. .modal, .offcanvas
and .dropdown are gone, replaced by .dialog, .drawer and .menu. These
are three of the most-used components in any Bootstrap application.
CoreUI v6 ships Dialog, Drawer and Menu with Bootstrap’s names, and keeps
Modal, Offcanvas and Dropdown next to them. Modal and Offcanvas run on the same
native <dialog> base as Dialog and Drawer. When the JavaScript starts on
v5-style <div class="modal"> markup, it builds a <dialog> inside it and
keeps the original element in the page as a display: contents wrapper, so
your references to it and your event listeners on it keep working. CSS aimed at
the old structure does not survive: the .modal-dialog and .modal-content
wrappers are gone, and state classes such as .show give way to the native
open attribute.
Modal, Offcanvas and Dropdown are deprecated in v6 and may be removed in v7, so new code should use Dialog, Drawer and Menu. Existing code keeps working while you get there.
UMD bundles and jQuery
Bootstrap 6 is ESM-only: no UMD build, no window.bootstrap global and no
jQuery support. In this alpha, CoreUI still ships UMD bundles, an ESM bundle,
per-file .js and .mjs modules, and the jQuery integration.
Many of our users load CoreUI with a plain <script> tag, often straight from
an admin template. Dropping UMD would break every one of those pages, and it
would save very little: in this alpha, the minified UMD and ESM bundles differ
by about 120 bytes gzip. Keeping the same names as Bootstrap is about names,
not about the format of the files we publish.
A lower browser floor
Bootstrap 6 supports Chrome and Edge 130, Firefox 132 and Safari 18. CoreUI v6 supports Chrome and Edge 123, Firefox 130 and Safari 18.
Our floor is set by the newest feature our code actually needs, nothing more:
light-dark() sets Chrome 123, <details name> for exclusive accordions sets
Firefox 130, and color-mix() in oklch with a grey or transparent side sets
Safari 18. That puts us level with Bootstrap on Safari and below it on Chrome
and Firefox, so a few more of your users stay supported.
A themed checkbox stays neutral until it is checked
Bootstrap’s themed checkbox paints the unchecked box in the full theme colour.
Measured on its compiled stylesheet, a .check.theme-success that is
unchecked has the same background and border colour as one that is checked;
only the tick tells them apart. A solid green square reads as “on” at a glance.
Bootstrap’s own source marks that line as something to revisit.
In CoreUI, a theme class on a control means “this colour when it is on”. The unchecked box stays neutral, and the checked state carries both the fill and the tick.
A close button you can hit
Bootstrap 6 sizes .btn-close at 1.25rem with no padding, a 20 px target at
the default font size. That is below the 24 px in the WCAG 2.2 AA target size
criterion, so it passes only when there is enough space around it. CoreUI’s
close button is the icon plus padding, 24 px at the default font size, and it
scales with the font.
Card body alignment
Bootstrap 6 sets align-items: stretch on .card-body. We tried it and
measured it in Chromium, on Bootstrap’s alpha and on ours. A logo with
height="32" went from 80×32 to 278×32 px, an avatar in a row went from
64×26 to 64×105 px, chips and links stretched to the full width of the card,
and a button in .card-body.align-items-center lost its centering. We left it
out.
Components we did not port
- Toggler. It toggles classes or attributes on arbitrary elements from data attributes. That fits vanilla JavaScript, but CoreUI also ships React, Vue and Angular editions, where state lives in the component, and the plugin has no sensible counterpart there.
- Datepicker. Bootstrap’s wraps Vanilla Calendar Pro. CoreUI has its own Calendar, Date Picker, Date Range Picker, Date Input and Time Picker, with no third-party dependency; the only peer dependency of the package is Floating UI.
- Stepper. Bootstrap’s is a static, CSS-only progress indicator. Ours is an interactive wizard with validation between steps.
- Not in this alpha: the
.btn-textand.btn-styledbutton variants, theaccenttheme colour,.proseand NavOverflow. If you rely on one of them coming from Bootstrap 6, it does not exist in CoreUI v6 yet.
Same name, different implementation
These are the cases our users never see in their markup, because nothing about the API changed.
Each element knows its own direction
Bootstrap 6 decides whether a component is right-to-left by reading the dir
attribute of <html>. CoreUI reads the computed direction where the
component sits in the page. A left-to-right widget inside an Arabic page, or a
right-to-left section inside an English one, positions its menus and tooltips
the right way.
Tooltip opacity that does not fade the text
Both libraries have the same --tooltip-opacity token with the same default,
.95. Bootstrap applies it to the whole element, which also fades the text and
lowers its contrast. CoreUI mixes it into the background colour only, so the
tooltip is just as translucent and the text keeps full contrast.
Accordion animation in every supported browser
Animating <details> with CSS alone needs interpolate-size, which not every
browser in either library’s support range has. Bootstrap’s own migration guide
says a collapse opens and closes instantly there, and names Firefox as the one
to watch. CoreUI’s Accordion checks for interpolate-size and animates with
JavaScript where it is missing, so the transition looks the same everywhere.
Collapse goes one step further with an option for hidden="until-found", so
the browser’s find-in-page opens the section that contains the match.
A palette with one knob per colour
Bootstrap’s palette is keyed by hue (--bs-blue-500), and each tint and shade
is mixed from a value fixed at build time. CoreUI’s palette is keyed by role,
and each stop mixes from the base custom property:
--cui-primary-200: color-mix(in oklch, var(--cui-white) 60%, var(--cui-primary-500));
Set --cui-primary-500 at runtime, and the whole primary ramp, every
component that uses it and every theme slot built on it follows.
Sanitizing every component that renders HTML
In Bootstrap, the sanitize, allowList and sanitizeFn options belong to
Tooltip (and Popover, which extends it), and Tooltip makes sure they cannot be
set through data attributes. In CoreUI, close to twenty components accept HTML
you pass in and carry the same three options, from Autocomplete and Calendar
to Toaster and Transfer. So the filter lives in the shared configuration code,
and every one of them gets it.
Tooltips you can reach with the pointer
Both libraries place a tooltip a few pixels away from its trigger, and both keep it open while the pointer moves onto it. In Bootstrap the pointer has to cross that gap over the arrow. In CoreUI an invisible strip covers the whole edge, so the pointer can cross anywhere.
What we have not synced yet
Bootstrap will publish more alphas, and we follow each one. A few things from the first alpha are not in ours yet, swipe-to-dismiss on the Drawer among them. Each difference we keep on purpose is written down with its reason, and it changes only when that reason does.
Moving from Bootstrap 6 to CoreUI v6
If you already use the Bootstrap 6 alpha, most of the move is mechanical:
- Replace the prefix:
--bs-with--cui-,data-bs-withdata-coreui-, and.bs.with.coreui.in event names. - Turn responsive and state prefixes back into suffixes:
.md:d-nonebecomes.d-md-none. - Move palette overrides from hue names to role names:
--bs-blue-500becomes--cui-primary-500. - Rename the few component classes that differ, for example
.form-adornto.form-control-groupand.chip-dismissto.chip-remove. - Keep
.dialog,.drawer,.menu, the theme classes and your component token overrides as they are.
The CoreUI package also ships an Agent Skill for exactly this move,
coreui-from-bootstrap. It lists the classes and options that differ, so a
coding agent can do the mechanical part for you.
The v6 documentation
and the migration guide
cover the rest.



