Search code examples
cssloopslessmedia-queries

Less looping through array keeps appending previous value


I'm attempting to create generated spacing classes for my CSS. I have a few embedded loops to create this, and inside my outer loop, I have my media queries defined. I have a set of inner styles that are being looped through and placed inside each of the 3 media queries.

For some reason, when the media queries are being looped through, it is adding the previous variable and appending 'and' for each media query, so my page looks like this:

@media (max-width: 991px) {...}
@media (max-width: 991px) and (max-width: 767px) {...}
@media (max-width: 991px) and (max-width: 767px) and (max-width: 480px) {...}

When it should look like this:

@media (max-width: 991px) {...}
@media (max-width: 767px) {...}
@media (max-width: 480px) {...}

I've added in other CSS properties in place of the @media to see if it was an issue with @media, but it still seems to duplicate in my loop. (FYI I'm using Less version 3.0)

Less

@screen-xs-min: 480px;
@screen-sm-min: 768px;
@screen-md-min: 992px;
@screen-lg-min: 1200px;
@screen-xl-min: 1340px;

@screen-xs-max: (@screen-sm-min - 1);
@screen-sm-max: (@screen-md-min - 1);
@screen-md-max: (@screen-lg-min - 1);

@directionlist: l, b, r, t;
@directions: left, bottom, right, top;
@spacingtype: padding, margin;
@sapcingtypeabv: p, m;
@responsivevars: screen-xs-min, screen-xs-max, screen-sm-max;
@responsivevarsabv: xs, sm, md;

@maxg: 3;
.g-loop (@g) when (@g > 0) {

    @responsive: extract(@responsivevars, @g);
    @responsiveabv: extract(@responsivevarsabv, @g);

    @media (max-width: @@responsive) {

    @maxh: 2;
    .h-loop (@h) when (@h > 0) {

        @maxi: 50;
        .i-loop (@i) when (@i > -1) {

            @maxj: 4;
            .j-loop (@j) when (@j > 0) {

                @direction: extract(@directionlist, @j);
                @directionname: extract(@directions, @j);
                @type: extract(@spacingtype, @h);
                @typeabv: extract(@sapcingtypeabv, @h);

                .@{typeabv}-@{responsiveabv}-@{direction}-@{i} {
                    @{type}-@{directionname}: @i*1px !important;
                }

                .j-loop(@j - 1);
            }

            .j-loop (0) {}
            .j-loop(@maxj);

            .i-loop(@i - 5);
        }

        .i-loop (0) {}
        .i-loop(@maxi);
        .h-loop(@h - 1);
    }

    .h-loop (0) {}
    .h-loop(@maxh);
    .g-loop(@g - 1);
    } //Closing Media Query
}

.g-loop (0) {}
.g-loop(@maxg);

You can view a compiled result by using LESS Tester.

Resulting CSS

