htmlcsssvglayoutclip-path

How can I get one SVG path/fill to only be drawn "inside" another, complex SVG path?


I have a bottle shape in SVG that has a decorative swirl and a few other lines crossing the interior such that I'm not sure what counts as "inside" and "outside" the path. I want to put a wave shape "inside" the bottle, but I'm not getting any positive results experimenting with <clipPath> and am not sure if the interior lines in the bottle are working against me.

What I currently have, where the wave shape lies on top of the bottle:

  * {
    margin: 0;
    box-sizing: border-box;
  }
  .container {
    height: 100dvh;
  }
  svg {
    display: block;
    padding-block: 5%;
  }
<div class="container">
<svg width="100%" height="100%" viewBox="0 0 250 457" fill="none" xmlns="http://www.w3.org/2000/svg">
    <path id="bottle" d="M89.6174 72.5661C78.3761 70.8794 34.9222 93.2227 19 135.503M89.6174 72.5661V2H125M89.6174 72.5661H125M19 135.503C-0.902714 188.355 -0.0185633 201.762 9.29124 288.909C17.1881 362.829 33.8318 424.189 44.8889 432.248C60.9111 443.925 84 454.607 125 454.607M19 135.503C130.2 168.307 155 202.255 125 251.842 M160.383 72.5661C171.624 70.8794 215.078 93.2227 231 135.503M160.383 72.5661V2H125M160.383 72.5661H125M231 135.503C250.903 188.355 250.019 201.762 240.709 288.909C232.812 362.829 216.168 424.189 205.111 432.248C189.089 443.925 166 454.607 125 454.607M231 135.503C119.8 168.307 95 202.255 125 251.842" stroke="#B9B9B9" stroke-width="4"/>
    <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="130px"
     viewBox="0 0 136 195" style="enable-background:new 0 0 136 195;" xml:space="preserve">
         <path fill="#04ACFF" id="waveShape" d="M300,300V2.5c0,0-0.6-0.1-1.1-0.1c0,0-25.5-2.3-40.5-2.4c-15,0-40.6,2.4-40.6,2.4c-12.3,1.1-30.3,1.8-31.9,1.9c-2-0.1-19.7-0.8-32-1.9c0,0-25.8-2.3-40.8-2.4c-15,0-40.8,2.4-40.8,2.4c-12.3,1.1-30.4,1.8-32,1.9c-2-0.1-20-0.8-32.2-1.9c0,0-3.1-0.3-8.1-0.7V300H300z"/>
    </svg>
</svg>
</div>

Surrounding the bottle path with a <clipPath> tag just results in the wave shape alone appearing:

  * {
    margin: 0;
    box-sizing: border-box;
  }
  .container {
    height: 100dvh;
  }
  svg {
    display: block;
    padding-block: 5%;
  }
<div class="container">
<svg width="100%" height="100%" viewBox="0 0 250 457" fill="none" xmlns="http://www.w3.org/2000/svg">
    <clipPath>
    <path id="bottle" d="M89.6174 72.5661C78.3761 70.8794 34.9222 93.2227 19 135.503M89.6174 72.5661V2H125M89.6174 72.5661H125M19 135.503C-0.902714 188.355 -0.0185633 201.762 9.29124 288.909C17.1881 362.829 33.8318 424.189 44.8889 432.248C60.9111 443.925 84 454.607 125 454.607M19 135.503C130.2 168.307 155 202.255 125 251.842 M160.383 72.5661C171.624 70.8794 215.078 93.2227 231 135.503M160.383 72.5661V2H125M160.383 72.5661H125M231 135.503C250.903 188.355 250.019 201.762 240.709 288.909C232.812 362.829 216.168 424.189 205.111 432.248C189.089 443.925 166 454.607 125 454.607M231 135.503C119.8 168.307 95 202.255 125 251.842" stroke="#B9B9B9" stroke-width="4"/>
    </clipPath>
    <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="130px"
     viewBox="0 0 136 195" style="enable-background:new 0 0 136 195;" xml:space="preserve">
         <path fill="#04ACFF" id="waveShape" d="M300,300V2.5c0,0-0.6-0.1-1.1-0.1c0,0-25.5-2.3-40.5-2.4c-15,0-40.6,2.4-40.6,2.4c-12.3,1.1-30.3,1.8-31.9,1.9c-2-0.1-19.7-0.8-32-1.9c0,0-25.8-2.3-40.8-2.4c-15,0-40.8,2.4-40.8,2.4c-12.3,1.1-30.4,1.8-32,1.9c-2-0.1-20-0.8-32.2-1.9c0,0-3.1-0.3-8.1-0.7V300H300z"/>
    </svg>
</svg>
</div>

Surrounding the wave path with <clipPath> just displays the bottle:

  * {
    margin: 0;
    box-sizing: border-box;
  }
  .container {
    height: 100dvh;
  }
  svg {
    display: block;
    padding-block: 5%;
  }
