1

I'm trying to reproduce this heart-shaped "like" button:

Screenshot of two buttons, one an outline of a heart for the inactive state and the other a filled heart for the hovering state.

(Inactive state at the top, hovering state at the bottom.)

The hover has to be animated with a transition, like this:

Animation showing hover transition.

I've tried using Font Awesome and Material Icons, but I can't figure out how to do a proper transition with them. background-color leaves the borders of the icon.

Darryl Noakes
  • 2,207
  • 1
  • 9
  • 27
8bitmd
  • 29
  • 8

2 Answers2

2

For icons like these, using Scalable Vector Graphics (SVGs) is ideal for a multitude of reasons, but in this specific case especially so because of the fact you can use CSS to style individual elements of the SVG.

.heart .heart-fill {
  /* notice the defined alpha component. you could also use opacity though, of course */
  fill: #00000000;
}

.heart .heart-outline {
  fill: #000000;
}

.heart .heart-outline,
.heart .heart-fill {
  transition-property: fill;
  transition-duration: 0.3s;
  transition-timing-function: ease-in-out;
}

#heart {
  width: 300px;
  height: auto;
}

#heart:hover {
  cursor: pointer;
}


/* this uses the bounding box of the whole svg for hovering, which is perfectly fine for small buttons, but may be slightly problematic for using this svg in a larger size (.heart-fill:hover + .heart-outline should work for that case though) */
#heart:hover .heart-fill,
#heart:hover .heart-outline {
  fill: #ff0000;
}
<!-- container -->
<div id="heart">
  <!-- source: https://www.svgrepo.com/svg/13666/heart -->
  <!-- edited with Adobe Illustrator to add the solid section -->
  <svg class="heart" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 800 800" style="enable-background:new 0 0 800 800;" xml:space="preserve">
<path class="heart-fill" d="M735.4,113.6C693.5,71.7,638,48.8,578.8,48.8s-114.8,23.1-156.7,65l-21.9,21.9L378,113.5c-41.9-41.9-97.7-65.1-156.9-65.1
    c-59,0-114.6,23.1-156.4,64.8C22.9,155-0.2,210.6,0,269.8C0,329,23.2,384.5,65.1,426.4l318.5,318.5c4.4,4.4,10.3,6.8,16.1,6.8
    s11.7-2.2,16.1-6.6l319.2-318c41.9-41.9,65-97.5,65-156.7C800.2,211.2,777.3,155.5,735.4,113.6z"/>
<path class="heart-outline" d="M735.4,113.6C693.5,71.7,638,48.8,578.8,48.8c-59.2,0-114.8,23.1-156.7,65l-21.9,21.9L378,113.5
    c-41.9-41.9-97.7-65.1-156.9-65.1c-59,0-114.6,23.1-156.4,64.8C22.9,155-0.2,210.6,0,269.8C0,329,23.2,384.5,65.1,426.4l318.5,318.5
    c4.4,4.4,10.3,6.8,16.1,6.8c5.8,0,11.7-2.2,16.1-6.6l319.2-318c41.9-41.9,65-97.5,65-156.7C800.2,211.2,777.3,155.5,735.4,113.6z
     M702.8,394.7L399.7,696.5L97.4,394.1c-33.2-33.2-51.6-77.3-51.6-124.3s18.1-91.1,51.4-124.1c33.1-33.1,77.2-51.4,124-51.4
    c47,0,91.2,18.3,124.5,51.6l38.3,38.3c9,9,23.4,9,32.4,0l38-38c33.2-33.2,77.5-51.6,124.3-51.6c46.8,0,90.9,18.3,124.1,51.4
    c33.2,33.2,51.4,77.3,51.4,124.3C754.4,317.3,736.1,361.4,702.8,394.7z"/>
</svg>
</div>

The only downside to this is that you need to actually include the svg tag everywhere, including all it's data. The styles can be in an external stylesheet though. Including the SVG as an img or an object would prevent you from interacting with it's styles.

