Avoid using media queries for this type of task, it's too unstable.
Think of the work involved when another flex item is added, or the text length changes, and you have to rejig those media queries.
Instead consider using the Resize Observer API.
A list of links in a flex box:
<ul class=flex_box role=list>
<li><a href="#/">Fake link 1</a></li>
<li><a href="#/">Fake link 2</a></li>
<li><a href="#/">Fake link 3</a></li>
<li><a href="#/">Fake link 4</a></li>
<li><a href="#/">Fake link 5</a></li>
</ul>
CSS:
.flex_box {
display: flex;
flex-wrap: wrap;
padding: 0;
gap: 1rem;
}
/* Optional coloring prettiness */
.flex_box > * {
background-color: #000;
color: #fff;
}
.flex_box-wrapped > .flex_item-wrapped {
background-color: #333
}
Upon wrapping, the script adds class flex_box-wrapped
to the flex container, and flex_item-wrapped
to each flex item which has wrapped.
This is achieved by testing the top position of each item against the first item.
When the browser width (or height, or font-size) changes the Resize Observer API recalculates and amends each class name accordingly.
// An IFEE here, but you can roll your own:
const flexBoxWrapDetection = (_ => {
'use strict';
const flexBoxQuery = '.flex_box';
const boxWrappedClass = 'flex_box-wrapped';
const itemWrappedClass = 'flex_item-wrapped';
// Rounded for inline-flex sub-pixel discrepencies:
const getTop = item => Math.round(item.getBoundingClientRect().top);
const markFlexboxAndItemsWrapState = flexBox => {
// Acts as a throttle,
// Prevents hitting ResizeObserver loop limit,
// Optimal timing for visual change:
requestAnimationFrame(_ => {
const flexItems = flexBox.children;
// Needs to be in a row for the calculations to work
flexBox.setAttribute('style', 'flex-direction: row');
const firstItemTop = getTop(flexItems[0]);
const lastItemTop = getTop(flexItems[flexItems.length - 1]);
// Add / remove wrapped class to each wrapped item
for (const flexItem of flexItems) {
if (firstItemTop < getTop(flexItem)) {
flexItem.classList.add(itemWrappedClass);
} else {
flexItem.classList.remove(itemWrappedClass);
}
}
// Remove flex-direction:row used for calculations
flexBox.removeAttribute('style');
// Add / remove wrapped class to the flex container
if (firstItemTop >= lastItemTop) {
flexBox.classList.remove(boxWrappedClass);
} else {
flexBox.classList.add(boxWrappedClass);
}
});
};
// Each flex box with the class .flex_box:
const flexBoxes = document.querySelectorAll(flexBoxQuery);
for (const flexBox of flexBoxes) {
markFlexboxAndItemsWrapState(flexBox);
// Listen for dimension changes on the flexbox
new ResizeObserver(entries =>
entries.forEach(entry => markFlexboxAndItemsWrapState(entry.target))
).observe(flexBox);
}
})();
There's a demo CodePen: Flex-wrap detection script which also shows how to switch between horizontal and vertical as soon as the flex box wraps or unwraps.
The demo also works with direction: rtl;
and / or flex-wrap: row-reverse;
.