Design
Add .btn-group-tabs class to the .btn-group to transform it in to segmented controls.
<div class="btn-group btn-group-tabs" role="group" aria-label="Segmented controls">
<button type="button" class="btn">Item 1</button>
<button type="button" class="btn active" aria-selected="true">Item 2</button>
<button type="button" class="btn">Item 3</button>
</div>
Small segments
Add .btn-group-tabs-sm class to wrapper or have it on a smaller size.
<div class="btn-group btn-group-tabs btn-group-tabs-sm" role="group" aria-label="Segmented controls">
<button type="button" class="btn">Item 1</button>
<button type="button" class="btn active" aria-selected="true">Item 2</button>
<button type="button" class="btn">Item 3</button>
</div>
Animated segmented controls
Add .btn-group-animation class to .btn-group-tabs wrapper.
The active button is highlighted by the pseudo-element :before attached to the .btn-group-animation container/wrapper.
To move the active button, you need to get the active button width and position relative to the left of the .btn-group-animation container, and add it to the :before pseudo-element.
You can target the :before pseudo-element directly from the script and assign the value to width and transform: translateX(...) attribute or by adding the value (like in our script example) attached to the .btn-group-animation container the CSS custome propertis / CSS variables: --btn-grp-animation-width the width value of the active button and --btn-grp-animation-offset for the active button X axis offset from the left.
<div class="btn-group btn-group-tabs btn-group-animation" role="group" aria-label="Segmented controls">
<button type="button" class="btn">Item 1</button>
<button type="button" class="btn active" aria-selected="true">Item 2</button>
<button type="button" class="btn">Item 3</button>
</div>
The functionality for this component, i.e. the script, is not provided! Example of the script for the segment controls with the option for animation as well:
document.addEventListener('DOMContentLoaded', ()=>{
// Demo for selected/active state for segment control
const segmentedModule = document.querySelectorAll('.btn-group-tabs');
if(segmentedModule.length > 0) {
segmentedModule.forEach(el => {
let children = el.querySelectorAll('.btn');
let animation = false;
let btnWidth = 0
let btnOffset = 0;
let elOffset = 0;
let elStyle = el.currentStyle || window.getComputedStyle(el);
let elPadding = parseInt(elStyle.getPropertyValue('padding-right'));
if(el.classList.contains('btn-group-animation')) {
animation = true;
for (let btn of children) {
if(btn.classList.contains('active')) {
btnWidth = btn.clientWidth;
elOffset = el.offsetLeft + elPadding;
btnOffset = btn.offsetLeft - elOffset;
el.style.setProperty('--btn-grp-animation-width', btnWidth + 'px');
el.style.setProperty('--btn-grp-animation-offset', btnOffset + 'px');
break;
}
}
}
el.addEventListener('click', e => {
let tr = e.target.closest('.btn');
if(tr) {
children.forEach(elm => {
elm.classList.remove('active');
elm.ariaSelected = false;
});
tr.classList.add('active');
tr.ariaSelected = true;
tr.blur();
if(animation) {
let trWidth = tr.clientWidth;
elOffset = el.offsetLeft + elPadding;
let trOffset = tr.offsetLeft - elOffset;
el.style.setProperty('--btn-grp-animation-width', trWidth + 'px');
el.style.setProperty('--btn-grp-animation-offset', trOffset + 'px');
}
}
});
});
}
});