You also need to have two separate elements (the outline/border, and the filled in shape) inside the SVG, as CSS border or outline do not get applied to those elements, but their bounding box instead. There is of course the stroke attribute that would work fine on the filled shape element, but you have no control over the direction of it (like applying the stroke inside the path), so it's not ideal.

The other solution would be using normal rasterized images stacked on top of one another and styling their opacity:

.heart-fill {
  opacity: 0;
}

.heart-outline {
  opacity: 1;
}

.heart-fill, .heart-outline {
  transition-property: opacity;
  transition-duration: 0.3s;
  transition-timing-function: ease-in-out;
}

#heart {
  width: 300px;
  height: auto;

  position: relative;
}

#heart .heart-outline,
#heart .heart-fill {
  width: 100%;
  height: auto;
}

#heart .heart-fill {
  position: absolute;
  top: 0;
  left: 0;
  
  pointer-events: none;
  
  z-index: 1;
}

#heart:hover {
  cursor: pointer;
}

#heart:hover .heart-fill {
  opacity: 1;
}

#heart:hover .heart-outline {
  opacity: 0;
}
<!-- container -->
<div id="heart">
  <!-- source: https://static.thenounproject.com/png/2175990-200.png -->
  <!-- edited with Adobe Photoshop to create the border version, then encoded as inline Base64 png -->
  <img class="heart-fill" type="image/png" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAF+mlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIzLTA4LTIzVDE2OjIyOjQ5KzAyOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMy0wOC0yM1QxNjoyNzo1MSswMjowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMy0wOC0yM1QxNjoyNzo1MSswMjowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpmOGNkM2JiYi0xNzBiLWRlNDEtYmNkOC0xMzNhM2UzOWI3MzgiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDowZDYxZWNiNi0yNjY0LTg3NDAtYWJmMC01ZTk3NGRjY2U0NjEiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3ODJiMjk0ZC1iZjkxLWU1NDQtODNjOS0yMmY0ZjhmZDJhNmYiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjc4MmIyOTRkLWJmOTEtZTU0NC04M2M5LTIyZjRmOGZkMmE2ZiIgc3RFdnQ6d2hlbj0iMjAyMy0wOC0yM1QxNjoyMjo0OSswMjowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDpmOGNkM2JiYi0xNzBiLWRlNDEtYmNkOC0xMzNhM2UzOWI3MzgiIHN0RXZ0OndoZW49IjIwMjMtMDgtMjNUMTY6Mjc6NTErMDI6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6dY3F1AAAItklEQVR42u3dXWiV9x3A8S8SJIQiYxRvvNhFYfSijJZR6oUUdjcGZb0ZY7splI3CRqGwwehg8Eh9ra3v1TXxJTOarFN7ool2mfOlqRbjrGgXnZupojaNtroZjBpfcp5d5LS4Lsa8nDzP/3/O9+J76YX/3/+T55znPC+kaYqZjZyLYCYQM4GYCcRMIGYCMROImUDMBGImEDMTiJlAzARiJhAzgZgJxEwgZgIxE4iZQCbwDyG6FsJTK+GVeli1GVoLcLgDeg7Axfeh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" />
  <img class="heart-outline" type="image/png" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAF+mlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIzLTA4LTIzVDE2OjIyOjQ5KzAyOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMy0wOC0yM1QxNjoyNzozMyswMjowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMy0wOC0yM1QxNjoyNzozMyswMjowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo5ZjE5ZTJmZC1lNGEyLTgyNDgtOGUyMS1hMDA4MzY5MDE2ZmMiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDpmMDZmODlkNy0xODE4LTkzNDEtODJmMC01ZjlhZTE2MjFmMjUiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo0ODViMmMyNC01MWE5LTlkNGUtYTY0Zi1jZDMzYjQwNTgyMDAiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjQ4NWIyYzI0LTUxYTktOWQ0ZS1hNjRmLWNkMzNiNDA1ODIwMCIgc3RFdnQ6d2hlbj0iMjAyMy0wOC0yM1QxNjoyMjo0OSswMjowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo5ZjE5ZTJmZC1lNGEyLTgyNDgtOGUyMS1hMDA4MzY5MDE2ZmMiIHN0RXZ0OndoZW49IjIwMjMtMDgtMjNUMTY6Mjc6MzMrMDI6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4e4hS0AAAK50lEQVR42u3da7BVZR3H8e85cOSWQBZyCQm6ieholCIx4nQRm9IyZhqUIqFRK16gmEipXaYSEwkRnUFDJjATIQoprUktG2nMW6FO3rpBSCZRiJoGEpzTi/OcyRgO7Gfttfa67O+a+b1xBmet//P/nL3Xs571bDo6OjDG7D8WwRiBGCMQYwRijECMEYgxAjFGIMYIxBiBGGMEYoxAjBGIMQIxRiDGCMQYgRgjEGMEYoxAEvyjch7DgPcCU4ELgfnACmAN8ENgLbAuZAVwFXAxMB04FRhOdY4jwjXNCNe4IFzzbSFrQ03WhP8+P9Rsaqjh0DJetED+dwwOg7kAuBvYBnSkkOeB9cCS0FxvKkEthgOfDuf8K2BHSrX4O3BX+ENyFjBIIMUF0hM4GbgC2AC0p9QEteRJYDFwGtCrALXoBZwOXAs81cA6tAO/AS4HTgJ6CCT/YwywMPw16yhAngeuB96TQy0mADek+AlRb54Lny6jBdLYozdwHvBgQRqhu/wBmAv0z7AW/YEvAH8seC3uB87J+xO26kD6Ap8Pf5k6SpQd4WvHG1OsxSBgHvBCyWrxLDAb6COQ9I5+4a/ktpI1w755BbgaOKyOWhwGLAr/rzLXYmuYPesjkPqOjwGbS94M++Yf4Stia0QdWoHPAv+sWC02hQkFgUQeI4HbK9YM++Yh4IQaanEi8HDFa7EOGCGQ2o4LgX9XvCG6sjc8hOu5nzq0hVmg9iapxcvALIEc+Pv1j3O4afwlsDLcH6wA7gS25DDLM/I1tRiVwyzdM+Hal4darAy1+VuDz2MtMFAg/3+Mb8C9xtYw6HOAU4BBB7nG1wMTgZmhaf6a8fm9AEwJyXp2agvwnXBtJwEDD1KLw4FJ4cb61gZMmGys8etnUwCZCezOoMj/Ae4FLgHGAi11L1yDo4DzgTuAXSX6+rIr3NPNAkanUIcW4N3AZXQuX9mTwTm/Cpzb7EC+nkFhnwjPSw7PdJVn5yfM54BfFxjGfWH2a2DGtRgSPl2yWNry5WYE0hqWRaRVxJ3AUmBcLkui4e3hZvvFAqB4EbgSeFtOtRgPLEv5E/a6yGnxUgM5BPhBig/fFgJDCvHuQOfN5WXhWUejYWwDLgUGFKQWw8JDzbRmJFeFmb1KA2kFvp/SlOCVB7vRzrE5+obp6u0NgLE9LN/oW9BaDA7T1WlAuSXc/1QWyHUpFOk2YEQp3kbrXH+1NDzvyOIZyg3AG0pSi7Qe/i6sKpAvpbEsoZSvbXZOWT6UIo4HgONLWoszUpjSn1M1IOfWWZCrgT6lfre58+vl3DD9nLQOu8NsUUvJa9GPzhe66nk5a1pVgJxQx3OO7cBHKrUJQOcsz6aED8/GVawWk0n+gtcu4LiyA+kP/LmO5RcjqtQQr6nfAGB1ZAZUtBaj6vj6+VT4NCotkNUJL3wJ0Oa2M02ytU7n1P+NCXtleVmBnJfwgr9h0zQtlAUJe+aTZQMyIuFbbzfaKE0NpAW4OUHfvEQNe3UVCciahC8Q+bVKJL2ARxP0z3fLAuT9CdcPvcUGMaG/RofVErFTvxOKDqRnWE0bC2SKjWH26bEZCfpow4EWNRYByAUJLmqpDWG66bPvJeinzxQVSK8Er2duLuoiO1MIIIcm6Km/dPOOf+5Akiwn+biNYA7Sa59Ka9o3TyCtwNORF/ELG8DUOPV7X2RvPVY0IJMTvC8+xgYwNfbb2ASvDHyoSEDWR578NQ68iey5b0f22F1FATIycnOzXcBQB91E9tybI18V2Lvv0/W8gHzR5SSmoNO+s4sA5LFI1Uc62CZh3x0bCeTBvIEcHbutpANt6kTy08iee2ueQL4SebITHWRTJ5BJkT13cZ5A7oncdKHFQTYpvNsf83T9J3kBaYvc6+ibDrBJCcmiyE3BW/MAMiHyo+5YB9ekBGRcZO+9Kw8gMdO7jzuwJmUkf4qd7m00kHURJ/g1B9WkDOSqiP5bnQeQJyNO8H0OqkkZyIcj+u+RRgNpjdjefnfZd0U0hd1brNYFjP9qNJCRMT/i4oCajJD8NqIPhzYSyCSnd03JpntPbiSQmE3hznAwTUZApkT04fRGArko4sSOcjBNRkDeGdGHsxoJ5KsRq3d7OZgmIyCviwBySSOB1LqH6iYH0mSMpNZ1WVc0Esj1tb726CCajIHcW2MvXttIICt8e9AUBEimvZj1J8jPHUTTjJ8gtd6DbHYQTTPeg9Q6i9UO9HYgTbPNYsU8B3GDOJMVkLFFfQ4Ssw/vZAfTZATkzKI+Sf9AxInNdzBNRkCuKeparBERJ/aAg2kyArKhqKt5W4CdERtV93NATco4Bhb5fRAif2rtFAfVpAzktMifZms4kLURJzjPQTUpA4n5TfVVeQCZG3GCTzuoJmUgGyP674I8gJwYuzeRA2tSwjE+svfG5gGkZ+RvWS9wcE1KQBZH9N2OvHZWBLg74kSfcW9ekwKOHsBzEX13R56bV18a+VHn/limXiCnRvbcnDyBHBl5src7yKZOID+L7LlRef/CVMzeRO3A0Q60acAmDR3A/UX4CbY5kSe9wsE2CftuZWSvnV8EIEdE/srtbmC4A24ie24UsCeiz/YAQ4ryO+n3RMpe4qCbyJ5bFtljdxbld9IBTo88+T3AcQ68qbHfjo9YmNiVDxYJSAvweOQFrHfwTY0rxx+I7K1H9tekeQIBmB55ER3AJ2wCc5Bem5Ggr6YWEUgbsCXyQp4FDrURTDd9NgDYGtlTG8PT9sIBAZiZQPvNNoPpps9WJeinc7prziIA6QE8muCizrYhzD49dk6CPnq4a2FiUYEATExwYS8D77AxTOivMcArkT3UHl7BoOhAAG5JgOQRfyrBAL2B3yXon+UHa8oiARkGvJTkfsQl8U0/pXtrgr55ARhcJiAA0xJcqC9WNTeQRQl75sxaGrJoQABuSnjBF9kwTYdjbsJeWVZrMxYRSD/g9wkuuh2YZuM0DY6zIxe8duUJoG+ZgXSt4d+V4OJ3Ax+1gSqPY3LYXDC2P3YCx8Q0YlGBdN2PJPkL8apIKo9jd8JvGGfFNmGRgSR5sUok4ugus5M0YNGBAHxLJKZOHPOTNl8ZgLSEZx0iEUeSHrgp9FBlgXSt+v2RSMQRmbVhs0KqDgTgEJGIIxJHW71NVyYgIhFHQ3GUEYhIxNEwHGUFIhJxNARHmYGIRByZ4yg7EJGII1McVQAiEnFkhqMqQEQiDgQiEnE0GEfVgIhEHAIRiTgahaOqQEQiDoGIRBwCEYk4csTRDEBEIg6BiEQcAhGJOAQiEnEUB0czAhGJOAQiEnEIRCTiEIhIxCEQkYijrajNIRCRiEMgIhGHQEQiDoGIRBwCEYk4BCIScQikzEBE0uQ4BCIScQhEJOIQiEjEIRCRiEMgIhGHQEQiDoFUGUglkYhDICIRh0BEIg6BiEQcAhGJOAQikhyQiEMgIhGHQEQiDoGIRBwCEUmWSMQhEJGIQyAiEYdARJIKEnEIRCTiEIhI4pCIQyAiEYdARBKHRBwCEUk3SMQhEJF0g0QcAhHJgZGIQyAiSTniEIhIxCEQkYhDICIRh0A8skQiDoGIRBwCEYk4BOKRChJxCEQk4hCIRxwScQikaY82YAnQvh8Ye4HFQE/LJJBmP44B5oVPlHXA5cAYy1JSIMY0SyyCMQIxRiDGCMQYgRgjEGMEYoxAjBGIMQIxxgjEGIEYIxBjBGKMQIwRiDECMUYgxgjEmCbPfwG89IKm2PNMXgAAAABJRU5ErkJggg==" />
