41

Read This

There are several "correct" answers. Since this question gets a lot of traffic, I figured I should keep up with what (I think) the best answer is (based on the LESS documentation) as the LESS project matures, and change my accepted answer accordingly.


I'm using LESS and I haven't been able to find a fix for allowing multiple CSS3 box-shadows. I have the following mixin:

.box-shadow(@arguments) {
    -webkit-box-shadow: @arguments;
    -moz-box-shadow: @arguments;
    box-shadow: @arguments;
}

and I'm attempting this:

.box-shadow(
    inset 0 0 50px rgba(0,0,0,0.3),
    0 0 10px rgba(0,0,0,0.5);
);

This works in normal CSS3, but fails when running from a LESS file. I've read somewhere that the comma separating the 2 shadows is what causes the issue in the LESS parser.

Does anyone know how to make this work? The only workaround I can think of is creating an additional CSS file that contains my multiple box-shadow properties.

Community
  • 1
  • 1
Mike McLin
  • 3,627
  • 7
  • 41
  • 49
  • right now your solution `.box-shadow( inset 0 0 50px rgba(0,0,0,0.3), 0 0 10px rgba(0,0,0,0.5); );` works as desired (tested with less2css.org). – Michał Rybak Oct 12 '13 at 16:23
  • see my answer for full explanation. – Michał Rybak Oct 21 '13 at 20:38
  • My older answer was updated as well to further help avoid confusion – iamnotsam Apr 06 '14 at 20:03
  • [This](http://stackoverflow.com/questions/11013528/using-undefined-number-of-arguments-in-less-mixins/26155408#26155408) answer has another method (Option 3) to do this with the latest Less features and offers a bit more flexibility. If you wish to, I will customize that answer for box-shadows and post here. – Harry Oct 04 '14 at 07:05

7 Answers7

73

This works with newer LESS versions:

.box-shadow(2px 2px 3px rgba(0,0,0,.4), 0px 0px 5px rgba(0,0,0,.8););
                                  // this semicolon is important! ^

And this uglier version works even with older LESS versions:

.box-shadow(~"2px 2px 3px rgba(0,0,0,.4), 0px 0px 5px rgba(0,0,0,.8)");

Update: LESS evolved, so this answer was updated and is now correct again. Thanks Michał Rybak

iamnotsam
  • 9,470
  • 7
  • 33
  • 30
  • 1
    This is more flexible yes, but still requires alot of messing around with RGBA values, when it can be done so much easier with the proper use of mixins. I still agree that this is the right answer for the time being. – Foxinni Aug 15 '12 at 10:30
  • 1
    But how do I add a darken function for example instead of the rgba color? – Tamik Soziev Mar 11 '13 at 18:15
  • for even more recent version, check my answer. – Michał Rybak Feb 25 '14 at 15:48
12

It should work just fine. I've used it before. Try with this mixin:

.box-shadow (@shadow1, @shadow2: transparent 0 0 0 ) {
    -moz-box-shadow: @shadow1, @shadow2;
    -webkit-box-shadow: @shadow1, @shadow2;
    box-shadow: @shadow1, @shadow2;
}

And then:

.box-shadow(inset 0 0 50px rgba(0,0,0,0.3), 0 0 10px rgba(0,0,0,0.5));
elclanrs
  • 92,861
  • 21
  • 134
  • 171
  • I was using something similar, but only passing in one argument (the overall box-shadow values). Since the argument had a comma, I guess it assumed that I was passing in 2 arguments. Didn't think about that. Thanks. – Mike McLin Feb 10 '12 at 17:01
  • 6
    This is only two... The fun part is adding a limitless amount of box-shadows. Ultimately this is not all that helpful. – Foxinni Aug 15 '12 at 10:29
8

LESS strips the comma. So the @arguments value becomes:

inset 0 0 50px rgba(0,0,0,0.3) 0 0 10px rgba(0,0,0,0.5);

Which is invalid as a box-shadow.

Instead, do this when you want a comma:

@temp: inset 0 0 50px rgba(0,0,0,0.3), 0 0 10px rgba(0,0,0,0.5);

.box-shadow(@temp);
Camilo Martin
  • 37,236
  • 20
  • 111
  • 154
6

The best solution is to make separate overloads for each number of shadows. Less handles the proper overload resolution:

.box-shadow(@shadow1) {
    -webkit-box-shadow: @shadow1;
    -moz-box-shadow: @shadow1;
    box-shadow: @shadow1;
}

.box-shadow(@shadow1, @shadow2) {
    -webkit-box-shadow: @shadow1, @shadow2;
    -moz-box-shadow: @shadow1, @shadow2;
    box-shadow: @shadow1, @shadow2;
}    

.box-shadow(@shadow1, @shadow2, @shadow3) {
    -webkit-box-shadow: @shadow1, @shadow2, @shadow3;
    -moz-box-shadow: @shadow1, @shadow2, @shadow3;
    box-shadow: @shadow1, @shadow2, @shadow3;
}

.box-shadow(@shadow1, @shadow2, @shadow3, @shadow4) {
    -webkit-box-shadow: @shadow1, @shadow2, @shadow3, @shadow4;
    -moz-box-shadow: @shadow1, @shadow2, @shadow3, @shadow4;
    box-shadow: @shadow1, @shadow2, @shadow3, @shadow4;
}

.box-shadow(@shadow1, @shadow2, @shadow3, @shadow4, @shadow5) {
    -webkit-box-shadow: @shadow1, @shadow2, @shadow3, @shadow4, @shadow5;
    -moz-box-shadow: @shadow1, @shadow2, @shadow3, @shadow4, @shadow5;
    box-shadow: @shadow1, @shadow2, @shadow3, @shadow4, @shadow5;
}

EDIT:

Ok, I'm still learning about LESS, but it appears that will actually mixin ALL of the overloads in certain situations, instead of the one with the most applicable parameter list, so you may get varying results. I've since revised my mixins so that they're named box-shadow-2 or box-shadow-3, to match the expected number of parameters. I'll revise my answer once I figure out what's going on / have a better solution.

Paul Gordon
  • 2,532
  • 3
  • 26
  • 24
  • I have tested the functionality and it works great... http://codepen.io/mikemclin/pen/cHtfu – Mike McLin May 11 '13 at 15:34
  • yes but this is unnecessary, and an incorrect way compared to the solution ".box-shadow(~"inset 0 0 50px rgba(0,0,0,0.3), 0 0 10px rgba(0,0,0,0.5)");" from a programming perspective for so many reasons. – Neo Jul 07 '13 at 19:16
  • @Neo -- Not necessarily. Dropping long regions of code inside strings kills basically all services provided by your IDE/editor, which IMO is far worse than any downside. Any duplicated code will be removed by an intelligent CSS minifier anyways. – Paul Gordon Jul 18 '13 at 12:19
  • @neo - Unless I'm mistaken, your suggestion couldn't perform a task like this in one step... `.box-shadow(0 0 10px darken(@red, 20%), 0 0 10px lighten(@green, 20%))`. But the answer I selected can. Please advise if I'm overlooking something. – Mike McLin Sep 16 '13 at 20:04
4
.box-shadow (@shadow1) {
    -webkit-box-shadow: @shadow1;
    -moz-box-shadow: @shadow1;
    -ms-box-shadow: @shadow1;
    -o-box-shadow: @shadow1;
    box-shadow: @shadow1;
}
.box-shadow (@shadow1, @shadow2) {
    @temp: @shadow1, @shadow2;
    .box-shadow(@temp);
}
.box-shadow (@shadow1, @shadow2, @shadow3) {
    @temp: @shadow1, @shadow2, @shadow3;
    .box-shadow(@temp);
}
胡忠晞
  • 49
  • 1
  • 1
4

This question is kind of out-of-date, as for now your solution actually works.


However, I'll try to explain why, because many people seem to be unaware of that:

Actually passing a list of comma-separated arguments to a mixin is pretty simple: just use semicolon (;) instead of comma in mixin call.

From LESS documentation:

Parameters are either semicolon or comma separated. It is recommended to use semicolon. The symbol comma has double meaning: it can be interpreted either as a mixin parameters separator or as css list separator. Using comma as mixin separator makes it impossible to use comma separated list as an argument.

Semicolon does not have such limitation. If the compiler sees at least one semicolon inside mixin call or declaration, it assumes that arguments are separated by semicolons. All commas then belong to css lists.


Mixin definition uses well-known, regular syntax:

.box-shadow(@params) {
  -webkit-box-shadow: @params;
  -moz-box-shadow: @params;
  box-shadow: @params;
}

But mixin call should use semicolons to separate arguments. If semicolons are used, commas are no longer treated as separators and are passed to mixin without problems:

.box-shadow(
  inset 0 0 50px rgba(0,0,0,0.3), 0 0 10px rgba(0,0,0,0.5);
);

Note that if (as in the case above) only one list is passed, you have to use semicolon at the end, too.

Check out my answer regarding multiple backgrounds to see how mixin call should look with multiple parameters, some of which are comma-separated lists.

Community
  • 1
  • 1
Michał Rybak
  • 8,648
  • 3
  • 42
  • 54
0

Here is another solution(which I prefer more):

Use e() string function. Ex: e("S1, S2") will return S1, S2 without quotes.

Long example:

Definition:

.c3_box-shadow(@val){
    -webkit-box-shadow: @val;
       -moz-box-shadow: @val;
            box-shadow: @val;
}

Usage:

.box-shadow( e("inset 0 1px 0 rgba(255,255,255,.4), inset 0 -1px 0 rgba(255,255,255,.2)") );

Output:

-webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.4), inset 0 -1px 0 rgba(255,255,255,.2);
-moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.4), inset 0 -1px 0 rgba(255,255,255,.2);
box-shadow: inset 0 1px 0 rgba(255,255,255,.4), inset 0 -1px 0 rgba(255,255,255,.2);

