Search code examples
htmlcssbackground-imagecss-shapes

Custom shape with background image


I wanna create a horizontal rectangle with a semicircular in its middle for my navbar menu. In addition to that all of this shape must have a background image.

Something like this:

sample

What is the best way to achieve this goal?


Solution

  • Using SVG: (recommended)

    The best tool to create shapes like these is SVG and not CSS. SVGs are scalable (and so useful for responsive designs), they allow us greater control over the shapes aspects - like the slope/curvature of the circle or ellipse and can also have images or gradients as fills (background) like show below.

    It is very easy to draw the shape using SVG. Just use a path element along with the commands like move (M), arc (A), line (L) and close-path (z). Once shape is drawn, apply the image as its fill using pattern and image elements. The xlink:href attribute refers to the image's source.

    Below is a very short explanation of what the above commands do. Detailed explanation can be found in this MDN page:

    • M - Moves pen to the point specified by the coordinate given immediately after the command.
    • A - Draw an arc with the specified X and Y radius, ending at the point specified after command.
    • L - Draw a straight line from one specified point to another.
    • z - Close the path by drawing a straight line from path's last point to its first point.

    path {
      fill: url(#g-image);
    }
    body {
      background-image: radial-gradient(circle, #3F9CBA 0%, #153346 100%);
    }
    <svg viewBox='0 0 500 60' preserveAspectRatio='none'>
      <defs>
        <pattern id="g-image" width="1" height="1" patternUnits="objectBoundingBox">
          <image xlink:href="http://lorempixel.com/500/60/abstract/6" width="500" height="60" />
        </pattern>
      </defs>
      <path d='M0,0 0,25 205,25 A50,50 0 0,0 295,25 L500,25 500,0z' />
    </svg>


    Using Clip-path:

    The other alternate is use to use a clip-path but its pure CSS version cannot be used because (a) it can create only simple/basic shapes and not paths like the one that we need and (b) it doesn't work in Firefox. So, we will have to use clip-path with an inline SVG element like shown below.

    The drawback of using clip-path is that this won't work in IE even with the inline SVG element.

    div {
      height: 75px;
      width: 600px;
      background: url(http://lorempixel.com/500/100/abstract/6);
      -webkit-clip-path: url(#clipper);
      clip-path: url(#clipper);
    }
    body {
      background-image: radial-gradient(circle, #3F9CBA 0%, #153346 100%);
    }
    <svg width='0' height='0'>
      <defs>
        <clipPath id='clipper' clipPathUnits='objectBoundingBox'>
          <path d='M0,0 0,.42 .41,.42 A.1,.83 0 0,0 .59,.42 L1,.42 1,0z' />
        </clipPath>
      </defs>
    </svg>
    <div></div>


    Using CSS Masks:

    This option is currently not supported due to its poor browser support but is a very good option when all browsers start supporting it. In this method, we create a radial-gradient based mask which cuts out the bottom part of the image except for that circular arc area. Since the gradients can take a fixed pixel value as radii for the X and Y axis of the circular cut, the length of that arc area will not increase even if the image is stretched to be responsive.

    div {
      height: 100px;
      width: 100%;
      background: url(http://lorempixel.com/600/100/abstract/6);
      -webkit-mask-image: linear-gradient(to right, white, white), radial-gradient(145px 145px at 50% -25px, white 50%, transparent 51%);
      -webkit-mask-position: 0% 0%, 0% 100%;
      -webkit-mask-size: 100% 50%;
      -webkit-mask-repeat: no-repeat;
    }
    <div></div>