</div>

I'd definitely recommend using the SVG approach though, as it is just so much more flexible, and also has the advantage of being a vector image. Adding a gradient (like in your example) to the SVG would be as simple as adding a CSS linear-gradient to the background of the .heart-fill class. And you could of course animate that too.

EDIT: Well, not quite. I misremembered that. You define an SVG linearGradient element inside the defs of the SVG, then use it as the fill style of the shape you want to apply the gradient on (by way of CSS url() and the id of the gradient).

/* need to use opacity for the transitions now, as fill transitions don't play nice with gradients */
.heart-fill {
  opacity: 0;
  fill: url(#heart-gradient);
}

.heart-outline {
  opacity: 1;
  fill: #000000;
}

.heart-outline,
.heart-fill {
  transition-property: opacity;
  transition-duration: 0.3s;
  transition-timing-function: ease-in-out;
}

.heart-fill {
  background:
}

#heart {
  width: 300px;
  height: auto;
}

#heart:hover {
  cursor: pointer;
}

#heart:hover .heart-fill {
  opacity: 1;
}

#heart:hover .heart-outline {
  opacity: 0;
}
<div id="heart">
  <!-- source: https://www.svgrepo.com/svg/13666/heart -->
  <!-- edited with Adobe Illustrator to add the solid section -->
  <svg class="heart" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 800 800" style="enable-background:new 0 0 800 800;" xml:space="preserve">
