{"componentChunkName":"component---src-templates-post-jsx","path":"/blog/faking-container-queries-with-css-grid/","result":{"data":{"mdx":{"body":"function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nconst _frontmatter = {\n  \"title\": \"Faking container queries with CSS Grid\",\n  \"cover\": \"/blogs/containerquerygrid/facebooks.png\",\n  \"date\": \"2021-02-02\",\n  \"noshadow\": true,\n  \"type\": [\"tutorial\"]\n};\nconst layoutProps = {\n  _frontmatter\n};\nconst MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  let {\n    components\n  } = _ref,\n      props = _objectWithoutProperties(_ref, [\"components\"]);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, `Container queries are an (hopefully) upcoming feature in CSS that will let you create responsive designs that respond to the dimensions of their container (like a div or other HTML element) rather than the dimensions of the entire page, which we currently do with media queries.`), mdx(\"p\", null, `This is super helpful for component-based design, since you no longer have to create different variants of a component when you need them to display slightly differently depending on where they're shown.`), mdx(\"p\", null, `Unfortunately, container queries don't exist yet.`), mdx(\"p\", null, `Recently this became an issue for me when Facebook updated their rich previews on desktop to be shown at three discrete widths depending on the viewport width.`), mdx(\"img\", {\n    alt: \"Polypane as shared on Facebook at three discrete widths\",\n    src: \"/blogs/containerquerygrid/facebooks.png\",\n    style: {\n      margin: '2rem auto',\n      display: 'block',\n      maxWidth: '100%'\n    }\n  }), mdx(\"p\", null, `That meant I had to update the previews in the Polypane side panel to show at only those three widths. The side panel can be sized independent of the viewport though, so media queries were not an option. Because they used discrete widths, I also couldn't use auto sizing to let the preview grow along with the width of the side panel. I needed container queries.`), mdx(\"p\", null, mdx(\"em\", {\n    parentName: \"p\"\n  }, `Note: this has since been updated/reverted (or I was in some A/B testing group) but the technique I came up with was useful enough to still write an article about.`)), mdx(\"p\", null, `With CSS Grid and some trickery I managed to implement this in a way that looks very close to container queries.`), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, `Just to note: real container queries would let you do significantly more than having three discrete widths so this is in no way a full replacement. But it's useful enough that I think it's a technique to know about.`)), mdx(\"h2\", {\n    \"id\": \"defining-the-problem\"\n  }, `Defining the problem`), mdx(\"p\", null, `The problem we're trying to solve is this: depending on the width of the viewport, the rich previews on Facebook are either 500, 590 or 680 pixels wide. In Polypane, we want to show the largest version that fits in the sidebar.`), mdx(\"p\", null, `We can't use the viewport width, since the width of the sidebar is independent of it. And we can't use a fluid width combined with a max-width, since we need it to snap to those three sizes so it acts just like Facebook.`), mdx(\"p\", null, `If we look at those three widths we see that from 500 pixels, the width increases by 90 pixels each time. This will come of use later.`), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, `Getting the preview exactly right might feel pedantic, but I want the Social media previews in Polypane to be `, mdx(\"strong\", {\n    parentName: \"p\"\n  }, `pixel perfect`), ` as well as `, mdx(\"strong\", {\n    parentName: \"p\"\n  }, `character perfect`), `, so you can be sure that what you see in Polypane is what people see on social media sites.`), mdx(\"br\", null), \"This is something not even the official validators of Facebook, Twitter and LinkedIn do. In fact, they all use outdated designs that no longer match with the actual sites, making Polypane the only place that matches with reality. That takes a lot of work, but it's totally worth it.\"), mdx(\"h2\", {\n    \"id\": \"css-grid\"\n  }, `CSS Grid`), mdx(\"p\", null, `Admittedly I haven't done a lot with CSS Grid yet so I am by no means a pro. I usually default to Flexbox as it fits most of the things I need to design. Here's how I decide whether to use CSS Grid or Flexbox:`), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, `Do I have a bunch of elements that I need to lay out? Then I use Flexbox`), mdx(\"li\", {\n    parentName: \"ul\"\n  }, `Do I have a bunch of areas that I need to put elements in? Then I use CSS Grid.`)), mdx(\"p\", null, `And in this case, the area should be leading, which led me (along with some help from the Fronteers Slack) to see if I could get CSS Grid for my solution. In CSS Grid you can define a set of areas in two dimensions (though we only need one dimension for this example) so that's what we'll do.`), mdx(\"h2\", {\n    \"id\": \"setup\"\n  }, `Setup`), mdx(\"p\", null, `We begin with setting up a wrapper for our Facebook preview to live in:`), mdx(\"div\", {\n    \"className\": \"gatsby-highlight\",\n    \"data-language\": \"html\"\n  }, mdx(\"pre\", _extends({\n    parentName: \"div\"\n  }, {\n    \"className\": \"language-html\"\n  }), mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-html\"\n  }), mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token tag\"\n  }), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token tag\"\n  }), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `<`), `div`), ` `, mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token attr-name\"\n  }), `class`), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token attr-value\"\n  }), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation attr-equals\"\n  }), `=`), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `\"`), `wrapper`, mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `\"`)), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `>`)), `\n  `, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token tag\"\n  }), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token tag\"\n  }), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `<`), `div`), ` `, mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token attr-name\"\n  }), `class`), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token attr-value\"\n  }), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation attr-equals\"\n  }), `=`), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `\"`), `preview`, mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `\"`)), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `>`)), `Facebook Preview`, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token tag\"\n  }), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token tag\"\n  }), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `</`), `div`), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `>`)), `\n`, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token tag\"\n  }), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token tag\"\n  }), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `</`), `div`), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `>`))))), mdx(\"p\", null, `And some CSS that will set up the grid in the wrapper, and then position the preview inside it:`), mdx(\"div\", {\n    \"className\": \"gatsby-highlight\",\n    \"data-language\": \"css\"\n  }, mdx(\"pre\", _extends({\n    parentName: \"div\"\n  }, {\n    \"className\": \"language-css\"\n  }), mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-css\"\n  }), mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token selector\"\n  }), `.wrapper`), ` `, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `{`), `\n  `, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token property\"\n  }), `display`), mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `:`), ` grid`, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `;`), `\n  `, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token property\"\n  }), `max-width`), mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `:`), ` 680px`, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `;`), `\n  `, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token property\"\n  }), `grid-template-columns`), mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `:`), ` 500px `, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token function\"\n  }), `repeat`), mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `(`), `auto-fill`, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `,`), ` 90px`, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `)`), mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `;`), `\n`, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `}`), `\n\n`, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token selector\"\n  }), `.preview`), ` `, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `{`), `\n  `, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token property\"\n  }), `grid-column`), mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `:`), ` 1 / -1`, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `;`), `\n`, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `}`)))), mdx(\"p\", null, `What we've done here is set up a CSS grid that's at most 680 pixels wide (the widest our preview should get), has one column that's 500px width (the smallest our preview should get) and then a \"repeat()\" function that adds columns of 90 pixels wide as long as they fit, which is what the `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `auto-fill`), ` function does.`), mdx(\"p\", null, `Then, for our preview, we set `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `grid-column`), ` to `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `1 / -1`), `. What this means is `, mdx(\"em\", {\n    parentName: \"p\"\n  }, `this element goes from the first grid line from the start (1), to the first grid line from the end (-1)`), `. This essentially means it will always occupy all grid areas regardless of how many grid areas there are.`), mdx(\"p\", null, `Here's a live, resizable preview you can try and see what happens. The white dotted lines indicate the borders of the grid areas (drag the bottom right corner):`), mdx(\"div\", {\n    style: {\n      resize: 'horizontal',\n      background: '#fff',\n      border: '2px solid rgb(30, 30, 63)',\n      padding: '1rem',\n      overflow: 'hidden',\n      position: 'relative',\n      marginLeft: 'auto',\n      marginRight: 'auto',\n      borderRadius: '4px',\n      boxShadow: 'inset rgba(20, 40, 50, 0.05) 0px 4px 4px, inset rgba(20, 40, 50, 0.05) 0px 1px 10px, inset rgba(20, 40, 50, 0.08) 0px 20px 30px'\n    }\n  }, mdx(\"div\", {\n    className: \"wrapper\",\n    style: {\n      position: 'relative',\n      display: 'grid',\n      maxWidth: '680px',\n      gridTemplateColumns: '500px repeat(auto-fill, 90px)',\n      justifyContent: 'center',\n      margin: '0 auto'\n    }\n  }, mdx(\"div\", {\n    className: \"preview\",\n    style: {\n      gridRow: '1 / 2',\n      gridColumn: '1 / -1',\n      background: 'rgba(0, 70, 150, 1)',\n      fontWeight: 'bold',\n      color: '#fff',\n      height: '300px',\n      display: 'grid',\n      placeItems: 'center',\n      borderRadius: '3px'\n    }\n  }, \"Preview\"), mdx(\"div\", {\n    className: \"gridLine\",\n    style: {\n      gridRow: '1 / 2',\n      gridColumn: '1 / 2',\n      borderRight: '1px dotted #fff8',\n      height: '300px'\n    }\n  }), mdx(\"div\", {\n    className: \"gridLine\",\n    style: {\n      gridRow: '1 / 2',\n      gridColumn: '2 / 3',\n      borderRight: '1px dotted #fff8',\n      height: '300px'\n    }\n  }))), mdx(\"p\", null, `If you look closely, you can see that it `, mdx(\"strong\", {\n    parentName: \"p\"\n  }, `doesn't actually work`), `: while the Facebook preview snaps to 680px and 590px wide, it doesn't go to our 500px width. This is because `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `repeat()`), ` always shows at least one column. I initially thought this would be at least zero.`), mdx(\"p\", null, `So how do we fix that?`), mdx(\"h2\", {\n    \"id\": \"getting-it-just-right\"\n  }, `Getting it just right`), mdx(\"p\", null, `Since we now know there's always at least one column of 90 pixels, and the minimum size we want is 500px, we need the first column in our CSS Grid to be 500 - 90 = 410 pixels wide. Let's try that:`), mdx(\"div\", {\n    \"className\": \"gatsby-highlight\",\n    \"data-language\": \"css\"\n  }, mdx(\"pre\", _extends({\n    parentName: \"div\"\n  }, {\n    \"className\": \"language-css\"\n  }), mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-css\"\n  }), mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token selector\"\n  }), `.wrapper`), ` `, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `{`), `\n  `, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token property\"\n  }), `display`), mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `:`), ` grid`, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `;`), `\n  `, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token property\"\n  }), `max-width`), mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `:`), ` 680px`, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `;`), `\n  `, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token property\"\n  }), `grid-template-columns`), mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `:`), ` 410px `, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token function\"\n  }), `repeat`), mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `(`), `auto-fill`, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `,`), ` 90px`, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `)`), mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `;`), `\n`, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `}`)))), mdx(\"p\", null, `And a new live preview:`), mdx(\"div\", {\n    style: {\n      resize: 'horizontal',\n      background: '#fff',\n      border: '2px solid rgb(30, 30, 63)',\n      padding: '1rem',\n      overflow: 'hidden',\n      position: 'relative',\n      marginLeft: 'auto',\n      marginRight: 'auto',\n      borderRadius: '4px',\n      boxShadow: 'inset rgba(20, 40, 50, 0.05) 0px 4px 4px, inset rgba(20, 40, 50, 0.05) 0px 1px 10px, inset rgba(20, 40, 50, 0.08) 0px 20px 30px'\n    }\n  }, mdx(\"div\", {\n    className: \"wrapper\",\n    style: {\n      display: 'grid',\n      maxWidth: '680px',\n      gridTemplateColumns: '410px repeat(auto-fill, 90px)',\n      justifyContent: 'center',\n      margin: '0 auto'\n    }\n  }, mdx(\"div\", {\n    className: \"preview\",\n    style: {\n      gridRow: '1 / 2',\n      gridColumn: '1 / -1',\n      background: 'rgba(0, 70, 150, 1)',\n      fontWeight: 'bold',\n      color: '#fff',\n      height: '300px',\n      display: 'grid',\n      placeItems: 'center',\n      borderRadius: '3px'\n    }\n  }, \"Preview\"), mdx(\"div\", {\n    className: \"gridLine\",\n    style: {\n      gridRow: '1 / 2',\n      gridColumn: '1 / 2',\n      borderRight: '1px dotted #fff8',\n      height: '300px'\n    }\n  }), mdx(\"div\", {\n    className: \"gridLine\",\n    style: {\n      gridRow: '1 / 2',\n      gridColumn: '2 / 3',\n      borderRight: '1px dotted #fff8',\n      height: '300px'\n    }\n  }), mdx(\"div\", {\n    className: \"gridLine\",\n    style: {\n      gridRow: '1 / 2',\n      gridColumn: '3 / 4',\n      borderRight: '1px dotted #fff8',\n      height: '300px'\n    }\n  }))), mdx(\"p\", null, `The preview area now snaps to 680, 590 and 500 pixels wide. We've created a container query-like experience where depending on the available total width we snap to a set of fixed sizes.`), mdx(\"p\", null, `As noted earlier in the article, Facebook has since reverted/updated to a fluid column that scales along with the viewport. This technique can still be useful to achieve certain effects. Maybe you want things to align on a pixel basis, or have a grid system but want to keep fixed widths within that grid.`), mdx(\"p\", null, `If you end up using this, `, mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://twitter.com/polypane\"\n  }), `let me know on Twitter!`)));\n}\n;\nMDXContent.isMDXComponent = true;","timeToRead":4,"tableOfContents":{"items":[{"url":"#defining-the-problem","title":"Defining the problem"},{"url":"#css-grid","title":"CSS Grid"},{"url":"#setup","title":"Setup"},{"url":"#getting-it-just-right","title":"Getting it just right"}]},"excerpt":"Container queries are an (hopefully) upcoming feature in CSS that will let you create responsive designs that respond to the dimensions of their container (like…","frontmatter":{"title":"Faking container queries with CSS Grid","cover":"/blogs/containerquerygrid/facebooks.png","date":"2021-02-02","updated":null},"fields":{"slug":"/blog/faking-container-queries-with-css-grid/","date":"2021-02-01T23:00:00.000Z","ogFileName":"faking-container-queries-with-css-grid"}}},"pageContext":{"slug":"/blog/faking-container-queries-with-css-grid/"}},"staticQueryHashes":["4164364741","425175329"]}