{"id":7560,"date":"2025-10-31T11:06:50","date_gmt":"2025-10-31T16:06:50","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=7560"},"modified":"2025-10-31T11:06:51","modified_gmt":"2025-10-31T16:06:51","slug":"super-simple-full-bleed-breakout-styles","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/super-simple-full-bleed-breakout-styles\/","title":{"rendered":"Super Simple Full-Bleed &amp; Breakout Styles"},"content":{"rendered":"\n<p>Recently, I saw someone <a href=\"https:\/\/www.reddit.com\/r\/css\/comments\/1o3j0cl\/comment\/niwx1pj\/\">asked on Reddit<\/a> what others are using these days for full-bleed and breakout elements. This refers to having a main content area of limited width (usually centered), but having the ability for some elements to be wider, either all the way to the browser edges or somewhere in-between.<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='297' src='https:\/\/videopress.com\/embed\/u7wAzkM3?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1739540970'><\/script><\/div>\n\t\t\t<figcaption>desired layout at various viewports \u2014 notice the image is a full-bleed element, the warning is a breakout element and the header is a\u00a0 breakout element with a full-bleed background<\/figcaption>\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>Is it still the\u00a0<a href=\"https:\/\/css-tricks.com\/full-width-containers-limited-width-parents\/\">old method<\/a>\u00a0that involves stretching elements to\u00a0<code>100vw<\/code>\u00a0and then moving them in the negative direction of the\u00a0<em>x<\/em>\u00a0axis via an offset, margin, or translation? <\/p>\n\n\n\n<p>Or is it the\u00a0<a href=\"https:\/\/www.youtube.com\/watch?v=c13gpBrnGEw\">newer method<\/a>\u00a0that involves a grid with a limited width main column in the middle then symmetrical columns on the sides, with elements spanning an odd number of columns that depends on whether we want them to have the normal width of the main column or we want them a bit wider, breaking out of that or we even want them to be full-bleed?<\/p>\n\n\n\n<p>There is no perfectly right answer. It depends on use case and how you look at it. We&#8217;re going to look at modified and combined versions and essentially achieve what we need to depending on the situation with modern CSS.<\/p>\n\n\n\n<p>The old method described in <a href=\"https:\/\/css-tricks.com\/full-width-containers-limited-width-parents\/\">the 2016 CSS-Tricks article<\/a> has the disadvantage of relying on a Firefox bug (that has been fixed since 2017) to work well in all situations. The problem is that <code>100vw<\/code> doesn&#8217;t take into account any vertical scrollbars we might have (and no, the new viewport units <a href=\"https:\/\/www.smashingmagazine.com\/2023\/12\/new-css-viewport-units-not-solve-classic-scrollbar-problem\/\">don&#8217;t solve<\/a> that problem either). This leads to the <code>100vw<\/code> width elements being wider than the available horizontal space if there is a vertical scrollbar, overflowing and causing a horizontal scrollbar, something I also often see with the bizarre practice of setting the <code>width<\/code> of the <code>body<\/code> to <code>100vw<\/code>. Now, considering the elements we normally want to be full-bleed are likely images, we can hide the problem with <code>overflow-x: hidden<\/code> on the <code>html<\/code>. But it still doesn&#8217;t feel quite right.<\/p>\n\n\n\n<p>Maybe it&#8217;s because I&#8217;m a tech, not a designer who thinks in terms of design grids, but I prefer to keep my grids minimal and when I look at the desired result, my first thought is: that&#8217;s a single column grid with the items that are wider than the column, and everything is center-aligned.<\/p>\n\n\n\n<p>So let&#8217;s take a look at the approach I most commonly use (or at least start from), which doesn&#8217;t involve a scary-looking grid column setup, and, for the simple base cases, doesn&#8217;t involve any containers or even any <code>calc()<\/code>, which some people find confusing.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"the-base-grid\">The Base Grid<\/h2>\n\n\n\n<p>We&#8217;re starting off with a <code>grid<\/code>, of course! We set a one limited width column <code>grid<\/code> on the <code>body<\/code> and we middle align this <code>grid<\/code> horizontally within the the <code>content-box<\/code> of the <code>body<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">body<\/span> {\n  <span class=\"hljs-attribute\">display<\/span>: grid;\n  <span class=\"hljs-attribute\">grid-template-columns<\/span>: <span class=\"hljs-built_in\">min<\/span>(<span class=\"hljs-number\">100%<\/span>, <span class=\"hljs-number\">60em<\/span>);\n  <span class=\"hljs-attribute\">justify-content<\/span>: center\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>By default, <code>display: grid<\/code> creates a one column grid that stretches horizontally across the entire <code>content-box<\/code> width of the element it&#8217;s set on. This makes all the children of the element getting <code>display: grid<\/code> be distributed in that one column, one on each row. The first on the first row, the second on the second row and so on.<\/p>\n\n\n\n<p>The <code>grid-template-columns<\/code> property is used here to max out the width of this one column at <code>60em<\/code> by setting its width to be the minimum between <code>100%<\/code> of the <code>content-box<\/code> width and <code>60em<\/code>. If the <code>content-box<\/code> of the element we&#8217;ve set the <code>grid<\/code> on has a width of up to <code>60em<\/code>, then the one column of the <code>grid<\/code> stretches horizontally across the entire <code>content-box<\/code>. If the <code>content-box<\/code> of the element we&#8217;ve set the <code>grid<\/code> on has a width above <code>60em<\/code>, then our one grid column doesn&#8217;t stretch horizontally across the entire <code>content-box<\/code> anymore, but instead stays <code>60em<\/code> wide, the maximum width it can take. Of course, this maximum width can be any other value we want.<\/p>\n\n\n\n<p>The <code>justify-content<\/code> property is used to align the <code>grid<\/code> horizontally within the <code>content-box<\/code> of the element it&#8217;s set on. In this case, our one grid column is center aligned.<\/p>\n\n\n\n<p>Note that I keep talking about the <code>content-box<\/code> here. This is because, even at really narrow viewports, we normally want a bit of space in between the text edge and the lateral edge of the available area (the viewport minus any scrollbars we might have). Initially, this space is the <a href=\"https:\/\/miriam.codes\/2022\/07\/04\/body-margin-8px\/\">default <code>margin<\/code><\/a> of <code>8px<\/code> on the <code>body<\/code>, though I also often do something similar to the approach Chris <a href=\"https:\/\/frontendmasters.com\/blog\/the-coyier-css-starter\/#body-spacing\">wrote about<\/a> recently and zero the default <code>margin<\/code> to replace it with a clamped font-relative <code>padding<\/code>. But whichever of them is used still gets subtracted from the available space (viewport width minus any vertical scrollbar we might have) to give us the <code>content-box<\/code> width of the <code>body<\/code>.<\/p>\n\n\n\n<p>Now whatever children the <code>body<\/code> may have (headings, paragraphs, images and so on), they&#8217;re all in the limited width grid cells of our one column, something that&#8217;s highlighted by the DevTools grid overlay in the screenshot below.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"512\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_00_layout_an_s.png?resize=1024%2C512&#038;ssl=1\" alt=\"Screenshot. Shows a middle aligned grid with a single column and multiple rows, something that's highlighted by the DevTools-enabled grid overlay.\" class=\"wp-image-7579\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_00_layout_an_s.png?resize=1024%2C512&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_00_layout_an_s.png?resize=300%2C150&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_00_layout_an_s.png?resize=768%2C384&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_00_layout_an_s.png?w=1280&amp;ssl=1 1280w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">the one limited width column grid layout with the DevTools grid lines overlay (<a href=\"https:\/\/codepen.io\/thebabydino\/pen\/ZYQJWRd\">live demo<\/a>)<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"full-bleed-elements\">Full-Bleed Elements<\/h2>\n\n\n\n<p>Let\u2019s say we want to make an element full-bleed (edge to edge). For example, an image or an image gallery, because that\u2019s what makes the most sense to have stretching all across the entire available page width. This means we want the full viewport width minus any scrollbars we might have.<\/p>\n\n\n\n<p>Nowadays we can get that by making the <code>html<\/code> a <code>container<\/code> so that its descendants know its available width (not including scrollbars) as <code>100cqw<\/code> (<strong>c<\/strong>ontainer <strong>q<\/strong>uery <strong>w<\/strong>idth).<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">html<\/span> { <span class=\"hljs-attribute\">container-type<\/span>: inline-size }<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Having this, we can create our full-bleed elements:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.full-bleed-elem<\/span> {\n  <span class=\"hljs-attribute\">justify-self<\/span>: center;\n  <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">100<\/span>cqw\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Setting <code>width: 100cqw<\/code> on our full-bleed elements means they get the full available <code>content-box<\/code> width of the nearest container, which is the <code>html<\/code> in this case.<\/p>\n\n\n\n<p>The <code>justify-self<\/code> aligns the element horizontally within its <code>grid-area<\/code> (which is limited to one grid cell in our case here). We need to set it here because the default is <code>start<\/code>, which means the left edge of the element starts from the left edge of its containing <code>grid-area<\/code>. The left edge of the containing <code>grid-area<\/code> is the same as the left edge of our one column <code>grid<\/code> here.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"512\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_01_fullbleed_elem_an_s.png?resize=1024%2C512&#038;ssl=1\" alt=\"Screenshot. Shows a middle aligned grid with a single column and multiple rows, something that's highlighted by the DevTools-enabled grid overlay. On some of these rows, we have full-bleed elements that expand all across the entire available page width (the viewport width minus any vertical scrollbars we might have).\" class=\"wp-image-7580\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_01_fullbleed_elem_an_s.png?resize=1024%2C512&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_01_fullbleed_elem_an_s.png?resize=300%2C150&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_01_fullbleed_elem_an_s.png?resize=768%2C384&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_01_fullbleed_elem_an_s.png?w=1280&amp;ssl=1 1280w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">one column grid with full-bleed elements and a DevTools grid overlay highlighting the grid lines<\/figcaption><\/figure>\n\n\n\n<p>Just like before, we still have a single column grid, center aligned.<\/p>\n\n\n\n<p>One thing to note here is this means we cannot have any <code>margin<\/code>, <code>border<\/code> or <code>padding<\/code> on the <code>html<\/code> element as any of these would reduce its <code>content-box<\/code>, whose size is what the container query units are based on. In practice, the <code>margin<\/code>, <code>border<\/code>, and <code>padding<\/code> on the <code>html<\/code> are all zero by default and I don&#8217;t think I&#8217;ve seen them set to anything else anywhere outside of some mind-bending <a href=\"https:\/\/www.reddit.com\/r\/css\/comments\/1gif7ew\/comment\/lv551jx\/\">CSS Battle solutions<\/a>.<\/p>\n\n\n\n<p>Another thing to note is that there may be cases where we need another container somewhere in between. In that case, we can still access the <code>content-box<\/code> width of the <code>html<\/code> as detailed in <a href=\"https:\/\/frontendmasters.com\/blog\/using-container-query-units-relative-to-an-outer-container\/\">a previous article<\/a>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@property<\/span> --full-w {\n  <span class=\"hljs-selector-tag\">syntax<\/span>: '&lt;<span class=\"hljs-selector-tag\">length<\/span>&gt;';\n  <span class=\"hljs-selector-tag\">initial-value<\/span>: 0<span class=\"hljs-selector-tag\">px<\/span>;\n  <span class=\"hljs-selector-tag\">inherits<\/span>: <span class=\"hljs-selector-tag\">true<\/span>;\n}\n\n<span class=\"hljs-selector-tag\">html<\/span> { <span class=\"hljs-attribute\">container-type<\/span>: inline-size }\n\n<span class=\"hljs-selector-tag\">body<\/span> { <span class=\"hljs-attribute\">--full-w<\/span>: <span class=\"hljs-number\">100<\/span>cqw }\n\n<span class=\"hljs-selector-class\">.full-bleed-elem<\/span> {\n  <span class=\"hljs-attribute\">justify-self<\/span>: center;\n  <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-built_in\">var<\/span>(--full-w);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Often times, we probably also want some padding on the full-bleed element if it is, for example, an image gallery, but not if it is a single <code>img<\/code> element. <\/p>\n\n\n\n<p>For <code>img<\/code> elements, the actual image always occupies just the <code>content-box<\/code>. Any padding we set on it is <em>empty space<\/em> around the <code>content-box<\/code>. This is not generally\u00a0 desirable in our case. Unless we want to add some kind of decorations around it via the background property (by layering CSS gradients to create some kind of <a href=\"https:\/\/codepen.io\/thebabydino\/pen\/JjZNXoQ\">cool pattern<\/a>, for example), we want the image to stretch all across the available viewport space after accounting for any vertical scrollbar we might have and not be left with empty space on the lateral sides. <\/p>\n\n\n\n<p>Furthermore, if the <code>img<\/code> uses a <code>box-sizing<\/code> of <code>content-box<\/code>, that empty padding space gets <em>added<\/em> to the <code>100cqw<\/code> width of its\u00a0<code>content-box<\/code>, making the <code>padding-box<\/code> width exceed the available space and causing a horizontal scrollbar on the page.<\/p>\n\n\n\n<p>When setting a\u00a0padding on full-bleed elements, it&#8217;s probably best to exclude\u00a0<code>img<\/code> elements:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.full-bleed-elem<\/span><span class=\"hljs-selector-pseudo\">:not(img)<\/span> { <span class=\"hljs-attribute\">padding<\/span>: .<span class=\"hljs-number\">5em<\/span> }<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Note that in this case, the full-bleed elements getting the <code>padding<\/code> need to also have <code>box-sizing<\/code> set to <code>border-box<\/code>. This is done so that the <code>padding<\/code> gets subtracted out of the set <code>width<\/code> and not added as it would happen in the default <code>content-box<\/code> case.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.full-bleed-elem<\/span><span class=\"hljs-selector-pseudo\">:not(img)<\/span> {\n  <span class=\"hljs-attribute\">box-sizing<\/span>: border-box;\n  <span class=\"hljs-attribute\">padding<\/span>: .<span class=\"hljs-number\">5em<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>You can see it in action and play with it in the following live demo:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_VYezjay\" src=\"\/\/codepen.io\/anon\/embed\/VYezjay?height=525&amp;theme-id=1&amp;slug-hash=VYezjay&amp;default-tab=result\" height=\"525\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed VYezjay\" title=\"CodePen Embed VYezjay\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p class=\"learn-more\">You might be wondering&#8230; is it even necessary to set <code>border-box<\/code> since setting <em>everything<\/em> to <code>border-box<\/code> is a pretty popular reset style?<\/p>\n\n\n\n<p>Personally, I don&#8217;t set that in resets anymore because I find that with the the new layout options we have, the number of cases where I still need to explicitly set dimensions in general and widths in particular has declined. Drastically. Most of the time, I just size columns, rows, set the <code>flex<\/code> property instead and let the <code>grid<\/code> or <code>flex<\/code> children get sized by those without explicitly setting any dimensions. And when I don&#8217;t have to set dimensions explicitly, the <code>box-sizing<\/code> becomes irrelevant and even problematic in <a href=\"https:\/\/www.youtube.com\/watch?v=PtAcpV6TAGM\">some situations<\/a>. So I just don&#8217;t bother with including <code>box-sizing: border-box<\/code> in the reset these days anymore and instead only set it in the cases where it&#8217;s needed.<\/p>\n\n\n\n<p>Like here, for the non-<code>img<\/code> full bleed elements.<\/p>\n\n\n\n<p class=\"learn-more\">Another thing you may be wondering about&#8230; how about just setting a negative lateral <code>margin<\/code>?<\/p>\n\n\n\n<p>We know the viewport width minus any scrollbars as <code>100cqw<\/code>, we know the column width as <code>100%<\/code>, so the difference between the two <code>100cqw - 100%<\/code> is the space on the left side of the column plus the space on the right side of the column. This means half the difference <code>.5*(100cqw - 100%)<\/code>, which we can also write as <code>50cqw - 50%<\/code>, is the space on just one side. And then we put a minus in front and get our lateral margin. Like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.full-bleed-elem<\/span> {\n  <span class=\"hljs-attribute\">margin<\/span>: .<span class=\"hljs-number\">5rem<\/span> <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">50%<\/span> - <span class=\"hljs-number\">50<\/span>cqw);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Or, if we want to avoid overriding the vertical margin:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.full-bleed-elem<\/span> {\n  <span class=\"hljs-attribute\">margin-inline<\/span>: <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">50%<\/span> - <span class=\"hljs-number\">50<\/span>cqw);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This seems like a good option. It&#8217;s just one <code>margin<\/code> property instead of a <code>justify-self<\/code> and a <code>width<\/code> one. And it also avoids having to set <code>box-sizing<\/code> to <code>border-box<\/code> if we want a <code>padding<\/code> on our full-bleed element. But we should also take into account what exactly we are most likely to make full-bleed.<\/p>\n\n\n\n<p>One case we considered here was that of full-bleed images. The thing with <code>img<\/code> elements is that, by default, they don&#8217;t size themselves to fit the grid areas containing them, they just use their own intrinsic size. For full-bleed images this means they are either going to not fill the entire available viewport space if their intrinsic width is smaller than the viewport or overflow the viewport if their intrinsic width is bigger than the available viewport space (the viewport width minus any vertical scrollbar we might have). So we need to set their <code>width<\/code> anyway.<\/p>\n\n\n\n<p>For the other case, that of the scrolling image gallery, the negative <code>margin<\/code> can be an option.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"breakout-elements\">Breakout Elements<\/h2>\n\n\n\n<p>These are wider than our main content, so they break out of our grid column, but are not full-bleed.<\/p>\n\n\n\n<p>So we would give them a width that&#8217;s smaller than the <code>content-box<\/code> width of the <code>html<\/code>, which we know as <code>100cqw<\/code>, but still bigger than the width of our only <code>grid<\/code> column, which we know as <code>100%<\/code>. Assuming we want breakout elements to extend out on each side by <code>4em<\/code>, this means:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.break-elem<\/span> {\n  <span class=\"hljs-attribute\">justify-self<\/span>: center;\n  <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-built_in\">min<\/span>(<span class=\"hljs-number\">100<\/span>cqw, <span class=\"hljs-number\">100%<\/span> + <span class=\"hljs-number\">2<\/span>*<span class=\"hljs-number\">4em<\/span>)\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Again, we might use a negative lateral <code>margin<\/code> instead. For breakout elements, which are a lot more likely to be text content elements, the negative <code>margin<\/code> approach makes more sense than for the full-bleed ones. Note that just like the width, the lateral <code>margin<\/code> also needs to be capped in case the lateral space on the sides of our column drops under <code>4em<\/code>.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.break-elem<\/span> { <span class=\"hljs-attribute\">margin<\/span>: <span class=\"hljs-number\">0<\/span> <span class=\"hljs-built_in\">max<\/span>(-<span class=\"hljs-number\">4em<\/span>, <span class=\"hljs-number\">50%<\/span> - <span class=\"hljs-number\">50<\/span>cqw) }<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Note that we use the <code>max()<\/code> because for negative values like the <code>margin<\/code> here, the smaller (minimum) one in absolute value (closer to 0) is the one that&#8217;s bigger when looking at the full axis going from minus to plus infinity.<\/p>\n\n\n\n<p>But then again, we might want to be consistent and set full-bleed and breakout styles the same way, maybe grouping them together:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.full-bleed-elem<\/span>, <span class=\"hljs-selector-class\">.break-elem<\/span> {\n  <span class=\"hljs-attribute\">justify-self<\/span>: center;\n  <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-built_in\">min<\/span>(<span class=\"hljs-number\">100<\/span>cqw var(--comp-w, ));\n}\n\n<span class=\"hljs-comment\">\/* This is valid! *\/<\/span>\n<span class=\"hljs-selector-class\">.break-elem<\/span> { <span class=\"hljs-attribute\">--comp-w<\/span>: , <span class=\"hljs-number\">100%<\/span> + <span class=\"hljs-number\">2<\/span>*<span class=\"hljs-number\">4em<\/span>  }\n\n<span class=\"hljs-selector-pseudo\">:is(.full-bleed-elem<\/span>, <span class=\"hljs-selector-class\">.break-elem<\/span>)<span class=\"hljs-selector-pseudo\">:not(img)<\/span> {\n  <span class=\"hljs-attribute\">box-sizing<\/span>: border-box;\n  <span class=\"hljs-attribute\">padding<\/span>: .<span class=\"hljs-number\">5em<\/span>;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Some people prefer <code>:where()<\/code> instead of <code>:is()<\/code> for <a href=\"https:\/\/css-tricks.com\/quick-reminder-that-is-and-where-are-basically-the-same-with-one-key-difference\/\">specificity reasons<\/a>, as <code>:where()<\/code> always has <code>0<\/code> specificity, while <code>:is()<\/code> has the specificity of the most specific selector in its arguments. But that is precisely one of my main reasons for using <code>:is()<\/code> here.<\/p>\n\n\n\n<p>And yes, both having an empty default for a CSS variable and its value starting with a comma is valid. Replacing <code>--comp-w<\/code> with its value gives us a <code>width<\/code> of <code>min(100cqw)<\/code> (which is the same as <code>100cqw<\/code>) for full-bleed elements and one of <code>min(100cqw, 100% + 2*4em)<\/code> for breakout elements.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"512\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_02_fullbleed_break_elem_an_s.png?resize=1024%2C512&#038;ssl=1\" alt=\"Screenshot. Shows a middle aligned grid with a single column and multiple rows, something that's highlighted by the DevTools-enabled grid overlay. On some of these rows, we have full-bleed images that expand all across the entire available page width (the viewport width minus any vertical scrollbars we might have). On others, we have breakout boxes that expand laterally outside their grid cells, but are not wide enough to be full-bleed.\" class=\"wp-image-7583\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_02_fullbleed_break_elem_an_s.png?resize=1024%2C512&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_02_fullbleed_break_elem_an_s.png?resize=300%2C150&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_02_fullbleed_break_elem_an_s.png?resize=768%2C384&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_02_fullbleed_break_elem_an_s.png?w=1280&amp;ssl=1 1280w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">one column grid with full-bleed and breakout elements, as well as a DevTools grid overlay highlighting the grid lines (<a href=\"https:\/\/codepen.io\/thebabydino\/pen\/YPwrbry\">live demo<\/a>)<\/figcaption><\/figure>\n\n\n\n<p>If we want to have different types of breakout elements that extend out more or less, not all exactly by the same fixed value, we make that value a custom property <code>--dx<\/code>, which we can change based on the type of breakout element:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.break-elem<\/span> { <span class=\"hljs-attribute\">--comp-w<\/span>: , <span class=\"hljs-number\">100%<\/span> + <span class=\"hljs-number\">2<\/span>*<span class=\"hljs-built_in\">var<\/span>(--dx, <span class=\"hljs-number\">4em<\/span>) }<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The <code>--dx<\/code> value could also be negative and, in this case, the element doesn&#8217;t really break out of the main column, it shrinks so it&#8217;s narrower.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.break-elem--mini<\/span> { <span class=\"hljs-attribute\">--dx<\/span>: -<span class=\"hljs-number\">2em<\/span> }\n<span class=\"hljs-selector-class\">.break-elem--maxi<\/span> { <span class=\"hljs-attribute\">--dx<\/span>: <span class=\"hljs-number\">8em<\/span> }\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/gist.github.com\/user-attachments\/assets\/8a19d473-6b3f-4273-a2f8-1a74efaea708\" alt=\"Screenshot. Shows a middle aligned grid with a single column and multiple rows, something that's highlighted by the DevTools-enabled grid overlay. One of these rows has a full-bleed image that expands all across the entire available page width (the viewport width minus any vertical scrollbars we might have). On other rows, we have breakout boxes that are not the same width as their grid cells, but are not wide enough to be full-bleed. Most of these boxes are wider than their containing grid cells, but one is narrower.\"\/><figcaption class=\"wp-element-caption\">one column grid with a full-bleed image and various sizes of breakout elements, as well as a DevTools grid overlay highlighting the grid lines (<a href=\"https:\/\/codepen.io\/thebabydino\/pen\/JoGORNp\">live demo<\/a>)<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"full-bleed-backgrounds-for-limited-width-elements\">Full-Bleed Backgrounds for Limited Width Elements<\/h2>\n\n\n\n<p>Sometimes we may want only the background of the element to be full-bleed, but not the element content. In the simplest case, we can do with a <code>border-image<\/code> and if you want to better understand this property, check out <a href=\"https:\/\/www.smashingmagazine.com\/2024\/01\/css-border-image-property\/\">this article<\/a> by Temani Afif detailing a lot of use cases.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.full-bleed-back<\/span> {\n  <span class=\"hljs-attribute\">border-image<\/span>: <span class=\"hljs-built_in\">var<\/span>(--img) fill <span class=\"hljs-number\">0<\/span>\/ \/ <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">50<\/span>cqw;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This works for mono backgrounds (like the one created for the full-bleed <code>header<\/code> and <code>footer<\/code> below with a single stop gradient), for most gradients and even for actual images in some cases.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"512\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_04_lim_elem_fullbleed_back_an_s-1.png?resize=1024%2C512&#038;ssl=1\" alt=\"Screenshot. Shows a middle aligned grid with a single column and multiple rows, something that's highlighted by the DevTools-enabled grid overlay. On the very first row, we have a limited width header with a solid full-bleed mono background. On other rows, we have full-bleed elements that expand all across the entire available page width (the viewport width minus any vertical scrollbars we might have). On other rows, we have breakout boxes that are not the same width as their grid cells, but are not wide enough to be full-bleed.\" class=\"wp-image-7620\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_04_lim_elem_fullbleed_back_an_s-1.png?resize=1024%2C512&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_04_lim_elem_fullbleed_back_an_s-1.png?resize=300%2C150&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_04_lim_elem_fullbleed_back_an_s-1.png?resize=768%2C384&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_04_lim_elem_fullbleed_back_an_s-1.png?w=1280&amp;ssl=1 1280w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">one column grid that has a tightly fit limited width header with a full-bleed mono background; it also has a full-bleed image and a breakout element, as well as a DevTools grid overlay highlighting the grid lines (<a href=\"https:\/\/codepen.io\/thebabydino\/pen\/jEWaRaP\">live demo<\/a>)<\/figcaption><\/figure>\n\n\n\n<p>The mono background above is created as follows (all these demos adapt to user theme preferences):<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">--img<\/span>: <span class=\"hljs-selector-tag\">conic-gradient<\/span>(<span class=\"hljs-selector-tag\">light-dark<\/span>(<span class=\"hljs-selector-id\">#ededed<\/span>, <span class=\"hljs-selector-id\">#121212<\/span>) 0 0)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This method is perfect for such mono backgrounds, but if we want gradient or image ones, there are some aspects we need to consider.<\/p>\n\n\n\n<p>The thing about the <code>0 50cqw<\/code> <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/border-image-outset\">outset<\/a> value is that it tells the browser to extend the area where the <code>border-image<\/code> is painted by <code>50cqw<\/code> outwards from the <code>padding-box<\/code> boundary on the lateral sides. This means it extends outside the vewport, but since this is just the <code>border-image<\/code>, not the <code>border<\/code> reserving space, it doesn&#8217;t cause overflow\/ a horizontal scrollbar, so we can keep it simple and use it like this for gradients.<\/p>\n\n\n\n<p>That is, if we can avoid percentage position trouble. While this is not an issue in linear top to bottom gradients, if we want to use percentages in linear left to right gradients or to position radial or conic ones, we need to scale the <code>[0%, 100%]<\/code> interval to the <code>[50% - 50cqw, 50% + 50cqw]<\/code> interval along the <em>x<\/em> axis.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.linear-horizontal<\/span> {\n  <span class=\"hljs-attribute\">--img<\/span>: \n    <span class=\"hljs-built_in\">linear-gradient<\/span>(\n      <span class=\"hljs-number\">90deg<\/span>, \n      var(--c0) <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">50%<\/span> - <span class=\"hljs-number\">50<\/span>cqw), \n      <span class=\"hljs-built_in\">var<\/span>(--c1) <span class=\"hljs-number\">50%<\/span>\n    );\n}\n\n<span class=\"hljs-selector-class\">.radial<\/span> {\n  <span class=\"hljs-attribute\">--img<\/span>: \n    <span class=\"hljs-built_in\">radial-gradient<\/span>(\n      <span class=\"hljs-number\">15<\/span>cqw at calc(<span class=\"hljs-number\">50%<\/span> - <span class=\"hljs-number\">25<\/span>cqw) <span class=\"hljs-number\">0<\/span>, \n      <span class=\"hljs-built_in\">var<\/span>(--c0), \n      <span class=\"hljs-built_in\">var<\/span>(--c1)\n    );\n}\n\n<span class=\"hljs-selector-class\">.conic<\/span> {\n  <span class=\"hljs-attribute\">--img<\/span>: \n    <span class=\"hljs-built_in\">conic-gradient<\/span>(\n      at calc(<span class=\"hljs-number\">50%<\/span> + <span class=\"hljs-number\">15<\/span>cqw), \n      <span class=\"hljs-built_in\">var<\/span>(--c1) <span class=\"hljs-number\">30%<\/span>, \n      <span class=\"hljs-built_in\">var<\/span>(--c0), \n      <span class=\"hljs-built_in\">var<\/span>(--c1) <span class=\"hljs-number\">70%<\/span>\n    );\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_RNrjXLe\" src=\"\/\/codepen.io\/anon\/embed\/RNrjXLe?height=585&amp;theme-id=1&amp;slug-hash=RNrjXLe&amp;default-tab=result\" height=\"585\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed RNrjXLe\" title=\"CodePen Embed RNrjXLe\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>However, this scaling is not enough for linear gradients at an angle that&#8217;s not a multiple of <code>90\u00b0<\/code>. And it may be overly complicated even for the types of gradients where it works well.<\/p>\n\n\n\n<p>So another option is compute how much the <code>border-image<\/code> needs to expand laterally out of the available horizontal space <code>100cqw<\/code> and the maximum <code>grid<\/code> column width <code>--grid-w<\/code>. This then allows us to use percentages normally inside any kind of gradient, including linear ones at an angle that&#8217;s not a multiple of <code>90\u00b0<\/code>.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">body<\/span> {\n  <span class=\"hljs-attribute\">--grid-w<\/span>: <span class=\"hljs-number\">60em<\/span>;\n  <span class=\"hljs-attribute\">display<\/span>: grid;\n  <span class=\"hljs-attribute\">grid-template-columns<\/span>: <span class=\"hljs-built_in\">min<\/span>(<span class=\"hljs-number\">100%<\/span>, var(--grid-w));\n  <span class=\"hljs-attribute\">justify-content<\/span>: center;\n}\n\n<span class=\"hljs-selector-class\">.full-bleed-back<\/span> {\n  <span class=\"hljs-attribute\">border-image<\/span>: \n    <span class=\"hljs-built_in\">var<\/span>(--img) fill <span class=\"hljs-number\">0<\/span>\/ \/ \n    <span class=\"hljs-number\">0<\/span> <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">50<\/span>cqw - .<span class=\"hljs-number\">5<\/span>*var(--grid-w));\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"512\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_05_lim_elem_fullbleed_grad_back_an_s-1.png?resize=1024%2C512&#038;ssl=1\" alt=\"Screenshot. Shows a middle aligned grid with a single column and multiple rows, something that's highlighted by the DevTools-enabled grid overlay. On the very first row, we have a limited width header with a solid full-bleed gradient background. On other rows, we have full-bleed elements that expand all across the entire available page width (the viewport width minus any vertical scrollbars we might have). On other rows, we have breakout boxes that are not the same width as their grid cells, but are not wide enough to be full-bleed.\" class=\"wp-image-7623\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_05_lim_elem_fullbleed_grad_back_an_s-1.png?resize=1024%2C512&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_05_lim_elem_fullbleed_grad_back_an_s-1.png?resize=300%2C150&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_05_lim_elem_fullbleed_grad_back_an_s-1.png?resize=768%2C384&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_05_lim_elem_fullbleed_grad_back_an_s-1.png?w=1280&amp;ssl=1 1280w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">one column grid that has a tightly fit limited width header with a full-bleed angled gradient background (at an angle that&#8217;s not a multiple of 90\u00b0); it also has a full-bleed image and a breakout element, as well as a DevTools grid overlay highlighting the grid lines (<a href=\"https:\/\/codepen.io\/thebabydino\/pen\/JoGMjxb\">live demo<\/a>)<\/figcaption><\/figure>\n\n\n\n<p>This has a tiny problem that other styling decisions we&#8217;re likely to take (and which we&#8217;ll discuss in a moment) prevent from happening, but, assuming we don&#8217;t make those choices, let&#8217;s take a look at it and how we can solve it.<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='250' src='https:\/\/videopress.com\/embed\/2BFbQGMp?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1739540970'><\/script><\/div>\n\t\t\t<figcaption>full-bleed background issue on narrow viewports<\/figcaption>\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>On narrow viewports, our background isn&#8217;t full-bleed anymore, it stops a tiny distance away from the lateral sides. That tiny distance is at most the size of the lateral <code>margin<\/code> or <code>padding<\/code> on the <code>body<\/code>. As mentioned before, I prefer to zero the default <code>margin<\/code> and use a <code>font-size<\/code>-relative <code>padding<\/code>, but in a lot of cases, it doesn&#8217;t make any difference whatsoever.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"512\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_05_lim_elem_fullbleed_grad_back_issue_mark_s-1.png?resize=1024%2C512&#038;ssl=1\" alt=\"Screenshot collage. Shows the top area of the page with the header in both the dark and light theme cases at a narrow viewport width of 400px. It also highlights the fact that the header's full-bleed background isn't quite full-bleed, but stops a tiny distance away from the lateral sides.\" class=\"wp-image-7624\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_05_lim_elem_fullbleed_grad_back_issue_mark_s-1.png?resize=1024%2C512&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_05_lim_elem_fullbleed_grad_back_issue_mark_s-1.png?resize=300%2C150&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_05_lim_elem_fullbleed_grad_back_issue_mark_s-1.png?resize=768%2C384&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_05_lim_elem_fullbleed_grad_back_issue_mark_s-1.png?w=1280&amp;ssl=1 1280w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">the problem in the narrow viewport case, highlighted for both the dark and the light themes<\/figcaption><\/figure>\n\n\n\n<p>This happens when the maximum <code>grid<\/code> column width <code>--grid-w<\/code> doesn&#8217;t fit anymore in the available viewport space (not including the scrollbar) minus the lateral spacing on the sides of our one column grid (set as a <code>margin<\/code> or <code>padding<\/code>).<\/p>\n\n\n\n<p>The solution is to use a <code>max()<\/code> instead of the <code>calc()<\/code> to ensure that the <code>border-image<\/code> expands laterally at the very least as much as that lateral spacing <code>--grid-s<\/code>.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">body<\/span> {\n  <span class=\"hljs-attribute\">--grid-w<\/span>: <span class=\"hljs-number\">60em<\/span>;\n  <span class=\"hljs-attribute\">--grid-s<\/span>: .<span class=\"hljs-number\">5em<\/span>;\n  <span class=\"hljs-attribute\">display<\/span>: grid;\n  <span class=\"hljs-attribute\">grid-template-columns<\/span>: <span class=\"hljs-built_in\">min<\/span>(<span class=\"hljs-number\">100%<\/span>, var(--grid-w));\n  <span class=\"hljs-attribute\">justify-content<\/span>: center;\n  <span class=\"hljs-attribute\">padding<\/span>: <span class=\"hljs-number\">0<\/span> <span class=\"hljs-built_in\">var<\/span>(--grid-s);\n}\n\n<span class=\"hljs-selector-class\">.full-bleed-back<\/span> {\n  <span class=\"hljs-attribute\">border-image<\/span>: \n    <span class=\"hljs-built_in\">var<\/span>(--img) fill <span class=\"hljs-number\">0<\/span>\/ \/ \n    <span class=\"hljs-number\">0<\/span> <span class=\"hljs-built_in\">max<\/span>(var(--grid-s), <span class=\"hljs-number\">50<\/span>cqw - .<span class=\"hljs-number\">5<\/span>*<span class=\"hljs-built_in\">var<\/span>(--grid-w));\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='250' src='https:\/\/videopress.com\/embed\/b3m6AHEq?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1739540970'><\/script><\/div>\n\t\t\t<figcaption>fix for full-bleed background issue on narrow viewports (<a href=\"https:\/\/codepen.io\/thebabydino\/pen\/bNEaEON\">live demo<\/a>)<\/figcaption>\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>For actual images however, we have an even bigger problem: <code>border-image<\/code> doesn&#8217;t offer the <code>cover<\/code> option we have for backgrounds or images and we don&#8217;t really have a reliable way of getting around this. One of the\u00a0<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/border-image-repeat\">repeat options<\/a>\u00a0might work for us in some scenarios, but I find that\u2019s rarely the case for the results I want in such situations.<\/p>\n\n\n\n<p>You can see the problem in <a href=\"https:\/\/codepen.io\/thebabydino\/pen\/qEOjaaV\">this demo<\/a> when resizing the viewport \u2014 for an element whose <code>height<\/code> is unknown as it depends on its content, the <code>border-image<\/code> option (the second one) means that if we want to avoid the image getting distorted, then its size needs to be intrinsic size. Always. It never scales, which means it repeats for large viewports and its sides get clipped off for small viewports.<\/p>\n\n\n\n<p>So if we want more control over an image background or multiple background layers, it&#8217;s probably better to use an absolutely positioned pseudo-element. This also avoids the earlier problem of the full-bleed background not going all the way to the edges without taking into account the lateral spacing on the grid container (in this case, the <code>body<\/code>).<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.full-bleed-back-xtra<\/span> {\n  <span class=\"hljs-attribute\">position<\/span>: relative;\n  <span class=\"hljs-attribute\">z-index<\/span>: <span class=\"hljs-number\">1<\/span>\n}\n\n<span class=\"hljs-selector-class\">.full-bleed-back-xtra<\/span><span class=\"hljs-selector-pseudo\">::before<\/span> {\n  <span class=\"hljs-attribute\">position<\/span>: absolute;\n  <span class=\"hljs-attribute\">inset<\/span>: <span class=\"hljs-number\">0<\/span> <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">50%<\/span> - <span class=\"hljs-number\">50<\/span>cqw);\n  <span class=\"hljs-attribute\">z-index<\/span>: -<span class=\"hljs-number\">1<\/span>;\n  <span class=\"hljs-attribute\">content<\/span>: <span class=\"hljs-string\">''<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The <code>inset<\/code> makes our pseudo to stretch across the entire <code>padding-box<\/code> of its parent vertically and outside of it (minus sign) by half the available viewport space (viewport width minus any scrollbars) minus half the pseudo parent&#8217;s width.<\/p>\n\n\n\n<p>The negative <code>z-index<\/code> on the pseudo ensures it&#8217;s behind the element&#8217;s text content. The positive <code>z-index<\/code> on the element itself ensures the pseudo doesn&#8217;t end up behind the grid container&#8217;s <code>background<\/code> too.<\/p>\n\n\n\n<p>The pseudo <code>background<\/code> can now be a <code>cover<\/code> image:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-20\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">background<\/span>: <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--img-pos<\/span>, <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--img<\/span>) 50%)\/ <span class=\"hljs-selector-tag\">cover<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>I\u2019m taking this approach here to allow easily overriding the\u00a0<code>background-position<\/code> together with each image if necessary. In such a case, we set\u00a0<code>--img-pos<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-21\" data-shcb-language-name=\"CSP\" data-shcb-language-slug=\"csp\"><span><code class=\"hljs language-csp\">--img-pos: url(my-back-img.jpg) 35% 65%<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-21\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">csp<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Otherwise, we only set <code>--img<\/code> and the default of <code>50%<\/code> gets used:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-22\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">--img-pos<\/span>: <span class=\"hljs-selector-tag\">url<\/span>(<span class=\"hljs-selector-tag\">my-back-img<\/span><span class=\"hljs-selector-class\">.jpg<\/span>)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-22\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>In the particular case of our demos so far, which use a light or dark theme to respect user preferences, we\u2019ve also set a <code>light-dark()<\/code> value for the <code>background-color<\/code>, as well as an <code>overlay<\/code> blend mode to either brighten or darken our full-bleed background depending on the theme. This ensures the header text\u00a0 remains readable in both scenarios.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"512\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_06_lim_elem_fullbleed_img_back_an_s.png?resize=1024%2C512&#038;ssl=1\" alt=\"Screenshot. Shows a middle aligned grid with a single column and multiple rows, something that's highlighted by the DevTools-enabled grid overlay. On the very first row, we have a limited width header with a solid full-bleed image background. On other rows, we have full-bleed elements that expand all across the entire available page width (the viewport width minus any vertical scrollbars we might have). On other rows, we have breakout boxes that are not the same width as their grid cells, but are not wide enough to be full-bleed.\" class=\"wp-image-7593\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_06_lim_elem_fullbleed_img_back_an_s.png?resize=1024%2C512&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_06_lim_elem_fullbleed_img_back_an_s.png?resize=300%2C150&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_06_lim_elem_fullbleed_img_back_an_s.png?resize=768%2C384&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_06_lim_elem_fullbleed_img_back_an_s.png?w=1280&amp;ssl=1 1280w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">one column grid that has a tightly fit limited width header with a full-bleed image background; it also has a full-bleed image and a breakout element, as well as a DevTools grid overlay highlighting the grid lines (<a href=\"https:\/\/codepen.io\/thebabydino\/pen\/ByjJZOx\">live demo<\/a>)<\/figcaption><\/figure>\n\n\n\n<p>We can also have multiple layers of gradients, maybe even blended, maybe even with a <code>filter<\/code> making them <a href=\"https:\/\/frontendmasters.com\/blog\/grainy-gradients\/\">grainy<\/a> (something that would help with the visible banding noticed in the <code>border-image<\/code> method examples) or creating a <a href=\"https:\/\/frontendmasters.com\/blog\/pure-css-halftone-effect-in-3-declarations\/\">halftone pattern<\/a>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"512\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_07_lim_elem_fullbleed_filtered_back_an_s.png?resize=1024%2C512&#038;ssl=1\" alt=\"Screenshot. Shows a middle aligned grid with a single column and multiple rows, something that's highlighted by the DevTools-enabled grid overlay. On the very first row, we have a limited width header with a solid full-bleed multi-gradient, filtered background. On other rows, we have full-bleed elements that expand all across the entire available page width (the viewport width minus any vertical scrollbars we might have). On other rows, we have breakout boxes that are not the same width as their grid cells, but are not wide enough to be full-bleed.\" class=\"wp-image-7594\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_07_lim_elem_fullbleed_filtered_back_an_s.png?resize=1024%2C512&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_07_lim_elem_fullbleed_filtered_back_an_s.png?resize=300%2C150&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_07_lim_elem_fullbleed_filtered_back_an_s.png?resize=768%2C384&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_07_lim_elem_fullbleed_filtered_back_an_s.png?w=1280&amp;ssl=1 1280w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">one column grid that has a tightly fit limited width header with a filtered full-bleed multi-layer background; it also has a full-bleed image and a breakout element, as well as a DevTools grid overlay highlighting the grid lines (<a href=\"https:\/\/codepen.io\/thebabydino\/pen\/dPGJZyP\">live demo<\/a>)<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"combining-options\">Combining options<\/h2>\n\n\n\n<p>We can of course also have a breakout element with a full-bleed background &#8211; in this case, we give it both classes, <code>break-elem<\/code> and <code>full-bleed-back<\/code>.<\/p>\n\n\n\n<p>Our recipe page header for example, probably looks better as a breakout element in addition to having a full-bleed background.<\/p>\n\n\n\n<p>If the breakout elements in general have a <code>border<\/code> or their own specific <code>background<\/code>, we should ensure these don&#8217;t apply if they also have full-bleed backgrounds:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-23\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.break-elem<\/span><span class=\"hljs-selector-pseudo\">:not(<\/span><span class=\"hljs-selector-attr\">&#91;class*=<span class=\"hljs-string\">'full-bleed-back'<\/span>]<\/span>) {\n  <span class=\"hljs-attribute\">border<\/span>: solid <span class=\"hljs-number\">1px<\/span>;\n  <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-built_in\">var<\/span>(--break-back)\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-23\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Or we can opt to separate these visual prettifying styles from the layout ones. For example, in the Halloween example demos, I&#8217;ve opted to set the <code>border<\/code> and <code>background<\/code> styles via a separate class <code>.box<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-24\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.box<\/span> {\n  <span class=\"hljs-attribute\">border<\/span>: solid <span class=\"hljs-number\">1px<\/span> <span class=\"hljs-built_in\">var<\/span>(--c);\n  <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-built_in\">lch<\/span>(from var(--c) l c h\/ .<span class=\"hljs-number\">15<\/span>)\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-24\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>And then set <code>--c<\/code> (as well as the warning icon in front) via a <code>.box--warn<\/code> class.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"512\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_08_break_elem_fullbleed_back_an_s.png?resize=1024%2C512&#038;ssl=1\" alt=\"Screenshot. Shows a middle aligned grid with a single column and multiple rows, something that's highlighted by the DevTools-enabled grid overlay. On the very first row, we have a breakout header (wider than its containing grid cell, but not wide enough to be full-bleed) with a solid full-bleed multi-gradient, filtered background. On other rows, we have full-bleed elements that expand all across the entire available page width (the viewport width minus any vertical scrollbars we might have). On other rows, we have breakout boxes.\" class=\"wp-image-7595\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_08_break_elem_fullbleed_back_an_s.png?resize=1024%2C512&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_08_break_elem_fullbleed_back_an_s.png?resize=300%2C150&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_08_break_elem_fullbleed_back_an_s.png?resize=768%2C384&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_08_break_elem_fullbleed_back_an_s.png?w=1280&amp;ssl=1 1280w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">one column grid that has a breakout header with a filtered full-bleed multi-layer background; it also has a full-bleed image and a breakout element, as well as a DevTools grid overlay highlighting the grid lines (<a href=\"https:\/\/codepen.io\/thebabydino\/pen\/VYeyGwz\">live demo<\/a>)<\/figcaption><\/figure>\n\n\n\n<p>Another thing to note here is that when having a full-bleed background for a breakout element and we use the <code>border-image<\/code> tactic, we don&#8217;t have to adapt our formula to take into account the lateral spacing, as that&#8217;s set as a <code>padding<\/code> on the breakout element and not on its grid parent.<\/p>\n\n\n\n<p>The most important of these techniques can also be seen in the meta demo below, which has the relevant CSS in style elements that got <code>display: block<\/code>.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_GgoEeqY\" src=\"\/\/codepen.io\/anon\/embed\/GgoEeqY?height=510&amp;theme-id=1&amp;slug-hash=GgoEeqY&amp;default-tab=result\" height=\"510\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed GgoEeqY\" title=\"CodePen Embed GgoEeqY\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"nesting\">Nesting<\/h2>\n\n\n\n<p>We may also have a <code>figure<\/code> whose <code>img<\/code> is full-bleed, while the <code>figcaption<\/code> uses the normal column width (or maybe it&#8217;s a breakout element).<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-25\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">figure<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">'full-bleed-img.jpg'<\/span> <span class=\"hljs-attr\">alt<\/span>=<span class=\"hljs-string\">'image description'<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">'full-bleed-elem'<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">figcaption<\/span>&gt;<\/span>image caption<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">figcaption<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">figure<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-25\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Not much extra code is required here.<\/p>\n\n\n\n<p>The simple modern solution is to make the <code>img<\/code> a <code>block<\/code> element so that the <code>justify-self<\/code> property set via the <code>.full-bleed-elem<\/code> middle aligns it even if it&#8217;s not a <code>grid<\/code> or <code>flex<\/code> item.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-26\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">img<\/span><span class=\"hljs-selector-class\">.full-bleed-elem<\/span> { <span class=\"hljs-attribute\">display<\/span>: block }<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-26\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>However, support for <code>justify-self<\/code> applying to <code>block<\/code> elements as per the <a href=\"https:\/\/drafts.csswg.org\/css-align\/#overview\">current spec<\/a> is still limited to only Chromium browsers at the moment. And while the <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=1930584\">Firefox bug<\/a> seems to have had some activity lately, the <a href=\"https:\/\/bugs.webkit.org\/show_bug.cgi?id=277022\">Safari one<\/a> looks like it&#8217;s dormant.<\/p>\n\n\n\n<p>So the easy cross-browser way to get around that without any further computations is to make the <code>figure<\/code> a <code>grid<\/code> too in this case.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-27\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">figure<\/span><span class=\"hljs-selector-pseudo\">:has(.full-bleed-elem<\/span>, <span class=\"hljs-selector-class\">.break-elem<\/span>) {\n  <span class=\"hljs-attribute\">display<\/span>: grid;\n  <span class=\"hljs-attribute\">grid-template-columns<\/span>: <span class=\"hljs-number\">100%<\/span>;\n  <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">100%<\/span>;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-27\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"512\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_09_nested_fullbleed_elem_an_s.png?resize=1024%2C512&#038;ssl=1\" alt=\"Screenshot. Shows a middle aligned grid with a single column and multiple rows, something that's highlighted by the DevTools-enabled grid overlay. This grid has a figure that is tightly fit inside its grid cell, but also has a full-bleed image spreading across the entire available horizontal space (the viewport width minus any vertical scrollbars) we might have. On other rows, we have full-bleed elements or breakout boxes (wider than their containing grid cells, but still not wide enough to be full-bleed on wide screens). We also have a combination that's a breakout header with a full-bleed background.\" class=\"wp-image-7596\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_09_nested_fullbleed_elem_an_s.png?resize=1024%2C512&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_09_nested_fullbleed_elem_an_s.png?resize=300%2C150&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_09_nested_fullbleed_elem_an_s.png?resize=768%2C384&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_09_nested_fullbleed_elem_an_s.png?w=1280&amp;ssl=1 1280w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">one column grid that has a figure, tightly fit horizontally within its containing column, but with a full-bleed image; there&#8217;s also a DevTools grid overlay highlighting the grid lines (<a href=\"https:\/\/codepen.io\/thebabydino\/pen\/PwZELQJ\">live demo<\/a>)<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"floating-problems\">Floating Problems<\/h2>\n\n\n\n<p>This is a problem that <a href=\"https:\/\/nitter.net\/hybrid_alex\/status\/1580173843267989506\">got mentioned<\/a> for the three column <code>grid<\/code> technique and I really didn&#8217;t understand it at first.<\/p>\n\n\n\n<p>I started playing with CSS to change the look of a blog and for some reason, maybe because that was what the first example I saw looked like, I got into the habit of putting any floated thumbnail and the text next to it into a wrapper. And it never occurred to me that the wrapper wasn&#8217;t necessary until I started writing this article and looked into it.<\/p>\n\n\n\n<p>Mostly because&#8230; <em>I almost never need to float things<\/em>. I did it for those blog post thumbnails fifteen years ago, for <a href=\"https:\/\/codepen.io\/thebabydino\/pen\/eYwjjWL\"><code>shape-outside<\/code><\/a> demos, for <a href=\"https:\/\/codepen.io\/thebabydino\/pen\/oggaZaQ\">drop caps<\/a>, but that was about it. As far as layouts go, I just used <code>position: absolute<\/code> for years before going straight to <code>flex<\/code> and <code>grid<\/code>.<\/p>\n\n\n\n<p>This was why I didn&#8217;t understand this problem at first. I thought that if you want to float something, you have to put it in a wrapper anyway. And at the end of the day, this is the easiest solution: put the entire content of our one column in a wrapper. In which case, until <code>justify-self<\/code> applying on <code>block<\/code> elements works cross-browser, we need to replace that declaration on full-bleed and breakout elements with our old friend <code>margin-left<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-28\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">margin-left<\/span>: <span class=\"hljs-selector-tag\">calc<\/span>(50% <span class=\"hljs-selector-tag\">-50cqw<\/span>)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-28\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This allows us to have floated elements inside the wrapper.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"512\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_10_floaties_an_s.png?resize=1024%2C512&#038;ssl=1\" alt=\"Screenshot. Shows a middle aligned grid with a single column and multiple rows, something that's highlighted by the DevTools-enabled grid overlay. This grid has a single grid child that is tightly fit inside its containing column and acts as a wrapper for full-bleed elements, breakout boxes (wider than their containing grid cells, but still not wide enough to be full-bleed on wide screens), combinations of these like a breakout header with a full-bleed background. But this wrapper also allows its children to be floated.\" class=\"wp-image-7597\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_10_floaties_an_s.png?resize=1024%2C512&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_10_floaties_an_s.png?resize=300%2C150&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_10_floaties_an_s.png?resize=768%2C384&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/example_10_floaties_an_s.png?w=1280&amp;ssl=1 1280w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">one column grid that has a single grid child, tightly fit horizontally within its containing column and acting as a wrapper for the entire page content; since this wrapper has no flex or grid layout, its children can be floated (<a href=\"https:\/\/codepen.io\/thebabydino\/pen\/yyevybL\">live demo<\/a>)<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"final-thoughts-do-we-even-really-need-grid\">Final Thoughts: Do we even really need grid?<\/h2>\n\n\n\n<p>At this point, getting to this floats solution begs the question: do we even really need grid?<\/p>\n\n\n\n<p>It depends.<\/p>\n\n\n\n<p>We could just set lateral <code>padding<\/code> or <code>margin<\/code> on the <code>body<\/code> instead. <\/p>\n\n\n\n<p>I&#8217;d normally prefer <code>padding<\/code> in this case, as <code>padding<\/code> doesn&#8217;t restrict the <code>background<\/code> and sometimes we want some full viewport backdrop effects involving both the <code>body<\/code> and the <code>html<\/code> background. <\/p>\n\n\n\n<p>Other times, we may want a <code>background<\/code> just for the limited <code>width<\/code> of the content in the middle, in which case <code>margin<\/code> on the <code>body<\/code> makes more sense.<\/p>\n\n\n\n<p>If we want to be ready for both situations, then we&#8217;re better off with not setting any <code>margin<\/code> or <code>padding<\/code> on the <code>body<\/code> and just wrapping all content in a limited width, middle aligned (good old <code>max-width<\/code> plus <code>auto<\/code> margins) <code>main<\/code> that also gets a <code>background<\/code>.<\/p>\n\n\n\n<p>At the same time, my uses cases for something like this have never involved using floats and have benefitted from other <code>grid<\/code> features like gaps, which make handling spacing easier than via margins or paddings.<\/p>\n\n\n\n<p>So at the end of the day, the best solution is going to depend on the context.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Having a width-limited centered column of content is common and good, but what do you do when you need to break out? It&#8217;s not hard these days, but it does depend on the situation.<\/p>\n","protected":false},"author":32,"featured_media":7609,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[48,7,418,123],"class_list":["post-7560","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-container-units","tag-css","tag-full-bleed","tag-grid"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/breakout.jpg?fit=2000%2C1200&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/7560","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/users\/32"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=7560"}],"version-history":[{"count":17,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/7560\/revisions"}],"predecessor-version":[{"id":7635,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/7560\/revisions\/7635"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/7609"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=7560"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=7560"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=7560"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}