<defs>
    <linearGradient id="heart-gradient" gradientTransform="rotate(90)">
      <stop offset="5%" stop-color="#ff7f00" />
      <stop offset="95%" stop-color="#ff0000" />
    </linearGradient>
</defs>
<path class="heart-fill" d="M735.4,113.6C693.5,71.7,638,48.8,578.8,48.8s-114.8,23.1-156.7,65l-21.9,21.9L378,113.5c-41.9-41.9-97.7-65.1-156.9-65.1
    c-59,0-114.6,23.1-156.4,64.8C22.9,155-0.2,210.6,0,269.8C0,329,23.2,384.5,65.1,426.4l318.5,318.5c4.4,4.4,10.3,6.8,16.1,6.8
    s11.7-2.2,16.1-6.6l319.2-318c41.9-41.9,65-97.5,65-156.7C800.2,211.2,777.3,155.5,735.4,113.6z"/>
<path class="heart-outline" d="M735.4,113.6C693.5,71.7,638,48.8,578.8,48.8c-59.2,0-114.8,23.1-156.7,65l-21.9,21.9L378,113.5
    c-41.9-41.9-97.7-65.1-156.9-65.1c-59,0-114.6,23.1-156.4,64.8C22.9,155-0.2,210.6,0,269.8C0,329,23.2,384.5,65.1,426.4l318.5,318.5
    c4.4,4.4,10.3,6.8,16.1,6.8c5.8,0,11.7-2.2,16.1-6.6l319.2-318c41.9-41.9,65-97.5,65-156.7C800.2,211.2,777.3,155.5,735.4,113.6z
     M702.8,394.7L399.7,696.5L97.4,394.1c-33.2-33.2-51.6-77.3-51.6-124.3s18.1-91.1,51.4-124.1c33.1-33.1,77.2-51.4,124-51.4
    c47,0,91.2,18.3,124.5,51.6l38.3,38.3c9,9,23.4,9,32.4,0l38-38c33.2-33.2,77.5-51.6,124.3-51.6c46.8,0,90.9,18.3,124.1,51.4
    c33.2,33.2,51.4,77.3,51.4,124.3C754.4,317.3,736.1,361.4,702.8,394.7z"/>
