Skip to content

@for

@for iterates over an array or object and produces CSS for each item. It runs at compile time; the output is standard CSS.


@for(item, target) {
/* CSS for each iteration */
}

The iteration value is accessed with ${item} and object item fields with ${item.key}. Arithmetic, string concatenation, and compile-time var() access are supported inside ${...}.


@for(i, [1, 2, 3]) {
.w-${i} { width: ${i * 10}px; }
}

Generated CSS:

.w-1 { width: 10px; }
.w-2 { width: 20px; }
.w-3 { width: 30px; }

Can also be written with range syntax:

@for(i, [1-4]) {
.p-${i} { padding: ${i * 4}px; }
}

--scale: [1-3];
--space: 4px;
@for(i, var(--scale)) {
.m-${i} {
margin: ${i} * var(--space);
}
}

Generated CSS:

.m-1 { margin: 4px; }
.m-2 { margin: 8px; }
.m-3 { margin: 12px; }

When array items are objects, fields are accessed with ${item.key}.

--breakpoints: [
{ name: sm; size: 640px },
{ name: md; size: 768px },
{ name: lg; size: 1024px }
];
@for(bp, var(--breakpoints)) {
@media (min-width: ${bp.size}) {
.container-${bp.name} {
max-width: ${bp.size};
}
}
}

Generated CSS:

@media (min-width: 640px) {
.container-sm { max-width: 640px; }
}
@media (min-width: 768px) {
.container-md { max-width: 768px; }
}
@media (min-width: 1024px) {
.container-lg { max-width: 1024px; }
}

This pattern is the most typical example of generating at-rules inside @for. A separate @media block is opened on each iteration.

@for can also be used when generating nested selector blocks:

--variants: [sm, md];
@for(size, var(--variants)) {
.card-${size} {
.title {
font-size: 14px;
}
}
}

Generated CSS:

.card-sm .title {
font-size: 14px;
}
.card-md .title {
font-size: 14px;
}

When @for(key, var(--obj)) is used, the iteration value becomes the object's keys. The value of that key is accessed with var(--obj[${key}]).

--alignContent: { normal: normal; start: flex-start; end: flex-end; };
@for(key, var(--alignContent)) {
.content-${key} {
align-content: var(--alignContent[${key}]);
}
}

Generated CSS:

.content-normal { align-content: normal; }
.content-start { align-content: flex-start; }
.content-end { align-content: flex-end; }

Part of the variable name inside var(...) can also be generated via a placeholder:

--themes: { light: light; dark: dark; };
--colors-light: { bg: #f8f9fa; };
--colors-dark: { bg: #0f1117; };
@for(t, var(--themes)) {
.theme-${t} {
--c-bg: var(--colors-${t}.bg);
}
}

In this example var(--colors-${t}.bg) resolves on each iteration as var(--colors-light.bg) and var(--colors-dark.bg). The same placeholder variable name generation can also be used inside @fun.

@for can also be used inside an object variable body to generate keys:

--factor: 2;
--range: [1-4];
--spacing: {
@for(i, var(--range)) {
${i}: ${i * var(--factor)}px;
}
};

This support is only for the @for(...) { key:value; } form inside object body; @if and @fun are not supported inside object body.


The loop index can be accessed with the (item, ind) form. Index starts from 0.

--colors: [red, blue, green];
@for((color, ind), var(--colors)) {
.color-${ind} {
background: ${color};
}
}

Generated CSS:

.color-0 { background: red; }
.color-1 { background: blue; }
.color-2 { background: green; }

Using index with nth-child:

--items: [card, panel, box];
@for((name, ind), var(--items)) {
.list:nth-child(${ind + 1}) {
content: '${name}';
}
}

Generated CSS:

.list:nth-child(1) { content: 'card'; }
.list:nth-child(2) { content: 'panel'; }
.list:nth-child(3) { content: 'box'; }

@for blocks can be nested.

--x: [1-2];
--y: [1-3];
@for(i, var(--x)) {
@for(j, var(--y)) {
.m-${i}-${j} {
padding: ${i * j}px;
}
}
}

Generated CSS:

.m-1-1 { padding: 1px; }
.m-1-2 { padding: 2px; }
.m-1-3 { padding: 3px; }
.m-2-1 { padding: 2px; }
.m-2-2 { padding: 4px; }
.m-2-3 { padding: 6px; }

String concatenation alternatives in selector:

.m-${i}-${j} /* .m-1-1 */
.m-${i+'-'+j} /* .m-1-1 — same result */

For details on arithmetic and string expressions, go to the Expression Engine page.


Top-level variable definitions can be generated inside a @for block. Generated variables behave like normal --name: value;.

@for(i, [100, 200, 300]) {
--duration-${i}: ${i}ms;
}

Generated variables:

--duration-100: 100ms;
--duration-200: 200ms;
--duration-300: 300ms;

These variables can then be used normally:

@for(i, [100, 200, 300]) {
--duration-${i}: ${i}ms;
}
.fade-100 { transition: opacity var(--duration-100) ease; }
.fade-200 { transition: opacity var(--duration-200) ease; }

Generated CSS:

:root {
--duration-100: 100ms;
--duration-200: 200ms;
--duration-300: 300ms;
}
.fade-100 { transition: opacity var(--duration-100) ease; }
.fade-200 { transition: opacity var(--duration-200) ease; }

If array items carry units, they can be multiplied by a unitless multiplier.

--scale: [1px-3px];
--factor: 4;
@for(i, var(--scale)) {
.space-${i} {
margin-top: ${i} * var(--factor);
}
}

Generated CSS:

.space-1px { margin-top: 4px; }
.space-2px { margin-top: 8px; }
.space-3px { margin-top: 12px; }

@if can be used inside a @for block. The condition is evaluated separately on each iteration.

--sizes: [sm, md, lg];
@for(size, var(--sizes)) {
@if(${size} === 'sm') {
.btn-${size} { padding: 4px 8px; }
}
elseif(${size} === 'md') {
.btn-${size} { padding: 8px 16px; }
}
else {
.btn-${size} { padding: 12px 24px; }
}
}

Generated CSS:

.btn-sm { padding: 4px 8px; }
.btn-md { padding: 8px 16px; }
.btn-lg { padding: 12px 24px; }