htmlcsssvgstroke

Stroke SVG Path only inside or only outside


Consider 2 html svg paths , a square (class inside) and a rectangle (class outside) having same height. When I apply stroke-width: 10px, the stroke gets applied 5px inside and 5px outside. Fiddle

enter image description here

How do I stroke only inside or only outside?

.inside { 
  stroke: #333;
  stroke-mode: inside;     // property does not exist
  stroke-width: 5px;
}

.outside {
   stroke: #333;
   stroke-mode: outside;   // property does not exist
   stroke-width: 5px;
}

If there is no such property, is there a workaround to achieve something like:

enter image description here


Solution

  • The comment above by @enxaneta is exactly right.

    Normally, where a filled rectangle is 48px wide and has a stroke with a stroke-width of 12px, the rectangle will display with:

    Why 36px rather than 48px?

    Because the 12px-wide stroke painted along the left side of the rectangle is obscuring 6px of the rectangle and the12px-wide stroke painted along the right side of the rectangle is obscuring 6px of the rectangle.

    See this example where the stroke has 50% opacity - you can see that half of the stroke overlaps the fill:

    svg rect {
      x: 10px;
      y: 10px;
      width: 200px;
      height: 100px;
      stroke: rgba(0, 0, 0, 0.5);
      stroke-width: 12px;
      fill: rgb(255, 0, 0);
    }
    <svg>
      <rect />
    </svg>


    Solution to create an Outer Border of 12px:

    To create an Outer Border, the solution is to paint the stroke first and then paint the fill over the top.

    We can achieve this using:

    paint-order: stroke;
    

    Now the rectangle will display with:

    svg rect {
      x: 10px;
      y: 10px;
      width: 200px;
      height: 100px;
      stroke: rgba(0, 0, 0, 0.5);
      stroke-width: 12px;
      fill: rgb(255, 0, 0);
      paint-order: stroke;
    }
    <svg>
      <rect />
    </svg>

    Finally, to ensure the rectangle displays with:

    change the stroke-width from 12px to 24px (i.e. double the intended display width):

    svg rect {
      x: 10px;
      y: 10px;
      width: 200px;
      height: 100px;
      stroke: rgb(0, 0, 0);
      stroke-width: 24px;      /* double the intended display width */
      fill: rgb(255, 0, 0);
      paint-order: stroke;
    }
    <svg>
      <rect />
    </svg>


    Solution to create an Inner Border of 12px:

    To create an Inner Border, we need three steps instead of two.

    The first two steps are straightforward:

    svg rect {
      x: 10px;
      y: 10px;
      width: 200px;
      height: 100px;
      stroke: rgba(0, 0, 0, 0.5);
      stroke-width: 24px;      /* double the intended display width */
      fill: rgb(255, 0, 0);
      paint-order: fill;
    }
    <svg>
      <rect />
    </svg>

    The third step is to define and apply a <clipPath> which exactly duplicates the shape which is to have an Inner Border.

    For instance, if the shape is:

    <rect width="200" height="100" />
    

    Then the <clipPath> should be:

    <clipPath id="my-clip-path">
      <rect width="200" height="100" /> <!-- Same as the shape -->
    </clipPath>
    

    Working Example:

    #my-rect {
      stroke: rgb(0, 0, 0);
      stroke-width: 24px;
      fill: rgb(255, 0, 0);
      paint-order: fill;
      clip-path: url(#my-clip-path);
    }
    
    #my-rect,
    #my-clip-path-rect {
      x: 10px;
      y: 10px;
      width: 200px;
      height: 100px;
    }
    <svg>
      <defs>
        <clipPath id="my-clip-path">
          <rect id="my-clip-path-rect" />
        </clipPath>
      </defs>
      
      <rect id="my-rect" />
    </svg>


    All of the rectangles together:

    svg {
      display: block;
      float: left;
      width: 220px;
      margin-left: 12px;
    }
    
    svg:nth-of-type(1) rect {
      x: 10px;
      y: 10px;
      width: 200px;
      height: 100px;
      stroke: rgba(0, 0, 0, 0.5);
      stroke-width: 12px;
      fill: rgb(255, 0, 0);
    }
    
    svg:nth-of-type(2) rect {
      x: 10px;
      y: 10px;
      width: 200px;
      height: 100px;
      stroke: rgba(0, 0, 0, 0.5);
      stroke-width: 24px;
      fill: rgb(255, 0, 0);
    }
    
    svg:nth-of-type(3) rect {
      x: 10px;
      y: 10px;
      width: 200px;
      height: 100px;
      stroke: rgb(0, 0, 0);
      stroke-width: 24px;
      fill: rgb(255, 0, 0);
      paint-order: stroke;
    }
    
    #svg3clip {
      width: 200px;
      height: 100px;
    }
    
    svg:nth-of-type(4) rect {
      x: 10px;
      y: 10px;
      width: 200px;
      height: 100px;
      stroke: rgb(0, 0, 0);
      stroke-width: 24px;
      fill: rgb(255, 0, 0);
      paint-order: fill;
      clip-path: url(#svg3clip);
    }
    <svg>
      <rect />
    </svg>
    
    <svg>
      <rect />
    </svg>
    
    <svg>
      <defs>
        <clipPath id="svg3clip">
          <rect />
        </clipPath>
      </defs>
      
      <rect />
    </svg>
    
    <svg>
      <rect />
    </svg>


    Further Reading: