What’s going on? I’m using the CSS Shapes spec to define two polygons around which to set the text - these need to be actual DOM elements, so using pseudo ::after and ::before is off the table. It mmeans two empty elements, but I can live with that.

The shape-outside rule defines the shape, where the coordinates are simple x,y pairs to describe the shape:

// left side
shape-outside:polygon(5%0,25%100%,5%100%);// right side
shape-outside:polygon(95%0,95%100%,75%100%);

That controls how the text flows, but we then also need to clip the element to only keep the part of the element outside the text:

// left side
clip-path:polygon(00,20%102%,0102%);// right side
clip-path:polygon(101%-4%,101%101%,81%101%);

The polygon definitions are different for the two rules, to add padding between the edge of the shape and the edge of the clip.

Just like anything CSS-related, there’s probably a raft of different ways to achieve the same effect.

One benefit of this approach is that the technique could be distilled into a SASS mixin, where the input parameters would be the width of the wedge and the padding distance:

The width of the wedge is relative to the shape, not the parent container, so in this case, given the shape-left and shape-right elements are 50% wide, the 20% wedge is actually 10% of the parent container (ie the black trapezoid).

The clip path is slightly larger than the container, just to avoid any unsightly sub-pixel whitespace.

This won’t work as a responsive solution without a bit of javascript to calculate the height of the floated elements on resize, but for the sake of this example I’ve set fixed heights. For unknown text lengths and/or different device widths, throw a little JS at it.

Like most interesting CSS stuff, this one needs to be prefixed, but you’re using a build task, so that just magically happens, right?