</svg>
</div>
TARN4T1ON
  • 522
  • 2
  • 12
  • 1
    That was incredibly helpful, thank you. I've been trying to implement this for at least 5 hours... Small problem: I can't seem to be able to add a gradient to `.heart-fill` with `linear-gradient`, I'm using the SVG method you mentioned. – 8bitmd Aug 23 '23 at 15:22
  • 1
    @8bitmd Oop, that's because I misremembered how linear gradients work for SVGs haha. You have to define a ```linearGradient``` element inside the SVG, give it an id, and then use ```url(#{id})``` as the value for the ```fill``` property of the shape you want to use it on. I'll add an example to my answer in a moment. – TARN4T1ON Aug 23 '23 at 15:37
  • 1
    @8bitmd Alright, I fixed the description and added an example. – TARN4T1ON Aug 23 '23 at 15:52
  • Done! Honestly, thank you so much. There's an extra issue I'm facing since apparently you can't transition gradients... I'm thinking about adding a second SVG with the gradient below the first SVG and transitioning the opacity instead, thoughts? – 8bitmd Aug 23 '23 at 15:53
  • 1
    @8bitmd I actually already did that in the gradient example. Since you have the outline and filled shape as 2 separate shapes in the SVG already, you can change their opacities independently too. Instead of transitioning the fill, you transition the opacity, and set the fill in the base class. No need for a second SVG, although that would work too of course. – TARN4T1ON Aug 23 '23 at 15:57
  • 1
    Oh, sorry, I didn't see your third example at first. Thank you so much again, truly appreciate it. I can finally take a break haha – 8bitmd Aug 23 '23 at 16:09
