Easier layout with margin-trim

If you write a lot of CSS, you are familiar with those moments when you aren’t quite sure how to accomplish what you want to accomplish. Usually, you’ll turn to tutorials or documentation, and learn more about CSS to get your work done. But every once in a while, you realize there is no “proper” way to do what you want to do. So you come up with (or borrow) a solution that feels hacky. Maybe it requires a lot of complex selectors. Or maybe it works for the content you have at the moment, but you worry that someday, someone might throw different HTML at the site, and the solution you wrote will break.

CSS has matured a lot over the last decade. Many robust solutions filled in gaps that previously required fragile hacks. And now, there’s one more — margin-trim.

Margin trim

The margin-trim property lets you tell a container to trim the margins off its children — any margins that push up against the container. In one fell swoop, all of the margin space between the children and the container is eliminated.

diagrams of how margin-trim affects layout — before and after.

This also works when the margins are on the grandchildren or great grand-children, or great great great great grand-children. If there is space created with margins on any of the content inside the container, and that space buts up against the container, it’s trimmed away when margin-trim is applied to the container.

Another diagram of how margin trim affects layout before & after — this time  with grandchildren that have margins

Let’s imagine a practical example. Let’s say we have multiple paragraphs inside an article element, and those paragraphs have margins. Also at the same time, the container has padding on it.

article {
  padding: 2lh;
  background: white;
  p {
    margin-block: 1lh;
  }
}

This is very typical code. The padding on the container is supposed to create an even amount of space all the way around the box, but instead there’s extra white space above and below the content. Like this:

Four paragraphs of text in a white box on a tan background. The white box has a lot more space above and below the text than it does on the sides of the text.

By using 1lh for the margins between the paragraphs, and 2lh for the padding on the article box, we’re attempting to create a beautiful typographic layout. Let’s turn on some guides to better see where the extra space is coming from. The padding on the article box and the margins on the paragraphs are each marked in separate colors.

The same example of text in a box with margins, now with one color marking the padding, and another color marking the margins.

The margins on the first and last paragraphs (1lh) are being added to the padding (2lh) to create a space in the block direction that measures 3lh.

It will be better for the design if we get rid of the margin above the first paragraph and the margin below the last paragraph. Before we had margin-trim, we would attempt to remove the margins from the first and last paragraphs, or lessen the padding in the block direction… but any approach we take will be dependent on the content inside. Perhaps another instance of this article will start with a headline that has a different amount for a top margin. Or start with an image that has no margin.

Without being 100% sure of what kind of content will be in the box, it’s hard to guarantee the spacing will come out as desired. Until now.

The new margin-trim property gives us an easy way to ask directly for what we want. We can tell the box to eliminate any margins that are butting up against that box.

For example:

article {
  margin-trim: block;
  padding: 2lh;
  background: white;
  p {
    margin-block: 1lh;
  }
}

Now the browser automatically chops off any margins that touch the edge of the article box in the block direction — in this case the top and bottom of the box.

The same example again, now with the margins above and below the text chopped off. The colored stripes marking margins no longer exist above and below the content.

Note that while the margins are defined on the <p> element, you declare margin-trim on the <article> element. You always apply margin-trim to the container, not the element that has the margin in the first place.

Here’s the end result.

The same demo, without any guides, now seeing the clean text, and seeing that the space above & below the text, and the space on the sides is the same amount.

Try it yourself

You can try out margin-trim in this live demo, in Safari 16.4 or greater.

Screenshot of the demo on the web where people can try it out for themselves.

Browser Support

Support formargin-trim shipped in Safari over two years ago. But so far, Safari is the only browser with support. So what should you do for browsers without support? For our demo, you could write fallback code inside of feature queries, like this:

article { 
  margin-trim: block;
  font-size: 1.2rem;
  line-height: 1.3;
  padding: 2lh;
  p {
    margin-block: 1lh;
  }
}
@support not (margin-trim: block) {
  article { 
    :first-child {
      margin-block-start: 0;
    }
    :last-child {
      margin-block-end: 0;
    }
  }
}

This helps to clarify the difference between margin-trim and the older techniques we’ve been using.

When using :first-child and :last-child any element that’s the first or last direct child of the container will have its margins trimmed. But any content that either isn’t wrapped in an element, or that is nested further down in the DOM structure will not.

Another diagram showing how the interaction of margins and margin trim works — this time with three drawings, to show margins on children and grandchildren with no margin trim, and older technique for solving this, and using margin trim.

For example, if the first element is a figure with a top margin, and the figure contains an image that also has a top margin, both of those margins will be trimmed by margin-trim, while only the figure margin will be trimmed by :first-child.

<article>
  <figure style="margin-top: 1em">
    <img  style="margin-top: 1em" src="photo.jxl" alt="[alt]">
    <figcaption>[caption]</figcaption>
  </figure>
</article>  

The margin-trim property makes trimming such margins easier and more robust than older techniques.

Even though Safari is the only browser with support at the moment, it makes sense to use it today. Put the hackier layout code in a feature query for the browsers without support ( like@support not (margin-trim: block) { }), while using margin-trim for the browsers that do have it. Hopefully the less robust code will work. It’s the code you are going to have to write anyway. But meanwhile, browsers with support get a more robust solution. And as more and more browsers add support, more and more users will be guaranteed to have a layout that never breaks, no matter what’s thrown at it.

Options for Margin Trim

The values for margin-trim are all logical values, referring to the block and inline directions.

  • margin-trim: none
  • margin-trim: block
  • margin-trim: inline
  • margin-trim: block-start
  • margin-trim: block-end
  • margin-trim: inline-start
  • margin-trim: inline-end

If you want to trim in both directions at the same time, you can do so by combining long-hand values. For example:

margin-trim: block-start block-end inline-start inline-end;

In December 2024, the CSSWG resolved to also allow the shorter block and inline keywords in combination, allowing for syntax like this:

margin-trim: block inline;

The work has been done in WebKit to support this last option. Look for it in Safari Technology Preview soon. Follow this issue for more updates.

Let us know

CSS has never been better. It’s my hope you learn about small improvements like this one, and use it to write more robust code. Let me know what you think on Bluesky or Mastodon. I’d love to hear your stories, feature requests, and questions.