New Jul 3, 2026

Fixing full-bleed CSS

More Front-end Bloggers All from dbushell.com (blog) View Fixing full-bleed CSS on dbushell.com

I’m a front-end developer not a medical practitioner. If you’re bleeding IRL visit the hospital and stop googling medical issues!

The full-bleed layout — as described there by Josh Comeau — can be done with CSS grid (and subgrid). Sometimes you can’t grid the entire page.

That’s where Andy Bell’s utility class is useful.

.full-bleed {
  width: 100vw;
  margin-left: calc(50% - 50vw);
}

But it ain’t perfect. The issue is that viewport units don’t solve the classic scrollbar problem. If you’re on macOS or a fancy OS that has fancy scrollbars, test on Windows! 100vw can be wider than the viewport. Because why would browsers do anything sensible?

It’s hard to see in Andy’s CodePen but a few pixels can be cropped either side. Add something like a border or shadow and it’s easier to see.

.full-bleed {
  box-shadow: inset 0 0 0 10px yellow;
}
Yellow border is slightly clipped either side.
Ignore the pink left border that’s my blog style.

This is not always a problem but it can lead to subtle alignment issues. By the way, macOS has a scrollbar setting “Show scroll bars > Always” that’ll let you test the issue.

macOS scrollbar settings on the appearance tab.

Andy solves this partially by hiding horizontal overflow on the <body> element.

body {
  overflow-x: hidden;
}

An alternative fix is to always reserve space for the classic scrollbar.

html {
  scrollbar-gutter: stable;
}

That can look weird if there is no vertical scroll necessary.

Modern solution

The “modern” approach is to use CSS containment. Turn the <body> element (or any 100% width child) into a container. Then replace the viewport units with container units.

body {
  container: body / inline-size;
  overflow-x: clip;
}

.full-bleed {
  inline-size: 100cqi;
  margin-inline-start: calc(50% - 50cqi);
}

Now hiding overflow is not strictly necessary. I prefer clip — see Overflow Clip guide by Ahmad Shadeed. I clip out of caution because I make dumb things. I also use logical properties and values to support right-to-left (RTL) text direction. Ahmad has an excellent RTL Styling 101 too.

But wait!

Using cqi units assumes body is the parent container of the .full-bleed element. What if we have nested containers? Check this out. I’ve forked Andy’s CodePen to add another .inside container that is not the full viewport width.

Inspecting CSS container layout using Chromium dev tools to show that the inside container is smaller.

This alone would usually break the new .full-bleed class and ruin the fun.

But we can fix that!

@property --body-size {
  syntax: "<length> | <percentage>";
  inherits: true;
  initial-value: 100%;
}

body {
  container: body / inline-size;
}

.inside {
  --body-size: 100cqi;
  container: inside / inline-size;
}

.full-bleed {
  inline-size: var(--body-size);
  margin-inline-start: calc(50% - (0.5 * var(--body-size))); /* † */
}

What is that @property magic?

To be honest I struggle to wrap my smooth brain around this!

Let me try to explain it to myself. Without the at-rule the value of --body-size is calculated at the time of use, i.e. within .full-bleed and therefore relative to the inside container. By explicitly defining a @property the value is now calculated when it’s set within .inside. There 100cqi refers to the parent body container and .full-bleed inherits that value.

But what if you have more containers?

(╯°□°)â•Żïž” ┻━┻

Gosh! Stop being so difficult!

I would have a direct child of <body> like <main> set the --body-size value.

<body>
  <main>
    <!-- blah -->
  </main>
</body>

† I’m multiplying by 0.5 because division is for chumps.

Next-gen solution

What CSS needs is a way to reference a container when using container units.

Ideas have been proposed for example:

.full-bleed {
  inline-size: 100cqi(body);
  inline-size: calc(100 * cqi(body));
  inline-size: calc(100 * container(cqi, body));
}

Cancel Interop 2026 and make this happen!


Thanks for reading! Follow me on Mastodon and Bluesky. Subscribe to my Blog and Notes or Combined feeds.

Scroll to top