Liquid effect in CSS with SVG filters

Everything in the DOM is a square and developers go to great lengths to give their CSS animations round or assymetric features. SVG filters introduced in CSS3 give us so many new options, I will show you how to create so called 'sticky/liquid' effect in few easy steps.

Everything in the DOM is a square and developers go to great lengths to give their CSS animations round or assymetric features. In this guide we won’t be using SVG filters introduced in CSS3, but SVG filter element that we place directly in HTML markup. I will show you how to create so called ‘sticky/liquid’ effect in few easy steps.

Final result (hover over buttons):

Full video guide: https://www.youtube.com/watch?v=eExhAt8luvU&t

  • 1) Choose any Font Awesome icon. Link Font Awesome CDN in the document head. You can Google the latest version or find the link here.

  • 2) Create div element with class of ‘button’. Give it two children – ‘i’ element holding FontAwesome icon and div with class of ‘blurred’. Then create two ‘span’ elements inside of ‘blurred’, we will use them for animation later. We need to structure the elements in this way, otherwise the color of our white icon will be mixing with the black background and the effect will not work

  • <div class="button">
         <i class="fab fa-facebook-f"></i>
              <div class="blurred">
                   <span></span><span></span>
              </div>
    </div>
  • 3) Set basic CSS styles for ‘button’, ‘i’ and ‘blurred’ elements – give ‘blurred’ circle shape with border-radius: 100%. Set ‘button’ to position:relative and center ‘i’ element in the middle with position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);. Also give ‘i’ element higher z-index:

  • .button {
          position: relative;
          width: 50px;
          height: 50px;
    }
    i {
          position: absolute;
          top: 50%; left: 50%;
          transform: translate(-50%, -50%);
          font-size: 20px;
          z-index: 10;
          color: white;
    }
    .blurred {
          position: relative;
          top: 0; left: 0;
          width: 50px;
          height: 50px;
          border-radius: 100%;
    }
    
  • 4) Create ::before and ::after pseudo elements on ‘blurred’ div and take both span elements we created earlier. Hide everything behind the button. ::before element will serve as the middle part that shrinks on hover, so we need to center it.

  • .blurred::before {
             content: '';
             position: absolute;
             width: 50px;
             height: 50px;
             transform: translate(-50%, -50%);
             top: 50%;
             left: 50%;
             background-color: black;
             border-radius: 100%;
             transition: .2s;
    }
    .blurred::after {
             content: "";
             position: absolute;
             width: 50px;
             height: 50px;
             top: 0;
             left: 0;
             background-color: black;
             border-radius: 100%;
             transition: .2s;
    }
     .blurred > span:nth-of-type(1) {
            position: absolute;
            width: 50px;
            height: 50px;
            top: 0;
            left: 0;
            background-color: black;
            border-radius: 100%;
            transition: .2s;
    }
    .blurred > span:nth-of-type(2) {
           position: absolute;
           width: 50px;
           height: 50px;
           top: 0;
           left: 0;
           background-color: black;
           border-radius: 100%;
           transition: .2s;
    }
    
  • 5) ::after element and two spans will create expanding particles on hover, you can experiment with sizes and positions, I chose the following:

  • .button:hover .blurred::before {
           width: 40px;
           height: 40px;
           top: 50%; left: 50%;
    }
    .button:hover .blurred::after {
           width: 15px;
           height: 15px;
           top: 30px;
           left: 45px;
    }
    .button:hover .blurred>span:nth-of-type(1) {
           width: 30px;
           height: 30px;
           top: -10px;
           left: -20px;
    }
    .button:hover > .blurred>span:nth-of-type(2) {
           width: 25px;
           height: 25px;
           top: 40px;
           left: -20px;
    }
    

    Add SVG filter into HTML markup

  • 6) Add SVG filter directly in our HTML markup, I usually leave it at the bottom of the page, just before the closing body tag

  • <svg>
     <defs>
      <filter id='goo'>
       <feGaussianBlur in='SourceGraphic' 
       stdDeviation='10' result='name'/>
        <feColorMatrix in='name' mode='matrix'
            values='1 0 0 0 0
                    0 1 0 0 0 
                    0 0 1 0 0
                    0 0 0 30 -15 ' result='aab'/>
        <feBlend in='SourceGraphic' in2='aab'/>
       </filter>
     </defs>
    </svg>
          

    You can also tweak the appearance of the effect by adjusting values in <feColorMatrix> SVG filter element. If you want to experiment and learn more you can find some information at https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feColorMatrix.

  • 7) Now we can use the filter on our ‘blurred’ div element to apply the effect.

  •  .blurred {
           filter: url(#goo);
    }
    
  • 8) I also will adjust values of ‘transition’ property on animated elements to make the effect more assymetric. Feel free to experiment with different timing, positions and cubic-beziers to create your own ‘goo’ animations.

  • Final CSS:

    .button {
          position: relative;
          width: 50px;
          height: 50px;
    }
    i {
          position: absolute;
          top: 50%; left: 50%;
          transform: translate(-50%, -50%);
          font-size: 20px;
          z-index: 10;
          color: white;
    }
    .blurred {
          position: relative;
          top: 0; left: 0;
          width: 50px;
          height: 50px;
          border-radius: 100%;
          filter: url(#goo);
    }
    .blurred::before {
          content: "";
          position: absolute;
          width: 50px;
          height: 50px;
          transform: translate(-50%, -50%);
          top: 50%;
          left: 50%;
          background-color: black;
          border-radius: 100%;
          transition: 1.5s cubic-bezier(0,2,.61,-0.05);
    }
    .blurred::after {
          content: "";
          position: absolute;
          width: 50px;
          height: 50px;
          top: 0;
          left: 0;
          background-color: black;
          border-radius: 100%;
          transition: 1.7s cubic-bezier(0,2,.61,-0.05);
    }
    .blurred > span:nth-of-type(1) {
           position: absolute;
           width: 50px;
           height: 50px;
           top: 0;
           left: 0;
           background-color: black;
           border-radius: 100%;
           transition: 1.5s cubic-bezier(0,2,.61,-0.05);
    }
    .blurred > span:nth-of-type(2) {
            position: absolute;
            width: 50px;
            height: 50px;
            top: 0;
            left: 0;
            background-color: black;
            border-radius: 100%;
            transition: 1.6s cubic-bezier(0,2,.61,-0.05);
    }
    .button:hover .blurred::before {
            width: 40px;
            height: 40px;
            top: 50%; left: 50%;
     }
    .button:hover .blurred::after {
            width: 15px;
            height: 15px;
            top: 30px;
            left: 45px;
    }
    .button:hover .blurred>span:nth-of-type(1) {
             width: 30px;
             height: 30px;
             top: -15px;
             left: -15px;
    }
    .button:hover > .blurred>span:nth-of-type(2) {
             width: 25px;
             height: 25px;
             top: 40px;
             left: -20px;
    }
    

    Leave a Reply