{"id":5761,"date":"2025-05-06T18:53:28","date_gmt":"2025-05-06T23:53:28","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=5761"},"modified":"2025-05-06T18:53:29","modified_gmt":"2025-05-06T23:53:29","slug":"using-container-query-units-relative-to-an-outer-container","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/using-container-query-units-relative-to-an-outer-container\/","title":{"rendered":"Using Container Query Units Relative to an Outer Container"},"content":{"rendered":"\n<p>Recently, Matt Wilcox&nbsp;<a href=\"https:\/\/mstdn.social\/@mattwilcox\/114386944917360151\">posted on Mastodon<\/a>:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>The fact you can\u2019t specify&nbsp;<em>which<\/em>&nbsp;container for container query units is a ballache. The moment you have nested containers you\u2019re [screwed]; because if you want the calculated gap from the row\u2019s container; but you\u2019re inside a nested container\u2026 tough. Your units are wrong. And you can\u2019t just say \u201cno; not relative to&nbsp;<em>this<\/em>&nbsp;container; relative to the named outer container!\u201d<\/p>\n<\/blockquote>\n\n\n\n<p>First off, if you&#8217;re not familiar with container queries and container query units, you can check out one of the many resources on the topic, for example this\u00a0<a href=\"https:\/\/ishadeed.com\/article\/css-container-query-guide\/\">interactive guide<\/a>\u00a0by Ahmad Shadeed, which I believe is the most recent out of all the detailed ones I&#8217;ve seen. As always, the date of the resources used is important for web stuff, especially since these units in particular have changed their name since\u00a0<a href=\"https:\/\/github.com\/w3c\/csswg-drafts\/issues\/5888\">they were first proposed<\/a>\u00a0and we got an early draft of the spec.<\/p>\n\n\n\n<p>Now, the problem at hand: let&#8217;s say we have an&nbsp;<code>.inner-container<\/code>&nbsp;inside an&nbsp;<code>.outer-container<\/code>&nbsp;&#8211; they are both made to be containers:<\/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-attr\">&#91;class*=<span class=\"hljs-string\">'container'<\/span>]<\/span> { <span class=\"hljs-attribute\">container-type<\/span>: size }<\/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>We want any&nbsp;<code>.inner-child<\/code>&nbsp;of the&nbsp;<code>.inner-container<\/code>&nbsp;to be able to use length values set in container query units relative to the&nbsp;<code>.outer-container<\/code>&nbsp;(more precisely, to its&nbsp;<code>content-box<\/code>&nbsp;dimensions). The problem is, if we do something like this (a&nbsp;<code>20cqw<\/code>&nbsp;light blue strip at the start of the gradient going towards 3 o&#8217;clock):<\/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-class\">.inner-child<\/span> {\n  <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-built_in\">linear-gradient<\/span>(<span class=\"hljs-number\">90deg<\/span>, #<span class=\"hljs-number\">0<\/span>a9396 <span class=\"hljs-number\">20<\/span>cqw, #<span class=\"hljs-number\">0000<\/span>)\n}<\/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>&#8230; then the&nbsp;<code>20cqw<\/code>&nbsp;value is&nbsp;<code>20%<\/code>&nbsp;(a fifth) of the&nbsp;<code>content-box<\/code>&nbsp;width of the&nbsp;<code>.inner-container<\/code>. This can be seen below, where we have purple guidelines&nbsp;<code>20%<\/code>&nbsp;of the width apart.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"633\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/05\/437958325-f8239b65-787e-498d-a43d-15c3373490a0-1.png?resize=1024%2C633&#038;ssl=1\" alt=\"Screenshot illustrating how a background sized to cqw on the child of the inner container is a fifth of the inner container's width.\" class=\"wp-image-5768\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/05\/437958325-f8239b65-787e-498d-a43d-15c3373490a0-1.png?resize=1024%2C633&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/05\/437958325-f8239b65-787e-498d-a43d-15c3373490a0-1.png?resize=300%2C185&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/05\/437958325-f8239b65-787e-498d-a43d-15c3373490a0-1.png?resize=768%2C475&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/05\/437958325-f8239b65-787e-498d-a43d-15c3373490a0-1.png?resize=1536%2C949&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/05\/437958325-f8239b65-787e-498d-a43d-15c3373490a0-1.png?w=1560&amp;ssl=1 1560w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">what 20cqw represents<\/figcaption><\/figure>\n\n\n\n<p>But what we want is for that&nbsp;<code>20cqw<\/code>&nbsp;value to be&nbsp;<code>20%<\/code>&nbsp;of the&nbsp;<code>content-box<\/code>&nbsp;width of the&nbsp;<code>.outer-container<\/code>.<\/p>\n\n\n\n<p>Strictly for the queries themselves, we could do something like this:<\/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\">.outer-container<\/span> { <span class=\"hljs-attribute\">container<\/span>: outer\/ size }\n<span class=\"hljs-selector-class\">.inner-container<\/span> { <span class=\"hljs-attribute\">container<\/span>: inner\/ size }\n\n<span class=\"hljs-keyword\">@container<\/span> outer (<span class=\"hljs-attribute\">min-width:<\/span> <span class=\"hljs-number\">500px<\/span>) {\n  <span class=\"hljs-selector-class\">.inner-child<\/span> { <span class=\"hljs-attribute\">background<\/span>: darkorange }\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>This allows us to set certain styles on the&nbsp;<code>.inner-child<\/code>&nbsp;elements based on where the&nbsp;<code>width<\/code>&nbsp;of the&nbsp;<code>.outer-container<\/code>&nbsp;(which isn&#8217;t the&nbsp;<em>nearest<\/em>&nbsp;container for&nbsp;<code>.inner-child<\/code>) is situated relative to the&nbsp;<code>500px<\/code>&nbsp;threshold.<\/p>\n\n\n\n<p>But we cannot do something like this to specify which container should be the one that the query units used on&nbsp;<code>.inner-child<\/code>&nbsp;are relative to:<\/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-selector-class\">.inner-child<\/span> {\n  <span class=\"hljs-comment\">\/* can't do this *\/<\/span>\n  <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-built_in\">linear-gradient<\/span>(<span class=\"hljs-number\">90deg<\/span>, #<span class=\"hljs-number\">0<\/span>a9396 outer <span class=\"hljs-number\">20<\/span>cqw, #<span class=\"hljs-number\">0000<\/span>)\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>Nor can we do this:<\/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\">.inner-child<\/span> {\n  <span class=\"hljs-comment\">\/* can't do this either *\/<\/span>\n  <span class=\"hljs-attribute\">--s<\/span>: outer <span class=\"hljs-number\">20<\/span>cqw;\n  <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-built_in\">linear-gradient<\/span>(<span class=\"hljs-number\">90deg<\/span>, #<span class=\"hljs-number\">0<\/span>a9396 var(--s), <span class=\"hljs-number\">#0000<\/span>)\n}<\/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>However, we&nbsp;<em>are<\/em>&nbsp;getting closer!<\/p>\n\n\n\n<p>What if we move the&nbsp;<code>--s<\/code>&nbsp;variable uspstream? After all, a&nbsp;<code>20cqw<\/code>&nbsp;length value set on the&nbsp;<code>.inner-container<\/code>&nbsp;is&nbsp;<code>20%<\/code>&nbsp;of the&nbsp;<code>content-box<\/code>&nbsp;width of its nearest container, which is the&nbsp;<code>.outer-container<\/code>. This would mean our code becomes:<\/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-attr\">&#91;class*=<span class=\"hljs-string\">'container'<\/span>]<\/span> { <span class=\"hljs-attribute\">container-type<\/span>: size }\n\n<span class=\"hljs-selector-class\">.inner-container<\/span> {\n  <span class=\"hljs-attribute\">--s<\/span>: <span class=\"hljs-number\">20<\/span>cqw;\n  <span class=\"hljs-attribute\">background<\/span>: \n    <span class=\"hljs-built_in\">repeating-linear-gradient<\/span>(<span class=\"hljs-number\">45deg<\/span>, #bb3e03 <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">5px<\/span>, #<span class=\"hljs-number\">0000<\/span> <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">1em<\/span>) \n      <span class=\"hljs-number\">0<\/span>\/ <span class=\"hljs-built_in\">var<\/span>(--s) no-repeat\n}\n\n<span class=\"hljs-selector-class\">.inner-child<\/span> {\n  <span class=\"hljs-attribute\">background<\/span>: \n    <span class=\"hljs-built_in\">linear-gradient<\/span>(<span class=\"hljs-number\">90deg<\/span>, #<span class=\"hljs-number\">0<\/span>a9396cc var(--s), <span class=\"hljs-number\">#0000<\/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>We also give the&nbsp;<code>.inner-container<\/code>&nbsp;a similar&nbsp;<code>background<\/code>&nbsp;restricted to&nbsp;<code>20cqw<\/code>&nbsp;from the left along the&nbsp;<em>x<\/em>&nbsp;axis and make the&nbsp;<code>.inner-child<\/code>&nbsp;semi-transparent, just to check if the&nbsp;<code>--s<\/code>&nbsp;values overlap (which is what we want,&nbsp;<code>--s<\/code>&nbsp;being&nbsp;<code>20%<\/code>&nbsp;or a fifth of the&nbsp;<code>.outer-container<\/code>&nbsp;width). However,&nbsp;<a href=\"https:\/\/codepen.io\/thebabydino\/pen\/xbbgKzp\">this fails<\/a>, as it can be seen 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=\"633\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/05\/437957569-0be163ba-6508-49d8-8f4e-7dd96d7d38fe.png?resize=1024%2C633&#038;ssl=1\" alt=\"Screenshot. Both the inner container and its child have a background sized to 20cqw. However, the container query units are relative to the outer container only for the inner container, the container query units used on its child being still relative to the inner container (one fifth of its content-box width).\" class=\"wp-image-5769\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/05\/437957569-0be163ba-6508-49d8-8f4e-7dd96d7d38fe.png?resize=1024%2C633&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/05\/437957569-0be163ba-6508-49d8-8f4e-7dd96d7d38fe.png?resize=300%2C185&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/05\/437957569-0be163ba-6508-49d8-8f4e-7dd96d7d38fe.png?resize=768%2C475&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/05\/437957569-0be163ba-6508-49d8-8f4e-7dd96d7d38fe.png?resize=1536%2C949&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/05\/437957569-0be163ba-6508-49d8-8f4e-7dd96d7d38fe.png?w=1560&amp;ssl=1 1560w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">screenshot of result<\/figcaption><\/figure>\n\n\n\n<p>For the&nbsp;<code>.inner-container<\/code>&nbsp;the&nbsp;<code>20cqw<\/code>&nbsp;of the&nbsp;<code>--s<\/code>&nbsp;is taken to be&nbsp;<code>20%<\/code>&nbsp;of the&nbsp;<code>content-box<\/code>&nbsp;width of its nearest container,&nbsp;<code>.outer-container<\/code>&nbsp;(dashed dark blue boundary). However, for the&nbsp;<code>.inner-child<\/code>, the&nbsp;<code>20cqw<\/code>&nbsp;of the&nbsp;<code>--s<\/code>&nbsp;aren&#8217;t taken to mean the same value. Instead, they are taken to mean&nbsp;<code>20%<\/code>&nbsp;of the&nbsp;<code>.content-box<\/code>&nbsp;width of the&nbsp;<code>.inner-container<\/code>&nbsp;(dotted dark red boundary).<\/p>\n\n\n\n<p>Boo!<\/p>\n\n\n\n<p>But what happens if we also register&nbsp;<code>--s<\/code>?<\/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-keyword\">@property<\/span> --s {\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}<\/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>Bingo,&nbsp;<a href=\"https:\/\/codepen.io\/thebabydino\/pen\/GggNemJ\">this works<\/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=\"633\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/05\/437957622-82985165-4171-456f-9e16-319410410db1-1.png?resize=1024%2C633&#038;ssl=1\" alt=\"Screenshot. Both the inner container and its child have a background sized to 20cqw, the container query units being relative to the outer container.\" class=\"wp-image-5770\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/05\/437957622-82985165-4171-456f-9e16-319410410db1-1.png?resize=1024%2C633&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/05\/437957622-82985165-4171-456f-9e16-319410410db1-1.png?resize=300%2C185&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/05\/437957622-82985165-4171-456f-9e16-319410410db1-1.png?resize=768%2C475&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/05\/437957622-82985165-4171-456f-9e16-319410410db1-1.png?resize=1536%2C949&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/05\/437957622-82985165-4171-456f-9e16-319410410db1-1.png?w=1560&amp;ssl=1 1560w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">desired result<\/figcaption><\/figure>\n\n\n\n<p>I hope you&#8217;ve enjoyed this little trick.<\/p>\n\n\n\n<p>Where would you use this?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Container units (e.g. cqi) are only able to reference the closest container. But if you register a custom property and set that higher up, you can get your hands on other containers units.<\/p>\n","protected":false},"author":32,"featured_media":5803,"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":[47,48,7,180],"class_list":["post-5761","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-container-queries","tag-container-units","tag-css","tag-custom-properties"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/05\/Using-Container-Query-Units-Relative-to-an-Outer-Container.jpg?fit=1140%2C676&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/5761","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=5761"}],"version-history":[{"count":6,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/5761\/revisions"}],"predecessor-version":[{"id":5805,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/5761\/revisions\/5805"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/5803"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=5761"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=5761"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=5761"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}