Reference: http://lesscss.org/functions/#string-functions-e

Andrei Surdu
  • 2,281
  • 3
  • 23
  • 32
  • 1
    Nope, this is not the recommended solution for the up-to-date Less. The recommended way is given in the question itself and in @Michał Rybak answer. – seven-phases-max Feb 25 '14 at 14:33
  • @seven-phases-max, it just doesn't work for me if I use the semicolon. I have no idea why. – Andrei Surdu Feb 25 '14 at 16:01
  • "just doesn't work for me" is barely an excuse to recommend the outdated hack as a "simple and powerful solution". AFAIK the "semicolon" way works even in ancient Less versions like 1.3.3 as well as in outdated ports like lessphp or dotless. There's nothing wrong in `~""` itself but it should not be used as a "no brainer". – seven-phases-max Feb 25 '14 at 16:12
  • OK, my bad. I've updated my answer and added a reference to the function used in example. – Andrei Surdu Feb 25 '14 at 16:23
  • Well, `e()` is just an alias for `~""` so nothing really changed. After all this ~"" method is already in @storypixel answer given 2 years ago so... – seven-phases-max Feb 25 '14 at 16:37
  • This was working for me and I thought it will be nice to share with others. I've googled for a solution like this and I was about to use the accepted answer from this thread, but I don't really need all this code. I just need a function to replace 3 lines -prefix- w/h one, nothing else. So this is how I do it and to allow LESS process it I've used ~ sign, now I've updated it with the recomended function from LESS docs. I've edited my post and said that this is my preference and probably other will apreciate my answer. That's all, end of story. P.S. I haven't seen @storypixel's answer, sorry. – Andrei Surdu Feb 25 '14 at 18:42
  • "I've googled for a solution like this and I was about to use the accepted answer from this thread" - yet again, please read the question itself and @Michał Rybak answer. The solution is *the same 3 lines* of code. – seven-phases-max Feb 25 '14 at 19:46