@media (max-width: 991px) {
  .m-md-t-50 {
    margin-top: 50px !important;
  }
  .m-md-r-50 {
    margin-right: 50px !important;
  }
  .m-md-b-50 {
    margin-bottom: 50px !important;
  }
  .m-md-l-50 {
    margin-left: 50px !important;
  }
  .m-md-t-45 {
    margin-top: 45px !important;
  }
  .m-md-r-45 {
    margin-right: 45px !important;
  }
  .m-md-b-45 {
    margin-bottom: 45px !important;
  }
  .m-md-l-45 {
    margin-left: 45px !important;
  }
  .m-md-t-40 {
    margin-top: 40px !important;
  }
  .m-md-r-40 {
    margin-right: 40px !important;
  }
  .m-md-b-40 {
    margin-bottom: 40px !important;
  }
  .m-md-l-40 {
    margin-left: 40px !important;
  }
  .m-md-t-35 {
    margin-top: 35px !important;
  }
  .m-md-r-35 {
    margin-right: 35px !important;
  }
  .m-md-b-35 {
    margin-bottom: 35px !important;
  }
  .m-md-l-35 {
    margin-left: 35px !important;
  }
  .m-md-t-30 {
    margin-top: 30px !important;
  }
  .m-md-r-30 {
    margin-right: 30px !important;
  }
  .m-md-b-30 {
    margin-bottom: 30px !important;
  }
  .m-md-l-30 {
    margin-left: 30px !important;
  }
  .m-md-t-25 {
    margin-top: 25px !important;
  }
  .m-md-r-25 {
    margin-right: 25px !important;
  }
  .m-md-b-25 {
    margin-bottom: 25px !important;
  }
  .m-md-l-25 {
    margin-left: 25px !important;
  }
  .m-md-t-20 {
    margin-top: 20px !important;
  }
  .m-md-r-20 {
    margin-right: 20px !important;
  }
  .m-md-b-20 {
    margin-bottom: 20px !important;
  }
  .m-md-l-20 {
    margin-left: 20px !important;
  }
  .m-md-t-15 {
    margin-top: 15px !important;
  }
  .m-md-r-15 {
    margin-right: 15px !important;
  }
  .m-md-b-15 {
    margin-bottom: 15px !important;
  }
  .m-md-l-15 {
    margin-left: 15px !important;
  }
  .m-md-t-10 {
    margin-top: 10px !important;
  }
  .m-md-r-10 {
    margin-right: 10px !important;
  }
  .m-md-b-10 {
    margin-bottom: 10px !important;
  }
  .m-md-l-10 {
    margin-left: 10px !important;
  }
  .m-md-t-5 {
    margin-top: 5px !important;
  }
  .m-md-r-5 {
    margin-right: 5px !important;
  }
  .m-md-b-5 {
    margin-bottom: 5px !important;
  }
  .m-md-l-5 {
    margin-left: 5px !important;
  }
  .m-md-t-0 {
    margin-top: 0px !important;
  }
  .m-md-r-0 {
    margin-right: 0px !important;
  }
  .m-md-b-0 {
    margin-bottom: 0px !important;
  }
  .m-md-l-0 {
    margin-left: 0px !important;
  }
  .p-md-t-50 {
    padding-top: 50px !important;
  }
  .p-md-r-50 {
    padding-right: 50px !important;
  }
  .p-md-b-50 {
    padding-bottom: 50px !important;
  }
  .p-md-l-50 {
    padding-left: 50px !important;
  }
  .p-md-t-45 {
    padding-top: 45px !important;
  }
  .p-md-r-45 {
    padding-right: 45px !important;
  }
  .p-md-b-45 {
    padding-bottom: 45px !important;
  }
  .p-md-l-45 {
    padding-left: 45px !important;
  }
  .p-md-t-40 {
    padding-top: 40px !important;
  }
  .p-md-r-40 {
    padding-right: 40px !important;
  }
  .p-md-b-40 {
    padding-bottom: 40px !important;
  }
  .p-md-l-40 {
    padding-left: 40px !important;
  }
  .p-md-t-35 {
    padding-top: 35px !important;
  }
  .p-md-r-35 {
    padding-right: 35px !important;
  }
  .p-md-b-35 {
    padding-bottom: 35px !important;
  }
  .p-md-l-35 {
    padding-left: 35px !important;
  }
  .p-md-t-30 {
    padding-top: 30px !important;
  }
  .p-md-r-30 {
    padding-right: 30px !important;
  }
  .p-md-b-30 {
    padding-bottom: 30px !important;
  }
  .p-md-l-30 {
    padding-left: 30px !important;
  }
  .p-md-t-25 {
    padding-top: 25px !important;
  }
  .p-md-r-25 {
    padding-right: 25px !important;
  }
  .p-md-b-25 {
    padding-bottom: 25px !important;
  }
  .p-md-l-25 {
    padding-left: 25px !important;
  }
  .p-md-t-20 {
    padding-top: 20px !important;
  }
  .p-md-r-20 {
    padding-right: 20px !important;
  }
  .p-md-b-20 {
    padding-bottom: 20px !important;
  }
  .p-md-l-20 {
    padding-left: 20px !important;
  }
  .p-md-t-15 {
    padding-top: 15px !important;
  }
  .p-md-r-15 {
    padding-right: 15px !important;
  }
  .p-md-b-15 {
    padding-bottom: 15px !important;
  }
  .p-md-l-15 {
    padding-left: 15px !important;
  }
  .p-md-t-10 {
    padding-top: 10px !important;
  }
  .p-md-r-10 {
    padding-right: 10px !important;
  }
  .p-md-b-10 {
    padding-bottom: 10px !important;
  }
  .p-md-l-10 {
    padding-left: 10px !important;
  }
  .p-md-t-5 {
    padding-top: 5px !important;
  }
  .p-md-r-5 {
    padding-right: 5px !important;
  }
  .p-md-b-5 {
    padding-bottom: 5px !important;
  }
  .p-md-l-5 {
    padding-left: 5px !important;
  }
  .p-md-t-0 {
    padding-top: 0px !important;
  }
  .p-md-r-0 {
    padding-right: 0px !important;
  }
  .p-md-b-0 {
    padding-bottom: 0px !important;
  }
  .p-md-l-0 {
    padding-left: 0px !important;
  }
}
@media (max-width: 991px) and (max-width: 767px) {
  .m-sm-t-50 {
    margin-top: 50px !important;
  }
  .m-sm-r-50 {
    margin-right: 50px !important;
  }
  .m-sm-b-50 {
    margin-bottom: 50px !important;
  }
  .m-sm-l-50 {
    margin-left: 50px !important;
  }
  .m-sm-t-45 {
    margin-top: 45px !important;
  }
  .m-sm-r-45 {
    margin-right: 45px !important;
  }
  .m-sm-b-45 {
    margin-bottom: 45px !important;
  }
  .m-sm-l-45 {
    margin-left: 45px !important;
  }
  .m-sm-t-40 {
    margin-top: 40px !important;
  }
  .m-sm-r-40 {
    margin-right: 40px !important;
  }
  .m-sm-b-40 {
    margin-bottom: 40px !important;
  }
  .m-sm-l-40 {
    margin-left: 40px !important;
  }
  .m-sm-t-35 {
    margin-top: 35px !important;
  }
  .m-sm-r-35 {
    margin-right: 35px !important;
  }
  .m-sm-b-35 {
    margin-bottom: 35px !important;
  }
  .m-sm-l-35 {
    margin-left: 35px !important;
  }
  .m-sm-t-30 {
    margin-top: 30px !important;
  }
  .m-sm-r-30 {
    margin-right: 30px !important;
  }
  .m-sm-b-30 {
    margin-bottom: 30px !important;
  }
  .m-sm-l-30 {
    margin-left: 30px !important;
  }
  .m-sm-t-25 {
    margin-top: 25px !important;
  }
  .m-sm-r-25 {
    margin-right: 25px !important;
  }
  .m-sm-b-25 {
    margin-bottom: 25px !important;
  }
  .m-sm-l-25 {
    margin-left: 25px !important;
  }
  .m-sm-t-20 {
    margin-top: 20px !important;
  }
  .m-sm-r-20 {
    margin-right: 20px !important;
  }
  .m-sm-b-20 {
    margin-bottom: 20px !important;
  }
  .m-sm-l-20 {
    margin-left: 20px !important;
  }
  .m-sm-t-15 {
    margin-top: 15px !important;
  }
  .m-sm-r-15 {
    margin-right: 15px !important;
  }
  .m-sm-b-15 {
    margin-bottom: 15px !important;
  }
  .m-sm-l-15 {
    margin-left: 15px !important;
  }
  .m-sm-t-10 {
    margin-top: 10px !important;
  }
  .m-sm-r-10 {
    margin-right: 10px !important;
  }
  .m-sm-b-10 {
    margin-bottom: 10px !important;
  }
  .m-sm-l-10 {
    margin-left: 10px !important;
  }
  .m-sm-t-5 {
    margin-top: 5px !important;
  }
  .m-sm-r-5 {
    margin-right: 5px !important;
  }
  .m-sm-b-5 {
    margin-bottom: 5px !important;
  }
  .m-sm-l-5 {
    margin-left: 5px !important;
  }
  .m-sm-t-0 {
    margin-top: 0px !important;
  }
  .m-sm-r-0 {
    margin-right: 0px !important;
  }
  .m-sm-b-0 {
    margin-bottom: 0px !important;
  }
  .m-sm-l-0 {
    margin-left: 0px !important;
  }
  .p-sm-t-50 {
    padding-top: 50px !important;
  }
  .p-sm-r-50 {
    padding-right: 50px !important;
  }
  .p-sm-b-50 {
    padding-bottom: 50px !important;
  }
  .p-sm-l-50 {
    padding-left: 50px !important;
  }
  .p-sm-t-45 {
    padding-top: 45px !important;
  }
  .p-sm-r-45 {
    padding-right: 45px !important;
  }
  .p-sm-b-45 {
    padding-bottom: 45px !important;
  }
  .p-sm-l-45 {
    padding-left: 45px !important;
  }
  .p-sm-t-40 {
    padding-top: 40px !important;
  }
  .p-sm-r-40 {
    padding-right: 40px !important;
  }
  .p-sm-b-40 {
    padding-bottom: 40px !important;
  }
  .p-sm-l-40 {
    padding-left: 40px !important;
  }
  .p-sm-t-35 {
    padding-top: 35px !important;
  }
  .p-sm-r-35 {
    padding-right: 35px !important;
  }
  .p-sm-b-35 {
    padding-bottom: 35px !important;
  }
  .p-sm-l-35 {
    padding-left: 35px !important;
  }
  .p-sm-t-30 {
    padding-top: 30px !important;
  }
  .p-sm-r-30 {
    padding-right: 30px !important;
  }
  .p-sm-b-30 {
    padding-bottom: 30px !important;
  }
  .p-sm-l-30 {
    padding-left: 30px !important;
  }
  .p-sm-t-25 {
    padding-top: 25px !important;
  }
  .p-sm-r-25 {
    padding-right: 25px !important;
  }
  .p-sm-b-25 {
    padding-bottom: 25px !important;
  }
  .p-sm-l-25 {
    padding-left: 25px !important;
  }
  .p-sm-t-20 {
    padding-top: 20px !important;
  }
  .p-sm-r-20 {
    padding-right: 20px !important;
  }
  .p-sm-b-20 {
    padding-bottom: 20px !important;
  }
  .p-sm-l-20 {
    padding-left: 20px !important;
  }
  .p-sm-t-15 {
    padding-top: 15px !important;
  }
  .p-sm-r-15 {
    padding-right: 15px !important;
  }
  .p-sm-b-15 {
    padding-bottom: 15px !important;
  }
  .p-sm-l-15 {
    padding-left: 15px !important;
  }
  .p-sm-t-10 {
    padding-top: 10px !important;
  }
  .p-sm-r-10 {
    padding-right: 10px !important;
  }
  .p-sm-b-10 {
    padding-bottom: 10px !important;
  }
  .p-sm-l-10 {
    padding-left: 10px !important;
  }
  .p-sm-t-5 {
    padding-top: 5px !important;
  }
  .p-sm-r-5 {
    padding-right: 5px !important;
  }
  .p-sm-b-5 {
    padding-bottom: 5px !important;
  }
  .p-sm-l-5 {
    padding-left: 5px !important;
  }
  .p-sm-t-0 {
    padding-top: 0px !important;
  }
  .p-sm-r-0 {
    padding-right: 0px !important;
  }
  .p-sm-b-0 {
    padding-bottom: 0px !important;
  }
  .p-sm-l-0 {
    padding-left: 0px !important;
  }
}
@media (max-width: 991px) and (max-width: 767px) and (max-width: 480px) {
  .m-xs-t-50 {
    margin-top: 50px !important;
  }
  .m-xs-r-50 {
    margin-right: 50px !important;
  }
  .m-xs-b-50 {
    margin-bottom: 50px !important;
  }
  .m-xs-l-50 {
    margin-left: 50px !important;
  }
  .m-xs-t-45 {
    margin-top: 45px !important;
  }
  .m-xs-r-45 {
    margin-right: 45px !important;
  }
  .m-xs-b-45 {
    margin-bottom: 45px !important;
  }
  .m-xs-l-45 {
    margin-left: 45px !important;
  }
  .m-xs-t-40 {
    margin-top: 40px !important;
  }
  .m-xs-r-40 {
    margin-right: 40px !important;
  }
  .m-xs-b-40 {
    margin-bottom: 40px !important;
  }
  .m-xs-l-40 {
    margin-left: 40px !important;
  }
  .m-xs-t-35 {
    margin-top: 35px !important;
  }
  .m-xs-r-35 {
    margin-right: 35px !important;
  }
  .m-xs-b-35 {
    margin-bottom: 35px !important;
  }
  .m-xs-l-35 {
    margin-left: 35px !important;
  }
  .m-xs-t-30 {
    margin-top: 30px !important;
  }
  .m-xs-r-30 {
    margin-right: 30px !important;
  }
  .m-xs-b-30 {
    margin-bottom: 30px !important;
  }
  .m-xs-l-30 {
    margin-left: 30px !important;
  }
  .m-xs-t-25 {
    margin-top: 25px !important;
  }
  .m-xs-r-25 {
    margin-right: 25px !important;
  }
  .m-xs-b-25 {
    margin-bottom: 25px !important;
  }
  .m-xs-l-25 {
    margin-left: 25px !important;
  }
  .m-xs-t-20 {
    margin-top: 20px !important;
  }
  .m-xs-r-20 {
    margin-right: 20px !important;
  }
  .m-xs-b-20 {
    margin-bottom: 20px !important;
  }
  .m-xs-l-20 {
    margin-left: 20px !important;
  }
  .m-xs-t-15 {
    margin-top: 15px !important;
  }
  .m-xs-r-15 {
    margin-right: 15px !important;
  }
  .m-xs-b-15 {
    margin-bottom: 15px !important;
  }
  .m-xs-l-15 {
    margin-left: 15px !important;
  }
  .m-xs-t-10 {
    margin-top: 10px !important;
  }
  .m-xs-r-10 {
    margin-right: 10px !important;
  }
  .m-xs-b-10 {
    margin-bottom: 10px !important;
  }
  .m-xs-l-10 {
    margin-left: 10px !important;
  }
  .m-xs-t-5 {
    margin-top: 5px !important;
  }
  .m-xs-r-5 {
    margin-right: 5px !important;
  }
  .m-xs-b-5 {
    margin-bottom: 5px !important;
  }
  .m-xs-l-5 {
    margin-left: 5px !important;
  }
  .m-xs-t-0 {
    margin-top: 0px !important;
  }
  .m-xs-r-0 {
    margin-right: 0px !important;
  }
  .m-xs-b-0 {
    margin-bottom: 0px !important;
  }
  .m-xs-l-0 {
    margin-left: 0px !important;
  }
  .p-xs-t-50 {
    padding-top: 50px !important;
  }
  .p-xs-r-50 {
    padding-right: 50px !important;
  }
  .p-xs-b-50 {
    padding-bottom: 50px !important;
  }
  .p-xs-l-50 {
    padding-left: 50px !important;
  }
  .p-xs-t-45 {
    padding-top: 45px !important;
  }
  .p-xs-r-45 {
    padding-right: 45px !important;
  }
  .p-xs-b-45 {
    padding-bottom: 45px !important;
  }
  .p-xs-l-45 {
    padding-left: 45px !important;
  }
  .p-xs-t-40 {
    padding-top: 40px !important;
  }
  .p-xs-r-40 {
    padding-right: 40px !important;
  }
  .p-xs-b-40 {
    padding-bottom: 40px !important;
  }
  .p-xs-l-40 {
    padding-left: 40px !important;
  }
  .p-xs-t-35 {
    padding-top: 35px !important;
  }
  .p-xs-r-35 {
    padding-right: 35px !important;
  }
  .p-xs-b-35 {
    padding-bottom: 35px !important;
  }
  .p-xs-l-35 {
    padding-left: 35px !important;
  }
  .p-xs-t-30 {
    padding-top: 30px !important;
  }
  .p-xs-r-30 {
    padding-right: 30px !important;
  }
  .p-xs-b-30 {
    padding-bottom: 30px !important;
  }
  .p-xs-l-30 {
    padding-left: 30px !important;
  }
  .p-xs-t-25 {
    padding-top: 25px !important;
  }
  .p-xs-r-25 {
    padding-right: 25px !important;
  }
  .p-xs-b-25 {
    padding-bottom: 25px !important;
  }
  .p-xs-l-25 {
    padding-left: 25px !important;
  }
  .p-xs-t-20 {
    padding-top: 20px !important;
  }
  .p-xs-r-20 {
    padding-right: 20px !important;
  }
  .p-xs-b-20 {
    padding-bottom: 20px !important;
  }
  .p-xs-l-20 {
    padding-left: 20px !important;
  }
  .p-xs-t-15 {
    padding-top: 15px !important;
  }
  .p-xs-r-15 {
    padding-right: 15px !important;
  }
  .p-xs-b-15 {
    padding-bottom: 15px !important;
  }
  .p-xs-l-15 {
    padding-left: 15px !important;
  }
  .p-xs-t-10 {
    padding-top: 10px !important;
  }
  .p-xs-r-10 {
    padding-right: 10px !important;
  }
  .p-xs-b-10 {
    padding-bottom: 10px !important;
  }
  .p-xs-l-10 {
    padding-left: 10px !important;
  }
  .p-xs-t-5 {
    padding-top: 5px !important;
  }
  .p-xs-r-5 {
    padding-right: 5px !important;
  }
  .p-xs-b-5 {
    padding-bottom: 5px !important;
  }
  .p-xs-l-5 {
    padding-left: 5px !important;
  }
  .p-xs-t-0 {
    padding-top: 0px !important;
  }
  .p-xs-r-0 {
    padding-right: 0px !important;
  }
  .p-xs-b-0 {
    padding-bottom: 0px !important;
  }
  .p-xs-l-0 {
    padding-left: 0px !important;
  }
}

