My stylesheet looks like this:
.thing-1 { background-image: url('top.png'); }
.thing-2 { background-image: url('top.png'); }
.thing-3 { background-image: url('top.png'); }
/* etc... */
Followed by this:
.thing-20 { background-image: url('bottom.png'); }
.thing-21 { background-image: url('bottom.png'); }
.thing-22 { background-image: url('bottom.png'); }
/* etc... */
I am looking for a way to simplify my stylesheet with LESS or something similar. Here's what I'd like to do:
.thing-[i < 15] { background-image: url('top.png'); }
.thing-[i >= 15] { background-image: url('bottom.png'); }
Is there a way I can do something like this with LESS? If not LESS, maybe SASS?
As you are asking for LESS and Sass, here are some solutions. You can achieve it in both with looping through values - but Sass is a bit stronger in this field as it has built-in control directives like @for
, @if
, @while
and @each
. There are of course multiple ways of implementing this but this were the first that came to mind:
LESS:
.bg (@i) when (@i < 15) { background-image: url('top.png'); }
.bg (@i) when (@i >= 15) { background-image: url('bottom.png'); }
.things (@max, @i: 1) when (@i < @max) {
.thing-@{i} { .bg (@i); }
.things (@max, (@i + 1));
}
.things(50);
and SCSS:
@function bg($i) {
@if $i < 15 { @return 'top.png' }
@else { @return 'bottom.png' }
}
@for $i from 1 through 50 {
.thing-#{$i} { background-image: url(bg($i)); }
}
where you achieve your exact output.
But a more dry output would be achieved with:
LESS: see @seven-phases-max's answer. However, there is the problem of always having to print out .thing-15
also if you only have less than 15 items. Unless you add another guard that adds .thing-15
only when needed like so:
.thing(@i) when (@i = 15) {
.thing-15 {background-image: url('bottom.png')}
}
you can try out the Less solutions at less2css.org
or SCSS:
%bg-top { background-image: url('top.png'); }
%bg-bottom { background-image: url('bottom.png'); }
@for $i from 1 through 50 {
.thing-#{$i} {
@if $i < 15 { @extend %bg-top; }
@else { @extend %bg-bottom; }
}
}
The last one in my opinion is the most elegant solution.
DEMO
Preprocessor Not Particularly Needed Under Some Conditions
Update: Made the solution fully general to allow for extra classes on either side of thing-#
class.
This is fairly practical with the numbers you are dealing with. Basically the technique is similar to what I answered for this question, but in your case the code is as follows (here is a tweaked example just using background color):
[class*="thing-"] {
background-image: url('top.png');
}
[class*="thing-1"]:not(.thing-1):not(.thing-10):not(.thing-11):not(.thing-12):not(.thing-13):not(.thing-14),
[class*="thing-2"]:not(.thing-2),
[class*="thing-3"]:not(.thing-3),
[class*="thing-4"]:not(.thing-4),
[class*="thing-5"]:not(.thing-5),
[class*="thing-6"]:not(.thing-6),
[class*="thing-7"]:not(.thing-7),
[class*="thing-8"]:not(.thing-8),
[class*="thing-9"]:not(.thing-9) {
background-image: url('bottom.png');
}
It uses the attribute selector when it is doing a "general" selection across multiple numbers, and then filters out for specific classes that the general should not apply to.
You Can Reduce the CSS Further
If you change your 1-9
classes to have preceeding zeros (thing-01
, thing-02
etc.), then the general css can be reduced further to this:
[class*="thing-"] {
background-image: url('top.png');
}
[class*="thing-"]:not([class*="thing-0"]):not(.thing-10):not(.thing-11):not(.thing-12):not(.thing-13):not(.thing-14) {
background-image: url('bottom.png');
}
Practical Limits
This would all get very cumbersome if a break point at a very large number was needed, as more filtering would need to occur. But still some larger levels can be achieved for breaking as my original answer for the other question demonstrated, and at that point, perhaps using LESS or SCSS to somehow do the break pointing might be possible, while still keeping output CSS low.
Yes, you can do this in LESS. However the code is a bit scary (basically it will require you to learn some advanced LESS concepts) so if your use-case is that simple I guess you'd prefer to just write these things manually (as suggested by @Ashkan).
LESS code:
.thing-1 {background-image: url('top.png')}
.thing-15 {background-image: url('bottom.png')}
.thing(@i) when (@i < 15) {
.thing-@{i} {&:extend(.thing-1);}
}
.thing(@i) when (@i > 15) {
.thing-@{i} {&:extend(.thing-15);}
}
.generate-things(@i) when (@i > 1) {
.generate-things((@i - 1));
.thing(@i);
}
.generate-things(30);
you can do either
.thing-1,.thing-2,.thing-3,... { background-image: url('top.png'); }
.thing-20,.thing-21,.thing-22,... { background-image: url('bottom.png'); }
of you can use more than one class for your elements:
.top { background-image: url('top.png'); }
.bottom { background-image: url('bottom.png'); }
.thing-1 { /*only thing-1 related css code*/}
.thing-2 { /*only thing-2 related css code*/}
and use it like:
<div class="top thing-1"></div>
<div class="bottom thing-1"></div>