Reputation: 3010
Can you write a function in LESS that outputs a media query when you pass in values for it's breakpoints?
I'd like to be able to create them on the fly like this:
// Something like this
.media(@min, @max) {
@query: ~"@media (min-width: @{min}) and (max-width: @{max})";
}
.class {
.media(100px, 400px) {
color: red;
}
.media(401px, 500px) {
color: green;
}
}
// Outputs this:
@media (min-width: 100px) and (max-width: 400px) {
.class {
color: red;
}
}
@media (min-width: 401px) and (max-width: 500px) {
.class {
color: green;
}
}
I thought I had this working, but because the mixins are called within the same scope, the variable isn't being assigned in the second call:
.media (@min, @max) {
@query: ~"(min-width: @{min}) and (max-width: @{max})";
}
.class {
width: 100%;
max-width: 300px;
.media(100px, 400px);
@media @query {
color: red;
}
.media(401px, 800px);
@media @query {
color: green;
}
}
Upvotes: 0
Views: 768
Reputation: 11820
The main problem of your first snippet is that you can't use mixin call to set an identifier for a {...}
block. In the snippet the following:
.media(100px, 400px) {
color: red;
}
is actually a new mixin definition and not really a previously defined .media
mixin call (so it simply outputs nothing since this new mixin is never invoked).
And proper mixin call syntax:
.media(100px, 400px); {
color: red;
}
in such context would be an equivalent to:
@query: ~"@media (min-width: 100px) and (max-width: 400px)"; {
color: red;
}
which of course does not make any sense for Less at all and it would throw a error.
-------
Your second snippet is more correct, but yes, since both mixin calls share the same scope there's only one @query
variable. It's possible to isolate them by putting each into unnamed namespace (which is simply a ruleset with &
name so it creates a new scope but then is output as part of the outer ruleset):
.class {
& {.media(100px, 400px);
@media @query {
color: red;
}}
& {.media(401px, 800px);
@media @query {
color: green;
}}
}
This does the trick but obviously it does not look like something really useful (too verbose and unreadable) so for the sake of reference it would make sense to mention other approaches:
-------
Today, the most clean solution for the particular case would be to use ruleset as mixin parameter:
.media(@min, @max, @styles) {
@media (min-width: @min)
and (max-width: @max) {
@styles();
}
}
.class {
.media(100px, 400px, {
color: red;
});
.media(401px, 800px, {
color: green;
});
}
Though I doubt that in a practical project you'd want to explicitly repeat pixel values every time you need the corresponding media so most likely eventually you end with more semantic mixins, e.g.:
.media(@min, @max, @styles) {
@media (min-width: @min)
and (max-width: @max) {
@styles();
}
}
.tiny-screen(@styles) {.media(100px, 400px, @styles)}
.not-so-tiny-screen(@styles) {.media(401px, 800px, @styles)}
.class {
.tiny-screen({
color: red;
});
.not-so-tiny-screen({
color: green;
});
}
------
Passing rulesets to mixins is not the only method to achieve the goal, there're other methods with various pros and cons (some of those can look even more readable if you go the "semantic media blocks" way). See for example https://stackoverflow.com/a/15842048/2712740 (obviously search for [less] media
here at SO will point to more inspirations).
Upvotes: 1