This is my first time using Less logic heavily, so please let me know if there is a better way of doing things.


Solution

  • The primary suggestion:

    Use each function instead.

    Since you're using Less 3.x anyway (and then there's no real reason to not upgrade to 3.7 as there's no hard-breaking changes between 3.* versions) - there's no reason to bother with recursive mixins for trivial loops. Recursive mixin loops remain useful only to generate "recursive values", i.e. when each new iteration needs to reuse a value generated by prev. iteration - it's something each can't do.


    Either way let's see what's wrong with your example - just for the reference.

    Your key mistake is that you put your "next-iteration-call" for media queries (i.e. .g-loop(@g - 1);) in wrong place. Basically you put it inside the media query block while it should be outside of it. So it's not a surprise they get nested into each other in the result.
    Let's start from a simplified example (just a single-level loop implemented the same way):

    .outer-iter(@i) when (@i > 0) {
        block {
            .outer-iter(@i - 1);
            itick: @i;
        }
    }
    
    .outer-iter(3);
    

    This will generate something like:

    block {
      itick: 3;
    }
    block block {
      itick: 2;
    }
    block block block {
      itick: 1;
    }
    

    exactly because each new block is generated inside of the previous one.
    The proper loop code in this case would be:

    .outer-iter(@i) when (@i > 0) {
        .outer-iter(@i - 1);
        block {
            itick: @i;
        }
    }
    

    There're a few more cosmetic mistakes (not quite hurting errors but still) that suggest you used some stone age tutorial (~6 years old or so). Briefly:

    • use of .*-loop (0) {} terminals - this is not necessary for a while (just a noise)
    • hardcoded n-iteration values - you'll have to find and fix them each time your data changes. Use length function instead.

    Well, I won't bother you with other cosmetic problems (yes, there're a few more), let's just stop with (sort of) fixed code:

    
    ...
    
    @max-pm-size: 50;
    
    .g-loop(@g) when (@g > 0) {
    
        @responsive: extract(@responsivevars, @g);
        @responsiveabv: extract(@responsivevarsabv, @g);
    
        @media (max-width: @@responsive) {
    
            .h-loop(@h) when (@h > 0) {
    
                .i-loop(@i) when (@i > -1) {
    
                    .j-loop(@j) when (@j > 0) {
                        @direction: extract(@directionlist, @j);
                        @directionname: extract(@directions, @j);
                        @type: extract(@spacingtype, @h);
                        @typeabv: extract(@sapcingtypeabv, @h);
    
                        .@{typeabv}-@{responsiveabv}-@{direction}-@{i} {
                            @{type}-@{directionname}: @i*1px !important; /* each time you use !important a kitty dies */
                        }
    
                        .j-loop(@j - 1);
                    }
    
                    .j-loop(length(@directions));
                    .i-loop(@i - 5);
                }
    
                .i-loop(@max-pm-size);
                .h-loop(@h - 1);
            }
    
            .h-loop(length(@spacingtype));
    
        } // ~ end of @media
    
        .g-loop(@g - 1); // <- this was your key mistake
    }
    
    .g-loop(length(@responsivevars));