<div class="container">
<svg width="100%" height="100%" viewBox="0 0 250 457" fill="none" xmlns="http://www.w3.org/2000/svg">
    <path id="bottle" d="M89.6174 72.5661C78.3761 70.8794 34.9222 93.2227 19 135.503M89.6174 72.5661V2H125M89.6174 72.5661H125M19 135.503C-0.902714 188.355 -0.0185633 201.762 9.29124 288.909C17.1881 362.829 33.8318 424.189 44.8889 432.248C60.9111 443.925 84 454.607 125 454.607M19 135.503C130.2 168.307 155 202.255 125 251.842 M160.383 72.5661C171.624 70.8794 215.078 93.2227 231 135.503M160.383 72.5661V2H125M160.383 72.5661H125M231 135.503C250.903 188.355 250.019 201.762 240.709 288.909C232.812 362.829 216.168 424.189 205.111 432.248C189.089 443.925 166 454.607 125 454.607M231 135.503C119.8 168.307 95 202.255 125 251.842" stroke="#B9B9B9" stroke-width="4"/>
    <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="130px"
     viewBox="0 0 136 195" style="enable-background:new 0 0 136 195;" xml:space="preserve">
         <clipPath>
         <path fill="#04ACFF" id="waveShape" d="M300,300V2.5c0,0-0.6-0.1-1.1-0.1c0,0-25.5-2.3-40.5-2.4c-15,0-40.6,2.4-40.6,2.4c-12.3,1.1-30.3,1.8-31.9,1.9c-2-0.1-19.7-0.8-32-1.9c0,0-25.8-2.3-40.8-2.4c-15,0-40.8,2.4-40.8,2.4c-12.3,1.1-30.4,1.8-32,1.9c-2-0.1-20-0.8-32.2-1.9c0,0-3.1-0.3-8.1-0.7V300H300z"/>
         </clipPath>
    </svg>
</svg>
</div>

This is the bottle path loaded in a browser SVG editor: https://yqnn.github.io/svg-path-editor/#P=M89.6174_72.5661C78.3761_70.8794_34.9222_93.2227_19_135.503M89.6174_72.5661V2H125M89.6174_72.5661H125M19_135.503C-0.902714_188.355_-0.0185633_201.762_9.29124_288.909C17.1881_362.829_33.8318_424.189_44.8889_432.248C60.9111_443.925_84_454.607_125_454.607M19_135.503C130.2_168.307_155_202.255_125_251.842M160.383_72.5661C171.624_70.8794_215.078_93.2227_231_135.503M160.383_72.5661V2H125M160.383_72.5661H125M231_135.503C250.903_188.355_250.019_201.762_240.709_288.909C232.812_362.829_216.168_424.189_205.111_432.248C189.089_443.925_166_454.607_125_454.607M231_135.503C119.8_168.307_95_202.255_125_251.842


