My approach involves exploring three potential solutions, starting with the simplest and progressively refining it until reaching the third option. The easiest solution prioritizes readability, while the most challenging one entails using a double nested LOOP.
In this context, I identify the "common" code shared by all three solutions (I choose to keep separate definitions for possible sizes to enhance clarity):
@sm-screen:~"(min-width: 320px)";
@md-screen:~"(min-width: 720px)";
@lg-screen:~"(min-width: 1200px)";
@xs-gutter:20px;
@sm-gutter:30px;
@md-gutter:40px;
@lg-gutter:50px;
@property:padding;
//@property:margin;
It is worth noting that @property
can be either "margin
" or "padding
" by simply switching the comment.
To these variable definitions, I propose the following three scenarios:
SOLUTION 1
This represents the simplest approach. However, it leads to redundant code as each rule generates its own media query:
.side(top, right, bottom, left);
.side(@possible-values...)
{
.generate-property-loop(1, @possible-values);
}
.generate-property-loop(@var; @possible-values) when (@var <= length(@possible-values))
{
//Extract values at position @var from list @possible-values
@direction: extract(@possible-values, @var);
.@{property}.@{direction}
{
@{property}-@{direction}: @xs-gutter;
@media @sm-screen
{
@{property}-@{direction}: @sm-gutter;
}
@media @md-screen
{
@{property}-@{direction}: @md-gutter;
}
@media @lg-screen
{
@{property}-@{direction}: @lg-gutter;
}
}
.generate-property-loop((@var + 1), @possible-values);
}
SOLUTION 2
An alternative approach involves moving the media queries outside the LOOP but still necessitates explicit media query definition:
.side(top, right, bottom, left);
.side(@possible-values...)
{
.generate-property-loop(1, @possible-values, @xs-gutter);
@media @sm-screen
{
.generate-property-loop(1, @possible-values, @sm-gutter);
}
@media @md-screen
{
.generate-property-loop(1, @possible-values, @md-gutter);
}
@media @lg-screen
{
.generate-property-loop(1, @possible-values, @lg-gutter);
}
}
.generate-property-loop(@var, @possible-values, @gutter) when (@var <= length(@possible-values))
{
//Extract values at position @var from list @possible-values
@direction: extract(@possible-values, @var);
.@{property}
{
&.@{direction}
{
@{property}-@{direction}: @gutter;
}
}
.generate-property-loop((@var + 1), @possible-values, @gutter);
}
SOLUTION 3
By implementing a double nested loop, it is possible to achieve ultimate flexibility by defining both directions and screen sizes as parameters. However, this may slightly compromise readability:
.side(top, right, bottom, left);
.side(@possible-values...)
{
.generate-property-loop(1, @possible-values, @xs-gutter);
.mediaquery-loop(sm,md,lg);
}
.mediaquery-loop(@possible-screens...)
{
.generate-mediaquery-loop(1, @possible-screens);
}
.generate-property-loop(@var, @possible-values, @gutter) when (@var <= length(@possible-values))
{
@direction: extract(@possible-values, @var);
.@{property}
{
&.@{direction}
{
@{property}-@{direction}: @gutter;
}
}
.generate-property-loop((@var + 1), @possible-values, @gutter);
}
.generate-mediaquery-loop(@var, @possible-sizes) when (@var <= length(@possible-screens))
{
@sizes: extract(@possible-sizes, @var);
@screen-size: ~"@{sizes}-screen";
@gutter-size: ~"@{sizes}-gutter";
@media @@screen-size
{
.generate-property-loop(1, @possible-values, @@gutter-size);
}
.generate-mediaquery-loop((@var + 1), @possible-screens);
}