1

I figure 2 ways of doing it, the first one:

In this way, you have to "draw" the heart manually with SVG, and then work on hover with css.

.icon-container {
      display: inline-block;
        position: relative;
        overflow: hidden;
        width: 150px;
        height: 150px;
  }

  .icon {
    width: 100%;
    height: 100%;
    fill: white; 
    stroke: black; 
    stroke-width: 0.5; 
    transition: fill 0.3s ease;
   }
  .icon-container:hover .icon path {
fill: red;
stroke: red;
  }
<body>
  <div class="icon-container">
   <svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
      <path d="M12 21.35l-1.45-1.32C5.4 16.36 2 13.28 2 9.5 2 6.42 4.42 4 7.5 4c1.74 0 3.41.81 4.5 2.09C15.09 4.81 16.76 4 18.5 4 21.58 4 24 6.42 24 9.5c0 3.78-3.4 6.86-8.55 10.54L12 21.35z"/>
    </svg>
  </div>
</body>

The second one:

That's the one i prefer, but you have to work with a PNG who have the transparency inside the heart, or you'll get (as the example) the entire image container coloured.

.icon-container {
    display: inline-block;
    position: relative;
    overflow: hidden;
    width: 150px;
    height: 150px;
  }

  .icon {
    width: 100%;
    height: 100%;
    background-image: url('https://cdn-icons-png.flaticon.com/512/1077/1077035.png');
    background-size: cover;
    transition: background-color 0.3s ease;
  }

  .icon-container:hover .icon {
    background-color: red;
  }
<body>
  <div class="icon-container">
    <div class="icon"></div>
  </div>
</body>

Hope it helps!

DarioS
  • 111
  • 1
  • 13
  • 1
    The PNG way really isn't nice... It won't work with icon libraries, it's hard to keep the background inside the outline (yours doesn't), and the outline doesn't change color. – Darryl Noakes Aug 23 '23 at 16:20
  • @DarrylNoakes you're right, wanted just to give some options :D – DarioS Aug 23 '23 at 16:23