Skip to content

Instantly share code, notes, and snippets.

@getflourish
Created June 19, 2021 08:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save getflourish/10594fc87479890e5a73d27c603eab50 to your computer and use it in GitHub Desktop.
Save getflourish/10594fc87479890e5a73d27c603eab50 to your computer and use it in GitHub Desktop.
<body>
<nav class="feed-list">
<h2>Typographic Reader</h2>
<ul>
<li>css-tricks</li>
<li>A list apart</li>
</ul>
</nav>
<nav class="article-list">
<article>
<h2>CSS Tricks</h2>
<ul>
<li>
<span>A Love Letter to HTML & CSS</span>
</li>
<li>
<span>How to Create Neon Text With CSS</span>
</li>
<li>
<span>Next Gen CSS: @container</span>
</li>
</ul>
</article>
</nav>
<main class="article-container">
<article class="article">
<h1 class="title" data-reader-unique-id="titleElement">Next Gen CSS:&nbsp;@container</h1>
<div class="metadata singleline">
<div data-reader-unique-id="373" class="date"><a href="https://css-tricks.com/author/unakravets/" aria-label="Author page of Una Kravets" data-reader-unique-id="374">
<noscript data-reader-unique-id="375">&lt;img alt='Avatar of Una Kravets' src='https://secure.gravatar.com/avatar/354bf07bc260006735563585f9e18d99?s=80&amp;#038;d=retro&amp;#038;r=pg' srcset='https://secure.gravatar.com/avatar/354bf07bc260006735563585f9e18d99?s=160&amp;#038;d=retro&amp;#038;r=pg 2x' class='avatar avatar-80 photo skip-lazy' height='80' width='80' loading='lazy'/&gt;</noscript> </a>
<div data-reader-unique-id="376">
<a href="https://css-tricks.com/author/unakravets/" data-reader-unique-id="377" style="font-weight: bold;">
Una Kravets </a>
on
<time datetime="" data-reader-unique-id="378">
May 11, 2021 </time>
(Updated on <time datetime="2021-06-03" data-reader-unique-id="379">Jun 3, 2021</time>)
</div>
</div>
</div>
<p data-reader-unique-id="1">Chrome is experimenting with <code data-reader-unique-id="2">@container</code>, a property within the CSS Working Group <a href="https://github.com/w3c/csswg-drafts/issues?q=is%3Aissue+label%3Acss-contain-3+" data-reader-unique-id="3">Containment Level 3 spec</a> being championed by <a href="https://twitter.com/TerribleMia" data-reader-unique-id="4">Miriam Suzanne</a> of <a href="https://css.oddbird.net/rwd/query/" data-reader-unique-id="5">Oddbird</a>, and a group of engineers across the web platform. <code data-reader-unique-id="6">@container</code> brings us the ability to <strong data-reader-unique-id="7">style elements based on the size of their parent container</strong>.</p>
<span data-reader-unique-id="8"></span>
<div data-reader-unique-id="9">
<div data-reader-unique-id="10">
<p data-reader-unique-id="11">The <code data-reader-unique-id="12">@container</code> API is not stable, and is subject to syntax changes. If you try it out on your own, you may encounter a few bugs. Please report those bugs to the appropriate browser engine!</p>
<p data-reader-unique-id="13"><strong data-reader-unique-id="14">Bugs:</strong> <a href="https://bugs.chromium.org/p/chromium/issues/list" data-reader-unique-id="15">Chrome</a> | <a href="https://bugzilla.mozilla.org/home" data-reader-unique-id="16">Firefox</a> | <a href="https://bugs.webkit.org/query.cgi?format=specific&amp;product=WebKit" data-reader-unique-id="17">Safari</a></p>
</div>
</div>
<p data-reader-unique-id="18">You can think of these like a media query (<code data-reader-unique-id="19">@media</code>), but instead of relying on the <strong data-reader-unique-id="20">viewport</strong> to adjust styles, the parent container of the element you’re targeting can adjust those styles.</p>
<h3 data-reader-unique-id="21"><span class="converted-anchor" data-reader-unique-id="22"></span>Container queries will be the single biggest change in web styling since CSS3, altering our perspective of what “responsive design” means.</h3>
<p data-reader-unique-id="25">No longer will the viewport and user agent be the only targets we have to create responsive layout and UI styles. With container queries, elements will be able to target their own parents and apply their own styles accordingly. This means that the same element that lives in the sidebar, body, or hero could look completely different based on its available size and dynamics.</p>
<h3 data-reader-unique-id="26"><span class="converted-anchor" data-reader-unique-id="27"></span><code data-reader-unique-id="30">@container</code> in action</h3>
<p data-reader-unique-id="34">In this example, I’m using two cards within a parent with the following markup:</p>
<div class="scrollable extendsBeyondTextColumn">
<pre rel="HTML" data-line="" class="clear" data-reader-unique-id="35" style="white-space: pre;"><code markup="tt" data-reader-unique-id="36"><span data-reader-unique-id="37"><span data-reader-unique-id="38"><span data-reader-unique-id="39">&lt;</span>div</span> <span data-reader-unique-id="40">class</span><span data-reader-unique-id="41"><span data-reader-unique-id="42">=</span><span data-reader-unique-id="43">"</span>card-container<span data-reader-unique-id="44">"</span></span><span data-reader-unique-id="45">&gt;</span></span>
<span data-reader-unique-id="46"><span data-reader-unique-id="47"><span data-reader-unique-id="48">&lt;</span>div</span> <span data-reader-unique-id="49">class</span><span data-reader-unique-id="50"><span data-reader-unique-id="51">=</span><span data-reader-unique-id="52">"</span>card<span data-reader-unique-id="53">"</span></span><span data-reader-unique-id="54">&gt;</span></span>
<span data-reader-unique-id="55"><span data-reader-unique-id="56"><span data-reader-unique-id="57">&lt;</span>figure</span><span data-reader-unique-id="58">&gt;</span></span> ... <span data-reader-unique-id="59"><span data-reader-unique-id="60"><span data-reader-unique-id="61">&lt;/</span>figure</span><span data-reader-unique-id="62">&gt;</span></span>
<span data-reader-unique-id="63"><span data-reader-unique-id="64"><span data-reader-unique-id="65">&lt;</span>div</span><span data-reader-unique-id="66">&gt;</span></span>
<span data-reader-unique-id="67"><span data-reader-unique-id="68"><span data-reader-unique-id="69">&lt;</span>div</span> <span data-reader-unique-id="70">class</span><span data-reader-unique-id="71"><span data-reader-unique-id="72">=</span><span data-reader-unique-id="73">"</span>meta<span data-reader-unique-id="74">"</span></span><span data-reader-unique-id="75">&gt;</span></span>
<span data-reader-unique-id="76"><span data-reader-unique-id="77"><span data-reader-unique-id="78">&lt;</span>h2</span><span data-reader-unique-id="79">&gt;</span></span>...<span data-reader-unique-id="80"><span data-reader-unique-id="81"><span data-reader-unique-id="82">&lt;/</span>h2</span><span data-reader-unique-id="83">&gt;</span></span>
<span data-reader-unique-id="84"><span data-reader-unique-id="85"><span data-reader-unique-id="86">&lt;</span>span</span> <span data-reader-unique-id="87">class</span><span data-reader-unique-id="88"><span data-reader-unique-id="89">=</span><span data-reader-unique-id="90">"</span>time<span data-reader-unique-id="91">"</span></span><span data-reader-unique-id="92">&gt;</span></span>...<span data-reader-unique-id="93"><span data-reader-unique-id="94"><span data-reader-unique-id="95">&lt;/</span>span</span><span data-reader-unique-id="96">&gt;</span></span>
<span data-reader-unique-id="97"><span data-reader-unique-id="98"><span data-reader-unique-id="99">&lt;/</span>div</span><span data-reader-unique-id="100">&gt;</span></span>
<span data-reader-unique-id="101"><span data-reader-unique-id="102"><span data-reader-unique-id="103">&lt;</span>div</span> <span data-reader-unique-id="104">class</span><span data-reader-unique-id="105"><span data-reader-unique-id="106">=</span><span data-reader-unique-id="107">"</span>notes<span data-reader-unique-id="108">"</span></span><span data-reader-unique-id="109">&gt;</span></span>
<span data-reader-unique-id="110"><span data-reader-unique-id="111"><span data-reader-unique-id="112">&lt;</span>p</span> <span data-reader-unique-id="113">class</span><span data-reader-unique-id="114"><span data-reader-unique-id="115">=</span><span data-reader-unique-id="116">"</span>desc<span data-reader-unique-id="117">"</span></span><span data-reader-unique-id="118">&gt;</span></span>...<span data-reader-unique-id="119"><span data-reader-unique-id="120"><span data-reader-unique-id="121">&lt;/</span>p</span><span data-reader-unique-id="122">&gt;</span></span>
<span data-reader-unique-id="123"><span data-reader-unique-id="124"><span data-reader-unique-id="125">&lt;</span>div</span> <span data-reader-unique-id="126">class</span><span data-reader-unique-id="127"><span data-reader-unique-id="128">=</span><span data-reader-unique-id="129">"</span>links<span data-reader-unique-id="130">"</span></span><span data-reader-unique-id="131">&gt;</span></span>...<span data-reader-unique-id="132"><span data-reader-unique-id="133"><span data-reader-unique-id="134">&lt;/</span>div</span><span data-reader-unique-id="135">&gt;</span></span>
<span data-reader-unique-id="136"><span data-reader-unique-id="137"><span data-reader-unique-id="138">&lt;/</span>div</span><span data-reader-unique-id="139">&gt;</span></span>
<span data-reader-unique-id="140"><span data-reader-unique-id="141"><span data-reader-unique-id="142">&lt;</span>button</span><span data-reader-unique-id="143">&gt;</span></span>...<span data-reader-unique-id="144"><span data-reader-unique-id="145"><span data-reader-unique-id="146">&lt;/</span>button</span><span data-reader-unique-id="147">&gt;</span></span>
<span data-reader-unique-id="148"><span data-reader-unique-id="149"><span data-reader-unique-id="150">&lt;/</span>div</span><span data-reader-unique-id="151">&gt;</span></span>
<span data-reader-unique-id="152"><span data-reader-unique-id="153"><span data-reader-unique-id="154">&lt;/</span>div</span><span data-reader-unique-id="155">&gt;</span></span>
<span data-reader-unique-id="156"><span data-reader-unique-id="157"><span data-reader-unique-id="158">&lt;/</span>div</span><span data-reader-unique-id="159">&gt;</span></span></code></pre>
</div>
<p data-reader-unique-id="160">Then, I’m setting containment (the <a href="https://css-tricks.com/almanac/properties/c/contain/" data-reader-unique-id="161"><code data-reader-unique-id="162">contain</code> property</a>) on the parent on which I’ll be querying the container styles (<code data-reader-unique-id="163">.card-container</code>). I’m also setting a relative grid layout on the parent of <code data-reader-unique-id="164">.card-container</code>, so its <code data-reader-unique-id="165">inline-size</code> will change based on that grid. This is what I’m querying for with <code data-reader-unique-id="166">@container</code>:</p>
<div class="scrollable extendsBeyondTextColumn">
<pre rel="CSS" data-line="" class="clear" data-reader-unique-id="167" style="white-space: pre;"><code markup="tt" data-reader-unique-id="168"><span data-reader-unique-id="169"><span data-reader-unique-id="170">.card-container</span></span> <span data-reader-unique-id="171">{</span>
<span data-reader-unique-id="172">contain</span><span data-reader-unique-id="173">:</span> layout inline-size<span data-reader-unique-id="174">;</span>
<span data-reader-unique-id="175">width</span><span data-reader-unique-id="176">:</span> <span data-reader-unique-id="177">100</span><span data-reader-unique-id="178">%</span><span data-reader-unique-id="179">;</span>
<span data-reader-unique-id="180">}</span></code></pre>
</div>
<p data-reader-unique-id="181">Now, I can query for container styles to adjust styles! This is very similar to how you would set styles using width-based media queries, using <code data-reader-unique-id="182">max-width</code> to set styles when an element is <em data-reader-unique-id="183">smaller</em> than a certain size, and <code data-reader-unique-id="184">min-width</code> when it is <em data-reader-unique-id="185">larger</em>.</p>
<div class="scrollable extendsBeyondTextColumn">
<pre rel="CSS" data-line="" class="clear" data-reader-unique-id="186" style="white-space: pre;"><code markup="tt" data-reader-unique-id="187">
<span data-reader-unique-id="188"><span data-reader-unique-id="189">@container</span> <span data-reader-unique-id="190">(</span><span data-reader-unique-id="191">max-width</span><span data-reader-unique-id="192">:</span> <span data-reader-unique-id="193">850</span><span data-reader-unique-id="194">px</span><span data-reader-unique-id="195">)</span></span> <span data-reader-unique-id="196">{</span>
<span data-reader-unique-id="197"><span data-reader-unique-id="198">.links</span></span> <span data-reader-unique-id="199">{</span>
<span data-reader-unique-id="200">display</span><span data-reader-unique-id="201">:</span> none<span data-reader-unique-id="202">;</span>
<span data-reader-unique-id="203">}</span>
<span data-reader-unique-id="204"><span data-reader-unique-id="205">.time</span></span> <span data-reader-unique-id="206">{</span>
<span data-reader-unique-id="207">font-size</span><span data-reader-unique-id="208">:</span> <span data-reader-unique-id="209">1.25</span><span data-reader-unique-id="210">rem</span><span data-reader-unique-id="211">;</span>
<span data-reader-unique-id="212">}</span>
<span data-reader-unique-id="213">}</span>
<span data-reader-unique-id="214"><span data-reader-unique-id="215">@container</span> <span data-reader-unique-id="216">(</span><span data-reader-unique-id="217">max-width</span><span data-reader-unique-id="218">:</span> <span data-reader-unique-id="219">650</span><span data-reader-unique-id="220">px</span><span data-reader-unique-id="221">)</span></span> <span data-reader-unique-id="222">{</span>
<span data-reader-unique-id="223"><span data-reader-unique-id="224">.card</span></span> <span data-reader-unique-id="225">{</span>
<span data-reader-unique-id="226">gap</span><span data-reader-unique-id="227">:</span> <span data-reader-unique-id="228">1</span><span data-reader-unique-id="229">rem</span><span data-reader-unique-id="230">;</span>
<span data-reader-unique-id="231">}</span>
<span data-reader-unique-id="232">}</span></code></pre>
</div>
<figure data-reader-unique-id="233">
<p data-reader-unique-id="234"><video autoplay="" controls="" loop="" muted="" src="https://css-tricks.com/wp-content/uploads/2021/04/Kapture-2021-03-24-at-12.04.23.mp4" playsinline="" name="fitvid0" data-reader-silent-looped-animation="" data-reader-unique-id="235" class="extendsBeyondTextColumn" style="width: 786.8125px; margin-inline-start: -70px;"></video></p>
</figure>
<h3 data-reader-unique-id="236"><span class="converted-anchor" data-reader-unique-id="237"></span>Container Queries + Media Queries</h3>
<p data-reader-unique-id="240">One of the best features of container queries is the ability to separate <em data-reader-unique-id="241">micro layouts</em> from <em data-reader-unique-id="242">macro layouts</em>. You can style individual elements with container queries, creating nuanced micro layouts, and style entire page layouts with media queries, the macro layout. This creates a new level of control that enables even more responsive interfaces.</p>
<p data-reader-unique-id="243">Here’s another example that shows the power of using media queries for macro layout (i.e. the calendar going from single-panel to multi-panel), and micro layout (i.e. the date layout/size and event margins/size shifting), to create a beautiful orchestra of queries.</p>
<figure data-reader-unique-id="244">
<p data-reader-unique-id="245"><video autoplay="" controls="" loop="" muted="" src="https://css-tricks.com/wp-content/uploads/2021/04/Kapture-2021-03-15-at-17.43.54.mp4" playsinline="" name="fitvid1" data-reader-silent-looped-animation="" data-reader-unique-id="246" class="extendsBeyondTextColumn" style="width: 786.8125px; margin-inline-start: -70px;"></video></p>
</figure>
<h3 data-reader-unique-id="250"><span class="converted-anchor" data-reader-unique-id="251"></span>Container Queries + CSS Grid</h3>
<p data-reader-unique-id="254">One of my personal favorite ways to see the impact of container queries is to see how they work within a grid. Take the following example of a plant commerce UI:</p>
<figure data-reader-unique-id="255">
<p data-reader-unique-id="256"><video autoplay="" controls="" loop="" muted="" src="https://css-tricks.com/wp-content/uploads/2021/04/Kapture-2021-03-15-at-17.27.15.mp4" playsinline="" name="fitvid2" data-reader-silent-looped-animation="" data-reader-unique-id="257" class="extendsBeyondTextColumn" style="width: 786.8125px; margin-inline-start: -70px;"></video></p>
</figure>
<p data-reader-unique-id="258">No media queries are used on this website at all. Instead, we are only using container queries along with CSS grid to display the shopping card component in different views.</p>
<p data-reader-unique-id="259">In the product grid, the layout is created with <code data-reader-unique-id="260">grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));</code>. This creates a layout that tells the cards to take up the available fractional space until they hit <code data-reader-unique-id="261">230px</code> in size, and then to flow to the next row. Check out more grid tricks at <a href="http://1linelayouts.glitch.me" data-reader-unique-id="262">1linelayouts.com</a>.</p>
<p data-reader-unique-id="263">Then, we have a container query that styles the cards to take on a vertical block layout when they are less than <code data-reader-unique-id="264">350px</code> wide, and shifts to a horizontal inline layout by applying <code data-reader-unique-id="265">display: flex</code> (which has an inline flow by default).</p>
<div class="scrollable extendsBeyondTextColumn">
<pre rel="CSS" data-line="" class="clear" data-reader-unique-id="266" style="white-space: pre;"><code markup="tt" data-reader-unique-id="267"><span data-reader-unique-id="268"><span data-reader-unique-id="269">@container</span> <span data-reader-unique-id="270">(</span><span data-reader-unique-id="271">min-width</span><span data-reader-unique-id="272">:</span> <span data-reader-unique-id="273">350</span><span data-reader-unique-id="274">px</span><span data-reader-unique-id="275">)</span></span> <span data-reader-unique-id="276">{</span>
<span data-reader-unique-id="277"><span data-reader-unique-id="278">.product-container</span></span> <span data-reader-unique-id="279">{</span>
<span data-reader-unique-id="280">padding</span><span data-reader-unique-id="281">:</span> <span data-reader-unique-id="282">0.5</span><span data-reader-unique-id="283">rem</span> <span data-reader-unique-id="284">0</span> <span data-reader-unique-id="285">0</span><span data-reader-unique-id="286">;</span>
<span data-reader-unique-id="287">display</span><span data-reader-unique-id="288">:</span> flex<span data-reader-unique-id="289">;</span>
<span data-reader-unique-id="290">}</span>
<span data-reader-unique-id="291">}</span></code></pre>
</div>
<p data-reader-unique-id="292">This means that each card <em data-reader-unique-id="293">owns its own responsive styling</em>. This yet another example of where you can create a macro layout with the product grid, and a micro layout with the product cards. Pretty cool!</p>
<h3 data-reader-unique-id="294"><span class="converted-anchor" data-reader-unique-id="295"></span>Usage</h3>
<p data-reader-unique-id="298">In order to use <code data-reader-unique-id="299">@container</code>, you first need to create a parent element that has <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/contain" data-reader-unique-id="300">containment</a>. In order to do so, you’ll need to set <code data-reader-unique-id="301">contain: layout inline-size</code> on the parent. You can use <code data-reader-unique-id="302">inline-size</code> since we currently can only apply container queries to the inline axis. This prevents your layout from breaking in the block direction.</p>
<p data-reader-unique-id="303">Setting <code data-reader-unique-id="304">contain: layout inline-size</code> creates a new <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block" data-reader-unique-id="305">containing block</a> and new <a href="https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Block_formatting_context" data-reader-unique-id="306">block formatting context</a>, letting the browser separate it from the rest of the layout. Now, we can query!</p>
<h3 data-reader-unique-id="307"><span class="converted-anchor" data-reader-unique-id="308"></span>Limitations</h3>
<p data-reader-unique-id="311">Currently, you cannot use height-based container queries, using only the block axis. In order to make grid children work with <code data-reader-unique-id="312">@container</code>, you’ll need to add a wrapper element. Despite this, adding a wrapper lets you still get the effects you want.</p>
<h3 data-reader-unique-id="313"><span class="converted-anchor" data-reader-unique-id="314"></span>Try it out</h3>
<p data-reader-unique-id="317">You can experiment with the <code data-reader-unique-id="318">@container</code> property in Chromium today, by navigating to: <code data-reader-unique-id="319">chrome://flags</code> in <a href="https://www.google.com/chrome/canary/" data-reader-unique-id="320">Chrome Canary</a> and turning on the <strong data-reader-unique-id="321">#experimental-container-queries</strong> flag.</p>
<figure data-reader-unique-id="322"><img loading="lazy" src="https://i2.wp.com/css-tricks.com/wp-content/uploads/2021/05/chrome-canary-conatiner-query-flag.png?resize=1902%2C1510&amp;ssl=1" alt="" srcset="https://i2.wp.com/css-tricks.com/wp-content/uploads/2021/05/chrome-canary-conatiner-query-flag.png?w=1902&amp;ssl=1 1902w, https://i2.wp.com/css-tricks.com/wp-content/uploads/2021/05/chrome-canary-conatiner-query-flag.png?resize=300%2C238&amp;ssl=1 300w, https://i2.wp.com/css-tricks.com/wp-content/uploads/2021/05/chrome-canary-conatiner-query-flag.png?resize=1024%2C813&amp;ssl=1 1024w, https://i2.wp.com/css-tricks.com/wp-content/uploads/2021/05/chrome-canary-conatiner-query-flag.png?resize=768%2C610&amp;ssl=1 768w, https://i2.wp.com/css-tricks.com/wp-content/uploads/2021/05/chrome-canary-conatiner-query-flag.png?resize=1536%2C1219&amp;ssl=1 1536w, https://i2.wp.com/css-tricks.com/wp-content/uploads/2021/05/chrome-canary-conatiner-query-flag.png?resize=1000%2C794&amp;ssl=1 1000w" sizes="(min-width: 735px) 864px, 96vw" data-reader-unique-id="323" class="extendsBeyondTextColumn"></figure>
</article>
</main>
</body>
// https://github.com/modularscale/modularscale-sass
@import "modularscale@3.*";
$modularscale: (
base: 12px,
ratio: 1.1,
375px: (
ratio: 1.1
),
900px: (
base: 16px,
ratio: 1.1
),
1200px: (
base: 16px,
ratio: 1.1
)
);
// Schriftgrössen: Modular Scale
.article p {
@include ms-respond(font-size, 0);
}
.article h1 {
@include ms-respond(font-size, 3);
}
.article h2 {
@include ms-respond(font-size, 2);
}
.article h3 {
@include ms-respond(font-size, 1);
}
// Custom Properties
:root {
--border-color: hsla(0, 0%, 0%, 0.1);
--column-padding: 1rem;
--heading-font: Helvetica Neue, sans-serif;
--body-font: Helvetica Neue, sans-serif;
--ui-font: Helvetica Neue, sans-serif;
}
* {
box-sizing: border-box;
min-height: 0vw; // Safari Bugfix: Enables viewport units inside calc()
}
html {
width: 100%;
}
// Layout: 3 Spalten Raster
body {
display: grid;
grid-template-columns: 200px 200px 1fr;
margin: 0;
width: 100%;
font-family: var(--ui-font);
}
img,
video,
iframe {
max-width: 100%;
height: auto;
}
// Layout: Die Liste mit den RSS Feeds (1. Spalte)
.feed-list {
border-right: 1px solid var(--border-color);
padding: var(--column-padding);
}
// Layout: Die Liste mit den Artikeln des ausgewählten RSS Feeds (2. Spalte)
.article-list {
border-right: 1px solid var(--border-color);
padding: var(--column-padding);
}
// Layout: Der Container vom Artikel (3. Spalte)
.article-container {
padding: var(--column-padding);
width: 100%;
}
// Font: Überschriften
.article :where(h1, h2, h3, h4, h5, h6) {
font-family: var(--heading-font);
}
// Font: Fliesstext
.article p {
font-family: var(--body-font);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment