134

I'm trying to create a mixin for placeholders in sass.

This is the mixin I've created.

@mixin placeholder ($css) {
  ::-webkit-input-placeholder {$css}
  :-moz-placeholder           {$css}
  ::-moz-placeholder          {$css}
  :-ms-input-placeholder      {$css}  
}

This is how I'd like to include the mixin:

@include placeholder(font-style:italic; color: white; font-weight:100;);

Obviously this isn't going to work because of all the colons and semi-colons that's being passed through to the mixin, but... I'd really like to just input static css and pass it through exactly like the above function.

Is this possible?

Shannon Hochkins
  • 11,763
  • 15
  • 62
  • 95

6 Answers6

270

You're looking for the @content directive:

@mixin placeholder {
  ::-webkit-input-placeholder {@content}
  :-moz-placeholder           {@content}
  ::-moz-placeholder          {@content}
  :-ms-input-placeholder      {@content}  
}

@include placeholder {
    font-style:italic;
    color: white;
    font-weight:100;
}

SASS Reference has more information, which can be found here: http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#mixin-content


As of Sass 3.4, this mixin can be written like so to work both nested and unnested:

@mixin optional-at-root($sel) {
  @at-root #{if(not &, $sel, selector-append(&, $sel))} {
    @content;
  }
}

@mixin placeholder {
  @include optional-at-root('::-webkit-input-placeholder') {
    @content;
  }

  @include optional-at-root(':-moz-placeholder') {
    @content;
  }

  @include optional-at-root('::-moz-placeholder') {
    @content;
  }

  @include optional-at-root(':-ms-input-placeholder') {
    @content;
  }
}

Usage:

.foo {
  @include placeholder {
    color: green;
  }
}

@include placeholder {
  color: red;
}

Output:

.foo::-webkit-input-placeholder {
  color: green;
}
.foo:-moz-placeholder {
  color: green;
}
.foo::-moz-placeholder {
  color: green;
}
.foo:-ms-input-placeholder {
  color: green;
}

::-webkit-input-placeholder {
  color: red;
}
:-moz-placeholder {
  color: red;
}
::-moz-placeholder {
  color: red;
}
:-ms-input-placeholder {
  color: red;
}
cimmanon
  • 67,211
  • 17
  • 165
  • 171
  • 4
    Not sure why this was reverted, but the answer is incorrect. You need '@' at the beginning of each of the individual vendor prefixes. Take a look at an answer further down by Dave Hein, or better yet - try to run this code, and you'll see it wont work. – Sk446 Mar 10 '14 at 10:06
  • 1
    @RickM It was reverted because the modifications you made don't compile (error: Base-level rules cannot contain the parent-selector-referencing character '&'.). This creates the exact output the OP is looking for, the answer you claim is "correct" does not. – cimmanon Mar 10 '14 at 12:27
  • Interesting...are you compiling via cli? Looks like there must be a versioning conflict as the exact opposite happens for me, and judging by other replies, a few others too. – Sk446 Mar 10 '14 at 16:47
  • 5
    Yes, CLI: Sass 3.3.0.rc.3 via Compass. Double checked with [CodePen](http://codepen.io/pen/) and [Sassmeister](http://sassmeister.com/). The `&` is necessary if you want to be able to get a selector like `input::-webkit-input-placeholder` with the mixin, but it will prevent you from using it at the root level. http://sassmeister.com/gist/9469073. Now if you're using LibSass, that's a different story. – cimmanon Mar 10 '14 at 16:58
  • This is not going to work if nested. Dave's answer below is the way to go. – adamj Nov 30 '16 at 07:09
  • This answer does not work answer anymore. The gist by @cimmanon is not same as mentioned in answer. – Tushar Shukla Sep 01 '18 at 07:43
176

I found the approach given by cimmanon and Kurt Mueller almost worked, but that I needed a parent reference (i.e., I need to add the '&' prefix to each vendor prefix); like this:

@mixin placeholder {
    &::-webkit-input-placeholder {@content}
    &:-moz-placeholder           {@content}
    &::-moz-placeholder          {@content}
    &:-ms-input-placeholder      {@content}  
}

I use the mixin like this:

input {
    @include placeholder {
        font-family: $base-font-family;
        color: red;
    }
}

With the parent reference in place, then correct css gets generated, e.g.:

input::-webkit-input-placeholder {
    font-family: Constantia, "Lucida Bright", Lucidabright, "Lucida Serif", Lucida, "DejaVu Serif", "Liberation Serif", Georgia, serif;
    color: red;
}

Without the parent reference (&), then a space is inserted before the vendor prefix and the CSS processor ignores the declaration; that looks like this:

input::-webkit-input-placeholder {
    font-family: Constantia, "Lucida Bright", Lucidabright, "Lucida Serif", Lucida, "DejaVu Serif", "Liberation Serif", Georgia, serif;
    color: red;
}
Jessica
  • 9,379
  • 14
  • 65
  • 136
Dave Hein
  • 2,153
  • 1
  • 14
  • 12
  • 9
    Thank you very much. You saved me hours of trial and error in order to make the accepted answer/solution work... – tmuecksch Feb 23 '14 at 01:02
  • It is worth noting that that your selectors are now slightly overqualified (unless you genuinely don't want it to work on input or select elements). Adding the parent selector prevents you from using the mixin without nesting (which was what the OP was looking for). – cimmanon Mar 09 '14 at 14:34
  • 1
    The `&` is totally necessary. Edited the popular answer to reflect this. – AlexKempton May 30 '14 at 11:41
  • 1
    +1. Some browsers support the official `::placeholder` property now, so I'd recommend adding this at the top: `&::placeholder {@content}` – Matt Browne Aug 10 '17 at 16:13
11

This is for shorthand syntax

=placeholder
  &::-webkit-input-placeholder
    @content
  &:-moz-placeholder
    @content
  &::-moz-placeholder
    @content
  &:-ms-input-placeholder
    @content

use it like

input
  +placeholder
    color: red
igrossiter
  • 744
  • 1
  • 12
  • 26
3

Why not something like this?

It uses a combination of lists, iteration, and interpolation.

@mixin placeholder ($rules) {

  @each $rule in $rules {
    ::-webkit-input-placeholder,
    :-moz-placeholder,
    ::-moz-placeholder,
    :-ms-input-placeholder {
      #{nth($rule, 1)}: #{nth($rule, 2)};
    }  
  }
}

$rules: (('border', '1px solid red'),
         ('color', 'green'));

@include placeholder( $rules );
Kurt Mueller
  • 3,173
  • 2
  • 29
  • 50
3

To avoid 'Unclosed block: CssSyntaxError' errors being thrown from sass compilers add a ';' to the end of @content.

@mixin placeholder {
   ::-webkit-input-placeholder { @content;}
   :-moz-placeholder           { @content;}
   ::-moz-placeholder          { @content;}
   :-ms-input-placeholder      { @content;}
}
0

I use exactly the same sass mixin placeholder as NoDirection wrote. I find it in sass mixins collection here and I'm very satisfied with it. There's a text that explains a mixins option more.

Nesha Zoric
  • 6,218
  • 42
  • 34