4
votes

I have this simplified Less script

.placeholder(@color: #333333) {
    &::-webkit-input-placeholder  { color: @color; }
}

input {
    .placeholder();
}

.placeholder {
    margin-top: 20px;
}

The output when I run this through my local compiler or winless online less compiler is

input {
  margin-top: 20px;
}
input::-webkit-input-placeholder {
  color: #333333;
}
.placeholder {
  margin-top: 20px;
}

Insted of the desired output

input::-webkit-input-placeholder {
  color: #333333;
}
.placeholder {
  margin-top: 20px;
}

Is this a bug or am I missing something here?

By the result it looks to me like I can't have CSS-selectors with the same name as mixins with default values.

I'm running into this problem when compiling Bootstrap with my site specific code. In this particular case I can work around it, but as the project grows and I include other projects I can't imaging I have to keep track of any mixins with default values?

Edit: I see now that I should have read the manual and pretty much seen on the first page of the docs that everything can be treated as a mixin.

3
I don't think there is any straight forward solution to this one as this is the way Less works. However can your .placeholder selector be made more specific (like div.placeholder or similar)? That should solve the issue. I am not adding it as answer because this still is a workaround but will do so if you wish me to. - Harry
Thank you Harry. You may do so and I can tick it as accepted. Like you says it's how Less works and I would have known if I had RTFM. - opex
I have just moved the Edit part to the bottom to make the question get prominence. Also just for your info, mentioning Edit etc are generally not required because SO does keep track of changes. It is not that it is bad but just that it is not required :) - Harry
I put a workaround at the corresponding issue ticket - seven-phases-max
@Harry, Sure I do not mind (as always). - seven-phases-max

3 Answers

2
votes

In Less, everything is technically a mixin irrespective of whether we write it with parantheses (as in with parameters) or without parantheses (as in like a CSS class selector). The only difference between the two is that when the parantheses are present, the properties present within it are not output unless called from within a selector block.

Quoting the Less Website:

It is legal to define multiple mixins with the same name and number of parameters. Less will use properties of all that can apply.

In this case, since the other mixin has a default value for its only parameter, both the properties can apply when called without any parameter and hence there is no way to avoid it from happening.

Workaround Solution: One possible solution to work-around this problem is to enclose all such conflicting rules within a parent selector (like body).

.placeholder(@color: #333333) {
    &::-webkit-input-placeholder  { color: @color; }
}

input {
    .placeholder();
}

body{
    .placeholder{
        margin-top: 20px;
    }
}

Compiled CSS:

input::-webkit-input-placeholder {
    color: #333333;
}
body .placeholder {
    margin-top: 20px;
}

Option 2: Extracted from the solution posted by seven-phases-max in the Less GitHub Issue thread.

For the particular use-case one of possible workarounds is to isolate conflicting classes in unnamed scope so they won't interfere with external names:

.placeholder(@color: #333333) {
    &::-webkit-input-placeholder  { color: @color; }
}

input {
    .placeholder();
}

& { // unnamed namespace
    .placeholder {
        background: #ffffff;
    }
} // ~ end of unnamed namespace

Note: The above is a straight copy/paste from the GitHub thread without any modifications so as to not tamper with the information.

1
votes
@mixin placeholder(@color: #333333) {
    &::-webkit-input-placeholder  { color: @color; }
}

input {
    @include placeholder();
}

.placeholder {
    margin-top: 20px;
}

that should work.

-1
votes

So if i understood right, you just want to add 20px on top of the placeholder ? Add padding-top to input instead.

input {
    padding-top: 20px;
}