I am using Hugo Universal theme, but saw that code in many other themes, too. To lay out a "table" of features, they use
[params.features]
cols = 2 # Default: 3, Available values 2,3,4,6
That kinda works when resizing the page, but I would prefer to change number of columns based on the page width.
I was thinking about creating a CSS variable and set it from multiple
@media (max-width: 991px) {
--col: 3
}
@media (max-width: 600px) {
--col: 2
}
But I can't find a way to use that var in a template. Is it possible? Maybe with some JS code?
Or another way to use 2 columns on small, 3 on medium and 4 on large?
Update 8/13/2025
With the help of @SirPeople I almost got it. "Last" issue - what is this mysterious ::before empty grid cell in the image below? There is ::after after the last element, but that doesn't bother me too much. The real question is - how to get rid of it?
Update 2:
Solved with the help from before:: pseudo-element span all columns in grid
This does not seem feasible by default
So if we inspect the code from Hugo Universal Theme. We can see how the parameters get injected into the theme itself:
{{ $elements := default 3 .Site.Params.features.cols }}
So the problem here is that Hugo being a static site builder, it is difficult to reverse it to inject the width of the screen as a parameter to modify $element variable.
Do you still want to work around it?
You could work around it modifying Hugo template, but this may have some risks. But basically, wrapping the rows with a grid, and then using media queries to set the col value in there. Like:
<section class="bar background-white">
<div class="container">
<div class="row features-grid">
{{ $features := sort .Site.Data.features "weight" }}
{{ range $i, $element := $features }}
<div class="col feature-item">
<div class="box-simple">
{{ with $element.url }}<a href="{{ $element.url }}">{{ end }}
<div class="icon">
<i class="{{ $element.icon }}"></i>
</div>
{{ with $element.url }}</a>{{ end }}
<h3>{{ $element.name | markdownify }}</h3>
<p>{{ $element.description | markdownify }}</p>
</div>
</div>
{{ end }}
</div>
</div>
</section>
And then:
CSS
:root {
--col: 3; /* Let's use Hugo default :D */
}
@media (max-width: 991px) {
:root {
--col: 3;
}
}
@media (max-width: 600px) {
:root {
--col: 2;
}
}
.features-grid {
display: grid;
grid-template-columns: repeat(var(--col), 1fr);
gap: 2rem;
}
.feature-item {
display: flex;
flex-direction: column;
}
Again, this is possible, but it will require you some customization and testing of all the layouts implied here. Specially given that you are kinda ignoring bootstrap for this part