@each
@each iterates over a list or map. Use it to generate utility classes, theme tokens, and per-variant rules from a single data definition. Combined with @use and modules, it lets a 20-line map produce hundreds of consistent CSS rules.
Generate utilities and component variants from maps
EXAMPLE
@use 'sass:map';
// 1) @each over a map of tokens -> spacing utilities
$space: (
'xs': .25rem,
'sm': .5rem,
'md': 1rem,
'lg': 2rem,
'xl': 4rem,
);
@each $name, $value in $space {
.p-#{$name} { padding: $value; }
.px-#{$name} { padding-inline: $value; }
.py-#{$name} { padding-block: $value; }
.m-#{$name} { margin: $value; }
.mx-#{$name} { margin-inline: $value; }
.my-#{$name} { margin-block: $value; }
.gap-#{$name} { gap: $value; }
}
// 2) @each over a nested map -> button variants
$buttons: (
'primary': ('bg': #2563eb, 'fg': white),
'secondary': ('bg': #e2e8f0, 'fg': #0f172a),
'danger': ('bg': #dc2626, 'fg': white),
'ghost': ('bg': transparent, 'fg': #2563eb),
);
@each $name, $colors in $buttons {
.btn-#{$name} {
background: map.get($colors, 'bg');
color: map.get($colors, 'fg');
padding: .5rem 1rem;
border-radius: 6px;
transition: background .15s ease;
&:hover { background: darken(map.get($colors, 'bg'), 8%); }
}
}
// 3) @each over a list (no keys, just values)
$priorities: 'low', 'medium', 'high', 'urgent';
$priority-colors: ('low': #64748b, 'medium': #2563eb, 'high': #f59e0b, 'urgent': #dc2626);
@each $p in $priorities {
.priority-#{$p} {
color: map.get($priority-colors, $p);
}
}
// 4) Generate responsive utilities
$breakpoints: ('sm': 480px, 'md': 768px, 'lg': 1024px, 'xl': 1280px);
@each $bp-name, $bp-value in $breakpoints {
@media (min-width: $bp-value) {
@each $name, $value in $space {
.#{$bp-name}\:p-#{$name} { padding: $value; }
.#{$bp-name}\:m-#{$name} { margin: $value; }
}
}
}
// Now .md\:p-md applies at min-width: 768px.
// 5) Generate colour palettes
$colors: (
'blue': (50: #eff6ff, 500: #2563eb, 900: #1e3a8a),
'red': (50: #fef2f2, 500: #dc2626, 900: #7f1d1d),
'gray': (50: #f8fafc, 500: #64748b, 900: #0f172a),
);
@each $color, $shades in $colors {
@each $shade, $value in $shades {
.text-#{$color}-#{$shade} { color: $value; }
.bg-#{$color}-#{$shade} { background-color: $value; }
.border-#{$color}-#{$shade} { border-color: $value; }
}
}
// 6) Iterate map with multiple values
$social: (
'twitter': (color: #1da1f2, url: 'https://twitter.com'),
'github': (color: #181717, url: 'https://github.com'),
'linkedin': (color: #0a66c2, url: 'https://linkedin.com'),
);
@each $name, $info in $social {
.social-#{$name} {
color: map.get($info, color);
&:hover { color: lighten(map.get($info, color), 10%); }
&::after { content: ' (' + map.get($info, url) + ')'; }
}
}
// 7) Combine with @for for indexed iteration
$grid-cols: 12;
@for $i from 1 through $grid-cols {
.col-#{$i} { grid-column: span $i; }
}
// 8) Patterns to internalise
// - Map = source of truth for design tokens
// - @each generates the CSS from the map; one place to update
// - Nest @each loops to cross-product (breakpoint x spacing)
// - Avoid generating thousands of unused rules; tree-shake with purgecss/tailwindcss
// - Use #{} for interpolation in selector names
// 9) Pitfalls
// - Generating massive CSS from huge maps -> bundle bloat
// - Forgetting @use 'sass:map' before map.get
// - Spaces inside #{} sometimes leak into selectors
// - Treating maps as ordered (they are, but rely on declaration order, not key sort)
Why it matters
A single SCSS map of tokens + an @each loop generates the consistent CSS your design system needs. The map IS the design system; the loop is the implementation. Change the token; the entire codebase updates — no find-and-replace, no drift between Figma and code.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
@each $name, $color in (primary #04AA6D, danger #c00) {
.text-\#{$name} { color: $color; }
}
Try it Yourself »
Exercise
Loop over a list.
$color in red, green, blue { /* … */ }
Starts with @ea.
Discussion
Loading…