How to Use shape-margin with shape-outside

Text wraps feel flat when every floated image forces a rectangular gutter. The CSS Shapes module fixes that with shape-outside, and shape-margin is the dial that controls breathing room around the contour. By the end of this article you will build two practical examples: a circular avatar that wraps elegantly, and an angled pullquote that the paragraph flows around. You will learn exactly when to adjust shape-margin, how it differs from standard margin, and how to animate the spacing without jitter.

Why shape-margin with shape-outside Matters

shape-outside defines the contour that inline content must avoid. It changes the wrap, not the element’s visible outline. Without shape-margin, text can sit too close to the contour and feel cramped or even collide with antialiased edges. shape-margin extends the avoidance region outward by a specified distance, creating reliable whitespace that tracks the curve or polygon. This gives you typographic control that standard margins cannot match because standard margins remain rectangular, while shape-margin hugs the shape.

Prerequisites

You only need a few building blocks to follow along. We will use floats because shape-outside applies to floating shapes. The tutorial is self-contained, and you can paste it into a single HTML file.

  • Basic HTML
  • CSS custom properties
  • CSS pseudo-elements (::before / ::after)

Step 1: The HTML Structure

The markup sets up two demo sections. Each example uses a flow-root wrapper so the float stays contained. The first example places a circular avatar on the left. The second example places a right-floated angled panel that the text avoids. The content paragraphs are long enough to show the wrap clearly.

<!-- HTML -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>How to Use shape-margin with shape-outside</title>
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
</head>
<body>
  <main class="page">
    <header class="intro">
      <h1>How to Use shape-margin with shape-outside</h1>
    </header>

    <section class="example example-avatar">
      <figure class="shape avatar" aria-hidden="true"></figure>
      <h2 class="example-title">Circular Avatar Wrap</h2>
      <p>This paragraph wraps around a circular shape. The contour comes from shape-outside: circle(), while shape-margin adds breathing room that follows the curve. Increase the margin and the text tracks the circle at a constant distance.</p>
      <p>Use this pattern for author bios, product thumbnails, or small feature callouts. The result reads cleaner than a rectangle because the whitespace matches the object's silhouette rather than the box edges.</p>
      <p>Hover this section to see how the margin grows smoothly without re-laying out the entire page. Only the local wrap updates, which keeps the interaction responsive.</p>
    </section>

    <section class="example example-pullquote">
      <aside class="shape kite" aria-hidden="true"></aside>
      <h2 class="example-title">Angled Pullquote Wrap</h2>
      <p>This panel uses shape-outside: polygon() to form a diagonal edge. The paragraph hugs the slanted face, and shape-margin provides a consistent gap that echoes the angle. The visual panel uses clip-path to match the polygon so the eye sees what the text feels.</p>
      <p>This comes in handy for pullquotes, promotional wedges, or offset images that need a dramatic edge without awkward rectangular gutters. Because shape-margin follows the diagonal, the whitespace remains even at every point along the slope.</p>
      <p>Try switching the float side for RTL layouts or to create alternating editorial blocks down a long article.</p>
    </section>

  </main>
</body>
</html>

Step 2: The Basic CSS & Styling

The foundation sets colors, spacing, and layout. Each example is a flow-root container so the float is contained without clearfix hacks. Custom properties control the theme and the shared shape-margin value. A @supports rule adds a simple fallback when a browser does not support shape-outside.

/* CSS */
:root {
  --bg: #0b1021;
  --surface: #121733;
  --text: #e7ebff;
  --muted: #b8c1ff;
  --accent: #6ad1ff;
  --accent-2: #9b8cff;
  --space-1: 0.75rem;
  --space-2: 1rem;
  --space-3: 1.5rem;
  --gutter: 1rem; /* shared shape-margin */
}

*,
*::before,
*::after { box-sizing: border-box; }

