I want to display some textual contents with a constant line width (or rather, a maximum line width), regardless of the available width. If that was my only requirement, I would set width: 30ex; (or rather max-width: 30ex;).
However, I also want to let the contents be fragmented in multiple columns, in order to make better use of the available width. Each column must have the exact line width that I want (or rather, be no wider than it), e.g. 30ex, and the number of columns depends on available width. So instead of setting width or max-width, I should do something like column-width: 30ex;. But the column-width property only specifies a minimum width for columns: after the number of columns is determined, columns are widened so as to fill all available width.
How can I prevent the columns from being widened? How can I make their width constant (or constrained by some upper bound)? Ideally, without modifying the HTML code.
Minimal reproducible example:
body {
margin: 0;
padding: 0;
}
#my-columns {
column-gap: 1em;
column-width: 30ex;
text-align: justify;
}
<html>
<head>
</head>
<body>
<div id="my-columns">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras gravida
rhoncus ligula eu facilisis. Etiam condimentum posuere felis elementum
ullamcorper. Maecenas lectus neque, volutpat tristique lorem eu, bibendum
viverra arcu. Suspendisse interdum velit ac neque porttitor, eget semper
orci suscipit.
</div>
</body>
</html>
Compute the number of columns that can fit in the available width (that is, the width of the parent), then set the columned element’s width to be this number multiplied by the desired column width plus gaps.
The math goes as follows:
column_count = floor( (available_width + gap) / (column_width + gap) )
width = column_count × (column_width + gap) − gap
To implement it in CSS , the two key ingredients are:
calc() function, which allows to perform mathematical computations;cqw length unit, which allows to compute lengths relative to the width of a containing element (this is a pretty recent addition to CSS, supported by major browsers since 2023); in our case, 100cqw will be the width of the parent of the columned element; for this to work, the parent must have container-type: inline-size;.As of 2025, some bits of it are not well supported by browsers (although they are allowed by CSS specification), so we need some hacks to work around it (described in this article by Jane Ori):
tan(atan2(Y, X)) computes the division of Y by X even when these quantities are lengths (the atan2() function is well supported since 2023);@property of type <length>, converts the value to pixels; this works around the fact that computing with mixed units does not work well yet (@property is well supported since 2024).@property --avail-width {
syntax: "<length>";
initial-value: 0px;
inherits: false;
}
@property --1ex {
syntax: "<length>";
initial-value: 0px;
inherits: false;
}
@property --1em {
syntax: "<length>";
initial-value: 0px;
inherits: false;
}
body {
margin: 0; padding: 0;
container-type: inline-size;
}
#my-columns {
/* set our custom lengths here: */
--gap: var(--1em);
--column-width: calc(30 * var(--1ex));
/* computed lengths: */
--1ex: 1ex;
--1em: 1em;
--avail-width: 100cqw;
--column-count: calc(round(down, tan(atan2(var(--avail-width) + var(--gap), var(--column-width) + var(--gap))), 1));
width: calc(var(--column-count) * (var(--column-width) + var(--gap)) - var(--gap));
column-gap: var(--gap);
column-width: var(--column-width);
text-align: justify;
}
<div id="my-columns">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras gravida
rhoncus ligula eu facilisis. Etiam condimentum posuere felis elementum
ullamcorper. Maecenas lectus neque, volutpat tristique lorem eu, bibendum
viverra arcu. Suspendisse interdum velit ac neque porttitor, eget semper
orci suscipit.
</div>