0

I've written some mixins for myself that use this standard syntax, and it's really helpful. But I'd like to hide some of the code if a certain value shows up.

Here's what works (breakpoints are fakes, and the ~"" code is a default telling LESS to compile to nothing):

// LESS MIXIN (CURRENT)
.margin(@lg: ~""; @md: ~""; @sm: ~""; @xs: ~""; @xxs: ~"") {
    margin: @lg;
    @media screen and (max-width: 1200px) {
        margin: @md;
    }
    @media screen and (max-width: 800px) {
        margin: @sm;
    }
    @media screen and (max-width: 600px) {
        margin: @xs;
    }
    @media screen and (max-width: 400px) {
        margin: @xxs;
    }
}

which can be implemented like this

// LESS IMPLEMENTATION (CURRENT & EXPECTED)
div {
    .margin(20px; ~""; 15px; ~""; 10px);
}

and will output like this:

/* CSS OUTPUT (CURRENT) */
div {
    margin: 20px;
}
@media screen and (max-width: 1200px) {
    div {
        margin: ;
    }
}
@media screen and (max-width: 800px) {
    div {
        margin: 15px;
    }
}
@media screen and (max-width: 600px) {
    div {
        margin: ;
    }
}
@media screen and (max-width: 400px) {
    div {
        margin: 10px;
    }
}

It's simple and keeps me from doing a ton of @media breakpoints for basic styles. But I'd LOVE to not even print an empty @media line; I'd like the code to compile like this:

/* CSS OUTPUT (DESIRED) */
div {
    margin: 20px;
}
@media screen and (max-width: 800px) {
    div {
        margin: 15px;
    }
}
@media screen and (max-width: 400px) {
    div {
        margin: 10px;
    }
}

SO... I was trying a few things and found this & when code here, but it doesn't seem to work when I implement it here. This is what I've tried:

// LESS MIXIN (FAILED ATTEMPT TO SMARTEN)
.margin(@lg: ~""; @md: ~""; @sm: ~""; @xs: ~""; @xxs: ~"") {
    & when not (@lg = ~"") {
        margin: @lg;
    }
    & when not (@md = ~"") {
        @media screen and (max-width: 1200px) {
            margin: @md;
        }
    }
    & when not (@sm = ~"") {
        @media screen and (max-width: 800px) {
            margin: @sm;
        }
    }
    & when not (@xs = ~"") {
        @media screen and (max-width: 600px) {
            margin: @xs;
        }
    }
    & when not (@xxs = ~"") {
        @media screen and (max-width: 400px) {
            margin: @xxs;
        }
    }
}

Anyone have any ideas to hide the @media code that would work in LESS?

Community
  • 1
  • 1
  • What you have tried should work in the latest compiler (though I'd try to do this with loops in-addition to the guards). – Harry May 10 '16 at 05:12
  • Not sure if there's a good answer in an SO context since the whole thing can be optimized to almost nothing as soon as we realize you're not going to repeat your `@media screen and (max-width: ***)` for *every single property* (aren't you?). So the suggestions is as always: learn mixins, pattern matching and lists. – seven-phases-max May 10 '16 at 12:40
  • @Harry I'm not sure what version of the complier we're using but that's good to know. – nathanakers May 10 '16 at 18:21
  • @seven-phases-max no, no. not every property. just margins and paddings and base font sizes (font sizes are a slightly different mixin structure but the idea is the same). this way my base structure is easy to scan. i'm trying to learn, hence the question. – nathanakers May 10 '16 at 18:23
  • Then probably a discussion [there](https://github.com/less/less.js/issues/2702) would be also a good start. – seven-phases-max May 10 '16 at 19:57
  • The answer to this would be to use multiple mixins with guards, but in the end it's not a good practice to structure your code this way. You're going to end up with a lot of bloat and unintuitive behaviors. – zzzzBov May 11 '16 at 13:11

1 Answers1

1

There are a number of ways to apply parts of a mixin depending on the parameters. The easiest I can think of is to use multiple mixins with guards. Disclaimer: I do not recommend this approach for media queries.

.test {
    .example(red; orange; yellow; green; blue);
}

.example(@xl: "", @lg: "", @med: "", @sm: "", @xs: "") when (iscolor(@xl)) {
    @media (min-width: 1200px) {
        background-color: @xl;
    }
}
.example(@xl: "", @lg: "", @med: "", @sm: "", @xs: "") when(iscolor(@lg)) {
    @media (min-width: 800px) and (max-width: 1199px) {
        background-color: @lg;
    }
}
.example(@xl: "", @lg: "", @med: "", @sm: "", @xs: "") when(iscolor(@med)) {
    @media (min-width: 600px) and (max-width: 799px) {
        background-color: @med;
    }
}
.example(@xl: "", @lg: "", @med: "", @sm: "", @xs: "") when(iscolor(@sm)) {
    @media (min-width: 400px) and (max-width: 588px) {
        background-color: @sm;
    }
}
.example(@xl: "", @lg: "", @med: "", @sm: "", @xs: "") when(iscolor(@xs)) {
    @media (max-width: 399px) {
        background-color: @xs;
    }
}

In this example, each part will be applied depending on which parameters were included when the mixin was called.


A better solution for handling media queries is to declare some variables for the various breakpoints used.

I often have a set of variable declarations along the lines of:

@extra-small: ~"                        (max-width:  479px)";
@small:       ~"(min-width:  480px) and (max-width:  767px)";
@medium:      ~"(min-width:  768px) and (max-width: 1023px)";
@large:       ~"(min-width: 1024px) and (max-width: 1199px)";
@extra-large: ~"(min-width: 1200px)                        ";

Sometimes I break these down further by using @small: @small-begin and @small-end where @small-begin and @small-end are the individual min-width and max-width declarations.

These variables can then be used as:

.test {
  background-color: blue;

  @media @small {
    background-color: green;
  }
  @media @medium {
    background-color: yellow;
  }
  @media @large {
    background-color: orange;
  }
  @media @extra-large {
    background-color: red;
  }
}

The advantage to this format is that you only need to specify styles in the media queries that change between breakpoints. You can also add multiple styles within each breakpoint, which reduces the amount of duplicate media query declarations in the output.

zzzzBov
  • 174,988
  • 54
  • 320
  • 367
  • just curious, why would you not do the above? while the syntax at the bottom is smaller, i was trying to avoid that syntax at all while creating a shorthand that would output the same. – nathanakers May 11 '16 at 21:59
  • @nathanakers, when you've got a thoughtful consistent design, most of the time you don't need to specify many properties, and the ones you do need to specify might not be changed for all breakpoints. With the later method it's quite clear which properties are set for which breakpoints, even when there's lots of declarations. With the multiple-mixin approach, the mixins themselves are verbose abstractions, and the code becomes muddied, making it harder to read, and harder to understand, both of which are bad for maintenance. – zzzzBov May 11 '16 at 22:59
  • I can see your point. I thought it was clever, but I'm just back to media styles. I took your advice and converted my common media queries into variables. Thanks. – nathanakers May 12 '16 at 23:05