html, body {
  height: 100%;
  background: linear-gradient(180deg, #0b1021 0%, #0f1530 100%);
  color: var(--text);
  font: 16px/1.6 system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif;
}

.page {
  max-width: 70ch;
  padding: clamp(1rem, 2vw + 1rem, 3rem);
  margin-inline: auto;
}

.intro h1 {
  font-size: clamp(1.4rem, 1rem + 2vw, 2.2rem);
  margin: 0 0 var(--space-3);
  color: #ffffff;
}

.example {
  display: flow-root; /* contain floats */
  background: color-mix(in oklab, var(--surface) 85%, black);
  border: 1px solid color-mix(in oklab, var(--surface) 70%, white);
  border-radius: 12px;
  padding: var(--space-3);
  margin-block: clamp(1rem, 1.5vw + 1rem, 2rem);
  box-shadow: 0 6px 20px rgba(0,0,0,.25);
}

.example-title {
  margin: 0 0 var(--space-2);
  font-size: 1.125rem;
  color: var(--accent);
}

.example p {
  margin: 0 0 var(--space-2);
  color: var(--muted);
}

/* fallback when shapes are not supported */
@supports not (shape-outside: circle(50%)) {
  .shape { /* keep it visible and spaced, but wrap will be rectangular */
    margin: 0 var(--space-2) var(--space-2) 0;
  }
}

Advanced Tip: Use a single custom property like –gutter for both the visual padding and shape-margin. This keeps the perceived whitespace consistent because the eye reads the whole unit: the shape’s contour plus its nearby void.

Step 3: Build the Circular Avatar Wrap

The circular avatar sets a float on the left, defines a shape-outside circle, and applies shape-margin using the shared –gutter. The element also has a circular border radius for visual consistency, though the wrap would still follow the circle even without the border radius because the circle is defined by shape-outside.

/* CSS */
.avatar {
  float: left;
  inline-size: 200px;
  block-size: 200px;

  /* The shape that text will avoid */
  shape-outside: circle(50% at 50% 50%);
  shape-margin: var(--gutter);

  /* Visuals (do not affect wrap) */
  border-radius: 50%;
  background:
    radial-gradient(65% 65% at 30% 30%, #8ee7ff 0%, #6ad1ff 40%, transparent 65%),
    conic-gradient(from 210deg at 70% 60%, #6ad1ff, #9b8cff, #6ad1ff 80%);
  background-blend-mode: screen, normal;
  border: 2px solid color-mix(in oklab, var(--accent) 60%, white);
  box-shadow: 0 8px 24px rgba(0,0,0,.35);
  margin: 0 var(--space-2) var(--space-2) 0;

  /* Smooth spacing animation demo */
  transition: shape-margin .35s ease, filter .2s ease;
}

.example-avatar:hover .avatar {
  shape-margin: calc(var(--gutter) * 1.8);
  filter: saturate(1.2);
}

How This Works (Code Breakdown)

shape-outside: circle(50% at 50% 50%) creates a circular wrap that fits inside the element’s box. The radius is 50 percent of the box size and the center sits at the middle. Inline content flows along that curve. The choice of circle() mirrors the look of real avatars and keeps the wrap predictable as you change the box size. If you want a refresher on circular geometry in CSS, see how to make a circle with CSS.

shape-margin: var(–gutter) offsets the wrap outward from the circle. You can think of it as an aura around the contour. If the circle’s radius is 100px, a shape-margin of 16px creates a wrap boundary that is a concentric circle with a 116px radius. This is different from margin-right or margin-left, which only add rectangular spacing and do not track the curve.

float: left activates the shaping behavior. shape-outside applies only to floats. The display remains block, but the element leaves normal flow. The wrapper uses display: flow-root so the float does not escape its container.

The border-radius and background gradients are visual. They do not affect the wrap. The wrap uses the function passed to shape-outside, not the visual background. The transition on shape-margin proves that spacing can animate smoothly. The reflow cost stays local to the example box instead of janking the entire document.

Step 4: Build the Angled Pullquote Wrap

The second component uses a polygon to create a dynamic diagonal edge. The element floats on the right, and the paragraph respects the slanted face. shape-margin again controls the breathing room, but now it follows line segments instead of a curve.

/* CSS */
.kite {
  float: right;
  inline-size: clamp(220px, 30vw, 280px);
  block-size: clamp(180px, 26vw, 220px);

  /* The wrapping contour */
  shape-outside: polygon(
    0% 0%,
    80% 0%,
    100% 50%,
    80% 100%,
    0% 100%
  );
  shape-margin: clamp(12px, 1.2vw, 18px);

  /* Match visuals to the shape for clarity */
  clip-path: polygon(
    0% 0%,
    80% 0%,
    100% 50%,
    80% 100%,
    0% 100%
  );
  background: linear-gradient(135deg, #6ad1ff, #9b8cff);
  border: 2px solid color-mix(in oklab, var(--accent-2) 70%, white);
  box-shadow: 0 10px 28px rgba(0,0,0,.35);
  margin: 0 0 var(--space-2) var(--space-2);

  /* Optional decoration */
  position: relative;
}

.kite::after {
  content: "";
  position: absolute;
  inset: auto 10% 10% auto;
  inline-size: 18px;
  block-size: 18px;
  border-radius: 50%;
  background: white;
  opacity: .25;
}

/* Show spacing control on interaction */
.example-pullquote:hover .kite {
  shape-margin: clamp(18px, 2vw, 28px);
}

How This Works (Code Breakdown)

The polygon’s points form a kite-like wedge with a sharp right edge. Text avoids the slanted edge because that side sits inside the float’s inline-start boundary when floated right. shape-margin grows the avoidance region outward in all directions from the polygon’s edges, which keeps the diagonal gap even. This is the key reason to pair shape-margin with shape-outside on non-rectangular silhouettes.

clip-path uses the same polygon to align the visual surface with the wrap. This is not required for shape-outside to work. It simply prevents the mismatch that occurs when your box looks rectangular but the text claims it is not. If you enjoy building geometric wedges by hand, the triangle functions in the library are handy references, such as how to make a triangle right with CSS. You can adapt those coordinates and expand them into larger polygons for more complex wraps.

The example sets shape-margin with clamp() so the spacing scales across viewports. At narrow widths you get a tight but readable gap, and at wide widths the margin grows slightly to balance the larger panel.

Advanced Techniques: Animations, Ellipses, and Fallbacks

You can swap shape functions without changing the structure. This snippet turns any circular avatar into an ellipse while keeping the same shared gutter, then it adds a hover animation that increases shape-margin for clarity. A support query guards the shape styles and provides a clean rectangular fallback.

/* CSS */
@supports (shape-outside: ellipse(40% 50% at 50% 50%)) {
  .example-avatar[data-shape="ellipse"] .avatar {
    shape-outside: ellipse(45% 35% at 50% 50%);
    /* re-use the same spacing variable */
    shape-margin: var(--gutter);
    /* optional: match the look using clip-path for clarity */
    clip-path: ellipse(45% 35% at 50% 50%);
  }

  .example-avatar[data-shape="ellipse"]:hover .avatar {
    shape-margin: calc(var(--gutter) * 2);
  }
}

/* JS-free toggle using a class for demonstration */
.example-avatar.is-ellipse .avatar {
  /* identical to the data attribute switch; either approach works */
  shape-outside: ellipse(45% 35% at 50% 50%);
  clip-path: ellipse(45% 35% at 50% 50%);
}

ellipses are useful for wider portraits or horizontal badges. If you want to practice building oval surfaces, this guide on how to make an ellipse with CSS pairs nicely with shape-outside’s ellipse() function.

When you need stronger control over minimum spacing on narrow screens, combine shape-margin with logical margins on the float so that both rectangular gutters and shape gutters cooperate. The rectangular margin handles a safety buffer at the container edge, while shape-margin preserves the curved or slanted inset inside the text block.

Accessibility & Performance

Accessibility

Shapes change wrapping, not reading order. Screen readers follow DOM order and ignore the shape geometry. Keep meaningful content in the natural flow and use shapes to enhance the visual experience, not to rearrange the story. The avatar and kite elements are decorative, so they include aria-hidden=”true”. If a floated image conveys meaning, provide alt text on an img inside the float instead of using a background. For motion, pair spacing animations with the prefers-reduced-motion media query and skip hover-driven reflow when the user requests less motion.

Performance

shape-outside runs in the layout stage and is well supported in modern engines. Circles and ellipses are cheap to compute; polygons with many points cost more, especially when animated. Animating shape-margin is reasonable because you animate a single length, yet every frame triggers layout for the affected text block. Keep the animated region small and the duration short. Flow-root containers isolate the reflow so the update stays local. Avoid using shape-outside on massive walls of text where every pixel of the viewport would need to recompute on scroll. Use @supports to keep fallbacks simple and robust.

Put shape-margin to work in your layouts

You built two wraps where the contour sets the vibe and shape-margin dials the whitespace with precision. You learned how to float a circle, craft a polygon wedge, switch to an ellipse, and animate spacing without chaos. Bring these patterns to editorial pages, bios, and promo blocks, and tune the gutter like a type designer. You now have a reliable toolkit for shaping text that feels intentional and refined.

Leave a Comment