clip-path Shapes, From a Parent's Side Project to TryShape

CSS clip-path is one of the most direct ways to cut an HTML element into a custom shape. The property clips a region of an element: everything inside the defined region stays visible, everything outside is hidden. The values it accepts for creating shapes are circle(), ellipse(), inset(), polygon(), path(), and a clip source via url().

All of these work against a simple mental model: the element's top-left corner is the origin (0,0), with the x-axis running right and the y-axis running down.

A blue square with its starting point located at a zero zero position on a chart with x and y axes.
Initial coordinates(0,0) with x-axis and y-axis

circle() takes a position and a radius. To place a circle’s center at (70, 70) with a 70px radius:

clip-path: circle(70px at 70px 70px)

The result clips only that circular region from the element, hiding everything else.

A blue circle on a grid with an x and y axis. The circle starts at the zero zero position and its center is located at 70 px and 70px. A zoomed in area shows the clipping path, which is also located at 70x and 70px.
The center of the circle is placed at (70, 70) coordinates with a 70px x 70px area clipped. Hence the full circle is shown.

If you position the same circle at (0,0), only the portion of the circle that fits inside the element will remain visible.

The center of a blue circle is placed at the 0,0 coordinates with a 70px by 70px area clipping the bottom-left region of the circle.
The center of the circle is placed at (0, 0) coordinates with a 70px x 70px area clipping the bottom-left region of the circle.

inset() defines rectangular regions by specifying the gap from each edge. This clips a region 30px inside every edge:

clip-path: inset(30px)
The inset() function allows us to clip and area from the outside edge of a shape.

polygon() lets you specify a set of vertices to define a custom polygonal region. For example:

clip-path: polygon(10% 10%, 90% 10%, 90% 90%, 10% 80%)
A square with four points inside of it located at 10% by 10%, 90% by 10%, 10% by 80% and 90% by 90%, creating the clipped area.
The polygon() function allows us to create polygonal shapes using the set of vertices passed to it.

ellipse() works like circle() but takes two radii—one for the x-axis, one for the y-axis—plus a position. An ellipse at (50%, 50%) with radii making it 70px wide and 100px tall looks like this:

A blue blue against a light blue background. The ellipse is 70 pixels wide and 100 pixels tall and it's radius is centered at 50% 50%.
We need to specify two radii values and a position to create an ellipse.

For SVG-based shapes, url() points to the ID of a clipPath element defined in an SVG, and path() accepts SVG path data directly in the CSS.

Showing the SVG code for a heart-shaped path and the actual heart next to it in blue.
Here, we are creating a heart shape using the url() function
Showing a line of CSS code for the clip-path property filled in with the code of an SVG path inside the path function. The result is below the code, a parabolic curve that's filled in blue.
Here we are creating a curvy shape using the path() function.

TryShape: A Practical Wrapper Around clip-path

The desire to build interactive math exercises for a 7-year-old led to a hands-on exploration of clip-path. That exploration grew into TryShape, an open-source app for creating, managing, sharing, and exporting shapes. The app lets you build banners, polygons, and other visual elements, export them as SVG, PNG, or JPEG files, and copy the generated CSS snippet for use elsewhere.

TryShape relies on a familiar React toolchain:

  • Next.js: powers pages, components, UI to back-end API interactions
  • HarperDB: the data store for the app's schema and tables; Next.js APIs perform CRUD operations against it
  • Firebase for social login (Google, GitHub, Twitter)
  • react-icons, date-fns, axios, and styled-components for icons, dates, API calls, and CSS-in-JS styling
  • react-clip-path: a homegrown npm module for handling clip-path in React
  • react-draggable for moving shape vertices on screen
  • downloadjs and html-to-image for file downloads and HTML-to-image conversion
  • Vercel for hosting

How the Editor Works

The shape editor is built around a 300px square Box container with two children. A Shadow component shows the area clipped away, rendered with a light background so users can see what's being hidden. A Component child applies the actual clip-path value to reveal the clipped area.

<Box 
  height="300px" 
  width="300px" 
  onClick={(e) => props.handleChange(e)}>
  { 
    props.shapeInformation.showShadow && 
    <Shadow 
      backgroundColor={props.shapeInformation.backgroundColor} 
      id="shapeShadow" /> 
  }
  <Component 
    formula={props.shapeInformation.formula} 
    backgroundColor={props.shapeInformation.backgroundColor} 
    id="clippedShape" />
</Box>

Both components are styled with styled-components. The Box sets the 300px square dimensions and relative positioning; Shadow sits behind with a subtle fill; Component overlays the clip-path, wherever that value comes from in the editor's state.

// The styled-components code to create the UI components using CSS properties

// The container div
const Box = styled.div`
  width: ${props => props.width || '100px'};
  height: ${props => props.height || '100px'};
  margin: 0 auto;
  position: relative;
`;

// Shadow defines the area that is hidden by the `clip-path` clipping
// We show a light color background to make this area partially visible.
const Shadow = styled.div`
  background-color: ${props => props.backgroundColor || '#00c4ff'};
  opacity: 0.25;
  position: absolute;
  top: 10px;
  left: 10px;
  right: 10px;
  bottom: 10px;
`;

// The actual component that takes the `clip-path` value (formula) and set
// to the `clip-path` property.
const Component = styled.div`
  clip-path: ${props => props.formula}; // the formula is the clip-path value
  background-color: ${props => props.backgroundColor || '#00c4ff'};
  position: absolute;
  top: 10px;
  left: 10px;
  right: 10px;
  bottom: 10px;
`;
An illustration of a blue box with its inset clip points drawing another square that indicates the visible area of the shape in a darker blue.
The components to show a shape(both visible and hidden areas) after the clipping.

What's Next: Curvy Edges

Current version of TryShape handles straight-edged shapes well—anything expressible with polygon(), inset(), and the basic geometry functions. The biggest gap is support for shapes with curved edges. That requires the two clip-path values the editor does not yet deploy: url() referencing SVG clip paths, and path() with direct path data.

Using an SVG clip source means defining a clipPath element with an ID, then referencing it from CSS:

<div class="heart">Heart</div>
<svg>
  <clipPath id="heart-path" clipPathUnits="objectBoundingBox">
    <path d="M0.5,1
      C 0.5,1,0,0.7,0,0.3
      A 0.25,0.25,1,1,1,0.5,0.3
      A 0.25,0.25,1,1,1,1,0.3
      C 1,0.7,0.5,1,0.5,1 Z" />
  </clipPath>
</svg>

The shape is then applied through the CSS rule:

.heart {
  clip-path: url(#heart-path);
}
It produces a heart shape like this.

For a purely CSS approach, path() takes over. With a simple div in the markup:

<div class="curve">Curve</div>

the CSS clips the element to the described path data:

.curve {
  clip-path: path("M 10 80 C 40 10, 65 10, 95 80 S 150 150, 180 80");
}

Both routes open the door to smooth, curved shapes. That's building on the same foundation already in place: checking what clip-path can do, then wrapping it in something others can use. TryShape is available on Vercel, the source is on GitHub, and the reusable React wrapper is published as react-clip-path on npm.