Search code examples
htmlioscsssafari

How to squircle an app icon image with just CSS


I've been banging my head against the wall trying to figure out how to use The Code Player's CSS3 Squircles example to create an iOS-7-style app icon on my website (testing in the Safari browser). The example uses pseudo tags to clip the background color, whereas I need to crop around an <img>. In case you're not familiar, a squircle is like a rounded-rect, but with the sides rounding beyond the corner radius, like so:

a squircle

.icons img {
  width: 100px;
  height: 100px;

  border-radius: 24%;
}

.icons a {
  text-decoration: none;
  display: inline-block;
  position: relative;
}
/*Now we will create fish-eye shapes using pseudo elements and clip them to add a curve to the sides of the icons*/
.icons a:before, .icons a:after {
  content: '';
  position: absolute; left: 0; top: 0;
  width: 100%; height: 100%;
  background: inherit;
  border-radius: 100%; /*circle*/
  /*time to transform the circle into fish-eye shape. iOS7 style now.*/
  -webkit-transform: scaleX(2) scaleY(1.05);
  transform: scaleX(2) scaleY(1.05);
  /*clipping the left and right sides - 17px from the left and right*/
  clip: rect(0, 66px, 100px, 34px);
  /*pushing it behind the icon*/
  z-index: -1;
}
/*duplicating the :before element and rotating it 90deg and swapping the X/Y transforms*/
.icons a:after {
  -webkit-transform: scaleY(2) scaleX(1.05) rotate(90deg);
}
<div class="icons">
  <a href="#"><img src="http://lorempixel.com/256/256/abstract/2/" /></a>
</div>


Solution

  • The easiest solution might be to create the image with a transparent background until some of the following features are implemented.

    If you can add the image via CSS then you could just add height, width, background-image and background-size to the link (.icons a).

    Note: This might not be the desired effect as it is complemented by a background colour.

    .icons a {
          height: 100px;
          width: 100px;
          background-image: url(https://picsum.photos/256/);
          background-size: cover;
          text-decoration: none;
          color: white;
          display: inline-block;
          margin: 20px;
          border-radius: 24px;
          position: relative;
        }
    
        .icons a:before,
        .icons a:after {
          content: '';
          position: absolute;
          left: 0;
          top: 0;
          width: 100%;
          height: 100%;
          background: inherit;
          border-radius: 100%;
          -webkit-transform: scaleX(2) scaleY(1.05);
          transform: scaleX(2) scaleY(1.05);
          clip: rect(0, 66px, 100px, 34px);
          z-index: -1;
        }
    
        .icons a:after {
          -webkit-transform: scaleY(2) scaleX(1.05) rotate(90deg);
          transform: scaleY(2) scaleX(1.05) rotate(90deg);
        }
    <div class="icons">
        <a href="#"></a>
    </div>

    If this is not the case you could add size and border radius to the image. In this case the the pseudo rounded borders are filled by a background colour on the '.icon a' element.

    Note: This might not be the desired effect as it is complemented by a background colour.

    .icons a {
          height: 100px;
          width: 100px;
          background: red;
          text-decoration: none;
          color: white;
          display: inline-block;
          margin: 20px;
          border-radius: 24px;
          position: relative;
        }
        .icons img{
          height: 100px;
          width: 100px;
          border-radius: 24px;
        }
        .icons a:before, .icons a:after {
          content: '';
          overflow: hidden;
          position: absolute; left: 0; top: 0;
          width: 100%; height: 100%;
          background: inherit;
          border-radius: 100%;
          -webkit-transform: scaleX(2) scaleY(1.05);
          transform: scaleX(2) scaleY(1.05);
          clip: rect(0, 66px, 100px, 34px);
          z-index: -1;
        }
        .icons a:after {
            -webkit-transform: scaleY(2) scaleX(1.05) rotate(90deg);
            transform: scaleY(2) scaleX(1.05) rotate(90deg);
        }
    <div class="icons">
        <a href="#">
        <img src="https://picsum.photos/256/">
        </a>
    </div>  

    SVG Solution 1: Use a cliping-path using an svg but this is not yet supported by webkit (sticks the clipped image at the top left of the screen). See this link for more info: https://css-tricks.com/clipping-masking-css/#comment-1587234

    #squircle{  
      -webkit-clip-path: url(#svg-shape);
      -moz-clip-path: url(#svg-shape);
      -o-clip-path: url(#svg-shape);
      -ms-clip-path: url(#svg-shape);
      clip-path: url(#svg-shape);
    }
    <img src="https://picsum.photos/400/" id="squircle">
    
    <svg height="0" width="0" version="1.1"
         xmlns="http://www.w3.org/2000/svg">
      <defs>
      <clipPath id="svg-shape">
    <path d="M100,200c43.8,0,68.2,0,84.1-15.9C200,168.2,200,143.8,200,100s0-68.2-15.9-84.1C168.2,0,143.8,0,100,0S31.8,0,15.9,15.9C0,31.8,0,56.2,0,100s0,68.2,15.9,84.1C31.8,200,56.2,200,100,200z" />
        </clipPath>
        </defs>
    </svg>

    SVG Solution 2: Use a pattern to add the image as a background image.

    svg.iOS-svg {
      height: 100px;
      width: 100px;
    }
    <svg class="iOS-svg" viewBox="0 0 200 200">
      <defs>
        <pattern id="squircle" patternUnits="userSpaceOnUse" width="200" height="200">
            <image xlink:href="https://picsum.photos/256/" x="0" y="0" width="200" height="200" />
        </pattern>
      </defs>
    
        <path d="M100,200c43.8,0,68.2,0,84.1-15.9C200,168.2,200,143.8,200,100s0-68.2-15.9-84.1C168.2,0,143.8,0,100,0S31.8,0,15.9,15.9C0,31.8,0,56.2,0,100s0,68.2,15.9,84.1C31.8,200,56.2,200,100,200z" fill="url(#squircle)" />
    </svg>

    Other Resources: http://caniuse.com/#search=clip-path (Partial support at time of writing) SVG support: http://caniuse.com/#search=svg