117

I am experimenting with css filters.

And I would like use the blur and grayscale at the same time, but I can't seem to use both simultaneously on the same image?

See fiddle here...

http://jsfiddle.net/joshmoto/fw0m9fzu/1/

.blur {
    filter: blur(5px);
    -webkit-filter: blur(5px);
    -moz-filter: blur(5px);
    -o-filter: blur(5px);
    -ms-filter: blur(5px);
}

.grayscale {
    filter: grayscale(1);
    -webkit-filter: grayscale(1);
    -moz-filter: grayscale(1);
    -o-filter: grayscale(1);
    -ms-filter: grayscale(1);
}


.blur-grayscale {
    filter: blur(5px) grayscale(1);
    -webkit-filter: blur(5px) grayscale(1);
    -moz-filter: blur(5px) grayscale(1);
    -o-filter: blur(5px) grayscale(1);
    -ms-filter: blur(5px) grayscale(1);
}
BenMorel
  • 34,448
  • 50
  • 182
  • 322
joshmoto
  • 1,650
  • 2
  • 14
  • 21

4 Answers4

184

Because it's one property named filter, every time you want to add a style to it you override it.

CSS version 1

Fortunately you can add multiple styles in some properties like background-image and filter! To get this working you'll have to put all the filter styles in one space separated filter property.

.grayscale.blur {
    filter: blur(5px) grayscale(1);
}

CSS version 2

An alternative, flexible, solution would be to create a "div soup" on purpose and set different filters in the html stack. e.g.

<div class='demo__blurwrap' style='filter: blur(5px);'>
    <div class="demo__graywrap" style='filter: grayscale(1);'>
        <img src="awesome_image.jpeg" alt="">
    </div>
</div>

CSS version 3

edit: just realised I just wrote this version with transforms, but the same idea applies.

Yet another solution is CSS vars. I wouldn't say it's ideal but it's a nice experiment. The major downside is that you need to declare a lot of variables, have default long rules for transform and nested transforms will definitely break.

// Added just for fun
setInterval(() => {
  yes_this_works_and_one_of_many_reasons_ids_are_bad.classList.toggle('translate');
}, 1000);
setInterval(() => {
  yes_this_works_and_one_of_many_reasons_ids_are_bad.classList.toggle('scale');
}, 1500);
:root {
  --scale: 1;
  --translate: 0px;
}
.box {
  background: blue;
  width: 20px;
  height: 20px;
  transform: 
    scale(var(--scale))
    translate(var(--translate), var(--translate));
  transition: transform .3s;
}
.box.translate {
  --translate: 20px;
}
.box.scale {
  --scale: 3;
}
<div 
  id='yes_this_works_and_one_of_many_reasons_ids_are_bad' 
  class='box scale translate'
></div>

Javascript

Lastly, if you were to use JavaScript to render the styles you can read the current applied filters using getComputedStyle and add more to the mix.

And a relevant article - this is more for animations and not yet supported by many browsers: Additive animations

And another relevant article on css-tricks: Houdini

Jimbo Jonny
  • 3,549
  • 1
  • 19
  • 23
René
  • 6,006
  • 3
  • 22
  • 40
  • 1
    Yeah, I like your "Maybe in the future" suggestion, which would be ideal. – MSC May 31 '18 at 06:31
  • And the same for transforms would be also great :) – yunzen Jul 04 '18 at 10:53
  • Is this "maybe in the future" section based on anything? Does it come from any sort of proposed addition to the spec? Any proprietary implementation? Or is it just a section of non-relevant musing? – Jimbo Jonny Dec 03 '19 at 02:27
  • @JimboJonny It's been a long time since I wrote this answer, if I remember correctly I saw it as a suggestion in some csswg mailing list. But it's probably closest to "non relevant musing" – René Dec 03 '19 at 07:56
  • Why are properties like `transform` comma separated but `filter` _space_ separated? confusing – Elijah Mock Dec 17 '20 at 17:47
  • @ElijahMock The usual difference is it either being an actual list (like multiple background images and transforms) or it being 1 property that accept an actual value-set like `margin: 0 1px 3px 0;`. For `background` a combination of both is available. Having only spaces for this would not work. Also, to make it easier to remember, lists are usually not limited in amount and order while value-sets are predefined. – René Dec 18 '20 at 07:43
8

I'm trying to create utility classes in vanilla CSS and this would be helpful but it looks like it can not be done this way.

<img class="brightness-20 image-grayscale-100">
.brightness-20 {
    filter:brightness(20%);
}
.image-grayscale-100 {
    filter: grayscale(100%);
}

I'm not sure why they didn't just create a more specific property like:

filter-brightness: 20%; filter-grayscale: 100%

After some more work I came up with this solution:

/*Initalize Variables No Adjustments*/
:root {
    --blur:0px;
    --contrast:100%;
    --brightness:100%;
    --contrast:100%;
    --dropshadow:0px 0px 0px black;
    --grayscale:0%;
    --hue-rotate:0deg;
    --invert:0%;
    --opacity:100%;
    --saturate:100%;
    --sepia:0%;
}
/*Apply Defult Variables To Image*/
.filter {
    filter: blur(var(--blur)) contrast(var(--contrast)) brightness(var(--brightness)) contrast(var(--contrast)) drop-shadow(var(--dropshadow)) grayscale(var(--grayscale)) hue-rotate(var(--hue-rotate)) invert(var(--invert)) opacity(var(--opacity)) saturate(var(--saturate)) sepia(var(--sepia)); 
}
/*Override Defults*/
.brightness-20 {
    --brightness:20%;
}
.image-grayscale-100 {
    --grayscale: 100%;
}
Billy Mitchell
  • 119
  • 2
  • 5
3

As this seems to be missing from the answers so far and only mentioned in a comment, here is what works:

Simply combine multiple filters separated by space, such as:

filter: grayscale() blur(5px);

Also see here.

Gorgsenegger
  • 7,356
  • 4
  • 51
  • 89
2

I am using this.

/* Multiple filters */
backdrop-filter: url(filters.svg#filter) blur(4px) saturate(150%);