htmlcssmultiple-columnscss-multicolumn-layout

How to make the column width constant in a multi-column layout? (NOT a table)


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>


Solution

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

    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):

    @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>