Solution

  • A lot of things need to be done to make your code work. But first, a bit of cleanup:

    1. version="1.1", style="enable-background:new 0 0 136 195;" and xml:space="preserve" have absolutely no effect. Just delete them.
    2. xmlns="http://www.w3.org/2000/svg" and xmlns:xlink="http://www.w3.org/1999/xlink" are not needed if you write SVG code inline inside a HTML page. But note that if you leave out the xmlns:xlink attribute, all occurences of xlink:href have also to be shortened to href.
    3. You want to draw the lines representing the bottle in front of the blue area representing the content. Therefor, it needs to be further down in the source code.

    Lets look at an interim result, leaving off the clip path:

    * {
        margin: 0;
        box-sizing: border-box;
      }
      .container {
        height: 100dvh;
      }
      svg {
        display: block;
        padding-block: 5%;
      }
    <div class="container">
    <svg width="100%" height="100%" viewBox="0 0 250 457" fill="none">
        <path id="bottle" d="M89.6174 72.5661C78.3761 70.8794 34.9222 93.2227 19 135.503M89.6174 72.5661V2H125M89.6174 72.5661H125M19 135.503C-0.902714 188.355 -0.0185633 201.762 9.29124 288.909C17.1881 362.829 33.8318 424.189 44.8889 432.248C60.9111 443.925 84 454.607 125 454.607M19 135.503C130.2 168.307 155 202.255 125 251.842 M160.383 72.5661C171.624 70.8794 215.078 93.2227 231 135.503M160.383 72.5661V2H125M160.383 72.5661H125M231 135.503C250.903 188.355 250.019 201.762 240.709 288.909C232.812 362.829 216.168 424.189 205.111 432.248C189.089 443.925 166 454.607 125 454.607M231 135.503C119.8 168.307 95 202.255 125 251.842" stroke="#B9B9B9" stroke-width="4"/>
        <svg x="0px" y="130px" viewBox="0 0 136 195">
             <path fill="#04ACFF" id="waveShape" d="M300,300V2.5c0,0-0.6-0.1-1.1-0.1c0,0-25.5-2.3-40.5-2.4c-15,0-40.6,2.4-40.6,2.4c-12.3,1.1-30.3,1.8-31.9,1.9c-2-0.1-19.7-0.8-32-1.9c0,0-25.8-2.3-40.8-2.4c-15,0-40.8,2.4-40.8,2.4c-12.3,1.1-30.4,1.8-32,1.9c-2-0.1-20-0.8-32.2-1.9c0,0-3.1-0.3-8.1-0.7V300H300z"/>
        </svg>
    </svg>
    </div>

    Now the most important part: a clip-path expresses a relationship between two entities: a grafical element, in your case a <svg> element, is clipped to an area expressed as a geometrical form, here just another <path> element. Wrapping one element with a <clipPath> does exactly nothing, as the relation to the other element is missing.

    The general pattern looks like this

    <clipPath id="clip">
        <path id="form" d="..."/>
    </clipPath>
    
    <path id="grafic d="..." clip-path="url(#clip)"/>
    

    The relationship "the grafic is clipped by the form" is expressed with the attribute clip-path="url(#clip)". Everything written inside the <clipPath> element is not rendered. It is only interpreted as the source of the clipping.

    In your case, things get complicated by the application of a clip path to a <svg> element. The reasoning how attributes viewBox and clip-path interact and why results look like they do is very subtle and not easy to understand. Most of the time, it is just easier to wrap the <svg> with a <g> element, and apply the clip path to that.

    If you want to have a path used both as the form to use as a clipping source, and drawn as a grafic, it needs to appear in two places in your code. Fortunately, you can easily reuse content with a <use> element. The general pattern looks like this:

    <clipPath id="clip">
        <path id="form" d="..."/>
    </clipPath>
    
    <path id="grafic d="..." clip-path="url(#clip)"/>
    <use href="#form"/>
    

    But before you do that, think about your grafic, and how it was represented in the browser editor. The areas shown on grey are what will be interpreted as "inside", everything else is "outside". This is clearly not what you meant. In your case, the reason lies in the somewhat chaotic structure of the path, which is actually composed of nine (!) independent subpaths, each of which have their own insides/outsides.

    Cleaning up that code is a task that the website SvgPathEditor is not really suited for. You need a full-featured vector grafic editor like Illustrator or Inkscape to effectivly do that. What exactly needs to be done is too complex for a short description here, I just present you with the result. You need to have one form that represents the outer form, and a separate one for everything shown in the interior of that form. Only the first is used for the clipping:

    <path d="M205.111 432.248c11.057-8.059 27.701-69.419 35.598-143.339 9.31-87.147 10.194-100.554
    -9.709-153.406-15.922-42.2803-59.376-64.6236-70.617-62.9369V2H89.6174V72.5661C78.3761
    70.8794 34.9222 93.2227 19 135.503-.9027 188.355-.0186 201.762 9.2912 288.909c7.8969
    73.92 24.5406 135.28 35.5977 143.339C60.9111 443.925 84 454.607 125 454.607s64.089-10.682
    80.111-22.359Z"/>
    
    <path d="M19 135.503c111.2 34.307 136 66.752 106 116.339-30
    -49.587-5.2-83.535 106-116.339M89.6174 72.5661H160.383"/>
    

    * {
        margin: 0;
        box-sizing: border-box;
      }
      .container {
        height: 100dvh;
      }
      svg {
        display: block;
        padding-block: 5%;
      }
    <div class="container">
    <svg width="100%" height="100%" viewBox="0 0 250 457">
        <clipPath id="clip">
            <path id="bottle" d="M205.111 432.248c11.057-8.059 27.701-69.419 35.598-143.339 9.31-87.147 10.194-100.554-9.709-153.406-15.922-42.2803-59.376-64.6236-70.617-62.9369V2H89.6174V72.5661C78.3761 70.8794 34.9222 93.2227 19 135.503-.9027 188.355-.0186 201.762 9.2912 288.909c7.8969 73.92 24.5406 135.28 35.5977 143.339C60.9111 443.925 84 454.607 125 454.607s64.089-10.682 80.111-22.359Z" fill="none" stroke="#B9B9B9" stroke-width="4"/>
        </clipPath>
        <g clip-path="url(#clip)">
            <svg x="0px" y="130px" viewBox="0 0 136 195">
               <path fill="#04ACFF" id="waveShape" d="M300,300V2.5c0,0-0.6-0.1-1.1-0.1c0,0-25.5-2.3-40.5-2.4c-15,0-40.6,2.4-40.6,2.4c-12.3,1.1-30.3,1.8-31.9,1.9c-2-0.1-19.7-0.8-32-1.9c0,0-25.8-2.3-40.8-2.4c-15,0-40.8,2.4-40.8,2.4c-12.3,1.1-30.4,1.8-32,1.9c-2-0.1-20-0.8-32.2-1.9c0,0-3.1-0.3-8.1-0.7V300H300z"/>
            </svg>
        </g>
        <use href="#bottle"/>
        <path id="bottle" d="M19 135.503c111.2 34.307 136 66.752 106 116.339-30-49.587-5.2-83.535 106-116.339M89.6174 72.5661H160.383" fill="none" stroke="#B9B9B9" stroke-width="4"/>
    </svg>
    </div>